Files
houseplan-card/test/status-label.test.mjs
T
Claude ae0e516af1 process: a rereview sets S7-code-review again instead of stripping it (#706)
The label step after integration ran one gh call
`--add-label "$TO" --remove-label "$FROM"`. For the rereview outcome
TO == FROM == S7-code-review, and gh added and removed the same label:
#699 was left without a status and no new round started (run
36491087708).

- scripts/status-label.mjs: the same label is removed and set again
  through relabel from process-reconcile (#555), so the labeled event
  starts the next round and a failed restore fails the step; a
  different label is still one call.
- _process.yml: the step calls the script.
- PROCESS.md: the exact-candidate rule names the relabel.
- test/status-label.test.mjs; mutants rereview-relabel-in-one-call and
  process-label-step-combined-again.

Issue: #706
User-Visible: no
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018qZfe7YS4rqEMKoVeS3GKd
2026-09-29 07:19:05 +03:00

57 lines
3.8 KiB
JavaScript

// #706: исход rereview возвращает задачу в ту же метку `S7-code-review`, и
// одним вызовом `gh` она добавлялась и тут же снималась — задача оставалась без
// статуса, новый заход не стартовал.
import assert from 'node:assert/strict';
import test from 'node:test';
import { readFileSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
import { moveStatusLabel } from '../scripts/status-label.mjs';
const REPO = 'Matysh/houseplan-card';
const recorder = (fail = () => false) => {
const calls = [];
const execute = (args, opts = {}) => {
calls.push(args.slice(5).join(' '));
const status = fail(args, calls.length) ? 1 : 0;
if (status && !opts.allowFailure) throw new Error(`gh ${args.join(' ')} → boom`);
return { status, stderr: status ? 'boom' : '' };
};
return { calls, execute };
};
test('#706 rereview: та же метка снимается и ставится заново отдельными вызовами', () => {
const { calls, execute } = recorder();
const how = moveStatusLabel({ repo: REPO, issue: 699, from: 'S7-code-review', to: 'S7-code-review', execute });
assert.equal(how, 'relabeled');
assert.deepEqual(calls, ['--remove-label S7-code-review', '--add-label S7-code-review'],
'снятие, затем постановка — событие labeled запускает новый заход');
});
test('#706 обычный исход — одна правка: новая метка ставится, прежняя снимается', () => {
const { calls, execute } = recorder();
assert.equal(moveStatusLabel({ repo: REPO, issue: 700, from: 'S7-code-review', to: 'S8-merged', execute }), 'moved');
assert.deepEqual(calls, ['--add-label S8-merged --remove-label S7-code-review']);
const none = recorder();
moveStatusLabel({ repo: REPO, issue: 700, from: '', to: 'S6-in-progress', execute: none.execute });
assert.deepEqual(none.calls, ['--add-label S6-in-progress'], 'без исходной метки снимать нечего');
});
test('#706 сбой повторной постановки роняет шаг, а не оставляет задачу без статуса молча', () => {
const once = recorder((args, n) => n === 2);
moveStatusLabel({ repo: REPO, issue: 699, from: 'S7-code-review', to: 'S7-code-review', execute: once.execute });
assert.deepEqual(once.calls, ['--remove-label S7-code-review', '--add-label S7-code-review', '--add-label S7-code-review'],
'одна попытка восстановления, как в автосверке (#555)');
const always = recorder((args) => args.includes('--add-label'));
assert.throws(() => moveStatusLabel({ repo: REPO, issue: 699, from: 'S7-code-review', to: 'S7-code-review', execute: always.execute }),
/could not restore S7-code-review/);
assert.throws(() => moveStatusLabel({ repo: REPO, issue: 699, from: 'S7-code-review', to: '', execute: always.execute }), /usage/);
});
test('#706 шаг конвейера переставляет метку через скрипт, а не одним вызовом gh', () => {
const workflow = readFileSync(fileURLToPath(new URL('../.github/workflows/_process.yml', import.meta.url)), 'utf8');
const step = workflow.slice(workflow.indexOf('- name: Переставить метку'), workflow.indexOf('- name: Сводка длительности стадий'));
assert.ok(step.length > 0, 'шаг найден');
assert.match(step, /node scripts\/status-label\.mjs --repo="\$\{\{ github\.repository \}\}" \\\n\s+--issue="\$NUM" --from="\$FROM" --to="\$TO"/);
assert.doesNotMatch(step, /gh issue edit/, 'совмещённый вызов снимал ту же метку, которую ставил');
});