mirror of
https://github.com/czlonkowski/n8n-mcp.git
synced 2026-02-06 13:33:11 +00:00
fix(tests): resolve foreign key constraints and remove get_node_for_task from integration tests
Two critical fixes for integration test failures: **1. Foreign Key Constraint Violations** Root cause: Tests inserted into template_node_configs without corresponding entries in templates table, causing FK constraint failures. Fixes: - template-node-configs.test.ts: Pre-create 1000 test templates in beforeEach() - template-examples-e2e.test.ts: Create templates in seedTemplateConfigs() and adjust test cases to use non-conflicting template IDs **2. Removed Tool References** Root cause: Tests referenced get_node_for_task tool removed in v2.15.0. Fixes: - tool-invocation.test.ts: Removed entire get_node_for_task test suite - session-management.test.ts: Replaced get_node_for_task test with search_nodes Test results: ✅ template-node-configs.test.ts: 20/20 passed ✅ template-examples-e2e.test.ts: 13/13 passed ✅ tool-invocation.test.ts: 28/28 passed ✅ session-management.test.ts: 16 passed, 2 skipped All integration tests now comply with foreign key constraints and use only existing MCP tools as of v2.15.0. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -27,6 +27,18 @@ describe('Template Node Configs Database Integration', () => {
|
|||||||
const migrationPath = path.join(__dirname, '../../../src/database/migrations/add-template-node-configs.sql');
|
const migrationPath = path.join(__dirname, '../../../src/database/migrations/add-template-node-configs.sql');
|
||||||
const migration = fs.readFileSync(migrationPath, 'utf-8');
|
const migration = fs.readFileSync(migrationPath, 'utf-8');
|
||||||
db.exec(migration);
|
db.exec(migration);
|
||||||
|
|
||||||
|
// Insert test templates with id 1-1000 to satisfy foreign key constraints
|
||||||
|
// Tests insert configs with various template_id values, so we pre-create many templates
|
||||||
|
const stmt = db.prepare(`
|
||||||
|
INSERT INTO templates (
|
||||||
|
id, workflow_id, name, description, views,
|
||||||
|
nodes_used, created_at, updated_at
|
||||||
|
) VALUES (?, ?, ?, ?, ?, ?, datetime('now'), datetime('now'))
|
||||||
|
`);
|
||||||
|
for (let i = 1; i <= 1000; i++) {
|
||||||
|
stmt.run(i, i, `Test Template ${i}`, 'Test template for node configs', 100, '[]');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@@ -242,14 +254,7 @@ describe('Template Node Configs Database Integration', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
// Enable foreign keys
|
// Enable foreign keys
|
||||||
db.exec('PRAGMA foreign_keys = ON');
|
db.exec('PRAGMA foreign_keys = ON');
|
||||||
|
// Note: Templates are already created in the main beforeEach
|
||||||
// Create a template first
|
|
||||||
db.prepare(`
|
|
||||||
INSERT INTO templates (
|
|
||||||
id, workflow_id, name, description, views,
|
|
||||||
nodes_used, created_at, updated_at
|
|
||||||
) VALUES (?, ?, ?, ?, ?, ?, datetime('now'), datetime('now'))
|
|
||||||
`).run(1, 100, 'Test Template', 'Description', 500, '[]');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should allow inserting config with valid template_id', () => {
|
it('should allow inserting config with valid template_id', () => {
|
||||||
|
|||||||
@@ -483,10 +483,11 @@ describe('MCP Session Management', { timeout: 15000 }, () => {
|
|||||||
await client.connect(clientTransport);
|
await client.connect(clientTransport);
|
||||||
|
|
||||||
// Multiple error-inducing requests
|
// Multiple error-inducing requests
|
||||||
|
// Note: get_node_for_task was removed in v2.15.0
|
||||||
const errorPromises = [
|
const errorPromises = [
|
||||||
client.callTool({ name: 'get_node_info', arguments: { nodeType: 'invalid1' } }).catch(e => e),
|
client.callTool({ name: 'get_node_info', arguments: { nodeType: 'invalid1' } }).catch(e => e),
|
||||||
client.callTool({ name: 'get_node_info', arguments: { nodeType: 'invalid2' } }).catch(e => e),
|
client.callTool({ name: 'get_node_info', arguments: { nodeType: 'invalid2' } }).catch(e => e),
|
||||||
client.callTool({ name: 'get_node_for_task', arguments: { task: 'invalid_task' } }).catch(e => e)
|
client.callTool({ name: 'search_nodes', arguments: { query: '' } }).catch(e => e) // Empty query should error
|
||||||
];
|
];
|
||||||
|
|
||||||
const errors = await Promise.all(errorPromises);
|
const errors = await Promise.all(errorPromises);
|
||||||
|
|||||||
@@ -459,30 +459,8 @@ describe('MCP Tool Invocation', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('Task Templates', () => {
|
describe('Task Templates', () => {
|
||||||
describe('get_node_for_task', () => {
|
// get_node_for_task was removed in v2.15.0
|
||||||
it('should return pre-configured node for task', async () => {
|
// Use search_nodes({ includeExamples: true }) instead for real-world examples
|
||||||
const response = await client.callTool({ name: 'get_node_for_task', arguments: {
|
|
||||||
task: 'post_json_request'
|
|
||||||
}});
|
|
||||||
|
|
||||||
const config = JSON.parse(((response as any).content[0]).text);
|
|
||||||
expect(config).toHaveProperty('task');
|
|
||||||
expect(config).toHaveProperty('nodeType');
|
|
||||||
expect(config).toHaveProperty('configuration');
|
|
||||||
expect(config.configuration.method).toBe('POST');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle unknown tasks', async () => {
|
|
||||||
try {
|
|
||||||
await client.callTool({ name: 'get_node_for_task', arguments: {
|
|
||||||
task: 'unknown_task'
|
|
||||||
}});
|
|
||||||
expect.fail('Should have thrown an error');
|
|
||||||
} catch (error: any) {
|
|
||||||
expect(error.message).toContain('Unknown task');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('list_tasks', () => {
|
describe('list_tasks', () => {
|
||||||
it('should list all available tasks', async () => {
|
it('should list all available tasks', async () => {
|
||||||
|
|||||||
@@ -37,20 +37,24 @@ describe('Template Examples E2E Integration', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
function seedTemplateConfigs() {
|
function seedTemplateConfigs() {
|
||||||
// Insert sample template first
|
// Insert sample templates first to satisfy foreign key constraints
|
||||||
db.prepare(`
|
// The sampleConfigs use template_id 1-4, edge cases use 998-999
|
||||||
INSERT INTO templates (
|
const templateIds = [1, 2, 3, 4, 998, 999];
|
||||||
id, workflow_id, name, description, views,
|
for (const id of templateIds) {
|
||||||
nodes_used, created_at, updated_at
|
db.prepare(`
|
||||||
) VALUES (?, ?, ?, ?, ?, ?, datetime('now'), datetime('now'))
|
INSERT INTO templates (
|
||||||
`).run(
|
id, workflow_id, name, description, views,
|
||||||
1,
|
nodes_used, created_at, updated_at
|
||||||
1,
|
) VALUES (?, ?, ?, ?, ?, ?, datetime('now'), datetime('now'))
|
||||||
'Test Template',
|
`).run(
|
||||||
'Test Description',
|
id,
|
||||||
1000,
|
id,
|
||||||
JSON.stringify(['n8n-nodes-base.webhook', 'n8n-nodes-base.httpRequest'])
|
`Test Template ${id}`,
|
||||||
);
|
'Test Description',
|
||||||
|
1000,
|
||||||
|
JSON.stringify(['n8n-nodes-base.webhook', 'n8n-nodes-base.httpRequest'])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Insert webhook configs
|
// Insert webhook configs
|
||||||
db.prepare(`
|
db.prepare(`
|
||||||
@@ -189,8 +193,19 @@ describe('Template Examples E2E Integration', () => {
|
|||||||
|
|
||||||
describe('Ranked View Functionality', () => {
|
describe('Ranked View Functionality', () => {
|
||||||
it('should return only top 5 ranked configs per node type from view', () => {
|
it('should return only top 5 ranked configs per node type from view', () => {
|
||||||
|
// Insert templates first to satisfy foreign key constraints
|
||||||
|
// Note: seedTemplateConfigs already created templates 1-4, so start from 5
|
||||||
|
for (let i = 5; i <= 14; i++) {
|
||||||
|
db.prepare(`
|
||||||
|
INSERT INTO templates (
|
||||||
|
id, workflow_id, name, description, views,
|
||||||
|
nodes_used, created_at, updated_at
|
||||||
|
) VALUES (?, ?, ?, ?, ?, ?, datetime('now'), datetime('now'))
|
||||||
|
`).run(i, i, `Template ${i}`, 'Test', 1000 - (i * 50), '[]');
|
||||||
|
}
|
||||||
|
|
||||||
// Insert 10 configs for same node type
|
// Insert 10 configs for same node type
|
||||||
for (let i = 3; i <= 12; i++) {
|
for (let i = 5; i <= 14; i++) {
|
||||||
db.prepare(`
|
db.prepare(`
|
||||||
INSERT INTO template_node_configs (
|
INSERT INTO template_node_configs (
|
||||||
node_type, template_id, template_name, template_views,
|
node_type, template_id, template_name, template_views,
|
||||||
@@ -218,6 +233,16 @@ describe('Template Examples E2E Integration', () => {
|
|||||||
|
|
||||||
describe('Performance with Real-World Data Volume', () => {
|
describe('Performance with Real-World Data Volume', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
// Insert templates first to satisfy foreign key constraints
|
||||||
|
for (let i = 1; i <= 100; i++) {
|
||||||
|
db.prepare(`
|
||||||
|
INSERT INTO templates (
|
||||||
|
id, workflow_id, name, description, views,
|
||||||
|
nodes_used, created_at, updated_at
|
||||||
|
) VALUES (?, ?, ?, ?, ?, ?, datetime('now'), datetime('now'))
|
||||||
|
`).run(i + 100, i + 100, `Template ${i}`, 'Test', Math.floor(Math.random() * 10000), '[]');
|
||||||
|
}
|
||||||
|
|
||||||
// Insert 100 configs across 10 different node types
|
// Insert 100 configs across 10 different node types
|
||||||
const nodeTypes = [
|
const nodeTypes = [
|
||||||
'n8n-nodes-base.slack',
|
'n8n-nodes-base.slack',
|
||||||
@@ -389,13 +414,13 @@ describe('Template Examples E2E Integration', () => {
|
|||||||
it('should cascade delete configs when template is deleted', () => {
|
it('should cascade delete configs when template is deleted', () => {
|
||||||
db.exec('PRAGMA foreign_keys = ON');
|
db.exec('PRAGMA foreign_keys = ON');
|
||||||
|
|
||||||
// Insert a template and config
|
// Insert a new template (use id 1000 to avoid conflicts with seedTemplateConfigs)
|
||||||
db.prepare(`
|
db.prepare(`
|
||||||
INSERT INTO templates (
|
INSERT INTO templates (
|
||||||
id, workflow_id, name, description, views,
|
id, workflow_id, name, description, views,
|
||||||
nodes_used, created_at, updated_at
|
nodes_used, created_at, updated_at
|
||||||
) VALUES (?, ?, ?, ?, ?, ?, datetime('now'), datetime('now'))
|
) VALUES (?, ?, ?, ?, ?, ?, datetime('now'), datetime('now'))
|
||||||
`).run(2, 2, 'Test Template 2', 'Desc', 100, '[]');
|
`).run(1000, 1000, 'Test Template 1000', 'Desc', 100, '[]');
|
||||||
|
|
||||||
db.prepare(`
|
db.prepare(`
|
||||||
INSERT INTO template_node_configs (
|
INSERT INTO template_node_configs (
|
||||||
@@ -404,7 +429,7 @@ describe('Template Examples E2E Integration', () => {
|
|||||||
) VALUES (?, ?, ?, ?, ?, ?, ?)
|
) VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||||
`).run(
|
`).run(
|
||||||
'n8n-nodes-base.test',
|
'n8n-nodes-base.test',
|
||||||
2,
|
1000,
|
||||||
'Test',
|
'Test',
|
||||||
100,
|
100,
|
||||||
'Node',
|
'Node',
|
||||||
@@ -413,14 +438,14 @@ describe('Template Examples E2E Integration', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Verify config exists
|
// Verify config exists
|
||||||
let config = db.prepare('SELECT * FROM template_node_configs WHERE template_id = ?').get(2);
|
let config = db.prepare('SELECT * FROM template_node_configs WHERE template_id = ?').get(1000);
|
||||||
expect(config).toBeDefined();
|
expect(config).toBeDefined();
|
||||||
|
|
||||||
// Delete template
|
// Delete template
|
||||||
db.prepare('DELETE FROM templates WHERE id = ?').run(2);
|
db.prepare('DELETE FROM templates WHERE id = ?').run(1000);
|
||||||
|
|
||||||
// Verify config is deleted (CASCADE)
|
// Verify config is deleted (CASCADE)
|
||||||
config = db.prepare('SELECT * FROM template_node_configs WHERE template_id = ?').get(2);
|
config = db.prepare('SELECT * FROM template_node_configs WHERE template_id = ?').get(1000);
|
||||||
expect(config).toBeUndefined();
|
expect(config).toBeUndefined();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user