| @@ -1,17 +1,79 @@ |
| 1 | import { describe, expect, it } from "vite-plus/test"; | 1 | import { describe, expect, it } from "vite-plus/test"; |
| 2 | | 2 | |
| 3 | import { | 3 | import { |
| | 4 | ClerkPublishableKeyDecodeError, |
| | 5 | ClerkPublishableKeyFrontendApiError, |
| 4 | clerkFrontendApiHostnameFromPublishableKey, | 6 | clerkFrontendApiHostnameFromPublishableKey, |
| | 7 | clerkFrontendApiUrlFromPublishableKey, |
| 5 | isAllowedClerkFrontendApiHostname, | 8 | isAllowedClerkFrontendApiHostname, |
| 6 | } from "./relayAuth.ts"; | 9 | } from "./relayAuth.ts"; |
| 7 | | 10 | |
| 8 | const clerkPublishableKey = (hostname: string): string => `pk_test_${btoa(`${hostname}$`)}`; | 11 | const clerkPublishableKey = (hostname: string): string => `pk_test_${btoa(`${hostname}$`)}`; |
| 9 | | 12 | |
| | 13 | const captureError = (run: () => unknown): unknown => { |
| | 14 | try { |
| | 15 | run(); |
| | 16 | } catch (cause) { |
| | 17 | return cause; |
| | 18 | } |
| | 19 | throw new Error("Expected operation to throw"); |
| | 20 | }; |
| | 21 | |
| 10 | describe("Clerk relay auth", () => { | 22 | describe("Clerk relay auth", () => { |
| 11 | it("derives a custom Frontend API hostname from a Clerk publishable key", () => { | 23 | it("derives a custom Frontend API hostname from a Clerk publishable key", () => { |
| 12 | expect(clerkFrontendApiHostnameFromPublishableKey(clerkPublishableKey("clerk.t3.codes"))).toBe( | 24 | expect(clerkFrontendApiHostnameFromPublishableKey(clerkPublishableKey("clerk.t3.codes"))).toBe( |
| 13 | "clerk.t3.codes", | 25 | "clerk.t3.codes", |
| 14 | ); | 26 | ); |
| | 27 | expect(clerkFrontendApiUrlFromPublishableKey(clerkPublishableKey("clerk.t3.codes"))).toBe( |
| | 28 | "https://clerk.t3.codes", |
| | 29 | ); |
| | 30 | }); |
| | 31 | |
| | 32 | it("preserves Clerk publishable key decoding failures", () => { |
| | 33 | const error = captureError(() => clerkFrontendApiUrlFromPublishableKey("pk_test_%")); |
| | 34 | |
| | 35 | expect(error).toBeInstanceOf(ClerkPublishableKeyDecodeError); |
| | 36 | expect(error).toMatchObject({ keyPrefix: "pk_test" }); |
| | 37 | expect((error as ClerkPublishableKeyDecodeError).cause).toBeInstanceOf(Error); |
| | 38 | expect((error as Error).message).toBe("Failed to decode Clerk publishable key (pk_test)."); |
| | 39 | }); |
| | 40 | |
| | 41 | it("reports semantic frontend API failures without inventing a cause", () => { |
| | 42 | const emptyError = captureError(() => clerkFrontendApiUrlFromPublishableKey("pk_test_")); |
| | 43 | const pathFrontendApi = "clerk.t3.codes/path"; |
| | 44 | const pathError = captureError(() => |
| | 45 | clerkFrontendApiUrlFromPublishableKey(clerkPublishableKey(pathFrontendApi)), |
| | 46 | ); |
| | 47 | |
| | 48 | expect(emptyError).toBeInstanceOf(ClerkPublishableKeyFrontendApiError); |
| | 49 | expect(emptyError).toMatchObject({ |
| | 50 | keyPrefix: "pk_test", |
| | 51 | frontendApi: "", |
| | 52 | reason: "empty", |
| | 53 | }); |
| | 54 | expect((emptyError as Error & { cause?: unknown }).cause).toBeUndefined(); |
| | 55 | expect(pathError).toBeInstanceOf(ClerkPublishableKeyFrontendApiError); |
| | 56 | expect(pathError).toMatchObject({ |
| | 57 | keyPrefix: "pk_test", |
| | 58 | frontendApi: pathFrontendApi, |
| | 59 | reason: "contains-path", |
| | 60 | }); |
| | 61 | expect((pathError as Error & { cause?: unknown }).cause).toBeUndefined(); |
| | 62 | }); |
| | 63 | |
| | 64 | it("preserves URL parser failures for decoded frontend APIs", () => { |
| | 65 | const frontendApi = "[invalid-host"; |
| | 66 | const error = captureError(() => |
| | 67 | clerkFrontendApiHostnameFromPublishableKey(clerkPublishableKey(frontendApi)), |
| | 68 | ); |
| | 69 | |
| | 70 | expect(error).toBeInstanceOf(ClerkPublishableKeyFrontendApiError); |
| | 71 | expect(error).toMatchObject({ |
| | 72 | keyPrefix: "pk_test", |
| | 73 | frontendApi, |
| | 74 | reason: "invalid-url", |
| | 75 | }); |
| | 76 | expect((error as ClerkPublishableKeyFrontendApiError).cause).toBeInstanceOf(Error); |
| 15 | }); | 77 | }); |
| 16 | | 78 | |
| 17 | it("allows standard Clerk hosts and an exact configured custom hostname", () => { | 79 | it("allows standard Clerk hosts and an exact configured custom hostname", () => { |