Skip to main content
Glama
YUChoe

SQLite MCP Server

by YUChoe
MCPServer.test.ts8.13 kB
/** * MCP 서버 Property-Based Testing * MCP SDK 의존성 없이 핵심 로직만 테스트 */ import { describe, test, expect, beforeEach, afterEach } from '@jest/globals'; import * as fc from 'fast-check'; import { DatabaseManager } from '../database/DatabaseManager'; import { ErrorHandler } from './ErrorHandler'; import fs from 'fs'; import path from 'path'; describe('MCP Server Property Tests', () => { let testDbDir: string; beforeEach(() => { testDbDir = path.join(__dirname, '../../test-dbs'); if (!fs.existsSync(testDbDir)) { fs.mkdirSync(testDbDir, { recursive: true }); } }); afterEach(() => { // 테스트 데이터베이스 정리 try { if (fs.existsSync(testDbDir)) { const files = fs.readdirSync(testDbDir); for (const file of files) { if (file.startsWith('test-mcp-')) { try { fs.unlinkSync(path.join(testDbDir, file)); } catch (error) { // Windows에서 파일 잠금으로 인한 삭제 실패 무시 } } } } } catch (error) { // 정리 실패 무시 } }); /** * **Feature: sqlite-mcp-server, Property 10: MCP 프로토콜 준수** * For any MCP 클라이언트 연결에 대해, 표준 MCP 핸드셰이크가 수행되고 모든 CRUD 도구가 노출되어야 한다 * Validates: Requirements 8.1, 8.2, 8.4 */ test('**Feature: sqlite-mcp-server, Property 10: MCP 프로토콜 준수**', async () => { await fc.assert(fc.asyncProperty( fc.record({ serverName: fc.string({ minLength: 1, maxLength: 50 }), version: fc.string({ minLength: 1, maxLength: 20 }) }), async (testCase) => { // MCP 서버 설정 검증 const config = { name: testCase.serverName, version: testCase.version, capabilities: { tools: { listChanged: true } } }; // 설정이 올바르게 생성되어야 함 expect(config.name).toBe(testCase.serverName); expect(config.version).toBe(testCase.version); expect(config.capabilities.tools.listChanged).toBe(true); // 모든 CRUD 도구가 정의되어야 함 const expectedTools = [ 'create_table', 'insert_data', 'select_data', 'get_schema', 'update_data', 'delete_data', 'meta_commands' ]; // 도구 목록 검증 expect(expectedTools.length).toBe(7); expect(expectedTools).toContain('create_table'); expect(expectedTools).toContain('insert_data'); expect(expectedTools).toContain('select_data'); expect(expectedTools).toContain('get_schema'); expect(expectedTools).toContain('update_data'); expect(expectedTools).toContain('delete_data'); expect(expectedTools).toContain('meta_commands'); } ), { numRuns: 100 }); }); /** * **Feature: sqlite-mcp-server, Property 11: 오류 처리 일관성** * For any 잘못된 입력(SQL 구문, 컬럼명, 테이블명, MCP 메시지)에 대해, 적절한 오류 메시지가 반환되어야 한다 * Validates: Requirements 2.3, 3.3, 4.4, 6.3, 8.3 */ test('**Feature: sqlite-mcp-server, Property 11: 오류 처리 일관성**', async () => { await fc.assert(fc.asyncProperty( fc.record({ invalidToolName: fc.string().filter(s => !['create_table', 'insert_data', 'select_data', 'get_schema', 'update_data', 'delete_data', 'meta_commands'].includes(s)), invalidDbPath: fc.oneof( fc.constant(''), fc.constant('/invalid/path/that/does/not/exist'), fc.constant('\\invalid\\windows\\path'), fc.string().filter(s => s.includes('\0') || s.includes('<') || s.includes('>')) ), invalidSQL: fc.oneof( fc.constant('INVALID SQL SYNTAX'), fc.constant('SELECT * FROM nonexistent_table'), fc.constant('UPDATE nonexistent_table SET col = 1'), fc.constant('DELETE FROM nonexistent_table') ) }), async (testCase) => { const dbPath = path.join(testDbDir, `test-mcp-${Date.now()}-${Math.random()}.db`); try { // 1. 잘못된 도구 이름은 오류를 발생시켜야 함 expect(testCase.invalidToolName).not.toMatch(/^(create_table|insert_data|select_data|get_schema|update_data|delete_data|meta_commands)$/); // 2. 잘못된 데이터베이스 경로에 대한 오류 처리 const dbManager = new DatabaseManager(); if (testCase.invalidDbPath === '' || testCase.invalidDbPath.includes('\0') || testCase.invalidDbPath.includes('<')) { // 빈 경로나 잘못된 문자가 포함된 경로는 오류를 발생시켜야 함 expect(() => { dbManager.getDatabase(testCase.invalidDbPath); }).toThrow(); } // 3. 잘못된 SQL에 대한 오류 처리 if (testCase.invalidSQL.includes('INVALID') || testCase.invalidSQL.includes('nonexistent_table')) { try { await dbManager.executeQuery(dbPath, testCase.invalidSQL); // 잘못된 SQL은 오류를 발생시켜야 하므로 여기에 도달하면 안됨 expect(false).toBe(true); } catch (error) { // 예상된 오류 - 오류 메시지가 있어야 함 expect(error).toBeDefined(); expect(error instanceof Error ? error.message : String(error)).toBeTruthy(); } } // 4. 오류 처리기 테스트 const testError = new Error('Test error'); const errorResponse = ErrorHandler.handleGenericError(testError, { tool: testCase.invalidToolName, path: testCase.invalidDbPath, query: testCase.invalidSQL }); // 오류 응답이 올바른 형식이어야 함 expect(errorResponse).toBeDefined(); expect(errorResponse.content).toBeDefined(); expect(errorResponse.isError).toBe(true); } finally { // 정리 try { if (fs.existsSync(dbPath)) { fs.unlinkSync(dbPath); } } catch (error) { // Windows 파일 잠금으로 인한 삭제 실패 무시 } } } ), { numRuns: 100 }); }); test('MCP 서버 기본 설정 테스트', () => { // 기본 설정 검증 const defaultConfig = { name: 'sqlite-mcp-server', version: '1.0.0', capabilities: { tools: { listChanged: true } } }; expect(defaultConfig.name).toBe('sqlite-mcp-server'); expect(defaultConfig.version).toBe('1.0.0'); expect(defaultConfig.capabilities.tools.listChanged).toBe(true); }); test('오류 처리 시스템 테스트', () => { // 데이터베이스 오류 처리 테스트 const dbError = new Error('database is locked'); const handledDbError = ErrorHandler.handleDatabaseError(dbError, '/test/path'); expect(handledDbError.type).toBeDefined(); expect(handledDbError.message).toBeTruthy(); expect(handledDbError.path).toBe('/test/path'); // SQL 오류 처리 테스트 const sqlError = new Error('syntax error near "INVALID"'); const handledSqlError = ErrorHandler.handleSQLError(sqlError, 'INVALID SQL'); expect(handledSqlError.type).toBeDefined(); expect(handledSqlError.message).toBeTruthy(); expect(handledSqlError.query).toBe('INVALID SQL'); // MCP 오류 처리 테스트 const mcpError = new Error('tool not found'); const handledMcpError = ErrorHandler.handleMCPError(mcpError); expect(handledMcpError.type).toBeDefined(); expect(handledMcpError.message).toBeTruthy(); expect(handledMcpError.code).toBeDefined(); }); });

Latest Blog Posts

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/YUChoe/sqlite-mcp'

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