Skip to content

Commit a5a0353

Browse files
[jsweep] Clean remove_trigger_label.cjs (#29251)
1 parent 0292d37 commit a5a0353

2 files changed

Lines changed: 221 additions & 2 deletions

File tree

actions/setup/js/remove_trigger_label.cjs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
/// <reference types="@actions/github-script" />
33

44
const { ERR_API, ERR_CONFIG } = require("./error_codes.cjs");
5+
const { getErrorMessage } = require("./error_helpers.cjs");
56

67
/**
78
* Remove the label that triggered this workflow from the issue, pull request, or discussion.
@@ -13,8 +14,6 @@ const { ERR_API, ERR_CONFIG } = require("./error_codes.cjs");
1314
async function main() {
1415
const labelNamesJSON = process.env.GH_AW_LABEL_NAMES;
1516

16-
const { getErrorMessage } = require("./error_helpers.cjs");
17-
1817
if (!labelNamesJSON) {
1918
core.setFailed(`${ERR_CONFIG}: Configuration error: GH_AW_LABEL_NAMES not specified.`);
2019
return;
Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
// @ts-check
2+
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
3+
const { ERR_CONFIG, ERR_API } = require("./error_codes.cjs");
4+
5+
const mockCore = {
6+
info: vi.fn(),
7+
warning: vi.fn(),
8+
error: vi.fn(),
9+
setFailed: vi.fn(),
10+
setOutput: vi.fn(),
11+
};
12+
13+
const mockGithub = {
14+
rest: {
15+
issues: {
16+
removeLabel: vi.fn(),
17+
},
18+
},
19+
graphql: vi.fn(),
20+
};
21+
22+
/** @type {any} */
23+
let mockContext = {
24+
eventName: "issues",
25+
repo: { owner: "testowner", repo: "testrepo" },
26+
payload: {
27+
label: { name: "ai-label", node_id: "LA_label1" },
28+
issue: { number: 42 },
29+
},
30+
};
31+
32+
global.core = mockCore;
33+
global.github = mockGithub;
34+
global.context = mockContext;
35+
36+
/** @returns {Promise<void>} */
37+
async function runScript() {
38+
const { main } = await import("./remove_trigger_label.cjs?" + Date.now());
39+
await main();
40+
}
41+
42+
describe("remove_trigger_label", () => {
43+
beforeEach(() => {
44+
vi.clearAllMocks();
45+
vi.resetModules();
46+
47+
process.env.GH_AW_LABEL_NAMES = JSON.stringify(["ai-label", "bot-run"]);
48+
49+
mockContext = {
50+
eventName: "issues",
51+
repo: { owner: "testowner", repo: "testrepo" },
52+
payload: {
53+
label: { name: "ai-label", node_id: "LA_label1" },
54+
issue: { number: 42 },
55+
},
56+
};
57+
global.context = mockContext;
58+
59+
mockGithub.rest.issues.removeLabel.mockResolvedValue({});
60+
mockGithub.graphql.mockResolvedValue({});
61+
});
62+
63+
afterEach(() => {
64+
delete process.env.GH_AW_LABEL_NAMES;
65+
});
66+
67+
describe("missing configuration", () => {
68+
it("should fail when GH_AW_LABEL_NAMES is not set", async () => {
69+
delete process.env.GH_AW_LABEL_NAMES;
70+
await runScript();
71+
expect(mockCore.setFailed).toHaveBeenCalledWith(expect.stringContaining(ERR_CONFIG));
72+
});
73+
74+
it("should fail when GH_AW_LABEL_NAMES is invalid JSON", async () => {
75+
process.env.GH_AW_LABEL_NAMES = "not-valid-json";
76+
await runScript();
77+
expect(mockCore.setFailed).toHaveBeenCalledWith(expect.stringContaining(ERR_CONFIG));
78+
});
79+
80+
it("should fail when GH_AW_LABEL_NAMES is not an array", async () => {
81+
process.env.GH_AW_LABEL_NAMES = JSON.stringify({ label: "ai-label" });
82+
await runScript();
83+
expect(mockCore.setFailed).toHaveBeenCalledWith(expect.stringContaining(ERR_CONFIG));
84+
});
85+
});
86+
87+
describe("workflow_dispatch event", () => {
88+
it("should skip label removal for workflow_dispatch", async () => {
89+
global.context = { ...mockContext, eventName: "workflow_dispatch", payload: {} };
90+
await runScript();
91+
expect(mockGithub.rest.issues.removeLabel).not.toHaveBeenCalled();
92+
expect(mockCore.setOutput).toHaveBeenCalledWith("label_name", "");
93+
});
94+
});
95+
96+
describe("no trigger label in payload", () => {
97+
it("should skip removal when payload has no label", async () => {
98+
global.context = { ...mockContext, payload: { issue: { number: 42 } } };
99+
await runScript();
100+
expect(mockGithub.rest.issues.removeLabel).not.toHaveBeenCalled();
101+
expect(mockCore.setOutput).toHaveBeenCalledWith("label_name", "");
102+
});
103+
});
104+
105+
describe("label not in configured list", () => {
106+
it("should skip removal when label is not configured", async () => {
107+
global.context = {
108+
...mockContext,
109+
payload: { label: { name: "random-label" }, issue: { number: 42 } },
110+
};
111+
await runScript();
112+
expect(mockGithub.rest.issues.removeLabel).not.toHaveBeenCalled();
113+
expect(mockCore.setOutput).toHaveBeenCalledWith("label_name", "random-label");
114+
});
115+
});
116+
117+
describe("issues event", () => {
118+
it("should remove label from issue", async () => {
119+
await runScript();
120+
expect(mockGithub.rest.issues.removeLabel).toHaveBeenCalledWith({
121+
owner: "testowner",
122+
repo: "testrepo",
123+
issue_number: 42,
124+
name: "ai-label",
125+
});
126+
expect(mockCore.setOutput).toHaveBeenCalledWith("label_name", "ai-label");
127+
});
128+
129+
it("should skip when issue number is missing", async () => {
130+
global.context = {
131+
...mockContext,
132+
payload: { label: { name: "ai-label" } },
133+
};
134+
await runScript();
135+
expect(mockGithub.rest.issues.removeLabel).not.toHaveBeenCalled();
136+
expect(mockCore.setOutput).toHaveBeenCalledWith("label_name", "ai-label");
137+
});
138+
});
139+
140+
describe("pull_request event", () => {
141+
it("should remove label from pull request", async () => {
142+
global.context = {
143+
...mockContext,
144+
eventName: "pull_request",
145+
payload: {
146+
label: { name: "ai-label" },
147+
pull_request: { number: 99 },
148+
},
149+
};
150+
await runScript();
151+
expect(mockGithub.rest.issues.removeLabel).toHaveBeenCalledWith({
152+
owner: "testowner",
153+
repo: "testrepo",
154+
issue_number: 99,
155+
name: "ai-label",
156+
});
157+
expect(mockCore.setOutput).toHaveBeenCalledWith("label_name", "ai-label");
158+
});
159+
160+
it("should skip when PR number is missing", async () => {
161+
global.context = {
162+
...mockContext,
163+
eventName: "pull_request",
164+
payload: { label: { name: "ai-label" } },
165+
};
166+
await runScript();
167+
expect(mockGithub.rest.issues.removeLabel).not.toHaveBeenCalled();
168+
});
169+
});
170+
171+
describe("discussion event", () => {
172+
it("should remove label from discussion via graphql", async () => {
173+
global.context = {
174+
...mockContext,
175+
eventName: "discussion",
176+
payload: {
177+
label: { name: "ai-label", node_id: "LA_label1" },
178+
discussion: { node_id: "D_disc1" },
179+
},
180+
};
181+
await runScript();
182+
expect(mockGithub.graphql).toHaveBeenCalledWith(
183+
expect.stringContaining("removeLabelsFromLabelable"),
184+
expect.objectContaining({
185+
labelableId: "D_disc1",
186+
labelIds: ["LA_label1"],
187+
})
188+
);
189+
expect(mockCore.setOutput).toHaveBeenCalledWith("label_name", "ai-label");
190+
});
191+
192+
it("should skip when discussion node_id is missing", async () => {
193+
global.context = {
194+
...mockContext,
195+
eventName: "discussion",
196+
payload: { label: { name: "ai-label" } },
197+
};
198+
await runScript();
199+
expect(mockGithub.graphql).not.toHaveBeenCalled();
200+
});
201+
});
202+
203+
describe("error handling", () => {
204+
it("should treat 404 error as already-removed (non-fatal)", async () => {
205+
const err = Object.assign(new Error("Not Found"), { status: 404 });
206+
mockGithub.rest.issues.removeLabel.mockRejectedValue(err);
207+
await runScript();
208+
expect(mockCore.setFailed).not.toHaveBeenCalled();
209+
expect(mockCore.setOutput).toHaveBeenCalledWith("label_name", "ai-label");
210+
});
211+
212+
it("should warn on non-404 API errors", async () => {
213+
const err = Object.assign(new Error("Server error"), { status: 500 });
214+
mockGithub.rest.issues.removeLabel.mockRejectedValue(err);
215+
await runScript();
216+
expect(mockCore.warning).toHaveBeenCalledWith(expect.stringContaining(ERR_API));
217+
expect(mockCore.setFailed).not.toHaveBeenCalled();
218+
});
219+
});
220+
});

0 commit comments

Comments
 (0)