648 lines · 21.2 KB · TypeScript
RawDownload
import { QueryClient } from "@tanstack/react-query";import type { WsRpcClient } from "@t3tools/client-runtime";import {  EnvironmentId,  ProjectId,  ProviderInstanceId,  ThreadId,  TurnId,  type OrchestrationShellSnapshot,} from "@t3tools/contracts";import { afterEach, beforeEach, describe, expect, it, vi } from "vite-plus/test"; const mockSubscribeThread = vi.fn();const mockThreadUnsubscribe = vi.fn();const mockCreateEnvironmentConnection = vi.fn();const mockCreateWsRpcClient = vi.fn();const mockWaitForSavedEnvironmentRegistryHydration = vi.fn();const mockListSavedEnvironmentRecords = vi.fn();const mockGetSavedEnvironmentRecord = vi.fn();const mockReadSavedEnvironmentBearerToken = vi.fn();const mockReadSavedEnvironmentCredential = vi.fn();const mockSavedEnvironmentRegistrySubscribe = vi.fn();const mockGetPrimaryKnownEnvironment = vi.hoisted(() => vi.fn());const mockFetchRemoteSessionState = vi.fn();const mockResolveRemoteWebSocketConnectionUrl = vi.fn(async () => "ws://remote.example.test/ws");const mockRemoteHttpRunPromise = vi.fn((effect: Promise<unknown>) => effect);const mockConnectionReconnects: Array<ReturnType<typeof vi.fn>> = [];let savedEnvironmentRegistryListener: (() => void) | null = null; function MockWsTransport() {  return undefined;} vi.mock("../primary", () => ({  getPrimaryKnownEnvironment: mockGetPrimaryKnownEnvironment,})); vi.mock("../../lib/runtime", () => ({  webRuntime: {    runPromise: mockRemoteHttpRunPromise,  },})); vi.mock("./catalog", () => ({  getSavedEnvironmentRecord: mockGetSavedEnvironmentRecord,  hasSavedEnvironmentRegistryHydrated: vi.fn(() => true),  listSavedEnvironmentRecords: mockListSavedEnvironmentRecords,  persistSavedEnvironmentRecord: vi.fn(),  readSavedEnvironmentBearerToken: mockReadSavedEnvironmentBearerToken,  readSavedEnvironmentCredential: mockReadSavedEnvironmentCredential,  removeSavedEnvironmentBearerToken: vi.fn(),  useSavedEnvironmentRegistryStore: {    subscribe: mockSavedEnvironmentRegistrySubscribe,    getState: () => ({      upsert: vi.fn(),      remove: vi.fn(),      markConnected: vi.fn(),      rename: vi.fn(),    }),  },  useSavedEnvironmentRuntimeStore: {    getState: () => ({      ensure: vi.fn(),      patch: vi.fn(),      clear: vi.fn(),    }),  },  waitForSavedEnvironmentRegistryHydration: mockWaitForSavedEnvironmentRegistryHydration,  writeSavedEnvironmentBearerToken: vi.fn(),  writeSavedEnvironmentCredential: vi.fn(),})); vi.mock("./connection", async (importOriginal) => ({  ...(await importOriginal<typeof import("./connection")>()),  createEnvironmentConnection: mockCreateEnvironmentConnection,})); vi.mock("@t3tools/client-runtime", async (importOriginal) => {  const actual = await importOriginal<typeof import("@t3tools/client-runtime")>();  const stubWsClient: WsRpcClient = {    dispose: async () => undefined,    reconnect: async () => undefined,    isHeartbeatFresh: () => false,    cloud: {      getRelayClientStatus: vi.fn(),      installRelayClient: vi.fn(),    },    orchestration: {      dispatchCommand: vi.fn(),      getTurnDiff: vi.fn(),      getFullThreadDiff: vi.fn(),      getArchivedShellSnapshot: vi.fn(),      subscribeShell: vi.fn(() => () => undefined),      subscribeThread: mockSubscribeThread,    },    terminal: {      open: vi.fn(),      attach: vi.fn(() => () => undefined),      write: vi.fn(),      resize: vi.fn(),      clear: vi.fn(),      restart: vi.fn(),      close: vi.fn(),      onEvent: vi.fn(() => () => undefined),      onMetadata: vi.fn(() => () => undefined),    },    projects: {      searchEntries: vi.fn(),      writeFile: vi.fn(),    },    filesystem: {      browse: vi.fn(),    },    sourceControl: {      lookupRepository: vi.fn(),      cloneRepository: vi.fn(),      publishRepository: vi.fn(),    },    shell: {      openInEditor: vi.fn(),    },    vcs: {      pull: vi.fn(),      refreshStatus: vi.fn(),      onStatus: vi.fn(() => () => undefined),      listRefs: vi.fn(),      createWorktree: vi.fn(),      removeWorktree: vi.fn(),      createRef: vi.fn(),      switchRef: vi.fn(),      init: vi.fn(),    },    git: {      runStackedAction: vi.fn(),      resolvePullRequest: vi.fn(),      preparePullRequestThread: vi.fn(),    },    review: {      getDiffPreview: vi.fn(),    },    server: {      getConfig: vi.fn(),      refreshProviders: vi.fn(),      discoverSourceControl: vi.fn(),      updateProvider: vi.fn(),      upsertKeybinding: vi.fn(),      removeKeybinding: vi.fn(),      getSettings: vi.fn(),      updateSettings: vi.fn(),      subscribeConfig: vi.fn(() => () => undefined),      subscribeLifecycle: vi.fn(() => () => undefined),      subscribeAuthAccess: vi.fn(() => () => undefined),      getTraceDiagnostics: vi.fn(),      getProcessDiagnostics: vi.fn(),      getProcessResourceHistory: vi.fn(),      signalProcess: vi.fn(),    },  };  return {    ...actual,    createWsRpcClient: vi.fn(() => stubWsClient),    fetchRemoteSessionState: mockFetchRemoteSessionState,    resolveRemoteWebSocketConnectionUrl: mockResolveRemoteWebSocketConnectionUrl,  };}); vi.mock("../../rpc/wsTransport", () => ({  WsTransport: MockWsTransport,})); function makeThreadShellSnapshot(params: {  readonly threadId: ThreadId;  readonly sessionStatus?:    | "idle"    | "starting"    | "running"    | "ready"    | "interrupted"    | "stopped"    | "error";  readonly hasPendingApprovals?: boolean;  readonly hasPendingUserInput?: boolean;  readonly hasActionableProposedPlan?: boolean;}): OrchestrationShellSnapshot {  const projectId = ProjectId.make("project-1");  const turnId = TurnId.make("turn-1");   return {    snapshotSequence: 1,    projects: [],    updatedAt: "2026-04-13T00:00:00.000Z",    threads: [      {        id: params.threadId,        projectId,        title: "Thread",        modelSelection: {          instanceId: ProviderInstanceId.make("codex"),          model: "gpt-5-codex",        },        runtimeMode: "full-access",        interactionMode: "default",        branch: null,        worktreePath: null,        latestTurn:          params.sessionStatus === "running"            ? {                turnId,                state: "running",                requestedAt: "2026-04-13T00:00:00.000Z",                startedAt: "2026-04-13T00:00:01.000Z",                completedAt: null,                assistantMessageId: null,              }            : null,        createdAt: "2026-04-13T00:00:00.000Z",        updatedAt: "2026-04-13T00:00:00.000Z",        archivedAt: null,        session: params.sessionStatus          ? {              threadId: params.threadId,              status: params.sessionStatus,              providerName: "codex",              runtimeMode: "full-access",              activeTurnId: params.sessionStatus === "running" ? turnId : null,              lastError: null,              updatedAt: "2026-04-13T00:00:00.000Z",            }          : null,        latestUserMessageAt: null,        hasPendingApprovals: params.hasPendingApprovals ?? false,        hasPendingUserInput: params.hasPendingUserInput ?? false,        hasActionableProposedPlan: params.hasActionableProposedPlan ?? false,      },    ],  };} describe("retainThreadDetailSubscription", () => {  beforeEach(() => {    vi.useFakeTimers();    vi.resetModules();    vi.clearAllMocks();    mockGetPrimaryKnownEnvironment.mockReturnValue({      id: "env-1",      label: "Primary environment",      source: "window-origin",      target: {        httpBaseUrl: "http://127.0.0.1:3000/",        wsBaseUrl: "ws://127.0.0.1:3000/",      },      environmentId: EnvironmentId.make("env-1"),    });     mockThreadUnsubscribe.mockImplementation(() => undefined);    mockSubscribeThread.mockImplementation(() => mockThreadUnsubscribe);    mockCreateWsRpcClient.mockReturnValue({      server: {        getConfig: vi.fn(async () => ({          environment: {            environmentId: EnvironmentId.make("env-remote"),            label: "Remote env",            platform: { os: "darwin", arch: "arm64" },            serverVersion: "0.0.0-test",            capabilities: { repositoryIdentity: true },          },        })),      },      isHeartbeatFresh: vi.fn(() => true),      orchestration: {        subscribeThread: mockSubscribeThread,      },    });    mockCreateEnvironmentConnection.mockImplementation((input) => {      const reconnect = vi.fn(async () => undefined);      mockConnectionReconnects.push(reconnect);      queueMicrotask(() => {        input.onConfigSnapshot?.({          environment: {            environmentId: input.knownEnvironment.environmentId,            label: input.knownEnvironment.label,            platform: { os: "darwin", arch: "arm64" },            serverVersion: "0.0.0-test",            capabilities: { repositoryIdentity: true },          },        });      });      return {        kind: input.kind,        environmentId: input.knownEnvironment.environmentId,        knownEnvironment: input.knownEnvironment,        client: input.client,        ensureBootstrapped: vi.fn(async () => undefined),        reconnect,        dispose: vi.fn(async () => undefined),      };    });    savedEnvironmentRegistryListener = null;    mockSavedEnvironmentRegistrySubscribe.mockImplementation((listener: () => void) => {      savedEnvironmentRegistryListener = listener;      return () => {        if (savedEnvironmentRegistryListener === listener) {          savedEnvironmentRegistryListener = null;        }      };    });    mockWaitForSavedEnvironmentRegistryHydration.mockResolvedValue(undefined);    mockListSavedEnvironmentRecords.mockReturnValue([]);    mockGetSavedEnvironmentRecord.mockReturnValue(null);    mockReadSavedEnvironmentBearerToken.mockResolvedValue(null);    mockReadSavedEnvironmentCredential.mockImplementation(async () => {      const token = await mockReadSavedEnvironmentBearerToken();      return token ? { version: 1, method: "bearer", token } : null;    });    mockFetchRemoteSessionState.mockResolvedValue({      authenticated: true,      scopes: ["orchestration:read"],    });    mockConnectionReconnects.length = 0;  });   afterEach(async () => {    const { resetEnvironmentServiceForTests } = await import("./service");    await resetEnvironmentServiceForTests();    vi.unstubAllGlobals();    vi.useRealTimers();  });   it("keeps thread detail subscriptions warm across releases until idle eviction", async () => {    const {      retainThreadDetailSubscription,      startEnvironmentConnectionService,      resetEnvironmentServiceForTests,    } = await import("./service");     const stop = startEnvironmentConnectionService(new QueryClient());    const environmentId = EnvironmentId.make("env-1");    const threadId = ThreadId.make("thread-1");     const releaseFirst = retainThreadDetailSubscription(environmentId, threadId);    expect(mockSubscribeThread).toHaveBeenCalledTimes(1);     releaseFirst();    expect(mockThreadUnsubscribe).not.toHaveBeenCalled();     const releaseSecond = retainThreadDetailSubscription(environmentId, threadId);    expect(mockSubscribeThread).toHaveBeenCalledTimes(1);     releaseSecond();    await vi.advanceTimersByTimeAsync(2 * 60 * 1000);    expect(mockThreadUnsubscribe).not.toHaveBeenCalled();     await vi.advanceTimersByTimeAsync(28 * 60 * 1000);    expect(mockThreadUnsubscribe).toHaveBeenCalledTimes(1);     stop();    await resetEnvironmentServiceForTests();  });   it("does not start the primary connection until the known environment has an id", async () => {    mockGetPrimaryKnownEnvironment.mockReturnValue({      id: "env-1",      label: "Primary environment",      source: "window-origin",      target: {        httpBaseUrl: "http://127.0.0.1:3000/",        wsBaseUrl: "ws://127.0.0.1:3000/",      },    });    const {      listEnvironmentConnections,      resetEnvironmentServiceForTests,      startEnvironmentConnectionService,    } = await import("./service");     const stop = startEnvironmentConnectionService(new QueryClient());     expect(mockCreateEnvironmentConnection).not.toHaveBeenCalled();    expect(listEnvironmentConnections()).toEqual([]);     stop();    await resetEnvironmentServiceForTests();  });   it("keeps non-idle thread detail subscriptions attached until the thread becomes idle", async () => {    const {      retainThreadDetailSubscription,      startEnvironmentConnectionService,      resetEnvironmentServiceForTests,    } = await import("./service");     const stop = startEnvironmentConnectionService(new QueryClient());    const environmentId = EnvironmentId.make("env-1");    const threadId = ThreadId.make("thread-active");     const connectionInput = mockCreateEnvironmentConnection.mock.calls[0]?.[0];    expect(connectionInput).toBeDefined();     connectionInput.syncShellSnapshot(      makeThreadShellSnapshot({        threadId,        sessionStatus: "ready",        hasPendingApprovals: true,      }),      environmentId,    );     const release = retainThreadDetailSubscription(environmentId, threadId);    expect(mockSubscribeThread).toHaveBeenCalledTimes(1);     release();    await vi.advanceTimersByTimeAsync(30 * 60 * 1000);    expect(mockThreadUnsubscribe).not.toHaveBeenCalled();     connectionInput.applyShellEvent(      {        kind: "thread-upserted",        sequence: 2,        thread: makeThreadShellSnapshot({          threadId,          sessionStatus: "idle",        }).threads[0]!,      },      environmentId,    );     await vi.advanceTimersByTimeAsync(30 * 60 * 1000);    expect(mockThreadUnsubscribe).toHaveBeenCalledTimes(1);     stop();    await resetEnvironmentServiceForTests();  });   it("reattaches retained thread detail subscriptions after a saved environment reconnect replaces the client", async () => {    const environmentId = EnvironmentId.make("env-remote");    const threadId = ThreadId.make("thread-reconnect");    const record = {      environmentId,      label: "Remote env",      httpBaseUrl: "http://remote.example.test",      wsBaseUrl: "ws://remote.example.test",      createdAt: "2026-05-01T00:00:00.000Z",      lastConnectedAt: "2026-05-01T00:00:00.000Z",    };    mockListSavedEnvironmentRecords.mockReturnValue([record]);    mockGetSavedEnvironmentRecord.mockReturnValue(record);    mockReadSavedEnvironmentBearerToken.mockResolvedValue("bearer-token");     const {      disconnectSavedEnvironment,      listEnvironmentConnections,      reconnectSavedEnvironment,      retainThreadDetailSubscription,      startEnvironmentConnectionService,      resetEnvironmentServiceForTests,    } = await import("./service");     const stop = startEnvironmentConnectionService(new QueryClient());    savedEnvironmentRegistryListener?.();    await vi.waitFor(() => {      expect(        listEnvironmentConnections().some(          (connection) => connection.environmentId === environmentId,        ),      ).toBe(true);    });    const createConnectionCallsBeforeReconnect = mockCreateEnvironmentConnection.mock.calls.length;     const release = retainThreadDetailSubscription(environmentId, threadId);    expect(mockSubscribeThread).toHaveBeenCalledTimes(1);     await disconnectSavedEnvironment(environmentId);    expect(mockThreadUnsubscribe).toHaveBeenCalledTimes(1);    expect(      listEnvironmentConnections().some((connection) => connection.environmentId === environmentId),    ).toBe(false);     const reconnectPromise = reconnectSavedEnvironment(environmentId);    await vi.advanceTimersByTimeAsync(200);    await reconnectPromise;    await vi.waitFor(() => {      expect(mockCreateEnvironmentConnection).toHaveBeenCalledTimes(        createConnectionCallsBeforeReconnect + 1,      );      expect(mockSubscribeThread).toHaveBeenCalledTimes(2);    });     release();    stop();    await resetEnvironmentServiceForTests();  });   it("keeps healthy environment streams connected when the browser resumes from the background", async () => {    let visibilityState: DocumentVisibilityState = "visible";    const documentTarget = new EventTarget();    const windowTarget = new EventTarget();    vi.stubGlobal("document", {      addEventListener: documentTarget.addEventListener.bind(documentTarget),      removeEventListener: documentTarget.removeEventListener.bind(documentTarget),      get visibilityState() {

Lines 1–500 of 648.