Skip to main content
Glama
YUChoe

SQLite MCP Server

by YUChoe
selectData.test.ts9.22 kB
/** * SELECT 쿼리 도구 테스트 * **Feature: sqlite-mcp-server, Property 5: SELECT 쿼리 결과 형식** * **Validates: Requirements 3.1, 3.2** */ import { describe, test, expect, beforeEach, afterEach } from '@jest/globals'; import * as fc from 'fast-check'; import * as fs from 'fs'; import * as path from 'path'; import { selectDataTool } from './selectData'; import { createTableTool } from './createTable'; import { insertDataTool } from './insertData'; import { DatabaseManager } from '../database/DatabaseManager'; describe('SELECT Data Tool Tests', () => { let testDbPath: string; let dbManager: DatabaseManager; beforeEach(() => { // 테스트용 임시 데이터베이스 파일 경로 생성 testDbPath = path.join(__dirname, '../../test-dbs', `test-select-${Date.now()}.db`); // 테스트 디렉토리 생성 const testDir = path.dirname(testDbPath); if (!fs.existsSync(testDir)) { fs.mkdirSync(testDir, { recursive: true }); } dbManager = new DatabaseManager(); }); afterEach(() => { // 테스트 후 정리 try { dbManager.closeDatabase(testDbPath); if (fs.existsSync(testDbPath)) { fs.unlinkSync(testDbPath); } } catch (error) { // 정리 중 오류는 무시 } }); test('**Feature: sqlite-mcp-server, Property 5: SELECT 쿼리 결과 형식**', async () => { await fc.assert( fc.asyncProperty( // 테이블 이름 생성기 fc.string({ minLength: 1, maxLength: 20 }) .filter(s => /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(s)), // 테스트 데이터 생성기 fc.array( fc.record({ id: fc.integer({ min: 1, max: 1000 }), name: fc.string({ minLength: 1, maxLength: 50 }), value: fc.integer({ min: 0, max: 100 }) }), { minLength: 1, maxLength: 10 } ), // WHERE 조건 생성기 (선택적) fc.option( fc.record({ column: fc.constantFrom('id', 'name', 'value'), operator: fc.constantFrom('=', '>', '<', '>=', '<='), value: fc.oneof( fc.integer({ min: 1, max: 1000 }), fc.string({ minLength: 1, maxLength: 50 }) ) }), { freq: 7 } // 70% 확률로 WHERE 조건 포함 ), async (tableName, testData, whereCondition) => { try { // 1. 테이블 생성 const createResult = await (createTableTool.handler as any)({ dbPath: testDbPath, tableName, columns: [ { name: 'id', type: 'INTEGER', constraints: 'PRIMARY KEY' }, { name: 'name', type: 'TEXT', constraints: 'NOT NULL' }, { name: 'value', type: 'INTEGER' } ] }); // structuredContent를 사용하여 결과 확인 const createResponse = createResult.structuredContent || { success: false }; if (!createResponse.success) { console.log('Table creation failed:', createResponse.error); console.log('Table name:', tableName); return; // 테이블 생성 실패 시 테스트 스킵 } expect(createResponse.success).toBe(true); // 2. 테스트 데이터 삽입 for (const data of testData) { const insertResult = await (insertDataTool.handler as any)({ dbPath: testDbPath, tableName, data }); const insertResponse = insertResult.structuredContent || { success: false }; if (!insertResponse.success) { console.log('Data insertion failed:', insertResponse.error); console.log('Data:', data); return; // 데이터 삽입 실패 시 테스트 스킵 } expect(insertResponse.success).toBe(true); } // 3. SELECT 쿼리 구성 let query = `SELECT * FROM ${tableName}`; let params: any[] = []; if (whereCondition) { query += ` WHERE ${whereCondition.column} ${whereCondition.operator} ?`; params = [whereCondition.value]; } // 4. SELECT 쿼리 실행 const selectResult = await (selectDataTool.handler as any)({ dbPath: testDbPath, query, params }); const response = selectResult.structuredContent || JSON.parse(selectResult.content[0].text); // Property 5: SELECT 쿼리 결과 형식 검증 // - 결과는 JSON 형태로 반환되어야 함 expect(response).toHaveProperty('success'); expect(typeof response.success).toBe('boolean'); if (response.success) { // 성공한 경우 data 속성이 배열이어야 함 expect(response).toHaveProperty('data'); expect(Array.isArray(response.data)).toBe(true); // WHERE 절이 있는 경우 필터링 검증 if (whereCondition && response.data.length > 0) { for (const row of response.data) { const columnValue = row[whereCondition.column]; switch (whereCondition.operator) { case '=': expect(columnValue).toBe(whereCondition.value); break; case '>': if (typeof columnValue === 'number' && typeof whereCondition.value === 'number') { expect(columnValue).toBeGreaterThan(whereCondition.value); } break; case '<': if (typeof columnValue === 'number' && typeof whereCondition.value === 'number') { expect(columnValue).toBeLessThan(whereCondition.value); } break; case '>=': if (typeof columnValue === 'number' && typeof whereCondition.value === 'number') { expect(columnValue).toBeGreaterThanOrEqual(whereCondition.value); } break; case '<=': if (typeof columnValue === 'number' && typeof whereCondition.value === 'number') { expect(columnValue).toBeLessThanOrEqual(whereCondition.value); } break; } } } // 반환된 데이터의 구조 검증 if (response.data.length > 0) { const firstRow = response.data[0]; expect(firstRow).toHaveProperty('id'); expect(firstRow).toHaveProperty('name'); expect(firstRow).toHaveProperty('value'); } } else { // 실패한 경우 error 속성이 있어야 함 expect(response).toHaveProperty('error'); expect(typeof response.error).toBe('string'); } } catch (error) { // 예상치 못한 오류가 발생한 경우 실패 throw new Error(`Property test failed: ${error instanceof Error ? error.message : 'Unknown error'}`); } } ), { numRuns: 100 } ); }); test('SELECT 쿼리가 아닌 경우 오류 반환', async () => { const result = await (selectDataTool.handler as any)({ dbPath: testDbPath, query: 'INSERT INTO test (name) VALUES (?)', params: ['test'] }); const response = result.structuredContent || JSON.parse(result.content[0].text); expect(response.success).toBe(false); expect(response.error).toContain('SELECT 쿼리만 허용됩니다'); }); test('존재하지 않는 테이블 조회 시 오류 반환', async () => { const result = await (selectDataTool.handler as any)({ dbPath: testDbPath, query: 'SELECT * FROM nonexistent_table' }); const response = result.structuredContent || JSON.parse(result.content[0].text); expect(response.success).toBe(false); expect(response.error).toBeDefined(); }); test('빈 결과 집합 반환', async () => { // 테이블 생성 await (createTableTool.handler as any)({ dbPath: testDbPath, tableName: 'empty_test', columns: [ { name: 'id', type: 'INTEGER', constraints: 'PRIMARY KEY' }, { name: 'name', type: 'TEXT' } ] }); // 빈 테이블에서 조회 const result = await (selectDataTool.handler as any)({ dbPath: testDbPath, query: 'SELECT * FROM empty_test' }); const response = result.structuredContent || JSON.parse(result.content[0].text); expect(response.success).toBe(true); expect(response.data).toEqual([]); }); });

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