fix(api): align frontend with REST contracts
This commit is contained in:
+41
-30
@@ -41,20 +41,34 @@ const makeStream = (chunks: string[]) =>
|
||||
},
|
||||
});
|
||||
|
||||
const mockNewSessionStream = (response: unknown) => {
|
||||
apiFetch
|
||||
.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
json: async () => ({ session_id: "s1" }),
|
||||
})
|
||||
.mockResolvedValueOnce(response);
|
||||
};
|
||||
|
||||
describe("streamAgentChat", () => {
|
||||
beforeEach(() => {
|
||||
apiFetch.mockReset();
|
||||
});
|
||||
|
||||
it("parses token and done events from chunked SSE", async () => {
|
||||
apiFetch.mockResolvedValue({
|
||||
ok: true,
|
||||
body: makeStream([
|
||||
'event: token\ndata: {"session_id":"s1","content":"he"}\n\n',
|
||||
'event: token\ndata: {"session_id":"s1","content":"llo"}\n\n',
|
||||
'event: done\ndata: {"session_id":"s1"}\n\n',
|
||||
]),
|
||||
});
|
||||
apiFetch
|
||||
.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
json: async () => ({ session_id: "s1" }),
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
body: makeStream([
|
||||
'event: token\ndata: {"session_id":"s1","content":"he"}\n\n',
|
||||
'event: token\ndata: {"session_id":"s1","content":"llo"}\n\n',
|
||||
'event: done\ndata: {"session_id":"s1"}\n\n',
|
||||
]),
|
||||
});
|
||||
|
||||
const events: Array<{ type: string; content?: string; sessionId?: string }> = [];
|
||||
|
||||
@@ -65,14 +79,13 @@ describe("streamAgentChat", () => {
|
||||
});
|
||||
|
||||
expect(apiFetch).toHaveBeenCalledWith(
|
||||
expect.stringContaining("/api/v1/agent/chat/stream"),
|
||||
expect.stringContaining("/api/v1/agent/sessions/s1/runs"),
|
||||
expect.objectContaining({
|
||||
method: "POST",
|
||||
projectHeaderMode: "include",
|
||||
skipAuthRedirect: true,
|
||||
body: JSON.stringify({
|
||||
message: "hi",
|
||||
session_id: undefined,
|
||||
model: "provider/model",
|
||||
approval_mode: undefined,
|
||||
}),
|
||||
@@ -111,7 +124,7 @@ describe("streamAgentChat", () => {
|
||||
});
|
||||
|
||||
expect(apiFetch).toHaveBeenCalledWith(
|
||||
expect.stringContaining("/api/v1/agent/chat/session/s1/stream"),
|
||||
expect.stringContaining("/api/v1/agent/sessions/s1/runs/current/events"),
|
||||
expect.objectContaining({
|
||||
method: "GET",
|
||||
projectHeaderMode: "include",
|
||||
@@ -132,7 +145,7 @@ describe("streamAgentChat", () => {
|
||||
});
|
||||
|
||||
it("parses progress events", async () => {
|
||||
apiFetch.mockResolvedValue({
|
||||
mockNewSessionStream({
|
||||
ok: true,
|
||||
body: makeStream([
|
||||
'event: progress\ndata: {"session_id":"s1","id":"p1","phase":"tool","status":"running","title":"正在调用后端数据查询","detail":"GET /api/v1/demo"}\n\n',
|
||||
@@ -159,7 +172,7 @@ describe("streamAgentChat", () => {
|
||||
});
|
||||
|
||||
it("parses tool_call arguments when params is empty", async () => {
|
||||
apiFetch.mockResolvedValue({
|
||||
mockNewSessionStream({
|
||||
ok: true,
|
||||
body: makeStream([
|
||||
'event: tool_call\ndata: {"session_id":"agent-1e75dd01-29e","tool":"locate_features","params":{},"arguments":"{\\"ids\\":[\\"142902\\"],\\"feature_type\\":\\"junction\\"}"}\n\n',
|
||||
@@ -183,7 +196,7 @@ describe("streamAgentChat", () => {
|
||||
});
|
||||
|
||||
it("parses permission request and response events", async () => {
|
||||
apiFetch.mockResolvedValue({
|
||||
mockNewSessionStream({
|
||||
ok: true,
|
||||
body: makeStream([
|
||||
'event: permission_request\ndata: {"session_id":"s1","request_id":"perm-1","permission":"bash","patterns":["rm *"],"target":"rm tmp.txt","always":["rm *"],"created_at":123}\n\n',
|
||||
@@ -220,7 +233,7 @@ describe("streamAgentChat", () => {
|
||||
});
|
||||
|
||||
it("parses question request, response, and todo update events", async () => {
|
||||
apiFetch.mockResolvedValue({
|
||||
mockNewSessionStream({
|
||||
ok: true,
|
||||
body: makeStream([
|
||||
'event: question_request\ndata: {"session_id":"s1","request_id":"q-1","questions":[{"header":"范围","question":"选择范围","options":[{"label":"城区","description":"中心城区"}],"multiple":false,"custom":true}],"tool":{"message_id":"m1","call_id":"c1"},"created_at":123}\n\n',
|
||||
@@ -283,7 +296,7 @@ describe("streamAgentChat", () => {
|
||||
});
|
||||
|
||||
it("emits error when response is not ok", async () => {
|
||||
apiFetch.mockResolvedValue({
|
||||
mockNewSessionStream({
|
||||
ok: false,
|
||||
body: null,
|
||||
text: async () => "bad request",
|
||||
@@ -301,7 +314,7 @@ describe("streamAgentChat", () => {
|
||||
});
|
||||
|
||||
it("emits re-login message on unauthorized response", async () => {
|
||||
apiFetch.mockResolvedValue({
|
||||
mockNewSessionStream({
|
||||
ok: false,
|
||||
status: 401,
|
||||
body: null,
|
||||
@@ -343,14 +356,11 @@ describe("streamAgentChat", () => {
|
||||
await abortAgentChat("s1");
|
||||
|
||||
expect(apiFetch).toHaveBeenCalledWith(
|
||||
expect.stringContaining("/api/v1/agent/chat/abort"),
|
||||
expect.stringContaining("/api/v1/agent/sessions/s1/runs/current"),
|
||||
expect.objectContaining({
|
||||
method: "POST",
|
||||
method: "DELETE",
|
||||
projectHeaderMode: "include",
|
||||
skipAuthRedirect: true,
|
||||
body: JSON.stringify({
|
||||
session_id: "s1",
|
||||
}),
|
||||
}),
|
||||
);
|
||||
});
|
||||
@@ -365,13 +375,13 @@ describe("streamAgentChat", () => {
|
||||
await replyAgentPermission("s1", "perm-1", "once");
|
||||
|
||||
expect(apiFetch).toHaveBeenCalledWith(
|
||||
expect.stringContaining("/api/v1/agent/chat/permission/perm-1/reply"),
|
||||
expect.stringContaining("/api/v1/agent/sessions/s1/permission-responses"),
|
||||
expect.objectContaining({
|
||||
method: "POST",
|
||||
projectHeaderMode: "include",
|
||||
skipAuthRedirect: true,
|
||||
body: JSON.stringify({
|
||||
session_id: "s1",
|
||||
request_id: "perm-1",
|
||||
reply: "once",
|
||||
}),
|
||||
}),
|
||||
@@ -389,25 +399,27 @@ describe("streamAgentChat", () => {
|
||||
await rejectAgentQuestion("s1", "q-2");
|
||||
|
||||
expect(apiFetch).toHaveBeenCalledWith(
|
||||
expect.stringContaining("/api/v1/agent/chat/question/q-1/reply"),
|
||||
expect.stringContaining("/api/v1/agent/sessions/s1/question-responses"),
|
||||
expect.objectContaining({
|
||||
method: "POST",
|
||||
projectHeaderMode: "include",
|
||||
skipAuthRedirect: true,
|
||||
body: JSON.stringify({
|
||||
session_id: "s1",
|
||||
request_id: "q-1",
|
||||
action: "reply",
|
||||
answers: [["城区"]],
|
||||
}),
|
||||
}),
|
||||
);
|
||||
expect(apiFetch).toHaveBeenCalledWith(
|
||||
expect.stringContaining("/api/v1/agent/chat/question/q-2/reject"),
|
||||
expect.stringContaining("/api/v1/agent/sessions/s1/question-responses"),
|
||||
expect.objectContaining({
|
||||
method: "POST",
|
||||
projectHeaderMode: "include",
|
||||
skipAuthRedirect: true,
|
||||
body: JSON.stringify({
|
||||
session_id: "s1",
|
||||
request_id: "q-2",
|
||||
action: "reject",
|
||||
}),
|
||||
}),
|
||||
);
|
||||
@@ -425,11 +437,10 @@ describe("streamAgentChat", () => {
|
||||
|
||||
expect(sessionId).toBe("forked-s1");
|
||||
expect(apiFetch).toHaveBeenCalledWith(
|
||||
expect.stringContaining("/api/v1/agent/chat/fork"),
|
||||
expect.stringContaining("/api/v1/agent/sessions/s1/forks"),
|
||||
expect.objectContaining({
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
session_id: "s1",
|
||||
keep_message_count: 3,
|
||||
}),
|
||||
}),
|
||||
|
||||
Reference in New Issue
Block a user