269 lines · 7.9 KB · TypeScript
RawDownload
import { EnvironmentId } from "@t3tools/contracts";import { describe, expect, it, vi } from "vitest"; import { createEnvironmentConnection } from "./connection";import type { WsRpcClient } from "~/rpc/wsRpcClient"; function createTestClient() {  const lifecycleListeners = new Set<(event: any) => void>();  const configListeners = new Set<(event: any) => void>();  const terminalListeners = new Set<(event: any) => void>();  const shellListeners = new Set<(event: any) => void>();  let shellResubscribe: (() => void) | undefined;   const client = {    dispose: vi.fn(async () => undefined),    reconnect: vi.fn(async () => {      shellResubscribe?.();    }),    server: {      getConfig: vi.fn(async () => ({        environment: {          environmentId: EnvironmentId.make("env-1"),        },      })),      subscribeConfig: vi.fn((listener: (event: any) => void) => {        configListeners.add(listener);        return () => configListeners.delete(listener);      }),      subscribeLifecycle: vi.fn((listener: (event: any) => void) => {        lifecycleListeners.add(listener);        return () => lifecycleListeners.delete(listener);      }),      subscribeAuthAccess: () => () => undefined,      refreshProviders: vi.fn(async () => undefined),      upsertKeybinding: vi.fn(async () => undefined),      getSettings: vi.fn(async () => undefined),      updateSettings: vi.fn(async () => undefined),    },    orchestration: {      dispatchCommand: vi.fn(async () => undefined),      getTurnDiff: vi.fn(async () => undefined),      getFullThreadDiff: vi.fn(async () => undefined),      subscribeShell: vi.fn(        (listener: (event: any) => void, options?: { onResubscribe?: () => void }) => {          shellListeners.add(listener);          shellResubscribe = options?.onResubscribe;          queueMicrotask(() => {            listener({              kind: "snapshot",              snapshot: {                snapshotSequence: 1,                projects: [],                threads: [],                updatedAt: "2026-04-12T00:00:00.000Z",              },            });          });          return () => {            shellListeners.delete(listener);            if (shellResubscribe === options?.onResubscribe) {              shellResubscribe = undefined;            }          };        },      ),      subscribeThread: vi.fn(() => () => undefined),    },    terminal: {      open: vi.fn(async () => undefined),      write: vi.fn(async () => undefined),      resize: vi.fn(async () => undefined),      clear: vi.fn(async () => undefined),      restart: vi.fn(async () => undefined),      close: vi.fn(async () => undefined),      onEvent: (listener: (event: any) => void) => {        terminalListeners.add(listener);        return () => terminalListeners.delete(listener);      },    },    projects: {      searchEntries: vi.fn(async () => []),      writeFile: vi.fn(async () => undefined),    },    shell: {      openInEditor: vi.fn(async () => undefined),    },    git: {      runStackedAction: vi.fn(async () => ({}) as any),      resolvePullRequest: vi.fn(async () => undefined),      preparePullRequestThread: vi.fn(async () => undefined),    },  } as unknown as WsRpcClient;   return {    client,    emitWelcome: (environmentId: EnvironmentId) => {      for (const listener of lifecycleListeners) {        listener({          type: "welcome",          payload: {            environment: {              environmentId,            },          },        });      }    },    emitConfigSnapshot: (environmentId: EnvironmentId) => {      for (const listener of configListeners) {        listener({          type: "snapshot",          config: {            environment: {              environmentId,            },          },        });      }    },    emitShellSnapshot: (snapshotSequence: number) => {      for (const listener of shellListeners) {        listener({          kind: "snapshot",          snapshot: {            snapshotSequence,            projects: [],            threads: [],            updatedAt: "2026-04-12T00:00:00.000Z",          },        });      }    },  };} describe("createEnvironmentConnection", () => {  it("bootstraps from the shell subscription snapshot", async () => {    const environmentId = EnvironmentId.make("env-1");    const { client } = createTestClient();    const syncShellSnapshot = vi.fn();     const connection = createEnvironmentConnection({      kind: "saved",      knownEnvironment: {        id: "env-1",        label: "Remote env",        source: "manual",        target: {          httpBaseUrl: "http://example.test",          wsBaseUrl: "ws://example.test",        },        environmentId,      },      client,      applyShellEvent: vi.fn(),      syncShellSnapshot,      applyTerminalEvent: vi.fn(),    });     await connection.ensureBootstrapped();     expect(syncShellSnapshot).toHaveBeenCalledWith(      expect.objectContaining({ snapshotSequence: 1 }),      environmentId,    );     await connection.dispose();  });   it("rejects welcome/config identity drift", async () => {    const environmentId = EnvironmentId.make("env-1");    const { client, emitWelcome } = createTestClient();     const connection = createEnvironmentConnection({      kind: "saved",      knownEnvironment: {        id: "env-1",        label: "Remote env",        source: "manual",        target: {          httpBaseUrl: "http://example.test",          wsBaseUrl: "ws://example.test",        },        environmentId,      },      client,      applyShellEvent: vi.fn(),      syncShellSnapshot: vi.fn(),      applyTerminalEvent: vi.fn(),    });     expect(() => emitWelcome(EnvironmentId.make("env-2"))).toThrow(      "Environment connection env-1 changed identity to env-2 via server lifecycle welcome.",    );     await connection.dispose();  });   it("waits for a fresh shell snapshot after reconnect", async () => {    const environmentId = EnvironmentId.make("env-1");    const { client, emitShellSnapshot } = createTestClient();    const syncShellSnapshot = vi.fn();     const connection = createEnvironmentConnection({      kind: "saved",      knownEnvironment: {        id: "env-1",        label: "Remote env",        source: "manual",        target: {          httpBaseUrl: "http://example.test",          wsBaseUrl: "ws://example.test",        },        environmentId,      },      client,      applyShellEvent: vi.fn(),      syncShellSnapshot,      applyTerminalEvent: vi.fn(),    });     await connection.ensureBootstrapped();     const reconnectPromise = connection.reconnect();    await Promise.resolve();    expect(syncShellSnapshot).toHaveBeenCalledTimes(1);     emitShellSnapshot(2);    await reconnectPromise;     expect(client.reconnect).toHaveBeenCalledTimes(1);    expect(syncShellSnapshot).toHaveBeenCalledTimes(2);    expect(syncShellSnapshot).toHaveBeenLastCalledWith(      expect.objectContaining({ snapshotSequence: 2 }),      environmentId,    );     await connection.dispose();  });   it("skips primary lifecycle/config subscriptions when no handlers are registered", async () => {    const environmentId = EnvironmentId.make("env-1");    const { client } = createTestClient();     const connection = createEnvironmentConnection({      kind: "primary",      knownEnvironment: {        id: "env-1",        label: "Local env",        source: "manual",        target: {          httpBaseUrl: "http://example.test",          wsBaseUrl: "ws://example.test",        },        environmentId,      },      client,      applyShellEvent: vi.fn(),      syncShellSnapshot: vi.fn(),      applyTerminalEvent: vi.fn(),    });     expect(client.server.subscribeLifecycle).not.toHaveBeenCalled();    expect(client.server.subscribeConfig).not.toHaveBeenCalled();    expect(client.orchestration.subscribeShell).toHaveBeenCalledOnce();     await connection.dispose();  });});