diff --git a/apps/desktop/src/app/capabilities/catalog/catalog-data.ts b/apps/desktop/src/app/capabilities/catalog/catalog-data.ts index 2482012e47..09c8c79a16 100644 --- a/apps/desktop/src/app/capabilities/catalog/catalog-data.ts +++ b/apps/desktop/src/app/capabilities/catalog/catalog-data.ts @@ -180,6 +180,7 @@ export async function fetchCatalog(kind: CatalogKind): Promise { // get the same deadline; an actually stalled download still terminates. const controller = new AbortController() let timer: ReturnType | undefined + const armDeadline = () => { clearTimeout(timer) timer = setTimeout(() => controller.abort(), 60_000) @@ -211,8 +212,14 @@ export async function fetchCatalog(kind: CatalogKind): Promise { while (true) { const { done, value } = await reader.read() - if (done) break - if (value.byteLength > 0) armDeadline() + if (done) { + break + } + + if (value.byteLength > 0) { + armDeadline() + } + chunks.push(decoder.decode(value, { stream: true })) } } finally { diff --git a/apps/desktop/src/app/capabilities/catalog/catalog-download.test.ts b/apps/desktop/src/app/capabilities/catalog/catalog-download.test.ts index dfab21db06..05ca9999d5 100644 --- a/apps/desktop/src/app/capabilities/catalog/catalog-download.test.ts +++ b/apps/desktop/src/app/capabilities/catalog/catalog-download.test.ts @@ -10,6 +10,7 @@ beforeEach(() => { vi.spyOn(AbortSignal, 'timeout').mockImplementation(ms => { const controller = new AbortController() setTimeout(() => controller.abort(new DOMException('Timed out', 'TimeoutError')), ms) + return controller.signal }) }) @@ -28,6 +29,7 @@ function serveStream() { 'fetch', vi.fn(async (_url, options) => { signal = options.signal + return new Response( new ReadableStream({ start(controller) { @@ -40,6 +42,7 @@ function serveStream() { ) }) ) + return { send: (text: string) => writer.enqueue(encoder.encode(text)), close: () => writer.close(), @@ -53,10 +56,12 @@ describe('catalog download liveness', () => { 'finishes a progressing %s download beyond the old total deadline', async kind => { const stream = serveStream() + const result = fetchCatalog(kind).then( value => ({ value }), error => ({ error }) ) + await vi.advanceTimersByTimeAsync(40_000) stream.send('[{"name":"slow-') await vi.advanceTimersByTimeAsync(40_000) @@ -103,8 +108,13 @@ describe('catalog download liveness', () => { vi.fn(async () => new Response(body)) ) const result = await fetchCatalog('skills').catch(error => error) - if (body === '[]') expect(result).toEqual([]) - else expect(result).toBeInstanceOf(SyntaxError) + + if (body === '[]') { + expect(result).toEqual([]) + } else { + expect(result).toBeInstanceOf(SyntaxError) + } + expect(vi.getTimerCount()).toBe(0) })