149 lines
4.5 KiB
TypeScript
149 lines
4.5 KiB
TypeScript
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
import { createAgentApiClient } from "./client";
|
|
|
|
describe("Agent API client sessions", () => {
|
|
afterEach(() => {
|
|
vi.unstubAllGlobals();
|
|
});
|
|
|
|
it("lists normalized sessions in newest-first order", async () => {
|
|
vi.stubGlobal(
|
|
"fetch",
|
|
vi.fn(async () =>
|
|
new Response(
|
|
JSON.stringify({
|
|
sessions: [
|
|
{ id: "old", title: "", created_at: 100, updated_at: 120 },
|
|
{ id: "new", title: " 新会话 ", created_at: 200, updated_at: 210, run_status: "running" }
|
|
]
|
|
}),
|
|
{ status: 200 }
|
|
)
|
|
)
|
|
);
|
|
|
|
const sessions = await createAgentApiClient("http://agent.local").listSessions();
|
|
|
|
expect(sessions).toEqual([
|
|
{
|
|
id: "new",
|
|
title: "新会话",
|
|
createdAt: 200,
|
|
updatedAt: 210,
|
|
status: undefined,
|
|
parentSessionId: undefined,
|
|
isStreaming: undefined,
|
|
runStatus: "running"
|
|
},
|
|
{
|
|
id: "old",
|
|
title: "新对话",
|
|
createdAt: 100,
|
|
updatedAt: 120,
|
|
status: undefined,
|
|
parentSessionId: undefined,
|
|
isStreaming: undefined,
|
|
runStatus: undefined
|
|
}
|
|
]);
|
|
});
|
|
|
|
it("returns null when loading a missing session", async () => {
|
|
vi.stubGlobal("fetch", vi.fn(async () => new Response(JSON.stringify({ message: "missing" }), { status: 404 })));
|
|
|
|
await expect(createAgentApiClient("http://agent.local").loadSession("missing")).resolves.toBeNull();
|
|
});
|
|
|
|
it("falls back when a candidate is not the agent service", async () => {
|
|
const fetchMock = vi
|
|
.fn()
|
|
.mockResolvedValueOnce(new Response(JSON.stringify({ message: "not found" }), { status: 404 }))
|
|
.mockResolvedValueOnce(
|
|
new Response(
|
|
JSON.stringify({
|
|
schema_version: "agent-ui-registry@1",
|
|
chart_grammars: [],
|
|
components: [],
|
|
actions: []
|
|
}),
|
|
{ status: 200 }
|
|
)
|
|
);
|
|
vi.stubGlobal("fetch", fetchMock);
|
|
|
|
await expect(createAgentApiClient(["http://not-agent.local", "http://agent.local"]).getUiRegistry()).resolves.toEqual({
|
|
schema_version: "agent-ui-registry@1",
|
|
chart_grammars: [],
|
|
components: [],
|
|
actions: []
|
|
});
|
|
expect(fetchMock).toHaveBeenNthCalledWith(
|
|
1,
|
|
"http://not-agent.local/api/v1/agent/chat/ui-registry",
|
|
undefined
|
|
);
|
|
expect(fetchMock).toHaveBeenNthCalledWith(2, "http://agent.local/api/v1/agent/chat/ui-registry", undefined);
|
|
});
|
|
|
|
it("sends title update payloads using backend field names", async () => {
|
|
const fetchMock = vi.fn(async () => new Response(JSON.stringify({ ok: true }), { status: 200 }));
|
|
vi.stubGlobal("fetch", fetchMock);
|
|
|
|
await createAgentApiClient("http://agent.local").updateSessionTitle("session-1", " 标题 ", {
|
|
isTitleManuallyEdited: true
|
|
});
|
|
|
|
expect(fetchMock).toHaveBeenCalledWith(
|
|
"http://agent.local/api/v1/agent/chat/session/session-1/title",
|
|
expect.objectContaining({
|
|
method: "PATCH",
|
|
body: JSON.stringify({
|
|
title: "标题",
|
|
is_title_manually_edited: true
|
|
})
|
|
})
|
|
);
|
|
});
|
|
|
|
it("streams session events from the backend SSE endpoint", async () => {
|
|
vi.stubGlobal(
|
|
"fetch",
|
|
vi.fn(async () =>
|
|
new Response(
|
|
[
|
|
'event: state\ndata: {"session_id":"session-1","messages":[{"id":"u1","role":"user","parts":[{"type":"text","text":"检查压力"}]}],"is_streaming":true,"run_status":"running"}\n\n',
|
|
'event: token\ndata: {"session_id":"session-1","content":"处理中"}\n\n',
|
|
'event: done\ndata: {"session_id":"session-1","duration_ms":20}\n\n'
|
|
].join(""),
|
|
{ status: 200 }
|
|
)
|
|
)
|
|
);
|
|
const events: unknown[] = [];
|
|
|
|
await createAgentApiClient("http://agent.local").streamSession("session-1", {
|
|
onEvent: (event) => events.push(event)
|
|
});
|
|
|
|
expect(events).toEqual([
|
|
{
|
|
type: "state",
|
|
sessionId: "session-1",
|
|
messages: [{ id: "u1", role: "user", parts: [{ type: "text", text: "检查压力" }] }],
|
|
isStreaming: true,
|
|
runStatus: "running"
|
|
},
|
|
{
|
|
type: "token",
|
|
sessionId: "session-1",
|
|
data: { session_id: "session-1", content: "处理中" }
|
|
},
|
|
{
|
|
type: "done",
|
|
sessionId: "session-1",
|
|
data: { session_id: "session-1", duration_ms: 20 }
|
|
}
|
|
]);
|
|
});
|
|
});
|