Repository navigation
Follow up the engine fetch's review: a fetch time limit, a testable skip decision, sturdier tests #22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Follow up the engine fetch's review: a fetch time limit, a testable skip decision, sturdier tests #22
Changes from all commits
ab5859f
271d154
c314012
5b64ebc
bf333dd
cb5177b
3362a68
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,7 +4,7 @@ | |
| import { appendFileSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'; | ||
| import path from 'node:path'; | ||
| import { fileURLToPath } from 'node:url'; | ||
| import { commandFromEvent, isFromFork } from './command.ts'; | ||
| import { commandFromEvent, skipReasonFor } from './command.ts'; | ||
| import { AnthropicTokenSource, secretValues, type Credentials } from './credentials.ts'; | ||
| import { fetchHeadFor, fetchPullRequestHead } from './fetch-head.ts'; | ||
| import { closedIssues, gather, maxIssueChars, readPullRequest } from './gather.ts'; | ||
|
|
@@ -85,22 +85,20 @@ | |
| headers: { accept: 'application/vnd.github+json', ...(githubToken ? { authorization: `Bearer ${githubToken}` } : {}) }, | ||
| }); | ||
| if (!response.ok) throw new Error(`Couldn't read pull request #${request.number}: HTTP ${response.status}`); | ||
| const pullRequest = (await response.json()) as { head?: { repo?: { full_name?: string } } }; | ||
| if (isFromFork(pullRequest, repository)) { | ||
| console.log(`#${request.number} comes from a fork, and forks aren't reviewed.`); | ||
| const pullRequest = (await response.json()) as { number?: number; head?: { sha?: string; repo?: { full_name?: string } | null } }; | ||
|
Check notice on line 88 in src/main.ts
|
||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Nit: Reviewed-head lookup closure passed on the /review path where it can never run F6 · hygiene On the comment path, the arrow Consequence: No runtime effect; a reader has to trace skipReasonFor to learn that /review never looks up the reviewed head, and the dead argument invites someone to pass a real lookup later. Fix: Pass the lookup only in the automatic branch, or document at the call site that the comment trigger ignores it. Evidencemain.ts: Confidence 0.30. |
||
| const reason = await skipReasonFor('comment', pullRequest, repository, () => lastReviewedHead(repository, request.number, githubToken)); | ||
| if (reason) { | ||
| console.log(reason); | ||
| return; | ||
| } | ||
| event = { pull_request: pullRequest }; | ||
| } else { | ||
| if (isFromFork(event.pull_request, repository)) { | ||
| console.log(`#${event.pull_request?.number} comes from a fork, and forks aren't reviewed.`); | ||
| return; | ||
| } | ||
| // An automatic run on a commit already reviewed, such as marking a draft ready without pushing, would pay again for | ||
| // the same review. /review still reviews it. | ||
| const head = event.pull_request?.head?.sha; | ||
| if (head && (await lastReviewedHead(repository, event.pull_request.number, githubToken)) === head) { | ||
| console.log(`${String(head).slice(0, 7)} was already reviewed; comment /review to review it again.`); | ||
| const pullRequest = event.pull_request; | ||
|
polewskm marked this conversation as resolved.
Resolved
|
||
| const reason = await skipReasonFor('automatic', pullRequest, repository, () => lastReviewedHead(repository, pullRequest.number, githubToken)); | ||
| if (reason) { | ||
| console.log(reason); | ||
| return; | ||
| } | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| import assert from 'node:assert/strict'; | ||
| import { test } from 'node:test'; | ||
| import { commandFromEvent, isFromFork, skipReason, skipReasonFor } from '../src/command.ts'; | ||
|
|
||
| test('commandFromEvent accepts only an exact /review from a writer on a pull request', () => { | ||
| const event = (body: string, association = 'MEMBER', pullRequest: unknown = {}) => ({ | ||
| issue: { number: 7, pull_request: pullRequest }, | ||
| comment: { body, author_association: association }, | ||
| }); | ||
| assert.deepEqual(commandFromEvent(event('/review')), { command: { kind: 'review' }, number: 7 }); | ||
| assert.deepEqual(commandFromEvent(event(' /review\n')), { command: { kind: 'review' }, number: 7 }); | ||
| assert.equal(commandFromEvent(event('/review please')), undefined); | ||
| assert.equal(commandFromEvent(event('Run /review again')), undefined); | ||
| assert.equal(commandFromEvent(event('/review', 'CONTRIBUTOR')), undefined); | ||
| assert.equal(commandFromEvent(event('/review', 'NONE')), undefined); | ||
| assert.equal(commandFromEvent(event('/review', 'MEMBER', null)), undefined); | ||
| }); | ||
|
|
||
| test('isFromFork treats a head in another repository, or in none, as a fork', () => { | ||
| assert.equal(isFromFork({ head: { repo: { full_name: 'NCodeGroup/code-review' } } }, 'NCodeGroup/code-review'), false); | ||
| assert.equal(isFromFork({ head: { repo: { full_name: 'someone/code-review' } } }, 'NCodeGroup/code-review'), true); | ||
| assert.equal(isFromFork({ head: { repo: null } }, 'NCodeGroup/code-review'), true); | ||
| assert.equal(isFromFork({ head: {} }, 'NCodeGroup/code-review'), true); | ||
| assert.equal(isFromFork(undefined, 'NCodeGroup/code-review'), true); | ||
| }); | ||
|
|
||
| const own = { number: 7, head: { sha: 'a'.repeat(40), repo: { full_name: 'NCodeGroup/code-review' } } }; | ||
|
|
||
| // A lookup that records whether it was called, and answers with the given head. | ||
| function lookup(head: string | undefined): { calls: number; fn: () => Promise<string | undefined> } { | ||
| const result = { calls: 0, fn: async () => { result.calls++; return head; } }; | ||
| return result; | ||
| } | ||
|
|
||
| test('skipReason skips a pull request from a fork or a deleted fork, without looking up the reviewed head', async () => { | ||
| const looked = lookup('a'.repeat(40)); | ||
| assert.match((await skipReason({ ...own, head: { ...own.head, repo: { full_name: 'someone/code-review' } } }, 'NCodeGroup/code-review', looked.fn)) ?? '', /#7 comes from a fork/); | ||
| assert.match((await skipReason({ ...own, head: { sha: own.head.sha, repo: null } }, 'NCodeGroup/code-review', looked.fn)) ?? '', /comes from a fork/); | ||
| assert.equal(looked.calls, 0); | ||
| }); | ||
|
|
||
| test('skipReason skips an automatic review of a head already reviewed', async () => { | ||
| assert.match((await skipReason(own, 'NCodeGroup/code-review', lookup('a'.repeat(40)).fn)) ?? '', /aaaaaaa was already reviewed; comment \/review/); | ||
| }); | ||
|
|
||
| test('skipReason reviews a head not yet reviewed', async () => { | ||
| assert.equal(await skipReason(own, 'NCodeGroup/code-review', lookup('b'.repeat(40)).fn), undefined); | ||
|
polewskm marked this conversation as resolved.
Resolved
|
||
| }); | ||
|
|
||
| test('skipReason reviews when no lookup is passed', async () => { | ||
| assert.equal(await skipReason(own, 'NCodeGroup/code-review'), undefined); | ||
| }); | ||
|
|
||
| test('skipReason reviews a pull request with no head commit, without looking up the reviewed head', async () => { | ||
| const looked = lookup('a'.repeat(40)); | ||
| assert.equal(await skipReason({ number: 7, head: { repo: { full_name: 'NCodeGroup/code-review' } } }, 'NCodeGroup/code-review', looked.fn), undefined); | ||
| assert.equal(looked.calls, 0); | ||
| }); | ||
|
|
||
| test('skipReasonFor reviews a head already reviewed again for a /review comment, without looking it up', async () => { | ||
| const looked = lookup('a'.repeat(40)); | ||
| assert.equal(await skipReasonFor('comment', own, 'NCodeGroup/code-review', looked.fn), undefined); | ||
| assert.equal(looked.calls, 0); | ||
| }); | ||
|
|
||
| test('skipReasonFor skips a head already reviewed for an automatic run', async () => { | ||
| const looked = lookup('a'.repeat(40)); | ||
| assert.match((await skipReasonFor('automatic', own, 'NCodeGroup/code-review', looked.fn)) ?? '', /already reviewed/); | ||
| assert.equal(looked.calls, 1); | ||
| }); | ||
|
|
||
| test('skipReasonFor skips a fork for either trigger', async () => { | ||
| const fork = { ...own, head: { ...own.head, repo: { full_name: 'someone/code-review' } } }; | ||
| for (const trigger of ['comment', 'automatic'] as const) { | ||
| assert.match((await skipReasonFor(trigger, fork, 'NCodeGroup/code-review', lookup(undefined).fn)) ?? '', /comes from a fork/); | ||
| } | ||
| }); | ||
Uh oh!
There was an error while loading. Please reload this page.