test(release-cleanup): 補強逾時、非陣列、token 邊界與 failError 測試
新增 fetchAllPages 的 AbortError 逾時與非陣列回應測試、loadConfig 的 GITEA_TOKEN 邊界測試,以及 logger.failError 測試。測試共 47 項全數通過。 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
f6d2ebeab2
commit
b9d7c4e970
@@ -56,3 +56,17 @@ test('loadConfig 在提供 token 時保留 token 值', () => {
|
|||||||
const cfg = loadConfig({ ...base, GITEA_TOKEN: 'secret' })
|
const cfg = loadConfig({ ...base, GITEA_TOKEN: 'secret' })
|
||||||
assert.equal(cfg.token, 'secret')
|
assert.equal(cfg.token, 'secret')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('loadConfig 在 GITEA_TOKEN 為空字串時視為匿名(token 為 null)', () => {
|
||||||
|
const cfg = loadConfig({ ...base, GITEA_TOKEN: '' })
|
||||||
|
assert.equal(cfg.token, null)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('loadConfig 接受極短與極長的 GITEA_TOKEN', () => {
|
||||||
|
const short = loadConfig({ ...base, GITEA_TOKEN: 'x' })
|
||||||
|
assert.equal(short.token, 'x')
|
||||||
|
|
||||||
|
const longToken = 'a'.repeat(1000)
|
||||||
|
const long = loadConfig({ ...base, GITEA_TOKEN: longToken })
|
||||||
|
assert.equal(long.token, longToken)
|
||||||
|
})
|
||||||
|
|||||||
@@ -90,6 +90,28 @@ test('fetchAllPages 在回應非 JSON 時丟出例外', async () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('fetchAllPages 在回應為非陣列 JSON 時丟出例外', async () => {
|
||||||
|
globalThis.fetch = async () => jsonResponse({ message: 'error object' })
|
||||||
|
const client = new GiteaClient({})
|
||||||
|
await assert.rejects(
|
||||||
|
() => client.fetchAllPages('https://example.com/api'),
|
||||||
|
/non-array JSON payload/,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('fetchAllPages 在 fetch 因逾時拋出 AbortError 時向外拋出', async () => {
|
||||||
|
globalThis.fetch = async () => {
|
||||||
|
const err = new Error('The operation was aborted due to timeout')
|
||||||
|
err.name = 'AbortError'
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
const client = new GiteaClient({})
|
||||||
|
await assert.rejects(
|
||||||
|
() => client.fetchAllPages('https://example.com/api'),
|
||||||
|
(err) => err.name === 'AbortError',
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
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')
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import { test, afterEach } from 'node:test'
|
||||||
|
import assert from 'node:assert/strict'
|
||||||
|
import { failError } from '../logger.js'
|
||||||
|
|
||||||
|
const realWrite = process.stderr.write
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
process.stderr.write = realWrite
|
||||||
|
})
|
||||||
|
|
||||||
|
// 攔截寫入 stderr 的內容
|
||||||
|
function captureStderr(fn) {
|
||||||
|
const chunks = []
|
||||||
|
process.stderr.write = (chunk) => {
|
||||||
|
chunks.push(String(chunk))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
fn()
|
||||||
|
} finally {
|
||||||
|
process.stderr.write = realWrite
|
||||||
|
}
|
||||||
|
return chunks.join('')
|
||||||
|
}
|
||||||
|
|
||||||
|
test('failError 對 Error 物件輸出名稱、訊息與堆疊', () => {
|
||||||
|
const out = captureStderr(() => failError(new TypeError('boom')))
|
||||||
|
assert.match(out, /\[ERR\] TypeError: boom/)
|
||||||
|
assert.match(out, /at /) // 堆疊內容
|
||||||
|
})
|
||||||
|
|
||||||
|
test('failError 對非 Error 值輸出其字串形式', () => {
|
||||||
|
const out = captureStderr(() => failError('plain failure'))
|
||||||
|
assert.match(out, /\[ERR\] plain failure/)
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user