murashit_codex-panel/tests/features/chat/controllers/thread/thread-identity-controller.test.ts

99 lines
3.3 KiB
TypeScript
Raw Normal View History

2026-05-28 21:25:33 +00:00
import { describe, expect, it, vi } from "vitest";
2026-05-28 22:12:13 +00:00
import { createChatState, createChatStateStore } from "../../../../../src/features/chat/chat-state";
import { ThreadIdentityController } from "../../../../../src/features/chat/controllers/thread/thread-identity-controller";
import type { RestoredThreadController } from "../../../../../src/features/chat/controllers/thread/restored-thread-controller";
import type { RestoredThreadPlaceholderState } from "../../../../../src/features/chat/view-lifecycle";
import type { Thread } from "../../../../../src/generated/app-server/v2/Thread";
2026-05-28 21:25:33 +00:00
function thread(id: string, name: string | null = null): Thread {
return {
id,
sessionId: id,
forkedFromId: null,
preview: "",
ephemeral: false,
modelProvider: "openai",
createdAt: 0,
updatedAt: 0,
status: { type: "idle" },
path: null,
cwd: "/vault",
cliVersion: "test",
source: "appServer",
threadSource: null,
agentNickname: null,
agentRole: null,
gitInfo: null,
name,
turns: [],
};
}
function createController() {
const stateStore = createChatStateStore(createChatState());
const restoredPlaceholder = vi.fn<() => RestoredThreadPlaceholderState | null>(() => null);
const restoredRename = vi.fn();
const restoredThread = {
clear: vi.fn(),
isPending: vi.fn(() => false),
placeholder: restoredPlaceholder,
rename: restoredRename,
} as unknown as RestoredThreadController;
const host = {
stateStore,
restoredThread,
invalidateResumeWork: vi.fn(),
clearDeferredRestoredThreadHydration: vi.fn(),
resetThreadTurnPresence: vi.fn(),
notifyActiveThreadIdentityChanged: vi.fn(),
refreshTabHeader: vi.fn(),
refreshLiveState: vi.fn(),
render: vi.fn(),
};
return { controller: new ThreadIdentityController(host), host, restoredPlaceholder, restoredRename, stateStore };
}
describe("ThreadIdentityController", () => {
it("clears the active thread when it is archived", () => {
const { controller, host, stateStore } = createController();
stateStore.dispatch({
type: "thread/resumed",
thread: thread("thread"),
cwd: "/vault",
model: null,
reasoningEffort: null,
serviceTier: null,
approvalPolicy: null,
approvalsReviewer: null,
activePermissionProfile: null,
});
controller.notifyThreadArchived("thread");
expect(stateStore.getState().activeThreadId).toBeNull();
expect(host.invalidateResumeWork).toHaveBeenCalledOnce();
expect(host.resetThreadTurnPresence).toHaveBeenCalledWith(false);
expect(host.notifyActiveThreadIdentityChanged).toHaveBeenCalledOnce();
expect(host.render).toHaveBeenCalledOnce();
});
it("updates listed and restored thread titles on rename notifications", () => {
const { controller, host, restoredPlaceholder, restoredRename, stateStore } = createController();
stateStore.dispatch({ type: "thread/list-applied", threads: [thread("thread", "Old")] });
restoredPlaceholder.mockReturnValue({
kind: "placeholder",
threadId: "thread",
title: "Old",
explicitName: "Old",
loading: null,
});
controller.notifyThreadRenamed("thread", "New");
expect(stateStore.getState().listedThreads[0]?.name).toBe("New");
expect(restoredRename).toHaveBeenCalledWith("thread", "New");
expect(host.render).toHaveBeenCalledOnce();
});
});