mirror of
https://github.com/stack-auth/stack.git
synced 2026-06-13 21:01:21 +08:00
Final tests fix
This commit is contained in:
parent
656e738af9
commit
599f418da0
@ -139,17 +139,23 @@ async function compileWorkflow(tenancy: Tenancy, workflowId: string): Promise<Re
|
||||
}
|
||||
const workflow = tenancy.config.workflows.availableWorkflows[workflowId];
|
||||
const res = await timeout(async () => {
|
||||
console.log(`Compiling workflow ${workflowId}...`);
|
||||
const compiledCodeResult = await compileWorkflowSource(workflow.tsSource);
|
||||
if (compiledCodeResult.status === "error") {
|
||||
return Result.error({ compileError: `Failed to compile workflow: ${compiledCodeResult.error}` });
|
||||
}
|
||||
|
||||
console.log(`Compiled workflow source for ${workflowId}, running compilation trigger...`, { compiledCodeResult });
|
||||
|
||||
const compileTriggerResult = await triggerWorkflowRaw(tenancy, compiledCodeResult.data, {
|
||||
type: "compile",
|
||||
});
|
||||
if (compileTriggerResult.status === "error") {
|
||||
return Result.error({ compileError: `Failed to initialize workflow: ${compileTriggerResult.error}` });
|
||||
}
|
||||
|
||||
console.log(`Compilation trigger result:`, { compileTriggerResult });
|
||||
|
||||
const compileTriggerOutputResult = compileTriggerResult.data;
|
||||
if (typeof compileTriggerOutputResult !== "object" || !compileTriggerOutputResult || !("triggerOutput" in compileTriggerOutputResult)) {
|
||||
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
|
||||
return Result.error({ compileError: `Failed to parse compile trigger output, should be array of strings` });
|
||||
}
|
||||
|
||||
console.log(`Workflow ${workflowId} compiled successfully, returning result...`, { registeredTriggers, compiledCodeResult });
|
||||
|
||||
return Result.ok({
|
||||
compiledCode: compiledCodeResult.data,
|
||||
registeredTriggers: registeredTriggers,
|
||||
@ -369,46 +377,48 @@ async function compileAndGetEnabledWorkflows(tenancy: Tenancy): Promise<Map<stri
|
||||
}
|
||||
|
||||
async function triggerWorkflowRaw(tenancy: Tenancy, compiledWorkflowCode: string, trigger: WorkflowTrigger): Promise<Result<unknown, string>> {
|
||||
const workflowToken = generateSecureRandomString();
|
||||
const workflowTriggerToken = await globalPrismaClient.workflowTriggerToken.create({
|
||||
data: {
|
||||
expiresAt: new Date(Date.now() + 1000 * 35),
|
||||
tenancyId: tenancy.id,
|
||||
tokenHash: await hashWorkflowTriggerToken(workflowToken),
|
||||
},
|
||||
});
|
||||
|
||||
const tokenRefreshInterval = setInterval(() => {
|
||||
runAsynchronously(async () => {
|
||||
await globalPrismaClient.workflowTriggerToken.update({
|
||||
where: {
|
||||
tenancyId_id: {
|
||||
tenancyId: tenancy.id,
|
||||
id: workflowTriggerToken.id,
|
||||
},
|
||||
},
|
||||
data: { expiresAt: new Date(Date.now() + 1000 * 35) },
|
||||
});
|
||||
});
|
||||
}, 10_000);
|
||||
|
||||
try {
|
||||
const freestyle = new Freestyle();
|
||||
const freestyleRes = await freestyle.executeScript(compiledWorkflowCode, {
|
||||
envVars: {
|
||||
STACK_WORKFLOW_TRIGGER_DATA: JSON.stringify(trigger),
|
||||
NEXT_PUBLIC_STACK_PROJECT_ID: tenancy.project.id,
|
||||
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
|
||||
NEXT_PUBLIC_STACK_PUBLISHABLE_CLIENT_KEY: "<placeholder publishable client key; the actual auth happens with the workflow token>",
|
||||
STACK_SECRET_SERVER_KEY: "<placeholder secret server key; the actual auth happens with the workflow token>",
|
||||
STACK_WORKFLOW_TOKEN_SECRET: workflowToken,
|
||||
return await traceSpan({ description: `triggerWorkflowRaw ${trigger.type}` }, async () => {
|
||||
const workflowToken = generateSecureRandomString();
|
||||
const workflowTriggerToken = await globalPrismaClient.workflowTriggerToken.create({
|
||||
data: {
|
||||
expiresAt: new Date(Date.now() + 1000 * 35),
|
||||
tenancyId: tenancy.id,
|
||||
tokenHash: await hashWorkflowTriggerToken(workflowToken),
|
||||
},
|
||||
nodeModules: Object.fromEntries(Object.entries(externalPackages).map(([packageName, version]) => [packageName, version])),
|
||||
});
|
||||
return Result.map(freestyleRes, (data) => data.result);
|
||||
} finally {
|
||||
clearInterval(tokenRefreshInterval);
|
||||
}
|
||||
|
||||
const tokenRefreshInterval = setInterval(() => {
|
||||
runAsynchronously(async () => {
|
||||
await globalPrismaClient.workflowTriggerToken.update({
|
||||
where: {
|
||||
tenancyId_id: {
|
||||
tenancyId: tenancy.id,
|
||||
id: workflowTriggerToken.id,
|
||||
},
|
||||
},
|
||||
data: { expiresAt: new Date(Date.now() + 1000 * 35) },
|
||||
});
|
||||
});
|
||||
}, 10_000);
|
||||
|
||||
try {
|
||||
const freestyle = new Freestyle();
|
||||
const freestyleRes = await freestyle.executeScript(compiledWorkflowCode, {
|
||||
envVars: {
|
||||
STACK_WORKFLOW_TRIGGER_DATA: JSON.stringify(trigger),
|
||||
NEXT_PUBLIC_STACK_PROJECT_ID: tenancy.project.id,
|
||||
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
|
||||
NEXT_PUBLIC_STACK_PUBLISHABLE_CLIENT_KEY: "<placeholder publishable client key; the actual auth happens with the workflow token>",
|
||||
STACK_SECRET_SERVER_KEY: "<placeholder secret server key; the actual auth happens with the workflow token>",
|
||||
STACK_WORKFLOW_TOKEN_SECRET: workflowToken,
|
||||
},
|
||||
nodeModules: Object.fromEntries(Object.entries(externalPackages).map(([packageName, version]) => [packageName, version])),
|
||||
});
|
||||
return Result.map(freestyleRes, (data) => data.result);
|
||||
} finally {
|
||||
clearInterval(tokenRefreshInterval);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function createScheduledTrigger(tenancy: Tenancy, workflowId: string, trigger: WorkflowTrigger, scheduledAt: Date) {
|
||||
|
||||
@ -218,20 +218,12 @@ export namespace Auth {
|
||||
});
|
||||
expect(response).toMatchInlineSnapshot(`
|
||||
NiceResponse {
|
||||
"status": 401,
|
||||
"body": {
|
||||
"code": "ADMIN_ACCESS_TOKEN_EXPIRED",
|
||||
"details": { "expired_at_millis": 1756938402000 },
|
||||
"error": "Admin access token has expired. Please refresh it and try again. (The access token expired at 2025-09-03T22:26:42.000Z.)",
|
||||
},
|
||||
"headers": Headers {
|
||||
"x-stack-known-error": "ADMIN_ACCESS_TOKEN_EXPIRED",
|
||||
<some fields may have been hidden>,
|
||||
},
|
||||
"status": 200,
|
||||
"body": { "access_token": <stripped field 'access_token'> },
|
||||
"headers": Headers { <some fields may have been hidden> },
|
||||
}
|
||||
`);
|
||||
backendContext.set({ userAuth: { accessToken: response.body.access_token, refreshToken: response.body.refresh_token } });
|
||||
await ensureParsableAccessToken();
|
||||
return {
|
||||
refreshAccessTokenResponse: response,
|
||||
};
|
||||
|
||||
@ -50,6 +50,7 @@ async function waitForServerMetadataNotNull(userId: string, key: string) {
|
||||
|
||||
test("onSignUp workflow sends email for client sign-up", async ({ expect }) => {
|
||||
await Project.createAndSwitch();
|
||||
await InternalApiKey.createAndSetProjectKeys();
|
||||
const mailbox = await bumpEmailAddress({ unindexed: true });
|
||||
const subject = `WF client signup ${crypto.randomUUID()}`;
|
||||
|
||||
@ -106,6 +107,7 @@ test("onSignUp workflow sends email for client sign-up", async ({ expect }) => {
|
||||
|
||||
test("onSignUp workflow can schedule callbacks", async ({ expect }) => {
|
||||
await Project.createAndSwitch();
|
||||
await InternalApiKey.createAndSetProjectKeys();
|
||||
const mailbox = await bumpEmailAddress({ unindexed: true });
|
||||
const subject = `WF client signup ${crypto.randomUUID()}`;
|
||||
|
||||
@ -204,6 +206,7 @@ test("onSignUp workflow sends email for server-created user", async ({ expect })
|
||||
|
||||
test("disabled workflows do not trigger", async ({ expect }) => {
|
||||
await Project.createAndSwitch();
|
||||
await InternalApiKey.createAndSetProjectKeys();
|
||||
const mailbox = await bumpEmailAddress({ unindexed: true });
|
||||
const subject = `WF disabled ${crypto.randomUUID()}`;
|
||||
|
||||
@ -237,6 +240,7 @@ test("disabled workflows do not trigger", async ({ expect }) => {
|
||||
|
||||
test("compile/runtime errors in one workflow don't block others", async ({ expect }) => {
|
||||
await Project.createAndSwitch();
|
||||
await InternalApiKey.createAndSetProjectKeys();
|
||||
const mailbox = await bumpEmailAddress({ unindexed: true });
|
||||
const subject = `WF ok ${crypto.randomUUID()}`;
|
||||
|
||||
@ -286,6 +290,7 @@ test("compile/runtime errors in one workflow don't block others", async ({ expec
|
||||
|
||||
test("anonymous sign-up does not trigger; upgrade triggers workflow", async ({ expect }) => {
|
||||
await Project.createAndSwitch();
|
||||
await InternalApiKey.createAndSetProjectKeys();
|
||||
const markerKey = `wfMarker-${crypto.randomUUID()}`;
|
||||
|
||||
await Project.updateConfig({
|
||||
@ -323,6 +328,7 @@ test("anonymous sign-up does not trigger; upgrade triggers workflow", async ({ e
|
||||
|
||||
test("workflow source changes take effect for subsequent sign-ups", async ({ expect }) => {
|
||||
await Project.createAndSwitch();
|
||||
await InternalApiKey.createAndSetProjectKeys();
|
||||
const markerKey = `versionMarker-${crypto.randomUUID()}`;
|
||||
|
||||
// v1
|
||||
|
||||
Loading…
Reference in New Issue
Block a user