Files
tutti/packages/agent/gui/agentActivityRuntime.sessionMessages.spec.tsx
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

135 lines
3.7 KiB
TypeScript

import { act, renderHook } from "@testing-library/react";
import type {
AgentActivityMessage,
AgentActivitySnapshot
} from "@tutti-os/agent-activity-core";
import type { PropsWithChildren } from "react";
import { describe, expect, it } from "vitest";
import {
AgentGUIRuntimeProvider,
useAgentActivitySessionMessages,
type AgentGUIRuntime
} from "./agentActivityRuntime";
describe("useAgentActivitySessionMessages", () => {
it("delivers projected streaming text without rendering an unrelated Session", () => {
const messagesA = [message("session-a", "partial")];
const messagesB = [message("session-b", "settled")];
const store = createRuntimeStore(
snapshot({ "session-a": messagesA, "session-b": messagesB })
);
const wrapper = ({ children }: PropsWithChildren) => (
<AgentGUIRuntimeProvider runtime={store.runtime}>
{children}
</AgentGUIRuntimeProvider>
);
let rendersA = 0;
let rendersB = 0;
const renderedA = renderHook(
() => {
rendersA += 1;
return useAgentActivitySessionMessages("workspace-1", ["session-a"]);
},
{ wrapper }
);
renderHook(
() => {
rendersB += 1;
return useAgentActivitySessionMessages("workspace-1", ["session-b"]);
},
{ wrapper }
);
const initialRendersA = rendersA;
const initialRendersB = rendersB;
act(() => {
store.publish(
snapshot({
"session-a": [message("session-a", "partial text")],
"session-b": messagesB
})
);
});
expect(renderedA.result.current["session-a"]?.[0]?.payload.text).toBe(
"partial text"
);
expect(rendersA).toBeGreaterThan(initialRendersA);
expect(rendersB).toBe(initialRendersB);
});
it("subscribes to projected messages for child Sessions in the family", () => {
const rootMessages = [message("root", "root text")];
const store = createRuntimeStore(
snapshot({
root: rootMessages,
child: [message("child", "child partial")]
})
);
const wrapper = ({ children }: PropsWithChildren) => (
<AgentGUIRuntimeProvider runtime={store.runtime}>
{children}
</AgentGUIRuntimeProvider>
);
const rendered = renderHook(
() => useAgentActivitySessionMessages("workspace-1", ["root", "child"]),
{ wrapper }
);
act(() => {
store.publish(
snapshot({
root: rootMessages,
child: [message("child", "child partial text")]
})
);
});
expect(rendered.result.current.child?.[0]?.payload.text).toBe(
"child partial text"
);
});
});
function createRuntimeStore(initial: AgentActivitySnapshot) {
let current = initial;
const listeners = new Set<() => void>();
const runtime = {
getSnapshot: () => current,
origin: "test",
subscribe(_workspaceId: string, listener: () => void) {
listeners.add(listener);
return () => listeners.delete(listener);
}
} as unknown as AgentGUIRuntime;
return {
publish(next: AgentActivitySnapshot) {
current = next;
for (const listener of listeners) listener();
},
runtime
};
}
function snapshot(
sessionMessagesById: Record<string, readonly AgentActivityMessage[]>
): AgentActivitySnapshot {
return { sessionMessagesById } as AgentActivitySnapshot;
}
function message(agentSessionId: string, text: string): AgentActivityMessage {
return {
agentSessionId,
kind: "text",
messageId: `message-${agentSessionId}`,
occurredAtUnixMs: 1,
payload: { text },
role: "assistant",
sequence: 1,
status: "streaming",
turnId: "turn-1",
version: 1,
workspaceId: "workspace-1"
};
}