test(calculate-version): 整併單元測試至 app/test 並移除無法被執行的重複測試目錄
CI / AI Code Review (pull_request) Successful in 38s

app/package.json 的 npm test(node --test,cwd 為 app/)只會探索 app/test/,
repo 根目錄另一份 test/ 中的 main 整合測試(index.test.js)因此從未被執行。
將 config/index/releases/version 收斂至 app/test/(修正 require 路徑),
保留 output 測試,移除重複且無法執行的 repo 根 test/。現 app/test/ 共 34 筆測試皆可執行。
This commit is contained in:
Jeffery
2026-06-26 11:18:53 +08:00
parent 8846b6d386
commit 0977e66ff4
7 changed files with 160 additions and 399 deletions
+89 -54
View File
@@ -1,70 +1,105 @@
'use strict';
const { test, afterEach } = require('node:test');
const test = require('node:test');
const assert = require('node:assert/strict');
const { RELEASES_PER_PAGE, fetchReleases } = require('../releases');
const realFetch = globalThis.fetch;
const { fetchReleases } = require('../releases');
afterEach(() => {
globalThis.fetch = realFetch;
});
function mockResponse(items) {
return { ok: true, text: async () => JSON.stringify(items) };
// 以可控的假回應替換全域 fetch,並於結束後還原
function withFetch(handler, run) {
const original = globalThis.fetch;
globalThis.fetch = handler;
return Promise.resolve()
.then(run)
.finally(() => {
globalThis.fetch = original;
});
}
test('fetchReleases 逐頁讀取直到不足一頁', async () => {
const fullPage = Array.from({ length: RELEASES_PER_PAGE }, (_, i) => ({ id: i }));
const lastPage = [{ id: 100 }, { id: 101 }];
const requested = [];
globalThis.fetch = async (url) => {
const page = Number(new URL(url).searchParams.get('page'));
requested.push(page);
return mockResponse(page === 1 ? fullPage : lastPage);
};
const releases = await fetchReleases('https://gitea.example.com/api/v1/repos/o/r/releases');
assert.equal(releases.length, RELEASES_PER_PAGE + lastPage.length);
assert.deepEqual(requested, [1, 2]);
// 建立模擬 Response 物件
const jsonResponse = (body, ok = true) => ({
ok,
text: async () => (typeof body === 'string' ? body : JSON.stringify(body)),
});
test('fetchReleases 在空陣列頁面停止', async () => {
globalThis.fetch = async () => mockResponse([]);
const releases = await fetchReleases('https://gitea.example.com/api/v1/repos/o/r/releases');
assert.deepEqual(releases, []);
});
test('fetchReleases 帶上授權標頭', async () => {
let seenHeaders;
globalThis.fetch = async (_url, opts) => {
seenHeaders = opts.headers;
return mockResponse([]);
};
await fetchReleases('https://gitea.example.com/api/v1/repos/o/r/releases', { token: 'secret' });
assert.equal(seenHeaders.Authorization, 'token secret');
});
test('fetchReleases 在 HTTP 錯誤時丟出例外', async () => {
globalThis.fetch = async () => ({ ok: false, status: 500 });
await assert.rejects(
() => fetchReleases('https://gitea.example.com/api/v1/repos/o/r/releases'),
/release API 請求失敗/,
test('fetchReleases 單頁(不足一頁即停止', async () => {
await withFetch(
async () => jsonResponse([{ tag_name: 'v1.0.0' }]),
async () => {
const releases = await fetchReleases('https://gitea.example.com/api');
assert.deepEqual(releases, [{ tag_name: 'v1.0.0' }]);
},
);
});
test('fetchReleases 在回傳非陣列時丟出例外', async () => {
globalThis.fetch = async () => mockResponse({ message: 'not an array' });
await assert.rejects(
() => fetchReleases('https://gitea.example.com/api/v1/repos/o/r/releases'),
/回傳非陣列資料/,
test('fetchReleases 跨頁合併直到不足一頁', async () => {
const pages = {
1: Array.from({ length: 10 }, (_, i) => ({ tag_name: `v1.0.${i}` })),
2: [{ tag_name: 'v1.1.0' }],
};
const seen = [];
await withFetch(
async (url) => {
const page = Number(new URL(url).searchParams.get('page'));
seen.push(page);
return jsonResponse(pages[page]);
},
async () => {
const releases = await fetchReleases('https://gitea.example.com/api');
assert.equal(releases.length, 11);
assert.deepEqual(seen, [1, 2]);
},
);
});
test('fetchReleases 在 null 回應時視為無更多資料', async () => {
globalThis.fetch = async () => ({ ok: true, text: async () => 'null' });
const releases = await fetchReleases('https://gitea.example.com/api/v1/repos/o/r/releases');
assert.deepEqual(releases, []);
test('fetchReleases 對非 2xx 回應拋錯', async () => {
await withFetch(
async () => jsonResponse('Not Found', false),
async () => {
await assert.rejects(
() => fetchReleases('https://gitea.example.com/api'),
/release API 請求失敗/,
);
},
);
});
test('fetchReleases 對無法解析的 JSON 回應拋錯', async () => {
await withFetch(
async () => jsonResponse('{ this is not valid json', true),
async () => {
await assert.rejects(
() => fetchReleases('https://gitea.example.com/api'),
/回傳資料無法解析/,
);
},
);
});
test('fetchReleases 對非陣列回應拋錯', async () => {
await withFetch(
async () => jsonResponse({ message: 'oops' }),
async () => {
await assert.rejects(
() => fetchReleases('https://gitea.example.com/api'),
/回傳非陣列資料/,
);
},
);
});
test('fetchReleases 有 token 時帶入授權標頭', async () => {
let captured;
await withFetch(
async (url, options) => {
captured = options;
return jsonResponse([]);
},
async () => {
await fetchReleases('https://gitea.example.com/api', { token: 'secret' });
assert.equal(captured.headers.Authorization, 'token secret');
},
);
});