import { scopeProjectRef } from "@t3tools/client-runtime";import { EnvironmentId, ProjectId } from "@t3tools/contracts";import { describe, expect, it, vi } from "vitest";import { resolveThreadActionProjectRef, startNewLocalThreadFromContext, 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"); function createContext(overrides: Partial<ChatThreadActionContext> = {}): ChatThreadActionContext { return { activeDraftThread: null, activeThread: undefined, defaultProjectRef: scopeProjectRef(ENVIRONMENT_ID, FALLBACK_PROJECT_ID), defaultThreadEnvMode: "local", handleNewThread: async () => {}, ...overrides, };} describe("chatThreadActions", () => { it("prefers the active draft thread project when resolving thread actions", () => { const projectRef = resolveThreadActionProjectRef( createContext({ activeDraftThread: { environmentId: ENVIRONMENT_ID, projectId: PROJECT_ID, branch: "feature/refactor", worktreePath: "/tmp/worktree", envMode: "worktree", }, }), ); 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("starts a contextual new thread from the active draft thread", async () => { const handleNewThread = vi.fn<ChatThreadActionContext["handleNewThread"]>(async () => {}); const didStart = await startNewThreadFromContext( createContext({ activeDraftThread: { environmentId: ENVIRONMENT_ID, projectId: PROJECT_ID, branch: "feature/refactor", worktreePath: "/tmp/worktree", envMode: "worktree", }, handleNewThread, }), ); expect(didStart).toBe(true); expect(handleNewThread).toHaveBeenCalledWith(scopeProjectRef(ENVIRONMENT_ID, PROJECT_ID), { branch: "feature/refactor", worktreePath: "/tmp/worktree", envMode: "worktree", }); }); it("starts a local thread with the configured default env mode", async () => { const handleNewThread = vi.fn<ChatThreadActionContext["handleNewThread"]>(async () => {}); const didStart = await startNewLocalThreadFromContext( createContext({ defaultProjectRef: scopeProjectRef(ENVIRONMENT_ID, PROJECT_ID), defaultThreadEnvMode: "worktree", handleNewThread, }), ); expect(didStart).toBe(true); expect(handleNewThread).toHaveBeenCalledWith(scopeProjectRef(ENVIRONMENT_ID, PROJECT_ID), { envMode: "worktree", }); }); 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(); });});
import { scopeProjectRef } from "@t3tools/client-runtime";
import { EnvironmentId, ProjectId } from "@t3tools/contracts";
import { describe, expect, it, vi } from "vitest";
import {
resolveThreadActionProjectRef,
startNewLocalThreadFromContext,
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");
function createContext(overrides: Partial<ChatThreadActionContext> = {}): ChatThreadActionContext {
return {
activeDraftThread: null,
activeThread: undefined,
defaultProjectRef: scopeProjectRef(ENVIRONMENT_ID, FALLBACK_PROJECT_ID),
defaultThreadEnvMode: "local",
handleNewThread: async () => {},
...overrides,
};
}
describe("chatThreadActions", () => {
it("prefers the active draft thread project when resolving thread actions", () => {
const projectRef = resolveThreadActionProjectRef(
createContext({
activeDraftThread: {
environmentId: ENVIRONMENT_ID,
projectId: PROJECT_ID,
branch: "feature/refactor",
worktreePath: "/tmp/worktree",
envMode: "worktree",
},
}),
);
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("starts a contextual new thread from the active draft thread", async () => {
const handleNewThread = vi.fn<ChatThreadActionContext["handleNewThread"]>(async () => {});
const didStart = await startNewThreadFromContext(
createContext({
activeDraftThread: {
environmentId: ENVIRONMENT_ID,
projectId: PROJECT_ID,
branch: "feature/refactor",
worktreePath: "/tmp/worktree",
envMode: "worktree",
},
handleNewThread,
}),
);
expect(didStart).toBe(true);
expect(handleNewThread).toHaveBeenCalledWith(scopeProjectRef(ENVIRONMENT_ID, PROJECT_ID), {
branch: "feature/refactor",
worktreePath: "/tmp/worktree",
envMode: "worktree",
});
});
it("starts a local thread with the configured default env mode", async () => {
const handleNewThread = vi.fn<ChatThreadActionContext["handleNewThread"]>(async () => {});
const didStart = await startNewLocalThreadFromContext(
createContext({
defaultProjectRef: scopeProjectRef(ENVIRONMENT_ID, PROJECT_ID),
defaultThreadEnvMode: "worktree",
handleNewThread,
}),
);
expect(didStart).toBe(true);
expect(handleNewThread).toHaveBeenCalledWith(scopeProjectRef(ENVIRONMENT_ID, PROJECT_ID), {
envMode: "worktree",
});
});
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();
});
});