From b003b2c0b3ce97dc9d626a4f05ad2e0ff7cee60f Mon Sep 17 00:00:00 2001 From: quality Date: Thu, 8 Oct 2026 04:16:28 -0400 Subject: [PATCH 1/3] test(bundle): drive the complete-tree OWNERS loader's failure arms through dist/index.js Three /approve runs on a pull request reach loadOwnersTree's failure arms in src/utils/owners.ts: the recursive tree listing of the base tip answering 500 (L197), an OWNERS blob answering 500 (L229), and a blob answering 200 without content to decode (L164). Each fails the run naming the base tip before anything is written. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: quality --- __tests__/bundle/ownersTreeLoadArms.test.ts | 77 +++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 __tests__/bundle/ownersTreeLoadArms.test.ts diff --git a/__tests__/bundle/ownersTreeLoadArms.test.ts b/__tests__/bundle/ownersTreeLoadArms.test.ts new file mode 100644 index 00000000..0094ba6a --- /dev/null +++ b/__tests__/bundle/ownersTreeLoadArms.test.ts @@ -0,0 +1,77 @@ +import type { FakeGithub } from './fakeGithub' +import { afterAll, afterEach, beforeAll, describe, expect, it, vi } from 'vitest' + +import { blobSha, prCommentEvent } from '../utils/ownersFixtures' +import { start } from './fakeGithub' +import { helpersFor, ownersProbe, ownersReads, queueRead, repo, token } from './helpers' +import { runBundle } from './runBundle' + +vi.setConfig({ testTimeout: 30_000 }) + +// the failure arms of the complete-tree OWNERS loader in src/utils/owners.ts (loadOwnersTree), driven through +// dist/index.js by `/approve` on a pull request: the recursive tree listing of the base tip cannot be read, an +// OWNERS blob cannot be read, or a blob answers without content to decode. Each fails the run naming the base tip +// before anything is written. The truncated-tree probes live in ownersProbeArms.test.ts. +describe('dist/index.js OWNERS tree loading failures', () => { + let gh: FakeGithub + const { expectCommandThenConfig, routeOwners } = helpersFor(() => gh) + + const rootOwners = 'approvers:\n- bob\n' + const rootBlobRead = `GET ${repo}/git/blobs/${blobSha('OWNERS')}` + + beforeAll(async () => { + gh = await start() + }) + afterEach(() => gh.reset()) + afterAll(() => gh.close()) + + function runApprove() { + return runBundle({ + eventName: 'issue_comment', + payload: prCommentEvent('/approve', 'bob'), + inputs: { ...token, 'prow-commands': '/approve' }, + apiUrl: gh.url, + }) + } + + // the failed command, then the post-command sweep's config reads, then tide's own reads of the pull request + // and the base branch before its merge-queue dequeue; nothing is written + function expectFailedLoad(result: Awaited>, command: string[]) { + expect(result.status, result.stdout).toBe(1) + expect(result.errors.some(e => e.includes('error loading OWNERS files at basesha'))).toBe(true) + expect(gh.requestsMatching('POST', /\/issues\//)).toEqual([]) + expect(gh.requestsMatching('PUT', /./)).toEqual([]) + expect(gh.requestsMatching('GET', /\/contents\/OWNERS/)).toEqual([]) + expectCommandThenConfig(command, [`GET ${repo}/pulls/1`, ownersProbe, queueRead]) + } + + it('a tree listing that fails other than 404: /approve fails naming the base tip; no blob is read', async () => { + // the first matching route wins, so the failing listing goes in before routeOwners' complete one + gh.route('GET', `${repo}/git/trees/basesha`, { status: 500, body: { message: 'boom' } }) + routeOwners({ OWNERS: rootOwners }, ['sdk/x.go']) + + const result = await runApprove() + + expectFailedLoad(result, ownersReads) + expect(gh.requestsMatching('GET', /\/git\/blobs\//)).toEqual([]) + }) + + it('an OWNERS blob that fails other than 404: /approve fails naming the base tip', async () => { + gh.route('GET', `${repo}/git/blobs/${blobSha('OWNERS')}`, { status: 500, body: { message: 'boom' } }) + routeOwners({ OWNERS: rootOwners }, ['sdk/x.go']) + + const result = await runApprove() + + expectFailedLoad(result, [...ownersReads, rootBlobRead]) + }) + + it('an OWNERS blob answering without content: /approve fails naming the file and the base tip', async () => { + gh.route('GET', `${repo}/git/blobs/${blobSha('OWNERS')}`, { status: 200, body: { sha: blobSha('OWNERS'), size: 0 } }) + routeOwners({ OWNERS: rootOwners }, ['sdk/x.go']) + + const result = await runApprove() + + expectFailedLoad(result, [...ownersReads, rootBlobRead]) + expect(result.errors.some(e => e.includes('invalid OWNERS file returned from GitHub API for OWNERS'))).toBe(true) + }) +}) From 08113047c41765ca37b80a145e39a4b9993bfb66 Mon Sep 17 00:00:00 2001 From: quality Date: Thu, 8 Oct 2026 22:58:49 -0400 Subject: [PATCH 2/3] test(bundle): drop the non-404 blob failure case the no-content case already reaches Signed-off-by: quality --- __tests__/bundle/ownersTreeLoadArms.test.ts | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/__tests__/bundle/ownersTreeLoadArms.test.ts b/__tests__/bundle/ownersTreeLoadArms.test.ts index 0094ba6a..50232e8b 100644 --- a/__tests__/bundle/ownersTreeLoadArms.test.ts +++ b/__tests__/bundle/ownersTreeLoadArms.test.ts @@ -9,9 +9,9 @@ import { runBundle } from './runBundle' vi.setConfig({ testTimeout: 30_000 }) // the failure arms of the complete-tree OWNERS loader in src/utils/owners.ts (loadOwnersTree), driven through -// dist/index.js by `/approve` on a pull request: the recursive tree listing of the base tip cannot be read, an -// OWNERS blob cannot be read, or a blob answers without content to decode. Each fails the run naming the base tip -// before anything is written. The truncated-tree probes live in ownersProbeArms.test.ts. +// dist/index.js by `/approve` on a pull request: the recursive tree listing of the base tip cannot be read, or an +// OWNERS blob answers without content to decode. Each fails the run naming the base tip before anything is written. +// The truncated-tree probes live in ownersProbeArms.test.ts (#320). describe('dist/index.js OWNERS tree loading failures', () => { let gh: FakeGithub const { expectCommandThenConfig, routeOwners } = helpersFor(() => gh) @@ -56,15 +56,6 @@ describe('dist/index.js OWNERS tree loading failures', () => { expect(gh.requestsMatching('GET', /\/git\/blobs\//)).toEqual([]) }) - it('an OWNERS blob that fails other than 404: /approve fails naming the base tip', async () => { - gh.route('GET', `${repo}/git/blobs/${blobSha('OWNERS')}`, { status: 500, body: { message: 'boom' } }) - routeOwners({ OWNERS: rootOwners }, ['sdk/x.go']) - - const result = await runApprove() - - expectFailedLoad(result, [...ownersReads, rootBlobRead]) - }) - it('an OWNERS blob answering without content: /approve fails naming the file and the base tip', async () => { gh.route('GET', `${repo}/git/blobs/${blobSha('OWNERS')}`, { status: 200, body: { sha: blobSha('OWNERS'), size: 0 } }) routeOwners({ OWNERS: rootOwners }, ['sdk/x.go']) From 0bb1cfb54963d0e57b558b625b74ecd3ffa56b38 Mon Sep 17 00:00:00 2001 From: Bob Killen Date: Fri, 9 Oct 2026 10:56:44 -0500 Subject: [PATCH 3/3] test(bundle): address review on owners tree load arms Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Bob Killen --- __tests__/bundle/ownersTreeLoadArms.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__tests__/bundle/ownersTreeLoadArms.test.ts b/__tests__/bundle/ownersTreeLoadArms.test.ts index 50232e8b..41b06c4f 100644 --- a/__tests__/bundle/ownersTreeLoadArms.test.ts +++ b/__tests__/bundle/ownersTreeLoadArms.test.ts @@ -11,7 +11,7 @@ vi.setConfig({ testTimeout: 30_000 }) // the failure arms of the complete-tree OWNERS loader in src/utils/owners.ts (loadOwnersTree), driven through // dist/index.js by `/approve` on a pull request: the recursive tree listing of the base tip cannot be read, or an // OWNERS blob answers without content to decode. Each fails the run naming the base tip before anything is written. -// The truncated-tree probes live in ownersProbeArms.test.ts (#320). +// The truncated-tree probes are covered by the owners probe arms test added in #320 (not yet merged). describe('dist/index.js OWNERS tree loading failures', () => { let gh: FakeGithub const { expectCommandThenConfig, routeOwners } = helpersFor(() => gh)