From 6faa951779216a4e0d456657708627dbf95ecf85 Mon Sep 17 00:00:00 2001 From: quality <280983584+hivecommons-hive[bot]@users.noreply.github.com> Date: Thu, 22 Oct 2026 12:26:00 -0400 Subject: [PATCH 1/2] test(bundle): record the fake GitHub API's unmatched requests and assert on them outside the application The bundle fake answered every request no route matched with a 404 and left each scenario to notice: a forgotten route for tide's merge queue read was swallowed by queueState into a warning and a direct merge, so the scenario passed on traffic it never modelled. fakeGithub.ts now keeps the unmatched requests apart (unmatched), takes explicit allowances for the probes a scenario relies on missing (allowUnmatched), and fails a scenario on the rest (unexpectedRequests, assertNoUnexpectedRequests). The configuration loader's reads are always allowed: every run without a routed prow configuration makes them. fakeGithubUnexpected.test.ts pins that contract with plain fetches. ownersParseArms.test.ts and ownersProbeArms.test.ts adopt it: the merge queue read is answered "no queue" through mergeQueueFallback as the lgtm arms already do, the OWNERS probes each scenario expects to miss are allowed in that scenario, and anything else unrouted fails it. Signed-off-by: quality <280983584+hivecommons-hive[bot]@users.noreply.github.com> --- .vitest/json/output.json | 1 + __tests__/bundle/fakeGithub.ts | 44 ++++++++ __tests__/bundle/fakeGithubUnexpected.test.ts | 106 ++++++++++++++++++ __tests__/bundle/ownersParseArms.test.ts | 18 ++- __tests__/bundle/ownersProbeArms.test.ts | 19 +++- 5 files changed, 184 insertions(+), 4 deletions(-) create mode 100644 .vitest/json/output.json create mode 100644 __tests__/bundle/fakeGithubUnexpected.test.ts diff --git a/.vitest/json/output.json b/.vitest/json/output.json new file mode 100644 index 00000000..a532e3f0 --- /dev/null +++ b/.vitest/json/output.json @@ -0,0 +1 @@ +{"numTotalTestSuites":4,"numPassedTestSuites":0,"numFailedTestSuites":4,"numPendingTestSuites":0,"numTotalTests":7,"numPassedTests":4,"numFailedTests":3,"numPendingTests":0,"numTodoTests":0,"snapshot":{"added":0,"failure":false,"filesAdded":0,"filesRemoved":0,"filesRemovedList":[],"filesUnmatched":0,"filesUpdated":0,"matched":0,"total":0,"unchecked":0,"uncheckedKeysByFile":[],"unmatched":0,"updated":0,"didUpdate":false},"startTime":1792686288234,"success":false,"testResults":[{"assertionResults":[{"ancestorTitles":["dist/index.js OWNERS probes","the base tip's tree is truncated: every candidate OWNERS path is read directly"],"fullName":"dist/index.js OWNERS probes the base tip's tree is truncated: every candidate OWNERS path is read directly root OWNERS present, sdk/OWNERS absent: bob approves from the root file; no blob is read","status":"failed","title":"root OWNERS present, sdk/OWNERS absent: bob approves from the root file; no blob is read","duration":270.5904479999999,"failureMessages":["Error: the fake GitHub API answered 1 request(s) no route or allowance covers:\n GET /repos/Codertocat/Hello-World/contents/sdk%2FOWNERS?ref=basesha\n at Object.assertNoUnexpectedRequests (/data/agents/quality/pga/__tests__/bundle/fakeGithub.ts:157:15)\n at /data/agents/quality/pga/__tests__/bundle/ownersProbeArms.test.ts:45:10\n at file:///data/agents/quality/pga/node_modules/vitest/dist/chunks/run.Cm2sd7-x.js:1632:35\n at wrapper (file:///data/agents/quality/pga/node_modules/vitest/dist/chunks/run.Cm2sd7-x.js:2015:37)\n at file:///data/agents/quality/pga/node_modules/vitest/dist/chunks/run.Cm2sd7-x.js:3314:20\n at new Promise ()\n at runWithTimeout (file:///data/agents/quality/pga/node_modules/vitest/dist/chunks/run.Cm2sd7-x.js:3272:10)\n at file:///data/agents/quality/pga/node_modules/vitest/dist/chunks/run.Cm2sd7-x.js:3599:52\n at run (file:///data/agents/quality/pga/node_modules/vitest/dist/chunks/run.Cm2sd7-x.js:1352:20)\n at limiterFn (file:///data/agents/quality/pga/node_modules/vitest/dist/chunks/run.Cm2sd7-x.js:1362:59)"],"meta":{},"tags":[],"benchmarks":[]},{"ancestorTitles":["dist/index.js OWNERS probes","the base tip's tree is truncated: every candidate OWNERS path is read directly"],"fullName":"dist/index.js OWNERS probes the base tip's tree is truncated: every candidate OWNERS path is read directly a probe that fails other than 404 fails /approve naming the base tip; nothing is written","status":"failed","title":"a probe that fails other than 404 fails /approve naming the base tip; nothing is written","duration":223.05113700000004,"failureMessages":["Error: the fake GitHub API answered 1 request(s) no route or allowance covers:\n GET /repos/Codertocat/Hello-World/git/trees/master?recursive=true\n at Object.assertNoUnexpectedRequests (/data/agents/quality/pga/__tests__/bundle/fakeGithub.ts:157:15)\n at /data/agents/quality/pga/__tests__/bundle/ownersProbeArms.test.ts:45:10\n at file:///data/agents/quality/pga/node_modules/vitest/dist/chunks/run.Cm2sd7-x.js:1632:35\n at wrapper (file:///data/agents/quality/pga/node_modules/vitest/dist/chunks/run.Cm2sd7-x.js:2015:37)\n at file:///data/agents/quality/pga/node_modules/vitest/dist/chunks/run.Cm2sd7-x.js:3314:20\n at new Promise ()\n at runWithTimeout (file:///data/agents/quality/pga/node_modules/vitest/dist/chunks/run.Cm2sd7-x.js:3272:10)\n at file:///data/agents/quality/pga/node_modules/vitest/dist/chunks/run.Cm2sd7-x.js:3599:52\n at run (file:///data/agents/quality/pga/node_modules/vitest/dist/chunks/run.Cm2sd7-x.js:1352:20)\n at limiterFn (file:///data/agents/quality/pga/node_modules/vitest/dist/chunks/run.Cm2sd7-x.js:1362:59)"],"meta":{},"tags":[],"benchmarks":[]},{"ancestorTitles":["dist/index.js OWNERS probes","the tide gate's probe of the base branch"],"fullName":"dist/index.js OWNERS probes the tide gate's probe of the base branch a complete tree without an OWNERS blob: the gate wants lgtm alone and the pull request merges","status":"passed","title":"a complete tree without an OWNERS blob: the gate wants lgtm alone and the pull request merges","duration":295.297012,"failureMessages":[],"meta":{},"tags":[],"benchmarks":[]},{"ancestorTitles":["dist/index.js OWNERS probes","the tide gate's probe of the base branch"],"fullName":"dist/index.js OWNERS probes the tide gate's probe of the base branch a truncated tree without an OWNERS blob and a root OWNERS file: the gate wants approved too","status":"passed","title":"a truncated tree without an OWNERS blob and a root OWNERS file: the gate wants approved too","duration":370.8064689999999,"failureMessages":[],"meta":{},"tags":[],"benchmarks":[]},{"ancestorTitles":["dist/index.js OWNERS probes","the tide gate's probe of the base branch"],"fullName":"dist/index.js OWNERS probes the tide gate's probe of the base branch a truncated tree without an OWNERS blob and no root OWNERS file: the gate wants lgtm alone","status":"failed","title":"a truncated tree without an OWNERS blob and no root OWNERS file: the gate wants lgtm alone","duration":286.96094500000004,"failureMessages":["Error: the fake GitHub API answered 1 request(s) no route or allowance covers:\n GET /repos/Codertocat/Hello-World/contents/OWNERS?ref=master\n at Object.assertNoUnexpectedRequests (/data/agents/quality/pga/__tests__/bundle/fakeGithub.ts:157:15)\n at /data/agents/quality/pga/__tests__/bundle/ownersProbeArms.test.ts:45:10\n at file:///data/agents/quality/pga/node_modules/vitest/dist/chunks/run.Cm2sd7-x.js:1632:35\n at wrapper (file:///data/agents/quality/pga/node_modules/vitest/dist/chunks/run.Cm2sd7-x.js:2015:37)\n at file:///data/agents/quality/pga/node_modules/vitest/dist/chunks/run.Cm2sd7-x.js:3314:20\n at new Promise ()\n at runWithTimeout (file:///data/agents/quality/pga/node_modules/vitest/dist/chunks/run.Cm2sd7-x.js:3272:10)\n at file:///data/agents/quality/pga/node_modules/vitest/dist/chunks/run.Cm2sd7-x.js:3599:52\n at run (file:///data/agents/quality/pga/node_modules/vitest/dist/chunks/run.Cm2sd7-x.js:1352:20)\n at limiterFn (file:///data/agents/quality/pga/node_modules/vitest/dist/chunks/run.Cm2sd7-x.js:1362:59)"],"meta":{},"tags":[],"benchmarks":[]},{"ancestorTitles":["dist/index.js OWNERS probes","the tide gate's probe of the base branch"],"fullName":"dist/index.js OWNERS probes the tide gate's probe of the base branch a truncated tree whose root OWNERS probe fails other than 404: the merge fails naming the branch","status":"passed","title":"a truncated tree whose root OWNERS probe fails other than 404: the merge fails naming the branch","duration":317.183863,"failureMessages":[],"meta":{},"tags":[],"benchmarks":[]},{"ancestorTitles":["dist/index.js OWNERS probes","the tide gate's probe of the base branch"],"fullName":"dist/index.js OWNERS probes the tide gate's probe of the base branch a tree listing that fails other than 404: the merge fails naming the branch; no root probe","status":"passed","title":"a tree listing that fails other than 404: the merge fails naming the branch; no root probe","duration":310.3439239999998,"failureMessages":[],"meta":{},"tags":[],"benchmarks":[]}],"startTime":1792686288789,"endTime":1792686290864.344,"status":"failed","message":"","name":"/data/agents/quality/pga/__tests__/bundle/ownersProbeArms.test.ts"}]} \ No newline at end of file diff --git a/__tests__/bundle/fakeGithub.ts b/__tests__/bundle/fakeGithub.ts index 7d766a75..c0915fdc 100644 --- a/__tests__/bundle/fakeGithub.ts +++ b/__tests__/bundle/fakeGithub.ts @@ -38,6 +38,23 @@ export interface MergeQueueFixture { export interface FakeGithub { url: string requests: RecordedRequest[] + /** the requests no route or fallback answered: the fake replied 404 to each of them */ + unmatched: RecordedRequest[] + /** + * declares that the scenario relies on the 404 the fake gives `method` requests to `pattern` + * (a membership check meaning "not a member", an OWNERS read meaning "no such file"), so + * `unexpectedRequests` and `assertNoUnexpectedRequests` leave them out + */ + allowUnmatched: (method: string, pattern: string | RegExp) => void + /** the unmatched requests no allowance covers; the configuration loader's probes are always allowed */ + unexpectedRequests: () => RecordedRequest[] + /** + * throws naming every unexpected request, so a scenario fails on traffic the application + * answered itself (a swallowed GraphQL error, a tolerated read failure) instead of passing on it. + * Call it from `afterEach`, with `reset` in a `finally`, so a failing scenario does not leak its + * routes into the next one. + */ + assertNoUnexpectedRequests: () => void route: (method: string, pattern: string | RegExp, response: Handler | FakeResponse) => void /** answers with one response per call in order, repeating the last one */ routeSequence: (method: string, pattern: string | RegExp, responses: FakeResponse[]) => void @@ -54,6 +71,13 @@ export interface FakeGithub { close: () => Promise } +/** + * the configuration loader's reads, `GET /repos///contents/`: every run that routes no prow configuration + * makes them and takes the defaults from their 404s, so they are never unexpected + */ +const configurationProbe = /^\/repos\/[^/]+\/[^/]+\/contents\/(?:\.github%2F)?\.?prow(?:labels)?\.ya?ml$/ + function matches(route: Route, req: RecordedRequest): boolean { if (route.method !== req.method) return false @@ -84,6 +108,8 @@ export async function start(): Promise { const routes: Route[] = [] const fallbacks: Route[] = [] const requests: RecordedRequest[] = [] + const unmatched: RecordedRequest[] = [] + const allowances: Route[] = [{ method: 'GET', pattern: configurationProbe, handler: () => ({ status: 404 }) }] const server = http.createServer(async (req, res) => { const recorded: RecordedRequest = { @@ -95,6 +121,8 @@ export async function start(): Promise { requests.push(recorded) const route = routes.find(r => matches(r, recorded)) ?? fallbacks.find(r => matches(r, recorded)) + if (route === undefined) + unmatched.push(recorded) const response: FakeResponse = route ? route.handler(recorded) : { status: 404, body: { message: 'Not Found (fake)' } } @@ -115,6 +143,20 @@ export async function start(): Promise { return { url: `http://127.0.0.1:${port}`, requests, + unmatched, + allowUnmatched(method, pattern) { + allowances.push({ method: method.toUpperCase(), pattern, handler: () => ({ status: 404 }) }) + }, + unexpectedRequests() { + return unmatched.filter(r => !allowances.some(a => matches(a, r))) + }, + assertNoUnexpectedRequests() { + const unexpected = this.unexpectedRequests() + if (unexpected.length > 0) { + const listing = unexpected.map(r => ` ${r.method} ${r.path}`).join('\n') + throw new Error(`the fake GitHub API answered ${unexpected.length} request(s) no route or allowance covers:\n${listing}`) + } + }, route(method, pattern, response) { const handler = typeof response === 'function' ? response : () => response routes.push({ method: method.toUpperCase(), pattern, handler }) @@ -143,6 +185,8 @@ export async function start(): Promise { routes.length = 0 fallbacks.length = 0 requests.length = 0 + unmatched.length = 0 + allowances.length = 1 }, close() { return new Promise((resolve, reject) => { diff --git a/__tests__/bundle/fakeGithubUnexpected.test.ts b/__tests__/bundle/fakeGithubUnexpected.test.ts new file mode 100644 index 00000000..536244e5 --- /dev/null +++ b/__tests__/bundle/fakeGithubUnexpected.test.ts @@ -0,0 +1,106 @@ +import type { FakeGithub } from './fakeGithub' +import { afterAll, afterEach, beforeAll, describe, expect, it } from 'vitest' + +import { start } from './fakeGithub' +import { repo } from './helpers' + +// The fake's own contract for traffic no route answers (fakeGithub.ts: `unmatched`, `allowUnmatched`, +// `unexpectedRequests`, `assertNoUnexpectedRequests`), driven with plain fetches rather than dist/index.js: +// a scenario that forgets a route must be told so by the harness, outside whatever fallback the +// application applies to the 404, and only the probes it declares intentional are left out. +describe('fakeGithub unexpected traffic', () => { + let gh: FakeGithub + + beforeAll(async () => { + gh = await start() + }) + afterEach(() => gh.reset()) + afterAll(() => gh.close()) + + function call(method: string, path: string): Promise { + return fetch(`${gh.url}${path}`, { method }) + } + + it('answers an unrouted request 404 and records it as unmatched and unexpected', async () => { + const response = await call('GET', `${repo}/pulls/1`) + + expect(response.status).toBe(404) + expect(gh.unmatched).toEqual([{ method: 'GET', path: `${repo}/pulls/1`, body: undefined }]) + expect(gh.unexpectedRequests()).toEqual(gh.unmatched) + expect(() => gh.assertNoUnexpectedRequests()).toThrow( + `the fake GitHub API answered 1 request(s) no route or allowance covers:\n GET ${repo}/pulls/1`, + ) + }) + + it('a routed request, and one a fallback answers, are neither unmatched nor unexpected', async () => { + gh.route('GET', `${repo}/pulls/1`, { status: 200, body: {} }) + gh.mergeQueueFallback({ pullRequestId: 'PR_none', headOid: 'head', enabled: false }) + + expect((await call('GET', `${repo}/pulls/1`)).status).toBe(200) + expect((await call('POST', '/graphql')).status).toBe(200) + + expect(gh.requests).toHaveLength(2) + expect(gh.unmatched).toEqual([]) + expect(() => gh.assertNoUnexpectedRequests()).not.toThrow() + }) + + it('the configuration loader\'s probes are unmatched but never unexpected', async () => { + const probes = [ + '/repos/Codertocat/.project/contents/prow.yaml', + '/repos/Codertocat/.github/contents/prow.yaml', + `${repo}/contents/.github%2Fprow.yaml`, + `${repo}/contents/.github%2Fprowlabels.yaml`, + `${repo}/contents/prow.yaml`, + `${repo}/contents/.prowlabels.yaml`, + `${repo}/contents/.github%2Fprow.yml`, + `${repo}/contents/.github%2Fprowlabels.yml`, + `${repo}/contents/prow.yml`, + `${repo}/contents/.prowlabels.yml`, + ] + for (const path of probes) + expect((await call('GET', path)).status).toBe(404) + + expect(gh.unmatched).toHaveLength(probes.length) + expect(gh.unexpectedRequests()).toEqual([]) + expect(() => gh.assertNoUnexpectedRequests()).not.toThrow() + }) + + it('an explicitly sourced configuration read is not a loader probe', async () => { + await call('GET', '/repos/cncf/prow-config/contents/configs%2Fprow.yaml?ref=v2') + + expect(gh.unexpectedRequests().map(r => r.path)).toEqual(['/repos/cncf/prow-config/contents/configs%2Fprow.yaml?ref=v2']) + }) + + it('allowUnmatched leaves the declared requests out, by exact path or pattern, and only for the declared method', async () => { + gh.allowUnmatched('GET', `${repo}/collaborators/stranger`) + gh.allowUnmatched('get', /\/orgs\/Codertocat\/members\//) + + await call('GET', `${repo}/collaborators/stranger`) + await call('GET', `${repo}/collaborators/stranger?per_page=1`) + await call('GET', '/orgs/Codertocat/members/stranger') + await call('POST', '/orgs/Codertocat/members/stranger') + await call('POST', '/graphql') + + expect(gh.unmatched).toHaveLength(5) + expect(gh.unexpectedRequests().map(r => `${r.method} ${r.path}`)).toEqual([ + 'POST /orgs/Codertocat/members/stranger', + 'POST /graphql', + ]) + expect(() => gh.assertNoUnexpectedRequests()).toThrow( + 'answered 2 request(s) no route or allowance covers:\n POST /orgs/Codertocat/members/stranger\n POST /graphql', + ) + }) + + it('reset clears the unmatched record and the scenario\'s allowances, keeping the loader probes allowed', async () => { + gh.allowUnmatched('POST', '/graphql') + await call('POST', '/graphql') + expect(gh.unexpectedRequests()).toEqual([]) + + gh.reset() + + expect(gh.unmatched).toEqual([]) + await call('POST', '/graphql') + await call('GET', `${repo}/contents/prow.yaml`) + expect(gh.unexpectedRequests().map(r => r.path)).toEqual(['/graphql']) + }) +}) diff --git a/__tests__/bundle/ownersParseArms.test.ts b/__tests__/bundle/ownersParseArms.test.ts index 8c30393f..9f8853af 100644 --- a/__tests__/bundle/ownersParseArms.test.ts +++ b/__tests__/bundle/ownersParseArms.test.ts @@ -1,5 +1,5 @@ import type { FakeGithub } from './fakeGithub' -import { afterAll, afterEach, beforeAll, describe, expect, it, vi } from 'vitest' +import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest' import { prCommentEvent } from '../utils/ownersData' import { start } from './fakeGithub' @@ -19,7 +19,21 @@ describe('dist/index.js OWNERS content shapes', () => { beforeAll(async () => { gh = await start() }) - afterEach(() => gh.reset()) + // the tide gate's merge queue read after `/approve` is answered "no queue" rather than left to the + // fake's 404, which queueState would swallow into a warning; the gate's OWNERS probe of master is + // expected to miss (nothing routes that tree); any other unrouted request fails the scenario + beforeEach(() => { + gh.mergeQueueFallback({ pullRequestId: 'PR_none', headOid: 'headsha', enabled: false }) + gh.allowUnmatched('GET', `${repo}/git/trees/master`) + }) + afterEach(() => { + try { + gh.assertNoUnexpectedRequests() + } + finally { + gh.reset() + } + }) afterAll(() => gh.close()) function routeApprove(owners: string) { diff --git a/__tests__/bundle/ownersProbeArms.test.ts b/__tests__/bundle/ownersProbeArms.test.ts index d1a3f3c2..9ebf40c4 100644 --- a/__tests__/bundle/ownersProbeArms.test.ts +++ b/__tests__/bundle/ownersProbeArms.test.ts @@ -1,6 +1,6 @@ import type { FakeGithub } from './fakeGithub' import { Buffer } from 'node:buffer' -import { afterAll, afterEach, beforeAll, describe, expect, it, vi } from 'vitest' +import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest' import { blobSha, prCommentEvent, pullBody } from '../utils/ownersData' import { start } from './fakeGithub' @@ -36,7 +36,18 @@ describe('dist/index.js OWNERS probes', () => { beforeAll(async () => { gh = await start() }) - afterEach(() => gh.reset()) + // `queueRead` is answered "no queue" rather than left to the fake's 404, which queueState would swallow + // into a warning; the OWNERS probes a scenario expects to miss are allowed in that scenario, and any + // other unrouted request fails it + beforeEach(() => gh.mergeQueueFallback({ pullRequestId: 'PR_none', headOid: 'headsha', enabled: false })) + afterEach(() => { + try { + gh.assertNoUnexpectedRequests() + } + finally { + gh.reset() + } + }) afterAll(() => gh.close()) function contents(text: string) { @@ -74,6 +85,7 @@ describe('dist/index.js OWNERS probes', () => { it('root OWNERS present, sdk/OWNERS absent: bob approves from the root file; no blob is read', async () => { routeTruncatedBase() gh.route('GET', `${repo}/contents/OWNERS`, contents(rootOwners)) + gh.allowUnmatched('GET', `${repo}/contents/sdk%2FOWNERS`) routeApprove(['lgtm']) gh.route('GET', `${repo}/git/trees/master`, { status: 200, body: { sha: 'master', truncated: false, tree: [{ path: 'OWNERS', type: 'blob', sha: 'o' }] } }) @@ -99,6 +111,8 @@ describe('dist/index.js OWNERS probes', () => { gh.route('GET', `${repo}/contents/OWNERS`, contents(rootOwners)) gh.route('GET', `${repo}/contents/sdk%2FOWNERS`, { status: 500, body: { message: 'boom' } }) routeApprove() + // the tide gate still probes master's tree after the failed evaluation, and nothing routes it + gh.allowUnmatched('GET', `${repo}/git/trees/master`) const result = await runApprove() @@ -153,6 +167,7 @@ describe('dist/index.js OWNERS probes', () => { it('a truncated tree without an OWNERS blob and no root OWNERS file: the gate wants lgtm alone', async () => { routeApprove(['lgtm']) + gh.allowUnmatched('GET', `${repo}/contents/OWNERS`) gh.route('GET', `${repo}/git/trees/master`, { status: 200, body: { sha: 'master', truncated: true, tree: [{ path: 'README.md', type: 'blob', sha: 'r' }] } }) const result = await runApprove() From d307c92b3141a02fd83f23df7f5dbe307f865ab3 Mon Sep 17 00:00:00 2001 From: quality <280983584+hivecommons-hive[bot]@users.noreply.github.com> Date: Fri, 23 Oct 2026 13:00:16 -0400 Subject: [PATCH 2/2] test(bundle): drop the stray .vitest/json/output.json reporter artifact The JSON reporter output was committed by mistake alongside the fake GitHub API change and fails the repository's eslint jsonc rules. Signed-off-by: quality <280983584+hivecommons-hive[bot]@users.noreply.github.com> --- .vitest/json/output.json | 1 - 1 file changed, 1 deletion(-) delete mode 100644 .vitest/json/output.json diff --git a/.vitest/json/output.json b/.vitest/json/output.json deleted file mode 100644 index a532e3f0..00000000 --- a/.vitest/json/output.json +++ /dev/null @@ -1 +0,0 @@ -{"numTotalTestSuites":4,"numPassedTestSuites":0,"numFailedTestSuites":4,"numPendingTestSuites":0,"numTotalTests":7,"numPassedTests":4,"numFailedTests":3,"numPendingTests":0,"numTodoTests":0,"snapshot":{"added":0,"failure":false,"filesAdded":0,"filesRemoved":0,"filesRemovedList":[],"filesUnmatched":0,"filesUpdated":0,"matched":0,"total":0,"unchecked":0,"uncheckedKeysByFile":[],"unmatched":0,"updated":0,"didUpdate":false},"startTime":1792686288234,"success":false,"testResults":[{"assertionResults":[{"ancestorTitles":["dist/index.js OWNERS probes","the base tip's tree is truncated: every candidate OWNERS path is read directly"],"fullName":"dist/index.js OWNERS probes the base tip's tree is truncated: every candidate OWNERS path is read directly root OWNERS present, sdk/OWNERS absent: bob approves from the root file; no blob is read","status":"failed","title":"root OWNERS present, sdk/OWNERS absent: bob approves from the root file; no blob is read","duration":270.5904479999999,"failureMessages":["Error: the fake GitHub API answered 1 request(s) no route or allowance covers:\n GET /repos/Codertocat/Hello-World/contents/sdk%2FOWNERS?ref=basesha\n at Object.assertNoUnexpectedRequests (/data/agents/quality/pga/__tests__/bundle/fakeGithub.ts:157:15)\n at /data/agents/quality/pga/__tests__/bundle/ownersProbeArms.test.ts:45:10\n at file:///data/agents/quality/pga/node_modules/vitest/dist/chunks/run.Cm2sd7-x.js:1632:35\n at wrapper (file:///data/agents/quality/pga/node_modules/vitest/dist/chunks/run.Cm2sd7-x.js:2015:37)\n at file:///data/agents/quality/pga/node_modules/vitest/dist/chunks/run.Cm2sd7-x.js:3314:20\n at new Promise ()\n at runWithTimeout (file:///data/agents/quality/pga/node_modules/vitest/dist/chunks/run.Cm2sd7-x.js:3272:10)\n at file:///data/agents/quality/pga/node_modules/vitest/dist/chunks/run.Cm2sd7-x.js:3599:52\n at run (file:///data/agents/quality/pga/node_modules/vitest/dist/chunks/run.Cm2sd7-x.js:1352:20)\n at limiterFn (file:///data/agents/quality/pga/node_modules/vitest/dist/chunks/run.Cm2sd7-x.js:1362:59)"],"meta":{},"tags":[],"benchmarks":[]},{"ancestorTitles":["dist/index.js OWNERS probes","the base tip's tree is truncated: every candidate OWNERS path is read directly"],"fullName":"dist/index.js OWNERS probes the base tip's tree is truncated: every candidate OWNERS path is read directly a probe that fails other than 404 fails /approve naming the base tip; nothing is written","status":"failed","title":"a probe that fails other than 404 fails /approve naming the base tip; nothing is written","duration":223.05113700000004,"failureMessages":["Error: the fake GitHub API answered 1 request(s) no route or allowance covers:\n GET /repos/Codertocat/Hello-World/git/trees/master?recursive=true\n at Object.assertNoUnexpectedRequests (/data/agents/quality/pga/__tests__/bundle/fakeGithub.ts:157:15)\n at /data/agents/quality/pga/__tests__/bundle/ownersProbeArms.test.ts:45:10\n at file:///data/agents/quality/pga/node_modules/vitest/dist/chunks/run.Cm2sd7-x.js:1632:35\n at wrapper (file:///data/agents/quality/pga/node_modules/vitest/dist/chunks/run.Cm2sd7-x.js:2015:37)\n at file:///data/agents/quality/pga/node_modules/vitest/dist/chunks/run.Cm2sd7-x.js:3314:20\n at new Promise ()\n at runWithTimeout (file:///data/agents/quality/pga/node_modules/vitest/dist/chunks/run.Cm2sd7-x.js:3272:10)\n at file:///data/agents/quality/pga/node_modules/vitest/dist/chunks/run.Cm2sd7-x.js:3599:52\n at run (file:///data/agents/quality/pga/node_modules/vitest/dist/chunks/run.Cm2sd7-x.js:1352:20)\n at limiterFn (file:///data/agents/quality/pga/node_modules/vitest/dist/chunks/run.Cm2sd7-x.js:1362:59)"],"meta":{},"tags":[],"benchmarks":[]},{"ancestorTitles":["dist/index.js OWNERS probes","the tide gate's probe of the base branch"],"fullName":"dist/index.js OWNERS probes the tide gate's probe of the base branch a complete tree without an OWNERS blob: the gate wants lgtm alone and the pull request merges","status":"passed","title":"a complete tree without an OWNERS blob: the gate wants lgtm alone and the pull request merges","duration":295.297012,"failureMessages":[],"meta":{},"tags":[],"benchmarks":[]},{"ancestorTitles":["dist/index.js OWNERS probes","the tide gate's probe of the base branch"],"fullName":"dist/index.js OWNERS probes the tide gate's probe of the base branch a truncated tree without an OWNERS blob and a root OWNERS file: the gate wants approved too","status":"passed","title":"a truncated tree without an OWNERS blob and a root OWNERS file: the gate wants approved too","duration":370.8064689999999,"failureMessages":[],"meta":{},"tags":[],"benchmarks":[]},{"ancestorTitles":["dist/index.js OWNERS probes","the tide gate's probe of the base branch"],"fullName":"dist/index.js OWNERS probes the tide gate's probe of the base branch a truncated tree without an OWNERS blob and no root OWNERS file: the gate wants lgtm alone","status":"failed","title":"a truncated tree without an OWNERS blob and no root OWNERS file: the gate wants lgtm alone","duration":286.96094500000004,"failureMessages":["Error: the fake GitHub API answered 1 request(s) no route or allowance covers:\n GET /repos/Codertocat/Hello-World/contents/OWNERS?ref=master\n at Object.assertNoUnexpectedRequests (/data/agents/quality/pga/__tests__/bundle/fakeGithub.ts:157:15)\n at /data/agents/quality/pga/__tests__/bundle/ownersProbeArms.test.ts:45:10\n at file:///data/agents/quality/pga/node_modules/vitest/dist/chunks/run.Cm2sd7-x.js:1632:35\n at wrapper (file:///data/agents/quality/pga/node_modules/vitest/dist/chunks/run.Cm2sd7-x.js:2015:37)\n at file:///data/agents/quality/pga/node_modules/vitest/dist/chunks/run.Cm2sd7-x.js:3314:20\n at new Promise ()\n at runWithTimeout (file:///data/agents/quality/pga/node_modules/vitest/dist/chunks/run.Cm2sd7-x.js:3272:10)\n at file:///data/agents/quality/pga/node_modules/vitest/dist/chunks/run.Cm2sd7-x.js:3599:52\n at run (file:///data/agents/quality/pga/node_modules/vitest/dist/chunks/run.Cm2sd7-x.js:1352:20)\n at limiterFn (file:///data/agents/quality/pga/node_modules/vitest/dist/chunks/run.Cm2sd7-x.js:1362:59)"],"meta":{},"tags":[],"benchmarks":[]},{"ancestorTitles":["dist/index.js OWNERS probes","the tide gate's probe of the base branch"],"fullName":"dist/index.js OWNERS probes the tide gate's probe of the base branch a truncated tree whose root OWNERS probe fails other than 404: the merge fails naming the branch","status":"passed","title":"a truncated tree whose root OWNERS probe fails other than 404: the merge fails naming the branch","duration":317.183863,"failureMessages":[],"meta":{},"tags":[],"benchmarks":[]},{"ancestorTitles":["dist/index.js OWNERS probes","the tide gate's probe of the base branch"],"fullName":"dist/index.js OWNERS probes the tide gate's probe of the base branch a tree listing that fails other than 404: the merge fails naming the branch; no root probe","status":"passed","title":"a tree listing that fails other than 404: the merge fails naming the branch; no root probe","duration":310.3439239999998,"failureMessages":[],"meta":{},"tags":[],"benchmarks":[]}],"startTime":1792686288789,"endTime":1792686290864.344,"status":"failed","message":"","name":"/data/agents/quality/pga/__tests__/bundle/ownersProbeArms.test.ts"}]} \ No newline at end of file