Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .github/scripts/__tests__/agents-verifier-context.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -424,16 +424,22 @@ test('buildVerifierContext selects CI results for the merge commit SHA', async (
workflow_name: 'Gate',
conclusion: 'success',
run_url: 'https://ci/gate-merge',
error_category: '',
error_message: '',
},
{
workflow_name: 'Selftest CI',
conclusion: 'success',
run_url: 'https://ci/selftest-merge',
error_category: '',
error_message: '',
},
{
workflow_name: 'PR 11 - Minimal invariant CI',
conclusion: 'success',
run_url: 'https://ci/pr11-merge',
error_category: '',
error_message: '',
},
]);

Expand Down Expand Up @@ -546,16 +552,22 @@ test('buildVerifierContext falls back to head SHA when merge runs are missing',
workflow_name: 'Gate',
conclusion: 'success',
run_url: 'https://ci/pr-00-gate.yml',
error_category: '',
error_message: '',
},
{
workflow_name: 'Selftest CI',
conclusion: 'success',
run_url: 'https://ci/selftest-ci.yml',
error_category: '',
error_message: '',
},
{
workflow_name: 'PR 11 - Minimal invariant CI',
conclusion: 'success',
run_url: 'https://ci/pr-11-ci-smoke.yml',
error_category: '',
error_message: '',
},
]);
for (const workflowId of workflowIds) {
Expand Down Expand Up @@ -627,16 +639,22 @@ test('buildVerifierContext uses merge commit SHA for push events', async () => {
workflow_name: 'Gate',
conclusion: 'success',
run_url: 'https://ci/gate-push',
error_category: '',
error_message: '',
},
{
workflow_name: 'Selftest CI',
conclusion: 'success',
run_url: 'https://ci/selftest-push',
error_category: '',
error_message: '',
},
{
workflow_name: 'PR 11 - Minimal invariant CI',
conclusion: 'success',
run_url: 'https://ci/pr11-push',
error_category: '',
error_message: '',
},
]);

Expand Down
113 changes: 110 additions & 3 deletions .github/scripts/__tests__/verifier-ci-query.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ const buildGithubStub = ({
actions: {
async listWorkflowRuns({ workflow_id: workflowId, head_sha: headSha }) {
if (errorWorkflow && workflowId === errorWorkflow) {
throw new Error('boom');
const error = new Error('boom');
error.status = 404;
throw error;
}
if (listWorkflowRunsHook) {
const hooked = await listWorkflowRunsHook({ workflow_id: workflowId, head_sha: headSha });
Expand Down Expand Up @@ -59,16 +61,22 @@ test('queryVerifierCiResults selects runs and reports conclusions', async () =>
workflow_name: 'Gate',
conclusion: 'success',
run_url: 'gate-url',
error_category: '',
error_message: '',
});
assert.deepEqual(results[1], {
workflow_name: 'Selftest CI',
conclusion: 'in_progress',
run_url: 'selftest-url',
error_category: '',
error_message: '',
});
assert.deepEqual(results[2], {
workflow_name: 'PR 11',
conclusion: 'not_found',
run_url: '',
error_category: '',
error_message: '',
});
});

Expand Down Expand Up @@ -97,11 +105,13 @@ test('queryVerifierCiResults supports workflowId/workflowName aliases', async ()
workflow_name: 'Selftest CI',
conclusion: 'success',
run_url: 'selftest-alias-url',
error_category: '',
error_message: '',
},
]);
});

test('queryVerifierCiResults treats query errors as not_found', async () => {
test('queryVerifierCiResults treats query errors as api_error', async () => {
const github = buildGithubStub({ errorWorkflow: 'pr-00-gate.yml' });
const context = { repo: { owner: 'octo', repo: 'workflows' } };
const workflows = [{ workflow_name: 'Gate', workflow_id: 'pr-00-gate.yml' }];
Expand All @@ -116,8 +126,10 @@ test('queryVerifierCiResults treats query errors as not_found', async () => {
assert.deepEqual(results, [
{
workflow_name: 'Gate',
conclusion: 'not_found',
conclusion: 'api_error',
run_url: '',
error_category: 'resource',
error_message: 'listWorkflowRuns:pr-00-gate.yml failed after 1 attempt(s): boom',
},
]);
});
Expand Down Expand Up @@ -145,6 +157,8 @@ test('queryVerifierCiResults uses latest run when no target SHA is provided', as
workflow_name: 'Gate',
conclusion: 'success',
run_url: 'gate-latest-url',
error_category: '',
error_message: '',
},
]);
});
Expand Down Expand Up @@ -184,6 +198,8 @@ test('queryVerifierCiResults falls back to secondary SHA when primary has no run
workflow_name: 'Gate',
conclusion: 'success',
run_url: 'head-url',
error_category: '',
error_message: '',
},
]);
assert.deepEqual(headShas, ['merge-sha', 'head-sha']);
Expand Down Expand Up @@ -216,16 +232,22 @@ test('queryVerifierCiResults falls back to default workflows', async () => {
workflow_name: 'Gate',
conclusion: 'success',
run_url: 'gate-default-url',
error_category: '',
error_message: '',
},
{
workflow_name: 'Selftest CI',
conclusion: 'failure',
run_url: 'selftest-default-url',
error_category: '',
error_message: '',
},
{
workflow_name: 'PR 11 - Minimal invariant CI',
conclusion: 'success',
run_url: 'pr11-default-url',
error_category: '',
error_message: '',
},
]);
});
Expand All @@ -251,6 +273,8 @@ test('queryVerifierCiResults uses API url when html_url is missing', async () =>
workflow_name: 'Gate',
conclusion: 'success',
run_url: 'api-url',
error_category: '',
error_message: '',
},
]);
});
Expand All @@ -276,6 +300,89 @@ test('queryVerifierCiResults treats completed runs without conclusion as unknown
workflow_name: 'Gate',
conclusion: 'unknown',
run_url: 'gate-url',
error_category: '',
error_message: '',
},
]);
});

test('queryVerifierCiResults retries transient errors and returns success', async () => {
let attempts = 0;
const warnings = [];
const github = buildGithubStub({
listWorkflowRunsHook: async () => {
attempts += 1;
if (attempts < 3) {
const error = new Error('Service unavailable');
error.status = 503;
throw error;
}
return {
data: {
workflow_runs: [
{ head_sha: 'retry-sha', conclusion: 'success', html_url: 'retry-url' },
],
},
};
},
});
const context = { repo: { owner: 'octo', repo: 'workflows' } };
const workflows = [{ workflow_name: 'Gate', workflow_id: 'pr-00-gate.yml' }];

const results = await queryVerifierCiResults({
github,
context,
targetSha: 'retry-sha',
workflows,
core: { warning: (message) => warnings.push(String(message)) },
retryOptions: { sleepFn: async () => {} },
});

assert.equal(attempts, 3);
assert.equal(warnings.length, 2);
assert.deepEqual(results, [
{
workflow_name: 'Gate',
conclusion: 'success',
run_url: 'retry-url',
error_category: '',
error_message: '',
},
]);
});

test('queryVerifierCiResults returns api_error after max retries', async () => {
let attempts = 0;
const warnings = [];
const github = buildGithubStub({
listWorkflowRunsHook: async () => {
attempts += 1;
const error = new Error('timeout');
error.status = 504;
throw error;
},
});
const context = { repo: { owner: 'octo', repo: 'workflows' } };
const workflows = [{ workflow_name: 'Gate', workflow_id: 'pr-00-gate.yml' }];

const results = await queryVerifierCiResults({
github,
context,
targetSha: 'retry-sha',
workflows,
core: { warning: (message) => warnings.push(String(message)) },
retryOptions: { sleepFn: async () => {} },
});

assert.equal(attempts, 4);
assert.equal(warnings.length, 4);
assert.deepEqual(results, [
{
workflow_name: 'Gate',
conclusion: 'api_error',
run_url: '',
error_category: 'transient',
error_message: 'listWorkflowRuns:pr-00-gate.yml failed after 4 attempt(s): timeout',
},
]);
});
Loading
Loading