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>
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* 在 .tmp/ 底下開一個真的 git repo。
|
||||
* git 操作不做 mock:在臨時 repo 上跑真的 git 比假的 git 可信,成本也低。
|
||||
*/
|
||||
import { execFileSync } from 'node:child_process';
|
||||
import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
import { tmpRoot } from './run-script.js';
|
||||
|
||||
/**
|
||||
* @returns {{dir: string, cleanup: Function}} dir 為已有一顆 commit 的 git repo
|
||||
*/
|
||||
export function makeTempRepo() {
|
||||
mkdirSync(tmpRoot, { recursive: true });
|
||||
const dir = mkdtempSync(join(tmpRoot, 'repo-'));
|
||||
const git = (...args) =>
|
||||
execFileSync('git', args, {
|
||||
cwd: dir,
|
||||
encoding: 'utf8',
|
||||
env: { ...process.env, GIT_CONFIG_GLOBAL: '/dev/null', GIT_CONFIG_SYSTEM: '/dev/null' },
|
||||
});
|
||||
|
||||
git('init', '-q', '-b', 'master');
|
||||
git('config', 'user.name', 'tester');
|
||||
git('config', 'user.email', 'tester@example.com');
|
||||
writeFileSync(join(dir, 'README.md'), '# temp\n');
|
||||
git('add', '-A');
|
||||
git('commit', '-qm', 'initial');
|
||||
|
||||
return { dir, cleanup: () => rmSync(dir, { recursive: true, force: true }) };
|
||||
}
|
||||
Reference in New Issue
Block a user