Skip to content
File

Blob: tests/integration/queue-runner-happy-path.test.ts

typescript118 lines
1import { afterAll, beforeAll, describe, it } from "vitest";
2import { join } from "node:path";
3import { mkdtemp, rm } from "node:fs/promises";
4import { tmpdir } from "node:os";
5 
6import {
7 type IntegrationContext,
8 type OperatorIdentity,
9 type SessionId,
10 applyMigrations,
11 assert,
12 closeContextLogs,
13 createOperatorIdentity,
14 oidcSignInOnce,
15 printFailureContext,
16 printLogTails,
17 startDevServer,
18 stopDevServer,
19} from "./queue-runner/harness";
20import {
21 scenarioBackToBackRunsAreSerial,
22 scenarioSingleRunPasses,
23 scenarioWebhookTriggeredRunPasses,
24} from "./queue-runner/scenarios";
25import { startMockOidcProvider, type MockOidcServer } from "../helpers/oidc-mock";
26 
27describe("queue runner integration", () => {
28 let context: IntegrationContext | null = null;
29 let mockOidc: MockOidcServer | null = null;
30 let operatorIdentity: OperatorIdentity | null = null;
31 let tempDir: string | null = null;
32 let preserveTempState = false;
33 
34 const requireContext = (): IntegrationContext => {
35 assert(context !== null, "Integration context not initialized.");
36 return context;
37 };
38 
39 const ensureOperatorSession = async (): Promise<SessionId> => {
40 const currentContext = requireContext();
41 
42 if (operatorIdentity === null) {
43 operatorIdentity = createOperatorIdentity();
44 }
45 
46 return await oidcSignInOnce(currentContext, operatorIdentity);
47 };
48 
49 const runScenario = async (scenario: (baseUrl: string, sessionId: SessionId) => Promise<void>): Promise<void> => {
50 const currentContext = requireContext();
51 
52 try {
53 const sessionId = await ensureOperatorSession();
54 await scenario(currentContext.baseUrl, sessionId);
55 } catch (error) {
56 preserveTempState = true;
57 throw error;
58 }
59 };
60 
61 beforeAll(async () => {
62 tempDir = await mkdtemp(join(tmpdir(), "anvil-queue-int-"));
63 console.log(`Using temp state: ${tempDir}`);
64 
65 try {
66 mockOidc = await startMockOidcProvider();
67 await applyMigrations(tempDir);
68 context = await startDevServer(tempDir, mockOidc.issuer);
69 } catch (error) {
70 preserveTempState = true;
71 throw error;
72 }
73 });
74 
75 afterAll(async () => {
76 if (context) {
77 await stopDevServer(context.serverProcess);
78 await closeContextLogs(context);
79 }
80 
81 if (mockOidc) {
82 await mockOidc.close();
83 }
84 
85 if (!tempDir) {
86 return;
87 }
88 
89 if (!preserveTempState) {
90 if (context) {
91 await rm(context.devVarsPath, { force: true });
92 }
93 await rm(tempDir, { recursive: true, force: true });
94 return;
95 }
96 
97 if (context) {
98 printFailureContext(context);
99 await printLogTails(context);
100 return;
101 }
102 
103 console.error(`Preserved temp state: ${tempDir}`);
104 });
105 
106 it("signs in with tessera and passes a single queued run", async () => {
107 await runScenario(scenarioSingleRunPasses);
108 });
109 
110 it("logs in and serializes back-to-back runs", async () => {
111 await runScenario(scenarioBackToBackRunsAreSerial);
112 });
113 
114 it("accepts a verified GitHub webhook and passes the queued run", async () => {
115 await runScenario(scenarioWebhookTriggeredRunPasses);
116 });
117});