import { test, afterEach } from 'node:test' import assert from 'node:assert/strict' import { main } from '../index.js' const realFetch = globalThis.fetch const savedEnv = { ...process.env } afterEach(() => { globalThis.fetch = realFetch process.env = { ...savedEnv } }) function jsonResponse(data) { return { ok: true, status: 200, headers: { get: () => 'application/json' }, json: async () => data, text: async () => JSON.stringify(data), } } // 端對端驗證:列出 release → 刪除舊版本 → 重新列出 release → 列出 tag → 刪除孤立 tag。 test('main 完整清理流程:刪除舊 release 與孤立 tag,呼叫順序符合預期', async () => { for (const k of ['GITEA_SERVER_URL', 'GITEA_REPOSITORY', 'KEEP_COUNT', 'GITEA_TOKEN']) { delete process.env[k] } Object.assign(process.env, { GITEA_SERVER_URL: 'https://gitea.example.com', GITEA_REPOSITORY: 'owner/repo', KEEP_COUNT: '2', }) const releases = [ { id: 3, tag_name: 'v3', name: 'r3', created_at: '2024-03-01T00:00:00Z' }, { id: 2, tag_name: 'v2', name: 'r2', created_at: '2024-02-01T00:00:00Z' }, { id: 1, tag_name: 'v1', name: 'r1', created_at: '2024-01-01T00:00:00Z' }, ] const tags = [{ name: 'v3' }, { name: 'v2' }, { name: 'v1' }, { name: 'orphan' }] const calls = { get: [], delete: [] } globalThis.fetch = async (url, opts) => { const u = new URL(url) const method = (opts && opts.method) || 'GET' if (method === 'DELETE') { calls.delete.push(u.pathname) return { status: 204 } } const page = u.searchParams.get('page') calls.get.push(`${u.pathname}?page=${page}`) if (u.pathname.endsWith('/releases')) { return jsonResponse(page === '1' ? releases : []) } if (u.pathname.endsWith('/tags')) { return jsonResponse(page === '1' ? tags : []) } return jsonResponse([]) } await main() // 保留最新 2 筆(v3、v2),刪除最舊 v1(id=1);孤立 tag 'orphan' 被刪除 assert.deepEqual(calls.delete, [ '/api/v1/repos/owner/repo/releases/1', '/api/v1/repos/owner/repo/tags/orphan', ]) // release 清單被讀取兩次(清理舊版本一次、清理 tag 前重新讀取一次) const releaseListGets = calls.get.filter((c) => c.includes('/releases?page=1')) assert.equal(releaseListGets.length, 2) })