Skip to main content
Glama

Scratchpad MCP

by pc035860
database.test.ts11.1 kB
/** * Basic database functionality tests */ import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import { existsSync, unlinkSync } from 'fs'; import { ScratchpadDatabase } from '../src/database/index.js'; describe('ScratchpadDatabase', () => { let db: ScratchpadDatabase; const testDbPath = ':memory:'; // Use in-memory database for tests beforeEach(() => { db = new ScratchpadDatabase({ filename: testDbPath }); }); afterEach(() => { db.close(); }); describe('Workflow Management', () => { it('should create and retrieve a workflow', () => { const workflow = db.createWorkflow({ name: 'Test Workflow', description: 'A test workflow', }); expect(workflow.id).toBeDefined(); expect(workflow.name).toBe('Test Workflow'); expect(workflow.description).toBe('A test workflow'); expect(workflow.scratchpad_count).toBe(0); const retrieved = db.getWorkflowById(workflow.id); expect(retrieved).toEqual(workflow); }); it('should list workflows', () => { const w1 = db.createWorkflow({ name: 'Workflow 1' }); // Small delay to ensure different timestamps const w2 = db.createWorkflow({ name: 'Workflow 2' }); const workflows = db.getWorkflows(); expect(workflows).toHaveLength(2); const workflowNames = workflows.map(w => w.name); expect(workflowNames).toContain('Workflow 1'); expect(workflowNames).toContain('Workflow 2'); }); }); describe('Scratchpad Management', () => { it('should create and retrieve a scratchpad', () => { const workflow = db.createWorkflow({ name: 'Test Workflow' }); const scratchpad = db.createScratchpad({ workflow_id: workflow.id, title: 'Test Scratchpad', content: 'This is test content', }); expect(scratchpad.id).toBeDefined(); expect(scratchpad.workflow_id).toBe(workflow.id); expect(scratchpad.title).toBe('Test Scratchpad'); expect(scratchpad.content).toBe('This is test content'); const retrieved = db.getScratchpadById(scratchpad.id); expect(retrieved).toEqual(scratchpad); }); it('should append to scratchpad', () => { const workflow = db.createWorkflow({ name: 'Test Workflow' }); const scratchpad = db.createScratchpad({ workflow_id: workflow.id, title: 'Test Scratchpad', content: 'Initial content', }); const updated = db.appendToScratchpad({ id: scratchpad.id, content: '\nAppended content', }); expect(updated.content).toBe('Initial content\n\n---\n<!--- block start --->\n\nAppended content'); expect(updated.updated_at).toBeGreaterThanOrEqual(scratchpad.updated_at); }); it('should list scratchpads by workflow', () => { const workflow = db.createWorkflow({ name: 'Test Workflow' }); const s1 = db.createScratchpad({ workflow_id: workflow.id, title: 'Scratchpad 1', content: 'Content 1', }); const s2 = db.createScratchpad({ workflow_id: workflow.id, title: 'Scratchpad 2', content: 'Content 2', }); const scratchpads = db.listScratchpads({ workflow_id: workflow.id }); expect(scratchpads).toHaveLength(2); const titles = scratchpads.map(s => s.title); expect(titles).toContain('Scratchpad 1'); expect(titles).toContain('Scratchpad 2'); }); }); describe('Search Functionality', () => { it('should search scratchpads by content', () => { const workflow = db.createWorkflow({ name: 'Test Workflow' }); db.createScratchpad({ workflow_id: workflow.id, title: 'JavaScript Notes', content: 'This is about JavaScript programming', }); db.createScratchpad({ workflow_id: workflow.id, title: 'Python Notes', content: 'This is about Python programming', }); const jsResults = db.searchScratchpads({ query: 'JavaScript' }); expect(jsResults).toHaveLength(1); expect(jsResults[0]?.scratchpad.title).toBe('JavaScript Notes'); const programmingResults = db.searchScratchpads({ query: 'programming' }); expect(programmingResults).toHaveLength(2); }); it('should handle hyphenated search terms correctly', () => { const workflow = db.createWorkflow({ name: 'Test Workflow' }); // 測試連字號搜尋 - 這是錯誤1的修復測試 db.createScratchpad({ workflow_id: workflow.id, title: 'Claude-MD 專案筆記', content: 'This project uses Claude-MD configuration system', }); db.createScratchpad({ workflow_id: workflow.id, title: 'Regular Notes', content: 'This is a regular note without special characters', }); // 測試帶連字號的搜尋(之前會導致 "no such column: MD" 錯誤) const hyphenResults = db.searchScratchpads({ query: 'Claude-MD' }); expect(hyphenResults).toHaveLength(1); expect(hyphenResults[0]?.scratchpad.title).toBe('Claude-MD 專案筆記'); }); it('should handle special characters in search queries', () => { const workflow = db.createWorkflow({ name: 'Test Workflow' }); // 測試各種特殊字符的搜尋 db.createScratchpad({ workflow_id: workflow.id, title: 'Special Chars: "quotes" (parentheses) *asterisks*', content: 'Content with various special characters', }); // 這些搜尋之前可能會導致 FTS5 語法錯誤 const quoteResults = db.searchScratchpads({ query: '"quotes"' }); expect(quoteResults.length).toBeGreaterThanOrEqual(0); // 至少不應該報錯 const parenResults = db.searchScratchpads({ query: '(parentheses)' }); expect(parenResults.length).toBeGreaterThanOrEqual(0); const asteriskResults = db.searchScratchpads({ query: '*asterisks*' }); expect(asteriskResults.length).toBeGreaterThanOrEqual(0); }); }); describe('Error Handling', () => { it('should throw error for non-existent workflow', () => { expect(() => { db.createScratchpad({ workflow_id: 'non-existent-id', title: 'Test', content: 'Content', }); }).toThrow('Workflow not found'); }); it('should throw error for oversized content', () => { const workflow = db.createWorkflow({ name: 'Test Workflow' }); const largeContent = 'x'.repeat(2 * 1024 * 1024); // 2MB content expect(() => { db.createScratchpad({ workflow_id: workflow.id, title: 'Large Scratchpad', content: largeContent, }); }).toThrow('Scratchpad content too large'); }); it('should throw error for non-existent scratchpad append', () => { expect(() => { db.appendToScratchpad({ id: 'non-existent-id', content: 'Additional content', }); }).toThrow('Scratchpad not found'); }); }); describe('Append Operation Stability', () => { it('should successfully append content multiple times', () => { const workflow = db.createWorkflow({ name: 'Test Workflow' }); const scratchpad = db.createScratchpad({ workflow_id: workflow.id, title: 'Append Test', content: 'Initial content', }); // 多次 append 操作,測試穩定性(錯誤2的修復驗證) const append1 = db.appendToScratchpad({ id: scratchpad.id, content: '\nFirst append', }); expect(append1.content).toBe('Initial content\n\n---\n<!--- block start --->\n\nFirst append'); const append2 = db.appendToScratchpad({ id: append1.id, content: '\nSecond append', }); expect(append2.content).toBe('Initial content\n\n---\n<!--- block start --->\n\nFirst append\n\n---\n<!--- block start --->\n\nSecond append'); const append3 = db.appendToScratchpad({ id: append2.id, content: '\nThird append', }); expect(append3.content).toBe('Initial content\n\n---\n<!--- block start --->\n\nFirst append\n\n---\n<!--- block start --->\n\nSecond append\n\n---\n<!--- block start --->\n\nThird append'); // 驗證更新時間有正確遞增 expect(append3.updated_at).toBeGreaterThanOrEqual(append2.updated_at); expect(append2.updated_at).toBeGreaterThanOrEqual(append1.updated_at); }); it('should handle append with special characters', () => { const workflow = db.createWorkflow({ name: 'Test Workflow' }); const scratchpad = db.createScratchpad({ workflow_id: workflow.id, title: 'Special Chars Test', content: 'Initial content', }); // 測試包含特殊字符的 append(這些字符之前可能導致 FTS5 問題) const updated = db.appendToScratchpad({ id: scratchpad.id, content: '\n\n## 🌸 Hanabi 的測試\n這是一個包含特殊字符的測試:Claude-MD、"引號"、(括號)、*星號*', }); expect(updated.content).toContain('🌸 Hanabi'); expect(updated.content).toContain('Claude-MD'); expect(updated.content).toContain('"引號"'); expect(updated.content).toContain('(括號)'); expect(updated.content).toContain('*星號*'); }); it('should maintain searchability after append operations', () => { const workflow = db.createWorkflow({ name: 'Test Workflow' }); const scratchpad = db.createScratchpad({ workflow_id: workflow.id, title: 'Search Test', content: 'Original searchable content', }); // Append 後應該仍然可以搜尋到 db.appendToScratchpad({ id: scratchpad.id, content: '\nAdded Claude-MD configuration', }); // 測試搜尋原始內容 const originalResults = db.searchScratchpads({ query: 'searchable' }); expect(originalResults).toHaveLength(1); expect(originalResults[0]?.scratchpad.id).toBe(scratchpad.id); // 測試搜尋新增的內容(包含連字號) const appendedResults = db.searchScratchpads({ query: 'Claude-MD' }); expect(appendedResults).toHaveLength(1); expect(appendedResults[0]?.scratchpad.id).toBe(scratchpad.id); expect(appendedResults[0]?.scratchpad.content).toContain('Claude-MD'); }); }); describe('Database Statistics', () => { it('should return correct statistics', () => { const initialStats = db.getStats(); expect(initialStats.totalWorkflows).toBe(0); expect(initialStats.totalScratchpads).toBe(0); expect(typeof initialStats.hasFTS5).toBe('boolean'); const workflow = db.createWorkflow({ name: 'Test Workflow' }); db.createScratchpad({ workflow_id: workflow.id, title: 'Test Scratchpad', content: 'Content', }); const updatedStats = db.getStats(); expect(updatedStats.totalWorkflows).toBe(1); expect(updatedStats.totalScratchpads).toBe(1); }); }); });

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/pc035860/scratchpad-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server