171 lines · 5.3 KB · TypeScript
RawDownload
import { scopeProjectRef } from "@t3tools/client-runtime/environment";import {  EnvironmentId,  ProjectId,  ProviderInstanceId,  type ModelSelection,} from "@t3tools/contracts";import { describe, expect, it, vi } from "vite-plus/test";import {  resolveThreadActionProjectRef,  hasExplicitComposerModelSelection,  resolveNewDraftStartFromOrigin,  resolveNewThreadModelSelectionOverride,  startNewThreadFromContext,  type ChatThreadActionContext,} from "./chatThreadActions"; const ENVIRONMENT_ID = EnvironmentId.make("environment-1");const PROJECT_ID = ProjectId.make("project-1");const FALLBACK_PROJECT_ID = ProjectId.make("project-2");const PROJECT_DEFAULT_SELECTION: ModelSelection = {  instanceId: ProviderInstanceId.make("codex"),  model: "project-default",};const CARRIED_SELECTION: ModelSelection = {  instanceId: ProviderInstanceId.make("codex"),  model: "carried-model",}; function createContext(overrides: Partial<ChatThreadActionContext> = {}): ChatThreadActionContext {  return {    activeDraftThread: null,    activeThread: undefined,    defaultProjectRef: scopeProjectRef(ENVIRONMENT_ID, FALLBACK_PROJECT_ID),    handleNewThread: async () => {},    ...overrides,  };} describe("chatThreadActions", () => {  it("only treats an active stored selection marked explicit as an explicit pick", () => {    const draft = {      activeProvider: PROJECT_DEFAULT_SELECTION.instanceId,      modelSelectionByProvider: {        [PROJECT_DEFAULT_SELECTION.instanceId]: PROJECT_DEFAULT_SELECTION,      },      modelSelectionExplicit: true,    };     expect(hasExplicitComposerModelSelection(draft)).toBe(true);    expect(hasExplicitComposerModelSelection({ ...draft, modelSelectionExplicit: false })).toBe(      false,    );    expect(hasExplicitComposerModelSelection({ ...draft, activeProvider: null })).toBe(false);  });   it("does not carry a non-explicit model from the destination draft back into itself", () => {    expect(      resolveNewThreadModelSelectionOverride({        projectDefaultSelection: null,        carrySelection: CARRIED_SELECTION,        carrySourceDraftId: "draft-a",        destinationDraftId: "draft-a",      }),    ).toBeNull();  });   it("still carries models between different threads when the project has no default", () => {    expect(      resolveNewThreadModelSelectionOverride({        projectDefaultSelection: null,        carrySelection: CARRIED_SELECTION,        carrySourceDraftId: "draft-a",        destinationDraftId: "draft-b",      }),    ).toEqual(CARRIED_SELECTION);  });   it("keeps the project default above any carried selection", () => {    expect(      resolveNewThreadModelSelectionOverride({        projectDefaultSelection: PROJECT_DEFAULT_SELECTION,        carrySelection: CARRIED_SELECTION,        carrySourceDraftId: "draft-a",        destinationDraftId: "draft-b",      }),    ).toEqual(PROJECT_DEFAULT_SELECTION);  });   it("only applies the start-from-origin default to new worktree drafts", () => {    expect(      resolveNewDraftStartFromOrigin({        envMode: "worktree",        newWorktreesStartFromOrigin: true,      }),    ).toBe(true);    expect(      resolveNewDraftStartFromOrigin({        envMode: "local",        newWorktreesStartFromOrigin: true,      }),    ).toBe(false);  });   it("prefers the active thread project when resolving thread actions", () => {    const projectRef = resolveThreadActionProjectRef(      createContext({        activeThread: {          environmentId: ENVIRONMENT_ID,          projectId: PROJECT_ID,        },      }),    );     expect(projectRef).toEqual(scopeProjectRef(ENVIRONMENT_ID, PROJECT_ID));  });   it("falls back to the active draft thread project when there is no active thread", () => {    const projectRef = resolveThreadActionProjectRef(      createContext({        activeDraftThread: {          environmentId: ENVIRONMENT_ID,          projectId: PROJECT_ID,        },      }),    );     expect(projectRef).toEqual(scopeProjectRef(ENVIRONMENT_ID, PROJECT_ID));  });   it("falls back to the default project ref when there is no active thread context", () => {    const projectRef = resolveThreadActionProjectRef(      createContext({        defaultProjectRef: scopeProjectRef(ENVIRONMENT_ID, PROJECT_ID),      }),    );     expect(projectRef).toEqual(scopeProjectRef(ENVIRONMENT_ID, PROJECT_ID));  });   it("inherits only the project from context, never branch or worktree state", async () => {    const handleNewThread = vi.fn<ChatThreadActionContext["handleNewThread"]>(async () => {});     const didStart = await startNewThreadFromContext(      createContext({        activeThread: {          environmentId: ENVIRONMENT_ID,          projectId: PROJECT_ID,        },        handleNewThread,      }),    );     expect(didStart).toBe(true);    expect(handleNewThread).toHaveBeenCalledWith(scopeProjectRef(ENVIRONMENT_ID, PROJECT_ID));  });   it("does not start a thread when there is no project context", async () => {    const handleNewThread = vi.fn<ChatThreadActionContext["handleNewThread"]>(async () => {});     const didStart = await startNewThreadFromContext(      createContext({        defaultProjectRef: null,        handleNewThread,      }),    );     expect(didStart).toBe(false);    expect(handleNewThread).not.toHaveBeenCalled();  });});