Fix tests

This commit is contained in:
Konstantin Wohlwend 2026-04-13 14:17:00 -07:00
parent 8aa80ceb2c
commit 3aa764802f
3 changed files with 52 additions and 52 deletions

View File

@ -48,20 +48,21 @@ it("should fail when account is new and sign ups are disabled", async ({ expect
cookie,
},
});
expect(response).toMatchInlineSnapshot(`
NiceResponse {
"status": 400,
"body": {
"code": "SIGN_UP_NOT_ENABLED",
"error": "Creation of new accounts is not enabled for this project. Please ask the project owner to enable it.",
},
"headers": Headers {
"set-cookie": <deleting cookie 'stack-oauth-inner-<stripped cookie name key>' at path '/'>,
"x-stack-known-error": "SIGN_UP_NOT_ENABLED",
<some fields may have been hidden>,
},
}
`);
expect(response.status).toBe(307);
const location = response.headers.get("location");
expect(location).toBeTruthy();
if (location == null) {
throw new Error("OAuth callback error redirect location is missing");
}
const locationUrl = new URL(location);
expect(locationUrl.origin).toBe("http://stack-test.localhost");
expect(locationUrl.pathname).toBe("/some-callback-url");
expect(locationUrl.searchParams.get("error")).toBe("server_error");
expect(locationUrl.searchParams.get("errorCode")).toBe("SIGN_UP_NOT_ENABLED");
expect(locationUrl.searchParams.get("error_description")).toBe("Creation of new accounts is not enabled for this project. Please ask the project owner to enable it.");
expect(locationUrl.searchParams.get("message")).toBe("Creation of new accounts is not enabled for this project. Please ask the project owner to enable it.");
expect(locationUrl.searchParams.get("details")).toBe("{}");
expect(response.headers.get("set-cookie")).toMatch(/stack-oauth-inner-/);
});
it("should fail when cookies are missing", async ({ expect }) => {

View File

@ -1,6 +1,26 @@
import { it, updateCookiesFromResponse } from "../../../../../../helpers";
import { Auth, ContactChannels, InternalApiKey, Project, backendContext, niceBackendFetch } from "../../../../../backend-helpers";
const getOAuthErrorRedirectLocationUrl = (location: string | null) => {
if (location == null) {
throw new Error("OAuth callback error redirect location is missing");
}
return new URL(location);
};
const expectOAuthErrorRedirect = (
locationUrl: URL,
{ expectedErrorCode, expectedDescription }: { expectedErrorCode: string, expectedDescription: string },
expectFn: (input: unknown) => { toBe: (value: unknown) => void },
) => {
expectFn(locationUrl.origin).toBe("http://stack-test.localhost");
expectFn(locationUrl.pathname).toBe("/some-callback-url");
expectFn(locationUrl.searchParams.get("error")).toBe("server_error");
expectFn(locationUrl.searchParams.get("errorCode")).toBe(expectedErrorCode);
expectFn(locationUrl.searchParams.get("error_description")).toBe(expectedDescription);
expectFn(locationUrl.searchParams.get("message")).toBe(expectedDescription);
};
it("should allow duplicates, if the merge strategy is set to allow_duplicates", async ({ expect }) => {
const proj = await Project.createAndSwitch({
config: {
@ -47,25 +67,13 @@ it("should not allow duplicates, if the merge strategy set to raise_error", asyn
expect(cc.used_for_auth).toBe(true);
const { response } = await Auth.OAuth.getMaybeFailingAuthorizationCode();
expect(response).toMatchInlineSnapshot(`
NiceResponse {
"status": 409,
"body": {
"code": "CONTACT_CHANNEL_ALREADY_USED_FOR_AUTH_BY_SOMEONE_ELSE",
"details": {
"contact_channel_value": "default-mailbox--<stripped UUID>@stack-generated.example.com",
"type": "email",
"would_work_if_email_was_verified": false,
},
"error": "This email \\"(default-mailbox--<stripped UUID>@stack-generated.example.com)\\" is already used for authentication by another account.",
},
"headers": Headers {
"set-cookie": <deleting cookie 'stack-oauth-inner-<stripped cookie name key>' at path '/'>,
"x-stack-known-error": "CONTACT_CHANNEL_ALREADY_USED_FOR_AUTH_BY_SOMEONE_ELSE",
<some fields may have been hidden>,
},
}
`);
expect(response.status).toBe(307);
const locationUrl = getOAuthErrorRedirectLocationUrl(response.headers.get("location"));
expectOAuthErrorRedirect(locationUrl, {
expectedErrorCode: "CONTACT_CHANNEL_ALREADY_USED_FOR_AUTH_BY_SOMEONE_ELSE",
expectedDescription: `This email "(${cc.value})" is already used for authentication by another account.`,
}, expect);
expect(response.headers.get("set-cookie")).toMatch(/stack-oauth-inner-/);
});
it("should merge accounts, if the merge strategy set to link_method", async ({ expect }) => {
@ -114,25 +122,13 @@ it("should not merge accounts if the merge strategy is set to link_method, but t
expect(cc.used_for_auth).toBe(true);
const { response } = await Auth.OAuth.getMaybeFailingAuthorizationCode();
expect(response).toMatchInlineSnapshot(`
NiceResponse {
"status": 409,
"body": {
"code": "CONTACT_CHANNEL_ALREADY_USED_FOR_AUTH_BY_SOMEONE_ELSE",
"details": {
"contact_channel_value": "default-mailbox--<stripped UUID>@stack-generated.example.com",
"type": "email",
"would_work_if_email_was_verified": true,
},
"error": "This email \\"(default-mailbox--<stripped UUID>@stack-generated.example.com)\\" is already used for authentication by another account but the email is not verified. Please login to your existing account with the method you used to sign up, and then verify your email to sign in with this login method.",
},
"headers": Headers {
"set-cookie": <deleting cookie 'stack-oauth-inner-<stripped cookie name key>' at path '/'>,
"x-stack-known-error": "CONTACT_CHANNEL_ALREADY_USED_FOR_AUTH_BY_SOMEONE_ELSE",
<some fields may have been hidden>,
},
}
`);
expect(response.status).toBe(307);
const locationUrl = getOAuthErrorRedirectLocationUrl(response.headers.get("location"));
expectOAuthErrorRedirect(locationUrl, {
expectedErrorCode: "CONTACT_CHANNEL_ALREADY_USED_FOR_AUTH_BY_SOMEONE_ELSE",
expectedDescription: `This email "(${cc.value})" is already used for authentication by another account but the email is not verified. Please login to your existing account with the method you used to sign up, and then verify your email to sign in with this login method.`,
}, expect);
expect(response.headers.get("set-cookie")).toMatch(/stack-oauth-inner-/);
});
it("should allow OAuth login with manually created account when sign-ups are disabled", async ({ expect }) => {

View File

@ -184,3 +184,6 @@ A: After exhausting transient-network retries in `OAuthBaseProvider.getCallback`
Q: How should OAuth callback errors be surfaced to handler-based clients?
A: In `apps/backend/src/app/api/latest/auth/oauth/callback/[provider_id]/route.tsx`, prefer redirecting known errors to the original OAuth callback URL (`redirectUri`) with `error`, `error_description`, `errorCode`, `message`, and `details` query params (fallback to `errorRedirectUrl` if needed). In template client handling (`packages/template/src/lib/auth.ts` + `components-page/oauth-callback.tsx`), detect those params, reconstruct a `KnownError`, and route to the handler error page so users get actionable UI instead of silent sign-in redirects.
Q: How should OAuth E2E tests assert callback failures after handler-based error redirects?
A: In OAuth callback/merge strategy E2E tests, assert `307` plus parsed `location` query params (`error`, `errorCode`, `error_description`, `message`, and optionally `details`) instead of snapshotting old `4xx` JSON error responses. This matches current callback semantics and avoids brittle encoded-URL snapshots.