Skip to main content
Glama
YUChoe

SQLite MCP Server

by YUChoe
QueryUtils.test.ts10.1 kB
/** * QueryUtils 단위 테스트 */ import { detectQueryType, validateSQLParams, validateTableName, validateColumnName, basicSQLInjectionCheck, TransactionBuilder, validateQueryResult, classifySQLError, measureQueryPerformance, createBatchOperations } from './QueryUtils'; import { SQLErrorType } from '../types/index'; describe('QueryUtils', () => { describe('detectQueryType', () => { test('should detect SELECT queries', () => { expect(detectQueryType('SELECT * FROM users')).toBe('SELECT'); expect(detectQueryType(' select id from table ')).toBe('SELECT'); }); test('should detect INSERT queries', () => { expect(detectQueryType('INSERT INTO users VALUES (1, "test")')).toBe('INSERT'); expect(detectQueryType('insert into table (col) values (?)')).toBe('INSERT'); }); test('should detect UPDATE queries', () => { expect(detectQueryType('UPDATE users SET name = ?')).toBe('UPDATE'); }); test('should detect DELETE queries', () => { expect(detectQueryType('DELETE FROM users WHERE id = ?')).toBe('DELETE'); }); test('should detect CREATE queries', () => { expect(detectQueryType('CREATE TABLE users (id INTEGER)')).toBe('CREATE'); }); test('should return UNKNOWN for unrecognized queries', () => { expect(detectQueryType('EXPLAIN QUERY PLAN SELECT * FROM users')).toBe('UNKNOWN'); }); }); describe('validateSQLParams', () => { test('should validate matching parameter counts', () => { expect(validateSQLParams('SELECT * FROM users WHERE id = ?', [1])).toBe(true); expect(validateSQLParams('INSERT INTO users (name, age) VALUES (?, ?)', ['John', 25])).toBe(true); }); test('should reject mismatched parameter counts', () => { expect(validateSQLParams('SELECT * FROM users WHERE id = ?', [])).toBe(false); expect(validateSQLParams('SELECT * FROM users WHERE id = ? AND name = ?', [1])).toBe(false); }); test('should handle queries without parameters', () => { expect(validateSQLParams('SELECT * FROM users', [])).toBe(true); }); }); describe('validateTableName', () => { test('should accept valid table names', () => { expect(validateTableName('users')).toBe(true); expect(validateTableName('user_profiles')).toBe(true); expect(validateTableName('_temp')).toBe(true); expect(validateTableName('Table123')).toBe(true); }); test('should reject invalid table names', () => { expect(validateTableName('123users')).toBe(false); // 숫자로 시작 expect(validateTableName('user-profiles')).toBe(false); // 하이픈 포함 expect(validateTableName('user profiles')).toBe(false); // 공백 포함 expect(validateTableName('')).toBe(false); // 빈 문자열 }); test('should reject overly long table names', () => { const longName = 'a'.repeat(65); expect(validateTableName(longName)).toBe(false); }); }); describe('validateColumnName', () => { test('should use same rules as table names', () => { expect(validateColumnName('user_id')).toBe(true); expect(validateColumnName('123invalid')).toBe(false); }); }); describe('basicSQLInjectionCheck', () => { test('should pass safe queries', () => { expect(basicSQLInjectionCheck('SELECT * FROM users WHERE id = ?')).toBe(true); expect(basicSQLInjectionCheck('INSERT INTO users (name) VALUES (?)')).toBe(true); }); test('should detect dangerous patterns', () => { expect(basicSQLInjectionCheck('SELECT * FROM users; DROP TABLE users;')).toBe(false); expect(basicSQLInjectionCheck('SELECT * FROM users UNION SELECT * FROM passwords')).toBe(false); expect(basicSQLInjectionCheck('SELECT * FROM users -- comment')).toBe(false); expect(basicSQLInjectionCheck('SELECT * FROM users /* comment */')).toBe(false); }); }); describe('TransactionBuilder', () => { let builder: TransactionBuilder; beforeEach(() => { builder = new TransactionBuilder(); }); test('should build insert operations', () => { builder.insert('users', { name: 'John', age: 25 }); const operations = builder.build(); expect(operations).toHaveLength(1); expect(operations[0].sql).toBe('INSERT INTO users (name, age) VALUES (?, ?)'); expect(operations[0].params).toEqual(['John', 25]); }); test('should build update operations', () => { builder.update('users', { name: 'Jane' }, 'id = ?', [1]); const operations = builder.build(); expect(operations).toHaveLength(1); expect(operations[0].sql).toBe('UPDATE users SET name = ? WHERE id = ?'); expect(operations[0].params).toEqual(['Jane', 1]); }); test('should build delete operations', () => { builder.delete('users', 'age < ?', [18]); const operations = builder.build(); expect(operations).toHaveLength(1); expect(operations[0].sql).toBe('DELETE FROM users WHERE age < ?'); expect(operations[0].params).toEqual([18]); }); test('should chain multiple operations', () => { builder .insert('users', { name: 'John' }) .update('users', { active: true }, 'name = ?', ['John']) .delete('logs', 'created_at < ?', ['2023-01-01']); const operations = builder.build(); expect(operations).toHaveLength(3); }); test('should clear operations', () => { builder.insert('users', { name: 'John' }); expect(builder.build()).toHaveLength(1); builder.clear(); expect(builder.build()).toHaveLength(0); }); test('should reject invalid table names', () => { expect(() => builder.insert('123invalid', { name: 'John' })).toThrow(); expect(() => builder.update('invalid-table', { name: 'John' }, 'id = ?', [1])).toThrow(); expect(() => builder.delete('invalid table', 'id = ?', [1])).toThrow(); }); }); describe('validateQueryResult', () => { test('should validate SELECT results', () => { const selectResult = { success: true, data: [{ id: 1, name: 'John' }] }; expect(validateQueryResult(selectResult, 'SELECT')).toBe(true); const emptySelectResult = { success: true, data: [] }; expect(validateQueryResult(emptySelectResult, 'SELECT')).toBe(true); }); test('should validate MODIFY results', () => { const modifyResult = { success: true, rowsAffected: 1 }; expect(validateQueryResult(modifyResult, 'MODIFY')).toBe(true); const noRowsResult = { success: true, rowsAffected: 0 }; expect(validateQueryResult(noRowsResult, 'MODIFY')).toBe(true); }); test('should reject failed results', () => { const failedResult = { success: false, error: 'SQL error' }; expect(validateQueryResult(failedResult, 'SELECT')).toBe(false); expect(validateQueryResult(failedResult, 'MODIFY')).toBe(false); }); }); describe('classifySQLError', () => { test('should classify syntax errors', () => { const syntaxError = new Error('syntax error near "FROM"'); expect(classifySQLError(syntaxError)).toBe(SQLErrorType.SYNTAX_ERROR); }); test('should classify table not exists errors', () => { const tableError = new Error('no such table: nonexistent_table'); expect(classifySQLError(tableError)).toBe(SQLErrorType.TABLE_NOT_EXISTS); }); test('should classify column not exists errors', () => { const columnError = new Error('no such column: nonexistent_column'); expect(classifySQLError(columnError)).toBe(SQLErrorType.COLUMN_NOT_EXISTS); }); test('should classify constraint violations', () => { const constraintError = new Error('UNIQUE constraint failed'); expect(classifySQLError(constraintError)).toBe(SQLErrorType.CONSTRAINT_VIOLATION); }); }); describe('measureQueryPerformance', () => { test('should measure execution time', () => { let measuredDuration: number | undefined; let measuredType: string | undefined; const result = measureQueryPerformance( () => 'test result', 'SELECT', (duration, type) => { measuredDuration = duration; measuredType = type; } ); expect(result).toBe('test result'); expect(measuredDuration).toBeGreaterThanOrEqual(0); expect(measuredType).toBe('SELECT'); }); test('should measure error execution time', () => { let measuredType: string | undefined; expect(() => { measureQueryPerformance( () => { throw new Error('test error'); }, 'INSERT', (_duration, type) => { measuredType = type; } ); }).toThrow('test error'); expect(measuredType).toBe('INSERT_ERROR'); }); }); describe('createBatchOperations', () => { test('should create batch operations', () => { const records = [ { name: 'John', age: 25 }, { name: 'Jane', age: 30 }, { name: 'Bob', age: 35 } ]; const batches = createBatchOperations('users', records, 2); expect(batches).toHaveLength(2); expect(batches[0]).toHaveLength(2); // 첫 번째 배치: 2개 레코드 expect(batches[1]).toHaveLength(1); // 두 번째 배치: 1개 레코드 expect(batches[0][0].sql).toBe('INSERT INTO users (name, age) VALUES (?, ?)'); expect(batches[0][0].params).toEqual(['John', 25]); }); test('should handle empty records', () => { const batches = createBatchOperations('users', []); expect(batches).toHaveLength(0); }); test('should reject invalid table names', () => { expect(() => createBatchOperations('123invalid', [{ name: 'John' }])).toThrow(); }); }); });

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