diff --git a/app/test/config.test.js b/app/test/config.test.js index d67f54e..c41bed6 100644 --- a/app/test/config.test.js +++ b/app/test/config.test.js @@ -10,15 +10,25 @@ const { loadConfig, } = require('../config'); -test('isUnset 將 undefined/空字串/null 字面值視為未設定', () => { +test('isUnset 對 undefined/null/空字串/字面 "null" 視為未設定', () => { assert.equal(isUnset(undefined), true); + assert.equal(isUnset(null), true); assert.equal(isUnset(''), true); assert.equal(isUnset('null'), true); - assert.equal(isUnset('value'), false); }); -test('requireEnv 未設定時拋錯,有值時回傳原值', () => { +test('isUnset 對其他非空值視為已設定', () => { + assert.equal(isUnset('value'), false); + assert.equal(isUnset('false'), false); + assert.equal(isUnset('0'), false); +}); + +test('requireEnv 未設定時拋錯', () => { assert.throws(() => requireEnv('FOO', ''), /FOO 未設定/); + assert.throws(() => requireEnv('FOO', undefined), /FOO 未設定/); +}); + +test('requireEnv 有值時回傳原值', () => { assert.equal(requireEnv('FOO', 'bar'), 'bar'); }); @@ -73,3 +83,22 @@ test('loadConfig 於 GITEA_SERVER_URL 使用非 http(s) 協定時拋錯', () => GITEA_REPOSITORY: 'owner/repo', }), /必須使用 http 或 https/); }); + +test('loadConfig 於 GITEA_REPOSITORY 格式錯誤或含路徑穿越時拋錯', () => { + assert.throws(() => loadConfig({ + GITEA_SERVER_URL: 'https://gitea.example.com', + GITEA_REPOSITORY: '../evil', + }), /GITEA_REPOSITORY 格式錯誤/); + assert.throws(() => loadConfig({ + GITEA_SERVER_URL: 'https://gitea.example.com', + GITEA_REPOSITORY: 'owner/repo/extra', + }), /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'); +}); diff --git a/app/test/logger.test.js b/app/test/logger.test.js new file mode 100644 index 0000000..390b4dc --- /dev/null +++ b/app/test/logger.test.js @@ -0,0 +1,55 @@ +'use strict'; + +const test = require('node:test'); +const assert = require('node:assert/strict'); + +const logger = require('../logger'); + +// 暫時攔截 process.stdout.write,回傳期間內寫出的內容 +function captureStdout(fn) { + const original = process.stdout.write; + let out = ''; + process.stdout.write = (chunk) => { out += chunk; return true; }; + try { + fn(); + } finally { + process.stdout.write = original; + } + return out; +} + +// 暫時攔截 process.stderr.write,回傳期間內寫出的內容 +function captureStderr(fn) { + const original = process.stderr.write; + let out = ''; + process.stderr.write = (chunk) => { out += chunk; return true; }; + try { + fn(); + } finally { + process.stderr.write = original; + } + return out; +} + +test('section 以主/次分隔線包夾標題輸出至 stdout', () => { + const out = captureStdout(() => logger.section('參數檢查')); + const line = '='.repeat(50); + const subline = '-'.repeat(50); + assert.equal(out, `\n${line}\n參數檢查\n${subline}\n`); +}); + +test('info 以 [info] 前綴與換行輸出至 stdout', () => { + const out = captureStdout(() => logger.info('IS_BETA=false')); + assert.equal(out, '[info] IS_BETA=false\n'); +}); + +test('error 以 [error] 前綴與換行輸出至 stderr', () => { + const out = captureStderr(() => logger.error('GITEA_SERVER_URL 未設定')); + assert.equal(out, '[error] GITEA_SERVER_URL 未設定\n'); +}); + +test('error 僅輸出不終止行程,呼叫後仍可繼續執行', () => { + let reached = false; + captureStderr(() => { logger.error('still alive'); reached = true; }); + assert.equal(reached, true); +}); diff --git a/app/test/version.test.js b/app/test/version.test.js index f24e040..13fdc43 100644 --- a/app/test/version.test.js +++ b/app/test/version.test.js @@ -18,6 +18,7 @@ const rel = (tag) => ({ tag_name: tag }); test('compareVersionArrays 逐區段比較且較短者較小', () => { assert.ok(compareVersionArrays([1, 10], [1, 2, 3]) > 0); assert.ok(compareVersionArrays([1, 2], [1, 2, 0]) < 0); + assert.ok(compareVersionArrays([2, 0, 0], [1, 9, 9]) > 0); assert.equal(compareVersionArrays([1, 2, 3], [1, 2, 3]), 0); });