feat: Add testing framework and initial test cases for various tools and database operations
This commit is contained in:
@@ -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(
|
||||
{
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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 });
|
||||
|
||||
|
||||
Reference in New Issue
Block a user