Skip to main content
Glama
OptimizationService.test.js13 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const vitest_1 = require("vitest"); const Column_1 = require("../entities/Column"); const ForeignKey_1 = require("../entities/ForeignKey"); const Index_1 = require("../entities/Index"); const Relationship_1 = require("../entities/Relationship"); const TableInfo_1 = require("../entities/TableInfo"); const Optimization_1 = require("../entities/Optimization"); const OptimizationService_1 = require("./OptimizationService"); (0, vitest_1.describe)('OptimizationService', () => { let service; (0, vitest_1.beforeEach)(() => { service = new OptimizationService_1.OptimizationService(); }); (0, vitest_1.describe)('checkMissingPrimaryKeys()', () => { (0, vitest_1.it)('should detect tables without primary keys', () => { const noPkTable = new TableInfo_1.TableInfo('logs', 'table', [ new Column_1.Column('timestamp', 'TEXT'), new Column_1.Column('message', 'TEXT'), ]); const optimizations = service.checkMissingPrimaryKeys([noPkTable]); (0, vitest_1.expect)(optimizations.length).toBe(1); (0, vitest_1.expect)(optimizations[0].type).toBe('missing_primary_key'); (0, vitest_1.expect)(optimizations[0].table).toBe('logs'); (0, vitest_1.expect)(optimizations[0].priority).toBe('high'); }); (0, vitest_1.it)('should not flag tables with primary keys', () => { const withPk = new TableInfo_1.TableInfo('users', 'table', [ new Column_1.Column('id', 'INTEGER', true), new Column_1.Column('name', 'TEXT'), ]); const optimizations = service.checkMissingPrimaryKeys([withPk]); (0, vitest_1.expect)(optimizations.length).toBe(0); }); (0, vitest_1.it)('should not flag views', () => { const view = new TableInfo_1.TableInfo('user_view', 'view', [ new Column_1.Column('id', 'INTEGER'), new Column_1.Column('name', 'TEXT'), ]); const optimizations = service.checkMissingPrimaryKeys([view]); (0, vitest_1.expect)(optimizations.length).toBe(0); }); }); (0, vitest_1.describe)('checkMissingIndexes()', () => { (0, vitest_1.it)('should detect foreign keys without indexes', () => { const postsTable = new TableInfo_1.TableInfo('posts', 'table', [ new Column_1.Column('id', 'INTEGER', true), new Column_1.Column('user_id', 'INTEGER'), new Column_1.Column('title', 'TEXT'), ], [], // No indexes! [new ForeignKey_1.ForeignKey('posts', 'user_id', 'users', 'id')]); const relationships = [new Relationship_1.Relationship('posts', 'user_id', 'users', 'id')]; const optimizations = service.checkMissingIndexes([postsTable], relationships); (0, vitest_1.expect)(optimizations.length).toBe(1); (0, vitest_1.expect)(optimizations[0].type).toBe('missing_index'); (0, vitest_1.expect)(optimizations[0].table).toBe('posts'); (0, vitest_1.expect)(optimizations[0].column).toBe('user_id'); (0, vitest_1.expect)(optimizations[0].priority).toBe('high'); }); (0, vitest_1.it)('should not flag foreign keys with indexes', () => { const postsTable = new TableInfo_1.TableInfo('posts', 'table', [new Column_1.Column('id', 'INTEGER', true), new Column_1.Column('user_id', 'INTEGER')], [new Index_1.Index('idx_user_id', 'posts', ['user_id'])], [new ForeignKey_1.ForeignKey('posts', 'user_id', 'users', 'id')]); const relationships = [new Relationship_1.Relationship('posts', 'user_id', 'users', 'id')]; const optimizations = service.checkMissingIndexes([postsTable], relationships); (0, vitest_1.expect)(optimizations.length).toBe(0); }); (0, vitest_1.it)('should check multiple foreign keys', () => { const commentsTable = new TableInfo_1.TableInfo('comments', 'table', [ new Column_1.Column('id', 'INTEGER', true), new Column_1.Column('post_id', 'INTEGER'), new Column_1.Column('user_id', 'INTEGER'), ], [], [ new ForeignKey_1.ForeignKey('comments', 'post_id', 'posts', 'id'), new ForeignKey_1.ForeignKey('comments', 'user_id', 'users', 'id'), ]); const relationships = [ new Relationship_1.Relationship('comments', 'post_id', 'posts', 'id'), new Relationship_1.Relationship('comments', 'user_id', 'users', 'id'), ]; const optimizations = service.checkMissingIndexes([commentsTable], relationships); (0, vitest_1.expect)(optimizations.length).toBe(2); }); }); (0, vitest_1.describe)('checkNullableForeignKeys()', () => { (0, vitest_1.it)('should detect nullable foreign keys', () => { const postsTable = new TableInfo_1.TableInfo('posts', 'table', [ new Column_1.Column('id', 'INTEGER', true, false), new Column_1.Column('user_id', 'INTEGER', false, true), // Nullable! ], [], [new ForeignKey_1.ForeignKey('posts', 'user_id', 'users', 'id')]); const optimizations = service.checkNullableForeignKeys([postsTable]); (0, vitest_1.expect)(optimizations.length).toBe(1); (0, vitest_1.expect)(optimizations[0].type).toBe('nullable_foreign_key'); (0, vitest_1.expect)(optimizations[0].priority).toBe('medium'); (0, vitest_1.expect)(optimizations[0].column).toBe('user_id'); }); (0, vitest_1.it)('should not flag non-nullable foreign keys', () => { const postsTable = new TableInfo_1.TableInfo('posts', 'table', [ new Column_1.Column('id', 'INTEGER', true, false), new Column_1.Column('user_id', 'INTEGER', false, false), // NOT nullable ], [], [new ForeignKey_1.ForeignKey('posts', 'user_id', 'users', 'id')]); const optimizations = service.checkNullableForeignKeys([postsTable]); (0, vitest_1.expect)(optimizations.length).toBe(0); }); }); (0, vitest_1.describe)('checkRedundantIndexes()', () => { (0, vitest_1.it)('should detect redundant single-column index', () => { const table = new TableInfo_1.TableInfo('posts', 'table', [new Column_1.Column('id', 'INTEGER', true), new Column_1.Column('user_id', 'INTEGER')], [ new Index_1.Index('idx_user_id', 'posts', ['user_id']), new Index_1.Index('idx_user_id_created', 'posts', ['user_id', 'created_at']), ]); const optimizations = service.checkRedundantIndexes(table); (0, vitest_1.expect)(optimizations.length).toBeGreaterThan(0); (0, vitest_1.expect)(optimizations[0].type).toBe('redundant_index'); (0, vitest_1.expect)(optimizations[0].priority).toBe('low'); }); (0, vitest_1.it)('should not flag non-redundant indexes', () => { const table = new TableInfo_1.TableInfo('posts', 'table', [new Column_1.Column('id', 'INTEGER', true), new Column_1.Column('user_id', 'INTEGER')], [ new Index_1.Index('idx_user_id', 'posts', ['user_id']), new Index_1.Index('idx_created', 'posts', ['created_at']), ]); const optimizations = service.checkRedundantIndexes(table); (0, vitest_1.expect)(optimizations.length).toBe(0); }); }); (0, vitest_1.describe)('analyzeSchema()', () => { (0, vitest_1.it)('should combine all optimization checks', () => { const noPkTable = new TableInfo_1.TableInfo('logs', 'table', [new Column_1.Column('message', 'TEXT')]); const postsTable = new TableInfo_1.TableInfo('posts', 'table', [new Column_1.Column('id', 'INTEGER', true), new Column_1.Column('user_id', 'INTEGER', false, true)], [], [new ForeignKey_1.ForeignKey('posts', 'user_id', 'users', 'id')]); const relationships = [new Relationship_1.Relationship('posts', 'user_id', 'users', 'id')]; const optimizations = service.analyzeSchema([noPkTable, postsTable], relationships); // Should find: missing PK, missing index, nullable FK (0, vitest_1.expect)(optimizations.length).toBeGreaterThanOrEqual(3); }); }); (0, vitest_1.describe)('filterByPriority()', () => { (0, vitest_1.it)('should filter high priority optimizations', () => { const opts = [ new Optimization_1.Optimization('missing_primary_key', 't1', 'r', 's', 'high'), new Optimization_1.Optimization('nullable_foreign_key', 't2', 'r', 's', 'medium'), new Optimization_1.Optimization('missing_index', 't3', 'r', 's', 'high'), ]; const high = service.filterByPriority(opts, 'high'); (0, vitest_1.expect)(high.length).toBe(2); (0, vitest_1.expect)(high.every((opt) => opt.priority === 'high')).toBe(true); }); }); (0, vitest_1.describe)('filterByType()', () => { (0, vitest_1.it)('should filter by optimization type', () => { const opts = [ new Optimization_1.Optimization('missing_index', 't1', 'r', 's', 'high'), new Optimization_1.Optimization('missing_primary_key', 't2', 'r', 's', 'high'), new Optimization_1.Optimization('missing_index', 't3', 'r', 's', 'high'), ]; const indexOpts = service.filterByType(opts, 'missing_index'); (0, vitest_1.expect)(indexOpts.length).toBe(2); (0, vitest_1.expect)(indexOpts.every((opt) => opt.type === 'missing_index')).toBe(true); }); }); (0, vitest_1.describe)('filterByTable()', () => { (0, vitest_1.it)('should filter by table name', () => { const opts = [ new Optimization_1.Optimization('missing_index', 'posts', 'r', 's', 'high'), new Optimization_1.Optimization('missing_primary_key', 'logs', 'r', 's', 'high'), new Optimization_1.Optimization('nullable_foreign_key', 'posts', 'r', 's', 'medium'), ]; const postsOpts = service.filterByTable(opts, 'posts'); (0, vitest_1.expect)(postsOpts.length).toBe(2); (0, vitest_1.expect)(postsOpts.every((opt) => opt.table === 'posts')).toBe(true); }); }); (0, vitest_1.describe)('sortByPriority()', () => { (0, vitest_1.it)('should sort by priority (high -> medium -> low)', () => { const opts = [ new Optimization_1.Optimization('redundant_index', 't1', 'r', 's', 'low'), new Optimization_1.Optimization('missing_index', 't2', 'r', 's', 'high'), new Optimization_1.Optimization('nullable_foreign_key', 't3', 'r', 's', 'medium'), ]; const sorted = service.sortByPriority(opts); (0, vitest_1.expect)(sorted[0].priority).toBe('high'); (0, vitest_1.expect)(sorted[1].priority).toBe('medium'); (0, vitest_1.expect)(sorted[2].priority).toBe('low'); }); (0, vitest_1.it)('should not mutate original array', () => { const opts = [ new Optimization_1.Optimization('redundant_index', 't1', 'r', 's', 'low'), new Optimization_1.Optimization('missing_index', 't2', 'r', 's', 'high'), ]; const original = [...opts]; service.sortByPriority(opts); (0, vitest_1.expect)(opts).toEqual(original); }); }); (0, vitest_1.describe)('getSummary()', () => { (0, vitest_1.it)('should provide optimization summary', () => { const opts = [ new Optimization_1.Optimization('missing_index', 't1', 'r', 's', 'high'), new Optimization_1.Optimization('missing_index', 't2', 'r', 's', 'high'), new Optimization_1.Optimization('missing_primary_key', 't3', 'r', 's', 'high'), new Optimization_1.Optimization('nullable_foreign_key', 't4', 'r', 's', 'medium'), new Optimization_1.Optimization('redundant_index', 't5', 'r', 's', 'low'), ]; const summary = service.getSummary(opts); (0, vitest_1.expect)(summary.total).toBe(5); (0, vitest_1.expect)(summary.byPriority.high).toBe(3); (0, vitest_1.expect)(summary.byPriority.medium).toBe(1); (0, vitest_1.expect)(summary.byPriority.low).toBe(1); (0, vitest_1.expect)(summary.byType.get('missing_index')).toBe(2); (0, vitest_1.expect)(summary.byType.get('missing_primary_key')).toBe(1); }); }); }); //# sourceMappingURL=OptimizationService.test.js.map

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/semanticintent/semantic-d1-mcp'

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