Files
tutti/packages/agent/gui/agent-gui/agentGuiNode/controller/agentGuiConversationRailQuerySnapshot.spec.ts
agent-radar 2ed3415fb7
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

220 lines
6.6 KiB
TypeScript

import { normalizeAgentActivitySession } from "@tutti-os/agent-activity-core";
import { describe, expect, it } from "vitest";
import { createTestAgentSessionEngine } from "../../../shared/testing/createTestAgentSessionEngine";
import {
EMPTY_CONVERSATION_SEARCH_QUERY_STATE,
createConversationRailQuerySnapshotSelector
} from "./agentConversationRailQuerySnapshot";
import { createConversationRailConversationsSelector } from "./agentGuiConversationRailQuerySnapshot";
describe("createConversationRailQuerySnapshotSelector", () => {
it("projects only rail-owned sessions and preserves unchanged identities", () => {
const engine = createTestAgentSessionEngine();
const sessions = Array.from({ length: 176 }, (_, index) =>
createSession(`session-${index}`, `Session ${index}`, index + 1)
);
engine.dispatch({ sessions, type: "session/snapshotReceived" });
const querySnapshot = createConversationRailQuerySnapshotSelector()(
{
queryState: {
pending: false,
reconcilingSessionIds: ["session-2"],
resolvedScopeKey: "all",
sectionPageStates: new Map(),
sections: [
{
id: "conversations",
kind: "conversations" as const,
project: null,
sessionIds: ["session-0", "session-1"]
}
]
},
runtimeRailFailed: false,
runtimeSectionsEnabled: true,
searchEnabled: true,
searchQuery: "session",
searchRequestKey: "search:session",
searchState: {
...EMPTY_CONVERSATION_SEARCH_QUERY_STATE,
requestKey: "search:session",
resolvedQuery: "session",
sessionIds: ["session-3"]
}
},
undefined
);
const selectConversations = createConversationRailConversationsSelector();
const first = selectConversations({
engineState: engine.getSnapshot(),
interactionLocked: false,
querySnapshot
});
expect(first.map(({ id }) => id)).toEqual([
"session-0",
"session-1",
"session-2",
"session-3"
]);
engine.dispatch({
session: createSession("session-175", "Unrelated update", 1_000),
type: "session/upserted"
});
const afterUnrelatedUpdate = selectConversations(
{
engineState: engine.getSnapshot(),
interactionLocked: false,
querySnapshot
},
first
);
expect(afterUnrelatedUpdate).toBe(first);
engine.dispatch({
session: createSession("session-1", "Changed title", 2_000),
type: "session/upserted"
});
const afterVisibleUpdate = selectConversations(
{
engineState: engine.getSnapshot(),
interactionLocked: false,
querySnapshot
},
afterUnrelatedUpdate
);
expect(afterVisibleUpdate).not.toBe(first);
expect(afterVisibleUpdate[0]).toBe(first[0]);
expect(afterVisibleUpdate[1]).not.toBe(first[1]);
expect(afterVisibleUpdate[1]?.title).toBe("Changed title");
expect(afterVisibleUpdate[2]).toBe(first[2]);
expect(afterVisibleUpdate[3]).toBe(first[3]);
const isolation = {
mode: "worktree" as const,
worktreePath: "/workspace/.worktrees/session-1",
branch: "tutti/session-1",
baseCommit: "base-commit"
};
engine.dispatch({
session: createSession("session-1", "Changed title", 2_000, isolation),
type: "session/upserted"
});
const afterIsolationProjection = selectConversations(
{
engineState: engine.getSnapshot(),
interactionLocked: false,
querySnapshot
},
afterVisibleUpdate
);
expect(afterIsolationProjection).not.toBe(afterVisibleUpdate);
expect(afterIsolationProjection[0]).toBe(afterVisibleUpdate[0]);
expect(afterIsolationProjection[1]).not.toBe(afterVisibleUpdate[1]);
expect(afterIsolationProjection[1]?.isolation).toEqual(isolation);
expect(afterIsolationProjection[2]).toBe(afterVisibleUpdate[2]);
expect(afterIsolationProjection[3]).toBe(afterVisibleUpdate[3]);
engine.dispose();
});
it("projects all running sessions for the exact target even before membership refresh", () => {
const engine = createTestAgentSessionEngine();
engine.dispatch({
sessions: [
createRunningSession("running-1", "local:codex"),
createRunningSession("running-2", "local:codex"),
createRunningSession("other-target", "local:opencode")
],
type: "session/snapshotReceived"
});
const querySnapshot = createConversationRailQuerySnapshotSelector()(
{
agentTargetId: "local:codex",
queryState: {
pending: false,
reconcilingSessionIds: [],
resolvedScopeKey: "codex",
sectionPageStates: new Map(),
sections: []
},
runtimeRailFailed: false,
runtimeSectionsEnabled: true,
searchEnabled: false,
searchQuery: "",
searchRequestKey: null,
searchState: EMPTY_CONVERSATION_SEARCH_QUERY_STATE
},
undefined
);
const projected = createConversationRailConversationsSelector()({
engineState: engine.getSnapshot(),
interactionLocked: false,
querySnapshot
});
expect(projected.map(({ id }) => id)).toEqual(["running-1", "running-2"]);
engine.dispose();
});
});
function createRunningSession(agentSessionId: string, agentTargetId: string) {
return normalizeAgentActivitySession({
activeTurn: {
agentSessionId,
error: null,
origin: "user_prompt",
outcome: null,
phase: "running",
settledAtUnixMs: null,
startedAtUnixMs: 1,
turnId: `${agentSessionId}-turn`,
updatedAtUnixMs: 2
},
activeTurnId: `${agentSessionId}-turn`,
agentSessionId,
agentTargetId,
cwd: "/workspace",
kind: "root",
latestTurnInteractions: [],
pendingInteractions: [],
provider: agentTargetId.replace("local:", ""),
railSectionKey: "conversations",
title: agentSessionId,
updatedAtUnixMs: 2,
workspaceId: "test-workspace"
});
}
function createSession(
agentSessionId: string,
title: string,
updatedAtUnixMs: number,
isolation?: {
mode: "worktree";
worktreePath: string;
branch: string;
baseCommit: string;
}
) {
return normalizeAgentActivitySession({
activeTurnId: null,
agentSessionId,
agentTargetId: "local:codex",
cwd: "/workspace",
latestTurnInteractions: [],
pendingInteractions: [],
provider: "codex",
railSectionKey: "conversations",
...(isolation ? { isolation } : {}),
title,
updatedAtUnixMs,
workspaceId: "test-workspace"
});
}