343 lines · 9.1 KB · TypeScript
RawDownload
import {  DEFAULT_SERVER_SETTINGS,  EnvironmentId,  ProjectId,  ThreadId,  type ServerConfig,  type ServerConfigStreamEvent,  type ServerLifecycleStreamEvent,  type ServerProvider,} from "@t3tools/contracts";import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import {  getServerConfig,  onProvidersUpdated,  onServerConfigUpdated,  onWelcome,  resetServerStateForTests,  startServerStateSync,} from "./serverState"; function registerListener<T>(listeners: Set<(event: T) => void>, listener: (event: T) => void) {  listeners.add(listener);  return () => {    listeners.delete(listener);  };} function createDeferredPromise<T>() {  let resolve!: (value: T) => void;  const promise = new Promise<T>((nextResolve) => {    resolve = nextResolve;  });   return { promise, resolve };} const lifecycleListeners = new Set<(event: ServerLifecycleStreamEvent) => void>();const configListeners = new Set<(event: ServerConfigStreamEvent) => void>(); const defaultProviders: ReadonlyArray<ServerProvider> = [  {    provider: "codex",    enabled: true,    installed: true,    version: "0.116.0",    status: "ready",    auth: { status: "authenticated" },    checkedAt: "2026-01-01T00:00:00.000Z",    models: [],    slashCommands: [],    skills: [],  },]; const baseEnvironment = {  environmentId: EnvironmentId.make("environment-local"),  label: "Local environment",  platform: {    os: "darwin" as const,    arch: "arm64" as const,  },  serverVersion: "0.0.0-test",  capabilities: {    repositoryIdentity: true,  },}; const baseServerConfig: ServerConfig = {  environment: baseEnvironment,  auth: {    policy: "loopback-browser",    bootstrapMethods: ["one-time-token"],    sessionMethods: ["browser-session-cookie", "bearer-session-token"],    sessionCookieName: "t3_session",  },  cwd: "/tmp/workspace",  keybindingsConfigPath: "/tmp/workspace/.config/keybindings.json",  keybindings: [],  issues: [],  providers: defaultProviders,  availableEditors: ["cursor"],  observability: {    logsDirectoryPath: "/tmp/workspace/.config/logs",    localTracingEnabled: true,    otlpTracesEnabled: false,    otlpMetricsEnabled: false,  },  settings: DEFAULT_SERVER_SETTINGS,}; const serverApi = {  getConfig: vi.fn<() => Promise<ServerConfig>>(),  subscribeConfig: vi.fn((listener: (event: ServerConfigStreamEvent) => void) =>    registerListener(configListeners, listener),  ),  subscribeLifecycle: vi.fn((listener: (event: ServerLifecycleStreamEvent) => void) =>    registerListener(lifecycleListeners, listener),  ),}; function emitLifecycleEvent(event: ServerLifecycleStreamEvent) {  for (const listener of lifecycleListeners) {    listener(event);  }} function emitServerConfigEvent(event: ServerConfigStreamEvent) {  for (const listener of configListeners) {    listener(event);  }} async function waitFor(assertion: () => void, timeoutMs = 1_000): Promise<void> {  const startedAt = Date.now();  for (;;) {    try {      assertion();      return;    } catch (error) {      if (Date.now() - startedAt >= timeoutMs) {        throw error;      }      await new Promise((resolve) => setTimeout(resolve, 10));    }  }} beforeEach(() => {  vi.clearAllMocks();  lifecycleListeners.clear();  configListeners.clear();  resetServerStateForTests();}); afterEach(() => {  resetServerStateForTests();}); describe("serverState", () => {  it("bootstraps the server config snapshot and replays it to late subscribers", async () => {    serverApi.getConfig.mockResolvedValueOnce(baseServerConfig);     const configListener = vi.fn();    const stop = startServerStateSync(serverApi);    const unsubscribe = onServerConfigUpdated(configListener);     await waitFor(() => {      expect(getServerConfig()).toEqual(baseServerConfig);    });     expect(serverApi.subscribeConfig).toHaveBeenCalledOnce();    expect(serverApi.subscribeLifecycle).toHaveBeenCalledOnce();    expect(serverApi.getConfig).toHaveBeenCalledOnce();    expect(configListener).toHaveBeenCalledWith(      {        issues: [],        providers: defaultProviders,        settings: DEFAULT_SERVER_SETTINGS,      },      "snapshot",    );     const lateListener = vi.fn();    const unsubscribeLate = onServerConfigUpdated(lateListener);    expect(lateListener).toHaveBeenCalledWith(      {        issues: [],        providers: defaultProviders,        settings: DEFAULT_SERVER_SETTINGS,      },      "snapshot",    );     unsubscribeLate();    unsubscribe();    stop();  });   it("keeps the streamed snapshot when it arrives before the fallback fetch resolves", async () => {    const deferred = createDeferredPromise<ServerConfig>();    serverApi.getConfig.mockReturnValueOnce(deferred.promise);    const stop = startServerStateSync(serverApi);     const streamedConfig: ServerConfig = {      ...baseServerConfig,      cwd: "/tmp/from-stream",    };     emitServerConfigEvent({      version: 1,      type: "snapshot",      config: streamedConfig,    });     await waitFor(() => {      expect(getServerConfig()).toEqual(streamedConfig);    });     deferred.resolve(baseServerConfig);    await new Promise((resolve) => setTimeout(resolve, 0));     expect(getServerConfig()).toEqual(streamedConfig);    stop();  });   it("replays welcome events to late subscribers", async () => {    serverApi.getConfig.mockResolvedValueOnce(baseServerConfig);    const stop = startServerStateSync(serverApi);     const listener = vi.fn();    const unsubscribe = onWelcome(listener);     emitLifecycleEvent({      version: 1,      sequence: 1,      type: "welcome",      payload: {        environment: baseEnvironment,        cwd: "/tmp/workspace",        projectName: "t3-code",        bootstrapProjectId: ProjectId.make("project-1"),        bootstrapThreadId: ThreadId.make("thread-1"),      },    });     expect(listener).toHaveBeenCalledWith({      environment: baseEnvironment,      cwd: "/tmp/workspace",      projectName: "t3-code",      bootstrapProjectId: ProjectId.make("project-1"),      bootstrapThreadId: ThreadId.make("thread-1"),    });     const lateListener = vi.fn();    const unsubscribeLate = onWelcome(lateListener);    expect(lateListener).toHaveBeenCalledWith({      environment: baseEnvironment,      cwd: "/tmp/workspace",      projectName: "t3-code",      bootstrapProjectId: ProjectId.make("project-1"),      bootstrapThreadId: ThreadId.make("thread-1"),    });     unsubscribeLate();    unsubscribe();    stop();  });   it("merges provider, settings, and keybinding updates into the cached config", async () => {    serverApi.getConfig.mockResolvedValueOnce(baseServerConfig);    const configListener = vi.fn();    const providersListener = vi.fn();    const stop = startServerStateSync(serverApi);    const unsubscribeConfig = onServerConfigUpdated(configListener);    const unsubscribeProviders = onProvidersUpdated(providersListener);     await waitFor(() => {      expect(getServerConfig()).toEqual(baseServerConfig);    });     const nextProviders: ReadonlyArray<ServerProvider> = [      {        ...defaultProviders[0]!,        status: "warning",        checkedAt: "2026-01-02T00:00:00.000Z",        message: "rate limited",      },    ];     emitServerConfigEvent({      version: 1,      type: "keybindingsUpdated",      payload: {        issues: [{ kind: "keybindings.malformed-config", message: "bad json" }],      },    });    emitServerConfigEvent({      version: 1,      type: "providerStatuses",      payload: {        providers: nextProviders,      },    });    emitServerConfigEvent({      version: 1,      type: "settingsUpdated",      payload: {        settings: {          ...DEFAULT_SERVER_SETTINGS,          enableAssistantStreaming: true,        },      },    });     await waitFor(() => {      expect(getServerConfig()).toEqual({        ...baseServerConfig,        issues: [{ kind: "keybindings.malformed-config", message: "bad json" }],        providers: nextProviders,        settings: {          ...DEFAULT_SERVER_SETTINGS,          enableAssistantStreaming: true,        },      });    });     expect(providersListener).toHaveBeenLastCalledWith({ providers: nextProviders });    expect(configListener).toHaveBeenNthCalledWith(      2,      {        issues: [{ kind: "keybindings.malformed-config", message: "bad json" }],        providers: defaultProviders,        settings: DEFAULT_SERVER_SETTINGS,      },      "keybindingsUpdated",    );    expect(configListener).toHaveBeenNthCalledWith(      3,      {        issues: [{ kind: "keybindings.malformed-config", message: "bad json" }],        providers: nextProviders,        settings: DEFAULT_SERVER_SETTINGS,      },      "providerStatuses",    );    expect(configListener).toHaveBeenLastCalledWith(      {        issues: [{ kind: "keybindings.malformed-config", message: "bad json" }],        providers: nextProviders,        settings: {          ...DEFAULT_SERVER_SETTINGS,          enableAssistantStreaming: true,        },      },      "settingsUpdated",    );     unsubscribeProviders();    unsubscribeConfig();    stop();  });});