Skip to content

Commit e5a467b

Browse files
committed
refactor: finish handling and testing results for runs
1 parent 001d816 commit e5a467b

File tree

3 files changed

+379
-7
lines changed

3 files changed

+379
-7
lines changed

src/__snapshots__/await-remote-run.spec.ts.snap

+94
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,99 @@
11
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
22

3+
exports[`await-remote-run > getWorkflowRunResult > retries on request failures 1`] = `
4+
[
5+
[
6+
"Failed to fetch run state, attempt 1...",
7+
],
8+
[
9+
"Failed to fetch run state, attempt 2...",
10+
],
11+
]
12+
`;
13+
14+
exports[`await-remote-run > getWorkflowRunResult > returns a failure if the status is unsupported 1`] = `
15+
[
16+
[
17+
"Run status is unsupported: weird",
18+
],
19+
]
20+
`;
21+
22+
exports[`await-remote-run > getWorkflowRunResult > returns a failure if the status is unsupported 2`] = `
23+
[
24+
[
25+
"Please open an issue with this status value",
26+
],
27+
]
28+
`;
29+
30+
exports[`await-remote-run > getWorkflowRunResult > returns a failure on an unsupported conclusion 1`] = `
31+
[
32+
[
33+
"Run has failed with unsupported conclusion: weird",
34+
],
35+
]
36+
`;
37+
38+
exports[`await-remote-run > getWorkflowRunResult > returns a failure on an unsupported conclusion 2`] = `
39+
[
40+
[
41+
"Please open an issue with this conclusion value",
42+
],
43+
]
44+
`;
45+
46+
exports[`await-remote-run > getWorkflowRunResult > returns a failure on timeout conclusion 1`] = `
47+
[
48+
[
49+
"Run has timeout out",
50+
],
51+
]
52+
`;
53+
54+
exports[`await-remote-run > getWorkflowRunResult > returns a timeout 1`] = `
55+
[
56+
[
57+
"Run is in progress, attempt 1...",
58+
],
59+
[
60+
"Run is in progress, attempt 2...",
61+
],
62+
[
63+
"Run is in progress, attempt 3...",
64+
],
65+
[
66+
"Run is in progress, attempt 4...",
67+
],
68+
[
69+
"Run is in progress, attempt 5...",
70+
],
71+
[
72+
"Run is in progress, attempt 6...",
73+
],
74+
[
75+
"Run is in progress, attempt 7...",
76+
],
77+
[
78+
"Run is in progress, attempt 8...",
79+
],
80+
[
81+
"Run is in progress, attempt 9...",
82+
],
83+
[
84+
"Run is in progress, attempt 10...",
85+
],
86+
]
87+
`;
88+
89+
exports[`await-remote-run > getWorkflowRunResult > returns the conclusion if available 1`] = `
90+
[
91+
[
92+
"Run has failed with conclusion: skipped",
93+
],
94+
]
95+
`;
96+
397
exports[`await-remote-run > handleActionFail > should fetch and log failed jobs from the remote run 2`] = `
498
"Job First Job:
599
ID: 0

src/await-remote-run.spec.ts

+263
Original file line numberDiff line numberDiff line change
@@ -328,4 +328,267 @@ describe("await-remote-run", () => {
328328
expect(coreErrorLogMock.mock.calls[1]?.[0]).toMatchSnapshot();
329329
});
330330
});
331+
332+
describe("getWorkflowRunResult", () => {
333+
let apiFetchWorkflowRunStateMock: MockInstance<
334+
typeof api.fetchWorkflowRunState
335+
>;
336+
let apiRetryOnErrorMock: MockInstance<typeof api.retryOnError>;
337+
338+
beforeEach(() => {
339+
vi.useFakeTimers();
340+
341+
apiFetchWorkflowRunStateMock = vi.spyOn(api, "fetchWorkflowRunState");
342+
apiRetryOnErrorMock = vi.spyOn(api, "retryOnError");
343+
});
344+
345+
afterEach(() => {
346+
vi.useRealTimers();
347+
});
348+
349+
it("succeeds on the completion of a run", async () => {
350+
apiFetchWorkflowRunStateMock.mockResolvedValue({
351+
status: WorkflowRunStatus.Completed,
352+
conclusion: WorkflowRunConclusion.Success,
353+
});
354+
apiRetryOnErrorMock.mockImplementation(async (toTry) => ({
355+
success: true,
356+
value: await toTry(),
357+
}));
358+
359+
// Behaviour
360+
const getWorkflowRunResultPromise = getWorkflowRunResult({
361+
startTime: Date.now(),
362+
pollIntervalMs: 100,
363+
runId: 0,
364+
runTimeoutMs: 10_000,
365+
});
366+
await expect(getWorkflowRunResultPromise).resolves.not.toThrow();
367+
const result = await getWorkflowRunResultPromise;
368+
expect(result).toStrictEqual({
369+
success: true,
370+
value: {
371+
conclusion: WorkflowRunConclusion.Success,
372+
status: WorkflowRunStatus.Completed,
373+
},
374+
});
375+
376+
// Logging
377+
assertNoneCalled();
378+
});
379+
380+
it("retries on request failures", async () => {
381+
const pollIntervalMs = 100;
382+
apiFetchWorkflowRunStateMock.mockResolvedValue({
383+
status: WorkflowRunStatus.Completed,
384+
conclusion: WorkflowRunConclusion.Success,
385+
});
386+
apiRetryOnErrorMock
387+
.mockImplementation(async (toTry) => ({
388+
success: true,
389+
value: await toTry(),
390+
}))
391+
.mockResolvedValueOnce({ success: false, reason: "timeout" })
392+
.mockResolvedValueOnce({ success: false, reason: "timeout" });
393+
394+
// Behaviour
395+
const getWorkflowRunResultPromise = getWorkflowRunResult({
396+
startTime: Date.now(),
397+
pollIntervalMs: pollIntervalMs,
398+
runId: 0,
399+
runTimeoutMs: 10_000,
400+
});
401+
402+
// First iteration
403+
await vi.advanceTimersByTimeAsync(1);
404+
expect(coreDebugLogMock).toHaveBeenCalledOnce();
405+
406+
// Second iteration
407+
await vi.advanceTimersByTimeAsync(100);
408+
expect(coreDebugLogMock).toHaveBeenCalledTimes(2);
409+
410+
// Final iteration
411+
await vi.advanceTimersByTimeAsync(100);
412+
await expect(getWorkflowRunResultPromise).resolves.not.toThrow();
413+
const result = await getWorkflowRunResultPromise;
414+
expect(result).toStrictEqual({
415+
success: true,
416+
value: {
417+
conclusion: WorkflowRunConclusion.Success,
418+
status: WorkflowRunStatus.Completed,
419+
},
420+
});
421+
422+
assertOnlyCalled(coreDebugLogMock);
423+
expect(coreDebugLogMock).toBeCalledTimes(2);
424+
expect(coreDebugLogMock.mock.calls).toMatchSnapshot();
425+
});
426+
427+
it("returns the conclusion if available", async () => {
428+
const expectedConclusion = WorkflowRunConclusion.Skipped;
429+
apiFetchWorkflowRunStateMock.mockResolvedValue({
430+
status: WorkflowRunStatus.Completed,
431+
conclusion: expectedConclusion,
432+
});
433+
apiRetryOnErrorMock.mockImplementation(async (toTry) => ({
434+
success: true,
435+
value: await toTry(),
436+
}));
437+
438+
// Behaviour
439+
const getWorkflowRunResultPromise = getWorkflowRunResult({
440+
startTime: Date.now(),
441+
pollIntervalMs: 100,
442+
runId: 0,
443+
runTimeoutMs: 10_000,
444+
});
445+
await expect(getWorkflowRunResultPromise).resolves.not.toThrow();
446+
const result = await getWorkflowRunResultPromise;
447+
expect(result).toStrictEqual({
448+
success: true,
449+
value: {
450+
conclusion: expectedConclusion,
451+
status: WorkflowRunStatus.Completed,
452+
},
453+
});
454+
455+
// Logging
456+
assertOnlyCalled(coreErrorLogMock);
457+
expect(coreErrorLogMock).toHaveBeenCalledOnce();
458+
expect(coreErrorLogMock.mock.calls).toMatchSnapshot();
459+
});
460+
461+
it("returns a failure on timeout conclusion", async () => {
462+
const expectedConclusion = WorkflowRunConclusion.TimedOut;
463+
apiFetchWorkflowRunStateMock.mockResolvedValue({
464+
status: WorkflowRunStatus.Completed,
465+
conclusion: expectedConclusion,
466+
});
467+
apiRetryOnErrorMock.mockImplementation(async (toTry) => ({
468+
success: true,
469+
value: await toTry(),
470+
}));
471+
472+
// Behaviour
473+
const getWorkflowRunResultPromise = getWorkflowRunResult({
474+
startTime: Date.now(),
475+
pollIntervalMs: 100,
476+
runId: 0,
477+
runTimeoutMs: 10_000,
478+
});
479+
await expect(getWorkflowRunResultPromise).resolves.not.toThrow();
480+
const result = await getWorkflowRunResultPromise;
481+
expect(result).toStrictEqual({
482+
success: false,
483+
reason: "timeout",
484+
});
485+
486+
// Logging
487+
assertOnlyCalled(coreErrorLogMock);
488+
expect(coreErrorLogMock).toHaveBeenCalledOnce();
489+
expect(coreErrorLogMock.mock.calls).toMatchSnapshot();
490+
});
491+
492+
it("returns a failure on an unsupported conclusion", async () => {
493+
const expectedConclusion = "weird";
494+
apiFetchWorkflowRunStateMock.mockResolvedValue({
495+
status: WorkflowRunStatus.Completed,
496+
conclusion: expectedConclusion as any,
497+
});
498+
apiRetryOnErrorMock.mockImplementation(async (toTry) => ({
499+
success: true,
500+
value: await toTry(),
501+
}));
502+
503+
// Behaviour
504+
const getWorkflowRunResultPromise = getWorkflowRunResult({
505+
startTime: Date.now(),
506+
pollIntervalMs: 100,
507+
runId: 0,
508+
runTimeoutMs: 10_000,
509+
});
510+
await expect(getWorkflowRunResultPromise).resolves.not.toThrow();
511+
const result = await getWorkflowRunResultPromise;
512+
expect(result).toStrictEqual({
513+
success: false,
514+
reason: "unsupported",
515+
value: expectedConclusion,
516+
});
517+
518+
// Logging
519+
assertOnlyCalled(coreErrorLogMock, coreInfoLogMock);
520+
expect(coreErrorLogMock).toHaveBeenCalledOnce();
521+
expect(coreErrorLogMock.mock.calls).toMatchSnapshot();
522+
expect(coreInfoLogMock).toHaveBeenCalledOnce();
523+
expect(coreInfoLogMock.mock.calls).toMatchSnapshot();
524+
});
525+
526+
it("returns a failure if the status is unsupported", async () => {
527+
const expectedStatus = "weird";
528+
apiFetchWorkflowRunStateMock.mockResolvedValue({
529+
status: expectedStatus as any,
530+
conclusion: WorkflowRunConclusion.Failure,
531+
});
532+
apiRetryOnErrorMock.mockImplementation(async (toTry) => ({
533+
success: true,
534+
value: await toTry(),
535+
}));
536+
537+
// Behaviour
538+
const getWorkflowRunResultPromise = getWorkflowRunResult({
539+
startTime: Date.now(),
540+
pollIntervalMs: 100,
541+
runId: 0,
542+
runTimeoutMs: 10_000,
543+
});
544+
await expect(getWorkflowRunResultPromise).resolves.not.toThrow();
545+
const result = await getWorkflowRunResultPromise;
546+
expect(result).toStrictEqual({
547+
success: false,
548+
reason: "unsupported",
549+
value: "weird",
550+
});
551+
552+
// Logging
553+
assertOnlyCalled(coreErrorLogMock, coreInfoLogMock);
554+
expect(coreErrorLogMock).toHaveBeenCalledOnce();
555+
expect(coreErrorLogMock.mock.calls).toMatchSnapshot();
556+
expect(coreInfoLogMock).toHaveBeenCalledOnce();
557+
expect(coreInfoLogMock.mock.calls).toMatchSnapshot();
558+
});
559+
560+
it("returns a timeout", async () => {
561+
const pollIntervalMs = 100;
562+
const runTimeoutMs = 1000;
563+
const expectedIterations = runTimeoutMs / pollIntervalMs;
564+
apiFetchWorkflowRunStateMock.mockResolvedValue({
565+
status: WorkflowRunStatus.InProgress,
566+
conclusion: null,
567+
});
568+
apiRetryOnErrorMock.mockImplementation(async (toTry) => ({
569+
success: true,
570+
value: await toTry(),
571+
}));
572+
573+
// Behaviour
574+
const getWorkflowRunResultPromise = getWorkflowRunResult({
575+
startTime: Date.now(),
576+
pollIntervalMs: pollIntervalMs,
577+
runId: 0,
578+
runTimeoutMs: runTimeoutMs,
579+
});
580+
await vi.advanceTimersByTimeAsync(1000);
581+
await expect(getWorkflowRunResultPromise).resolves.not.toThrow();
582+
const result = await getWorkflowRunResultPromise;
583+
expect(result).toStrictEqual({
584+
success: false,
585+
reason: "timeout",
586+
});
587+
588+
// Logging
589+
assertOnlyCalled(coreDebugLogMock);
590+
expect(coreDebugLogMock).toHaveBeenCalledTimes(expectedIterations);
591+
expect(coreDebugLogMock.mock.calls).toMatchSnapshot();
592+
});
593+
});
331594
});

0 commit comments

Comments
 (0)