Files
chatgpt-exporter/tests/queue.test.ts
Pionxzh 471e877919 feat: cache conversations and images to skip refetching on export
Batch exports keep raw conversations in memory for the page lifetime, so
exporting the same selection in another format reuses them while the
list's update_time still matches. Downloaded images are cached in
IndexedDB for 30 days and shared by single and batch exports.

fetchConversation now always returns the raw conversation, and
withImageAssets returns a copy with image pointers replaced, so cached
raw data is never mutated.
2026-09-24 23:33:26 +08:00

68 lines
2.2 KiB
TypeScript

import { describe, expect, it, vi } from 'vitest'
// The queue only needs the RateLimitError class; skip the real api module and its page globals.
vi.mock('../src/api', () => ({ RateLimitError: class extends Error {} }))
const { RequestQueue } = await import('../src/utils/queue')
async function failing(): Promise<string> {
throw new Error('500')
}
function run<T>(queue: InstanceType<typeof RequestQueue<T>>) {
return new Promise<T[]>((resolve) => {
queue.on('done', resolve)
queue.start()
})
}
describe('requestQueue skipped requests', () => {
it('reports requests dropped after exhausting retries', async () => {
const queue = new RequestQueue<string>(0, 0)
queue.add({ name: 'ok-1', request: async () => 'a' })
queue.add({ name: 'broken', request: failing })
queue.add({ name: 'ok-2', request: async () => 'b' })
const results = await run(queue)
expect(results).toEqual(['a', 'b'])
expect(queue.getSkipped()).toEqual(['broken'])
})
it('does not skip a request that succeeds on retry', async () => {
const queue = new RequestQueue<string>(0, 0)
let attempts = 0
queue.add({
name: 'flaky',
request: async () => {
if (++attempts < 3) throw new Error('500')
return 'ok'
},
})
expect(await run(queue)).toEqual(['ok'])
expect(queue.getSkipped()).toEqual([])
})
it('clear() resets the skipped list for the next batch', async () => {
const queue = new RequestQueue<string>(0, 0)
queue.add({ name: 'broken', request: failing })
await run(queue)
expect(queue.getSkipped()).toEqual(['broken'])
queue.clear()
expect(queue.getSkipped()).toEqual([])
})
})
describe('requestQueue cached requests', () => {
it('does not pause after a cached request', async () => {
// A 60s backoff would time the test out if cached requests still waited
const queue = new RequestQueue<string>(60_000, 60_000)
queue.add({ name: 'a', cached: true, request: async () => 'a' })
queue.add({ name: 'b', cached: true, request: async () => 'b' })
expect(await run(queue)).toEqual(['a', 'b'])
})
})