VibeGame / tests /tools.test.ts
dylanebert's picture
refactor
bc7e9cd
raw
history blame
5.51 kB
import { describe, test, expect, beforeEach } from "bun:test";
import {
mcpClientManager,
setMCPWebSocketConnection,
} from "../src/lib/server/mcp-client";
import {
updateEditorContent,
observeConsoleTool,
} from "../src/lib/server/tools";
import { consoleBuffer } from "../src/lib/server/console-buffer";
import type { WebSocket } from "ws";
describe("MCP Editor Tools", () => {
const defaultContent = `<canvas id="game-canvas"></canvas>
<world canvas="#game-canvas">
<static-part pos="0 0 0" shape="box"></static-part>
</world>`;
let tools: ReturnType<typeof mcpClientManager.getTools>;
beforeEach(async () => {
updateEditorContent(defaultContent);
consoleBuffer.clear();
await mcpClientManager.initialize();
tools = mcpClientManager.getTools();
});
test("reads complete editor content", async () => {
const readTool = tools.find((t) => t.name === "read_editor");
expect(readTool).toBeDefined();
const result = await readTool!.func({});
expect(result).toContain("game-canvas");
expect(result).toContain("static-part");
});
test("reads specific lines from editor", async () => {
const readLinesTool = tools.find((t) => t.name === "read_editor_lines");
expect(readLinesTool).toBeDefined();
const result = await readLinesTool!.func({ startLine: 2, endLine: 3 });
expect(result).toContain("Lines 2-3");
expect(result).toContain("world");
expect(result).toContain("static-part");
});
test("handles line reading out of bounds", async () => {
const readLinesTool = tools.find((t) => t.name === "read_editor_lines");
const result = await readLinesTool!.func({ startLine: 999 });
expect(result).toContain("Error:");
expect(result).toContain("exceeds total lines");
});
test("searches for text in editor", async () => {
const searchTool = tools.find((t) => t.name === "search_editor");
expect(searchTool).toBeDefined();
const result = await searchTool!.func({ query: "canvas" });
expect(result).toContain("Found");
expect(result).toContain("canvas");
expect(result).toContain("game-canvas");
});
test("handles search with no results", async () => {
const searchTool = tools.find((t) => t.name === "search_editor");
const result = await searchTool!.func({ query: "nonexistent" });
expect(result).toContain("No matches found");
});
test("edits existing content", async () => {
const editTool = tools.find((t) => t.name === "edit_editor");
const readTool = tools.find((t) => t.name === "read_editor");
expect(editTool).toBeDefined();
await editTool!.func({
oldText: 'shape="box"',
newText: 'shape="sphere"',
});
const content = await readTool!.func({});
expect(content).toContain("sphere");
expect(content).not.toContain('shape="box"');
});
test("handles edit with non-matching text", async () => {
const editTool = tools.find((t) => t.name === "edit_editor");
const result = await editTool!.func({
oldText: "nonexistent",
newText: "replacement",
});
expect(result).toContain("Error");
});
test("writes complete new content", async () => {
const writeTool = tools.find((t) => t.name === "write_editor");
const readTool = tools.find((t) => t.name === "read_editor");
expect(writeTool).toBeDefined();
const newContent = "<div>New content</div>";
await writeTool!.func({ content: newContent });
const result = await readTool!.func({});
expect(result).toContain("New content");
expect(result).not.toContain("game-canvas");
});
});
describe("Console Tool", () => {
beforeEach(() => {
consoleBuffer.clear();
});
test("observes empty console", async () => {
const result = await observeConsoleTool.func("");
expect(result).toContain("No new console messages");
});
test("observes console messages", async () => {
consoleBuffer.addMessage({
id: "1",
type: "log",
message: "Test message",
timestamp: Date.now(),
});
const result = await observeConsoleTool.func("");
expect(result).toContain("Test message");
expect(result).toContain("[log]");
});
test("marks messages as read", async () => {
consoleBuffer.addMessage({
id: "1",
type: "error",
message: "Error occurred",
timestamp: Date.now(),
});
await observeConsoleTool.func("");
const secondRead = await observeConsoleTool.func("");
expect(secondRead).toContain("No new console messages");
});
test("detects game state from console", async () => {
consoleBuffer.addMessage({
id: "1",
type: "log",
message: "🎮 Starting game",
timestamp: Date.now(),
});
const result = await observeConsoleTool.func("");
expect(result).toContain("Loading: true");
consoleBuffer.addMessage({
id: "2",
type: "log",
message: "✅ Game started",
timestamp: Date.now() + 100,
});
const result2 = await observeConsoleTool.func("");
expect(result2).toContain("Ready: true");
});
});
describe("MCP WebSocket Connection", () => {
test("sets WebSocket connection for MCP client", () => {
const mockWs = {
readyState: 1, // OPEN
OPEN: 1,
send: (data: string) => {
const message = JSON.parse(data);
expect(message.type).toBeDefined();
expect(message.payload).toBeDefined();
},
} as unknown as WebSocket;
setMCPWebSocketConnection(mockWs);
});
});