Download .github/scripts/bounty/tests/rules.test.ts from SaylorTwift/forgecode: direct link, hf CLI and curl.
- Browser
- Download file 13.5 kB
-
https://huggingface.co/SaylorTwift/forgecode/resolve/main/.github/scripts/bounty/tests/rules.test.ts
- Command line
-
hf download hf://SaylorTwift/forgecode/.github/scripts/bounty/tests/rules.test.ts
-
curl -L -o rules.test.ts https://huggingface.co/SaylorTwift/forgecode/resolve/main/.github/scripts/bounty/tests/rules.test.ts
13.5 kB
| import { describe, it } from "node:test"; | |
| import assert from "node:assert/strict"; | |
| import { | |
| computeIssuePatch, | |
| computePrPatch, | |
| linkedIssueNumbers, | |
| BOUNTY_GENERIC, | |
| BOUNTY_CLAIMED, | |
| BOUNTY_REWARDED, | |
| } from "../src/rules.js"; | |
| import type { Issue, PullRequest, IssueState, PrState } from "../src/types.js"; | |
| // --------------------------------------------------------------------------- | |
| // Fixtures | |
| // --------------------------------------------------------------------------- | |
| function makeIssue(overrides: Partial<Issue> & { number: number }): Issue { | |
| return { | |
| title: "Test issue", | |
| html_url: `https://github.com/owner/repo/issues/${overrides.number}`, | |
| state: "open", | |
| labels: [], | |
| assignees: [], | |
| ...overrides, | |
| }; | |
| } | |
| function makePr(overrides: Partial<PullRequest> & { number: number }): PullRequest { | |
| return { | |
| title: "Test PR", | |
| state: "open", | |
| merged: false, | |
| body: null, | |
| labels: [], | |
| user: { login: "contributor" }, | |
| html_url: `https://github.com/owner/repo/pull/${overrides.number}`, | |
| ...overrides, | |
| }; | |
| } | |
| function labelNames(...names: string[]): { name: string }[] { | |
| return names.map((name) => ({ name })); | |
| } | |
| function issueState(issue: Issue): IssueState { | |
| return { issue, currentLabels: new Set(issue.labels.map((l) => l.name)) }; | |
| } | |
| // --------------------------------------------------------------------------- | |
| // computeIssuePatch β issue rules | |
| // --------------------------------------------------------------------------- | |
| describe("computeIssuePatch", () => { | |
| describe("no bounty label", () => { | |
| it("produces no patch when issue has no bounty labels at all", () => { | |
| const issue = makeIssue({ number: 1, labels: labelNames("type: bug") }); | |
| const patch = computeIssuePatch(issueState(issue)); | |
| assert.deepEqual(patch.ops, []); | |
| }); | |
| it("removes generic bounty label when no value label present", () => { | |
| const issue = makeIssue({ number: 1, labels: labelNames(BOUNTY_GENERIC) }); | |
| const patch = computeIssuePatch(issueState(issue)); | |
| assert.equal(patch.ops.length, 1); | |
| assert.deepEqual(patch.ops[0]!.remove, [BOUNTY_GENERIC]); | |
| assert.deepEqual(patch.ops[0]!.add, []); | |
| }); | |
| it("removes claimed label when no value label present", () => { | |
| const issue = makeIssue({ | |
| number: 1, | |
| labels: labelNames(BOUNTY_CLAIMED), | |
| assignees: [{ login: "alice" }], | |
| }); | |
| const patch = computeIssuePatch(issueState(issue)); | |
| assert.equal(patch.ops.length, 1); | |
| assert.deepEqual(patch.ops[0]!.remove, [BOUNTY_CLAIMED]); | |
| }); | |
| }); | |
| describe("has value label, no assignees", () => { | |
| it("adds generic label when missing", () => { | |
| const issue = makeIssue({ number: 1, labels: labelNames("bounty: π° $500") }); | |
| const patch = computeIssuePatch(issueState(issue)); | |
| assert.equal(patch.ops.length, 1); | |
| assert.deepEqual(patch.ops[0]!.add, [BOUNTY_GENERIC]); | |
| assert.deepEqual(patch.ops[0]!.remove, []); | |
| }); | |
| it("recognises legacy label format (no emoji) as a value label", () => { | |
| const issue = makeIssue({ number: 1, labels: labelNames("bounty: $300") }); | |
| const patch = computeIssuePatch(issueState(issue)); | |
| assert.equal(patch.ops.length, 1); | |
| assert.deepEqual(patch.ops[0]!.add, [BOUNTY_GENERIC]); | |
| assert.deepEqual(patch.ops[0]!.remove, []); | |
| }); | |
| it("produces no patch when generic already present and no assignees", () => { | |
| const issue = makeIssue({ | |
| number: 1, | |
| labels: labelNames("bounty: π° $500", BOUNTY_GENERIC), | |
| }); | |
| const patch = computeIssuePatch(issueState(issue)); | |
| assert.deepEqual(patch.ops, []); | |
| }); | |
| it("removes claimed when no assignees", () => { | |
| const issue = makeIssue({ | |
| number: 1, | |
| labels: labelNames("bounty: π° $500", BOUNTY_GENERIC, BOUNTY_CLAIMED), | |
| assignees: [], | |
| }); | |
| const patch = computeIssuePatch(issueState(issue)); | |
| assert.equal(patch.ops.length, 1); | |
| assert.deepEqual(patch.ops[0]!.remove, [BOUNTY_CLAIMED]); | |
| assert.deepEqual(patch.ops[0]!.add, []); | |
| }); | |
| }); | |
| describe("has value label and assignees", () => { | |
| it("adds both generic and claimed when both missing", () => { | |
| const issue = makeIssue({ | |
| number: 1, | |
| labels: labelNames("bounty: π° $300"), | |
| assignees: [{ login: "alice" }], | |
| }); | |
| const patch = computeIssuePatch(issueState(issue)); | |
| assert.equal(patch.ops.length, 1); | |
| assert.deepEqual(patch.ops[0]!.add.sort(), [BOUNTY_CLAIMED, BOUNTY_GENERIC].sort()); | |
| assert.deepEqual(patch.ops[0]!.remove, []); | |
| }); | |
| it("produces no patch when generic and claimed both already present", () => { | |
| const issue = makeIssue({ | |
| number: 1, | |
| labels: labelNames("bounty: π° $300", BOUNTY_GENERIC, BOUNTY_CLAIMED), | |
| assignees: [{ login: "alice" }], | |
| }); | |
| const patch = computeIssuePatch(issueState(issue)); | |
| assert.deepEqual(patch.ops, []); | |
| }); | |
| it("preserves non-bounty labels untouched", () => { | |
| const issue = makeIssue({ | |
| number: 1, | |
| labels: labelNames("type: bug", "bounty: π° $100", BOUNTY_GENERIC, BOUNTY_CLAIMED), | |
| assignees: [{ login: "alice" }], | |
| }); | |
| const patch = computeIssuePatch(issueState(issue)); | |
| assert.deepEqual(patch.ops, []); | |
| }); | |
| }); | |
| describe("rewarded state", () => { | |
| it("leaves rewarded label alone (owned by PR sync)", () => { | |
| const issue = makeIssue({ | |
| number: 1, | |
| labels: labelNames("bounty: π° $500", BOUNTY_GENERIC, BOUNTY_REWARDED), | |
| assignees: [], | |
| }); | |
| const patch = computeIssuePatch(issueState(issue)); | |
| // rewarded is not touched; only generic should be kept since value label present | |
| assert.deepEqual(patch.ops, []); | |
| }); | |
| }); | |
| }); | |
| // --------------------------------------------------------------------------- | |
| // linkedIssueNumbers | |
| // --------------------------------------------------------------------------- | |
| describe("linkedIssueNumbers", () => { | |
| it("returns empty array for null body", () => { | |
| assert.deepEqual(linkedIssueNumbers(null), []); | |
| }); | |
| it("parses closes/fixes/resolves keywords", () => { | |
| const body = "Closes #10\nFixes #20\nResolves #30"; | |
| assert.deepEqual(linkedIssueNumbers(body), [10, 20, 30]); | |
| }); | |
| it("is case-insensitive", () => { | |
| const body = "CLOSES #5 fixes #6 RESOLVES #7"; | |
| assert.deepEqual(linkedIssueNumbers(body), [5, 6, 7]); | |
| }); | |
| it("handles plural forms: close/fix/resolve", () => { | |
| const body = "close #1 fix #2 resolve #3"; | |
| assert.deepEqual(linkedIssueNumbers(body), [1, 2, 3]); | |
| }); | |
| it("returns empty when no keywords present", () => { | |
| assert.deepEqual(linkedIssueNumbers("See issue #99 for context."), []); | |
| }); | |
| }); | |
| // --------------------------------------------------------------------------- | |
| // computePrPatch β PR rules | |
| // --------------------------------------------------------------------------- | |
| describe("computePrPatch", () => { | |
| describe("label propagation (pre-merge)", () => { | |
| it("produces no patch when PR body has no linked issues", () => { | |
| const pr = makePr({ number: 10, body: "No references here." }); | |
| const patch = computePrPatch({ pr, currentLabels: new Set(), linkedIssues: [] }); | |
| assert.deepEqual(patch.ops, []); | |
| }); | |
| it("produces no patch when linked issues have no bounty labels", () => { | |
| const pr = makePr({ number: 10, body: "Closes #1" }); | |
| const issue = makeIssue({ number: 1, labels: labelNames("type: bug") }); | |
| const patch = computePrPatch({ pr, currentLabels: new Set(), linkedIssues: [issue] }); | |
| assert.deepEqual(patch.ops, []); | |
| }); | |
| it("copies bounty value label from linked issue to PR", () => { | |
| const pr = makePr({ number: 10, body: "Closes #1" }); | |
| const issue = makeIssue({ number: 1, labels: labelNames("bounty: π° $500") }); | |
| const patch = computePrPatch({ pr, currentLabels: new Set(), linkedIssues: [issue] }); | |
| const prOp = patch.ops.find((o) => o.target === 10); | |
| assert.ok(prOp); | |
| assert.deepEqual(prOp.add, ["bounty: π° $500"]); | |
| assert.deepEqual(prOp.remove, []); | |
| }); | |
| it("does not duplicate a value label already on the PR", () => { | |
| const pr = makePr({ number: 10, body: "Closes #1" }); | |
| const issue = makeIssue({ number: 1, labels: labelNames("bounty: π° $500") }); | |
| const patch = computePrPatch({ | |
| pr, | |
| currentLabels: new Set(["bounty: π° $500"]), | |
| linkedIssues: [issue], | |
| }); | |
| assert.deepEqual(patch.ops, []); | |
| }); | |
| it("posts a comment on the linked issue when label is propagated", () => { | |
| const pr = makePr({ number: 10, body: "Closes #1" }); | |
| const issue = makeIssue({ number: 1, labels: labelNames("bounty: π° $200") }); | |
| const patch = computePrPatch({ pr, currentLabels: new Set(), linkedIssues: [issue] }); | |
| const issueOp = patch.ops.find((o) => o.target === 1); | |
| assert.ok(issueOp); | |
| assert.ok(issueOp.comment?.includes("PR")); | |
| assert.ok(issueOp.comment?.includes("#10")); | |
| assert.ok(issueOp.comment?.includes("contributor")); | |
| }); | |
| it("does not post a comment when label was already on the PR", () => { | |
| const pr = makePr({ number: 10, body: "Closes #1" }); | |
| const issue = makeIssue({ number: 1, labels: labelNames("bounty: π° $200") }); | |
| const patch = computePrPatch({ | |
| pr, | |
| currentLabels: new Set(["bounty: π° $200"]), | |
| linkedIssues: [issue], | |
| }); | |
| const issueOp = patch.ops.find((o) => o.target === 1); | |
| assert.ok(!issueOp); | |
| }); | |
| it("merges value labels from multiple linked issues onto the PR", () => { | |
| const pr = makePr({ number: 10, body: "Closes #1\nCloses #2" }); | |
| const issue1 = makeIssue({ number: 1, labels: labelNames("bounty: π° $100") }); | |
| const issue2 = makeIssue({ number: 2, labels: labelNames("bounty: π° $200") }); | |
| const patch = computePrPatch({ | |
| pr, | |
| currentLabels: new Set(), | |
| linkedIssues: [issue1, issue2], | |
| }); | |
| const prOp = patch.ops.find((o) => o.target === 10); | |
| assert.ok(prOp); | |
| assert.deepEqual(prOp.add.sort(), ["bounty: π° $100", "bounty: π° $200"].sort()); | |
| }); | |
| }); | |
| describe("rewarded on merge", () => { | |
| it("produces no patch when PR was closed but not merged", () => { | |
| const pr = makePr({ number: 10, state: "closed", merged: false, body: "Closes #1" }); | |
| const issue = makeIssue({ number: 1, labels: labelNames("bounty: π° $500") }); | |
| const patch = computePrPatch({ | |
| pr, | |
| currentLabels: new Set(["bounty: π° $500"]), | |
| linkedIssues: [issue], | |
| }); | |
| const prOp = patch.ops.find((o) => o.target === 10); | |
| assert.ok(!prOp?.add.includes(BOUNTY_REWARDED)); | |
| }); | |
| it("adds rewarded to PR on merge", () => { | |
| const pr = makePr({ | |
| number: 10, | |
| merged: true, | |
| body: "Closes #1", | |
| labels: labelNames("bounty: π° $500"), | |
| }); | |
| const issue = makeIssue({ number: 1, labels: labelNames("bounty: π° $500") }); | |
| const patch = computePrPatch({ | |
| pr, | |
| currentLabels: new Set(["bounty: π° $500"]), | |
| linkedIssues: [issue], | |
| }); | |
| const prOp = patch.ops.find((o) => o.target === 10); | |
| assert.ok(prOp); | |
| assert.ok(prOp.add.includes(BOUNTY_REWARDED)); | |
| }); | |
| it("adds rewarded to linked issue on merge", () => { | |
| const pr = makePr({ | |
| number: 10, | |
| merged: true, | |
| body: "Closes #1", | |
| labels: labelNames("bounty: π° $500"), | |
| }); | |
| const issue = makeIssue({ number: 1, labels: labelNames("bounty: π° $500", BOUNTY_CLAIMED) }); | |
| const patch = computePrPatch({ | |
| pr, | |
| currentLabels: new Set(["bounty: π° $500"]), | |
| linkedIssues: [issue], | |
| }); | |
| const issueOp = patch.ops.find((o) => o.target === 1); | |
| assert.ok(issueOp); | |
| assert.ok(issueOp.add.includes(BOUNTY_REWARDED)); | |
| assert.ok(issueOp.remove.includes(BOUNTY_CLAIMED)); | |
| }); | |
| it("does not add rewarded again when already present", () => { | |
| const pr = makePr({ | |
| number: 10, | |
| merged: true, | |
| body: "Closes #1", | |
| labels: labelNames("bounty: π° $500", BOUNTY_REWARDED), | |
| }); | |
| const issue = makeIssue({ | |
| number: 1, | |
| labels: labelNames("bounty: π° $500", BOUNTY_REWARDED), | |
| }); | |
| const patch = computePrPatch({ | |
| pr, | |
| currentLabels: new Set(["bounty: π° $500", BOUNTY_REWARDED]), | |
| linkedIssues: [issue], | |
| }); | |
| assert.deepEqual(patch.ops, []); | |
| }); | |
| it("skips issues with no bounty label when merged PR has a bounty", () => { | |
| const pr = makePr({ | |
| number: 10, | |
| merged: true, | |
| body: "Closes #1\nCloses #2", | |
| labels: labelNames("bounty: π° $500"), | |
| }); | |
| const issue1 = makeIssue({ number: 1, labels: labelNames("bounty: π° $500") }); | |
| const issue2 = makeIssue({ number: 2, labels: labelNames("type: bug") }); | |
| const patch = computePrPatch({ | |
| pr, | |
| currentLabels: new Set(["bounty: π° $500"]), | |
| linkedIssues: [issue1, issue2], | |
| }); | |
| // issue2 has no bounty so its desired state equals current: no op | |
| const issue2Op = patch.ops.find((o) => o.target === 2); | |
| assert.ok(!issue2Op); | |
| }); | |
| }); | |
| }); | |