| ... | ... | @@ -1,7 +1,8 @@ |
| 1 | 1 | import { assert, describe, it } from "@effect/vitest"; |
| 2 | import * as Cause from "effect/Cause"; |
| 2 | 3 | import * as Effect from "effect/Effect"; |
| 3 | 4 | import * as Layer from "effect/Layer"; |
| 4 | | import { vi } from "vite-plus/test"; |
| 5 | import { beforeEach, vi } from "vite-plus/test"; |
| 5 | 6 | |
| 6 | 7 | const { createClerkBridgeMock, storageAdapter, storageMock } = vi.hoisted(() => ({ |
| 7 | 8 | createClerkBridgeMock: vi.fn(), |
| ... | ... | @@ -24,7 +25,23 @@ vi.mock("@clerk/electron/storage", () => ({ |
| 24 | 25 | import * as DesktopClerk from "./DesktopClerk.ts"; |
| 25 | 26 | import * as DesktopEnvironment from "./DesktopEnvironment.ts"; |
| 26 | 27 | |
| 28 | const makeDesktopClerkLayer = (isDevelopment = true) => { |
| 29 | const environment = DesktopEnvironment.DesktopEnvironment.of({ |
| 30 | stateDir: "/tmp/t3-state", |
| 31 | isDevelopment, |
| 32 | } as unknown as DesktopEnvironment.DesktopEnvironment["Service"]); |
| 33 | |
| 34 | return DesktopClerk.layer.pipe( |
| 35 | Layer.provide(Layer.succeed(DesktopEnvironment.DesktopEnvironment, environment)), |
| 36 | ); |
| 37 | }; |
| 38 | |
| 27 | 39 | describe("DesktopClerk", () => { |
| 40 | beforeEach(() => { |
| 41 | createClerkBridgeMock.mockReset(); |
| 42 | storageMock.mockReset(); |
| 43 | }); |
| 44 | |
| 28 | 45 | it("derives the Clerk Frontend API hostname used by the desktop CSP", () => { |
| 29 | 46 | const publishableKey = `pk_test_${btoa("clerk.t3.codes$")}`; |
| 30 | 47 | |
| ... | ... | @@ -40,19 +57,9 @@ describe("DesktopClerk", () => { |
| 40 | 57 | const cleanup = vi.fn(); |
| 41 | 58 | storageMock.mockReturnValue(storageAdapter); |
| 42 | 59 | createClerkBridgeMock.mockReturnValue({ cleanup }); |
| 43 | | const environment = DesktopEnvironment.DesktopEnvironment.of({ |
| 44 | | stateDir: "/tmp/t3-state", |
| 45 | | isDevelopment: true, |
| 46 | | } as unknown as DesktopEnvironment.DesktopEnvironment["Service"]); |
| 47 | 60 | |
| 48 | 61 | return Effect.gen(function* () { |
| 49 | | yield* Effect.scoped( |
| 50 | | Layer.build( |
| 51 | | DesktopClerk.layer.pipe( |
| 52 | | Layer.provide(Layer.succeed(DesktopEnvironment.DesktopEnvironment, environment)), |
| 53 | | ), |
| 54 | | ), |
| 55 | | ); |
| 62 | yield* Effect.scoped(Layer.build(makeDesktopClerkLayer())); |
| 56 | 63 | |
| 57 | 64 | assert.deepEqual(createClerkBridgeMock.mock.calls, [ |
| 58 | 65 | [ |
| ... | ... | @@ -69,6 +76,54 @@ describe("DesktopClerk", () => { |
| 69 | 76 | }); |
| 70 | 77 | }); |
| 71 | 78 | |
| 79 | it.effect("preserves bridge initialization failures", () => { |
| 80 | const cause = new Error("bridge initialization failed"); |
| 81 | storageMock.mockReturnValue(storageAdapter); |
| 82 | createClerkBridgeMock.mockImplementationOnce(() => { |
| 83 | throw cause; |
| 84 | }); |
| 85 | |
| 86 | return Effect.gen(function* () { |
| 87 | const error = yield* Effect.scoped(Layer.build(makeDesktopClerkLayer())).pipe(Effect.flip); |
| 88 | |
| 89 | assert.instanceOf(error, DesktopClerk.DesktopClerkBridgeInitializationError); |
| 90 | assert.equal(error.stateDir, "/tmp/t3-state"); |
| 91 | assert.equal(error.isDevelopment, true); |
| 92 | assert.strictEqual(error.cause, cause); |
| 93 | assert.equal( |
| 94 | error.message, |
| 95 | 'Failed to initialize the desktop Clerk bridge for state directory "/tmp/t3-state" (development: true).', |
| 96 | ); |
| 97 | }); |
| 98 | }); |
| 99 | |
| 100 | it.effect("preserves bridge cleanup failures", () => { |
| 101 | const cause = new Error("bridge cleanup failed"); |
| 102 | storageMock.mockReturnValue(storageAdapter); |
| 103 | createClerkBridgeMock.mockReturnValue({ |
| 104 | cleanup: () => { |
| 105 | throw cause; |
| 106 | }, |
| 107 | }); |
| 108 | |
| 109 | return Effect.gen(function* () { |
| 110 | const exit = yield* Effect.exit(Effect.scoped(Layer.build(makeDesktopClerkLayer(false)))); |
| 111 | |
| 112 | assert.equal(exit._tag, "Failure"); |
| 113 | if (exit._tag === "Failure") { |
| 114 | const error = Cause.squash(exit.cause); |
| 115 | assert.instanceOf(error, DesktopClerk.DesktopClerkBridgeCleanupError); |
| 116 | assert.equal(error.stateDir, "/tmp/t3-state"); |
| 117 | assert.equal(error.isDevelopment, false); |
| 118 | assert.strictEqual(error.cause, cause); |
| 119 | assert.equal( |
| 120 | error.message, |
| 121 | 'Failed to clean up the desktop Clerk bridge for state directory "/tmp/t3-state" (development: false).', |
| 122 | ); |
| 123 | } |
| 124 | }); |
| 125 | }); |
| 126 | |
| 72 | 127 | it.each([ |
| 73 | 128 | { isDevelopment: true, scheme: "t3code-dev" }, |
| 74 | 129 | { isDevelopment: false, scheme: "t3code" }, |