Skip to content

Commit aeb5f77

Browse files
authored
[Fix] Flaky Neon, Email Delivery, and Other Tests (#1235)
### Summary of Changes Just bumped up polling, removed unnecessary wait checks in tests that don't need them. Minor changes, not an exhaustive list of flaky test fixes Note that importing a function into a file B that was exported from a test file A causes vitest to see all the tests in test file A as being under file B. This messes up CI and makes it harder to track failing tests.
1 parent 5686691 commit aeb5f77

12 files changed

Lines changed: 69 additions & 56 deletions

File tree

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -455,7 +455,7 @@ export namespace Auth {
455455
break;
456456
}
457457
await wait(100 + i * 20);
458-
if (i >= 30) {
458+
if (i >= 40) {
459459
throw new StackAssertionError(`Sign-in code message not found after ${i} attempts`, {
460460
response,
461461
messages: messages.map(m => ({ ...m, body: m.body && omit(m.body, ["html"]) })),

apps/e2e/tests/backend/endpoints/api/v1/auth/passkey/register.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@ import { Auth, niceBackendFetch, Project } from "../../../../../backend-helpers"
66

77
it("should allow initiating passkey registration", async ({ expect }) => {
88
await Project.createAndSwitch({ config: { passkey_enabled: true } });
9-
const res = await Auth.Password.signUpWithEmail();
9+
await Auth.Password.signUpWithEmail({ noWaitForEmail: true });
1010
await Auth.Passkey.initiateRegistration();
1111
});
1212

1313

1414
it("should successfully register a passkey", async ({ expect }) => {
1515
await Project.createAndSwitch({ config: { passkey_enabled: true } });
16-
const res = await Auth.Password.signUpWithEmail();
16+
await Auth.Password.signUpWithEmail({ noWaitForEmail: true });
1717
await Auth.Passkey.register();
1818
});
1919

apps/e2e/tests/backend/endpoints/api/v1/emails/outbox-api.test.ts

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ const slowTemplate = deindent`
3838
3939
// Artificial delay to make the email slow to render
4040
const startTime = performance.now();
41-
while (performance.now() - startTime < 500) {
42-
// Busy wait - 500ms delay
41+
while (performance.now() - startTime < 2000) {
42+
// Busy wait - 2000ms delay
4343
}
4444
4545
export function EmailTemplate({ user, project }) {
@@ -648,8 +648,8 @@ describe("email outbox API", () => {
648648
649649
// Artificial delay to make the email slow to render
650650
const startTime = performance.now();
651-
while (performance.now() - startTime < 500) {
652-
// Busy wait - 500ms delay
651+
while (performance.now() - startTime < 2000) {
652+
// Busy wait - 2000ms delay
653653
}
654654
655655
export function EmailTemplate({ user, project }) {
@@ -670,12 +670,12 @@ describe("email outbox API", () => {
670670
`);
671671
break;
672672
} else {
673-
if (i >= 20) {
673+
if (i >= 50) {
674674
throw new StackAssertionError(`Timeout waiting for email in the outbox`, {
675675
outboxEmails: await getOutboxEmails(),
676676
});
677677
}
678-
await wait(25);
678+
await wait(100);
679679
}
680680
}
681681

@@ -884,7 +884,7 @@ describe("email outbox API", () => {
884884
let emailId: string | null = null;
885885
let pauseSucceeded = false;
886886

887-
for (let i = 0; i < 20; i++) {
887+
for (let i = 0; i < 50; i++) {
888888
const listResponse = await niceBackendFetch("/api/v1/emails/outbox", {
889889
method: "GET",
890890
accessType: "server",
@@ -908,7 +908,7 @@ describe("email outbox API", () => {
908908
}
909909
}
910910

911-
await wait(25);
911+
await wait(100);
912912
}
913913

914914
// These assertions must always run - test fails if we couldn't pause
@@ -937,15 +937,20 @@ describe("email outbox API", () => {
937937
// After unpausing, the email should go back to processing (preparing/rendering/scheduled/etc)
938938
expect(unpauseResponse.body.status).not.toBe("paused");
939939

940-
// Wait for the email to be sent (since we unpaused it)
941-
await wait(7_000);
942-
943-
// Verify the email was eventually sent
944-
const finalGetResponse = await niceBackendFetch(`/api/v1/emails/outbox/${emailId}`, {
945-
method: "GET",
946-
accessType: "server",
947-
});
948-
expect(finalGetResponse.body.status).toBe("sent");
940+
// Poll until the email is sent (since we unpaused it)
941+
for (let i = 0; ; i++) {
942+
const finalGetResponse = await niceBackendFetch(`/api/v1/emails/outbox/${emailId}`, {
943+
method: "GET",
944+
accessType: "server",
945+
});
946+
if (finalGetResponse.body.status === "sent") break;
947+
if (i >= 50) {
948+
throw new StackAssertionError(`Timed out waiting for email to be sent after unpause`, {
949+
status: finalGetResponse.body.status,
950+
});
951+
}
952+
await wait(500);
953+
}
949954
});
950955

951956
it("should cancel email with MANUALLY_CANCELLED reason", async ({ expect }) => {
@@ -1000,7 +1005,7 @@ describe("email outbox API", () => {
10001005
let emailId: string | null = null;
10011006
let pauseSucceeded = false;
10021007

1003-
for (let i = 0; i < 20; i++) {
1008+
for (let i = 0; i < 50; i++) {
10041009
const listResponse = await niceBackendFetch("/api/v1/emails/outbox", {
10051010
method: "GET",
10061011
accessType: "server",
@@ -1024,7 +1029,7 @@ describe("email outbox API", () => {
10241029
}
10251030
}
10261031

1027-
await wait(25);
1032+
await wait(100);
10281033
}
10291034

10301035
// We need to have successfully paused the email to test cancel
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { niceBackendFetch } from "../../../../../../backend-helpers";
2+
3+
export async function provisionProject() {
4+
return await niceBackendFetch("/api/v1/integrations/custom/projects/provision", {
5+
method: "POST",
6+
body: {
7+
display_name: "Test project",
8+
},
9+
headers: {
10+
"Authorization": "Basic bmVvbi1sb2NhbDpuZW9uLWxvY2FsLXNlY3JldA==",
11+
},
12+
});
13+
}

apps/e2e/tests/backend/endpoints/api/v1/integrations/custom/projects/provision.test.ts

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,6 @@
11
import { it } from "../../../../../../../helpers";
22
import { backendContext, niceBackendFetch } from "../../../../../../backend-helpers";
3-
4-
export async function provisionProject() {
5-
return await niceBackendFetch("/api/v1/integrations/custom/projects/provision", {
6-
method: "POST",
7-
body: {
8-
display_name: "Test project",
9-
},
10-
headers: {
11-
"Authorization": "Basic bmVvbi1sb2NhbDpuZW9uLWxvY2FsLXNlY3JldA==",
12-
},
13-
});
14-
}
3+
import { provisionProject } from "./provision-helpers";
154

165
it("should be able to provision a new project if client details are correct", async ({ expect }) => {
176
const response = await provisionProject();

apps/e2e/tests/backend/endpoints/api/v1/integrations/custom/projects/transfer.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { urlString } from "@stackframe/stack-shared/dist/utils/urls";
22
import { expect } from "vitest";
33
import { it } from "../../../../../../../helpers";
44
import { Auth, Project, niceBackendFetch } from "../../../../../../backend-helpers";
5-
import { provisionProject } from "./provision.test";
5+
import { provisionProject } from "./provision-helpers";
66

77
async function initiateTransfer(projectId: string) {
88
const response = await niceBackendFetch("/api/v1/integrations/custom/projects/transfer/initiate", {
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { niceBackendFetch } from "../../../../../../backend-helpers";
2+
3+
export async function provisionProject() {
4+
return await niceBackendFetch("/api/v1/integrations/neon/projects/provision", {
5+
method: "POST",
6+
body: {
7+
display_name: "Test project",
8+
},
9+
headers: {
10+
"Authorization": "Basic bmVvbi1sb2NhbDpuZW9uLWxvY2FsLXNlY3JldA==",
11+
},
12+
});
13+
}

apps/e2e/tests/backend/endpoints/api/v1/integrations/neon/projects/provision.test.ts

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,7 @@
11
import { decryptValue, hashKey } from "@stackframe/stack-shared/dist/helpers/vault/client-side";
22
import { it } from "../../../../../../../helpers";
33
import { Auth, InternalApiKey, InternalProjectKeys, backendContext, niceBackendFetch } from "../../../../../../backend-helpers";
4-
5-
export async function provisionProject() {
6-
return await niceBackendFetch("/api/v1/integrations/neon/projects/provision", {
7-
method: "POST",
8-
body: {
9-
display_name: "Test project",
10-
},
11-
headers: {
12-
"Authorization": "Basic bmVvbi1sb2NhbDpuZW9uLWxvY2FsLXNlY3JldA==",
13-
},
14-
});
15-
}
4+
import { provisionProject } from "./provision-helpers";
165

176
it("should be able to provision a new project if neon client details are correct", async ({ expect }) => {
187
const response = await provisionProject();
@@ -112,7 +101,7 @@ it("should be able to provision a new project if neon client details are correct
112101
`);
113102

114103
// ensure we can create a user in the new project (make sure it's writable)
115-
const signInResponse = await Auth.Password.signUpWithEmail({ password: "test1234" });
104+
const signInResponse = await Auth.Password.signUpWithEmail({ password: "test1234", noWaitForEmail: true });
116105
expect(signInResponse).toMatchInlineSnapshot(`
117106
{
118107
"email": "default-mailbox--<stripped UUID>@stack-generated.example.com",

apps/e2e/tests/backend/endpoints/api/v1/integrations/neon/projects/transfer.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { urlString } from "@stackframe/stack-shared/dist/utils/urls";
22
import { expect } from "vitest";
33
import { it } from "../../../../../../../helpers";
44
import { Auth, Project, niceBackendFetch } from "../../../../../../backend-helpers";
5-
import { provisionProject } from "./provision.test";
5+
import { provisionProject } from "./provision-helpers";
66

77
async function initiateTransfer(projectId: string) {
88
const response = await niceBackendFetch("/api/v1/integrations/neon/projects/transfer/initiate", {

apps/e2e/tests/backend/endpoints/api/v1/integrations/neon/webhooks.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { it } from "../../../../../../helpers";
22
import { niceBackendFetch } from "../../../../../backend-helpers";
3-
import { provisionProject } from "./projects/provision.test";
3+
import { provisionProject } from "./projects/provision-helpers";
44

55

66
it("should be able to create a webhook", async ({ expect }) => {

0 commit comments

Comments
 (0)