mirror of
https://github.com/stack-auth/stack.git
synced 2026-06-19 21:00:40 +08:00
<!-- ONTRIBUTING.md guidelines: https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md --> <!-- RECURSEML_SUMMARY:START --> ## High-level PR Summary This PR changes the default development ports for several background services to avoid conflicts. PostgreSQL moves from port `5432` to `8128`, Inbucket SMTP from `2500` to `8129`, Inbucket POP3 from `1100` to `8130`, and the OpenTelemetry collector from `4318` to `8131`. All references across configuration files, Docker Compose setups, environment files, CI/CD workflows, test files, and documentation have been updated to reflect these new port assignments. A knowledge base document has been added to document the new port mappings. ⏱️ Estimated Review Time: 15-30 minutes <details> <summary>💡 Review Order Suggestion</summary> | Order | File Path | | --- | --- | | 1 | `claude/CLAUDE-KNOWLEDGE.md` | | 2 | `apps/dev-launchpad/public/index.html` | | 3 | `docker/dependencies/docker.compose.yaml` | | 4 | `docker/emulator/docker.compose.yaml` | | 5 | `apps/backend/.env` | | 6 | `apps/backend/.env.development` | | 7 | `docker/server/.env.example` | | 8 | `package.json` | | 9 | `.devcontainer/devcontainer.json` | | 10 | `apps/e2e/.env.development` | | 11 | `.github/workflows/check-prisma-migrations.yaml` | | 12 | `.github/workflows/docker-server-test.yaml` | | 13 | `.github/workflows/e2e-api-tests.yaml` | | 14 | `.github/workflows/e2e-source-of-truth-api-tests.yaml` | | 15 | `.github/workflows/restart-dev-and-test.yaml` | | 16 | `apps/e2e/tests/backend/endpoints/api/v1/internal/email-drafts.test.ts` | | 17 | `apps/e2e/tests/backend/endpoints/api/v1/internal/email.test.ts` | | 18 | `apps/e2e/tests/backend/endpoints/api/v1/send-email.test.ts` | | 19 | `apps/e2e/tests/backend/endpoints/api/v1/unsubscribe-link.test.ts` | | 20 | `apps/e2e/tests/backend/workflows.test.ts` | | 21 | `docs/templates/others/self-host.mdx` | </details> [](https://discord.gg/n3SsVDAW6U) [ <!-- RECURSEML_SUMMARY:END --> <!-- ELLIPSIS_HIDDEN --> ---- > [!IMPORTANT] > This PR introduces customizable development ports using `NEXT_PUBLIC_STACK_PORT_PREFIX`, updating configurations, documentation, and tests accordingly. > > - **Behavior**: > - Default development ports for services are now customizable via `NEXT_PUBLIC_STACK_PORT_PREFIX`. > - PostgreSQL port changed from `5432` to `${NEXT_PUBLIC_STACK_PORT_PREFIX:-81}28`. > - Inbucket SMTP port changed from `2500` to `${NEXT_PUBLIC_STACK_PORT_PREFIX:-81}29`. > - Inbucket POP3 port changed from `1100` to `${NEXT_PUBLIC_STACK_PORT_PREFIX:-81}30`. > - OpenTelemetry collector port changed from `4318` to `${NEXT_PUBLIC_STACK_PORT_PREFIX:-81}31`. > - **Configuration**: > - Updated `docker.compose.yaml` to use new port variables for services like PostgreSQL, Inbucket, and OpenTelemetry. > - Environment files in `apps/backend`, `apps/dashboard`, and `apps/e2e` updated to use `NEXT_PUBLIC_STACK_PORT_PREFIX`. > - `package.json` scripts updated to reflect new port configurations. > - **Documentation**: > - Added `CLAUDE-KNOWLEDGE.md` to document new port mappings. > - Updated `self-host.mdx` to reflect new port configurations. > - **Testing**: > - Updated test files in `apps/e2e/tests` to use new port configurations. > - Added `helpers/ports.ts` for port-related utilities in tests. > > <sup>This description was created by </sup>[<img alt="Ellipsis" src="https://img.shields.io/badge/Ellipsis-blue?color=175173">](https://www.ellipsis.dev?ref=stack-auth%2Fstack-auth&utm_source=github&utm_medium=referral)<sup> for76ef55f58f. You can [customize](https://app.ellipsis.dev/stack-auth/settings/summaries) this summary. It will automatically update as commits are pushed.</sup> ---- <!-- ELLIPSIS_HIDDEN --> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Enable configurable development ports via a NEXT_PUBLIC_STACK_PORT_PREFIX, allowing parallel local environments with custom port prefixes. - **Bug Fixes** - Updated local service port mappings and CI/workflow settings so tooling and tests use the new prefixed ports consistently. - **Documentation** - Added docs and contributor guidance for running multiple parallel workspaces with custom port prefixes. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: N2D4 <N2D4@users.noreply.github.com>
375 lines
16 KiB
TypeScript
375 lines
16 KiB
TypeScript
import { wait } from "@stackframe/stack-shared/dist/utils/promises";
|
|
import { Mailbox, test } from "../helpers";
|
|
import { withPortPrefix } from "../helpers/ports";
|
|
import { Auth, InternalApiKey, Project, bumpEmailAddress, createMailbox, niceBackendFetch } from "./backend-helpers";
|
|
|
|
async function configureEmailAndWorkflow(workflowId: string, tsSource: string, enabled = true) {
|
|
await Project.updateConfig({
|
|
emails: {
|
|
server: {
|
|
isShared: false,
|
|
host: "localhost",
|
|
port: Number(withPortPrefix("29")),
|
|
username: "test",
|
|
password: "test",
|
|
senderEmail: "test@example.com",
|
|
senderName: "Test Project",
|
|
},
|
|
},
|
|
workflows: {
|
|
availableWorkflows: {
|
|
[workflowId]: {
|
|
displayName: workflowId,
|
|
tsSource,
|
|
enabled,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
}
|
|
|
|
const waitRetries = 25;
|
|
|
|
async function waitForMailboxSubject(mailbox: Mailbox, subject: string) {
|
|
for (let i = 0; i < waitRetries; i++) {
|
|
const messages = await mailbox.fetchMessages();
|
|
const message = messages.find((m) => m.subject === subject);
|
|
if (message) return;
|
|
await wait(1_000);
|
|
}
|
|
throw new Error(`Message with subject ${subject} not found after ${waitRetries} tries`);
|
|
}
|
|
|
|
async function waitForServerMetadataNotNull(userId: string, key: string) {
|
|
for (let i = 0; i < waitRetries; i++) {
|
|
const user = await niceBackendFetch(`/api/v1/users/${userId}`, { accessType: "server" });
|
|
if (user.body.server_metadata?.[key]) return;
|
|
await wait(1_000);
|
|
}
|
|
throw new Error(`Server metadata for user ${userId} with key ${key} not found after ${waitRetries} tries`);
|
|
}
|
|
|
|
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()}`;
|
|
|
|
await configureEmailAndWorkflow("wf-email", `
|
|
onSignUp(async (user) => {
|
|
await stackApp.sendEmail({ userIds: [user.id], subject: ${JSON.stringify(subject)}, html: "<p>hi</p>" });
|
|
|
|
// schedule a callback as an example (we don't actually test whether it executed successfully)
|
|
return scheduleCallback({
|
|
scheduleAt: new Date(Date.now() + 7_000),
|
|
data: { "example": "data" },
|
|
callbackId: "my-callback",
|
|
});
|
|
});
|
|
|
|
registerCallback("my-callback", async (data) => {
|
|
console.log("my-callback", data);
|
|
});
|
|
`);
|
|
|
|
await Auth.Password.signUpWithEmail({ password: "password" });
|
|
|
|
await waitForMailboxSubject(mailbox, subject);
|
|
|
|
expect(await mailbox.fetchMessages()).toMatchInlineSnapshot(`
|
|
[
|
|
MailboxMessage {
|
|
"attachments": [],
|
|
"body": {
|
|
"html": "http://localhost:12345/some-callback-url?code=%3Cstripped+query+param%3E",
|
|
"text": "http://localhost:12345/some-callback-url?code=%3Cstripped+query+param%3E",
|
|
},
|
|
"from": "Test Project <test@example.com>",
|
|
"subject": "Verify your email at New Project",
|
|
"to": ["<unindexed-mailbox--<stripped UUID>@stack-generated.example.com>"],
|
|
<some fields may have been hidden>,
|
|
},
|
|
MailboxMessage {
|
|
"attachments": [],
|
|
"body": {
|
|
"html": "<!DOCTYPE html PUBLIC \\"-//W3C//DTD XHTML 1.0 Transitional//EN\\" \\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\\"><html dir=\\"ltr\\" lang=\\"en\\"><head><meta content=\\"text/html; charset=UTF-8\\" http-equiv=\\"Content-Type\\"/><meta name=\\"x-apple-disable-message-reformatting\\"/></head><body style=\\"background-color:rgb(250,251,251);font-family:ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";font-size:1rem;line-height:1.5rem\\"><!--$--><table align=\\"center\\" width=\\"100%\\" border=\\"0\\" cellPadding=\\"0\\" cellSpacing=\\"0\\" role=\\"presentation\\" style=\\"background-color:rgb(255,255,255);padding:45px;border-radius:0.5rem;max-width:37.5em\\"><tbody><tr style=\\"width:100%\\"><td><div><p>hi</p></div></td></tr></tbody></table><!--7--><!--/$--></body></html>",
|
|
"text": "hi",
|
|
},
|
|
"from": "Test Project <test@example.com>",
|
|
"subject": "WF client signup <stripped UUID>",
|
|
"to": ["<unindexed-mailbox--<stripped UUID>@stack-generated.example.com>"],
|
|
<some fields may have been hidden>,
|
|
},
|
|
]
|
|
`);
|
|
}, {
|
|
timeout: 60_000,
|
|
});
|
|
|
|
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()}`;
|
|
|
|
await configureEmailAndWorkflow("wf-email", `
|
|
onSignUp(async (user) => {
|
|
return scheduleCallback({
|
|
scheduleAt: new Date(Date.now() + 7_000),
|
|
data: { "userId": user.id },
|
|
callbackId: "my-callback",
|
|
});
|
|
});
|
|
|
|
registerCallback("my-callback", async (data) => {
|
|
await stackApp.sendEmail({ userIds: [data.userId], subject: ${JSON.stringify(subject)}, html: "<p>hi</p>" });
|
|
});
|
|
`);
|
|
|
|
await Auth.Password.signUpWithEmail({ password: "password" });
|
|
|
|
// since we wait for the callback, add some extra time
|
|
await wait(10_000);
|
|
await waitForMailboxSubject(mailbox, subject);
|
|
|
|
expect(await mailbox.fetchMessages()).toMatchInlineSnapshot(`
|
|
[
|
|
MailboxMessage {
|
|
"attachments": [],
|
|
"body": {
|
|
"html": "http://localhost:12345/some-callback-url?code=%3Cstripped+query+param%3E",
|
|
"text": "http://localhost:12345/some-callback-url?code=%3Cstripped+query+param%3E",
|
|
},
|
|
"from": "Test Project <test@example.com>",
|
|
"subject": "Verify your email at New Project",
|
|
"to": ["<unindexed-mailbox--<stripped UUID>@stack-generated.example.com>"],
|
|
<some fields may have been hidden>,
|
|
},
|
|
MailboxMessage {
|
|
"attachments": [],
|
|
"body": {
|
|
"html": "<!DOCTYPE html PUBLIC \\"-//W3C//DTD XHTML 1.0 Transitional//EN\\" \\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\\"><html dir=\\"ltr\\" lang=\\"en\\"><head><meta content=\\"text/html; charset=UTF-8\\" http-equiv=\\"Content-Type\\"/><meta name=\\"x-apple-disable-message-reformatting\\"/></head><body style=\\"background-color:rgb(250,251,251);font-family:ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";font-size:1rem;line-height:1.5rem\\"><!--$--><table align=\\"center\\" width=\\"100%\\" border=\\"0\\" cellPadding=\\"0\\" cellSpacing=\\"0\\" role=\\"presentation\\" style=\\"background-color:rgb(255,255,255);padding:45px;border-radius:0.5rem;max-width:37.5em\\"><tbody><tr style=\\"width:100%\\"><td><div><p>hi</p></div></td></tr></tbody></table><!--7--><!--/$--></body></html>",
|
|
"text": "hi",
|
|
},
|
|
"from": "Test Project <test@example.com>",
|
|
"subject": "WF client signup <stripped UUID>",
|
|
"to": ["<unindexed-mailbox--<stripped UUID>@stack-generated.example.com>"],
|
|
<some fields may have been hidden>,
|
|
},
|
|
]
|
|
`);
|
|
}, {
|
|
timeout: 60_000,
|
|
});
|
|
|
|
test("onSignUp workflow sends email for server-created user", async ({ expect }) => {
|
|
await Project.createAndSwitch();
|
|
await InternalApiKey.createAndSetProjectKeys();
|
|
|
|
const mailbox = createMailbox(`wf-server-${crypto.randomUUID()}@stack-generated.example.com`);
|
|
const subject = `WF server create ${crypto.randomUUID()}`;
|
|
|
|
await configureEmailAndWorkflow("wf-email-server", `
|
|
onSignUp(async (user) => {
|
|
await stackApp.sendEmail({ userIds: [user.id], subject: ${JSON.stringify(subject)}, html: "<p>server</p>" });
|
|
});
|
|
`);
|
|
|
|
const createUserRes = await niceBackendFetch("/api/v1/users", {
|
|
method: "POST",
|
|
accessType: "server",
|
|
body: {
|
|
primary_email: mailbox.emailAddress,
|
|
primary_email_verified: true,
|
|
},
|
|
});
|
|
expect(createUserRes.status).toBe(201);
|
|
|
|
await waitForMailboxSubject(mailbox, subject);
|
|
expect(await mailbox.fetchMessages()).toMatchInlineSnapshot(`
|
|
[
|
|
MailboxMessage {
|
|
"attachments": [],
|
|
"body": {
|
|
"html": "<!DOCTYPE html PUBLIC \\"-//W3C//DTD XHTML 1.0 Transitional//EN\\" \\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\\"><html dir=\\"ltr\\" lang=\\"en\\"><head><meta content=\\"text/html; charset=UTF-8\\" http-equiv=\\"Content-Type\\"/><meta name=\\"x-apple-disable-message-reformatting\\"/></head><body style=\\"background-color:rgb(250,251,251);font-family:ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";font-size:1rem;line-height:1.5rem\\"><!--$--><table align=\\"center\\" width=\\"100%\\" border=\\"0\\" cellPadding=\\"0\\" cellSpacing=\\"0\\" role=\\"presentation\\" style=\\"background-color:rgb(255,255,255);padding:45px;border-radius:0.5rem;max-width:37.5em\\"><tbody><tr style=\\"width:100%\\"><td><div><p>server</p></div></td></tr></tbody></table><!--7--><!--/$--></body></html>",
|
|
"text": "server",
|
|
},
|
|
"from": "Test Project <test@example.com>",
|
|
"subject": "WF server create <stripped UUID>",
|
|
"to": ["<wf-server-<stripped UUID>@stack-generated.example.com>"],
|
|
<some fields may have been hidden>,
|
|
},
|
|
]
|
|
`);
|
|
}, {
|
|
timeout: 60_000,
|
|
});
|
|
|
|
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()}`;
|
|
|
|
await configureEmailAndWorkflow("wf-disabled", `
|
|
onSignUp(async (user) => {
|
|
await stackApp.sendEmail({ userIds: [user.id], subject: ${JSON.stringify(subject)}, html: "<p>nope</p>" });
|
|
});
|
|
`, /* enabled */ false);
|
|
|
|
await Auth.Password.signUpWithEmail({ password: "password" });
|
|
|
|
await wait(waitRetries * 1_000 * 1.3);
|
|
await Auth.refreshAccessToken();
|
|
|
|
expect(await mailbox.fetchMessages()).toMatchInlineSnapshot(`
|
|
[
|
|
MailboxMessage {
|
|
"attachments": [],
|
|
"body": {
|
|
"html": "http://localhost:12345/some-callback-url?code=%3Cstripped+query+param%3E",
|
|
"text": "http://localhost:12345/some-callback-url?code=%3Cstripped+query+param%3E",
|
|
},
|
|
"from": "Test Project <test@example.com>",
|
|
"subject": "Verify your email at New Project",
|
|
"to": ["<unindexed-mailbox--<stripped UUID>@stack-generated.example.com>"],
|
|
<some fields may have been hidden>,
|
|
},
|
|
]
|
|
`);
|
|
}, {
|
|
timeout: 90_000,
|
|
});
|
|
|
|
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()}`;
|
|
|
|
// bad compile
|
|
await configureEmailAndWorkflow("wf-bad-compile", `return return`);
|
|
// bad runtime
|
|
await configureEmailAndWorkflow("wf-bad-runtime", `onSignUp(() => { throw new Error('boom') });`);
|
|
// good one
|
|
await configureEmailAndWorkflow("wf-good", `
|
|
onSignUp(async (user) => {
|
|
await stackApp.sendEmail({ userIds: [user.id], subject: ${JSON.stringify(subject)}, html: "<p>ok</p>" });
|
|
});
|
|
`);
|
|
|
|
await Auth.Password.signUpWithEmail({ password: "password" });
|
|
|
|
await waitForMailboxSubject(mailbox, subject);
|
|
expect(await mailbox.fetchMessages()).toMatchInlineSnapshot(`
|
|
[
|
|
MailboxMessage {
|
|
"attachments": [],
|
|
"body": {
|
|
"html": "http://localhost:12345/some-callback-url?code=%3Cstripped+query+param%3E",
|
|
"text": "http://localhost:12345/some-callback-url?code=%3Cstripped+query+param%3E",
|
|
},
|
|
"from": "Test Project <test@example.com>",
|
|
"subject": "Verify your email at New Project",
|
|
"to": ["<unindexed-mailbox--<stripped UUID>@stack-generated.example.com>"],
|
|
<some fields may have been hidden>,
|
|
},
|
|
MailboxMessage {
|
|
"attachments": [],
|
|
"body": {
|
|
"html": "<!DOCTYPE html PUBLIC \\"-//W3C//DTD XHTML 1.0 Transitional//EN\\" \\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\\"><html dir=\\"ltr\\" lang=\\"en\\"><head><meta content=\\"text/html; charset=UTF-8\\" http-equiv=\\"Content-Type\\"/><meta name=\\"x-apple-disable-message-reformatting\\"/></head><body style=\\"background-color:rgb(250,251,251);font-family:ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";font-size:1rem;line-height:1.5rem\\"><!--$--><table align=\\"center\\" width=\\"100%\\" border=\\"0\\" cellPadding=\\"0\\" cellSpacing=\\"0\\" role=\\"presentation\\" style=\\"background-color:rgb(255,255,255);padding:45px;border-radius:0.5rem;max-width:37.5em\\"><tbody><tr style=\\"width:100%\\"><td><div><p>ok</p></div></td></tr></tbody></table><!--7--><!--/$--></body></html>",
|
|
"text": "ok",
|
|
},
|
|
"from": "Test Project <test@example.com>",
|
|
"subject": "WF ok <stripped UUID>",
|
|
"to": ["<unindexed-mailbox--<stripped UUID>@stack-generated.example.com>"],
|
|
<some fields may have been hidden>,
|
|
},
|
|
]
|
|
`);
|
|
}, {
|
|
timeout: 60_000,
|
|
});
|
|
|
|
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({
|
|
workflows: {
|
|
availableWorkflows: {
|
|
"wf-anon-upgrade": {
|
|
displayName: "wf-anon-upgrade",
|
|
enabled: true,
|
|
tsSource: `onSignUp(async (user) => { await user.update({ serverMetadata: { ${JSON.stringify(markerKey)}: user.primaryEmail } }); });`,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
// create anonymous session/user
|
|
const { userId: anonUserId } = await Auth.Anonymous.signUp();
|
|
|
|
// ensure marker not present yet
|
|
await wait(waitRetries * 1_000 * 1.3);
|
|
await Auth.refreshAccessToken();
|
|
const me1 = await niceBackendFetch("/api/v1/users/me", { accessType: "client" });
|
|
expect(me1.body.server_metadata?.[markerKey]).toBeUndefined();
|
|
|
|
// upgrade via password sign-up
|
|
const { userId } = await Auth.Password.signUpWithEmail({ password: "password" });
|
|
expect(userId).toEqual(anonUserId);
|
|
|
|
await waitForServerMetadataNotNull(anonUserId, markerKey);
|
|
const me2 = await niceBackendFetch("/api/v1/users/me", { accessType: "server" });
|
|
expect(me2.body.is_anonymous).toBe(false);
|
|
expect(me2.body.server_metadata?.[markerKey]).toBe(me2.body.primary_email);
|
|
}, {
|
|
timeout: 90_000,
|
|
});
|
|
|
|
test("workflow source changes take effect for subsequent sign-ups", async ({ expect }) => {
|
|
await Project.createAndSwitch();
|
|
await InternalApiKey.createAndSetProjectKeys();
|
|
const markerKey = `versionMarker-${crypto.randomUUID()}`;
|
|
|
|
// v1
|
|
await Project.updateConfig({
|
|
workflows: {
|
|
availableWorkflows: {
|
|
"wf-versioned": {
|
|
displayName: "wf-versioned",
|
|
enabled: true,
|
|
tsSource: `onSignUp(async (user) => { await user.update({ serverMetadata: { ${JSON.stringify(markerKey)}: "v1" } }); });`,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
await bumpEmailAddress({ unindexed: true });
|
|
await Auth.Password.signUpWithEmail({ password: "password" });
|
|
await waitForServerMetadataNotNull("me", markerKey);
|
|
const me1 = await niceBackendFetch("/api/v1/users/me", { accessType: "server" });
|
|
expect(me1.body.server_metadata?.[markerKey]).toBe("v1");
|
|
|
|
// v2
|
|
await Project.updateConfig({
|
|
workflows: {
|
|
availableWorkflows: {
|
|
"wf-versioned": {
|
|
displayName: "wf-versioned",
|
|
enabled: true,
|
|
tsSource: `onSignUp(async (user) => { await user.update({ serverMetadata: { ${JSON.stringify(markerKey)}: "v2" } }); });`,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
await bumpEmailAddress({ unindexed: true });
|
|
await Auth.Password.signUpWithEmail({ password: "password" });
|
|
await waitForServerMetadataNotNull("me", markerKey);
|
|
const me2 = await niceBackendFetch("/api/v1/users/me", { accessType: "server" });
|
|
expect(me2.body.server_metadata?.[markerKey]).toBe("v2");
|
|
}, {
|
|
timeout: 90_000,
|
|
});
|