Files
tea-sdlc/test/status.test.js
T
jiantw83andClaude Opus 5 17178f0c4d feat(佈署): 以 npm 裝出 tea-sdlc 指令並產生各平台轉接檔
單一入口 bin/tea-sdlc.js 認四個子指令。第一個位置參數是子指令,其餘 argv 原樣
交出去——既有的 flag 解析拒絕位置參數,所以子指令必須在那之前就被取走。

轉接檔裡沒有路徑,只有一句 tea-sdlc prompt --name <指令名>。正本在哪由 PATH 上
的 tea-sdlc 自己回推:fnm 把 Node 版號寫進全域安裝路徑,寫死路徑的話升一次
Node,七個平台的轉接檔會同時指向不存在的檔案,而且不會有任何錯誤訊息。

prompt 是全專案唯一輸出非 JSON 的路徑,理由只有一個:它的輸出要餵給模型讀。
失敗仍走 envelope——成功是內容,失敗才需要結構。

status 的 ok 不兼差表達環境好壞,健康與否放在 data.healthy:呼叫端要分得出
「status 掛了」與「status 成功查到你環境有問題」。

install 只寫進偵測得到的平台;缺 git/tea 只警告不中止,因為那兩個完全不影響
轉接檔產生,硬擋等於逼使用者為了裝 plugin 先去裝 tea。uninstall 只刪帶產生標記
的檔案,使用者自己寫的同名檔案一律留著並在輸出裡交代。裝哪些指令以 prompts/ 裡
實際存在的正本為準,不是寫死的六個名字——裝出指向不存在正本的轉接檔,使用者只會
看到 PROMPT_NOT_FOUND。

流程正本的 description 前綴在抄進轉接檔之前就檢查:有三個平台關不掉自動觸發,
全靠那句話把 description 窄到不會被誤判,不能等使用者發現誤觸才知道漏了。

議題 #26 #27 #28 #29 #17

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

93 lines
3.4 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.
/**
* status 子指令:在出事之前先知道自己是什麼狀態。
*
* 這裡最要緊的一條是 ok 與健康狀態分離:ok 的意思一直是「這支指令跑成功了」,
* 不兼差表達環境好壞。混用的話呼叫端就分不出「status 掛了」與「status 成功查到
* 你環境有問題」,而這兩件事的下一步完全不同。
*/
import test from 'node:test';
import assert from 'node:assert/strict';
import { join } from 'node:path';
import { manifest, pathWithOnly, repoRoot, runBin } from './helpers/run-script.js';
import { healthyRoutes, stubEnv, withStubGitea } from './helpers/stub-gitea.js';
const REPO = 'plugins/tea-sdlc';
const version = () => manifest().version;
test('回報版本、正本位置、是不是 link 模式、健康旗標與環境四項', async (t) => {
const stub = await withStubGitea(t, healthyRoutes(REPO));
const { code, json } = await runBin(['status'], { env: stubEnv(stub) });
assert.equal(code, 0);
assert.equal(json.ok, true);
assert.equal(json.data.version, version());
assert.equal(json.data.root, repoRoot);
assert.equal(json.data.healthy, true);
assert.deepEqual(Object.keys(json.data.environment).sort(), ['git', 'login', 'node', 'tea']);
});
test('從 working tree 跑的時候是 link 模式,root 指向 working tree', async (t) => {
const stub = await withStubGitea(t, healthyRoutes(REPO));
const { json } = await runBin(['status'], { env: stubEnv(stub) });
assert.equal(json.data.linked, true);
assert.equal(json.data.root.includes('node_modules'), false);
});
test('缺少 git 或 tea 時對應欄位為 false,健康旗標跟著倒下', async (t) => {
const stub = await withStubGitea(t, healthyRoutes(REPO));
const { code, json } = await runBin(['status'], {
env: stubEnv(stub),
path: pathWithOnly(['node']),
});
// 查詢本身仍然成功——它成功查到你的環境有問題
assert.equal(code, 0);
assert.equal(json.ok, true);
assert.equal(json.data.environment.node, true);
assert.equal(json.data.environment.git, false);
assert.equal(json.data.environment.tea, false);
assert.equal(json.data.healthy, false);
});
test('Gitea 登入失效時 ok 仍為 true,healthy 為 false', async (t) => {
const stub = await withStubGitea(t, {
...healthyRoutes(REPO),
'GET /api/v1/user': { status: 401, body: { message: 'token expired' } },
});
const { code, json } = await runBin(['status'], { env: stubEnv(stub) });
assert.equal(code, 0);
assert.equal(json.ok, true);
assert.equal(json.data.environment.login, false);
assert.equal(json.data.healthy, false);
});
test('登入檢查走既有的單一 HTTP 出口,打的是 /user', async (t) => {
const stub = await withStubGitea(t, healthyRoutes(REPO));
await runBin(['status'], { env: stubEnv(stub) });
assert.deepEqual(
stub.requests.map((request) => `${request.method} ${request.path}`),
['GET /api/v1/user'],
);
assert.equal(stub.requests[0].authorization, 'token stub-token');
});
test('根本還沒登入時也只是 login 為 false,不會讓整支指令失敗', async (t) => {
const { code, json } = await runBin(['status'], {
env: { TEA_SDLC_CONFIG: join(repoRoot, '.tmp', 'no-such-tea-config.yml') },
});
assert.equal(code, 0);
assert.equal(json.ok, true);
assert.equal(json.data.environment.login, false);
assert.equal(json.data.healthy, false);
});