feat: Add testing framework and initial test cases for various tools and database operations

This commit is contained in:
ethan.chen
2026-01-07 10:05:04 +08:00
parent 372b52b214
commit 47ecc40186
23 changed files with 1781 additions and 40 deletions

View File

@@ -19,6 +19,11 @@ class MCPServer {
private server: Server;
private tools: Map<string, { tool: Tool; handler: ToolHandler }> = new Map();
// Expose tools for testing
getTools(): Map<string, { tool: Tool; handler: ToolHandler }> {
return this.tools;
}
constructor() {
this.server = new Server(
{

View File

@@ -3,10 +3,18 @@
* Uses JSON file storage for simplicity
*/
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
import { join } from 'path';
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
import { join } from "path";
const DATA_DIR = join(process.cwd(), 'data');
// Use environment variable for test data directory, or default to project data directory
const getDataDir = () => {
if (process.env.MCP_TEST_DATA_DIR) {
return process.env.MCP_TEST_DATA_DIR;
}
return join(process.cwd(), "data");
};
const DATA_DIR = getDataDir();
export interface CodeSnippet {
id: string;
@@ -64,15 +72,20 @@ export interface GameWishlist {
}
class Database {
private getDataDir(): string {
return getDataDir();
}
private ensureDataDir(): void {
if (!existsSync(DATA_DIR)) {
mkdirSync(DATA_DIR, { recursive: true });
const dataDir = this.getDataDir();
if (!existsSync(dataDir)) {
mkdirSync(dataDir, { recursive: true });
}
}
private getFilePath(collection: string): string {
this.ensureDataDir();
return join(DATA_DIR, `${collection}.json`);
return join(this.getDataDir(), `${collection}.json`);
}
private readCollection<T>(collection: string): T[] {
@@ -81,7 +94,7 @@ class Database {
return [];
}
try {
const content = readFileSync(filePath, 'utf-8');
const content = readFileSync(filePath, "utf-8");
return JSON.parse(content);
} catch (error) {
console.error(`Error reading ${collection}:`, error);
@@ -92,7 +105,7 @@ class Database {
private writeCollection<T>(collection: string, data: T[]): void {
const filePath = this.getFilePath(collection);
try {
writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf-8');
writeFileSync(filePath, JSON.stringify(data, null, 2), "utf-8");
} catch (error) {
console.error(`Error writing ${collection}:`, error);
throw error;
@@ -101,37 +114,37 @@ class Database {
// Code Snippets
saveCodeSnippet(snippet: CodeSnippet): void {
const snippets = this.readCollection<CodeSnippet>('codeSnippets');
const snippets = this.readCollection<CodeSnippet>("codeSnippets");
const index = snippets.findIndex((s) => s.id === snippet.id);
if (index >= 0) {
snippets[index] = { ...snippet, updatedAt: new Date().toISOString() };
} else {
snippets.push(snippet);
}
this.writeCollection('codeSnippets', snippets);
this.writeCollection("codeSnippets", snippets);
}
getCodeSnippets(): CodeSnippet[] {
return this.readCollection<CodeSnippet>('codeSnippets');
return this.readCollection<CodeSnippet>("codeSnippets");
}
getCodeSnippet(id: string): CodeSnippet | undefined {
const snippets = this.readCollection<CodeSnippet>('codeSnippets');
const snippets = this.readCollection<CodeSnippet>("codeSnippets");
return snippets.find((s) => s.id === id);
}
deleteCodeSnippet(id: string): boolean {
const snippets = this.readCollection<CodeSnippet>('codeSnippets');
const snippets = this.readCollection<CodeSnippet>("codeSnippets");
const filtered = snippets.filter((s) => s.id !== id);
if (filtered.length < snippets.length) {
this.writeCollection('codeSnippets', filtered);
this.writeCollection("codeSnippets", filtered);
return true;
}
return false;
}
searchCodeSnippets(query: string, tags?: string[]): CodeSnippet[] {
const snippets = this.readCollection<CodeSnippet>('codeSnippets');
const snippets = this.readCollection<CodeSnippet>("codeSnippets");
const lowerQuery = query.toLowerCase();
return snippets.filter((s) => {
const matchesQuery =
@@ -146,27 +159,27 @@ class Database {
// Notes
saveNote(note: Note): void {
const notes = this.readCollection<Note>('notes');
const notes = this.readCollection<Note>("notes");
const index = notes.findIndex((n) => n.id === note.id);
if (index >= 0) {
notes[index] = { ...note, updatedAt: new Date().toISOString() };
} else {
notes.push(note);
}
this.writeCollection('notes', notes);
this.writeCollection("notes", notes);
}
getNotes(): Note[] {
return this.readCollection<Note>('notes');
return this.readCollection<Note>("notes");
}
getNote(id: string): Note | undefined {
const notes = this.readCollection<Note>('notes');
const notes = this.readCollection<Note>("notes");
return notes.find((n) => n.id === id);
}
searchNotes(query: string): Note[] {
const notes = this.readCollection<Note>('notes');
const notes = this.readCollection<Note>("notes");
const lowerQuery = query.toLowerCase();
return notes.filter(
(n) =>
@@ -177,10 +190,10 @@ class Database {
}
deleteNote(id: string): boolean {
const notes = this.readCollection<Note>('notes');
const notes = this.readCollection<Note>("notes");
const filtered = notes.filter((n) => n.id !== id);
if (filtered.length < notes.length) {
this.writeCollection('notes', filtered);
this.writeCollection("notes", filtered);
return true;
}
return false;
@@ -188,18 +201,18 @@ class Database {
// Tasks
saveTask(task: Task): void {
const tasks = this.readCollection<Task>('tasks');
const tasks = this.readCollection<Task>("tasks");
const index = tasks.findIndex((t) => t.id === task.id);
if (index >= 0) {
tasks[index] = task;
} else {
tasks.push(task);
}
this.writeCollection('tasks', tasks);
this.writeCollection("tasks", tasks);
}
getTasks(completed?: boolean): Task[] {
const tasks = this.readCollection<Task>('tasks');
const tasks = this.readCollection<Task>("tasks");
if (completed === undefined) {
return tasks;
}
@@ -207,39 +220,39 @@ class Database {
}
getTask(id: string): Task | undefined {
const tasks = this.readCollection<Task>('tasks');
const tasks = this.readCollection<Task>("tasks");
return tasks.find((t) => t.id === id);
}
// Baby Milestones
saveBabyMilestone(milestone: BabyMilestone): void {
const milestones = this.readCollection<BabyMilestone>('babyMilestones');
const milestones = this.readCollection<BabyMilestone>("babyMilestones");
milestones.push(milestone);
this.writeCollection('babyMilestones', milestones);
this.writeCollection("babyMilestones", milestones);
}
getBabyMilestones(): BabyMilestone[] {
return this.readCollection<BabyMilestone>('babyMilestones');
return this.readCollection<BabyMilestone>("babyMilestones");
}
// Math Resources
saveMathResource(resource: MathResource): void {
const resources = this.readCollection<MathResource>('mathResources');
const resources = this.readCollection<MathResource>("mathResources");
const index = resources.findIndex((r) => r.id === resource.id);
if (index >= 0) {
resources[index] = resource;
} else {
resources.push(resource);
}
this.writeCollection('mathResources', resources);
this.writeCollection("mathResources", resources);
}
getMathResources(): MathResource[] {
return this.readCollection<MathResource>('mathResources');
return this.readCollection<MathResource>("mathResources");
}
searchMathResources(query: string, grade?: string): MathResource[] {
const resources = this.readCollection<MathResource>('mathResources');
const resources = this.readCollection<MathResource>("mathResources");
const lowerQuery = query.toLowerCase();
return resources.filter((r) => {
const matchesQuery =
@@ -253,25 +266,25 @@ class Database {
// Game Wishlist
saveGameWishlist(game: GameWishlist): void {
const games = this.readCollection<GameWishlist>('gameWishlist');
const games = this.readCollection<GameWishlist>("gameWishlist");
const index = games.findIndex((g) => g.id === game.id);
if (index >= 0) {
games[index] = game;
} else {
games.push(game);
}
this.writeCollection('gameWishlist', games);
this.writeCollection("gameWishlist", games);
}
getGameWishlist(): GameWishlist[] {
return this.readCollection<GameWishlist>('gameWishlist');
return this.readCollection<GameWishlist>("gameWishlist");
}
deleteGameWishlist(id: string): boolean {
const games = this.readCollection<GameWishlist>('gameWishlist');
const games = this.readCollection<GameWishlist>("gameWishlist");
const filtered = games.filter((g) => g.id !== id);
if (filtered.length < games.length) {
this.writeCollection('gameWishlist', filtered);
this.writeCollection("gameWishlist", filtered);
return true;
}
return false;
@@ -279,4 +292,3 @@ class Database {
}
export const database = new Database();

View File

@@ -1,3 +1,9 @@
/*
* @Date: 2026-01-06 15:03:24
* @LastEditors: 陈子健
* @LastEditTime: 2026-01-07 10:04:47
* @FilePath: /cloud-mcp/src/tools/programming/projectTemplate.ts
*/
/**
* Project template generation tools
*/
@@ -323,6 +329,7 @@ bun run build
try {
mkdirSync(projectPath, { recursive: true });
mkdirSync(join(projectPath, "frontend"), { recursive: true });
mkdirSync(join(projectPath, "frontend", "src"), { recursive: true });
mkdirSync(join(projectPath, "backend"), { recursive: true });
mkdirSync(join(projectPath, "backend", "src"), { recursive: true });