Skip to main content
Glama

n8n-MCP

by 88-888
database-utils.test.ts13.6 kB
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; import * as fs from 'fs'; import * as path from 'path'; import { createTestDatabase, seedTestNodes, seedTestTemplates, createTestNode, createTestTemplate, resetDatabase, createDatabaseSnapshot, restoreDatabaseSnapshot, loadFixtures, dbHelpers, createMockDatabaseAdapter, withTransaction, measureDatabaseOperation, TestDatabase } from '../../utils/database-utils'; describe('Database Utils', () => { let testDb: TestDatabase; afterEach(async () => { if (testDb) { await testDb.cleanup(); } }); describe('createTestDatabase', () => { it('should create an in-memory database by default', async () => { testDb = await createTestDatabase(); expect(testDb.adapter).toBeDefined(); expect(testDb.nodeRepository).toBeDefined(); expect(testDb.templateRepository).toBeDefined(); expect(testDb.path).toBe(':memory:'); }); it('should create a file-based database when requested', async () => { const dbPath = path.join(__dirname, '../../temp/test-file.db'); testDb = await createTestDatabase({ inMemory: false, dbPath }); expect(testDb.path).toBe(dbPath); expect(fs.existsSync(dbPath)).toBe(true); }); it('should initialize schema when requested', async () => { testDb = await createTestDatabase({ initSchema: true }); // Verify tables exist const tables = testDb.adapter .prepare("SELECT name FROM sqlite_master WHERE type='table'") .all() as { name: string }[]; const tableNames = tables.map(t => t.name); expect(tableNames).toContain('nodes'); expect(tableNames).toContain('templates'); }); it('should skip schema initialization when requested', async () => { testDb = await createTestDatabase({ initSchema: false }); // Verify tables don't exist (SQLite has internal tables, so check for our specific tables) const tables = testDb.adapter .prepare("SELECT name FROM sqlite_master WHERE type='table' AND name IN ('nodes', 'templates')") .all() as { name: string }[]; expect(tables.length).toBe(0); }); }); describe('seedTestNodes', () => { beforeEach(async () => { testDb = await createTestDatabase(); }); it('should seed default test nodes', async () => { const nodes = await seedTestNodes(testDb.nodeRepository); expect(nodes).toHaveLength(3); expect(nodes[0].nodeType).toBe('nodes-base.httpRequest'); expect(nodes[1].nodeType).toBe('nodes-base.webhook'); expect(nodes[2].nodeType).toBe('nodes-base.slack'); }); it('should seed custom nodes along with defaults', async () => { const customNodes = [ { nodeType: 'nodes-base.custom1', displayName: 'Custom 1' }, { nodeType: 'nodes-base.custom2', displayName: 'Custom 2' } ]; const nodes = await seedTestNodes(testDb.nodeRepository, customNodes); expect(nodes).toHaveLength(5); // 3 default + 2 custom expect(nodes[3].nodeType).toBe('nodes-base.custom1'); expect(nodes[4].nodeType).toBe('nodes-base.custom2'); }); it('should save nodes to database', async () => { await seedTestNodes(testDb.nodeRepository); const count = dbHelpers.countRows(testDb.adapter, 'nodes'); expect(count).toBe(3); const httpNode = testDb.nodeRepository.getNode('nodes-base.httpRequest'); expect(httpNode).toBeDefined(); expect(httpNode.displayName).toBe('HTTP Request'); }); }); describe('seedTestTemplates', () => { beforeEach(async () => { testDb = await createTestDatabase(); }); it('should seed default test templates', async () => { const templates = await seedTestTemplates(testDb.templateRepository); expect(templates).toHaveLength(2); expect(templates[0].name).toBe('Simple HTTP Workflow'); expect(templates[1].name).toBe('Webhook to Slack'); }); it('should seed custom templates', async () => { const customTemplates = [ { id: 100, name: 'Custom Template' } ]; const templates = await seedTestTemplates(testDb.templateRepository, customTemplates); expect(templates).toHaveLength(3); expect(templates[2].id).toBe(100); expect(templates[2].name).toBe('Custom Template'); }); }); describe('createTestNode', () => { it('should create a node with defaults', () => { const node = createTestNode(); expect(node.nodeType).toBe('nodes-base.test'); expect(node.displayName).toBe('Test Node'); expect(node.style).toBe('programmatic'); expect(node.isAITool).toBe(false); }); it('should override defaults', () => { const node = createTestNode({ nodeType: 'nodes-base.custom', displayName: 'Custom Node', isAITool: true }); expect(node.nodeType).toBe('nodes-base.custom'); expect(node.displayName).toBe('Custom Node'); expect(node.isAITool).toBe(true); }); }); describe('resetDatabase', () => { beforeEach(async () => { testDb = await createTestDatabase(); }); it('should clear all data and reinitialize schema', async () => { // Add some data await seedTestNodes(testDb.nodeRepository); await seedTestTemplates(testDb.templateRepository); // Verify data exists expect(dbHelpers.countRows(testDb.adapter, 'nodes')).toBe(3); expect(dbHelpers.countRows(testDb.adapter, 'templates')).toBe(2); // Reset database await resetDatabase(testDb.adapter); // Verify data is cleared expect(dbHelpers.countRows(testDb.adapter, 'nodes')).toBe(0); expect(dbHelpers.countRows(testDb.adapter, 'templates')).toBe(0); // Verify tables still exist const tables = testDb.adapter .prepare("SELECT name FROM sqlite_master WHERE type='table'") .all() as { name: string }[]; const tableNames = tables.map(t => t.name); expect(tableNames).toContain('nodes'); expect(tableNames).toContain('templates'); }); }); describe('Database Snapshots', () => { beforeEach(async () => { testDb = await createTestDatabase(); }); it('should create and restore database snapshot', async () => { // Seed initial data await seedTestNodes(testDb.nodeRepository); await seedTestTemplates(testDb.templateRepository); // Create snapshot const snapshot = await createDatabaseSnapshot(testDb.adapter); expect(snapshot.metadata.nodeCount).toBe(3); expect(snapshot.metadata.templateCount).toBe(2); expect(snapshot.nodes).toHaveLength(3); expect(snapshot.templates).toHaveLength(2); // Clear database await resetDatabase(testDb.adapter); expect(dbHelpers.countRows(testDb.adapter, 'nodes')).toBe(0); // Restore from snapshot await restoreDatabaseSnapshot(testDb.adapter, snapshot); // Verify data is restored expect(dbHelpers.countRows(testDb.adapter, 'nodes')).toBe(3); expect(dbHelpers.countRows(testDb.adapter, 'templates')).toBe(2); const httpNode = testDb.nodeRepository.getNode('nodes-base.httpRequest'); expect(httpNode).toBeDefined(); expect(httpNode.displayName).toBe('HTTP Request'); }); }); describe('loadFixtures', () => { beforeEach(async () => { testDb = await createTestDatabase(); }); it('should load fixtures from JSON file', async () => { // Create a temporary fixture file const fixturePath = path.join(__dirname, '../../temp/test-fixtures.json'); const fixtures = { nodes: [ createTestNode({ nodeType: 'nodes-base.fixture1' }), createTestNode({ nodeType: 'nodes-base.fixture2' }) ], templates: [ createTestTemplate({ id: 1000, name: 'Fixture Template' }) ] }; // Ensure directory exists const dir = path.dirname(fixturePath); if (!fs.existsSync(dir)) { fs.mkdirSync(dir, { recursive: true }); } fs.writeFileSync(fixturePath, JSON.stringify(fixtures, null, 2)); // Load fixtures await loadFixtures(testDb.adapter, fixturePath); // Verify data was loaded expect(dbHelpers.countRows(testDb.adapter, 'nodes')).toBe(2); expect(dbHelpers.countRows(testDb.adapter, 'templates')).toBe(1); expect(dbHelpers.nodeExists(testDb.adapter, 'nodes-base.fixture1')).toBe(true); expect(dbHelpers.nodeExists(testDb.adapter, 'nodes-base.fixture2')).toBe(true); // Cleanup fs.unlinkSync(fixturePath); }); }); describe('dbHelpers', () => { beforeEach(async () => { testDb = await createTestDatabase(); await seedTestNodes(testDb.nodeRepository); }); it('should count rows correctly', () => { const count = dbHelpers.countRows(testDb.adapter, 'nodes'); expect(count).toBe(3); }); it('should check if node exists', () => { expect(dbHelpers.nodeExists(testDb.adapter, 'nodes-base.httpRequest')).toBe(true); expect(dbHelpers.nodeExists(testDb.adapter, 'nodes-base.nonexistent')).toBe(false); }); it('should get all node types', () => { const nodeTypes = dbHelpers.getAllNodeTypes(testDb.adapter); expect(nodeTypes).toHaveLength(3); expect(nodeTypes).toContain('nodes-base.httpRequest'); expect(nodeTypes).toContain('nodes-base.webhook'); expect(nodeTypes).toContain('nodes-base.slack'); }); it('should clear table', () => { expect(dbHelpers.countRows(testDb.adapter, 'nodes')).toBe(3); dbHelpers.clearTable(testDb.adapter, 'nodes'); expect(dbHelpers.countRows(testDb.adapter, 'nodes')).toBe(0); }); }); describe('createMockDatabaseAdapter', () => { it('should create a mock adapter with all required methods', () => { const mockAdapter = createMockDatabaseAdapter(); expect(mockAdapter.prepare).toBeDefined(); expect(mockAdapter.exec).toBeDefined(); expect(mockAdapter.close).toBeDefined(); expect(mockAdapter.pragma).toBeDefined(); expect(mockAdapter.transaction).toBeDefined(); expect(mockAdapter.checkFTS5Support).toBeDefined(); // Test that methods are mocked expect(vi.isMockFunction(mockAdapter.prepare)).toBe(true); expect(vi.isMockFunction(mockAdapter.exec)).toBe(true); }); }); describe('withTransaction', () => { beforeEach(async () => { testDb = await createTestDatabase(); }); it('should rollback transaction for testing', async () => { // Insert a node await seedTestNodes(testDb.nodeRepository, [ { nodeType: 'nodes-base.transaction-test' } ]); const initialCount = dbHelpers.countRows(testDb.adapter, 'nodes'); // Try to insert in a transaction that will rollback const result = await withTransaction(testDb.adapter, async () => { testDb.nodeRepository.saveNode(createTestNode({ nodeType: 'nodes-base.should-rollback' })); // Verify it was inserted within transaction const midCount = dbHelpers.countRows(testDb.adapter, 'nodes'); expect(midCount).toBe(initialCount + 1); return 'test-result'; }); // Transaction should have rolled back expect(result).toBeNull(); const finalCount = dbHelpers.countRows(testDb.adapter, 'nodes'); expect(finalCount).toBe(initialCount); }); }); describe('measureDatabaseOperation', () => { beforeEach(async () => { testDb = await createTestDatabase(); }); it('should measure operation duration', async () => { const duration = await measureDatabaseOperation('test operation', async () => { await seedTestNodes(testDb.nodeRepository); // Add a small delay to ensure measurable time passes await new Promise(resolve => setTimeout(resolve, 1)); }); expect(duration).toBeGreaterThanOrEqual(0); expect(duration).toBeLessThan(1000); // Should be fast }); }); describe('Integration Tests', () => { it('should handle complex database operations', async () => { testDb = await createTestDatabase({ enableFTS5: true }); // Seed initial data const nodes = await seedTestNodes(testDb.nodeRepository); const templates = await seedTestTemplates(testDb.templateRepository); // Create snapshot const snapshot = await createDatabaseSnapshot(testDb.adapter); // Add more data await seedTestNodes(testDb.nodeRepository, [ { nodeType: 'nodes-base.extra1' }, { nodeType: 'nodes-base.extra2' } ]); expect(dbHelpers.countRows(testDb.adapter, 'nodes')).toBe(5); // Restore snapshot await restoreDatabaseSnapshot(testDb.adapter, snapshot); // Should be back to original state expect(dbHelpers.countRows(testDb.adapter, 'nodes')).toBe(3); // Test FTS5 if supported if (testDb.adapter.checkFTS5Support()) { // FTS5 operations would go here expect(true).toBe(true); } }); }); });

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/88-888/n8n-mcp'

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