diff --git a/__tests__/bundle/meow.test.ts b/__tests__/bundle/meow.test.ts index b70e0280..01b2ae4a 100644 --- a/__tests__/bundle/meow.test.ts +++ b/__tests__/bundle/meow.test.ts @@ -23,6 +23,8 @@ interface CatResponse { status: number body?: unknown headers?: Record + // drop the connection without answering: the child's fetch rejects with a TypeError + destroy?: boolean } // a stand-in for api.thecatapi.com: answers one queued response per request, repeating the last @@ -32,6 +34,10 @@ async function startCatApi() { const server = http.createServer((req, res) => { requests.push({ path: req.url ?? '', headers: req.headers }) const next = queue.length > 1 ? queue.shift()! : queue[0] ?? { status: 500 } + if (next.destroy) { + req.socket.destroy() + return + } res.writeHead(next.status, { 'content-type': 'application/json', ...next.headers }) res.end(next.body === undefined ? '' : JSON.stringify(next.body)) }) @@ -174,6 +180,38 @@ describe('dist/index.js issue_comment /meow', () => { expect(postedComments()).toEqual([fallback]) }) + // the other parseCatImage refusals: each is a 2xx the bundle must not render, so none is retried + it.each([ + ['an empty list', [], 'cat api returned no images'], + ['a record without a url', [{ id: 'MTY3ODIyMQ' }], 'cat api returned an invalid image record'], + ['an excessively long url', [{ url: `https://cdn2.thecatapi.com/images/${'a'.repeat(4096)}.jpg` }], 'cat api returned an excessively long image url'], + ['an unparsable url', [{ url: 'not a url' }], 'cat api returned an invalid image url'], + ['an http: url', [{ url: 'http://cdn2.thecatapi.com/images/MTY3ODIyMQ.jpg' }], 'cat api returned an unusable image url'], + ])('falls back to the note without retrying when the cat api returns %s', async (_, body, message) => { + cat.answer({ status: 200, body }) + gh.route('POST', `${repo}/issues/1/comments`, { status: 201, body: {} }) + + const result = await meow('/meow') + + expect(result.status, result.stdout).toBe(0) + expect(result.errors).toEqual([]) + expect(cat.requests).toHaveLength(1) + expect(result.stdout).toContain(`::warning::Could not fetch a cat image: Error: ${message}`) + expect(postedComments()).toEqual([fallback]) + }) + + it('retries a dropped connection to the cat api and posts the image once it answers', async () => { + cat.answer({ status: 0, destroy: true }, { status: 200, body: [{ url: image }] }) + gh.route('POST', `${repo}/issues/1/comments`, { status: 201, body: {} }) + + const result = await meow('/meow') + + expect(result.status, result.stdout).toBe(0) + expect(result.stdout).not.toMatch(/::warning::/) + expect(cat.requests).toHaveLength(2) + expect(postedComments()).toEqual([`![cat](<${image}>)`]) + }) + it('ignores /meow inside a fenced code block without calling either api', async () => { const result = await meow('```\n/meow\n```')