Files
tea-sdlc/test/issue-link.test.js
T
2026-09-17 13:55:29 +08:00

166 lines
5.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 議題相依:把工作包之間的阻擋/先決關係建到 Gitea 原生的相依上,
* 讓看板呈現真實的開發順序,而不是一堆平鋪的議題。
*/
import test from 'node:test';
import assert from 'node:assert/strict';
import { runScript } from './helpers/run-script.js';
import { healthyRoutes, stubEnv as envFor, withStubGitea } from './helpers/stub-gitea.js';
const REPO = 'plugins/tea-sdlc';
const INDEX = 12;
/** 預設情境:這顆議題目前沒有任何相依 */
function routes(overrides = {}) {
return healthyRoutes(REPO, {
[`GET /api/v1/repos/${REPO}/issues/${INDEX}/dependencies`]: { status: 200, body: [] },
[`GET /api/v1/repos/${REPO}/issues/${INDEX}/blocks`]: { status: 200, body: [] },
[`POST /api/v1/repos/${REPO}/issues/${INDEX}/dependencies`]: { status: 201, body: {} },
[`POST /api/v1/repos/${REPO}/issues/${INDEX}/blocks`]: { status: 201, body: {} },
...overrides,
});
}
const withStub = (t, overrides = {}) => withStubGitea(t, routes(overrides));
const run = (args, stub) =>
runScript('issue-link.js', ['--repo', REPO, '--index', String(INDEX), ...args], {
env: envFor(stub),
});
const posts = (stub, kind) =>
stub.requests.filter((r) => r.method === 'POST' && r.path.endsWith(`/${kind}`));
// ── 建立相依 ───────────────────────────────────────────────────────
test('--depends 建立「這顆被誰擋住」的相依', async (t) => {
const stub = await withStub(t);
const { code, json } = await run(['--depends', '10,11'], stub);
assert.equal(code, 0);
assert.equal(json.ok, true);
assert.deepEqual(posts(stub, 'dependencies').map((r) => r.body.index), [10, 11]);
});
test('--blocks 建立「這顆擋住誰」的相依', async (t) => {
const stub = await withStub(t);
await run(['--blocks', '13'], stub);
assert.deepEqual(posts(stub, 'blocks').map((r) => r.body.index), [13]);
});
test('請求帶上 owner 與 repo:Gitea 的相依端點少了它們會回 404', async (t) => {
const stub = await withStub(t);
await run(['--depends', '10'], stub);
const [post] = posts(stub, 'dependencies');
assert.deepEqual(post.body, { owner: 'plugins', repo: 'tea-sdlc', index: 10 });
});
test('兩種相依可以一次建立', async (t) => {
const stub = await withStub(t);
const { json } = await run(['--depends', '10', '--blocks', '13'], stub);
assert.deepEqual(json.data.added, { depends: [10], blocks: [13] });
});
test('回傳這顆議題最後的相依狀態', async (t) => {
const stub = await withStub(t);
const { json } = await run(['--depends', '10'], stub);
assert.equal(json.data.index, INDEX);
assert.equal(json.data.repo, REPO);
});
// ── 冪等 ───────────────────────────────────────────────────────────
test('已經存在的相依不重建', async (t) => {
const stub = await withStub(t, {
[`GET /api/v1/repos/${REPO}/issues/${INDEX}/dependencies`]: {
status: 200,
body: [{ number: 10 }],
},
});
const { json } = await run(['--depends', '10,11'], stub);
assert.deepEqual(posts(stub, 'dependencies').map((r) => r.body.index), [11], '只補缺的那一個');
assert.deepEqual(json.data.added.depends, [11]);
assert.deepEqual(json.data.skipped.depends, [10]);
});
test('全部都已存在時一個請求都不發', async (t) => {
const stub = await withStub(t, {
[`GET /api/v1/repos/${REPO}/issues/${INDEX}/dependencies`]: {
status: 200,
body: [{ number: 10 }],
},
});
const { code, json } = await run(['--depends', '10'], stub);
assert.equal(code, 0);
assert.deepEqual(posts(stub, 'dependencies'), []);
assert.deepEqual(json.data.added.depends, []);
});
// ── 參數 ───────────────────────────────────────────────────────────
test('沒給任何相依時就不必問 Gitea', async (t) => {
const stub = await withStub(t);
const { code, json } = await run([], stub);
assert.equal(code, 1);
assert.equal(json.error.code, 'NOTHING_TO_LINK');
assert.equal(stub.requests.length, 0);
});
test('相依編號不是正整數時擋在打 Gitea 之前', async (t) => {
const stub = await withStub(t);
const { json } = await run(['--depends', '10,abc'], stub);
assert.equal(json.error.code, 'BAD_INDEX');
assert.equal(stub.requests.length, 0);
});
test('不能把議題設成自己的先決', async (t) => {
const stub = await withStub(t);
const { json } = await run(['--depends', String(INDEX)], stub);
assert.equal(json.error.code, 'SELF_DEPENDENCY');
assert.equal(stub.requests.length, 0);
});
// ── --dry-run ─────────────────────────────────────────────────────
test('--dry-run 列出將建立的相依,且不發出任何寫入請求', async (t) => {
const stub = await withStub(t, {
[`GET /api/v1/repos/${REPO}/issues/${INDEX}/dependencies`]: {
status: 200,
body: [{ number: 10 }],
},
});
const { code, json } = await run(['--depends', '10,11', '--blocks', '13', '--dry-run'], stub);
assert.equal(code, 0);
assert.equal(json.data.dryRun, true);
assert.deepEqual(
json.data.requests.map((r) => `${r.method} ${r.path} ${r.body.index}`),
[
`POST /repos/${REPO}/issues/${INDEX}/dependencies 11`,
`POST /repos/${REPO}/issues/${INDEX}/blocks 13`,
],
'已存在的 10 不該出現在預覽裡',
);
assert.equal(stub.requests.some((r) => r.method !== 'GET'), false);
});