refactor: streamline test suite - cut 33 files, enable parallel execution (11.9x speedup)

Remove duplicate, low-value, and fragmented test files while preserving
all meaningful coverage. Enable parallel test execution and remove
the entire benchmark infrastructure.

Key changes:
- Consolidate workflow-validator tests (13 files -> 3)
- Consolidate config-validator tests (9 files -> 3)
- Consolidate telemetry tests (11 files -> 6)
- Merge AI validator tests (2 files -> 1)
- Remove example/demo test files, mock-testing files, and already-skipped tests
- Remove benchmark infrastructure (10 files, CI workflow, 4 npm scripts)
- Enable parallel test execution (remove singleThread: true)
- Remove retry:2 that was masking flaky tests
- Slim CI publish-results job

Results: 224 -> 191 test files, 4690 -> 4303 tests, 121K -> 106K lines
Local runtime: 319s -> 27s (11.9x speedup)

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

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
czlonkowski
2026-03-26 23:41:06 +01:00
parent 07bd1d4cc2
commit fdc37efde7
62 changed files with 2998 additions and 20806 deletions

View File

@@ -1830,4 +1830,513 @@ describe('n8n-validation', () => {
expect(validateWorkflowStructure(forUpdate)).toEqual([]);
});
});
describe('Sticky Notes Bug Fix', () => {
describe('sticky notes should be excluded from disconnected nodes validation', () => {
it('should allow workflow with sticky notes and connected functional nodes', () => {
const workflow: Partial<Workflow> = {
name: 'Test Workflow',
nodes: [
{
id: '1',
name: 'Webhook',
type: 'n8n-nodes-base.webhook',
typeVersion: 1,
position: [250, 300],
parameters: { path: '/test' }
},
{
id: '2',
name: 'HTTP Request',
type: 'n8n-nodes-base.httpRequest',
typeVersion: 3,
position: [450, 300],
parameters: {}
},
{
id: 'sticky1',
name: 'Documentation Note',
type: 'n8n-nodes-base.stickyNote',
typeVersion: 1,
position: [250, 100],
parameters: { content: 'This is a documentation note' }
}
],
connections: {
'Webhook': {
main: [[{ node: 'HTTP Request', type: 'main', index: 0 }]]
}
}
};
const errors = validateWorkflowStructure(workflow);
expect(errors).toEqual([]);
});
it('should handle multiple sticky notes without errors', () => {
const workflow: Partial<Workflow> = {
name: 'Documented Workflow',
nodes: [
{
id: '1',
name: 'Webhook',
type: 'n8n-nodes-base.webhook',
typeVersion: 1,
position: [250, 300],
parameters: { path: '/test' }
},
{
id: '2',
name: 'Process',
type: 'n8n-nodes-base.set',
typeVersion: 3,
position: [450, 300],
parameters: {}
},
...Array.from({ length: 10 }, (_, i) => ({
id: `sticky${i}`,
name: `Note ${i}`,
type: 'n8n-nodes-base.stickyNote',
typeVersion: 1,
position: [100 + i * 50, 100] as [number, number],
parameters: { content: `Documentation note ${i}` }
}))
],
connections: {
'Webhook': {
main: [[{ node: 'Process', type: 'main', index: 0 }]]
}
}
};
const errors = validateWorkflowStructure(workflow);
expect(errors).toEqual([]);
});
it('should handle all sticky note type variations', () => {
const stickyTypes = [
'n8n-nodes-base.stickyNote',
'nodes-base.stickyNote',
'@n8n/n8n-nodes-base.stickyNote'
];
stickyTypes.forEach((stickyType, index) => {
const workflow: Partial<Workflow> = {
name: 'Test Workflow',
nodes: [
{
id: '1',
name: 'Webhook',
type: 'n8n-nodes-base.webhook',
typeVersion: 1,
position: [250, 300],
parameters: { path: '/test' }
},
{
id: `sticky${index}`,
name: `Note ${index}`,
type: stickyType,
typeVersion: 1,
position: [250, 100],
parameters: { content: `Note ${index}` }
}
],
connections: {}
};
const errors = validateWorkflowStructure(workflow);
expect(errors.every(e => !e.includes(`Note ${index}`))).toBe(true);
});
});
it('should handle complex workflow with multiple sticky notes (real-world scenario)', () => {
const workflow: Partial<Workflow> = {
name: 'POST /auth/login',
nodes: [
{
id: 'webhook1',
name: 'Webhook Trigger',
type: 'n8n-nodes-base.webhook',
typeVersion: 1,
position: [250, 300],
parameters: { path: '/auth/login', httpMethod: 'POST' }
},
{
id: 'http1',
name: 'Authenticate',
type: 'n8n-nodes-base.httpRequest',
typeVersion: 3,
position: [450, 300],
parameters: {}
},
{
id: 'respond1',
name: 'Return Success',
type: 'n8n-nodes-base.respondToWebhook',
typeVersion: 1,
position: [650, 250],
parameters: {}
},
{
id: 'respond2',
name: 'Return Error',
type: 'n8n-nodes-base.respondToWebhook',
typeVersion: 1,
position: [650, 350],
parameters: {}
},
{
id: 'sticky1',
name: 'Webhook Trigger Note',
type: 'n8n-nodes-base.stickyNote',
typeVersion: 1,
position: [250, 150],
parameters: { content: 'Receives login request' }
},
{
id: 'sticky2',
name: 'Authenticate with Supabase Note',
type: 'n8n-nodes-base.stickyNote',
typeVersion: 1,
position: [450, 150],
parameters: { content: 'Validates credentials' }
},
{
id: 'sticky3',
name: 'Return Tokens Note',
type: 'n8n-nodes-base.stickyNote',
typeVersion: 1,
position: [650, 150],
parameters: { content: 'Returns access and refresh tokens' }
},
{
id: 'sticky4',
name: 'Return Error Note',
type: 'n8n-nodes-base.stickyNote',
typeVersion: 1,
position: [650, 450],
parameters: { content: 'Returns error message' }
}
],
connections: {
'Webhook Trigger': {
main: [[{ node: 'Authenticate', type: 'main', index: 0 }]]
},
'Authenticate': {
main: [
[{ node: 'Return Success', type: 'main', index: 0 }],
[{ node: 'Return Error', type: 'main', index: 0 }]
]
}
}
};
const errors = validateWorkflowStructure(workflow);
expect(errors).toEqual([]);
});
});
describe('validation should still detect truly disconnected functional nodes', () => {
it('should detect disconnected HTTP node but ignore sticky note', () => {
const workflow: Partial<Workflow> = {
name: 'Test Workflow',
nodes: [
{
id: '1',
name: 'Webhook',
type: 'n8n-nodes-base.webhook',
typeVersion: 1,
position: [250, 300],
parameters: { path: '/test' }
},
{
id: '2',
name: 'Disconnected HTTP',
type: 'n8n-nodes-base.httpRequest',
typeVersion: 3,
position: [450, 300],
parameters: {}
},
{
id: 'sticky1',
name: 'Sticky Note',
type: 'n8n-nodes-base.stickyNote',
typeVersion: 1,
position: [250, 100],
parameters: { content: 'Note' }
}
],
connections: {}
};
const errors = validateWorkflowStructure(workflow);
expect(errors.length).toBeGreaterThan(0);
const disconnectedError = errors.find(e => e.includes('Disconnected'));
expect(disconnectedError).toBeDefined();
expect(disconnectedError).toContain('Disconnected HTTP');
expect(disconnectedError).not.toContain('Sticky Note');
});
it('should detect multiple disconnected functional nodes but ignore sticky notes', () => {
const workflow: Partial<Workflow> = {
name: 'Test Workflow',
nodes: [
{
id: '1',
name: 'Webhook',
type: 'n8n-nodes-base.webhook',
typeVersion: 1,
position: [250, 300],
parameters: { path: '/test' }
},
{
id: '2',
name: 'Disconnected HTTP',
type: 'n8n-nodes-base.httpRequest',
typeVersion: 3,
position: [450, 300],
parameters: {}
},
{
id: '3',
name: 'Disconnected Set',
type: 'n8n-nodes-base.set',
typeVersion: 3,
position: [650, 300],
parameters: {}
},
{
id: 'sticky1',
name: 'Note 1',
type: 'n8n-nodes-base.stickyNote',
typeVersion: 1,
position: [250, 100],
parameters: { content: 'Note 1' }
},
{
id: 'sticky2',
name: 'Note 2',
type: 'n8n-nodes-base.stickyNote',
typeVersion: 1,
position: [450, 100],
parameters: { content: 'Note 2' }
}
],
connections: {}
};
const errors = validateWorkflowStructure(workflow);
expect(errors.length).toBeGreaterThan(0);
const connectionError = errors.find(e => e.includes('no connections') || e.includes('Disconnected'));
expect(connectionError).toBeDefined();
expect(connectionError).not.toContain('Note 1');
expect(connectionError).not.toContain('Note 2');
});
it('should allow sticky notes but still validate functional node connections', () => {
const workflow: Partial<Workflow> = {
name: 'Test Workflow',
nodes: [
{
id: '1',
name: 'Webhook',
type: 'n8n-nodes-base.webhook',
typeVersion: 1,
position: [250, 300],
parameters: { path: '/test' }
},
{
id: '2',
name: 'Connected HTTP',
type: 'n8n-nodes-base.httpRequest',
typeVersion: 3,
position: [450, 300],
parameters: {}
},
{
id: '3',
name: 'Disconnected Set',
type: 'n8n-nodes-base.set',
typeVersion: 3,
position: [650, 300],
parameters: {}
},
{
id: 'sticky1',
name: 'Sticky Note',
type: 'n8n-nodes-base.stickyNote',
typeVersion: 1,
position: [250, 100],
parameters: { content: 'Note' }
}
],
connections: {
'Webhook': {
main: [[{ node: 'Connected HTTP', type: 'main', index: 0 }]]
}
}
};
const errors = validateWorkflowStructure(workflow);
expect(errors.length).toBeGreaterThan(0);
const disconnectedError = errors.find(e => e.includes('Disconnected'));
expect(disconnectedError).toBeDefined();
expect(disconnectedError).toContain('Disconnected Set');
expect(disconnectedError).not.toContain('Connected HTTP');
expect(disconnectedError).not.toContain('Sticky Note');
});
});
describe('regression tests - ensure sticky notes work like in n8n UI', () => {
it('single webhook with sticky notes should be valid (matches n8n UI behavior)', () => {
const workflow: Partial<Workflow> = {
name: 'Webhook Only with Notes',
nodes: [
{
id: '1',
name: 'Webhook',
type: 'n8n-nodes-base.webhook',
typeVersion: 1,
position: [250, 300],
parameters: { path: '/test' }
},
{
id: 'sticky1',
name: 'Usage Instructions',
type: 'n8n-nodes-base.stickyNote',
typeVersion: 1,
position: [250, 100],
parameters: { content: 'Call this webhook to trigger the workflow' }
}
],
connections: {}
};
const errors = validateWorkflowStructure(workflow);
expect(errors).toEqual([]);
});
it('workflow with only sticky notes should be invalid (no executable nodes)', () => {
const workflow: Partial<Workflow> = {
name: 'Only Notes',
nodes: [
{
id: 'sticky1',
name: 'Note 1',
type: 'n8n-nodes-base.stickyNote',
typeVersion: 1,
position: [250, 100],
parameters: { content: 'Note 1' }
},
{
id: 'sticky2',
name: 'Note 2',
type: 'n8n-nodes-base.stickyNote',
typeVersion: 1,
position: [450, 100],
parameters: { content: 'Note 2' }
}
],
connections: {}
};
const errors = validateWorkflowStructure(workflow);
expect(errors.length).toBeGreaterThan(0);
expect(errors.some(e => e.includes('at least one executable node'))).toBe(true);
});
it('complex production workflow structure should validate correctly', () => {
const workflow: Partial<Workflow> = {
name: 'Production API Endpoint',
nodes: [
{
id: 'webhook1',
name: 'API Webhook',
type: 'n8n-nodes-base.webhook',
typeVersion: 1,
position: [250, 300],
parameters: { path: '/api/endpoint' }
},
{
id: 'validate1',
name: 'Validate Input',
type: 'n8n-nodes-base.code',
typeVersion: 2,
position: [450, 300],
parameters: {}
},
{
id: 'branch1',
name: 'Check Valid',
type: 'n8n-nodes-base.if',
typeVersion: 2,
position: [650, 300],
parameters: {}
},
{
id: 'process1',
name: 'Process Request',
type: 'n8n-nodes-base.httpRequest',
typeVersion: 3,
position: [850, 250],
parameters: {}
},
{
id: 'success1',
name: 'Return Success',
type: 'n8n-nodes-base.respondToWebhook',
typeVersion: 1,
position: [1050, 250],
parameters: {}
},
{
id: 'error1',
name: 'Return Error',
type: 'n8n-nodes-base.respondToWebhook',
typeVersion: 1,
position: [850, 350],
parameters: {}
},
...Array.from({ length: 11 }, (_, i) => ({
id: `sticky${i}`,
name: `Documentation ${i}`,
type: 'n8n-nodes-base.stickyNote',
typeVersion: 1,
position: [250 + i * 100, 100] as [number, number],
parameters: { content: `Documentation section ${i}` }
}))
],
connections: {
'API Webhook': {
main: [[{ node: 'Validate Input', type: 'main', index: 0 }]]
},
'Validate Input': {
main: [[{ node: 'Check Valid', type: 'main', index: 0 }]]
},
'Check Valid': {
main: [
[{ node: 'Process Request', type: 'main', index: 0 }],
[{ node: 'Return Error', type: 'main', index: 0 }]
]
},
'Process Request': {
main: [[{ node: 'Return Success', type: 'main', index: 0 }]]
}
}
};
const errors = validateWorkflowStructure(workflow);
expect(errors).toEqual([]);
});
});
});
});