Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit c2e9dc9

Browse files
authored
[EngSys] Add tests for GitHub Actions (Azure#32409)
- 100% code coverage
1 parent 5c3e400 commit c2e9dc9

File tree

5 files changed

+537
-24
lines changed

5 files changed

+537
-24
lines changed

.github/actions/update-labels/src/update-labels.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,26 @@ export default async function updateLabels({ github, context, core }) {
1919
run_id = run_id || inputs.run_id;
2020
}
2121

22+
await updateLabelsImpl({ owner, repo, issue_number, run_id, github, core });
23+
}
24+
25+
/**
26+
* @param {Object} params
27+
* @param {string} params.owner
28+
* @param {string} params.repo
29+
* @param {number} params.issue_number
30+
* @param {number} params.run_id
31+
* @param {(import("@octokit/core").Octokit & import("@octokit/plugin-rest-endpoint-methods/dist-types/types.js").Api & { paginate: import("@octokit/plugin-paginate-rest").PaginateInterface; })} params.github
32+
* @param {typeof import("@actions/core")} params.core
33+
*/
34+
export async function updateLabelsImpl({
35+
owner,
36+
repo,
37+
issue_number,
38+
run_id,
39+
github,
40+
core,
41+
}) {
2242
/** @type {string[]} */
2343
let artifactNames = [];
2444

Lines changed: 310 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,315 @@
1-
import { describe, expect, it } from "vitest";
2-
import updateLabels from "../src/update-labels.js";
1+
import { describe, expect, it, vi } from "vitest";
2+
import { createMockCore } from "../../../test/mocks.js";
3+
import updateLabels, { updateLabelsImpl } from "../src/update-labels.js";
4+
5+
describe("updateLabels", () => {
6+
it("loads inputs from env", async () => {
7+
const github = createMockGithub();
8+
github.rest.actions.listWorkflowRunArtifacts.mockResolvedValue({
9+
data: {
10+
artifacts: [{ name: "label-foo=true" }],
11+
},
12+
});
13+
14+
try {
15+
process.env.OWNER = "TestRepoOwnerLoginEnv";
16+
process.env.REPO = "TestRepoNameEnv";
17+
process.env.ISSUE_NUMBER = "123";
18+
process.env.RUN_ID = "456";
19+
20+
await expect(
21+
updateLabels({
22+
github: github,
23+
context: null,
24+
core: createMockCore(),
25+
}),
26+
).resolves.toBeUndefined();
27+
} finally {
28+
delete process.env.OWNER;
29+
delete process.env.REPO;
30+
delete process.env.ISSUE_NUMBER;
31+
delete process.env.RUN_ID;
32+
}
33+
34+
expect(github.rest.actions.listWorkflowRunArtifacts).toBeCalledWith({
35+
owner: "TestRepoOwnerLoginEnv",
36+
repo: "TestRepoNameEnv",
37+
run_id: 456,
38+
});
39+
expect(github.rest.issues.addLabels).toBeCalledWith({
40+
owner: "TestRepoOwnerLoginEnv",
41+
repo: "TestRepoNameEnv",
42+
issue_number: 123,
43+
labels: ["foo"],
44+
});
45+
expect(github.rest.issues.removeLabel).toBeCalledTimes(0);
46+
});
47+
48+
it("loads inputs from context", async () => {
49+
const github = createMockGithub();
50+
github.rest.actions.listWorkflowRunArtifacts.mockResolvedValue({
51+
data: {
52+
artifacts: [{ name: "label-foo=true" }],
53+
},
54+
});
55+
56+
const context = {
57+
eventName: "workflow_run",
58+
payload: {
59+
action: "completed",
60+
workflow_run: {
61+
head_sha: "abc123",
62+
id: 456,
63+
repository: {
64+
name: "TestRepoName",
65+
owner: {
66+
login: "TestRepoOwnerLogin",
67+
},
68+
},
69+
pull_requests: [{ number: 123 }],
70+
},
71+
},
72+
};
73+
74+
await expect(
75+
updateLabels({
76+
github: github,
77+
context: context,
78+
core: createMockCore(),
79+
}),
80+
).resolves.toBeUndefined();
81+
82+
expect(github.rest.actions.listWorkflowRunArtifacts).toBeCalledWith({
83+
owner: "TestRepoOwnerLogin",
84+
repo: "TestRepoName",
85+
run_id: 456,
86+
});
87+
expect(github.rest.issues.addLabels).toBeCalledWith({
88+
owner: "TestRepoOwnerLogin",
89+
repo: "TestRepoName",
90+
issue_number: 123,
91+
labels: ["foo"],
92+
});
93+
expect(github.rest.issues.removeLabel).toBeCalledTimes(0);
94+
});
95+
96+
it("loads inputs from env and context", async () => {
97+
const github = createMockGithub();
98+
github.rest.actions.listWorkflowRunArtifacts.mockResolvedValue({
99+
data: {
100+
artifacts: [{ name: "label-foo=true" }],
101+
},
102+
});
103+
104+
const context = {
105+
eventName: "workflow_run",
106+
payload: {
107+
action: "completed",
108+
workflow_run: {
109+
head_sha: "abc123",
110+
id: 456,
111+
repository: {
112+
name: "TestRepoName",
113+
owner: {
114+
login: "TestRepoOwnerLogin",
115+
},
116+
},
117+
pull_requests: [{ number: 123 }],
118+
},
119+
},
120+
};
121+
122+
try {
123+
process.env.OWNER = "TestRepoOwnerLoginEnv";
124+
process.env.REPO = "TestRepoNameEnv";
125+
126+
await expect(
127+
updateLabels({
128+
github: github,
129+
context: context,
130+
core: createMockCore(),
131+
}),
132+
).resolves.toBeUndefined();
133+
} finally {
134+
delete process.env.OWNER;
135+
delete process.env.REPO;
136+
}
137+
138+
expect(github.rest.actions.listWorkflowRunArtifacts).toBeCalledWith({
139+
owner: "TestRepoOwnerLoginEnv",
140+
repo: "TestRepoNameEnv",
141+
run_id: 456,
142+
});
143+
expect(github.rest.issues.addLabels).toBeCalledWith({
144+
owner: "TestRepoOwnerLoginEnv",
145+
repo: "TestRepoNameEnv",
146+
issue_number: 123,
147+
labels: ["foo"],
148+
});
149+
expect(github.rest.issues.removeLabel).toBeCalledTimes(0);
150+
});
151+
});
152+
153+
describe("updateLabelsImpl", () => {
154+
it("throws if no run_id", async () => {
155+
const github = createMockGithub();
3156

4-
describe("update-labels", () => {
5-
// TODO: Replace with better tests
6-
it("throws if inputs null", async () => {
7157
await expect(
8-
updateLabels({ github: null, context: null, core: null }),
158+
updateLabelsImpl({
159+
owner: "owner",
160+
repo: "repo",
161+
issue_number: 123,
162+
run_id: NaN,
163+
github: github,
164+
core: createMockCore(),
165+
}),
9166
).rejects.toThrow();
167+
168+
expect(github.rest.issues.addLabels).toBeCalledTimes(0);
169+
expect(github.rest.issues.removeLabel).toBeCalledTimes(0);
170+
});
171+
172+
it("adds and removes labels for artifacts", async () => {
173+
const github = createMockGithub();
174+
github.rest.actions.listWorkflowRunArtifacts.mockResolvedValue({
175+
data: {
176+
artifacts: [
177+
{ name: "label-foo=true" },
178+
{ name: "label-bar=true" },
179+
{ name: "label-baz=false" },
180+
{ name: "label-qux=false" },
181+
{ name: "ignoredNoEquals" },
182+
{ name: "=ignoredNoKey" },
183+
{ name: "label=ignoredKeyNotStartsWithLabelDash" },
184+
],
185+
},
186+
});
187+
188+
await expect(
189+
updateLabelsImpl({
190+
owner: "owner",
191+
repo: "repo",
192+
issue_number: 123,
193+
run_id: 456,
194+
github: github,
195+
core: createMockCore(),
196+
}),
197+
).resolves.toBeUndefined();
198+
199+
expect(github.rest.actions.listWorkflowRunArtifacts).toBeCalledWith({
200+
owner: "owner",
201+
repo: "repo",
202+
run_id: 456,
203+
});
204+
expect(github.rest.issues.addLabels).toBeCalledWith({
205+
owner: "owner",
206+
repo: "repo",
207+
issue_number: 123,
208+
labels: ["foo", "bar"],
209+
});
210+
expect(github.rest.issues.removeLabel).toBeCalledTimes(2);
211+
expect(github.rest.issues.removeLabel).nthCalledWith(1, {
212+
owner: "owner",
213+
repo: "repo",
214+
issue_number: 123,
215+
name: "baz",
216+
});
217+
expect(github.rest.issues.removeLabel).nthCalledWith(2, {
218+
owner: "owner",
219+
repo: "repo",
220+
issue_number: 123,
221+
name: "qux",
222+
});
223+
});
224+
225+
it("throws for invalid label value", async () => {
226+
const github = createMockGithub();
227+
github.rest.actions.listWorkflowRunArtifacts.mockResolvedValue({
228+
data: {
229+
artifacts: [
230+
{ name: "label-foo=true" },
231+
{ name: "label-bar=false" },
232+
{ name: "label-baz=invalid" },
233+
],
234+
},
235+
});
236+
237+
await expect(
238+
updateLabelsImpl({
239+
owner: "owner",
240+
repo: "repo",
241+
issue_number: 123,
242+
run_id: 456,
243+
github: github,
244+
core: createMockCore(),
245+
}),
246+
).rejects.toThrow();
247+
248+
expect(github.rest.actions.listWorkflowRunArtifacts).toBeCalledWith({
249+
owner: "owner",
250+
repo: "repo",
251+
run_id: 456,
252+
});
253+
254+
// Ensure no labels are added or removed if any are invalid
255+
expect(github.rest.issues.addLabels).toBeCalledTimes(0);
256+
expect(github.rest.issues.removeLabel).toBeCalledTimes(0);
10257
});
258+
259+
it.each([404, 500, 501])(
260+
"handles error removing label (%s)",
261+
async (status) => {
262+
const github = createMockGithub();
263+
github.rest.actions.listWorkflowRunArtifacts.mockResolvedValue({
264+
data: {
265+
artifacts: [{ name: "label-foo=false" }],
266+
},
267+
});
268+
github.rest.issues.removeLabel.mockRejectedValue({ status: status });
269+
270+
const updateLabelsImplPromise = updateLabelsImpl({
271+
owner: "owner",
272+
repo: "repo",
273+
issue_number: 123,
274+
run_id: 456,
275+
github: github,
276+
core: createMockCore(),
277+
});
278+
279+
if (status == 404) {
280+
await expect(updateLabelsImplPromise).resolves.toBeUndefined();
281+
} else {
282+
await expect(updateLabelsImplPromise).rejects.toThrow();
283+
}
284+
285+
expect(github.rest.actions.listWorkflowRunArtifacts).toBeCalledWith({
286+
owner: "owner",
287+
repo: "repo",
288+
run_id: 456,
289+
});
290+
expect(github.rest.issues.addLabels).toBeCalledTimes(0);
291+
expect(github.rest.issues.removeLabel).toBeCalledWith({
292+
owner: "owner",
293+
repo: "repo",
294+
issue_number: 123,
295+
name: "foo",
296+
});
297+
},
298+
);
11299
});
300+
301+
function createMockGithub() {
302+
return {
303+
rest: {
304+
actions: {
305+
listWorkflowRunArtifacts: vi
306+
.fn()
307+
.mockResolvedValue({ data: { artifacts: [] } }),
308+
},
309+
issues: {
310+
addLabels: vi.fn(),
311+
removeLabel: vi.fn(),
312+
},
313+
},
314+
};
315+
}

0 commit comments

Comments
 (0)