Files
tea-sdlc/test/tea-login.test.js
T
jiantw83andClaude Opus 5 28dec83498 test(腳本契約): 以子行程與 stub server 驗證契約與四層前置檢查
測試一律以子行程執行腳本、比對 stdout 的單行 JSON 與 exit code,因為那正是
七個平台共用的實際呼叫方式,不會因內部重構而破碎。

Gitea 以本機 stub server 替身並錄下每一筆請求,藉此斷言「腳本到底發了哪些
請求」——包含 --dry-run 不得發出任何請求、前一層檢查沒過就不再往下打、
寫入權探針不得挾帶任何要寫入的欄位。git 則在 .tmp/ 下的臨時 repo 跑真實
指令,比 mock 可信且成本低。

lib-exports.test.js 是唯一直接 import lib 的例外:冪等查重與 git 執行點在
CLI 邊界上還沒有消費者,檔頭已註明等 #4 與 #10 落地後即可縮小或移除。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-17 04:26:51 +00:00

125 lines
4.3 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.
/**
* 認證來源:預設沿用使用者既有的 `tea login`,不另外要求設定一組 token。
* TEA_SDLC_API_BASE / TEA_SDLC_TOKEN 只是給測試與 CI 的覆寫出口。
*/
import test from 'node:test';
import assert from 'node:assert/strict';
import { mkdtempSync, mkdirSync, writeFileSync } from 'node:fs';
import { join } from 'node:path';
import { runScript, tmpRoot } from './helpers/run-script.js';
import { startStubGitea, healthyRoutes } from './helpers/stub-gitea.js';
const REPO = 'plugins/tea-sdlc';
/** 寫一份 tea 風格的 config.yml 到暫存目錄,回傳路徑 */
function writeTeaConfig(logins) {
mkdirSync(tmpRoot, { recursive: true });
const dir = mkdtempSync(join(tmpRoot, 'tea-'));
const path = join(dir, 'config.yml');
const body = logins
.map((l) =>
[
` - name: ${l.name}`,
` url: ${l.url}`,
` token: ${l.token}`,
` default: ${l.default ?? false}`,
` ssh_key: ""`,
` user: ${l.user ?? 'tester'}`,
].join('\n'),
)
.join('\n');
writeFileSync(path, `logins:\n${body}\npreferences:\n editor: false\n`);
return path;
}
async function withStub(t) {
const stub = await startStubGitea(healthyRoutes(REPO));
t.after(() => stub.close());
return stub;
}
/** stub 的 base 是 .../api/v1,tea config 記的是站台根網址 */
const siteUrl = (stub) => stub.base.replace(/\/api\/v1$/, '');
test('沒有環境變數覆寫時,採用 tea 設定中 default 為真的登入', async (t) => {
const stub = await withStub(t);
const config = writeTeaConfig([
{ name: 'OTHER', url: 'https://gitea.example.com', token: 'wrong-token' },
{ name: 'JSC', url: siteUrl(stub), token: 'config-token', default: true },
]);
const { code, json } = await runScript('labels-list.js', ['--repo', REPO], {
env: { TEA_SDLC_CONFIG: config },
});
assert.equal(code, 0);
assert.equal(json.ok, true);
assert.equal(stub.requests[0].authorization, 'token config-token');
});
test('--host 指定站台時,挑出對應的那一組登入', async (t) => {
const stub = await withStub(t);
const config = writeTeaConfig([
{ name: 'JSC', url: siteUrl(stub), token: 'jsc-token' },
{ name: 'HP', url: 'https://gitea.example.com', token: 'hp-token', default: true },
]);
const { code, json } = await runScript(
'labels-list.js',
['--repo', REPO, '--host', siteUrl(stub)],
{ env: { TEA_SDLC_CONFIG: config } },
);
assert.equal(code, 0, JSON.stringify(json));
assert.equal(stub.requests[0].authorization, 'token jsc-token');
});
test('--host 指定的站台沒有對應登入時,明確說是哪個站台缺登入', async (t) => {
const config = writeTeaConfig([{ name: 'JSC', url: 'https://gitea.example.com', token: 't', default: true }]);
const { code, json } = await runScript(
'labels-list.js',
['--repo', REPO, '--host', 'https://gitea.missing.tw'],
{ env: { TEA_SDLC_CONFIG: config } },
);
assert.equal(code, 1);
assert.equal(json.error.code, 'LOGIN_INVALID');
assert.match(json.error.message, /gitea\.missing\.tw/);
});
test('tea 設定檔不存在時,訊息指向 tea login add', async (t) => {
const { json } = await runScript('labels-list.js', ['--repo', REPO], {
env: { TEA_SDLC_CONFIG: join(tmpRoot, 'definitely-not-here', 'config.yml') },
});
assert.equal(json.error.code, 'LOGIN_INVALID');
assert.match(json.error.message, /tea login add/);
});
test('設定檔存在但沒有任何登入時,同樣指向 tea login add', async (t) => {
mkdirSync(tmpRoot, { recursive: true });
const dir = mkdtempSync(join(tmpRoot, 'tea-'));
const path = join(dir, 'config.yml');
writeFileSync(path, 'logins: []\npreferences:\n editor: false\n');
const { json } = await runScript('labels-list.js', ['--repo', REPO], {
env: { TEA_SDLC_CONFIG: path },
});
assert.equal(json.error.code, 'LOGIN_INVALID');
assert.match(json.error.message, /tea login add/);
});
test('只有一組登入且未標 default 時,就用那一組', async (t) => {
const stub = await withStub(t);
const config = writeTeaConfig([{ name: 'ONLY', url: siteUrl(stub), token: 'only-token' }]);
const { code } = await runScript('labels-list.js', ['--repo', REPO], {
env: { TEA_SDLC_CONFIG: config },
});
assert.equal(code, 0);
assert.equal(stub.requests[0].authorization, 'token only-token');
});