1import {
2 type DesktopBridge,
3 EnvironmentId,
4 type RelayClientInstallProgressEvent,
5 WS_METHODS,
6} from "@t3tools/contracts";
7import { RelayWebClientId } from "@t3tools/contracts/relay";
8import { describe, expect, it } from "@effect/vitest";
9import * as Effect from "effect/Effect";
10import * as Layer from "effect/Layer";
11import * as Option from "effect/Option";
12import * as Stream from "effect/Stream";
13import * as SubscriptionRef from "effect/SubscriptionRef";
14import { HttpClient } from "effect/http";
15import { afterEach, beforeEach, vi } from "vite-plus/test";
16import {
17 AVAILABLE_CONNECTION_STATE,
18 type PreparedConnection,
19 PrimaryConnectionTarget,
20 EnvironmentSupervisor,
21} from "@t3tools/client-runtime/connection";
22import { type RpcSession } from "@t3tools/client-runtime/rpc";
23import { EnvironmentRegistry } from "@t3tools/client-runtime/connection";
24import { ManagedRelay } from "@t3tools/client-runtime/relay";
25import { layerRemoteHttpClient } from "@t3tools/client-runtime/rpc";
26import { __resetDesktopPrimaryAuthForTests } from "../environments/primary/desktopAuth";
27
28import {
29 linkPrimaryEnvironmentToCloud,
30 readPrimaryCloudLinkState,
31 type CloudLinkTarget,
32 unlinkPrimaryEnvironmentFromCloud,
33 updatePrimaryCloudPreferences,
34} from "./linkEnvironment";
35
36const TARGET: CloudLinkTarget = {
37 environmentId: "environment-1",
38 label: "Desktop",
39 httpBaseUrl: "http://127.0.0.1:3000",
40 wsBaseUrl: "ws://127.0.0.1:3000",
41};
42
43const relayClientInstallDialog = vi.hoisted(() => ({
44 requestConfirmation: vi.fn(),
45 reportProgress: vi.fn(),
46 finish: vi.fn(),
47}));
48
49vi.mock("./relayClientInstallDialog", () => ({
50 requestRelayClientInstallConfirmation: relayClientInstallDialog.requestConfirmation,
51 reportRelayClientInstallProgress: relayClientInstallDialog.reportProgress,
52 finishRelayClientInstall: relayClientInstallDialog.finish,
53}));
54
55const createProof = vi.fn(() => Effect.succeed("dpop-proof"));
56const layerDpopSigner = Layer.succeed(
57 ManagedRelay.ManagedRelayDpopSigner,
58 ManagedRelay.ManagedRelayDpopSigner.of({
59 thumbprint: Effect.succeed("thumbprint"),
60 createProof,
61 }),
62);
63
64function layerRelay() {
65 const layerHttp = layerRemoteHttpClient(globalThis.fetch);
66 return Layer.mergeAll(
67 layerHttp,
68 ManagedRelay.layer({
69 relayUrl: "https://relay.example.test",
70 clientId: RelayWebClientId,
71 }).pipe(Layer.provideMerge(layerDpopSigner), Layer.provide(layerHttp)),
72 );
73}
74
75function layerRegistry(options?: {
76 readonly status?: { readonly status: "available"; readonly version: string };
77 readonly installEvents?: ReadonlyArray<RelayClientInstallProgressEvent>;
78}) {
79 return Layer.effect(
80 EnvironmentRegistry.EnvironmentRegistry,
81 Effect.gen(function* () {
82 const client = {
83 [WS_METHODS.cloudGetRelayClientStatus]: () =>
84 Effect.succeed(options?.status ?? { status: "available", version: "2026.6.0" }),
85 [WS_METHODS.cloudInstallRelayClient]: () =>
86 Stream.fromIterable(options?.installEvents ?? []),
87 } as unknown as RpcSession["client"];
88 const session: RpcSession = {
89 client,
90 initialConfig: Effect.never,
91 subscribeServerConfig: (input) => client.subscribeServerConfig(input),
92 ready: Effect.void,
93 probe: Effect.void,
94 closed: Effect.never,
95 };
96 const target = new PrimaryConnectionTarget({
97 environmentId: EnvironmentId.make(TARGET.environmentId),
98 label: TARGET.label,
99 httpBaseUrl: TARGET.httpBaseUrl,
100 wsBaseUrl: TARGET.wsBaseUrl,
101 });
102 const supervisor = EnvironmentSupervisor.EnvironmentSupervisor.of({
103 target,
104 state: yield* SubscriptionRef.make(AVAILABLE_CONNECTION_STATE),
105 session: yield* SubscriptionRef.make(Option.some(session)),
106 prepared: yield* SubscriptionRef.make(Option.none<PreparedConnection>()),
107 connect: Effect.void,
108 disconnect: Effect.void,
109 retryNow: Effect.void,
110 } satisfies EnvironmentSupervisor.EnvironmentSupervisor["Service"]);
111 const registry = {
112 run: <A, E, R>(_environmentId: EnvironmentId, effect: Effect.Effect<A, E, R>) =>
113 Effect.provideService(effect, EnvironmentSupervisor.EnvironmentSupervisor, supervisor),
114 runStream: <A, E, R>(_environmentId: EnvironmentId, stream: Stream.Stream<A, E, R>) =>
115 Stream.provideService(stream, EnvironmentSupervisor.EnvironmentSupervisor, supervisor),
116 } as unknown as EnvironmentRegistry.EnvironmentRegistry["Service"];
117 return EnvironmentRegistry.EnvironmentRegistry.of(registry);
118 }),
119 );
120}
121
122function layerServices(options?: Parameters<typeof layerRegistry>[0]) {
123 return Layer.mergeAll(layerRelay(), layerRegistry(options));
124}
125
126function withServices<A, E>(
127 effect: Effect.Effect<
128 A,
129 E,
130 | HttpClient.HttpClient
131 | ManagedRelay.ManagedRelayClient
132 | EnvironmentRegistry.EnvironmentRegistry
133 >,
134 options?: Parameters<typeof layerRegistry>[0],
135) {
136 return effect.pipe(Effect.provide(layerServices(options)));
137}
138
139function bodyText(body: BodyInit | null | undefined): string {
140 return body instanceof Uint8Array ? new TextDecoder().decode(body) : String(body ?? "");
141}
142
143beforeEach(() => {
144 vi.clearAllMocks();
145 vi.stubEnv("VITE_T3CODE_RELAY_URL", "https://relay.example.test");
146 relayClientInstallDialog.requestConfirmation.mockResolvedValue(true);
147});
148
149afterEach(() => {
150 __resetDesktopPrimaryAuthForTests();
151 vi.unstubAllGlobals();
152 vi.unstubAllEnvs();
153 vi.restoreAllMocks();
154});
155
156describe("web cloud link environment client", () => {
157 it.effect("reads primary cloud link state from the explicit target", () =>
158 Effect.gen(function* () {
159 const fetchMock = vi.fn().mockResolvedValue(
160 Response.json({
161 linked: true,
162 cloudUserId: "user-1",
163 relayUrl: "https://relay.example.test",
164 relayIssuer: "https://relay.example.test",
165 managedTunnelActive: true,
166 publishAgentActivity: false,
167 }),
168 );
169 vi.stubGlobal("fetch", fetchMock);
170
171 const state = yield* withServices(readPrimaryCloudLinkState({ target: TARGET }));
172
173 expect(Option.fromNullishOr(state)).toEqual(
174 Option.some({
175 linked: true,
176 cloudUserId: "user-1",
177 relayUrl: "https://relay.example.test",
178 relayIssuer: "https://relay.example.test",
179 managedTunnelActive: true,
180 publishAgentActivity: false,
181 }),
182 );
183 expect(String(fetchMock.mock.calls[0]?.[0])).toBe(
184 "http://127.0.0.1:3000/api/connect/link-state",
185 );
186 }),
187 );
188
189 it.effect("uses desktop bearer auth for primary cloud link state", () =>
190 Effect.gen(function* () {
191 const fetchMock = vi.fn().mockResolvedValue(
192 Response.json({
193 linked: true,
194 cloudUserId: "user-1",
195 relayUrl: "https://relay.example.test",
196 relayIssuer: "https://relay.example.test",
197 managedTunnelActive: true,
198 publishAgentActivity: false,
199 }),
200 );
201 vi.stubGlobal("fetch", fetchMock);
202 vi.stubGlobal("window", {
203 location: { origin: "t3code://app" },
204 desktopBridge: {
205 getLocalEnvironmentBearerToken: vi.fn().mockResolvedValue("desktop-bearer-token"),
206 } as unknown as DesktopBridge,
207 });
208
209 yield* withServices(readPrimaryCloudLinkState({ target: TARGET }));
210
211 const request = new Request(fetchMock.mock.calls[0]?.[0], fetchMock.mock.calls[0]?.[1]);
212 expect(request.credentials).not.toBe("include");
213 expect(request.headers.get("authorization")).toBe("Bearer desktop-bearer-token");
214 }),
215 );
216
217 it.effect("updates agent activity publishing for the explicit primary target", () =>
218 Effect.gen(function* () {
219 const fetchMock = vi.fn().mockResolvedValue(
220 Response.json({
221 linked: true,
222 cloudUserId: "user-1",
223 relayUrl: "https://relay.example.test",
224 relayIssuer: "https://relay.example.test",
225 managedTunnelActive: true,
226 publishAgentActivity: true,
227 }),
228 );
229 vi.stubGlobal("fetch", fetchMock);
230
231 const state = yield* withServices(
232 updatePrimaryCloudPreferences({
233 target: TARGET,
234 publishAgentActivity: true,
235 }),
236 );
237
238 expect(state.publishAgentActivity).toBe(true);
239 expect(String(fetchMock.mock.calls[0]?.[0])).toBe(
240 "http://127.0.0.1:3000/api/connect/preferences",
241 );
242 expect(fetchMock.mock.calls[0]?.[1]?.method).toBe("POST");
243 expect(JSON.parse(bodyText(fetchMock.mock.calls[0]?.[1]?.body))).toEqual({
244 publishAgentActivity: true,
245 });
246 }),
247 );
248
249 it.effect("links an available primary environment without invoking installation", () =>
250 Effect.gen(function* () {
251 const fetchMock = vi
252 .fn()
253 .mockResolvedValueOnce(
254 Response.json({
255 challenge: "challenge",
256 expiresAt: "2026-06-06T00:05:00.000Z",
257 }),
258 )
259 .mockResolvedValueOnce(Response.json("signed-proof"))
260 .mockResolvedValueOnce(
261 Response.json({
262 ok: true,
263 environmentId: TARGET.environmentId,
264 endpoint: {
265 httpBaseUrl: "https://desktop.example.test",
266 wsBaseUrl: "wss://desktop.example.test",
267 providerKind: "cloudflare_tunnel",
268 },
269 endpointRuntime: null,
270 relayIssuer: "https://relay.example.test",
271 cloudUserId: "user-1",
272 environmentCredential: "environment-credential",
273 cloudMintPublicKey: "public-key",
274 }),
275 )
276 .mockResolvedValueOnce(
277 Response.json({ ok: true, endpointRuntimeStatus: { status: "configured" } }),
278 );
279 vi.stubGlobal("fetch", fetchMock);
280
281 yield* withServices(
282 linkPrimaryEnvironmentToCloud({
283 target: TARGET,
284 clerkToken: "clerk-token",
285 }),
286 );
287
288 expect(relayClientInstallDialog.requestConfirmation).not.toHaveBeenCalled();
289 expect(String(fetchMock.mock.calls[1]?.[0])).toBe(
290 "http://127.0.0.1:3000/api/connect/link-proof",
291 );
292 expect(JSON.parse(bodyText(fetchMock.mock.calls[1]?.[1]?.body))).toMatchObject({
293 challenge: "challenge",
294 endpoint: {
295 httpBaseUrl: TARGET.httpBaseUrl,
296 wsBaseUrl: TARGET.wsBaseUrl,
297 },
298 });
299 }),
300 );
301
302 it.effect("links publish-only without a managed tunnel", () =>
303 Effect.gen(function* () {
304 const fetchMock = vi
305 .fn()
306 .mockResolvedValueOnce(
307 Response.json({
308 challenge: "challenge",
309 expiresAt: "2026-06-06T00:05:00.000Z",
310 }),
311 )
312 .mockResolvedValueOnce(Response.json("signed-proof"))
313 .mockResolvedValueOnce(
314 Response.json({
315 ok: true,
316 environmentId: TARGET.environmentId,
317 endpoint: {
318 httpBaseUrl: TARGET.httpBaseUrl,
319 wsBaseUrl: TARGET.wsBaseUrl,
320 providerKind: "manual",
321 },
322 endpointRuntime: null,
323 relayIssuer: "https://relay.example.test",
324 cloudUserId: "user-1",
325 environmentCredential: "environment-credential",
326 cloudMintPublicKey: "public-key",
327 }),
328 )
329 .mockResolvedValueOnce(
330 Response.json({ ok: true, endpointRuntimeStatus: { status: "disabled" } }),
331 );
332 vi.stubGlobal("fetch", fetchMock);
333
334 yield* withServices(
335 linkPrimaryEnvironmentToCloud({
336 target: TARGET,
337 clerkToken: "clerk-token",
338 mode: "publish_only",
339 }),
340 );
341
342 expect(JSON.parse(bodyText(fetchMock.mock.calls[0]?.[1]?.body))).toMatchObject({
343 managedTunnelsEnabled: false,
344 });
345 expect(JSON.parse(bodyText(fetchMock.mock.calls[1]?.[1]?.body))).toMatchObject({
346 endpoint: { providerKind: "manual" },
347 });
348 }),
349 );
350
351 it.effect("installs a missing relay client before linking", () =>
352 Effect.gen(function* () {
353 vi.stubGlobal("fetch", vi.fn().mockResolvedValue(Response.json({ malformed: true })));
354
355 yield* withServices(
356 linkPrimaryEnvironmentToCloud({
357 target: TARGET,
358 clerkToken: "clerk-token",
359 }),
360 {
361 status: { status: "available", version: "2026.6.0" },
362 installEvents: [],
363 },
364 ).pipe(Effect.flip);
365
366 expect(relayClientInstallDialog.requestConfirmation).not.toHaveBeenCalled();
367 }),
368 );
369
370 it.effect("unlinks locally before revoking the relay record", () =>
371 Effect.gen(function* () {
372 const fetchMock = vi
373 .fn()
374 .mockResolvedValueOnce(
375 Response.json({ ok: true, endpointRuntimeStatus: { status: "disabled" } }),
376 )
377 .mockResolvedValueOnce(Response.json({ ok: true }));
378 vi.stubGlobal("fetch", fetchMock);
379
380 yield* withServices(
381 unlinkPrimaryEnvironmentFromCloud({
382 target: TARGET,
383 clerkToken: "clerk-token",
384 }),
385 );
386
387 expect(String(fetchMock.mock.calls[0]?.[0])).toBe("http://127.0.0.1:3000/api/connect/unlink");
388 expect(String(fetchMock.mock.calls[1]?.[0])).toContain(
389 `/v1/client/environment-links/${TARGET.environmentId}`,
390 );
391 }),
392 );
393});