mirror of
https://github.com/czlonkowski/n8n-mcp.git
synced 2026-03-26 20:23:08 +00:00
fix: update test assertions for ON CONFLICT saveNode SQL
Tests expected old INSERT OR REPLACE SQL, updated to match new INSERT INTO ... ON CONFLICT(node_type) DO UPDATE SET pattern. Conceived by Romuald Członkowski - https://www.aiadvisors.pl/en Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -87,7 +87,7 @@ class InMemoryDatabaseAdapter implements DatabaseAdapter {
|
||||
|
||||
class InMemoryPreparedStatement implements PreparedStatement {
|
||||
run = vi.fn((...params: any[]): RunResult => {
|
||||
if (this.sql.includes('INSERT OR REPLACE INTO nodes')) {
|
||||
if (this.sql.includes('INSERT INTO nodes')) {
|
||||
const node = this.paramsToNode(params);
|
||||
this.adapter.saveNode(node);
|
||||
return { changes: 1, lastInsertRowid: 1 };
|
||||
|
||||
115
tests/integration/n8n-api/scripts/cleanup-non-test-workflows.ts
Normal file
115
tests/integration/n8n-api/scripts/cleanup-non-test-workflows.ts
Normal file
@@ -0,0 +1,115 @@
|
||||
#!/usr/bin/env tsx
|
||||
/**
|
||||
* Cleanup Non-Test Workflows
|
||||
*
|
||||
* Deletes all workflows from the n8n test instance EXCEPT those
|
||||
* with "[TEST]" in the name. This helps keep the test instance
|
||||
* clean and prevents list endpoint pagination issues.
|
||||
*
|
||||
* Usage:
|
||||
* npx tsx tests/integration/n8n-api/scripts/cleanup-non-test-workflows.ts
|
||||
* npx tsx tests/integration/n8n-api/scripts/cleanup-non-test-workflows.ts --dry-run
|
||||
*/
|
||||
|
||||
import { getN8nCredentials, validateCredentials } from '../utils/credentials';
|
||||
|
||||
const DRY_RUN = process.argv.includes('--dry-run');
|
||||
|
||||
interface Workflow {
|
||||
id: string;
|
||||
name: string;
|
||||
active: boolean;
|
||||
}
|
||||
|
||||
async function fetchAllWorkflows(baseUrl: string, apiKey: string): Promise<Workflow[]> {
|
||||
const all: Workflow[] = [];
|
||||
let cursor: string | undefined;
|
||||
|
||||
while (true) {
|
||||
const url = new URL('/api/v1/workflows', baseUrl);
|
||||
url.searchParams.set('limit', '100');
|
||||
if (cursor) url.searchParams.set('cursor', cursor);
|
||||
|
||||
const res = await fetch(url.toString(), {
|
||||
headers: { 'X-N8N-API-KEY': apiKey }
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
throw new Error(`Failed to list workflows: ${res.status} ${res.statusText}`);
|
||||
}
|
||||
|
||||
const body = await res.json() as { data: Workflow[]; nextCursor?: string };
|
||||
all.push(...body.data);
|
||||
|
||||
if (!body.nextCursor) break;
|
||||
cursor = body.nextCursor;
|
||||
}
|
||||
|
||||
return all;
|
||||
}
|
||||
|
||||
async function deleteWorkflow(baseUrl: string, apiKey: string, id: string): Promise<void> {
|
||||
const res = await fetch(`${baseUrl}/api/v1/workflows/${id}`, {
|
||||
method: 'DELETE',
|
||||
headers: { 'X-N8N-API-KEY': apiKey }
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
throw new Error(`Failed to delete workflow ${id}: ${res.status} ${res.statusText}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const creds = getN8nCredentials();
|
||||
validateCredentials(creds);
|
||||
|
||||
console.log(`n8n Instance: ${creds.url}`);
|
||||
console.log(`Mode: ${DRY_RUN ? 'DRY RUN' : 'LIVE DELETE'}\n`);
|
||||
|
||||
const workflows = await fetchAllWorkflows(creds.url, creds.apiKey);
|
||||
console.log(`Total workflows found: ${workflows.length}\n`);
|
||||
|
||||
const toKeep = workflows.filter(w => w.name.includes('[TEST]'));
|
||||
const toDelete = workflows.filter(w => !w.name.includes('[TEST]'));
|
||||
|
||||
console.log(`Keeping (${toKeep.length}):`);
|
||||
for (const w of toKeep) {
|
||||
console.log(` ✅ ${w.id} - ${w.name}`);
|
||||
}
|
||||
|
||||
console.log(`\nDeleting (${toDelete.length}):`);
|
||||
for (const w of toDelete) {
|
||||
console.log(` 🗑️ ${w.id} - ${w.name}${w.active ? ' (ACTIVE)' : ''}`);
|
||||
}
|
||||
|
||||
if (DRY_RUN) {
|
||||
console.log('\nDry run complete. No workflows were deleted.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (toDelete.length === 0) {
|
||||
console.log('\nNothing to delete.');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`\nDeleting ${toDelete.length} workflows...`);
|
||||
let deleted = 0;
|
||||
let failed = 0;
|
||||
|
||||
for (const w of toDelete) {
|
||||
try {
|
||||
await deleteWorkflow(creds.url, creds.apiKey, w.id);
|
||||
deleted++;
|
||||
} catch (err) {
|
||||
console.error(` Failed to delete ${w.id} (${w.name}): ${err}`);
|
||||
failed++;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`\nDone! Deleted: ${deleted}, Failed: ${failed}, Kept: ${toKeep.length}`);
|
||||
}
|
||||
|
||||
main().catch(err => {
|
||||
console.error('Fatal error:', err);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -120,7 +120,7 @@ class MockPreparedStatement implements PreparedStatement {
|
||||
}
|
||||
|
||||
// saveNode - INSERT OR REPLACE
|
||||
if (this.sql.includes('INSERT OR REPLACE INTO nodes')) {
|
||||
if (this.sql.includes('INSERT INTO nodes')) {
|
||||
this.run = vi.fn((...params: any[]): RunResult => {
|
||||
const nodes = this.mockData.get('community_nodes') || [];
|
||||
const nodeType = params[0];
|
||||
|
||||
@@ -91,7 +91,7 @@ describe('NodeRepository - Core Functionality', () => {
|
||||
repository.saveNode(parsedNode);
|
||||
|
||||
// Verify prepare was called with correct SQL
|
||||
expect(mockAdapter.prepare).toHaveBeenCalledWith(expect.stringContaining('INSERT OR REPLACE INTO nodes'));
|
||||
expect(mockAdapter.prepare).toHaveBeenCalledWith(expect.stringContaining('INSERT INTO nodes'));
|
||||
|
||||
// Get the prepared statement and verify run was called
|
||||
const stmt = mockAdapter._getStatement(mockAdapter.prepare.mock.lastCall?.[0] || '');
|
||||
|
||||
@@ -56,7 +56,7 @@ describe('NodeRepository - Outputs Handling', () => {
|
||||
repository.saveNode(node);
|
||||
|
||||
expect(mockDb.prepare).toHaveBeenCalledWith(`
|
||||
INSERT OR REPLACE INTO nodes (
|
||||
INSERT INTO nodes (
|
||||
node_type, package_name, display_name, description,
|
||||
category, development_style, is_ai_tool, is_trigger,
|
||||
is_webhook, is_versioned, is_tool_variant, tool_variant_of,
|
||||
|
||||
Reference in New Issue
Block a user