1import * as NodeCrypto from "@effect/platform-node/NodeCrypto";
2import { assert, it } from "@effect/vitest";
3import * as Effect from "effect/Effect";
4import * as Option from "effect/Option";
5import * as ServerSecretStore from "../auth/ServerSecretStore.ts";
6import * as ProviderCredentialStore from "./ProviderCredentialStore.ts";
7
8it.effect("isolates provider bindings and preserves opaque credentials", () =>
9 Effect.gen(function* () {
10 const data = new Map<string, Uint8Array>();
11 const secretStore = ServerSecretStore.ServerSecretStore.of({
12 get: (name) => Effect.sync(() => Option.fromUndefinedOr(data.get(name))),
13 set: (name, value) =>
14 Effect.sync(() => {
15 data.set(name, value);
16 }),
17 remove: (name) =>
18 Effect.sync(() => {
19 data.delete(name);
20 }),
21 create: () => Effect.die("unused"),
22 getOrCreateRandom: () => Effect.die("unused"),
23 });
24 const a = yield* ProviderCredentialStore.make("cursor", "../../personal").pipe(
25 Effect.provideService(ServerSecretStore.ServerSecretStore, secretStore),
26 );
27 const b = yield* ProviderCredentialStore.make("cursor", "work").pipe(
28 Effect.provideService(ServerSecretStore.ServerSecretStore, secretStore),
29 );
30 const c = yield* ProviderCredentialStore.make("other", "../../personal").pipe(
31 Effect.provideService(ServerSecretStore.ServerSecretStore, secretStore),
32 );
33 const bytes = Uint8Array.from([0, 255, 128, 1]);
34 yield* a.set(bytes);
35 assert.deepStrictEqual(Option.getOrThrow(yield* a.get), bytes);
36 assert.isTrue(Option.isNone(yield* b.get));
37 assert.isTrue(Option.isNone(yield* c.get));
38 assert.isFalse(a.binding.key.includes("/"));
39 // Stored credentials are found by this name, so it must not change between releases.
40 assert.strictEqual(
41 a.binding.key,
42 "provider-auth-db3cc9436c8272847fe0c152ba8b0d4695af90acd916f555649e17ba6efe6094",
43 );
44 const long = yield* ProviderCredentialStore.make("a".repeat(64), "b".repeat(64)).pipe(
45 Effect.provideService(ServerSecretStore.ServerSecretStore, secretStore),
46 );
47 assert.isBelow(long.binding.key.length, 255);
48 const delimiter = yield* ProviderCredentialStore.make("cur", "sor../../personal").pipe(
49 Effect.provideService(ServerSecretStore.ServerSecretStore, secretStore),
50 );
51 assert.notStrictEqual(delimiter.binding.key, a.binding.key);
52 yield* a.remove;
53 assert.isTrue(Option.isNone(yield* a.get));
54 }).pipe(Effect.provide(NodeCrypto.layer)),
55);