75 lines
4.3 KiB
TypeScript
75 lines
4.3 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import test from "node:test";
|
|
|
|
import type { ChatMessage, WorkspaceRecord } from "../src/types/index.ts";
|
|
import { defaultProviderProfileOptions, normalizeRecentDrafts, providerProfileOptionsFromPayload, recordRecentDraft, resolveComposerState, sortSessionTabs, workspaceWithClearedActiveTrace } from "../src/stores/workbench-session.ts";
|
|
|
|
test("R2 composer ignores stale workspace activeTraceId without verified active message", () => {
|
|
const workspace = workspaceRecord({ activeTraceId: "trc_stale", sessionStatus: "running" });
|
|
const composer = resolveComposerState({ workspace, messages: [], conversations: [], activeConversationId: "cnv_r2", chatPending: false });
|
|
assert.equal(composer.submitMode, "turn");
|
|
assert.equal(composer.route, "/v1/agent/chat");
|
|
assert.equal(composer.targetTraceId, null);
|
|
assert.equal(composer.sessionId, "ses_r2");
|
|
});
|
|
|
|
test("R2 composer steers only when an active agent message or status exists", () => {
|
|
const messages: ChatMessage[] = [agentMessage({ status: "running", traceId: "trc_running", conversationId: "cnv_r2", sessionId: "ses_r2" })];
|
|
const composer = resolveComposerState({ workspace: workspaceRecord({ activeTraceId: "trc_running", sessionStatus: "running" }), messages, activeConversationId: "cnv_r2", chatPending: true });
|
|
assert.equal(composer.submitMode, "steer");
|
|
assert.equal(composer.route, "/v1/agent/chat/steer");
|
|
assert.equal(composer.targetTraceId, "trc_running");
|
|
});
|
|
|
|
test("R2 active trace cleanup preserves workspace and records stale reason", () => {
|
|
const cleared = workspaceWithClearedActiveTrace(workspaceRecord({ activeTraceId: "trc_stale", sessionStatus: "running" }), "trc_stale", "steer-trace-not-found");
|
|
assert.equal(cleared?.activeTraceId, null);
|
|
assert.equal(cleared?.workspace?.activeTraceId, null);
|
|
assert.equal(cleared?.workspace?.staleActiveTraceId, "trc_stale");
|
|
assert.equal(cleared?.workspace?.staleActiveTraceReason, "steer-trace-not-found");
|
|
assert.equal(cleared?.workspace?.sessionStatus, "failed");
|
|
});
|
|
|
|
test("R2 drafts keep recent unique values and normalize corrupt storage", () => {
|
|
const next = recordRecentDraft([{ text: "旧输入", ts: "2026-01-01T00:00:00.000Z" }], "新输入", "2026-01-02T00:00:00.000Z");
|
|
assert.deepEqual(next.map((item) => item.text), ["新输入", "旧输入"]);
|
|
const deduped = recordRecentDraft(next, "旧输入", "2026-01-03T00:00:00.000Z");
|
|
assert.deepEqual(deduped.map((item) => item.text), ["旧输入", "新输入"]);
|
|
assert.deepEqual(normalizeRecentDrafts([{ text: "ok", ts: "now" }, { text: 1 }]).map((item) => item.text), ["ok"]);
|
|
});
|
|
|
|
test("R2 session tabs expose label, model metadata source, count and active state", () => {
|
|
const tabs = sortSessionTabs([{ conversationId: "cnv_r2", sessionId: "ses_r2", lastTraceId: "trc_done", messageCount: 2, firstUserMessagePreview: "请执行一次长任务", status: "completed", updatedAt: "2026-01-02T00:00:00.000Z" }], "cnv_r2");
|
|
assert.equal(tabs.length, 1);
|
|
assert.equal(tabs[0]?.active, true);
|
|
assert.equal(tabs[0]?.label, "请执行一次长任务");
|
|
assert.match(tabs[0]?.subtitle ?? "", /trace trc_done/u);
|
|
});
|
|
|
|
test("R2 provider options follow catalog and keep selected fallback", () => {
|
|
const catalog = providerProfileOptionsFromPayload({ profiles: [{ profile: "codex-api", configured: true }, { profile: "deepseek", configured: false }] }, "minimax-m3");
|
|
assert.deepEqual(catalog.map((item) => item.value), ["deepseek", "codex-api", "minimax-m3"]);
|
|
assert.equal(catalog.find((item) => item.value === "codex-api")?.configured, true);
|
|
assert.ok(defaultProviderProfileOptions("custom-profile").some((item) => item.value === "custom-profile"));
|
|
});
|
|
|
|
function workspaceRecord(input: { activeTraceId: string | null; sessionStatus: string | null }): WorkspaceRecord {
|
|
return {
|
|
workspaceId: "wsp_r2",
|
|
projectId: "prj_hwpod_workbench",
|
|
selectedConversationId: "cnv_r2",
|
|
selectedAgentSessionId: "ses_r2",
|
|
activeTraceId: input.activeTraceId,
|
|
workspace: {
|
|
selectedConversationId: "cnv_r2",
|
|
selectedAgentSessionId: "ses_r2",
|
|
activeTraceId: input.activeTraceId,
|
|
sessionStatus: input.sessionStatus
|
|
}
|
|
};
|
|
}
|
|
|
|
function agentMessage(patch: Partial<ChatMessage>): ChatMessage {
|
|
return { id: "msg_r2", role: "agent", title: "Code Agent", text: "running", status: "running", createdAt: "2026-01-01T00:00:00.000Z", ...patch };
|
|
}
|