feat: Add testing framework and initial test cases for various tools and database operations
This commit is contained in:
87
tests/unit/storage/config.test.ts
Normal file
87
tests/unit/storage/config.test.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* @Date: 2026-01-07 09:09:22
|
||||
* @LastEditors: 陈子健
|
||||
* @LastEditTime: 2026-01-07 10:04:55
|
||||
* @FilePath: /cloud-mcp/tests/unit/storage/config.test.ts
|
||||
*/
|
||||
/**
|
||||
* Configuration management tests
|
||||
*/
|
||||
|
||||
import { describe, test, expect, beforeEach, afterEach } from "bun:test";
|
||||
import { configManager } from "../../../src/storage/config.js";
|
||||
import { setTestEnv } from "../../helpers/test-utils.js";
|
||||
|
||||
describe("ConfigManager", () => {
|
||||
let cleanupEnv: () => void;
|
||||
|
||||
beforeEach(() => {
|
||||
cleanupEnv = setTestEnv({
|
||||
NAS_HOST: "test-nas-host",
|
||||
NAS_USERNAME: "test-user",
|
||||
NAS_PASSWORD: "test-password",
|
||||
NAS_PROTOCOL: "smb",
|
||||
SERVER_HOST: "test-server",
|
||||
SERVER_USERNAME: "test-server-user",
|
||||
SERVER_PORT: "2222",
|
||||
SERVER_KEY_PATH: "/test/key/path",
|
||||
ROUTER_HOST: "test-router",
|
||||
ROUTER_USERNAME: "test-router-user",
|
||||
ROUTER_PASSWORD: "test-router-password",
|
||||
FOOTBALL_API_KEY: "test-football-key",
|
||||
});
|
||||
configManager.reload();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cleanupEnv();
|
||||
});
|
||||
|
||||
test("should load NAS configuration from environment", () => {
|
||||
const nasConfig = configManager.getNASConfig();
|
||||
|
||||
expect(nasConfig.host).toBe("test-nas-host");
|
||||
expect(nasConfig.username).toBe("test-user");
|
||||
expect(nasConfig.password).toBe("test-password");
|
||||
expect(nasConfig.protocol).toBe("smb");
|
||||
});
|
||||
|
||||
test("should load server configuration from environment", () => {
|
||||
const serverConfig = configManager.getServerConfig();
|
||||
|
||||
expect(serverConfig.host).toBe("test-server");
|
||||
expect(serverConfig.username).toBe("test-server-user");
|
||||
expect(serverConfig.port).toBe(2222);
|
||||
expect(serverConfig.keyPath).toBe("/test/key/path");
|
||||
});
|
||||
|
||||
test("should load router configuration from environment", () => {
|
||||
const routerConfig = configManager.getRouterConfig();
|
||||
|
||||
expect(routerConfig.host).toBe("test-router");
|
||||
expect(routerConfig.username).toBe("test-router-user");
|
||||
expect(routerConfig.password).toBe("test-router-password");
|
||||
});
|
||||
|
||||
test("should get full configuration", () => {
|
||||
const config = configManager.getConfig();
|
||||
|
||||
expect(config.nas.host).toBe("test-nas-host");
|
||||
expect(config.server.host).toBe("test-server");
|
||||
expect(config.router.host).toBe("test-router");
|
||||
expect(config.footballApiKey).toBe("test-football-key");
|
||||
});
|
||||
|
||||
test("should handle missing environment variables", () => {
|
||||
cleanupEnv();
|
||||
// Clear all relevant env vars
|
||||
delete process.env.NAS_HOST;
|
||||
delete process.env.NAS_USERNAME;
|
||||
delete process.env.NAS_PASSWORD;
|
||||
delete process.env.NAS_PROTOCOL;
|
||||
configManager.reload();
|
||||
|
||||
const nasConfig = configManager.getNASConfig();
|
||||
expect(nasConfig.host).toBeUndefined();
|
||||
});
|
||||
});
|
||||
212
tests/unit/storage/database.test.ts
Normal file
212
tests/unit/storage/database.test.ts
Normal file
@@ -0,0 +1,212 @@
|
||||
/**
|
||||
* Database storage layer tests
|
||||
*/
|
||||
|
||||
import { describe, test, expect, beforeEach, afterEach } from "bun:test";
|
||||
import { database } from "../../../src/storage/database.js";
|
||||
import { createTempDir } from "../../helpers/test-utils.js";
|
||||
import { setupTestDatabase } from "../../helpers/database-helper.js";
|
||||
import {
|
||||
testCodeSnippet,
|
||||
testNote,
|
||||
testTask,
|
||||
testBabyMilestone,
|
||||
testMathResource,
|
||||
testGameWishlist,
|
||||
} from "../../fixtures/test-data.js";
|
||||
|
||||
describe("Database", () => {
|
||||
let testContext: ReturnType<typeof createTempDir>;
|
||||
let cleanupDb: () => void;
|
||||
|
||||
beforeEach(() => {
|
||||
testContext = createTempDir();
|
||||
cleanupDb = setupTestDatabase(testContext);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cleanupDb();
|
||||
testContext.cleanup();
|
||||
});
|
||||
|
||||
describe("Code Snippets", () => {
|
||||
test("should save and retrieve code snippet", () => {
|
||||
database.saveCodeSnippet(testCodeSnippet);
|
||||
const snippet = database.getCodeSnippet(testCodeSnippet.id);
|
||||
|
||||
expect(snippet).toBeDefined();
|
||||
expect(snippet?.title).toBe(testCodeSnippet.title);
|
||||
expect(snippet?.code).toBe(testCodeSnippet.code);
|
||||
expect(snippet?.language).toBe(testCodeSnippet.language);
|
||||
});
|
||||
|
||||
test("should list all code snippets", () => {
|
||||
database.saveCodeSnippet(testCodeSnippet);
|
||||
const snippets = database.getCodeSnippets();
|
||||
|
||||
expect(snippets.length).toBeGreaterThan(0);
|
||||
expect(snippets.find((s) => s.id === testCodeSnippet.id)).toBeDefined();
|
||||
});
|
||||
|
||||
test("should search code snippets", () => {
|
||||
database.saveCodeSnippet(testCodeSnippet);
|
||||
const results = database.searchCodeSnippets("Test");
|
||||
|
||||
expect(results.length).toBeGreaterThan(0);
|
||||
expect(results[0].title).toContain("Test");
|
||||
});
|
||||
|
||||
test("should search code snippets by tags", () => {
|
||||
database.saveCodeSnippet(testCodeSnippet);
|
||||
const results = database.searchCodeSnippets("", ["test"]);
|
||||
|
||||
expect(results.length).toBeGreaterThan(0);
|
||||
expect(results[0].tags).toContain("test");
|
||||
});
|
||||
|
||||
test("should delete code snippet", () => {
|
||||
database.saveCodeSnippet(testCodeSnippet);
|
||||
const deleted = database.deleteCodeSnippet(testCodeSnippet.id);
|
||||
|
||||
expect(deleted).toBe(true);
|
||||
expect(database.getCodeSnippet(testCodeSnippet.id)).toBeUndefined();
|
||||
});
|
||||
|
||||
test("should update existing code snippet", () => {
|
||||
database.saveCodeSnippet(testCodeSnippet);
|
||||
const updated = {
|
||||
...testCodeSnippet,
|
||||
title: "Updated Title",
|
||||
};
|
||||
database.saveCodeSnippet(updated);
|
||||
|
||||
const snippet = database.getCodeSnippet(testCodeSnippet.id);
|
||||
expect(snippet?.title).toBe("Updated Title");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Notes", () => {
|
||||
test("should save and retrieve note", () => {
|
||||
database.saveNote(testNote);
|
||||
const note = database.getNote(testNote.id);
|
||||
|
||||
expect(note).toBeDefined();
|
||||
expect(note?.title).toBe(testNote.title);
|
||||
expect(note?.content).toBe(testNote.content);
|
||||
});
|
||||
|
||||
test("should list all notes", () => {
|
||||
database.saveNote(testNote);
|
||||
const notes = database.getNotes();
|
||||
|
||||
expect(notes.length).toBeGreaterThan(0);
|
||||
expect(notes.find((n) => n.id === testNote.id)).toBeDefined();
|
||||
});
|
||||
|
||||
test("should search notes", () => {
|
||||
database.saveNote(testNote);
|
||||
const results = database.searchNotes("Test");
|
||||
|
||||
expect(results.length).toBeGreaterThan(0);
|
||||
expect(results[0].title).toContain("Test");
|
||||
});
|
||||
|
||||
test("should delete note", () => {
|
||||
database.saveNote(testNote);
|
||||
const deleted = database.deleteNote(testNote.id);
|
||||
|
||||
expect(deleted).toBe(true);
|
||||
expect(database.getNote(testNote.id)).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("Tasks", () => {
|
||||
test("should save and retrieve task", () => {
|
||||
database.saveTask(testTask);
|
||||
const task = database.getTask(testTask.id);
|
||||
|
||||
expect(task).toBeDefined();
|
||||
expect(task?.title).toBe(testTask.title);
|
||||
expect(task?.completed).toBe(false);
|
||||
});
|
||||
|
||||
test("should list all tasks", () => {
|
||||
database.saveTask(testTask);
|
||||
const tasks = database.getTasks();
|
||||
|
||||
expect(tasks.length).toBeGreaterThan(0);
|
||||
expect(tasks.find((t) => t.id === testTask.id)).toBeDefined();
|
||||
});
|
||||
|
||||
test("should filter tasks by completion status", () => {
|
||||
database.saveTask(testTask);
|
||||
const completedTask = { ...testTask, id: "task-2", completed: true };
|
||||
database.saveTask(completedTask);
|
||||
|
||||
const pendingTasks = database.getTasks(false);
|
||||
const completedTasks = database.getTasks(true);
|
||||
|
||||
expect(pendingTasks.length).toBeGreaterThan(0);
|
||||
expect(completedTasks.length).toBeGreaterThan(0);
|
||||
expect(pendingTasks.find((t) => t.id === testTask.id)).toBeDefined();
|
||||
expect(completedTasks.find((t) => t.id === "task-2")).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("Baby Milestones", () => {
|
||||
test("should save and retrieve baby milestone", () => {
|
||||
database.saveBabyMilestone(testBabyMilestone);
|
||||
const milestones = database.getBabyMilestones();
|
||||
|
||||
expect(milestones.length).toBeGreaterThan(0);
|
||||
expect(milestones.find((m) => m.id === testBabyMilestone.id)).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("Math Resources", () => {
|
||||
test("should save and retrieve math resource", () => {
|
||||
database.saveMathResource(testMathResource);
|
||||
const resources = database.getMathResources();
|
||||
|
||||
expect(resources.length).toBeGreaterThan(0);
|
||||
expect(resources.find((r) => r.id === testMathResource.id)).toBeDefined();
|
||||
});
|
||||
|
||||
test("should search math resources", () => {
|
||||
database.saveMathResource(testMathResource);
|
||||
const results = database.searchMathResources("Addition");
|
||||
|
||||
expect(results.length).toBeGreaterThan(0);
|
||||
expect(results[0].title).toContain("Addition");
|
||||
});
|
||||
|
||||
test("should filter math resources by grade", () => {
|
||||
database.saveMathResource(testMathResource);
|
||||
const results = database.searchMathResources("", "1st");
|
||||
|
||||
expect(results.length).toBeGreaterThan(0);
|
||||
expect(results[0].grade).toBe("1st");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Game Wishlist", () => {
|
||||
test("should save and retrieve game wishlist", () => {
|
||||
database.saveGameWishlist(testGameWishlist);
|
||||
const games = database.getGameWishlist();
|
||||
|
||||
expect(games.length).toBeGreaterThan(0);
|
||||
expect(games.find((g) => g.id === testGameWishlist.id)).toBeDefined();
|
||||
});
|
||||
|
||||
test("should delete game from wishlist", () => {
|
||||
database.saveGameWishlist(testGameWishlist);
|
||||
const deleted = database.deleteGameWishlist(testGameWishlist.id);
|
||||
|
||||
expect(deleted).toBe(true);
|
||||
const games = database.getGameWishlist();
|
||||
expect(games.find((g) => g.id === testGameWishlist.id)).toBeUndefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
97
tests/unit/tools/common/notes.test.ts
Normal file
97
tests/unit/tools/common/notes.test.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* @Date: 2026-01-07 09:11:08
|
||||
* @LastEditors: 陈子健
|
||||
* @LastEditTime: 2026-01-07 10:04:45
|
||||
* @FilePath: /cloud-mcp/tests/unit/tools/common/notes.test.ts
|
||||
*/
|
||||
/**
|
||||
* Notes tools tests
|
||||
*/
|
||||
|
||||
import { describe, test, expect, beforeEach, afterEach } from "bun:test";
|
||||
import { registerNoteTools } from "../../../../src/tools/common/notes.js";
|
||||
import { callTool } from "../../../helpers/tool-helper.js";
|
||||
import { createTempDir } from "../../../helpers/test-utils.js";
|
||||
import { setupTestDatabase } from "../../../helpers/database-helper.js";
|
||||
|
||||
describe("Notes Tools", () => {
|
||||
let testContext: ReturnType<typeof createTempDir>;
|
||||
let cleanupDb: () => void;
|
||||
|
||||
beforeEach(() => {
|
||||
testContext = createTempDir();
|
||||
cleanupDb = setupTestDatabase(testContext);
|
||||
registerNoteTools();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cleanupDb();
|
||||
testContext.cleanup();
|
||||
});
|
||||
|
||||
test("should create note", async () => {
|
||||
const result = await callTool("note_create", {
|
||||
title: "Test Note",
|
||||
content: "This is a test note",
|
||||
tags: ["test"],
|
||||
});
|
||||
|
||||
expect(result.content[0].text).toContain("saved successfully");
|
||||
expect(result.content[0].text).toContain("Test Note");
|
||||
});
|
||||
|
||||
test("should search notes", async () => {
|
||||
// Create a note first
|
||||
await callTool("note_create", {
|
||||
title: "Test Note",
|
||||
content: "This is a test note",
|
||||
tags: ["test"],
|
||||
});
|
||||
|
||||
const result = await callTool("note_search", {
|
||||
query: "Test",
|
||||
});
|
||||
|
||||
expect(result.content[0].text).toContain("Found");
|
||||
expect(result.content[0].text).toContain("Test Note");
|
||||
});
|
||||
|
||||
test("should list notes", async () => {
|
||||
// Create a note
|
||||
await callTool("note_create", {
|
||||
title: "Test Note",
|
||||
content: "This is a test note",
|
||||
});
|
||||
|
||||
const result = await callTool("note_list", {});
|
||||
|
||||
expect(result.content[0].text).toContain("Total");
|
||||
expect(result.content[0].text).toContain("Test Note");
|
||||
});
|
||||
|
||||
test("should delete note", async () => {
|
||||
// Create a note
|
||||
const createResult = await callTool("note_create", {
|
||||
title: "Test Note",
|
||||
content: "This is a test note",
|
||||
});
|
||||
|
||||
// Extract ID
|
||||
const idMatch = createResult.content[0].text.match(/ID: ([a-f0-9-]+)/);
|
||||
if (!idMatch) {
|
||||
throw new Error("Could not extract ID");
|
||||
}
|
||||
const id = idMatch[1];
|
||||
|
||||
// Delete it
|
||||
const result = await callTool("note_delete", { id });
|
||||
|
||||
expect(result.content[0].text).toContain("deleted successfully");
|
||||
});
|
||||
|
||||
test("should handle empty notes list", async () => {
|
||||
const result = await callTool("note_list", {});
|
||||
|
||||
expect(result.content[0].text).toMatch(/No notes found|Use note_create/i);
|
||||
});
|
||||
});
|
||||
92
tests/unit/tools/common/tasks.test.ts
Normal file
92
tests/unit/tools/common/tasks.test.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* @Date: 2026-01-07 09:11:15
|
||||
* @LastEditors: 陈子健
|
||||
* @LastEditTime: 2026-01-07 10:04:50
|
||||
* @FilePath: /cloud-mcp/tests/unit/tools/common/tasks.test.ts
|
||||
*/
|
||||
/**
|
||||
* Tasks tools tests
|
||||
*/
|
||||
|
||||
import { describe, test, expect, beforeEach, afterEach } from "bun:test";
|
||||
import { registerTaskTools } from "../../../../src/tools/common/tasks.js";
|
||||
import { callTool } from "../../../helpers/tool-helper.js";
|
||||
import { createTempDir } from "../../../helpers/test-utils.js";
|
||||
import { setupTestDatabase } from "../../../helpers/database-helper.js";
|
||||
|
||||
describe("Tasks Tools", () => {
|
||||
let testContext: ReturnType<typeof createTempDir>;
|
||||
let cleanupDb: () => void;
|
||||
|
||||
beforeEach(() => {
|
||||
testContext = createTempDir();
|
||||
cleanupDb = setupTestDatabase(testContext);
|
||||
registerTaskTools();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cleanupDb();
|
||||
testContext.cleanup();
|
||||
});
|
||||
|
||||
test("should add task", async () => {
|
||||
const result = await callTool("task_add", {
|
||||
title: "Test Task",
|
||||
description: "This is a test task",
|
||||
});
|
||||
|
||||
expect(result.content[0].text).toContain("added successfully");
|
||||
expect(result.content[0].text).toContain("Test Task");
|
||||
});
|
||||
|
||||
test("should list tasks", async () => {
|
||||
// Add a task first
|
||||
await callTool("task_add", {
|
||||
title: "Test Task",
|
||||
});
|
||||
|
||||
const result = await callTool("task_list", {});
|
||||
|
||||
expect(result.content[0].text).toContain("Tasks");
|
||||
expect(result.content[0].text).toContain("Test Task");
|
||||
});
|
||||
|
||||
test("should filter tasks by completion status", async () => {
|
||||
// Add a task
|
||||
await callTool("task_add", {
|
||||
title: "Test Task",
|
||||
});
|
||||
|
||||
const allTasks = await callTool("task_list", {});
|
||||
const pendingTasks = await callTool("task_list", { completed: false });
|
||||
|
||||
expect(allTasks.content[0].text).toContain("Tasks");
|
||||
expect(pendingTasks.content[0].text).toContain("Pending");
|
||||
});
|
||||
|
||||
test("should complete task", async () => {
|
||||
// Add a task
|
||||
const addResult = await callTool("task_add", {
|
||||
title: "Test Task",
|
||||
});
|
||||
|
||||
// Extract ID
|
||||
const idMatch = addResult.content[0].text.match(/ID: ([a-f0-9-]+)/);
|
||||
if (!idMatch) {
|
||||
throw new Error("Could not extract ID");
|
||||
}
|
||||
const id = idMatch[1];
|
||||
|
||||
// Complete it
|
||||
const result = await callTool("task_complete", { id });
|
||||
|
||||
expect(result.content[0].text).toContain("marked as completed");
|
||||
});
|
||||
|
||||
test("should handle empty tasks list", async () => {
|
||||
const result = await callTool("task_list", {});
|
||||
|
||||
// The message varies based on completion status filter
|
||||
expect(result.content[0].text).toMatch(/No.*tasks|Use task_add/i);
|
||||
});
|
||||
});
|
||||
71
tests/unit/tools/devops/server.test.ts
Normal file
71
tests/unit/tools/devops/server.test.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* @Date: 2026-01-07 09:11:20
|
||||
* @LastEditors: 陈子健
|
||||
* @LastEditTime: 2026-01-07 10:04:41
|
||||
* @FilePath: /cloud-mcp/tests/unit/tools/devops/server.test.ts
|
||||
*/
|
||||
/**
|
||||
* Server tools tests (with mocked SSH)
|
||||
*/
|
||||
|
||||
import { describe, test, expect, beforeEach } from "bun:test";
|
||||
import { registerServerTools } from "../../../../src/tools/devops/server.js";
|
||||
import { callTool } from "../../../helpers/tool-helper.js";
|
||||
import { setTestEnv } from "../../../helpers/test-utils.js";
|
||||
|
||||
describe("Server Tools", () => {
|
||||
let cleanupEnv: () => void;
|
||||
|
||||
beforeEach(() => {
|
||||
cleanupEnv = setTestEnv({
|
||||
SERVER_HOST: "test-server",
|
||||
SERVER_USERNAME: "test-user",
|
||||
SERVER_PORT: "22",
|
||||
SERVER_KEY_PATH: "/test/key/path",
|
||||
});
|
||||
registerServerTools();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cleanupEnv();
|
||||
});
|
||||
|
||||
test("should handle server status request", async () => {
|
||||
const result = await callTool("server_status", {});
|
||||
|
||||
// Should either return status or handle connection error gracefully
|
||||
expect(result.content[0].text).toBeDefined();
|
||||
// Since we don't have actual SSH connection, it will likely return an error
|
||||
// which is expected behavior
|
||||
}, 15000); // Longer timeout for SSH attempts
|
||||
|
||||
test("should handle server logs request", async () => {
|
||||
const result = await callTool("server_logs", {
|
||||
logPath: "/var/log/test.log",
|
||||
lines: 10,
|
||||
});
|
||||
|
||||
// Should either return logs or handle connection error gracefully
|
||||
expect(result.content[0].text).toBeDefined();
|
||||
}, 15000);
|
||||
|
||||
test("should handle deploy request", async () => {
|
||||
const result = await callTool("server_deploy", {
|
||||
localPath: "/local/path",
|
||||
remotePath: "/remote/path",
|
||||
command: "pm2 restart app",
|
||||
});
|
||||
|
||||
expect(result.content[0].text).toContain("Deployment initiated");
|
||||
});
|
||||
|
||||
test("should handle missing server configuration", async () => {
|
||||
cleanupEnv();
|
||||
cleanupEnv = setTestEnv({});
|
||||
registerServerTools();
|
||||
|
||||
const result = await callTool("server_status", {});
|
||||
|
||||
expect(result.content[0].text).toContain("configuration not found");
|
||||
});
|
||||
});
|
||||
70
tests/unit/tools/family/baby.test.ts
Normal file
70
tests/unit/tools/family/baby.test.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* Baby tools tests
|
||||
*/
|
||||
|
||||
import { describe, test, expect, beforeEach, afterEach } from "bun:test";
|
||||
import { registerBabyTools } from "../../../../src/tools/family/baby.js";
|
||||
import { callTool } from "../../../helpers/tool-helper.js";
|
||||
import { createTempDir } from "../../../helpers/test-utils.js";
|
||||
import { setupTestDatabase } from "../../../helpers/database-helper.js";
|
||||
|
||||
describe("Baby Tools", () => {
|
||||
let testContext: ReturnType<typeof createTempDir>;
|
||||
let cleanupDb: () => void;
|
||||
|
||||
beforeEach(() => {
|
||||
testContext = createTempDir();
|
||||
cleanupDb = setupTestDatabase(testContext);
|
||||
registerBabyTools();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cleanupDb();
|
||||
testContext.cleanup();
|
||||
});
|
||||
|
||||
test("should add baby milestone", async () => {
|
||||
const result = await callTool("baby_milestone_add", {
|
||||
title: "First Steps",
|
||||
description: "Baby took first steps today",
|
||||
date: "2024-01-01",
|
||||
});
|
||||
|
||||
expect(result.content[0].text).toContain("recorded successfully");
|
||||
expect(result.content[0].text).toContain("First Steps");
|
||||
});
|
||||
|
||||
test("should list baby milestones", async () => {
|
||||
// Add a milestone first
|
||||
await callTool("baby_milestone_add", {
|
||||
title: "First Steps",
|
||||
description: "Baby took first steps",
|
||||
date: "2024-01-01",
|
||||
});
|
||||
|
||||
const result = await callTool("baby_milestone_list", {});
|
||||
|
||||
expect(result.content[0].text).toContain("Total");
|
||||
expect(result.content[0].text).toContain("First Steps");
|
||||
});
|
||||
|
||||
test("should set baby reminder", async () => {
|
||||
const result = await callTool("baby_reminder_set", {
|
||||
title: "Vaccine",
|
||||
description: "DTaP vaccine due",
|
||||
date: "2024-02-01",
|
||||
type: "vaccine",
|
||||
});
|
||||
|
||||
expect(result.content[0].text).toContain("reminder set successfully");
|
||||
expect(result.content[0].text).toContain("Vaccine");
|
||||
});
|
||||
|
||||
test("should handle empty milestones list", async () => {
|
||||
const result = await callTool("baby_milestone_list", {});
|
||||
|
||||
expect(result.content[0].text).toMatch(
|
||||
/No milestones recorded|Use baby_milestone_add/i
|
||||
);
|
||||
});
|
||||
});
|
||||
109
tests/unit/tools/family/math.test.ts
Normal file
109
tests/unit/tools/family/math.test.ts
Normal file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* @Date: 2026-01-07 09:10:17
|
||||
* @LastEditors: 陈子健
|
||||
* @LastEditTime: 2026-01-07 10:04:38
|
||||
* @FilePath: /cloud-mcp/tests/unit/tools/family/math.test.ts
|
||||
*/
|
||||
/**
|
||||
* Math tools tests
|
||||
*/
|
||||
|
||||
import { describe, test, expect, beforeEach, afterEach } from "bun:test";
|
||||
import { registerMathTools } from "../../../../src/tools/family/math.js";
|
||||
import { callTool } from "../../../helpers/tool-helper.js";
|
||||
import { createTempDir } from "../../../helpers/test-utils.js";
|
||||
import { setupTestDatabase } from "../../../helpers/database-helper.js";
|
||||
|
||||
describe("Math Tools", () => {
|
||||
let testContext: ReturnType<typeof createTempDir>;
|
||||
let cleanupDb: () => void;
|
||||
|
||||
beforeEach(() => {
|
||||
testContext = createTempDir();
|
||||
cleanupDb = setupTestDatabase(testContext);
|
||||
registerMathTools();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cleanupDb();
|
||||
testContext.cleanup();
|
||||
});
|
||||
|
||||
test("should generate math problems for elementary grade", async () => {
|
||||
const result = await callTool("math_problem_generate", {
|
||||
grade: "1st",
|
||||
difficulty: "easy",
|
||||
count: 5,
|
||||
});
|
||||
|
||||
expect(result.content[0].text).toContain("Generated");
|
||||
expect(result.content[0].text).toContain("1st");
|
||||
expect(result.content[0].text).toContain("problem");
|
||||
});
|
||||
|
||||
test("should generate math problems for middle school", async () => {
|
||||
const result = await callTool("math_problem_generate", {
|
||||
grade: "middle",
|
||||
difficulty: "medium",
|
||||
topic: "algebra",
|
||||
count: 3,
|
||||
});
|
||||
|
||||
expect(result.content[0].text).toContain("Generated");
|
||||
expect(result.content[0].text).toContain("middle");
|
||||
expect(result.content[0].text).toContain("algebra");
|
||||
});
|
||||
|
||||
test("should save math resource", async () => {
|
||||
const result = await callTool("math_resource_save", {
|
||||
title: "Addition Worksheet",
|
||||
content: "1 + 1 = 2",
|
||||
grade: "1st",
|
||||
tags: ["addition"],
|
||||
});
|
||||
|
||||
expect(result.content[0].text).toContain("saved successfully");
|
||||
expect(result.content[0].text).toContain("Addition Worksheet");
|
||||
});
|
||||
|
||||
test("should search math resources", async () => {
|
||||
// First save a resource
|
||||
await callTool("math_resource_save", {
|
||||
title: "Addition Worksheet",
|
||||
content: "1 + 1 = 2",
|
||||
grade: "1st",
|
||||
tags: ["addition"],
|
||||
});
|
||||
|
||||
const result = await callTool("math_resource_search", {
|
||||
query: "Addition",
|
||||
});
|
||||
|
||||
expect(result.content[0].text).toContain("Found");
|
||||
expect(result.content[0].text).toContain("Addition Worksheet");
|
||||
});
|
||||
|
||||
test("should search math resources by grade", async () => {
|
||||
await callTool("math_resource_save", {
|
||||
title: "Addition Worksheet",
|
||||
content: "1 + 1 = 2",
|
||||
grade: "1st",
|
||||
tags: ["addition"],
|
||||
});
|
||||
|
||||
const result = await callTool("math_resource_search", {
|
||||
query: "Addition",
|
||||
grade: "1st",
|
||||
});
|
||||
|
||||
expect(result.content[0].text).toContain("Found");
|
||||
});
|
||||
|
||||
test("should handle search with no results", async () => {
|
||||
const result = await callTool("math_resource_search", {
|
||||
query: "NonExistent",
|
||||
});
|
||||
|
||||
expect(result.content[0].text).toContain("No math resources found");
|
||||
});
|
||||
});
|
||||
61
tests/unit/tools/hobbies/football.test.ts
Normal file
61
tests/unit/tools/hobbies/football.test.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* Football tools tests
|
||||
*/
|
||||
|
||||
import { describe, test, expect, beforeEach } from "bun:test";
|
||||
import { registerFootballTools } from "../../../../src/tools/hobbies/football.js";
|
||||
import { callTool } from "../../../helpers/tool-helper.js";
|
||||
import { setTestEnv } from "../../../helpers/test-utils.js";
|
||||
|
||||
describe("Football Tools", () => {
|
||||
let cleanupEnv: () => void;
|
||||
|
||||
beforeEach(() => {
|
||||
cleanupEnv = setTestEnv({
|
||||
FOOTBALL_API_KEY: "test-key",
|
||||
});
|
||||
registerFootballTools();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cleanupEnv();
|
||||
});
|
||||
|
||||
test("should get football matches", async () => {
|
||||
const result = await callTool("football_matches", {
|
||||
days: 7,
|
||||
});
|
||||
|
||||
// Should return matches or placeholder message
|
||||
expect(result.content[0].text).toBeDefined();
|
||||
expect(result.content[0].text.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
test("should get team information", async () => {
|
||||
const result = await callTool("football_team_info", {
|
||||
team: "Manchester United",
|
||||
});
|
||||
|
||||
// Should return team info or placeholder
|
||||
expect(result.content[0].text).toBeDefined();
|
||||
});
|
||||
|
||||
test("should get league standings", async () => {
|
||||
const result = await callTool("football_standings", {
|
||||
league: "Premier League",
|
||||
});
|
||||
|
||||
// Should return standings or placeholder
|
||||
expect(result.content[0].text).toBeDefined();
|
||||
});
|
||||
|
||||
test("should handle missing API key gracefully", async () => {
|
||||
cleanupEnv();
|
||||
cleanupEnv = setTestEnv({});
|
||||
registerFootballTools();
|
||||
|
||||
const result = await callTool("football_matches", {});
|
||||
|
||||
expect(result.content[0].text).toContain("FOOTBALL_API_KEY");
|
||||
});
|
||||
});
|
||||
100
tests/unit/tools/hobbies/games.test.ts
Normal file
100
tests/unit/tools/hobbies/games.test.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
/**
|
||||
* Game tools tests
|
||||
*/
|
||||
|
||||
import { describe, test, expect, beforeEach, afterEach } from "bun:test";
|
||||
import { registerGameTools } from "../../../../src/tools/hobbies/games.js";
|
||||
import { callTool } from "../../../helpers/tool-helper.js";
|
||||
import { createTempDir } from "../../../helpers/test-utils.js";
|
||||
import { setupTestDatabase } from "../../../helpers/database-helper.js";
|
||||
|
||||
describe("Game Tools", () => {
|
||||
let testContext: ReturnType<typeof createTempDir>;
|
||||
let cleanupDb: () => void;
|
||||
|
||||
beforeEach(() => {
|
||||
testContext = createTempDir();
|
||||
cleanupDb = setupTestDatabase(testContext);
|
||||
registerGameTools();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cleanupDb();
|
||||
testContext.cleanup();
|
||||
});
|
||||
|
||||
test("should get game information", async () => {
|
||||
const result = await callTool("game_info", {
|
||||
name: "Minecraft",
|
||||
});
|
||||
|
||||
// Should either return game info or handle API error gracefully
|
||||
expect(result.content[0].text).toBeDefined();
|
||||
}, 10000); // Longer timeout for API calls
|
||||
|
||||
test("should get game deals", async () => {
|
||||
const result = await callTool("game_deals", {
|
||||
platform: "steam",
|
||||
});
|
||||
|
||||
// Should either return deals or handle API error gracefully
|
||||
expect(result.content[0].text).toBeDefined();
|
||||
}, 10000);
|
||||
|
||||
test("should add game to wishlist", async () => {
|
||||
const result = await callTool("game_wishlist", {
|
||||
action: "add",
|
||||
gameName: "Test Game",
|
||||
platform: "PC",
|
||||
});
|
||||
|
||||
expect(result.content[0].text).toContain("added to wishlist");
|
||||
expect(result.content[0].text).toContain("Test Game");
|
||||
});
|
||||
|
||||
test("should list game wishlist", async () => {
|
||||
// Add a game first
|
||||
await callTool("game_wishlist", {
|
||||
action: "add",
|
||||
gameName: "Test Game",
|
||||
platform: "PC",
|
||||
});
|
||||
|
||||
const result = await callTool("game_wishlist", {
|
||||
action: "list",
|
||||
});
|
||||
|
||||
expect(result.content[0].text).toMatch(/wishlist|Test Game/i);
|
||||
});
|
||||
|
||||
test("should remove game from wishlist", async () => {
|
||||
// Add a game first
|
||||
const addResult = await callTool("game_wishlist", {
|
||||
action: "add",
|
||||
gameName: "Test Game",
|
||||
});
|
||||
|
||||
// Extract ID
|
||||
const idMatch = addResult.content[0].text.match(/ID: ([a-f0-9-]+)/);
|
||||
if (!idMatch) {
|
||||
throw new Error("Could not extract ID");
|
||||
}
|
||||
const id = idMatch[1];
|
||||
|
||||
// Remove it
|
||||
const result = await callTool("game_wishlist", {
|
||||
action: "remove",
|
||||
id,
|
||||
});
|
||||
|
||||
expect(result.content[0].text).toContain("removed from wishlist");
|
||||
});
|
||||
|
||||
test("should handle empty wishlist", async () => {
|
||||
const result = await callTool("game_wishlist", {
|
||||
action: "list",
|
||||
});
|
||||
|
||||
expect(result.content[0].text).toMatch(/empty|Use game_wishlist.*add/i);
|
||||
});
|
||||
});
|
||||
71
tests/unit/tools/programming/codeReview.test.ts
Normal file
71
tests/unit/tools/programming/codeReview.test.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* Code review tools tests
|
||||
*/
|
||||
|
||||
import { describe, test, expect, beforeEach } from "bun:test";
|
||||
import { registerCodeReviewTools } from "../../../../src/tools/programming/codeReview.js";
|
||||
import { callTool } from "../../../helpers/tool-helper.js";
|
||||
|
||||
describe("Code Review Tools", () => {
|
||||
beforeEach(() => {
|
||||
registerCodeReviewTools();
|
||||
});
|
||||
|
||||
test("should review code and find issues", async () => {
|
||||
const result = await callTool("code_review", {
|
||||
code: "let x: any = 1;",
|
||||
language: "typescript",
|
||||
});
|
||||
|
||||
expect(result.content[0].text).toContain("Code Review");
|
||||
expect(result.content[0].text).toContain("any");
|
||||
});
|
||||
|
||||
test("should suggest improvements", async () => {
|
||||
const result = await callTool("code_review", {
|
||||
code: "console.log('test');",
|
||||
language: "javascript",
|
||||
});
|
||||
|
||||
expect(result.content[0].text).toContain("Suggestions");
|
||||
expect(result.content[0].text).toContain("console.log");
|
||||
});
|
||||
|
||||
test("should detect var usage", async () => {
|
||||
const result = await callTool("code_review", {
|
||||
code: "var x = 1;",
|
||||
language: "javascript",
|
||||
});
|
||||
|
||||
expect(result.content[0].text).toContain("var");
|
||||
});
|
||||
|
||||
test("should provide optimization suggestions", async () => {
|
||||
const result = await callTool("code_optimize", {
|
||||
code: "if (x == 1) { }",
|
||||
language: "javascript",
|
||||
});
|
||||
|
||||
expect(result.content[0].text).toContain("Optimization");
|
||||
expect(result.content[0].text).toContain("===");
|
||||
});
|
||||
|
||||
test("should suggest Vue optimizations", async () => {
|
||||
const result = await callTool("code_optimize", {
|
||||
code: "<div v-for='item in items'>",
|
||||
language: "vue",
|
||||
});
|
||||
|
||||
expect(result.content[0].text).toContain("Optimization");
|
||||
expect(result.content[0].text).toContain(":key");
|
||||
});
|
||||
|
||||
test("should handle code with no issues", async () => {
|
||||
const result = await callTool("code_review", {
|
||||
code: "const x: number = 1;",
|
||||
language: "typescript",
|
||||
});
|
||||
|
||||
expect(result.content[0].text).toContain("Code Review");
|
||||
});
|
||||
});
|
||||
100
tests/unit/tools/programming/codeSnippet.test.ts
Normal file
100
tests/unit/tools/programming/codeSnippet.test.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
/**
|
||||
* Code snippet tools tests
|
||||
*/
|
||||
|
||||
import { describe, test, expect, beforeEach, afterEach } from "bun:test";
|
||||
import { registerCodeSnippetTools } from "../../../../src/tools/programming/codeSnippet.js";
|
||||
import { callTool } from "../../../helpers/tool-helper.js";
|
||||
import { createTempDir } from "../../../helpers/test-utils.js";
|
||||
import { setupTestDatabase } from "../../../helpers/database-helper.js";
|
||||
|
||||
describe("Code Snippet Tools", () => {
|
||||
let testContext: ReturnType<typeof createTempDir>;
|
||||
let cleanupDb: () => void;
|
||||
|
||||
beforeEach(() => {
|
||||
testContext = createTempDir();
|
||||
cleanupDb = setupTestDatabase(testContext);
|
||||
registerCodeSnippetTools();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cleanupDb();
|
||||
testContext.cleanup();
|
||||
});
|
||||
|
||||
test("should save code snippet", async () => {
|
||||
const result = await callTool("code_snippet_save", {
|
||||
title: "Test Snippet",
|
||||
code: "const x = 1;",
|
||||
language: "typescript",
|
||||
tags: ["test"],
|
||||
});
|
||||
|
||||
expect(result.content[0].text).toContain("saved successfully");
|
||||
expect(result.content[0].text).toContain("Test Snippet");
|
||||
});
|
||||
|
||||
test("should search code snippets", async () => {
|
||||
// First save a snippet
|
||||
await callTool("code_snippet_save", {
|
||||
title: "Test Snippet",
|
||||
code: "const x = 1;",
|
||||
language: "typescript",
|
||||
tags: ["test"],
|
||||
});
|
||||
|
||||
// Then search
|
||||
const result = await callTool("code_snippet_search", {
|
||||
query: "Test",
|
||||
});
|
||||
|
||||
expect(result.content[0].text).toContain("Found");
|
||||
expect(result.content[0].text).toContain("Test Snippet");
|
||||
});
|
||||
|
||||
test("should list code snippets", async () => {
|
||||
// Save a snippet
|
||||
await callTool("code_snippet_save", {
|
||||
title: "Test Snippet",
|
||||
code: "const x = 1;",
|
||||
language: "typescript",
|
||||
tags: ["test"],
|
||||
});
|
||||
|
||||
const result = await callTool("code_snippet_list", {});
|
||||
|
||||
expect(result.content[0].text).toContain("Total");
|
||||
expect(result.content[0].text).toContain("Test Snippet");
|
||||
});
|
||||
|
||||
test("should delete code snippet", async () => {
|
||||
// Save a snippet
|
||||
const saveResult = await callTool("code_snippet_save", {
|
||||
title: "Test Snippet",
|
||||
code: "const x = 1;",
|
||||
language: "typescript",
|
||||
tags: ["test"],
|
||||
});
|
||||
|
||||
// Extract ID from save result
|
||||
const idMatch = saveResult.content[0].text.match(/ID: ([a-f0-9-]+)/);
|
||||
if (!idMatch) {
|
||||
throw new Error("Could not extract ID from save result");
|
||||
}
|
||||
const id = idMatch[1];
|
||||
|
||||
// Delete it
|
||||
const result = await callTool("code_snippet_delete", { id });
|
||||
|
||||
expect(result.content[0].text).toContain("deleted successfully");
|
||||
});
|
||||
|
||||
test("should handle search with no results", async () => {
|
||||
const result = await callTool("code_snippet_search", {
|
||||
query: "NonExistent",
|
||||
});
|
||||
|
||||
expect(result.content[0].text).toContain("No code snippets found");
|
||||
});
|
||||
});
|
||||
61
tests/unit/tools/programming/docs.test.ts
Normal file
61
tests/unit/tools/programming/docs.test.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* Documentation tools tests
|
||||
*/
|
||||
|
||||
import { describe, test, expect, beforeEach } from "bun:test";
|
||||
import { registerDocsTools } from "../../../../src/tools/programming/docs.js";
|
||||
import { callTool } from "../../../helpers/tool-helper.js";
|
||||
|
||||
describe("Documentation Tools", () => {
|
||||
beforeEach(() => {
|
||||
registerDocsTools();
|
||||
});
|
||||
|
||||
test("should get TypeScript documentation", async () => {
|
||||
const result = await callTool("docs_typescript", {});
|
||||
|
||||
expect(result.content[0].text).toContain("TypeScript Documentation");
|
||||
expect(result.content[0].text).toContain("typescriptlang.org");
|
||||
});
|
||||
|
||||
test("should get TypeScript documentation with topic", async () => {
|
||||
const result = await callTool("docs_typescript", {
|
||||
topic: "generics",
|
||||
});
|
||||
|
||||
expect(result.content[0].text).toContain("TypeScript Documentation");
|
||||
expect(result.content[0].text).toContain("generics");
|
||||
});
|
||||
|
||||
test("should get Vue3 documentation", async () => {
|
||||
const result = await callTool("docs_vue3", {});
|
||||
|
||||
expect(result.content[0].text).toContain("Vue 3 Documentation");
|
||||
expect(result.content[0].text).toContain("vuejs.org");
|
||||
});
|
||||
|
||||
test("should get Vue3 documentation with topic", async () => {
|
||||
const result = await callTool("docs_vue3", {
|
||||
topic: "composition",
|
||||
});
|
||||
|
||||
expect(result.content[0].text).toContain("Vue 3 Documentation");
|
||||
expect(result.content[0].text).toContain("composition");
|
||||
});
|
||||
|
||||
test("should get Bun documentation", async () => {
|
||||
const result = await callTool("docs_bun", {});
|
||||
|
||||
expect(result.content[0].text).toContain("Bun Documentation");
|
||||
expect(result.content[0].text).toContain("bun.sh");
|
||||
});
|
||||
|
||||
test("should get Bun documentation with topic", async () => {
|
||||
const result = await callTool("docs_bun", {
|
||||
topic: "runtime",
|
||||
});
|
||||
|
||||
expect(result.content[0].text).toContain("Bun Documentation");
|
||||
expect(result.content[0].text).toContain("runtime");
|
||||
});
|
||||
});
|
||||
98
tests/unit/tools/programming/projectTemplate.test.ts
Normal file
98
tests/unit/tools/programming/projectTemplate.test.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
/**
|
||||
* Project template tools tests
|
||||
*/
|
||||
|
||||
import { describe, test, expect, beforeEach, afterEach } from "bun:test";
|
||||
import { registerProjectTemplateTools } from "../../../../src/tools/programming/projectTemplate.js";
|
||||
import { callTool } from "../../../helpers/tool-helper.js";
|
||||
import { createTempDir } from "../../../helpers/test-utils.js";
|
||||
import { existsSync, readFileSync } from "fs";
|
||||
import { join } from "path";
|
||||
|
||||
describe("Project Template Tools", () => {
|
||||
let testContext: ReturnType<typeof createTempDir>;
|
||||
|
||||
beforeEach(() => {
|
||||
testContext = createTempDir();
|
||||
registerProjectTemplateTools();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
testContext.cleanup();
|
||||
});
|
||||
|
||||
test("should create Vite + Vue3 project", async () => {
|
||||
const projectName = "test-vue-project";
|
||||
const projectPath = join(testContext.tempDir, projectName);
|
||||
|
||||
const result = await callTool("project_template_create", {
|
||||
name: projectName,
|
||||
path: testContext.tempDir,
|
||||
});
|
||||
|
||||
expect(result.content[0].text).toContain("created successfully");
|
||||
expect(existsSync(join(projectPath, "package.json"))).toBe(true);
|
||||
expect(existsSync(join(projectPath, "vite.config.ts"))).toBe(true);
|
||||
expect(existsSync(join(projectPath, "src", "main.ts"))).toBe(true);
|
||||
expect(existsSync(join(projectPath, "src", "App.vue"))).toBe(true);
|
||||
});
|
||||
|
||||
test("should create project with Pinia", async () => {
|
||||
const projectName = "test-vue-pinia";
|
||||
const projectPath = join(testContext.tempDir, projectName);
|
||||
|
||||
await callTool("project_template_create", {
|
||||
name: projectName,
|
||||
path: testContext.tempDir,
|
||||
usePinia: true,
|
||||
});
|
||||
|
||||
const packageJson = JSON.parse(
|
||||
readFileSync(join(projectPath, "package.json"), "utf-8")
|
||||
);
|
||||
expect(packageJson.dependencies.pinia).toBeDefined();
|
||||
});
|
||||
|
||||
test("should create fullstack project", async () => {
|
||||
const projectName = "test-fullstack";
|
||||
const projectPath = join(testContext.tempDir, projectName);
|
||||
|
||||
const result = await callTool("project_template_create_fullstack", {
|
||||
name: projectName,
|
||||
path: testContext.tempDir,
|
||||
});
|
||||
|
||||
expect(result.content[0].text).toContain("created successfully");
|
||||
expect(existsSync(join(projectPath, "frontend", "package.json"))).toBe(
|
||||
true
|
||||
);
|
||||
expect(existsSync(join(projectPath, "backend", "package.json"))).toBe(true);
|
||||
expect(existsSync(join(projectPath, "backend", "src", "index.ts"))).toBe(
|
||||
true
|
||||
);
|
||||
});
|
||||
|
||||
test("should list available templates", async () => {
|
||||
const result = await callTool("project_template_list", {});
|
||||
|
||||
expect(result.content[0].text).toContain("Available project templates");
|
||||
expect(result.content[0].text).toContain("Vite + Vue3");
|
||||
expect(result.content[0].text).toContain("Fullstack");
|
||||
});
|
||||
|
||||
test("should handle existing directory error", async () => {
|
||||
const projectName = "existing-project";
|
||||
const projectPath = join(testContext.tempDir, projectName);
|
||||
|
||||
// Create directory first
|
||||
const { mkdirSync } = await import("fs");
|
||||
mkdirSync(projectPath, { recursive: true });
|
||||
|
||||
const result = await callTool("project_template_create", {
|
||||
name: projectName,
|
||||
path: testContext.tempDir,
|
||||
});
|
||||
|
||||
expect(result.content[0].text).toContain("already exists");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user