generated from nhcarrigan/template
feat(tools): set up proper CI (#2)
### Explanation _No response_ ### Issue _No response_ ### Attestations - [ ] I have read and agree to the [Code of Conduct](https://docs.nhcarrigan.com/community/coc/) - [ ] I have read and agree to the [Community Guidelines](https://docs.nhcarrigan.com/community/guide/). - [ ] My contribution complies with the [Contributor Covenant](https://docs.nhcarrigan.com/dev/covenant/). ### Dependencies - [ ] I have pinned the dependencies to a specific patch version. ### Style - [ ] I have run the linter and resolved any errors. - [ ] My pull request uses an appropriate title, matching the conventional commit standards. - [ ] My scope of feat/fix/chore/etc. correctly matches the nature of changes in my pull request. ### Tests - [ ] My contribution adds new code, and I have added tests to cover it. - [ ] My contribution modifies existing code, and I have updated the tests to reflect these changes. - [ ] All new and existing tests pass locally with my changes. - [ ] Code coverage remains at or above the configured threshold. ### Documentation _No response_ ### Versioning _No response_ Reviewed-on: #2 Co-authored-by: Naomi Carrigan <commits@nhcarrigan.com> Co-committed-by: Naomi Carrigan <commits@nhcarrigan.com>
This commit was merged in pull request #2.
This commit is contained in:
@@ -0,0 +1,240 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { mapMessageToState, extractTextFromMessage, extractToolInfo } from "./stateMapper";
|
||||
import type { ClaudeStreamMessage } from "$lib/types/messages";
|
||||
|
||||
describe("stateMapper", () => {
|
||||
describe("mapMessageToState", () => {
|
||||
it("returns idle for system init message", () => {
|
||||
const message: ClaudeStreamMessage = {
|
||||
type: "system",
|
||||
subtype: "init",
|
||||
session_id: "test-session",
|
||||
cwd: "/home/test",
|
||||
tools: ["Read", "Write", "Edit"],
|
||||
};
|
||||
expect(mapMessageToState(message)).toBe("idle");
|
||||
});
|
||||
|
||||
it("returns null for non-init system messages", () => {
|
||||
const message: ClaudeStreamMessage = {
|
||||
type: "system",
|
||||
subtype: "compact_boundary",
|
||||
};
|
||||
expect(mapMessageToState(message)).toBeNull();
|
||||
});
|
||||
|
||||
it("returns searching for Read tool", () => {
|
||||
const message: ClaudeStreamMessage = {
|
||||
type: "assistant",
|
||||
message: {
|
||||
content: [
|
||||
{
|
||||
type: "tool_use",
|
||||
id: "tool-1",
|
||||
name: "Read",
|
||||
input: { file_path: "/test/file.txt" },
|
||||
},
|
||||
],
|
||||
model: "claude-3",
|
||||
stop_reason: "tool_use",
|
||||
},
|
||||
};
|
||||
expect(mapMessageToState(message)).toBe("searching");
|
||||
});
|
||||
|
||||
it("returns coding for Edit tool", () => {
|
||||
const message: ClaudeStreamMessage = {
|
||||
type: "assistant",
|
||||
message: {
|
||||
content: [
|
||||
{
|
||||
type: "tool_use",
|
||||
id: "tool-1",
|
||||
name: "Edit",
|
||||
input: { file_path: "/test/file.txt", old_string: "foo", new_string: "bar" },
|
||||
},
|
||||
],
|
||||
model: "claude-3",
|
||||
stop_reason: "tool_use",
|
||||
},
|
||||
};
|
||||
expect(mapMessageToState(message)).toBe("coding");
|
||||
});
|
||||
|
||||
it("returns mcp for mcp__ prefixed tools", () => {
|
||||
const message: ClaudeStreamMessage = {
|
||||
type: "assistant",
|
||||
message: {
|
||||
content: [
|
||||
{
|
||||
type: "tool_use",
|
||||
id: "tool-1",
|
||||
name: "mcp__github__list_repos",
|
||||
input: {},
|
||||
},
|
||||
],
|
||||
model: "claude-3",
|
||||
stop_reason: "tool_use",
|
||||
},
|
||||
};
|
||||
expect(mapMessageToState(message)).toBe("mcp");
|
||||
});
|
||||
|
||||
it("returns thinking for Task tool", () => {
|
||||
const message: ClaudeStreamMessage = {
|
||||
type: "assistant",
|
||||
message: {
|
||||
content: [
|
||||
{
|
||||
type: "tool_use",
|
||||
id: "tool-1",
|
||||
name: "Task",
|
||||
input: { prompt: "test task" },
|
||||
},
|
||||
],
|
||||
model: "claude-3",
|
||||
stop_reason: "tool_use",
|
||||
},
|
||||
};
|
||||
expect(mapMessageToState(message)).toBe("thinking");
|
||||
});
|
||||
|
||||
it("returns typing for text content", () => {
|
||||
const message: ClaudeStreamMessage = {
|
||||
type: "assistant",
|
||||
message: {
|
||||
content: [{ type: "text", text: "Hello, Naomi!" }],
|
||||
model: "claude-3",
|
||||
stop_reason: "end_turn",
|
||||
},
|
||||
};
|
||||
expect(mapMessageToState(message)).toBe("typing");
|
||||
});
|
||||
|
||||
it("returns success for result success message", () => {
|
||||
const message: ClaudeStreamMessage = {
|
||||
type: "result",
|
||||
subtype: "success",
|
||||
result: "Task completed",
|
||||
};
|
||||
expect(mapMessageToState(message)).toBe("success");
|
||||
});
|
||||
|
||||
it("returns error for result error message", () => {
|
||||
const message: ClaudeStreamMessage = {
|
||||
type: "result",
|
||||
subtype: "error_max_turns",
|
||||
};
|
||||
expect(mapMessageToState(message)).toBe("error");
|
||||
});
|
||||
|
||||
it("returns null for user messages", () => {
|
||||
const message: ClaudeStreamMessage = {
|
||||
type: "user",
|
||||
message: { content: [{ type: "text", text: "Hello" }] },
|
||||
};
|
||||
expect(mapMessageToState(message)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("extractTextFromMessage", () => {
|
||||
it("extracts text from assistant message", () => {
|
||||
const message: ClaudeStreamMessage = {
|
||||
type: "assistant",
|
||||
message: {
|
||||
content: [
|
||||
{ type: "text", text: "Hello!" },
|
||||
{ type: "text", text: "How are you?" },
|
||||
],
|
||||
model: "claude-3",
|
||||
stop_reason: "end_turn",
|
||||
},
|
||||
};
|
||||
expect(extractTextFromMessage(message)).toBe("Hello!\nHow are you?");
|
||||
});
|
||||
|
||||
it("returns null for assistant message without text", () => {
|
||||
const message: ClaudeStreamMessage = {
|
||||
type: "assistant",
|
||||
message: {
|
||||
content: [
|
||||
{
|
||||
type: "tool_use",
|
||||
id: "tool-1",
|
||||
name: "Read",
|
||||
input: { file_path: "/test/file.txt" },
|
||||
},
|
||||
],
|
||||
model: "claude-3",
|
||||
stop_reason: "tool_use",
|
||||
},
|
||||
};
|
||||
expect(extractTextFromMessage(message)).toBeNull();
|
||||
});
|
||||
|
||||
it("extracts text from stream_event delta", () => {
|
||||
const message: ClaudeStreamMessage = {
|
||||
type: "stream_event",
|
||||
event: {
|
||||
type: "content_block_delta",
|
||||
index: 0,
|
||||
delta: { type: "text_delta", text: "streaming text" },
|
||||
},
|
||||
};
|
||||
expect(extractTextFromMessage(message)).toBe("streaming text");
|
||||
});
|
||||
|
||||
it("extracts result from result message", () => {
|
||||
const message: ClaudeStreamMessage = {
|
||||
type: "result",
|
||||
subtype: "success",
|
||||
result: "Completed successfully",
|
||||
};
|
||||
expect(extractTextFromMessage(message)).toBe("Completed successfully");
|
||||
});
|
||||
});
|
||||
|
||||
describe("extractToolInfo", () => {
|
||||
it("extracts tool info from assistant message", () => {
|
||||
const message: ClaudeStreamMessage = {
|
||||
type: "assistant",
|
||||
message: {
|
||||
content: [
|
||||
{
|
||||
type: "tool_use",
|
||||
id: "tool-1",
|
||||
name: "Read",
|
||||
input: { file_path: "/test/file.txt" },
|
||||
},
|
||||
{
|
||||
type: "tool_use",
|
||||
id: "tool-2",
|
||||
name: "Edit",
|
||||
input: { file_path: "/test/file.txt", old_string: "a", new_string: "b" },
|
||||
},
|
||||
],
|
||||
model: "claude-3",
|
||||
stop_reason: "tool_use",
|
||||
},
|
||||
};
|
||||
const tools = extractToolInfo(message);
|
||||
expect(tools).toHaveLength(2);
|
||||
expect(tools[0]).toEqual({
|
||||
name: "Read",
|
||||
input: { file_path: "/test/file.txt" },
|
||||
});
|
||||
expect(tools[1]).toEqual({
|
||||
name: "Edit",
|
||||
input: { file_path: "/test/file.txt", old_string: "a", new_string: "b" },
|
||||
});
|
||||
});
|
||||
|
||||
it("returns empty array for non-assistant messages", () => {
|
||||
const message: ClaudeStreamMessage = {
|
||||
type: "user",
|
||||
message: { content: [{ type: "text", text: "Hello" }] },
|
||||
};
|
||||
expect(extractToolInfo(message)).toEqual([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user