From 639061ac2f50f10347d860f89272d78cb5a040ae Mon Sep 17 00:00:00 2001 From: diegosouzapw Date: Tue, 19 May 2026 03:10:38 -0300 Subject: [PATCH] test(triage): add end-to-end integration test with mocked deps --- .../unit/feature-triage/integration.test.mjs | 162 ++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 tests/unit/feature-triage/integration.test.mjs diff --git a/tests/unit/feature-triage/integration.test.mjs b/tests/unit/feature-triage/integration.test.mjs new file mode 100644 index 0000000000..d72256396c --- /dev/null +++ b/tests/unit/feature-triage/integration.test.mjs @@ -0,0 +1,162 @@ +import { describe, it } from "node:test"; +import assert from "node:assert/strict"; +import { tmpdir } from "node:os"; +import { mkdirSync, mkdtempSync, rmSync, readFileSync } from "node:fs"; +import { join } from "node:path"; +import { main } from "../../../scripts/features/feature-triage.mjs"; + +function makeDeps(state) { + return { + ghIssueListNumbers: () => state.openIssues.map((i) => i.number), + ghIssueListFeatureTitled: () => [], + ghIssueView: (_o, _r, n) => state.issuesById[n], + ghPrSearchMerged: (_o, _r, n) => state.prsByIssue[n] ?? [], + gitTagsByDate: () => state.tags ?? [], + gitLogGrep: (pattern) => state.commitsByPattern?.[pattern] ?? [], + gitIsAncestor: () => true, + gitCurrentReleaseBranch: () => state.releaseBranch ?? null, + }; +} + +function captureExit() { + const orig = process.exit; + let code = null; + process.exit = (c) => { + code = c; + throw new Error("__EXIT__"); + }; + return { + code: () => code, + restore: () => { + process.exit = orig; + }, + }; +} + +describe("feature-triage integration", () => { + it("classifies 5 mixed issues into expected buckets", async () => { + const tmp = mkdtempSync(join(tmpdir(), "triage-")); + mkdirSync(join(tmp, "viable", "need_details"), { recursive: true }); + mkdirSync(join(tmp, "defer"), { recursive: true }); + + const now = new Date(); + const day = (n) => new Date(now.getTime() - n * 86400_000).toISOString(); + + const state = { + openIssues: [ + { number: 100 }, + { number: 200 }, + { number: 300 }, + { number: 400 }, + { number: 500 }, + ], + issuesById: { + 100: { + number: 100, + title: "fresh", + url: "u", + author: { login: "a" }, + createdAt: day(3), + labels: [], + assignees: [], + comments: [], + reactionGroups: [], + timelineItems: [], + }, + 200: { + number: 200, + title: "ripe", + url: "u", + author: { login: "b" }, + createdAt: day(20), + labels: [], + assignees: [], + comments: [], + reactionGroups: [], + timelineItems: [], + }, + 300: { + number: 300, + title: "shipped", + url: "u", + author: { login: "c" }, + createdAt: day(40), + labels: [], + assignees: [], + comments: [], + reactionGroups: [], + timelineItems: [], + }, + 400: { + number: 400, + title: "delegated", + url: "u", + author: { login: "d" }, + createdAt: day(40), + labels: [], + assignees: [{ login: "alice" }], + comments: [], + reactionGroups: [], + timelineItems: [], + }, + 500: { + number: 500, + title: "in PR", + url: "u", + author: { login: "e" }, + createdAt: day(40), + labels: [], + assignees: [], + comments: [], + reactionGroups: [], + timelineItems: [ + { + __typename: "CrossReferencedEvent", + source: { __typename: "PullRequest", state: "OPEN", number: 999 }, + }, + ], + }, + }, + prsByIssue: { + 300: [ + { + number: 1000, + title: "feat: shipped (closes #300)", + body: "closes #300", + mergedAt: day(10), + mergeCommit: { oid: "abc" }, + }, + ], + }, + tags: [{ date: new Date(now.getTime() - 5 * 86400_000), name: "v3.7.2" }], + releaseBranch: "release/v3.8.0", + }; + + const deps = makeDeps(state); + const outPath = join(tmp, "_triage.json"); + + const exit = captureExit(); + try { + await main( + ["--owner", "x", "--repo", "y", "--ideia-dir", tmp, "--output", outPath], + {}, + deps + ).catch((e) => { + if (e.message !== "__EXIT__") throw e; + }); + } finally { + exit.restore(); + } + + assert.equal(exit.code(), 0); + const out = JSON.parse(readFileSync(outPath, "utf8")); + assert.equal(out.counts.dormant, 1); + assert.equal(out.counts.absorb, 1); + assert.equal(out.counts.already_delivered, 1); + assert.equal(out.counts.skip_assigned, 1); + assert.equal(out.counts.skip_has_pr, 1); + assert.equal(out.buckets.already_delivered[0].version, "v3.7.2"); + + rmSync(tmp, { recursive: true, force: true }); + }); +});