1/// <reference types="node" />
2
3import * as NodeCrypto from "node:crypto";
4
5import { vi } from "vite-plus/test";
6import { assert, describe, it } from "@effect/vitest";
7import { ManagedRelay } from "@t3tools/client-runtime/relay";
8import * as Effect from "effect/Effect";
9import * as Layer from "effect/Layer";
10import { FetchHttpClient } from "effect/http";
11
12import * as Dpop from "./dpop";
13import * as ManagedRelayLayer from "./managedRelayLayer";
14
15vi.mock("expo-crypto", () => ({
16 CryptoDigestAlgorithm: { SHA256: "SHA-256" },
17 getRandomBytes: (byteCount: number) => new Uint8Array(NodeCrypto.randomBytes(byteCount)),
18 getRandomBytesAsync: (byteCount: number) =>
19 Promise.resolve(new Uint8Array(NodeCrypto.randomBytes(byteCount))),
20 digest: (algorithm: string, data: Uint8Array) =>
21 Promise.resolve(new Uint8Array(NodeCrypto.createHash(algorithm).update(data).digest()).buffer),
22}));
23
24const secureStore = new Map<string, string>();
25let failNextRead = false;
26vi.mock("expo", async () => {
27 const { createNodeDpopKeysModule } = await import("./dpop.testing");
28 const dpopKeys = createNodeDpopKeysModule();
29 return { requireNativeModule: () => dpopKeys };
30});
31
32vi.mock("expo-secure-store", () => ({
33 getItemAsync: (key: string) => {
34 if (failNextRead) {
35 failNextRead = false;
36 return Promise.reject(new Error("keychain locked"));
37 }
38 return Promise.resolve(secureStore.get(key) ?? null);
39 },
40 setItemAsync: (key: string, value: string) => {
41 secureStore.set(key, value);
42 return Promise.resolve();
43 },
44 deleteItemAsync: (key: string) => {
45 secureStore.delete(key);
46 return Promise.resolve();
47 },
48}));
49
50describe("managed relay DPoP signer", () => {
51 it.effect("loads the proof key again after a failed read", () =>
52 Effect.gen(function* () {
53 const signer = yield* ManagedRelay.ManagedRelayDpopSigner;
54 failNextRead = true;
55 const failed = yield* Effect.flip(signer.thumbprint);
56 assert.equal(failed._tag, "ManagedRelayDpopKeyLoadError");
57
58 const thumbprint = yield* signer.thumbprint;
59 assert.equal(yield* signer.thumbprint, thumbprint);
60 }).pipe(
61 Effect.provide(
62 ManagedRelayLayer.layer("https://relay.example.test").pipe(
63 Layer.provide(Layer.mergeAll(FetchHttpClient.layer, Dpop.layer)),
64 ),
65 ),
66 ),
67 );
68});