test(calculate-version): 整併重複測試至 app/test 並新增 logger 測試
This commit is contained in:
+42
-4
@@ -36,6 +36,7 @@ test('normalizeBetaFlag 僅將字面值 "true" 視為啟用', () => {
|
||||
assert.equal(normalizeBetaFlag('yes'), false);
|
||||
assert.equal(normalizeBetaFlag(undefined), false);
|
||||
assert.equal(normalizeBetaFlag('null'), false);
|
||||
assert.equal(normalizeBetaFlag('TRUE'), false);
|
||||
});
|
||||
|
||||
test('loadConfig 從環境變數組出設定', () => {
|
||||
@@ -53,16 +54,53 @@ test('loadConfig 從環境變數組出設定', () => {
|
||||
});
|
||||
});
|
||||
|
||||
test('loadConfig 將未提供的 token 正規化為 null,beta 預設 false', () => {
|
||||
const config = loadConfig({
|
||||
test('loadConfig 將未提供或字面 "null" 的 token 正規化為 null,beta 預設 false', () => {
|
||||
const omitted = loadConfig({
|
||||
GITEA_SERVER_URL: 'https://gitea.example.com',
|
||||
GITEA_REPOSITORY: 'owner/repo',
|
||||
});
|
||||
assert.equal(config.token, null);
|
||||
assert.equal(config.isBeta, false);
|
||||
assert.equal(omitted.token, null);
|
||||
assert.equal(omitted.isBeta, false);
|
||||
|
||||
const literalNull = loadConfig({
|
||||
GITEA_SERVER_URL: 'https://gitea.example.com',
|
||||
GITEA_REPOSITORY: 'owner/repo',
|
||||
GITEA_TOKEN: 'null',
|
||||
});
|
||||
assert.equal(literalNull.token, null);
|
||||
});
|
||||
|
||||
test('loadConfig 在缺少必填環境變數時丟出錯誤', () => {
|
||||
assert.throws(() => loadConfig({ GITEA_REPOSITORY: 'owner/repo' }), /GITEA_SERVER_URL 未設定/);
|
||||
assert.throws(() => loadConfig({ GITEA_SERVER_URL: 'https://x' }), /GITEA_REPOSITORY 未設定/);
|
||||
});
|
||||
|
||||
test('loadConfig 於 GITEA_SERVER_URL 非合法 URL 時拋錯', () => {
|
||||
assert.throws(() => loadConfig({
|
||||
GITEA_SERVER_URL: 'not-a-url',
|
||||
GITEA_REPOSITORY: 'owner/repo',
|
||||
}), /GITEA_SERVER_URL 格式錯誤/);
|
||||
});
|
||||
|
||||
test('loadConfig 於 GITEA_SERVER_URL 使用非 http(s) 協定時拋錯', () => {
|
||||
assert.throws(() => loadConfig({
|
||||
GITEA_SERVER_URL: 'ftp://gitea.example.com',
|
||||
GITEA_REPOSITORY: 'owner/repo',
|
||||
}), /必須使用 http 或 https/);
|
||||
});
|
||||
|
||||
test('loadConfig 於 GITEA_REPOSITORY 非 owner/repo 形式或含路徑穿越時拋錯', () => {
|
||||
const base = { GITEA_SERVER_URL: 'https://gitea.example.com' };
|
||||
assert.throws(() => loadConfig({ ...base, GITEA_REPOSITORY: 'no-slash' }), /GITEA_REPOSITORY 格式錯誤/);
|
||||
assert.throws(() => loadConfig({ ...base, GITEA_REPOSITORY: 'owner/repo/extra' }), /GITEA_REPOSITORY 格式錯誤/);
|
||||
assert.throws(() => loadConfig({ ...base, GITEA_REPOSITORY: '../owner/repo' }), /GITEA_REPOSITORY 格式錯誤/);
|
||||
assert.throws(() => loadConfig({ ...base, GITEA_REPOSITORY: 'owner/..' }), /GITEA_REPOSITORY 格式錯誤/);
|
||||
});
|
||||
|
||||
test('loadConfig 接受含點號的合法 owner/repo', () => {
|
||||
const config = loadConfig({
|
||||
GITEA_SERVER_URL: 'https://gitea.example.com',
|
||||
GITEA_REPOSITORY: 'my.org/my.repo',
|
||||
});
|
||||
assert.equal(config.repository, 'my.org/my.repo');
|
||||
});
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
'use strict';
|
||||
|
||||
const test = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
|
||||
const { main } = require('../index');
|
||||
|
||||
// 安靜的 log 記錄器,避免測試輸出雜訊
|
||||
const silentLog = { section() {}, info() {}, error() {} };
|
||||
|
||||
const okConfig = {
|
||||
serverUrl: 'https://gitea.example.com',
|
||||
repository: 'owner/repo',
|
||||
token: null,
|
||||
isBeta: false,
|
||||
};
|
||||
|
||||
test('main 成功時計算版本並寫出 output', async () => {
|
||||
let written;
|
||||
const version = await main({
|
||||
loadConfig: () => okConfig,
|
||||
fetchReleases: async () => ([{ tag_name: 'v1.2.3' }]),
|
||||
writeOutput: (name, value) => { written = { name, value }; },
|
||||
log: silentLog,
|
||||
});
|
||||
|
||||
assert.equal(version, '1.2.4');
|
||||
assert.deepEqual(written, { name: 'version', value: '1.2.4' });
|
||||
});
|
||||
|
||||
test('main 將 release URL 與 token 傳給 fetchReleases', async () => {
|
||||
let receivedUrl;
|
||||
let receivedOptions;
|
||||
await main({
|
||||
loadConfig: () => ({ ...okConfig, token: 'secret' }),
|
||||
fetchReleases: async (url, options) => { receivedUrl = url; receivedOptions = options; return []; },
|
||||
writeOutput: () => {},
|
||||
log: silentLog,
|
||||
});
|
||||
|
||||
assert.equal(receivedUrl, 'https://gitea.example.com/api/v1/repos/owner/repo/releases');
|
||||
assert.equal(receivedOptions.token, 'secret');
|
||||
});
|
||||
|
||||
test('main 於 loadConfig 失敗時向外拋出', async () => {
|
||||
await assert.rejects(() => main({
|
||||
loadConfig: () => { throw new Error('GITEA_SERVER_URL 未設定'); },
|
||||
log: silentLog,
|
||||
}), /GITEA_SERVER_URL 未設定/);
|
||||
});
|
||||
|
||||
test('main 於 fetchReleases 失敗時向外拋出', async () => {
|
||||
await assert.rejects(() => main({
|
||||
loadConfig: () => okConfig,
|
||||
fetchReleases: async () => { throw new Error('release API 請求失敗 (page=1)'); },
|
||||
writeOutput: () => {},
|
||||
log: silentLog,
|
||||
}), /release API 請求失敗/);
|
||||
});
|
||||
@@ -0,0 +1,53 @@
|
||||
'use strict';
|
||||
|
||||
const { test, afterEach } = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const logger = require('../logger');
|
||||
|
||||
const realStdout = process.stdout.write;
|
||||
const realStderr = process.stderr.write;
|
||||
|
||||
afterEach(() => {
|
||||
process.stdout.write = realStdout;
|
||||
process.stderr.write = realStderr;
|
||||
});
|
||||
|
||||
// 同時攔截 stdout 與 stderr,回傳本次 run() 內各自寫出的字串
|
||||
function capture(run) {
|
||||
const out = [];
|
||||
const err = [];
|
||||
process.stdout.write = (chunk) => { out.push(String(chunk)); return true; };
|
||||
process.stderr.write = (chunk) => { err.push(String(chunk)); return true; };
|
||||
try {
|
||||
run();
|
||||
} finally {
|
||||
process.stdout.write = realStdout;
|
||||
process.stderr.write = realStderr;
|
||||
}
|
||||
return { out: out.join(''), err: err.join('') };
|
||||
}
|
||||
|
||||
const TIME = /\[\d{4}\/\d{2}\/\d{2} \d{2}:\d{2}:\d{2}\]/;
|
||||
|
||||
test('section 於 stdout 輸出標題與 50 寬分隔線區塊', () => {
|
||||
const { out, err } = capture(() => logger.section('參數檢查'));
|
||||
assert.equal(out, `\n${'='.repeat(50)}\n參數檢查\n${'-'.repeat(50)}\n`);
|
||||
assert.equal(err, '');
|
||||
});
|
||||
|
||||
test('info 以 [INF][時間]: 格式輸出至 stdout', () => {
|
||||
const { out, err } = capture(() => logger.info('IS_BETA=false'));
|
||||
assert.match(out, new RegExp(`^\\[INF\\]${TIME.source}: IS_BETA=false\\n$`));
|
||||
assert.equal(err, '');
|
||||
});
|
||||
|
||||
test('error 以 [ERR][時間]: 格式輸出至 stderr,且不寫入 stdout', () => {
|
||||
const { out, err } = capture(() => logger.error('GITEA_SERVER_URL 未設定'));
|
||||
assert.match(err, new RegExp(`^\\[ERR\\]${TIME.source}: GITEA_SERVER_URL 未設定\\n$`));
|
||||
assert.equal(out, '');
|
||||
});
|
||||
|
||||
test('info/error 的時間戳為 yyyy/MM/dd HH:mm:ss 格式', () => {
|
||||
const { out } = capture(() => logger.info('x'));
|
||||
assert.match(out, TIME);
|
||||
});
|
||||
@@ -68,3 +68,11 @@ test('fetchReleases 在 null 回應時視為無更多資料', async () => {
|
||||
const releases = await fetchReleases('https://gitea.example.com/api/v1/repos/o/r/releases');
|
||||
assert.deepEqual(releases, []);
|
||||
});
|
||||
|
||||
test('fetchReleases 在回傳無法解析的 JSON 時丟出例外', async () => {
|
||||
globalThis.fetch = async () => ({ ok: true, text: async () => '{ this is not valid json' });
|
||||
await assert.rejects(
|
||||
() => fetchReleases('https://gitea.example.com/api/v1/repos/o/r/releases'),
|
||||
/回傳資料無法解析/,
|
||||
);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user