Files
n8n-mcp/tests/integration/n8n-api/scripts/cleanup-non-test-workflows.ts
Romuald Członkowski 07bd1d4cc2 chore: update n8n to 2.13.3 (#666)
* chore: update n8n to 2.13.3 and bump version to 2.41.0

- Updated n8n from 2.12.3 to 2.13.3
- Updated n8n-core from 2.12.0 to 2.13.1
- Updated n8n-workflow from 2.12.0 to 2.13.1
- Updated @n8n/n8n-nodes-langchain from 2.12.0 to 2.13.1
- Rebuilt node database with 1,396 nodes (812 core + 584 community: 516 verified + 68 npm)
- Refreshed community nodes with 581 AI-generated documentation summaries
- Improved documentation generator: strip <think> tags, raw fetch for vLLM chat_template_kwargs
- Incremental community updates: saveNode uses ON CONFLICT DO UPDATE preserving READMEs/AI summaries
- fetch:community now upserts by default (use --rebuild for clean slate)
- Updated README badge and node counts
- Updated CHANGELOG and MEMORY_N8N_UPDATE.md

Conceived by Romuald Członkowski - https://www.aiadvisors.pl/en

Co-Authored-By: Claude <noreply@anthropic.com>

* chore: update MCP SDK from 1.27.1 to 1.28.0

- Pinned @modelcontextprotocol/sdk to 1.28.0 (was ^1.27.1)
- Updated CI dependency check to expect 1.28.0
- SDK 1.28.0 includes: loopback port relaxation, inputSchema fix,
  timeout cleanup fix, OAuth scope improvements
- All 15 MCP tool tests pass with no regressions

Conceived by Romuald Członkowski - https://www.aiadvisors.pl/en

Co-Authored-By: Claude <noreply@anthropic.com>

* 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>

* chore: remove documentation generator tests

These tests mocked the OpenAI SDK which was replaced with raw fetch.
Documentation generation is a local LLM utility, not core functionality.

Conceived by Romuald Członkowski - https://www.aiadvisors.pl/en

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: relax SQL assertion in outputs test to match ON CONFLICT pattern

Conceived by Romuald Członkowski - https://www.aiadvisors.pl/en

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: use INSERT OR REPLACE with docs preservation instead of ON CONFLICT

ON CONFLICT DO UPDATE caused FTS5 trigger conflicts ("database disk
image is malformed") in CI. Reverted to INSERT OR REPLACE but now
reads existing npm_readme/ai_documentation_summary/ai_summary_generated_at
before saving and carries them through the replace.

Conceived by Romuald Członkowski - https://www.aiadvisors.pl/en

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: update saveNode test mocks for docs preservation pattern

Tests now account for the SELECT query that reads existing docs
before INSERT OR REPLACE, and the 3 extra params (npm_readme,
ai_documentation_summary, ai_summary_generated_at).

Conceived by Romuald Członkowski - https://www.aiadvisors.pl/en

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: update community integration test mock for INSERT OR REPLACE

The mock SQL matching used 'INSERT INTO nodes' which doesn't match
'INSERT OR REPLACE INTO nodes'. Also added handler for the new
SELECT npm_readme query in saveNode.

Conceived by Romuald Członkowski - https://www.aiadvisors.pl/en

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-26 22:21:56 +01:00

116 lines
3.1 KiB
TypeScript

#!/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);
});