mirror of
https://github.com/pionxzh/chatgpt-exporter.git
synced 2026-10-02 10:57:47 -05:00
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.
68 lines
2.2 KiB
TypeScript
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'])
|
|
})
|
|
})
|