Julius Marmingefix expectationd6099ed7 months agoHistory
434 lines · 12.8 KB · TypeScript
RawDownload
import {  CommandId,  type ContextMenuItem,  EventId,  ORCHESTRATION_WS_CHANNELS,  ORCHESTRATION_WS_METHODS,  type OrchestrationEvent,  ProjectId,  ThreadId,  type WsPushChannel,  type WsPushData,  type WsPushMessage,  WS_CHANNELS,  WS_METHODS,  type WsPush,  type ServerProviderStatus,} from "@t3tools/contracts";import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; const requestMock = vi.fn<(...args: Array<unknown>) => Promise<unknown>>();const showContextMenuFallbackMock =  vi.fn<    <T extends string>(      items: readonly ContextMenuItem<T>[],      position?: { x: number; y: number },    ) => Promise<T | null>  >();const channelListeners = new Map<string, Set<(message: WsPush) => void>>();const latestPushByChannel = new Map<string, WsPush>();const subscribeMock = vi.fn<  (    channel: string,    listener: (message: WsPush) => void,    options?: { replayLatest?: boolean },  ) => () => void>((channel, listener, options) => {  const listeners = channelListeners.get(channel) ?? new Set<(message: WsPush) => void>();  listeners.add(listener);  channelListeners.set(channel, listeners);  const latest = latestPushByChannel.get(channel);  if (latest && options?.replayLatest) {    listener(latest);  }  return () => {    listeners.delete(listener);    if (listeners.size === 0) {      channelListeners.delete(channel);    }  };}); vi.mock("./wsTransport", () => {  return {    WsTransport: class MockWsTransport {      request = requestMock;      subscribe = subscribeMock;      getLatestPush(channel: string) {        return latestPushByChannel.get(channel) ?? null;      }    },  };}); vi.mock("./contextMenuFallback", () => ({  showContextMenuFallback: showContextMenuFallbackMock,})); let nextPushSequence = 1; function emitPush<C extends WsPushChannel>(channel: C, data: WsPushData<C>): void {  const listeners = channelListeners.get(channel);  const message = {    type: "push" as const,    sequence: nextPushSequence++,    channel,    data,  } as WsPushMessage<C>;  latestPushByChannel.set(channel, message);  if (!listeners) return;  for (const listener of listeners) {    listener(message);  }} function getWindowForTest(): Window & typeof globalThis & { desktopBridge?: unknown } {  const testGlobal = globalThis as typeof globalThis & {    window?: Window & typeof globalThis & { desktopBridge?: unknown };  };  if (!testGlobal.window) {    testGlobal.window = {} as Window & typeof globalThis & { desktopBridge?: unknown };  }  return testGlobal.window;} const defaultProviders: ReadonlyArray<ServerProviderStatus> = [  {    provider: "codex",    status: "ready",    available: true,    authStatus: "authenticated",    checkedAt: "2026-01-01T00:00:00.000Z",  },]; beforeEach(() => {  vi.resetModules();  requestMock.mockReset();  showContextMenuFallbackMock.mockReset();  subscribeMock.mockClear();  channelListeners.clear();  latestPushByChannel.clear();  nextPushSequence = 1;  Reflect.deleteProperty(getWindowForTest(), "desktopBridge");}); afterEach(() => {  vi.restoreAllMocks();}); describe("wsNativeApi", () => {  it("delivers and caches valid server.welcome payloads", async () => {    const { createWsNativeApi, onServerWelcome } = await import("./wsNativeApi");     createWsNativeApi();    const listener = vi.fn();    onServerWelcome(listener);     const payload = { cwd: "/tmp/workspace", projectName: "t3-code" };    emitPush(WS_CHANNELS.serverWelcome, payload);     expect(listener).toHaveBeenCalledTimes(1);    expect(listener).toHaveBeenCalledWith(expect.objectContaining(payload));     const lateListener = vi.fn();    onServerWelcome(lateListener);     expect(lateListener).toHaveBeenCalledTimes(1);    expect(lateListener).toHaveBeenCalledWith(expect.objectContaining(payload));  });   it("preserves bootstrap ids from server.welcome payloads", async () => {    const { createWsNativeApi, onServerWelcome } = await import("./wsNativeApi");     createWsNativeApi();    const listener = vi.fn();    onServerWelcome(listener);     emitPush(WS_CHANNELS.serverWelcome, {      cwd: "/tmp/workspace",      projectName: "t3-code",      bootstrapProjectId: ProjectId.makeUnsafe("project-1"),      bootstrapThreadId: ThreadId.makeUnsafe("thread-1"),    });     expect(listener).toHaveBeenCalledTimes(1);    expect(listener).toHaveBeenCalledWith(      expect.objectContaining({        cwd: "/tmp/workspace",        projectName: "t3-code",        bootstrapProjectId: "project-1",        bootstrapThreadId: "thread-1",      }),    );  });   it("delivers successive server.welcome payloads to active listeners", async () => {    const { createWsNativeApi, onServerWelcome } = await import("./wsNativeApi");     createWsNativeApi();    const listener = vi.fn();    onServerWelcome(listener);     emitPush(WS_CHANNELS.serverWelcome, { cwd: "/tmp/one", projectName: "one" });    emitPush(WS_CHANNELS.serverWelcome, { cwd: "/tmp/workspace", projectName: "t3-code" });     expect(listener).toHaveBeenCalledTimes(2);    expect(listener).toHaveBeenLastCalledWith(      expect.objectContaining({        cwd: "/tmp/workspace",        projectName: "t3-code",      }),    );  });   it("delivers and caches valid server.configUpdated payloads", async () => {    const { createWsNativeApi, onServerConfigUpdated } = await import("./wsNativeApi");     createWsNativeApi();    const listener = vi.fn();    onServerConfigUpdated(listener);     const payload = {      issues: [        {          kind: "keybindings.invalid-entry",          index: 1,          message: "Entry at index 1 is invalid.",        },      ],      providers: defaultProviders,    } as const;    emitPush(WS_CHANNELS.serverConfigUpdated, payload);     expect(listener).toHaveBeenCalledTimes(1);    expect(listener).toHaveBeenCalledWith(payload);     const lateListener = vi.fn();    onServerConfigUpdated(lateListener);    expect(lateListener).toHaveBeenCalledTimes(1);    expect(lateListener).toHaveBeenCalledWith(payload);  });   it("delivers successive server.configUpdated payloads to active listeners", async () => {    const { createWsNativeApi, onServerConfigUpdated } = await import("./wsNativeApi");     createWsNativeApi();    const listener = vi.fn();    onServerConfigUpdated(listener);     emitPush(WS_CHANNELS.serverConfigUpdated, {      issues: [{ kind: "keybindings.malformed-config", message: "bad json" }],      providers: defaultProviders,    });    emitPush(WS_CHANNELS.serverConfigUpdated, {      issues: [],      providers: defaultProviders,    });     expect(listener).toHaveBeenCalledTimes(2);    expect(listener).toHaveBeenLastCalledWith({      issues: [],      providers: defaultProviders,    });  });   it("forwards valid terminal and orchestration events", async () => {    const { createWsNativeApi } = await import("./wsNativeApi");     const api = createWsNativeApi();    const onTerminalEvent = vi.fn();    const onDomainEvent = vi.fn();    const onActionProgress = vi.fn();     api.terminal.onEvent(onTerminalEvent);    api.orchestration.onDomainEvent(onDomainEvent);    api.git.onActionProgress(onActionProgress);     const terminalEvent = {      threadId: "thread-1",      terminalId: "terminal-1",      createdAt: "2026-02-24T00:00:00.000Z",      type: "output",      data: "hello",    } as const;    emitPush(WS_CHANNELS.terminalEvent, terminalEvent);     const orchestrationEvent = {      sequence: 1,      eventId: EventId.makeUnsafe("event-1"),      aggregateKind: "project",      aggregateId: ProjectId.makeUnsafe("project-1"),      occurredAt: "2026-02-24T00:00:00.000Z",      commandId: null,      causationEventId: null,      correlationId: null,      metadata: {},      type: "project.created",      payload: {        projectId: ProjectId.makeUnsafe("project-1"),        title: "Project",        workspaceRoot: "/tmp/workspace",        defaultModelSelection: null,        scripts: [],        createdAt: "2026-02-24T00:00:00.000Z",        updatedAt: "2026-02-24T00:00:00.000Z",      },    } satisfies Extract<OrchestrationEvent, { type: "project.created" }>;    emitPush(ORCHESTRATION_WS_CHANNELS.domainEvent, orchestrationEvent);    emitPush(WS_CHANNELS.gitActionProgress, {      actionId: "action-1",      cwd: "/repo",      action: "commit",      kind: "phase_started",      phase: "commit",      label: "Committing...",    });     expect(onTerminalEvent).toHaveBeenCalledTimes(1);    expect(onTerminalEvent).toHaveBeenCalledWith(terminalEvent);    expect(onDomainEvent).toHaveBeenCalledTimes(1);    expect(onDomainEvent).toHaveBeenCalledWith(orchestrationEvent);    expect(onActionProgress).toHaveBeenCalledTimes(1);    expect(onActionProgress).toHaveBeenCalledWith({      actionId: "action-1",      cwd: "/repo",      action: "commit",      kind: "phase_started",      phase: "commit",      label: "Committing...",    });  });   it("wraps orchestration dispatch commands in the command envelope", async () => {    requestMock.mockResolvedValue(undefined);    const { createWsNativeApi } = await import("./wsNativeApi");     const api = createWsNativeApi();    const command = {      type: "project.create",      commandId: CommandId.makeUnsafe("cmd-1"),      projectId: ProjectId.makeUnsafe("project-1"),      title: "Project",      workspaceRoot: "/tmp/project",      defaultModelSelection: {        provider: "codex",        model: "gpt-5-codex",      },      createdAt: "2026-02-24T00:00:00.000Z",    } as const;    await api.orchestration.dispatchCommand(command);     expect(requestMock).toHaveBeenCalledWith(ORCHESTRATION_WS_METHODS.dispatchCommand, {      command,    });  });   it("forwards workspace file writes to the websocket project method", async () => {    requestMock.mockResolvedValue({ relativePath: "plan.md" });    const { createWsNativeApi } = await import("./wsNativeApi");     const api = createWsNativeApi();    await api.projects.writeFile({      cwd: "/tmp/project",      relativePath: "plan.md",      contents: "# Plan\n",    });     expect(requestMock).toHaveBeenCalledWith(WS_METHODS.projectsWriteFile, {      cwd: "/tmp/project",      relativePath: "plan.md",      contents: "# Plan\n",    });  });   it("uses no client timeout for git.runStackedAction", async () => {    requestMock.mockResolvedValue({      action: "commit",      branch: { status: "skipped_not_requested" },      commit: { status: "created", commitSha: "abc1234", subject: "Test" },      push: { status: "skipped_not_requested" },      pr: { status: "skipped_not_requested" },    });    const { createWsNativeApi } = await import("./wsNativeApi");     const api = createWsNativeApi();    await api.git.runStackedAction({      actionId: "action-1",      cwd: "/repo",      action: "commit",    });     expect(requestMock).toHaveBeenCalledWith(      WS_METHODS.gitRunStackedAction,      {        actionId: "action-1",        cwd: "/repo",        action: "commit",      },      { timeoutMs: null },    );  });   it("forwards full-thread diff requests to the orchestration websocket method", async () => {    requestMock.mockResolvedValue({ diff: "patch" });    const { createWsNativeApi } = await import("./wsNativeApi");     const api = createWsNativeApi();    await api.orchestration.getFullThreadDiff({      threadId: ThreadId.makeUnsafe("thread-1"),      toTurnCount: 1,    });     expect(requestMock).toHaveBeenCalledWith(ORCHESTRATION_WS_METHODS.getFullThreadDiff, {      threadId: "thread-1",      toTurnCount: 1,    });  });   it("forwards context menu metadata to desktop bridge", async () => {    const showContextMenu = vi.fn().mockResolvedValue("delete");    Object.defineProperty(getWindowForTest(), "desktopBridge", {      configurable: true,      writable: true,      value: {        showContextMenu,      },    });     const { createWsNativeApi } = await import("./wsNativeApi");    const api = createWsNativeApi();    await api.contextMenu.show(      [        { id: "rename", label: "Rename thread" },        { id: "delete", label: "Delete", destructive: true },      ],      { x: 200, y: 300 },    );     expect(showContextMenu).toHaveBeenCalledWith(      [        { id: "rename", label: "Rename thread" },        { id: "delete", label: "Delete", destructive: true },      ],      { x: 200, y: 300 },    );  });   it("uses fallback context menu when desktop bridge is unavailable", async () => {    showContextMenuFallbackMock.mockResolvedValue("delete");    Reflect.deleteProperty(getWindowForTest(), "desktopBridge");     const { createWsNativeApi } = await import("./wsNativeApi");    const api = createWsNativeApi();    await api.contextMenu.show([{ id: "delete", label: "Delete", destructive: true }], {      x: 20,      y: 30,    });     expect(showContextMenuFallbackMock).toHaveBeenCalledWith(      [{ id: "delete", label: "Delete", destructive: true }],      { x: 20, y: 30 },    );  });});