Skip to content

Commit 599f418

Browse files
committed
Final tests fix
1 parent 656e738 commit 599f418

3 files changed

Lines changed: 55 additions & 47 deletions

File tree

apps/backend/src/lib/workflows.tsx

Lines changed: 46 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -139,17 +139,23 @@ async function compileWorkflow(tenancy: Tenancy, workflowId: string): Promise<Re
139139
}
140140
const workflow = tenancy.config.workflows.availableWorkflows[workflowId];
141141
const res = await timeout(async () => {
142+
console.log(`Compiling workflow ${workflowId}...`);
142143
const compiledCodeResult = await compileWorkflowSource(workflow.tsSource);
143144
if (compiledCodeResult.status === "error") {
144145
return Result.error({ compileError: `Failed to compile workflow: ${compiledCodeResult.error}` });
145146
}
146147

148+
console.log(`Compiled workflow source for ${workflowId}, running compilation trigger...`, { compiledCodeResult });
149+
147150
const compileTriggerResult = await triggerWorkflowRaw(tenancy, compiledCodeResult.data, {
148151
type: "compile",
149152
});
150153
if (compileTriggerResult.status === "error") {
151154
return Result.error({ compileError: `Failed to initialize workflow: ${compileTriggerResult.error}` });
152155
}
156+
157+
console.log(`Compilation trigger result:`, { compileTriggerResult });
158+
153159
const compileTriggerOutputResult = compileTriggerResult.data;
154160
if (typeof compileTriggerOutputResult !== "object" || !compileTriggerOutputResult || !("triggerOutput" in compileTriggerOutputResult)) {
155161
captureError("workflows-compile-trigger-output", new StackAssertionError(`Failed to parse compile trigger output`, { compileTriggerOutputResult }));
@@ -161,6 +167,8 @@ async function compileWorkflow(tenancy: Tenancy, workflowId: string): Promise<Re
161167
return Result.error({ compileError: `Failed to parse compile trigger output, should be array of strings` });
162168
}
163169

170+
console.log(`Workflow ${workflowId} compiled successfully, returning result...`, { registeredTriggers, compiledCodeResult });
171+
164172
return Result.ok({
165173
compiledCode: compiledCodeResult.data,
166174
registeredTriggers: registeredTriggers,
@@ -369,46 +377,48 @@ async function compileAndGetEnabledWorkflows(tenancy: Tenancy): Promise<Map<stri
369377
}
370378

371379
async function triggerWorkflowRaw(tenancy: Tenancy, compiledWorkflowCode: string, trigger: WorkflowTrigger): Promise<Result<unknown, string>> {
372-
const workflowToken = generateSecureRandomString();
373-
const workflowTriggerToken = await globalPrismaClient.workflowTriggerToken.create({
374-
data: {
375-
expiresAt: new Date(Date.now() + 1000 * 35),
376-
tenancyId: tenancy.id,
377-
tokenHash: await hashWorkflowTriggerToken(workflowToken),
378-
},
379-
});
380+
return await traceSpan({ description: `triggerWorkflowRaw ${trigger.type}` }, async () => {
381+
const workflowToken = generateSecureRandomString();
382+
const workflowTriggerToken = await globalPrismaClient.workflowTriggerToken.create({
383+
data: {
384+
expiresAt: new Date(Date.now() + 1000 * 35),
385+
tenancyId: tenancy.id,
386+
tokenHash: await hashWorkflowTriggerToken(workflowToken),
387+
},
388+
});
380389

381-
const tokenRefreshInterval = setInterval(() => {
382-
runAsynchronously(async () => {
383-
await globalPrismaClient.workflowTriggerToken.update({
384-
where: {
385-
tenancyId_id: {
386-
tenancyId: tenancy.id,
387-
id: workflowTriggerToken.id,
390+
const tokenRefreshInterval = setInterval(() => {
391+
runAsynchronously(async () => {
392+
await globalPrismaClient.workflowTriggerToken.update({
393+
where: {
394+
tenancyId_id: {
395+
tenancyId: tenancy.id,
396+
id: workflowTriggerToken.id,
397+
},
388398
},
399+
data: { expiresAt: new Date(Date.now() + 1000 * 35) },
400+
});
401+
});
402+
}, 10_000);
403+
404+
try {
405+
const freestyle = new Freestyle();
406+
const freestyleRes = await freestyle.executeScript(compiledWorkflowCode, {
407+
envVars: {
408+
STACK_WORKFLOW_TRIGGER_DATA: JSON.stringify(trigger),
409+
NEXT_PUBLIC_STACK_PROJECT_ID: tenancy.project.id,
410+
NEXT_PUBLIC_STACK_API_URL: getEnvVariable("NEXT_PUBLIC_STACK_API_URL").replace("http://localhost", "http://host.docker.internal"), // the replace is a hardcoded hack for the Freestyle mock server
411+
NEXT_PUBLIC_STACK_PUBLISHABLE_CLIENT_KEY: "<placeholder publishable client key; the actual auth happens with the workflow token>",
412+
STACK_SECRET_SERVER_KEY: "<placeholder secret server key; the actual auth happens with the workflow token>",
413+
STACK_WORKFLOW_TOKEN_SECRET: workflowToken,
389414
},
390-
data: { expiresAt: new Date(Date.now() + 1000 * 35) },
415+
nodeModules: Object.fromEntries(Object.entries(externalPackages).map(([packageName, version]) => [packageName, version])),
391416
});
392-
});
393-
}, 10_000);
394-
395-
try {
396-
const freestyle = new Freestyle();
397-
const freestyleRes = await freestyle.executeScript(compiledWorkflowCode, {
398-
envVars: {
399-
STACK_WORKFLOW_TRIGGER_DATA: JSON.stringify(trigger),
400-
NEXT_PUBLIC_STACK_PROJECT_ID: tenancy.project.id,
401-
NEXT_PUBLIC_STACK_API_URL: getEnvVariable("NEXT_PUBLIC_STACK_API_URL").replace("http://localhost", "http://host.docker.internal"), // the replace is a hardcoded hack for the Freestyle mock server
402-
NEXT_PUBLIC_STACK_PUBLISHABLE_CLIENT_KEY: "<placeholder publishable client key; the actual auth happens with the workflow token>",
403-
STACK_SECRET_SERVER_KEY: "<placeholder secret server key; the actual auth happens with the workflow token>",
404-
STACK_WORKFLOW_TOKEN_SECRET: workflowToken,
405-
},
406-
nodeModules: Object.fromEntries(Object.entries(externalPackages).map(([packageName, version]) => [packageName, version])),
407-
});
408-
return Result.map(freestyleRes, (data) => data.result);
409-
} finally {
410-
clearInterval(tokenRefreshInterval);
411-
}
417+
return Result.map(freestyleRes, (data) => data.result);
418+
} finally {
419+
clearInterval(tokenRefreshInterval);
420+
}
421+
});
412422
}
413423

414424
async function createScheduledTrigger(tenancy: Tenancy, workflowId: string, trigger: WorkflowTrigger, scheduledAt: Date) {

apps/e2e/tests/backend/backend-helpers.ts

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -218,20 +218,12 @@ export namespace Auth {
218218
});
219219
expect(response).toMatchInlineSnapshot(`
220220
NiceResponse {
221-
"status": 401,
222-
"body": {
223-
"code": "ADMIN_ACCESS_TOKEN_EXPIRED",
224-
"details": { "expired_at_millis": 1756938402000 },
225-
"error": "Admin access token has expired. Please refresh it and try again. (The access token expired at 2025-09-03T22:26:42.000Z.)",
226-
},
227-
"headers": Headers {
228-
"x-stack-known-error": "ADMIN_ACCESS_TOKEN_EXPIRED",
229-
<some fields may have been hidden>,
230-
},
221+
"status": 200,
222+
"body": { "access_token": <stripped field 'access_token'> },
223+
"headers": Headers { <some fields may have been hidden> },
231224
}
232225
`);
233226
backendContext.set({ userAuth: { accessToken: response.body.access_token, refreshToken: response.body.refresh_token } });
234-
await ensureParsableAccessToken();
235227
return {
236228
refreshAccessTokenResponse: response,
237229
};

apps/e2e/tests/backend/workflows.test.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ async function waitForServerMetadataNotNull(userId: string, key: string) {
5050

5151
test("onSignUp workflow sends email for client sign-up", async ({ expect }) => {
5252
await Project.createAndSwitch();
53+
await InternalApiKey.createAndSetProjectKeys();
5354
const mailbox = await bumpEmailAddress({ unindexed: true });
5455
const subject = `WF client signup ${crypto.randomUUID()}`;
5556

@@ -106,6 +107,7 @@ test("onSignUp workflow sends email for client sign-up", async ({ expect }) => {
106107

107108
test("onSignUp workflow can schedule callbacks", async ({ expect }) => {
108109
await Project.createAndSwitch();
110+
await InternalApiKey.createAndSetProjectKeys();
109111
const mailbox = await bumpEmailAddress({ unindexed: true });
110112
const subject = `WF client signup ${crypto.randomUUID()}`;
111113

@@ -204,6 +206,7 @@ test("onSignUp workflow sends email for server-created user", async ({ expect })
204206

205207
test("disabled workflows do not trigger", async ({ expect }) => {
206208
await Project.createAndSwitch();
209+
await InternalApiKey.createAndSetProjectKeys();
207210
const mailbox = await bumpEmailAddress({ unindexed: true });
208211
const subject = `WF disabled ${crypto.randomUUID()}`;
209212

@@ -237,6 +240,7 @@ test("disabled workflows do not trigger", async ({ expect }) => {
237240

238241
test("compile/runtime errors in one workflow don't block others", async ({ expect }) => {
239242
await Project.createAndSwitch();
243+
await InternalApiKey.createAndSetProjectKeys();
240244
const mailbox = await bumpEmailAddress({ unindexed: true });
241245
const subject = `WF ok ${crypto.randomUUID()}`;
242246

@@ -286,6 +290,7 @@ test("compile/runtime errors in one workflow don't block others", async ({ expec
286290

287291
test("anonymous sign-up does not trigger; upgrade triggers workflow", async ({ expect }) => {
288292
await Project.createAndSwitch();
293+
await InternalApiKey.createAndSetProjectKeys();
289294
const markerKey = `wfMarker-${crypto.randomUUID()}`;
290295

291296
await Project.updateConfig({
@@ -323,6 +328,7 @@ test("anonymous sign-up does not trigger; upgrade triggers workflow", async ({ e
323328

324329
test("workflow source changes take effect for subsequent sign-ups", async ({ expect }) => {
325330
await Project.createAndSwitch();
331+
await InternalApiKey.createAndSetProjectKeys();
326332
const markerKey = `versionMarker-${crypto.randomUUID()}`;
327333

328334
// v1

0 commit comments

Comments
 (0)