Skip to content

Commit 90368c7

Browse files
committed
test: add full retry test
1 parent 83057f1 commit 90368c7

File tree

3 files changed

+168
-32
lines changed

3 files changed

+168
-32
lines changed
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2+
3+
exports[`return-dispatch > handleAction > getRunIdAndUrl > called fetchWorkflowRunIds with the provided workflowId and branch 1`] = `
4+
[
5+
"Attempting to identify Run ID for workflow.yml (123)",
6+
]
7+
`;
8+
9+
exports[`return-dispatch > handleAction > getRunIdAndUrl > called fetchWorkflowRunIds with the provided workflowId and branch 2`] = `
10+
[
11+
"Attempting to get step names for Run IDs: [0]",
12+
]
13+
`;
14+
15+
exports[`return-dispatch > handleAction > getRunIdAndUrl > called fetchWorkflowRunIds with the provided workflowId and branch 3`] = `
16+
[
17+
"Attempting to identify run ID from steps...",
18+
]
19+
`;
20+
21+
exports[`return-dispatch > handleAction > getRunIdAndUrl > should call retryOrTimeout with the larger workflowTimeoutMs timeout value 1`] = `
22+
[
23+
"Attempting to identify Run ID for workflow.yml (123)",
24+
]
25+
`;
26+
27+
exports[`return-dispatch > handleAction > getRunIdAndUrl > should call retryOrTimeout with the larger workflowTimeoutMs timeout value 2`] = `
28+
[
29+
"Attempting to get step names for Run IDs: [0]",
30+
]
31+
`;
32+
33+
exports[`return-dispatch > handleAction > getRunIdAndUrl > should call retryOrTimeout with the larger workflowTimeoutMs timeout value 3`] = `
34+
[
35+
"Attempting to identify run ID from steps...",
36+
]
37+
`;
38+
39+
exports[`return-dispatch > handleAction > getRunIdAndUrl > should retry until an ID is found 1`] = `"Attempting to identify run ID from steps..."`;
40+
41+
exports[`return-dispatch > handleAction > getRunIdAndUrl > should retry until an ID is found 2`] = `"No Run IDs found for workflow, attempt 1..."`;
42+
43+
exports[`return-dispatch > handleAction > getRunIdAndUrl > should retry until an ID is found 3`] = `"Attempting to identify Run ID for workflow.yml (123)"`;
44+
45+
exports[`return-dispatch > handleAction > getRunIdAndUrl > should retry until an ID is found 4`] = `"No Run IDs found for workflow, attempt 2..."`;
46+
47+
exports[`return-dispatch > handleAction > getRunIdAndUrl > should retry until an ID is found 5`] = `"Attempting to get step names for Run IDs: [0]"`;
48+
49+
exports[`return-dispatch > handleAction > getRunIdAndUrl > should return the ID when found 1`] = `
50+
[
51+
"Attempting to identify Run ID for workflow.yml (123)",
52+
]
53+
`;
54+
55+
exports[`return-dispatch > handleAction > getRunIdAndUrl > should return the ID when found 2`] = `
56+
[
57+
"Attempting to get step names for Run IDs: [0]",
58+
]
59+
`;
60+
61+
exports[`return-dispatch > handleAction > getRunIdAndUrl > should return the ID when found 3`] = `
62+
[
63+
"Attempting to identify run ID from steps...",
64+
]
65+
`;

src/return-dispatch.spec.ts

Lines changed: 102 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import {
2525
type GetRunIdAndUrlOpts,
2626
} from "./return-dispatch.ts";
2727
import { mockLoggingFunctions } from "./test-utils/logging.mock.ts";
28-
import type { BranchNameResult } from "./utils.ts";
28+
import * as utils from "./utils.ts";
2929

3030
vi.mock("@actions/core");
3131
vi.mock("./api.ts");
@@ -39,6 +39,16 @@ describe("return-dispatch", () => {
3939
assertNoneCalled,
4040
} = mockLoggingFunctions();
4141

42+
function resetLogMocks(): void {
43+
for (const logMock of [
44+
coreDebugLogMock,
45+
coreInfoLogMock,
46+
coreErrorLogMock,
47+
]) {
48+
logMock.mockReset();
49+
}
50+
}
51+
4252
afterAll(() => {
4353
vi.restoreAllMocks();
4454
});
@@ -457,7 +467,7 @@ describe("return-dispatch", () => {
457467
const distinctId = crypto.randomUUID();
458468
const workflow = "workflow.yml";
459469
const workflowId = 123;
460-
const branch: BranchNameResult = Object.freeze({
470+
const branch: utils.BranchNameResult = Object.freeze({
461471
isTag: false,
462472
ref: "/refs/heads/main",
463473
branchName: "main",
@@ -481,6 +491,7 @@ describe("return-dispatch", () => {
481491
typeof api.fetchWorkflowRunUrl
482492
>;
483493
let apiRetryOrTimeoutMock: MockInstance<typeof api.retryOrTimeout>;
494+
let utilSleepMock: MockInstance<typeof utils.sleep>;
484495

485496
beforeEach(() => {
486497
vi.useFakeTimers();
@@ -492,6 +503,12 @@ describe("return-dispatch", () => {
492503
);
493504
apiFetchWorkflowRunUrlMock = vi.spyOn(api, "fetchWorkflowRunUrl");
494505
apiRetryOrTimeoutMock = vi.spyOn(api, "retryOrTimeout");
506+
507+
utilSleepMock = vi
508+
.spyOn(utils, "sleep")
509+
.mockImplementation(
510+
(ms: number) => new Promise((resolve) => setTimeout(resolve, ms)),
511+
);
495512
});
496513

497514
afterEach(() => {
@@ -526,21 +543,16 @@ describe("return-dispatch", () => {
526543
expect(apiRetryOrTimeoutMock).toHaveBeenCalledOnce();
527544
expect(apiFetchWorkflowRunJobStepsMock).toHaveBeenCalledOnce();
528545
expect(apiFetchWorkflowRunIdsMock).not.toHaveBeenCalled();
546+
expect(utilSleepMock).not.toHaveBeenCalled();
529547

530548
// Logging
531549
assertOnlyCalled(coreDebugLogMock, coreInfoLogMock);
532550
expect(coreDebugLogMock).toHaveBeenCalledTimes(2);
533-
expect(coreDebugLogMock.mock.calls[0]?.[0]).toMatchInlineSnapshot(
534-
`"Attempting to identify Run ID for workflow.yml (123)"`,
535-
);
536-
expect(coreDebugLogMock.mock.calls[1]?.[0]).toMatchInlineSnapshot(
537-
`"Attempting to get step names for Run IDs: [0]"`,
538-
);
551+
expect(coreDebugLogMock.mock.calls[0]).toMatchSnapshot();
552+
expect(coreDebugLogMock.mock.calls[1]).toMatchSnapshot();
539553

540554
expect(coreInfoLogMock).toHaveBeenCalledOnce();
541-
expect(coreInfoLogMock.mock.calls[0]?.[0]).toMatchInlineSnapshot(
542-
`"Attempt to identify run ID from steps..."`,
543-
);
555+
expect(coreInfoLogMock.mock.calls[0]).toMatchSnapshot();
544556
});
545557

546558
it("should call retryOrTimeout with the larger WORKFLOW_FETCH_TIMEOUT_MS timeout value", async () => {
@@ -567,6 +579,7 @@ describe("return-dispatch", () => {
567579
);
568580
expect(apiFetchWorkflowRunJobStepsMock).toHaveBeenCalledOnce();
569581
expect(apiFetchWorkflowRunIdsMock).not.toHaveBeenCalled();
582+
expect(utilSleepMock).not.toHaveBeenCalled();
570583

571584
// Logging
572585
assertOnlyCalled(coreDebugLogMock, coreInfoLogMock);
@@ -580,7 +593,7 @@ describe("return-dispatch", () => {
580593

581594
expect(coreInfoLogMock).toHaveBeenCalledOnce();
582595
expect(coreInfoLogMock.mock.calls[0]?.[0]).toMatchInlineSnapshot(
583-
`"Attempt to identify run ID from steps..."`,
596+
`"Attempting to identify run ID from steps..."`,
584597
);
585598
});
586599

@@ -608,21 +621,16 @@ describe("return-dispatch", () => {
608621
);
609622
expect(apiFetchWorkflowRunJobStepsMock).toHaveBeenCalledOnce();
610623
expect(apiFetchWorkflowRunIdsMock).not.toHaveBeenCalled();
624+
expect(utilSleepMock).not.toHaveBeenCalled();
611625

612626
// Logging
613627
assertOnlyCalled(coreDebugLogMock, coreInfoLogMock);
614628
expect(coreDebugLogMock).toHaveBeenCalledTimes(2);
615-
expect(coreDebugLogMock.mock.calls[0]?.[0]).toMatchInlineSnapshot(
616-
`"Attempting to identify Run ID for workflow.yml (123)"`,
617-
);
618-
expect(coreDebugLogMock.mock.calls[1]?.[0]).toMatchInlineSnapshot(
619-
`"Attempting to get step names for Run IDs: [0]"`,
620-
);
629+
expect(coreDebugLogMock.mock.calls[0]).toMatchSnapshot();
630+
expect(coreDebugLogMock.mock.calls[1]).toMatchSnapshot();
621631

622632
expect(coreInfoLogMock).toHaveBeenCalledOnce();
623-
expect(coreInfoLogMock.mock.calls[0]?.[0]).toMatchInlineSnapshot(
624-
`"Attempt to identify run ID from steps..."`,
625-
);
633+
expect(coreInfoLogMock.mock.calls[0]).toMatchSnapshot();
626634
});
627635

628636
it("called fetchWorkflowRunIds with the provided workflowId and branch", async () => {
@@ -653,20 +661,83 @@ describe("return-dispatch", () => {
653661
// Logging
654662
assertOnlyCalled(coreDebugLogMock, coreInfoLogMock);
655663
expect(coreDebugLogMock).toHaveBeenCalledTimes(2);
656-
expect(coreDebugLogMock.mock.calls[0]?.[0]).toMatchInlineSnapshot(
657-
`"Attempting to identify Run ID for workflow.yml (123)"`,
658-
);
659-
expect(coreDebugLogMock.mock.calls[1]?.[0]).toMatchInlineSnapshot(
660-
`"Attempting to get step names for Run IDs: [0]"`,
661-
);
664+
expect(coreDebugLogMock.mock.calls[0]).toMatchSnapshot();
665+
expect(coreDebugLogMock.mock.calls[1]).toMatchSnapshot();
662666

663667
expect(coreInfoLogMock).toHaveBeenCalledOnce();
664-
expect(coreInfoLogMock.mock.calls[0]?.[0]).toMatchInlineSnapshot(
665-
`"Attempt to identify run ID from steps..."`,
666-
);
668+
expect(coreInfoLogMock.mock.calls[0]).toMatchSnapshot();
667669
});
668670

669-
it("should retry until an ID is found");
671+
it("should retry until an ID is found", async () => {
672+
const runId = 0;
673+
const runUrl = "test-url";
674+
apiRetryOrTimeoutMock
675+
.mockResolvedValue({
676+
success: true,
677+
value: [runId],
678+
})
679+
.mockResolvedValueOnce({ success: true, value: [] })
680+
.mockResolvedValueOnce({ success: true, value: [] });
681+
apiFetchWorkflowRunJobStepsMock.mockResolvedValue([distinctId]);
682+
apiFetchWorkflowRunUrlMock.mockResolvedValue(runUrl);
683+
vi.spyOn(
684+
constants,
685+
"WORKFLOW_JOB_STEPS_RETRY_MS",
686+
"get",
687+
).mockReturnValue(5000);
688+
689+
const getRunIdAndUrlPromise = getRunIdAndUrl({
690+
...defaultOpts,
691+
workflowTimeoutMs: 60 * 60 * 1000,
692+
});
693+
694+
// First attempt
695+
expect(apiRetryOrTimeoutMock).toHaveBeenCalledOnce();
696+
await vi.advanceTimersByTimeAsync(1); // deplete queue
697+
assertOnlyCalled(coreDebugLogMock, coreInfoLogMock);
698+
699+
expect(coreInfoLogMock).toHaveBeenCalledTimes(2);
700+
expect(coreInfoLogMock.mock.calls[0]?.[0]).toMatchSnapshot();
701+
expect(coreInfoLogMock.mock.calls[1]?.[0]).toMatchSnapshot();
702+
703+
expect(coreDebugLogMock).toHaveBeenCalledTimes(1);
704+
expect(coreDebugLogMock.mock.calls[0]?.[0]).toMatchSnapshot();
705+
resetLogMocks();
706+
707+
// Sleep should be called
708+
expect(utilSleepMock).toHaveBeenCalledOnce();
709+
expect(utilSleepMock).toHaveBeenCalledWith(5000);
710+
await vi.advanceTimersByTimeAsync(5000);
711+
712+
// Second attempt
713+
expect(apiRetryOrTimeoutMock).toHaveBeenCalledTimes(2);
714+
await vi.advanceTimersByTimeAsync(1); // deplete queue
715+
assertOnlyCalled(coreInfoLogMock);
716+
717+
expect(coreInfoLogMock).toHaveBeenCalledOnce();
718+
expect(coreInfoLogMock.mock.calls[0]?.[0]).toMatchSnapshot();
719+
resetLogMocks();
720+
await vi.advanceTimersByTimeAsync(5000);
721+
722+
// Third attempt
723+
expect(apiRetryOrTimeoutMock).toHaveBeenCalledTimes(3);
724+
await vi.advanceTimersByTimeAsync(1); // deplete queue
725+
assertOnlyCalled(coreDebugLogMock);
726+
727+
expect(coreDebugLogMock).toHaveBeenCalledOnce();
728+
expect(coreDebugLogMock.mock.calls[0]?.[0]).toMatchSnapshot();
729+
resetLogMocks();
730+
await vi.advanceTimersByTimeAsync(5000);
731+
732+
// Result
733+
const run = await getRunIdAndUrlPromise;
734+
if (!run.success) {
735+
expect.fail("expected call to succeed");
736+
}
737+
expect(run.value.id).toStrictEqual(runId);
738+
expect(run.value.url).toStrictEqual(runUrl);
739+
assertNoneCalled();
740+
});
670741

671742
it("should timeout when unable failing to get the run IDs");
672743

src/return-dispatch.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ export async function getRunIdAndUrl({
145145
workflowTimeoutMs,
146146
);
147147

148-
core.info("Attempt to identify run ID from steps...");
148+
core.info("Attempting to identify run ID from steps...");
149149
core.debug(`Attempting to identify Run ID for ${workflow} (${workflowId})`);
150150

151151
let attemptNo = 0;

0 commit comments

Comments
 (0)