Archived
test: cover client API and message parsing
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { parseCommandResult, parseFileContentResponse, parseFileSuggestion, parseGitStatusResponse, parseMessagePage, parseSessionStatus, parseSlashCommand } from "./parsers";
|
||||
|
||||
describe("API parsers", () => {
|
||||
it("accepts legacy array message pages and paged message responses", () => {
|
||||
expect(parseMessagePage(["a", "b"])).toEqual({ messages: ["a", "b"], start: 0, total: 2 });
|
||||
expect(parseMessagePage({ messages: ["c"], start: 3, total: 9 })).toEqual({ messages: ["c"], start: 3, total: 9 });
|
||||
});
|
||||
|
||||
it("validates session status including optional model and nullable context usage", () => {
|
||||
expect(parseSessionStatus({
|
||||
sessionId: "s1",
|
||||
isStreaming: false,
|
||||
isCompacting: true,
|
||||
isBashRunning: false,
|
||||
pendingMessageCount: 2,
|
||||
tokens: { input: 1, output: 2, cacheRead: 3, cacheWrite: 4, total: 10 },
|
||||
cost: 0.12,
|
||||
model: { provider: "p", id: "m", contextWindow: 100, reasoning: { effort: "low" } },
|
||||
contextUsage: { tokens: null, contextWindow: 100, percent: 0.5 },
|
||||
thinkingLevel: "medium",
|
||||
})).toEqual({
|
||||
sessionId: "s1",
|
||||
isStreaming: false,
|
||||
isCompacting: true,
|
||||
isBashRunning: false,
|
||||
pendingMessageCount: 2,
|
||||
tokens: { input: 1, output: 2, cacheRead: 3, cacheWrite: 4, total: 10 },
|
||||
cost: 0.12,
|
||||
model: { provider: "p", id: "m", contextWindow: 100, reasoning: { effort: "low" } },
|
||||
contextUsage: { tokens: null, contextWindow: 100, percent: 0.5 },
|
||||
thinkingLevel: "medium",
|
||||
});
|
||||
});
|
||||
|
||||
it("rejects invalid enum-like fields", () => {
|
||||
expect(() => parseSlashCommand({ name: "bad", source: "remote" })).toThrow("Invalid command source");
|
||||
expect(() => parseFileSuggestion({ path: "a", kind: "deleted" })).toThrow("Invalid file kind");
|
||||
expect(() => parseGitStatusResponse({ isGitRepo: true, hash: "h", files: [{ path: "a", index: "weird", workingTree: "modified" }] })).toThrow("Invalid git file state");
|
||||
});
|
||||
|
||||
it("validates file content responses", () => {
|
||||
expect(parseFileContentResponse({
|
||||
path: "README.md",
|
||||
language: "markdown",
|
||||
encoding: "utf8",
|
||||
size: 4,
|
||||
modifiedAt: "now",
|
||||
content: "text",
|
||||
truncated: false,
|
||||
binary: false,
|
||||
})).toMatchObject({ path: "README.md", language: "markdown", content: "text" });
|
||||
|
||||
expect(() => parseFileContentResponse({ encoding: "base64" })).toThrow("Invalid file encoding");
|
||||
});
|
||||
|
||||
it("parses command result variants", () => {
|
||||
expect(parseCommandResult({ type: "unsupported", message: "nope" })).toEqual({ type: "unsupported", message: "nope" });
|
||||
expect(parseCommandResult({ type: "select", requestId: "r1", title: "Pick", options: [{ value: "v", label: "Label", description: "desc" }] })).toEqual({ type: "select", requestId: "r1", title: "Pick", options: [{ value: "v", label: "Label", description: "desc" }] });
|
||||
expect(parseCommandResult({ type: "done", message: "ok" })).toEqual({ type: "done", message: "ok" });
|
||||
expect(() => parseCommandResult({ type: "later" })).toThrow("Invalid command result type");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,60 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { appendText, normalizeMessage, normalizeMessages, textMessage } from "./chatMessages";
|
||||
|
||||
describe("chat message normalization", () => {
|
||||
it("normalizes simple text messages and drops empty content", () => {
|
||||
expect(normalizeMessages([
|
||||
{ role: "user", content: "hello" },
|
||||
{ role: "assistant", content: "" },
|
||||
{ role: "unknown", content: "system text" },
|
||||
])).toEqual([
|
||||
textMessage("user", "hello"),
|
||||
textMessage("system", "system text"),
|
||||
]);
|
||||
});
|
||||
|
||||
it("normalizes tool calls and tool results", () => {
|
||||
expect(normalizeMessage({ role: "assistant", content: [{ type: "toolCall", name: "bash", arguments: { command: "npm test" } }] })).toEqual([
|
||||
{ role: "assistant", parts: [{ type: "toolCall", toolName: "bash", summary: "npm test" }] },
|
||||
]);
|
||||
expect(normalizeMessage({ role: "toolResult", toolName: "bash", isError: true, content: [{ type: "text", text: "failed" }] })).toEqual([
|
||||
{ role: "tool", parts: [{ type: "toolResult", toolName: "bash", text: "failed", isError: true }] },
|
||||
]);
|
||||
});
|
||||
|
||||
it("extracts skill invocation blocks into dedicated skill and user messages", () => {
|
||||
expect(normalizeMessage({ role: "user", content: "<skill name=\"playwright\" location=\"/skills/playwright\">\nUse browser\n</skill>\n\nNow test the UI" })).toEqual([
|
||||
{ role: "user", parts: [{ type: "skillInvocation", name: "playwright", location: "/skills/playwright", content: "Use browser" }] },
|
||||
textMessage("user", "Now test the UI"),
|
||||
]);
|
||||
});
|
||||
|
||||
it("formats bash execution records as bash chat lines", () => {
|
||||
expect(normalizeMessage({
|
||||
role: "bashExecution",
|
||||
command: "npm test",
|
||||
excludeFromContext: true,
|
||||
output: "ok",
|
||||
exitCode: 0,
|
||||
truncated: true,
|
||||
fullOutputPath: "/tmp/out.log",
|
||||
})).toEqual([
|
||||
textMessage("bash", "excluded from context\n\n$ npm test\n\nok\n\nexit 0\n\noutput truncated\n\nfull output: /tmp/out.log"),
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("appendText", () => {
|
||||
it("appends to the previous same-role text message", () => {
|
||||
expect(appendText([textMessage("assistant", "hello")], "assistant", " world")).toEqual([
|
||||
textMessage("assistant", "hello world"),
|
||||
]);
|
||||
});
|
||||
|
||||
it("starts a new message when role or last part does not match", () => {
|
||||
expect(appendText([textMessage("user", "hello")], "assistant", "hi")).toEqual([
|
||||
textMessage("user", "hello"),
|
||||
textMessage("assistant", "hi"),
|
||||
]);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user