pr-shepherd 0.23.0 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +2 -1
  3. package/bin/cli/fix-formatter.mjs +2 -1
  4. package/bin/cli/iterate-instructions.mjs +1 -1
  5. package/bin/cli/list-formatters.mjs +3 -2
  6. package/bin/commands/check.mjs +6 -2
  7. package/bin/commands/iterate/classify.mjs +13 -7
  8. package/bin/commands/iterate/fix-code.mjs +2 -2
  9. package/bin/commands/iterate/index.mjs +4 -1
  10. package/bin/commands/iterate/render.mjs +3 -0
  11. package/bin/commands/iterate/stall.mjs +1 -1
  12. package/bin/commands/resolve-instructions.mjs +5 -2
  13. package/bin/commands/resolve-mutate.mjs +14 -6
  14. package/bin/commands/resolve.mjs +7 -3
  15. package/bin/comments/authors.mjs +19 -1
  16. package/bin/comments/minimize-policy.mjs +6 -5
  17. package/bin/comments/review-thread-markers.mjs +18 -0
  18. package/bin/comments/thread-visibility.mjs +5 -5
  19. package/bin/comments/visible-comments.mjs +2 -2
  20. package/bin/config/load.mjs +7 -0
  21. package/bin/config.json +14 -0
  22. package/bin/github/batch-parsers.mjs +2 -0
  23. package/bin/github/client.mjs +1 -2
  24. package/bin/github/gql/batch-pr.gql +3 -0
  25. package/bin/reporters/agent.mjs +1 -0
  26. package/bin/state/seen-comments.mjs +40 -4
  27. package/package.json +8 -4
  28. package/plugins/pr-shepherd/.codex-plugin/plugin.json +1 -1
  29. package/bin/checks/triage.test-support.mjs +0 -60
  30. package/bin/cli/iterate-lean.test-support.mjs +0 -3
  31. package/bin/cli-parser.clean.test-support.mjs +0 -44
  32. package/bin/cli-parser.commit-suggestion.test-support.mjs +0 -65
  33. package/bin/cli-parser.iterate-fix.test-support.mjs +0 -43
  34. package/bin/cli-parser.iterate-fixtures.mjs +0 -75
  35. package/bin/cli-parser.iterate.test-support.mjs +0 -44
  36. package/bin/cli-parser.test-support.mjs +0 -47
  37. package/bin/commands/check.test-support.mjs +0 -147
  38. package/bin/commands/clean.test-support.mjs +0 -47
  39. package/bin/commands/commit-suggestion.apply.test-support.mjs +0 -87
  40. package/bin/commands/commit-suggestion.test-support.mjs +0 -112
  41. package/bin/commands/iterate-stall.test-support.mjs +0 -24
  42. package/bin/commands/iterate-test-support.mjs +0 -150
  43. package/bin/commands/iterate-thread-test-support.mjs +0 -18
  44. package/bin/commands/iterate.fix-code-in-progress.test-support.mjs +0 -127
  45. package/bin/commands/poll.test-support.mjs +0 -77
  46. package/bin/commands/resolve.test-support.mjs +0 -116
  47. package/bin/commands/shepherd-journal.test-support.mjs +0 -7
  48. package/bin/comments/outdated.mjs +0 -15
  49. package/bin/comments/resolve.test-support.mjs +0 -44
  50. package/bin/github/batch-parsers.test-support.mjs +0 -66
  51. package/bin/github/batch.test-support.mjs +0 -66
  52. package/bin/github/client.test-support.mjs +0 -55
  53. package/bin/github/http.test-support.mjs +0 -51
  54. package/bin/state/seen-comments.test-support.mjs +0 -19
  55. package/bin/suggestions/patch.test-support.mjs +0 -2
@@ -1,87 +0,0 @@
1
- import { vi } from "vitest";
2
- // ---------------------------------------------------------------------------
3
- // Hoisted mocks
4
- // ---------------------------------------------------------------------------
5
- const { mockExecFile } = vi.hoisted(() => ({ mockExecFile: vi.fn() }));
6
- vi.mock("node:child_process", () => ({
7
- execFile: (cmd, args, optsOrCb, maybeCb) => {
8
- const cb = typeof optsOrCb === "function" ? optsOrCb : maybeCb;
9
- mockExecFile(cmd, args)
10
- .then((result) => cb(null, result))
11
- .catch((err) => cb(err, { stdout: "", stderr: err.stderr ?? "" }));
12
- },
13
- }));
14
- vi.mock("node:fs/promises", () => ({
15
- readFile: vi.fn(),
16
- }));
17
- vi.mock("../github/client.mts", () => ({
18
- getRepoInfo: vi.fn().mockResolvedValue({ owner: "owner", name: "repo" }),
19
- getCurrentPrNumber: vi.fn().mockResolvedValue(42),
20
- getCurrentBranch: vi.fn().mockResolvedValue("feature/foo"),
21
- }));
22
- vi.mock("../github/batch.mts", () => ({
23
- fetchPrBatch: vi.fn(),
24
- }));
25
- import { runCommitSuggestion } from "./commit-suggestion.mjs";
26
- import { getCurrentBranch } from "../github/client.mjs";
27
- import { fetchPrBatch } from "../github/batch.mjs";
28
- import { readFile } from "node:fs/promises";
29
- const mockGetCurrentBranch = vi.mocked(getCurrentBranch);
30
- const mockFetchBatch = vi.mocked(fetchPrBatch);
31
- const mockReadFile = vi.mocked(readFile);
32
- function makeThread(overrides = {}) {
33
- return {
34
- id: "PRRT_x",
35
- isResolved: false,
36
- isOutdated: false,
37
- isMinimized: false,
38
- path: "src/foo.ts",
39
- line: 5,
40
- startLine: null,
41
- author: "alice",
42
- authorType: "Unknown",
43
- body: "Use a const here.\n\n```suggestion\nconst x = 10;\n```",
44
- url: "",
45
- createdAtUnix: 0,
46
- ...overrides,
47
- };
48
- }
49
- function makeBatch(threads) {
50
- return {
51
- nodeId: "PR_kgDOAAA",
52
- number: 42,
53
- state: "OPEN",
54
- isDraft: false,
55
- mergeable: "MERGEABLE",
56
- mergeStateStatus: "CLEAN",
57
- reviewDecision: "APPROVED",
58
- headRefOid: "headsha",
59
- headRefName: "feature/foo",
60
- headRepoWithOwner: "owner/repo",
61
- baseRefName: "main",
62
- reviewRequests: [],
63
- latestReviews: [],
64
- reviewThreads: threads,
65
- checks: [],
66
- comments: [],
67
- changesRequestedReviews: [],
68
- reviewSummaries: [],
69
- approvedReviews: [],
70
- branchProtection: null,
71
- };
72
- }
73
- const FILE_CONTENT = "line1\n" +
74
- "line2\n" +
75
- "line3\n" +
76
- "line4\n" +
77
- "const x = 1;\n" + // line 5 — matches the suggestion anchor
78
- "line6\n" +
79
- "line7\n";
80
- const GLOBAL_OPTS = { format: "text" };
81
- function makeGitSuccess(stdout = "") {
82
- return Promise.resolve({ stdout, stderr: "" });
83
- }
84
- // ---------------------------------------------------------------------------
85
- // Output shape and instruction content
86
- // ---------------------------------------------------------------------------
87
- export { FILE_CONTENT, GLOBAL_OPTS, fetchPrBatch, getCurrentBranch, makeBatch, makeGitSuccess, makeThread, mockExecFile, mockFetchBatch, mockGetCurrentBranch, mockReadFile, readFile, runCommitSuggestion, };
@@ -1,112 +0,0 @@
1
- import { vi, beforeEach } from "vitest";
2
- // ---------------------------------------------------------------------------
3
- // Hoisted mocks
4
- // ---------------------------------------------------------------------------
5
- const { mockExecFile } = vi.hoisted(() => ({ mockExecFile: vi.fn() }));
6
- vi.mock("node:child_process", () => ({
7
- execFile: (cmd, args, optsOrCb, maybeCb) => {
8
- const cb = typeof optsOrCb === "function" ? optsOrCb : maybeCb;
9
- mockExecFile(cmd, args)
10
- .then((result) => cb(null, result))
11
- .catch((err) => cb(err, { stdout: "", stderr: err.stderr ?? "" }));
12
- },
13
- }));
14
- vi.mock("node:fs/promises", () => ({
15
- readFile: vi.fn(),
16
- }));
17
- vi.mock("../github/client.mts", () => ({
18
- getRepoInfo: vi.fn().mockResolvedValue({ owner: "owner", name: "repo" }),
19
- getCurrentPrNumber: vi.fn().mockResolvedValue(42),
20
- getCurrentBranch: vi.fn().mockResolvedValue("feature/foo"),
21
- }));
22
- vi.mock("../github/batch.mts", () => ({
23
- fetchPrBatch: vi.fn(),
24
- }));
25
- const { mockLoadConfig } = vi.hoisted(() => ({ mockLoadConfig: vi.fn() }));
26
- vi.mock("../config/load.mts", () => ({
27
- loadConfig: mockLoadConfig,
28
- }));
29
- import { runCommitSuggestion } from "./commit-suggestion.mjs";
30
- import { getCurrentBranch, getCurrentPrNumber } from "../github/client.mjs";
31
- import { fetchPrBatch } from "../github/batch.mjs";
32
- import { readFile } from "node:fs/promises";
33
- const mockGetCurrentBranch = vi.mocked(getCurrentBranch);
34
- const mockGetCurrentPrNumber = vi.mocked(getCurrentPrNumber);
35
- const mockFetchBatch = vi.mocked(fetchPrBatch);
36
- const mockReadFile = vi.mocked(readFile);
37
- // ---------------------------------------------------------------------------
38
- // Fixtures
39
- // ---------------------------------------------------------------------------
40
- function makeThread(overrides = {}) {
41
- return {
42
- id: "PRRT_x",
43
- isResolved: false,
44
- isOutdated: false,
45
- isMinimized: false,
46
- path: "src/foo.ts",
47
- line: 5,
48
- startLine: null,
49
- author: "alice",
50
- authorType: "Unknown",
51
- body: "Use a const here.\n\n```suggestion\nconst x = 10;\n```",
52
- url: "",
53
- createdAtUnix: 0,
54
- ...overrides,
55
- };
56
- }
57
- function makeBatch(threads, headRepoWithOwner = "owner/repo") {
58
- return {
59
- nodeId: "PR_kgDOAAA",
60
- number: 42,
61
- state: "OPEN",
62
- isDraft: false,
63
- mergeable: "MERGEABLE",
64
- mergeStateStatus: "CLEAN",
65
- reviewDecision: "APPROVED",
66
- headRefOid: "headsha",
67
- headRefName: "feature/foo",
68
- headRepoWithOwner,
69
- baseRefName: "main",
70
- reviewRequests: [],
71
- latestReviews: [],
72
- reviewThreads: threads,
73
- checks: [],
74
- comments: [],
75
- changesRequestedReviews: [],
76
- reviewSummaries: [],
77
- approvedReviews: [],
78
- branchProtection: null,
79
- };
80
- }
81
- const FILE_CONTENT = "line1\n" +
82
- "line2\n" +
83
- "line3\n" +
84
- "line4\n" +
85
- "const x = 1;\n" + // line 5 — matches the suggestion anchor
86
- "line6\n" +
87
- "line7\n";
88
- const GLOBAL_OPTS = { format: "text" };
89
- function makeGitSuccess(stdout = "") {
90
- return Promise.resolve({ stdout, stderr: "" });
91
- }
92
- function setupHappyPath() {
93
- mockFetchBatch.mockResolvedValue({ data: makeBatch([makeThread()]) });
94
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
95
- mockReadFile.mockResolvedValue(FILE_CONTENT);
96
- mockExecFile.mockImplementation((cmd, args) => {
97
- if (cmd === "git" && args[0] === "rev-parse")
98
- return makeGitSuccess("headsha\n");
99
- if (cmd === "git" && args[0] === "status")
100
- return makeGitSuccess(""); // file is clean
101
- throw new Error(`Unexpected execFile call: ${cmd} ${args.join(" ")}`);
102
- });
103
- }
104
- // ---------------------------------------------------------------------------
105
- // Validation
106
- // ---------------------------------------------------------------------------
107
- export function registerHooks() {
108
- beforeEach(() => {
109
- mockLoadConfig.mockReturnValue({});
110
- });
111
- }
112
- export { FILE_CONTENT, GLOBAL_OPTS, fetchPrBatch, getCurrentBranch, getCurrentPrNumber, makeBatch, makeGitSuccess, makeThread, mockExecFile, mockFetchBatch, mockGetCurrentBranch, mockGetCurrentPrNumber, mockLoadConfig, mockReadFile, readFile, runCommitSuggestion, setupHappyPath, };
@@ -1,24 +0,0 @@
1
- import { vi } from "vitest";
2
- import { NOW, makeOpts } from "./iterate-test-support.mjs";
3
- import { readStallState, writeStallState } from "../state/iterate-stall.mjs";
4
- const mockReadStallState = vi.mocked(readStallState);
5
- const mockWriteStallState = vi.mocked(writeStallState);
6
- const STALL_TIMEOUT_S = 1800;
7
- const RESOLUTION_ONLY_THREAD = {
8
- id: "thread-resolution-only",
9
- isResolved: false,
10
- isOutdated: true,
11
- isMinimized: false,
12
- path: "src/old.mts",
13
- line: null,
14
- startLine: null,
15
- author: "reviewer",
16
- authorType: "Unknown",
17
- body: "Already addressed on an old diff",
18
- url: "",
19
- createdAtUnix: NOW - 3600,
20
- };
21
- function makeOpts30mStall(overrides = {}) {
22
- return makeOpts({ stallTimeoutSeconds: STALL_TIMEOUT_S, noAutoMarkReady: true, ...overrides });
23
- }
24
- export { RESOLUTION_ONLY_THREAD, STALL_TIMEOUT_S, makeOpts30mStall, mockReadStallState, mockWriteStallState, };
@@ -1,150 +0,0 @@
1
- import { vi, beforeEach, afterEach } from "vitest";
2
- const mockFetch = vi.fn();
3
- vi.stubGlobal("fetch", mockFetch);
4
- const { mockExecFile } = vi.hoisted(() => ({ mockExecFile: vi.fn() }));
5
- vi.mock("node:child_process", () => ({
6
- execFile: (cmd, args, optsOrCb, maybeCb) => {
7
- const cb = typeof optsOrCb === "function" ? optsOrCb : maybeCb;
8
- mockExecFile(cmd, args)
9
- .then((result) => cb(null, result))
10
- .catch((err) => cb(err, { stdout: "", stderr: err.stderr ?? "" }));
11
- },
12
- }));
13
- vi.mock("./check.mts", () => ({ runCheck: vi.fn() }));
14
- vi.mock("./ready-delay.mts", () => ({ updateReadyDelay: vi.fn() }));
15
- vi.mock("../github/client.mts", () => ({ getCurrentPrNumber: vi.fn().mockResolvedValue(42) }));
16
- vi.mock("../state/fix-attempts.mts", () => ({
17
- readFixAttempts: vi.fn().mockResolvedValue(null),
18
- writeFixAttempts: vi.fn().mockResolvedValue(undefined),
19
- }));
20
- vi.mock("../state/iterate-stall.mts", () => ({
21
- readStallState: vi.fn().mockResolvedValue(null),
22
- writeStallState: vi.fn().mockResolvedValue(undefined),
23
- clearStallState: vi.fn().mockResolvedValue(undefined),
24
- }));
25
- const { mockLoadConfig } = vi.hoisted(() => ({ mockLoadConfig: vi.fn() }));
26
- vi.mock("../config/load.mts", () => ({ loadConfig: mockLoadConfig }));
27
- import { runCheck } from "./check.mjs";
28
- import { updateReadyDelay } from "./ready-delay.mjs";
29
- import { getCurrentPrNumber } from "../github/client.mjs";
30
- import { readFixAttempts, writeFixAttempts } from "../state/fix-attempts.mjs";
31
- import { clearStallState, readStallState, writeStallState } from "../state/iterate-stall.mjs";
32
- import { buildEscalateHumanMessage, buildEscalateSuggestion, checkEscalateTriggers, } from "./iterate/escalate.mjs";
33
- import { buildRelevantChecks, buildWaitLog, getCurrentHeadSha } from "./iterate/helpers.mjs";
34
- const mockRunCheck = vi.mocked(runCheck);
35
- const mockUpdateReadyDelay = vi.mocked(updateReadyDelay);
36
- const mockGetCurrentPrNumber = vi.mocked(getCurrentPrNumber);
37
- const mockReadFixAttempts = vi.mocked(readFixAttempts);
38
- const mockWriteFixAttempts = vi.mocked(writeFixAttempts);
39
- const mockReadStallState = vi.mocked(readStallState);
40
- const mockWriteStallState = vi.mocked(writeStallState);
41
- const mockClearStallState = vi.mocked(clearStallState);
42
- const NOW = 1_700_000_000;
43
- const READY_STATE_DEFAULT = { isReady: true, shouldCancel: false, remainingSeconds: 300 };
44
- function makeReport(overrides = {}) {
45
- return {
46
- pr: 42,
47
- nodeId: "PR_kgDOAAA",
48
- repo: "owner/repo",
49
- status: "READY",
50
- baseBranch: "main",
51
- mergeStatus: {
52
- status: "CLEAN",
53
- state: "OPEN",
54
- isDraft: false,
55
- mergeable: "MERGEABLE",
56
- reviewDecision: "APPROVED",
57
- blockingBotReviewInProgress: false,
58
- mergeStateStatus: "CLEAN",
59
- },
60
- checks: {
61
- passing: [
62
- {
63
- name: "ci",
64
- status: "COMPLETED",
65
- conclusion: "SUCCESS",
66
- detailsUrl: "https://github.com/owner/repo/actions/runs/1",
67
- event: "pull_request",
68
- runId: "run-1",
69
- category: "passed",
70
- },
71
- ],
72
- failing: [],
73
- inProgress: [],
74
- skipped: [],
75
- filtered: [],
76
- filteredNames: [],
77
- blockedByFilteredCheck: false,
78
- },
79
- threads: {
80
- actionable: [],
81
- resolutionOnly: [],
82
- autoResolved: [],
83
- autoResolveErrors: [],
84
- firstLook: [],
85
- },
86
- comments: { actionable: [], firstLook: [] },
87
- changesRequestedReviews: [],
88
- reviewSummaries: [],
89
- firstLookSummaries: [],
90
- editedSummaries: [],
91
- approvedReviews: [],
92
- branchProtection: null,
93
- ...overrides,
94
- };
95
- }
96
- function makeOpts(overrides = {}) {
97
- return { prNumber: 42, format: "json", readyDelaySeconds: 600, ...overrides };
98
- }
99
- function makeReview(id, author, body) {
100
- return { id, author, authorType: "Unknown", body };
101
- }
102
- function defaultConfig() {
103
- return {
104
- iterate: {
105
- fixAttemptsPerThread: 3,
106
- stallTimeoutMinutes: 60,
107
- minimizeApprovals: false,
108
- minimizeComments: "all",
109
- },
110
- watch: { readyDelayMinutes: 10 },
111
- resolve: {
112
- concurrency: 4,
113
- shaPoll: { intervalMs: 2000, maxAttempts: 10 },
114
- fetchReviewSummaries: true,
115
- },
116
- checks: { ciTriggerEvents: ["pull_request", "pull_request_target"] },
117
- mergeStatus: { blockingReviewerLogins: ["copilot"] },
118
- actions: { autoResolveOutdated: true, autoMarkReady: true, commitSuggestions: true },
119
- };
120
- }
121
- function registerIterateHooks(config = defaultConfig) {
122
- beforeEach(() => {
123
- vi.clearAllMocks();
124
- mockLoadConfig.mockReturnValue(config());
125
- process.env["GH_TOKEN"] = "test-token";
126
- mockExecFile.mockImplementation((cmd, args) => {
127
- if (cmd === "git" && args[0] === "rev-parse") {
128
- return Promise.resolve({ stdout: "abc123", stderr: "" });
129
- }
130
- return Promise.resolve({ stdout: "", stderr: "" });
131
- });
132
- mockFetch.mockResolvedValue({
133
- ok: true,
134
- status: 200,
135
- headers: new Headers({ "content-type": "application/json" }),
136
- json: () => Promise.resolve({ data: {} }),
137
- text: () => Promise.resolve('{"data":{}}'),
138
- });
139
- vi.useFakeTimers();
140
- vi.setSystemTime(NOW * 1000);
141
- mockUpdateReadyDelay.mockResolvedValue(READY_STATE_DEFAULT);
142
- mockReadFixAttempts.mockResolvedValue(null);
143
- mockWriteFixAttempts.mockResolvedValue(undefined);
144
- mockReadStallState.mockResolvedValue(null);
145
- mockWriteStallState.mockResolvedValue(undefined);
146
- mockClearStallState.mockResolvedValue(undefined);
147
- });
148
- afterEach(() => vi.useRealTimers());
149
- }
150
- export { NOW, buildEscalateHumanMessage, buildEscalateSuggestion, buildRelevantChecks, buildWaitLog, checkEscalateTriggers, defaultConfig, getCurrentHeadSha, makeOpts, makeReport, makeReview, mockExecFile, mockFetch, mockGetCurrentPrNumber, mockClearStallState, mockLoadConfig, mockReadFixAttempts, mockReadStallState, mockRunCheck, mockUpdateReadyDelay, mockWriteFixAttempts, mockWriteStallState, registerIterateHooks, };
@@ -1,18 +0,0 @@
1
- import { NOW } from "./iterate-test-support.mjs";
2
- export function makeThread(overrides = {}) {
3
- return {
4
- id: "thread-1",
5
- isResolved: false,
6
- isOutdated: false,
7
- isMinimized: false,
8
- path: "src/foo.mts",
9
- line: 10,
10
- startLine: null,
11
- author: "reviewer",
12
- authorType: "Unknown",
13
- body: "Fix this",
14
- url: "",
15
- createdAtUnix: NOW - 3600,
16
- ...overrides,
17
- };
18
- }
@@ -1,127 +0,0 @@
1
- import { vi, beforeEach, afterEach } from "vitest";
2
- const mockFetch = vi.fn();
3
- vi.stubGlobal("fetch", mockFetch);
4
- const { mockExecFile } = vi.hoisted(() => ({ mockExecFile: vi.fn() }));
5
- vi.mock("node:child_process", () => ({
6
- execFile: (cmd, args, optsOrCb, maybeCb) => {
7
- const cb = typeof optsOrCb === "function" ? optsOrCb : maybeCb;
8
- mockExecFile(cmd, args)
9
- .then((result) => cb(null, result))
10
- .catch((err) => cb(err, { stdout: "", stderr: err.stderr ?? "" }));
11
- },
12
- }));
13
- vi.mock("./check.mts", () => ({ runCheck: vi.fn() }));
14
- vi.mock("./ready-delay.mts", () => ({ updateReadyDelay: vi.fn() }));
15
- vi.mock("../github/client.mts", () => ({
16
- getCurrentPrNumber: vi.fn().mockResolvedValue(42),
17
- }));
18
- vi.mock("../state/fix-attempts.mts", () => ({
19
- readFixAttempts: vi.fn().mockResolvedValue(null),
20
- writeFixAttempts: vi.fn().mockResolvedValue(undefined),
21
- }));
22
- vi.mock("../state/iterate-stall.mts", () => ({
23
- readStallState: vi.fn().mockResolvedValue(null),
24
- writeStallState: vi.fn().mockResolvedValue(undefined),
25
- }));
26
- vi.mock("../state/seen-comments.mts", async (importOriginal) => {
27
- const actual = await importOriginal();
28
- return {
29
- ...actual,
30
- markSeen: vi.fn().mockResolvedValue(undefined),
31
- };
32
- });
33
- const { mockLoadConfig } = vi.hoisted(() => ({ mockLoadConfig: vi.fn() }));
34
- vi.mock("../config/load.mts", () => ({ loadConfig: mockLoadConfig }));
35
- import { runIterate } from "./iterate/index.mjs";
36
- import { runCheck } from "./check.mjs";
37
- import { updateReadyDelay } from "./ready-delay.mjs";
38
- import { readFixAttempts, writeFixAttempts } from "../state/fix-attempts.mjs";
39
- import { readStallState, writeStallState } from "../state/iterate-stall.mjs";
40
- import { markSeen } from "../state/seen-comments.mjs";
41
- const mockRunCheck = vi.mocked(runCheck);
42
- const mockUpdateReadyDelay = vi.mocked(updateReadyDelay);
43
- const mockReadFixAttempts = vi.mocked(readFixAttempts);
44
- const mockWriteFixAttempts = vi.mocked(writeFixAttempts);
45
- const mockReadStallState = vi.mocked(readStallState);
46
- const mockWriteStallState = vi.mocked(writeStallState);
47
- const mockMarkSeen = vi.mocked(markSeen);
48
- function makeReport(overrides = {}) {
49
- return {
50
- pr: 42,
51
- nodeId: "PR_kgDOAAA",
52
- repo: "owner/repo",
53
- status: "READY",
54
- baseBranch: "main",
55
- branchProtection: null,
56
- mergeStatus: {
57
- status: "CLEAN",
58
- state: "OPEN",
59
- isDraft: false,
60
- mergeable: "MERGEABLE",
61
- reviewDecision: "APPROVED",
62
- blockingBotReviewInProgress: false,
63
- mergeStateStatus: "CLEAN",
64
- },
65
- checks: {
66
- passing: [],
67
- failing: [],
68
- inProgress: [],
69
- skipped: [],
70
- filtered: [],
71
- filteredNames: [],
72
- blockedByFilteredCheck: false,
73
- },
74
- threads: {
75
- actionable: [],
76
- resolutionOnly: [],
77
- autoResolved: [],
78
- autoResolveErrors: [],
79
- firstLook: [],
80
- },
81
- comments: { actionable: [], firstLook: [] },
82
- changesRequestedReviews: [],
83
- reviewSummaries: [],
84
- firstLookSummaries: [],
85
- editedSummaries: [],
86
- approvedReviews: [],
87
- ...overrides,
88
- };
89
- }
90
- function makeOpts(overrides = {}) {
91
- return {
92
- prNumber: 42,
93
- format: "json",
94
- readyDelaySeconds: 600,
95
- ...overrides,
96
- };
97
- }
98
- export function registerHooks() {
99
- beforeEach(() => {
100
- vi.clearAllMocks();
101
- mockExecFile.mockResolvedValue({ stdout: "abc1234\n", stderr: "" });
102
- mockLoadConfig.mockReturnValue({
103
- iterate: {
104
- fixAttemptsPerThread: 3,
105
- stallTimeoutMinutes: 60,
106
- minimizeApprovals: false,
107
- },
108
- watch: { readyDelayMinutes: 10 },
109
- resolve: {
110
- concurrency: 4,
111
- shaPoll: { intervalMs: 2000, maxAttempts: 10 },
112
- fetchReviewSummaries: true,
113
- },
114
- checks: { ciTriggerEvents: ["pull_request"] },
115
- mergeStatus: { blockingReviewerLogins: [] },
116
- actions: { autoResolveOutdated: false, autoMarkReady: false, commitSuggestions: false },
117
- });
118
- mockReadFixAttempts.mockResolvedValue(null);
119
- mockWriteFixAttempts.mockResolvedValue(undefined);
120
- mockReadStallState.mockResolvedValue(null);
121
- mockWriteStallState.mockResolvedValue(undefined);
122
- });
123
- afterEach(() => {
124
- vi.restoreAllMocks();
125
- });
126
- }
127
- export { makeOpts, makeReport, mockExecFile, mockFetch, mockLoadConfig, mockReadFixAttempts, mockReadStallState, mockMarkSeen, mockRunCheck, mockUpdateReadyDelay, mockWriteFixAttempts, mockWriteStallState, readFixAttempts, readStallState, runCheck, runIterate, updateReadyDelay, writeFixAttempts, writeStallState, };
@@ -1,77 +0,0 @@
1
- import { vi, beforeEach, afterEach } from "vitest";
2
- vi.mock("./iterate/index.mts", () => ({ runIterate: vi.fn() }));
3
- import { runIterate } from "./iterate/index.mjs";
4
- const mockRunIterate = vi.mocked(runIterate);
5
- function makeWaitResult(overrides = {}) {
6
- return {
7
- action: "wait",
8
- pr: 42,
9
- repo: "owner/repo",
10
- status: "IN_PROGRESS",
11
- state: "OPEN",
12
- mergeStateStatus: "BLOCKED",
13
- mergeStatus: "BLOCKED",
14
- reviewDecision: "REVIEW_REQUIRED",
15
- blockingBotReviewInProgress: false,
16
- isDraft: false,
17
- shouldCancel: false,
18
- remainingSeconds: 0,
19
- summary: { passing: 2, failing: 0, inProgress: 1, skipped: 0, filtered: 0 },
20
- baseBranch: "main",
21
- checks: [],
22
- log: "WAIT: 2 passing, 1 in-progress",
23
- ...overrides,
24
- };
25
- }
26
- function makeCancelResult() {
27
- return {
28
- action: "cancel",
29
- pr: 42,
30
- repo: "owner/repo",
31
- status: "READY",
32
- state: "MERGED",
33
- mergeStateStatus: "CLEAN",
34
- mergeStatus: "CLEAN",
35
- reviewDecision: "APPROVED",
36
- blockingBotReviewInProgress: false,
37
- isDraft: false,
38
- shouldCancel: true,
39
- remainingSeconds: 0,
40
- summary: { passing: 3, failing: 0, inProgress: 0, skipped: 0, filtered: 0 },
41
- baseBranch: "main",
42
- checks: [],
43
- reason: "merged",
44
- log: "CANCEL: PR #42 is merged — stopping",
45
- };
46
- }
47
- function makeMarkReadyResult() {
48
- return {
49
- action: "mark_ready",
50
- pr: 42,
51
- repo: "owner/repo",
52
- status: "READY",
53
- state: "OPEN",
54
- mergeStateStatus: "CLEAN",
55
- mergeStatus: "CLEAN",
56
- reviewDecision: "APPROVED",
57
- blockingBotReviewInProgress: false,
58
- isDraft: false,
59
- shouldCancel: false,
60
- remainingSeconds: 0,
61
- summary: { passing: 3, failing: 0, inProgress: 0, skipped: 0, filtered: 0 },
62
- baseBranch: "main",
63
- checks: [],
64
- markedReady: true,
65
- log: "MARKED READY: PR #42 converted from draft to ready for review",
66
- };
67
- }
68
- function registerPollHooks() {
69
- beforeEach(() => {
70
- vi.clearAllMocks();
71
- vi.useFakeTimers();
72
- });
73
- afterEach(() => {
74
- vi.useRealTimers();
75
- });
76
- }
77
- export { mockRunIterate, makeWaitResult, makeCancelResult, makeMarkReadyResult, registerPollHooks };