From 46923871a88b45db70f7303a42f3e737b850ef6f Mon Sep 17 00:00:00 2001 From: quality Date: Tue, 6 Oct 2026 23:04:26 -0400 Subject: [PATCH] test(bundle): drive run()'s top-level setFailed catch through dist/index.js with a body-less issue_comment payload Every event handler swallows its own rejections, so src/run.ts:40 was never reached by the bundle suite (npm run test:coverage:e2e: run.ts 88.88% lines). handleIssueComment matches the comment body against each configured command before any per-command catch, so an issue_comment payload with no comment object or a null body throws a TypeError straight up to run(). Pin that contract: exit 1, exactly one ::error:: line, no API calls. run.ts e2e lines 88.88% -> 100%. Closes #360 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: quality --- __tests__/bundle/runTopLevelCatch.test.ts | 49 +++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 __tests__/bundle/runTopLevelCatch.test.ts diff --git a/__tests__/bundle/runTopLevelCatch.test.ts b/__tests__/bundle/runTopLevelCatch.test.ts new file mode 100644 index 0000000..318c3e9 --- /dev/null +++ b/__tests__/bundle/runTopLevelCatch.test.ts @@ -0,0 +1,49 @@ +import type { FakeGithub } from './fakeGithub' +import { afterAll, afterEach, beforeAll, describe, expect, it, vi } from 'vitest' + +import issueCommentEvent from '../fixtures/issues/issueCommentEvent.json' +import { start } from './fakeGithub' +import { token } from './helpers' +import { runBundle } from './runBundle' + +vi.setConfig({ testTimeout: 30_000 }) + +// run()'s top-level catch (src/run.ts), driven through dist/index.js. Every event handler swallows +// its own rejections, so the only way to reach it is a throw that happens before a per-command +// catch: handleIssueComment matches the comment body against each configured command without +// guarding it, and a payload whose comment has no body makes that match throw a TypeError. +describe('dist/index.js fails the run once from run()\'s top-level catch', () => { + let gh: FakeGithub + + beforeAll(async () => { + gh = await start() + }) + afterEach(() => gh.reset()) + afterAll(() => gh.close()) + + function withoutBody(body: unknown) { + const payload = structuredClone(issueCommentEvent) as Record + if (body === undefined) + delete payload.comment + else + payload.comment.body = body + return payload + } + + it.each([ + ['no comment object', undefined, 'undefined'], + ['a null comment body', null, 'null'], + ])('issue_comment with %s: exits 1 with one error and no api call', async (_name, body, kind) => { + const result = await runBundle({ + eventName: 'issue_comment', + payload: withoutBody(body), + inputs: { ...token, 'prow-commands': '/assign /lgtm' }, + apiUrl: gh.url, + }) + + expect(result.status, result.stdout).toBe(1) + expect(result.errors).toEqual([`Cannot read properties of ${kind} (reading 'replace')`]) + expect(result.stdout).not.toContain('not yet supported') + expect(gh.requests).toEqual([]) + }) +})