import { beforeEach, describe, expect, it, vi } from "vitest"; import type { DaemonClientConfig } from "@getpaseo/client/internal/daemon-client"; import type { DaemonConnectionDependencies, DaemonProbeClient } from "./test-daemon-connection"; class FakeDaemonClient implements DaemonProbeClient { readonly lastError: string | null; constructor( private readonly probe: FakeDaemonProbe, readonly config: DaemonClientConfig, ) { this.lastError = probe.nextLastError; } async connect(): Promise { if (this.probe.nextConnectError) { throw this.probe.nextConnectError; } } getLastServerInfoMessage() { return { serverId: "srv_probe_test", hostname: "probe-host", }; } async close(): Promise { this.probe.closedClients.push(this); } } class FakeDaemonProbe { createdClients: FakeDaemonClient[] = []; closedClients: FakeDaemonClient[] = []; clientIdsRequested = 0; nextConnectError: Error | null = null; nextLastError: string | null = null; readonly deps: DaemonConnectionDependencies = { getClientId: async () => { this.clientIdsRequested += 1; return "cid_shared_probe_test"; }, resolveAppVersion: () => null, createLocalTransportFactory: () => null, buildLocalTransportUrl: ({ transportType, transportPath }) => `paseo+local://${transportType}?path=${encodeURIComponent(transportPath)}`, createClient: (config) => { const client = new FakeDaemonClient(this, config); this.createdClients.push(client); return client; }, }; failNextConnection(error: Error, lastError: string | null): void { this.nextConnectError = error; this.nextLastError = lastError; } createdConfigs(): DaemonClientConfig[] { return this.createdClients.map((client) => client.config); } } describe("test-daemon-connection connectToDaemon", () => { let probe: FakeDaemonProbe; beforeEach(() => { vi.stubGlobal("__DEV__", false); probe = new FakeDaemonProbe(); }); it("reuses the app clientId for direct connections", async () => { const { connectToDaemon } = await import("./test-daemon-connection"); const first = await connectToDaemon( { id: "direct:lan:6767", type: "directTcp", endpoint: "lan:6767", }, undefined, probe.deps, ); await first.client.close(); const second = await connectToDaemon( { id: "direct:lan:6767", type: "directTcp", endpoint: "lan:6767", }, undefined, probe.deps, ); await second.client.close(); const [firstConfig, secondConfig] = probe.createdConfigs(); expect(firstConfig?.clientId).toBe("cid_shared_probe_test"); expect(secondConfig?.clientId).toBe("cid_shared_probe_test"); expect(probe.clientIdsRequested).toBe(2); }); it("encodes the local socket target into the client config", async () => { const { connectToDaemon } = await import("./test-daemon-connection"); const result = await connectToDaemon( { id: "socket:/tmp/paseo.sock", type: "directSocket", path: "/tmp/paseo.sock", }, undefined, probe.deps, ); await result.client.close(); expect(probe.createdConfigs()[0]?.url).toBe("paseo+local://socket?path=%2Ftmp%2Fpaseo.sock"); }); it("passes direct TCP connection passwords into the client config", async () => { const { connectToDaemon } = await import("./test-daemon-connection"); const result = await connectToDaemon( { id: "direct:lan:6767", type: "directTcp", endpoint: "lan:6767", password: "shared-secret", }, undefined, probe.deps, ); await result.client.close(); expect(probe.createdConfigs()[0]?.password).toBe("shared-secret"); }); it("uses relay TLS from the stored connection", async () => { const { connectToDaemon } = await import("./test-daemon-connection"); const tlsResult = await connectToDaemon( { id: "relay:wss:[::1]:443", type: "relay", relayEndpoint: "[::1]:443", useTls: true, daemonPublicKeyB64: "pubkey", }, { serverId: "srv_probe_test" }, probe.deps, ); await tlsResult.client.close(); const plainResult = await connectToDaemon( { id: "relay:relay.paseo.sh:443", type: "relay", relayEndpoint: "relay.paseo.sh:443", useTls: false, daemonPublicKeyB64: "pubkey", }, { serverId: "srv_probe_test" }, probe.deps, ); await plainResult.client.close(); expect(probe.createdConfigs()[0]?.url).toMatch(/^wss:\/\/\[::1\]\/ws\?/); expect(probe.createdConfigs()[1]?.url).toMatch(/^ws:\/\/relay\.paseo\.sh:443\/ws\?/); }); it("surfaces auth rejection as an incorrect password", async () => { const { connectToDaemon } = await import("./test-daemon-connection"); probe.failNextConnection( new Error("Transport closed (code 4001)"), "Transport closed (code 4001)", ); await expect( connectToDaemon( { id: "direct:lan:6767", type: "directTcp", endpoint: "lan:6767", password: "wrong-secret", }, undefined, probe.deps, ), ).rejects.toMatchObject({ message: "Incorrect password", }); }); it("keeps generic transport failures generic when a password was supplied", async () => { const { connectToDaemon } = await import("./test-daemon-connection"); probe.failNextConnection(new Error("Transport error"), "Transport error"); await expect( connectToDaemon( { id: "direct:lan:6767", type: "directTcp", endpoint: "lan:6767", password: "shared-secret", }, undefined, probe.deps, ), ).rejects.toMatchObject({ message: "Transport error", }); }); });