test(release-cleanup): 補上 MAX_PAGES 邊界與刪除失敗記錄測試
新增 fetchAllPages 於第 MAX_PAGES 頁回空陣列時正常結束的測試,以及 cleanupReleases/cleanupOrphanTags 在 deleteResource 回非 204 時繼續處理 並寫入 stderr 錯誤記錄的測試。測試共 59 項全數通過。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
76bccfeea9
commit
fd2dfa8732
@@ -142,6 +142,18 @@ test('fetchAllPages 的錯誤訊息會清理控制字元並限制長度(sanitize
|
|||||||
assert.ok((message.match(/x/g) || []).length <= 200)
|
assert.ok((message.match(/x/g) || []).length <= 200)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('fetchAllPages 在第 MAX_PAGES 頁回傳空陣列時正常結束(不拋例外)', async () => {
|
||||||
|
let page = 0
|
||||||
|
globalThis.fetch = async () => {
|
||||||
|
page += 1
|
||||||
|
// 前 999 頁有資料,第 1000 頁(MAX_PAGES)回空陣列,應正常結束
|
||||||
|
return jsonResponse(page < 1000 ? [{ id: page }] : [])
|
||||||
|
}
|
||||||
|
const client = new GiteaClient({})
|
||||||
|
const items = await client.fetchAllPages('https://example.com/api')
|
||||||
|
assert.equal(items.length, 999)
|
||||||
|
})
|
||||||
|
|
||||||
test('deleteResource 回傳 HTTP 狀態碼', async () => {
|
test('deleteResource 回傳 HTTP 狀態碼', async () => {
|
||||||
globalThis.fetch = async (url, opts) => {
|
globalThis.fetch = async (url, opts) => {
|
||||||
assert.equal(opts.method, 'DELETE')
|
assert.equal(opts.method, 'DELETE')
|
||||||
|
|||||||
@@ -103,6 +103,28 @@ test('cleanupReleases 對 id 進行 URL 編碼(防路徑穿越)', async () => {
|
|||||||
assert.deepEqual(client.deleted, ['http://x/releases/..%2Fevil'])
|
assert.deepEqual(client.deleted, ['http://x/releases/..%2Fevil'])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('cleanupReleases 在刪除回傳非 204 時記錄錯誤至 stderr', async () => {
|
||||||
|
const client = fakeClient(
|
||||||
|
[
|
||||||
|
{ id: 1, tag_name: 'v1', name: 'n1', created_at: '2024-01-01T00:00:00Z' },
|
||||||
|
{ id: 2, tag_name: 'v2', name: 'n2', created_at: '2024-02-01T00:00:00Z' },
|
||||||
|
],
|
||||||
|
() => 500,
|
||||||
|
)
|
||||||
|
const realWrite = process.stderr.write
|
||||||
|
const chunks = []
|
||||||
|
process.stderr.write = (chunk) => {
|
||||||
|
chunks.push(String(chunk))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await cleanupReleases(client, { releaseApiUrl: 'http://x/releases', keepCount: 1 })
|
||||||
|
} finally {
|
||||||
|
process.stderr.write = realWrite
|
||||||
|
}
|
||||||
|
assert.match(chunks.join(''), /\[ERR\]/)
|
||||||
|
})
|
||||||
|
|
||||||
test('cleanupReleases 略過沒有 id 的 release', async () => {
|
test('cleanupReleases 略過沒有 id 的 release', async () => {
|
||||||
const client = fakeClient([
|
const client = fakeClient([
|
||||||
{ id: 1, tag_name: 'v3', name: 'n3', created_at: '2024-03-01T00:00:00Z' },
|
{ id: 1, tag_name: 'v3', name: 'n3', created_at: '2024-03-01T00:00:00Z' },
|
||||||
|
|||||||
@@ -87,6 +87,33 @@ test('cleanupOrphanTags 在沒有任何 release 時刪除所有具名 tag', asyn
|
|||||||
assert.deepEqual(client.deleted, ['http://x/tags/v2.0.0', 'http://x/tags/v1.0.0'])
|
assert.deepEqual(client.deleted, ['http://x/tags/v2.0.0', 'http://x/tags/v1.0.0'])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('cleanupOrphanTags 在刪除回傳非 204 時繼續處理其餘 tag 並記錄錯誤', async () => {
|
||||||
|
const deleted = []
|
||||||
|
const client = {
|
||||||
|
async fetchAllPages(url) {
|
||||||
|
return url.includes('/releases') ? [] : [{ name: 'v1' }, { name: 'v2' }]
|
||||||
|
},
|
||||||
|
async deleteResource(url) {
|
||||||
|
deleted.push(url)
|
||||||
|
return 500
|
||||||
|
},
|
||||||
|
}
|
||||||
|
const realWrite = process.stderr.write
|
||||||
|
const chunks = []
|
||||||
|
process.stderr.write = (chunk) => {
|
||||||
|
chunks.push(String(chunk))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await cleanupOrphanTags(client, config)
|
||||||
|
} finally {
|
||||||
|
process.stderr.write = realWrite
|
||||||
|
}
|
||||||
|
// 兩個孤立 tag 都被嘗試刪除(失敗不中斷),且有錯誤記錄
|
||||||
|
assert.equal(deleted.length, 2)
|
||||||
|
assert.match(chunks.join(''), /\[ERR\]/)
|
||||||
|
})
|
||||||
|
|
||||||
test('cleanupOrphanTags 對含特殊字元的 tag 名稱進行 URL 編碼(防路徑穿越)', async () => {
|
test('cleanupOrphanTags 對含特殊字元的 tag 名稱進行 URL 編碼(防路徑穿越)', async () => {
|
||||||
const client = fakeClient({
|
const client = fakeClient({
|
||||||
releases: [],
|
releases: [],
|
||||||
|
|||||||
Reference in New Issue
Block a user