1import {
2 OAUTH_CLIENT_ID,
3 exchange,
4 usesCurrentClient,
5} from "@/Planetscale/OAuthClient.ts";
6import { describe, expect, it } from "alchemy-test";
7import * as Effect from "effect/Effect";
8import * as Redacted from "effect/Redacted";
9import * as FetchHttpClient from "effect/http/FetchHttpClient";
10
11describe(
12 "PlanetScale OAuth client credentials",
13 { tags: ["unit", "provider:planetscale", "local"] },
14 () => {
15 it("maps the token response onto stored credentials", async () => {
16 // SAFETY: the test double implements the callable fetch contract; Bun's
17 // nonstandard static `preconnect` member is not used by FetchHttpClient.
18 const fetch = (async (_input, _init) =>
19 new Response(
20 JSON.stringify({
21 access_token: "access",
22 refresh_token: "refresh",
23 expires_in: 3600,
24 scope: "read write",
25 }),
26 { status: 200, headers: { "Content-Type": "application/json" } },
27 )) as typeof globalThis.fetch;
28
29 const before = Date.now();
30 const credentials = await Effect.runPromise(
31 exchange("code").pipe(
32 Effect.provideService(FetchHttpClient.Fetch, fetch),
33 ),
34 );
35 expect(credentials.type).toBe("oauth");
36 expect(credentials.clientId).toBe(OAUTH_CLIENT_ID);
37 expect(Redacted.value(credentials.access)).toBe("access");
38 expect(Redacted.value(credentials.refresh)).toBe("refresh");
39 expect(credentials.scopes).toEqual(["read", "write"]);
40 expect(credentials.expires).toBeGreaterThanOrEqual(
41 before + 3_600_000 - 1_000,
42 );
43 });
44
45 it("flags credentials issued to a rotated client id as stale", () => {
46 expect(usesCurrentClient({ clientId: OAUTH_CLIENT_ID })).toBe(true);
47 expect(usesCurrentClient({ clientId: "pscale_app_old" })).toBe(false);
48 // Credentials persisted before clientId was stored must read as stale so
49 // login falls back to a fresh browser authorization.
50 expect(usesCurrentClient({})).toBe(false);
51 });
52 },
53);