agentic-qe 2.5.5 → 2.5.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/agents/n8n/n8n-base-agent.md +376 -0
- package/.claude/agents/n8n/n8n-bdd-scenario-tester.md +613 -0
- package/.claude/agents/n8n/n8n-chaos-tester.md +654 -0
- package/.claude/agents/n8n/n8n-ci-orchestrator.md +850 -0
- package/.claude/agents/n8n/n8n-compliance-validator.md +685 -0
- package/.claude/agents/n8n/n8n-expression-validator.md +560 -0
- package/.claude/agents/n8n/n8n-integration-test.md +602 -0
- package/.claude/agents/n8n/n8n-monitoring-validator.md +589 -0
- package/.claude/agents/n8n/n8n-node-validator.md +455 -0
- package/.claude/agents/n8n/n8n-performance-tester.md +630 -0
- package/.claude/agents/n8n/n8n-security-auditor.md +786 -0
- package/.claude/agents/n8n/n8n-trigger-test.md +500 -0
- package/.claude/agents/n8n/n8n-unit-tester.md +633 -0
- package/.claude/agents/n8n/n8n-version-comparator.md +567 -0
- package/.claude/agents/n8n/n8n-workflow-executor.md +392 -0
- package/.claude/skills/n8n-expression-testing/SKILL.md +434 -0
- package/.claude/skills/n8n-integration-testing-patterns/SKILL.md +540 -0
- package/.claude/skills/n8n-security-testing/SKILL.md +599 -0
- package/.claude/skills/n8n-trigger-testing-strategies/SKILL.md +541 -0
- package/.claude/skills/n8n-workflow-testing-fundamentals/SKILL.md +447 -0
- package/CHANGELOG.md +111 -0
- package/README.md +7 -4
- package/dist/adapters/MemoryStoreAdapter.d.ts +75 -123
- package/dist/adapters/MemoryStoreAdapter.d.ts.map +1 -1
- package/dist/adapters/MemoryStoreAdapter.js +204 -219
- package/dist/adapters/MemoryStoreAdapter.js.map +1 -1
- package/dist/agents/AccessibilityAllyAgent.d.ts.map +1 -1
- package/dist/agents/AccessibilityAllyAgent.js +17 -1
- package/dist/agents/AccessibilityAllyAgent.js.map +1 -1
- package/dist/agents/BaseAgent.d.ts +18 -250
- package/dist/agents/BaseAgent.d.ts.map +1 -1
- package/dist/agents/BaseAgent.js +122 -520
- package/dist/agents/BaseAgent.js.map +1 -1
- package/dist/agents/n8n/N8nAPIClient.d.ts +121 -0
- package/dist/agents/n8n/N8nAPIClient.d.ts.map +1 -0
- package/dist/agents/n8n/N8nAPIClient.js +367 -0
- package/dist/agents/n8n/N8nAPIClient.js.map +1 -0
- package/dist/agents/n8n/N8nAuditPersistence.d.ts +120 -0
- package/dist/agents/n8n/N8nAuditPersistence.d.ts.map +1 -0
- package/dist/agents/n8n/N8nAuditPersistence.js +473 -0
- package/dist/agents/n8n/N8nAuditPersistence.js.map +1 -0
- package/dist/agents/n8n/N8nBDDScenarioTesterAgent.d.ts +159 -0
- package/dist/agents/n8n/N8nBDDScenarioTesterAgent.d.ts.map +1 -0
- package/dist/agents/n8n/N8nBDDScenarioTesterAgent.js +697 -0
- package/dist/agents/n8n/N8nBDDScenarioTesterAgent.js.map +1 -0
- package/dist/agents/n8n/N8nBaseAgent.d.ts +126 -0
- package/dist/agents/n8n/N8nBaseAgent.d.ts.map +1 -0
- package/dist/agents/n8n/N8nBaseAgent.js +446 -0
- package/dist/agents/n8n/N8nBaseAgent.js.map +1 -0
- package/dist/agents/n8n/N8nCIOrchestratorAgent.d.ts +164 -0
- package/dist/agents/n8n/N8nCIOrchestratorAgent.d.ts.map +1 -0
- package/dist/agents/n8n/N8nCIOrchestratorAgent.js +610 -0
- package/dist/agents/n8n/N8nCIOrchestratorAgent.js.map +1 -0
- package/dist/agents/n8n/N8nChaosTesterAgent.d.ts +205 -0
- package/dist/agents/n8n/N8nChaosTesterAgent.d.ts.map +1 -0
- package/dist/agents/n8n/N8nChaosTesterAgent.js +729 -0
- package/dist/agents/n8n/N8nChaosTesterAgent.js.map +1 -0
- package/dist/agents/n8n/N8nComplianceValidatorAgent.d.ts +228 -0
- package/dist/agents/n8n/N8nComplianceValidatorAgent.d.ts.map +1 -0
- package/dist/agents/n8n/N8nComplianceValidatorAgent.js +986 -0
- package/dist/agents/n8n/N8nComplianceValidatorAgent.js.map +1 -0
- package/dist/agents/n8n/N8nContractTesterAgent.d.ts +213 -0
- package/dist/agents/n8n/N8nContractTesterAgent.d.ts.map +1 -0
- package/dist/agents/n8n/N8nContractTesterAgent.js +989 -0
- package/dist/agents/n8n/N8nContractTesterAgent.js.map +1 -0
- package/dist/agents/n8n/N8nExpressionValidatorAgent.d.ts +99 -0
- package/dist/agents/n8n/N8nExpressionValidatorAgent.d.ts.map +1 -0
- package/dist/agents/n8n/N8nExpressionValidatorAgent.js +632 -0
- package/dist/agents/n8n/N8nExpressionValidatorAgent.js.map +1 -0
- package/dist/agents/n8n/N8nFailureModeTesterAgent.d.ts +238 -0
- package/dist/agents/n8n/N8nFailureModeTesterAgent.d.ts.map +1 -0
- package/dist/agents/n8n/N8nFailureModeTesterAgent.js +956 -0
- package/dist/agents/n8n/N8nFailureModeTesterAgent.js.map +1 -0
- package/dist/agents/n8n/N8nIdempotencyTesterAgent.d.ts +242 -0
- package/dist/agents/n8n/N8nIdempotencyTesterAgent.d.ts.map +1 -0
- package/dist/agents/n8n/N8nIdempotencyTesterAgent.js +992 -0
- package/dist/agents/n8n/N8nIdempotencyTesterAgent.js.map +1 -0
- package/dist/agents/n8n/N8nIntegrationTestAgent.d.ts +104 -0
- package/dist/agents/n8n/N8nIntegrationTestAgent.d.ts.map +1 -0
- package/dist/agents/n8n/N8nIntegrationTestAgent.js +653 -0
- package/dist/agents/n8n/N8nIntegrationTestAgent.js.map +1 -0
- package/dist/agents/n8n/N8nMonitoringValidatorAgent.d.ts +210 -0
- package/dist/agents/n8n/N8nMonitoringValidatorAgent.d.ts.map +1 -0
- package/dist/agents/n8n/N8nMonitoringValidatorAgent.js +669 -0
- package/dist/agents/n8n/N8nMonitoringValidatorAgent.js.map +1 -0
- package/dist/agents/n8n/N8nNodeValidatorAgent.d.ts +142 -0
- package/dist/agents/n8n/N8nNodeValidatorAgent.d.ts.map +1 -0
- package/dist/agents/n8n/N8nNodeValidatorAgent.js +1090 -0
- package/dist/agents/n8n/N8nNodeValidatorAgent.js.map +1 -0
- package/dist/agents/n8n/N8nPerformanceTesterAgent.d.ts +198 -0
- package/dist/agents/n8n/N8nPerformanceTesterAgent.d.ts.map +1 -0
- package/dist/agents/n8n/N8nPerformanceTesterAgent.js +653 -0
- package/dist/agents/n8n/N8nPerformanceTesterAgent.js.map +1 -0
- package/dist/agents/n8n/N8nReplayabilityTesterAgent.d.ts +245 -0
- package/dist/agents/n8n/N8nReplayabilityTesterAgent.d.ts.map +1 -0
- package/dist/agents/n8n/N8nReplayabilityTesterAgent.js +952 -0
- package/dist/agents/n8n/N8nReplayabilityTesterAgent.js.map +1 -0
- package/dist/agents/n8n/N8nSecretsHygieneAuditorAgent.d.ts +325 -0
- package/dist/agents/n8n/N8nSecretsHygieneAuditorAgent.d.ts.map +1 -0
- package/dist/agents/n8n/N8nSecretsHygieneAuditorAgent.js +1187 -0
- package/dist/agents/n8n/N8nSecretsHygieneAuditorAgent.js.map +1 -0
- package/dist/agents/n8n/N8nSecurityAuditorAgent.d.ts +91 -0
- package/dist/agents/n8n/N8nSecurityAuditorAgent.d.ts.map +1 -0
- package/dist/agents/n8n/N8nSecurityAuditorAgent.js +825 -0
- package/dist/agents/n8n/N8nSecurityAuditorAgent.js.map +1 -0
- package/dist/agents/n8n/N8nTestHarness.d.ts +131 -0
- package/dist/agents/n8n/N8nTestHarness.d.ts.map +1 -0
- package/dist/agents/n8n/N8nTestHarness.js +456 -0
- package/dist/agents/n8n/N8nTestHarness.js.map +1 -0
- package/dist/agents/n8n/N8nTriggerTestAgent.d.ts +119 -0
- package/dist/agents/n8n/N8nTriggerTestAgent.d.ts.map +1 -0
- package/dist/agents/n8n/N8nTriggerTestAgent.js +652 -0
- package/dist/agents/n8n/N8nTriggerTestAgent.js.map +1 -0
- package/dist/agents/n8n/N8nUnitTesterAgent.d.ts +130 -0
- package/dist/agents/n8n/N8nUnitTesterAgent.d.ts.map +1 -0
- package/dist/agents/n8n/N8nUnitTesterAgent.js +522 -0
- package/dist/agents/n8n/N8nUnitTesterAgent.js.map +1 -0
- package/dist/agents/n8n/N8nVersionComparatorAgent.d.ts +201 -0
- package/dist/agents/n8n/N8nVersionComparatorAgent.d.ts.map +1 -0
- package/dist/agents/n8n/N8nVersionComparatorAgent.js +645 -0
- package/dist/agents/n8n/N8nVersionComparatorAgent.js.map +1 -0
- package/dist/agents/n8n/N8nWorkflowExecutorAgent.d.ts +120 -0
- package/dist/agents/n8n/N8nWorkflowExecutorAgent.d.ts.map +1 -0
- package/dist/agents/n8n/N8nWorkflowExecutorAgent.js +347 -0
- package/dist/agents/n8n/N8nWorkflowExecutorAgent.js.map +1 -0
- package/dist/agents/n8n/index.d.ts +119 -0
- package/dist/agents/n8n/index.d.ts.map +1 -0
- package/dist/agents/n8n/index.js +298 -0
- package/dist/agents/n8n/index.js.map +1 -0
- package/dist/agents/n8n/types.d.ts +486 -0
- package/dist/agents/n8n/types.d.ts.map +1 -0
- package/dist/agents/n8n/types.js +8 -0
- package/dist/agents/n8n/types.js.map +1 -0
- package/dist/agents/utils/generators.d.ts +30 -0
- package/dist/agents/utils/generators.d.ts.map +1 -0
- package/dist/agents/utils/generators.js +44 -0
- package/dist/agents/utils/generators.js.map +1 -0
- package/dist/agents/utils/index.d.ts +10 -0
- package/dist/agents/utils/index.d.ts.map +1 -0
- package/dist/agents/utils/index.js +19 -0
- package/dist/agents/utils/index.js.map +1 -0
- package/dist/agents/utils/validation.d.ts +72 -0
- package/dist/agents/utils/validation.d.ts.map +1 -0
- package/dist/agents/utils/validation.js +75 -0
- package/dist/agents/utils/validation.js.map +1 -0
- package/dist/cli/init/agents.d.ts.map +1 -1
- package/dist/cli/init/agents.js +29 -0
- package/dist/cli/init/agents.js.map +1 -1
- package/dist/cli/init/skills.d.ts.map +1 -1
- package/dist/cli/init/skills.js +7 -1
- package/dist/cli/init/skills.js.map +1 -1
- package/dist/core/memory/HNSWVectorMemory.js +1 -1
- package/dist/core/memory/SwarmMemoryManager.d.ts +114 -90
- package/dist/core/memory/SwarmMemoryManager.d.ts.map +1 -1
- package/dist/core/memory/SwarmMemoryManager.js +277 -235
- package/dist/core/memory/SwarmMemoryManager.js.map +1 -1
- package/dist/learning/baselines/StandardTaskSuite.d.ts.map +1 -1
- package/dist/learning/baselines/StandardTaskSuite.js +38 -0
- package/dist/learning/baselines/StandardTaskSuite.js.map +1 -1
- package/dist/mcp/server-instructions.d.ts +1 -1
- package/dist/mcp/server-instructions.js +1 -1
- package/dist/types/memory-interfaces.d.ts +76 -68
- package/dist/types/memory-interfaces.d.ts.map +1 -1
- package/dist/types/memory-interfaces.js +3 -0
- package/dist/types/memory-interfaces.js.map +1 -1
- package/docs/reference/agents.md +91 -2
- package/docs/reference/skills.md +97 -2
- package/package.json +2 -2
|
@@ -0,0 +1,652 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* N8nTriggerTestAgent
|
|
4
|
+
*
|
|
5
|
+
* Tests n8n workflow triggers including:
|
|
6
|
+
* - Webhook triggers (authentication, payload validation)
|
|
7
|
+
* - Schedule/cron triggers
|
|
8
|
+
* - Polling triggers
|
|
9
|
+
* - Event-driven activation
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.N8nTriggerTestAgent = void 0;
|
|
13
|
+
const N8nBaseAgent_1 = require("./N8nBaseAgent");
|
|
14
|
+
const TRIGGER_TYPES = {
|
|
15
|
+
webhook: ['n8n-nodes-base.webhook', 'n8n-nodes-base.webhookTest'],
|
|
16
|
+
schedule: ['n8n-nodes-base.cron', 'n8n-nodes-base.schedule', 'n8n-nodes-base.scheduleTrigger'],
|
|
17
|
+
email: ['n8n-nodes-base.emailTrigger', 'n8n-nodes-base.emailReadImap'],
|
|
18
|
+
database: ['n8n-nodes-base.postgresTrigger', 'n8n-nodes-base.mysqlTrigger'],
|
|
19
|
+
messaging: [
|
|
20
|
+
'n8n-nodes-base.slackTrigger',
|
|
21
|
+
'n8n-nodes-base.telegramTrigger',
|
|
22
|
+
'n8n-nodes-base.discordTrigger',
|
|
23
|
+
],
|
|
24
|
+
file: ['n8n-nodes-base.localFileTrigger', 'n8n-nodes-base.s3Trigger'],
|
|
25
|
+
api: ['n8n-nodes-base.httpPollTrigger'],
|
|
26
|
+
};
|
|
27
|
+
class N8nTriggerTestAgent extends N8nBaseAgent_1.N8nBaseAgent {
|
|
28
|
+
constructor(config) {
|
|
29
|
+
const capabilities = [
|
|
30
|
+
{
|
|
31
|
+
name: 'trigger-identification',
|
|
32
|
+
version: '1.0.0',
|
|
33
|
+
description: 'Identify and classify workflow triggers',
|
|
34
|
+
parameters: {},
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: 'webhook-testing',
|
|
38
|
+
version: '1.0.0',
|
|
39
|
+
description: 'Test webhook trigger configurations',
|
|
40
|
+
parameters: {},
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
name: 'authentication-validation',
|
|
44
|
+
version: '1.0.0',
|
|
45
|
+
description: 'Validate trigger authentication settings',
|
|
46
|
+
parameters: {},
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: 'trigger-simulation',
|
|
50
|
+
version: '1.0.0',
|
|
51
|
+
description: 'Simulate trigger activation',
|
|
52
|
+
parameters: {},
|
|
53
|
+
},
|
|
54
|
+
];
|
|
55
|
+
super({
|
|
56
|
+
...config,
|
|
57
|
+
type: 'n8n-trigger-test',
|
|
58
|
+
capabilities: [...capabilities, ...(config.capabilities || [])],
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
async performTask(task) {
|
|
62
|
+
const triggerTask = task;
|
|
63
|
+
if (triggerTask.type !== 'trigger-test') {
|
|
64
|
+
throw new Error(`Unsupported task type: ${triggerTask.type}`);
|
|
65
|
+
}
|
|
66
|
+
return this.testTriggers(triggerTask.target, triggerTask.options);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Test all triggers in a workflow
|
|
70
|
+
*/
|
|
71
|
+
async testTriggers(workflowId, options) {
|
|
72
|
+
const workflow = await this.getWorkflow(workflowId);
|
|
73
|
+
// Identify triggers
|
|
74
|
+
const triggers = this.identifyTriggers(workflow);
|
|
75
|
+
// Run tests on each trigger
|
|
76
|
+
const testResults = [];
|
|
77
|
+
for (const trigger of triggers) {
|
|
78
|
+
// Configuration tests
|
|
79
|
+
testResults.push(...this.testTriggerConfiguration(trigger, workflow));
|
|
80
|
+
// Authentication tests
|
|
81
|
+
if (options?.testAuthentication) {
|
|
82
|
+
testResults.push(...this.testTriggerAuthentication(trigger));
|
|
83
|
+
}
|
|
84
|
+
// Payload tests (for webhooks)
|
|
85
|
+
if (trigger.type.includes('webhook') && options?.testPayloads) {
|
|
86
|
+
for (const payload of options.testPayloads) {
|
|
87
|
+
testResults.push(await this.testWebhookPayload(trigger, payload, workflowId));
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// Simulate trigger if requested
|
|
91
|
+
if (options?.simulateTriggers) {
|
|
92
|
+
testResults.push(await this.simulateTrigger(trigger, workflowId));
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
const result = {
|
|
96
|
+
workflowId,
|
|
97
|
+
triggers,
|
|
98
|
+
testResults,
|
|
99
|
+
summary: {
|
|
100
|
+
total: testResults.length,
|
|
101
|
+
passed: testResults.filter(r => r.actualResult === 'pass').length,
|
|
102
|
+
failed: testResults.filter(r => r.actualResult === 'fail').length,
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
// Store result
|
|
106
|
+
await this.storeTestResult(`trigger-test:${workflowId}`, result);
|
|
107
|
+
// Emit event
|
|
108
|
+
this.emitEvent('trigger.test.completed', {
|
|
109
|
+
workflowId,
|
|
110
|
+
triggersFound: triggers.length,
|
|
111
|
+
testsPassed: result.summary.passed,
|
|
112
|
+
testsFailed: result.summary.failed,
|
|
113
|
+
});
|
|
114
|
+
return result;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Identify all triggers in workflow
|
|
118
|
+
*/
|
|
119
|
+
identifyTriggers(workflow) {
|
|
120
|
+
const triggers = [];
|
|
121
|
+
for (const node of workflow.nodes) {
|
|
122
|
+
if (this.isTriggerNode(node)) {
|
|
123
|
+
triggers.push({
|
|
124
|
+
nodeId: node.id,
|
|
125
|
+
nodeName: node.name,
|
|
126
|
+
type: node.type,
|
|
127
|
+
configuration: node.parameters,
|
|
128
|
+
authentication: this.getAuthenticationType(node),
|
|
129
|
+
isSecure: this.isSecureTrigger(node),
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return triggers;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Check if node is a trigger
|
|
137
|
+
*/
|
|
138
|
+
isTriggerNode(node) {
|
|
139
|
+
const allTriggerTypes = Object.values(TRIGGER_TYPES).flat();
|
|
140
|
+
return (allTriggerTypes.some(t => node.type.includes(t)) ||
|
|
141
|
+
node.type.toLowerCase().includes('trigger'));
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Get trigger authentication type
|
|
145
|
+
*/
|
|
146
|
+
getAuthenticationType(node) {
|
|
147
|
+
const params = node.parameters;
|
|
148
|
+
if (params.authentication) {
|
|
149
|
+
return params.authentication;
|
|
150
|
+
}
|
|
151
|
+
if (params.headerAuth) {
|
|
152
|
+
return 'headerAuth';
|
|
153
|
+
}
|
|
154
|
+
if (params.basicAuth) {
|
|
155
|
+
return 'basicAuth';
|
|
156
|
+
}
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Check if trigger is secure
|
|
161
|
+
*/
|
|
162
|
+
isSecureTrigger(node) {
|
|
163
|
+
const auth = this.getAuthenticationType(node);
|
|
164
|
+
// Webhook without auth is insecure
|
|
165
|
+
if (node.type.includes('webhook') && (!auth || auth === 'none')) {
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
return true;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Test trigger configuration
|
|
172
|
+
*/
|
|
173
|
+
testTriggerConfiguration(trigger, workflow) {
|
|
174
|
+
const tests = [];
|
|
175
|
+
const startTime = Date.now();
|
|
176
|
+
// Test: Trigger has required configuration
|
|
177
|
+
const hasRequiredConfig = this.hasRequiredTriggerConfig(trigger);
|
|
178
|
+
tests.push({
|
|
179
|
+
triggerId: trigger.nodeId,
|
|
180
|
+
testName: 'Required configuration present',
|
|
181
|
+
input: null,
|
|
182
|
+
expectedBehavior: 'All required parameters configured',
|
|
183
|
+
actualResult: hasRequiredConfig ? 'pass' : 'fail',
|
|
184
|
+
duration: Date.now() - startTime,
|
|
185
|
+
errorMessage: hasRequiredConfig ? undefined : 'Missing required configuration',
|
|
186
|
+
});
|
|
187
|
+
// Test: Trigger is connected to downstream nodes
|
|
188
|
+
const isConnected = this.isTriggerConnected(trigger, workflow);
|
|
189
|
+
tests.push({
|
|
190
|
+
triggerId: trigger.nodeId,
|
|
191
|
+
testName: 'Trigger connected to workflow',
|
|
192
|
+
input: null,
|
|
193
|
+
expectedBehavior: 'Trigger has downstream connections',
|
|
194
|
+
actualResult: isConnected ? 'pass' : 'fail',
|
|
195
|
+
duration: Date.now() - startTime,
|
|
196
|
+
errorMessage: isConnected ? undefined : 'Trigger not connected to any nodes',
|
|
197
|
+
});
|
|
198
|
+
// Test: Security check
|
|
199
|
+
tests.push({
|
|
200
|
+
triggerId: trigger.nodeId,
|
|
201
|
+
testName: 'Security configuration',
|
|
202
|
+
input: null,
|
|
203
|
+
expectedBehavior: 'Trigger is securely configured',
|
|
204
|
+
actualResult: trigger.isSecure ? 'pass' : 'fail',
|
|
205
|
+
duration: Date.now() - startTime,
|
|
206
|
+
errorMessage: trigger.isSecure ? undefined : 'Trigger lacks authentication',
|
|
207
|
+
});
|
|
208
|
+
return tests;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Test trigger authentication
|
|
212
|
+
*/
|
|
213
|
+
testTriggerAuthentication(trigger) {
|
|
214
|
+
const tests = [];
|
|
215
|
+
const startTime = Date.now();
|
|
216
|
+
// Only test webhooks for authentication
|
|
217
|
+
if (!trigger.type.includes('webhook')) {
|
|
218
|
+
return tests;
|
|
219
|
+
}
|
|
220
|
+
// Test: Has authentication configured
|
|
221
|
+
tests.push({
|
|
222
|
+
triggerId: trigger.nodeId,
|
|
223
|
+
testName: 'Authentication enabled',
|
|
224
|
+
input: null,
|
|
225
|
+
expectedBehavior: 'Webhook has authentication',
|
|
226
|
+
actualResult: trigger.authentication && trigger.authentication !== 'none' ? 'pass' : 'fail',
|
|
227
|
+
duration: Date.now() - startTime,
|
|
228
|
+
});
|
|
229
|
+
// Test: Authentication type is secure
|
|
230
|
+
const secureAuthTypes = ['headerAuth', 'basicAuth', 'jwtAuth', 'oauth2'];
|
|
231
|
+
const isSecureAuth = trigger.authentication
|
|
232
|
+
? secureAuthTypes.includes(trigger.authentication)
|
|
233
|
+
: false;
|
|
234
|
+
tests.push({
|
|
235
|
+
triggerId: trigger.nodeId,
|
|
236
|
+
testName: 'Secure authentication type',
|
|
237
|
+
input: null,
|
|
238
|
+
expectedBehavior: 'Uses secure authentication method',
|
|
239
|
+
actualResult: isSecureAuth ? 'pass' : 'fail',
|
|
240
|
+
duration: Date.now() - startTime,
|
|
241
|
+
errorMessage: isSecureAuth ? undefined : `Authentication type "${trigger.authentication}" may not be secure`,
|
|
242
|
+
});
|
|
243
|
+
return tests;
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Test webhook with payload via ACTUAL HTTP invocation
|
|
247
|
+
*
|
|
248
|
+
* PRODUCTION TESTING: Tries both test-mode and production URLs
|
|
249
|
+
* - Test URL: /webhook-test/<workflowId>/<path> (doesn't require active workflow)
|
|
250
|
+
* - Production URL: /webhook/<path> (requires active workflow)
|
|
251
|
+
*/
|
|
252
|
+
async testWebhookPayload(trigger, payload, workflowId) {
|
|
253
|
+
const startTime = Date.now();
|
|
254
|
+
// Get both webhook URLs
|
|
255
|
+
const urls = this.getAllWebhookUrls(trigger, workflowId);
|
|
256
|
+
// Try test webhook URL first (doesn't require active workflow)
|
|
257
|
+
if (urls.test) {
|
|
258
|
+
try {
|
|
259
|
+
const result = await this.invokeWebhookHttp(trigger, urls.test, payload, startTime);
|
|
260
|
+
// If test webhook succeeds, return that result
|
|
261
|
+
if (result.actualResult === 'pass') {
|
|
262
|
+
return {
|
|
263
|
+
...result,
|
|
264
|
+
testName: `Webhook test (test-mode): ${JSON.stringify(payload).substring(0, 50)}...`,
|
|
265
|
+
metadata: { ...result.metadata, urlType: 'test' },
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
catch {
|
|
270
|
+
// Test webhook failed, try production webhook
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
// Try production webhook URL (requires workflow to be active)
|
|
274
|
+
if (urls.production) {
|
|
275
|
+
try {
|
|
276
|
+
const result = await this.invokeWebhookHttp(trigger, urls.production, payload, startTime);
|
|
277
|
+
return {
|
|
278
|
+
...result,
|
|
279
|
+
metadata: { ...result.metadata, urlType: 'production' },
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
catch {
|
|
283
|
+
// Production webhook also failed, fall through to direct execution
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
// Fallback: Execute workflow directly with test data
|
|
287
|
+
try {
|
|
288
|
+
const execution = await this.executeWorkflow(workflowId, payload, {
|
|
289
|
+
waitForCompletion: true,
|
|
290
|
+
timeout: 10000,
|
|
291
|
+
});
|
|
292
|
+
return {
|
|
293
|
+
triggerId: trigger.nodeId,
|
|
294
|
+
testName: `Payload test (direct execution): ${JSON.stringify(payload).substring(0, 50)}...`,
|
|
295
|
+
input: payload,
|
|
296
|
+
expectedBehavior: 'Workflow executes successfully with payload',
|
|
297
|
+
actualResult: execution.status === 'success' ? 'pass' : 'fail',
|
|
298
|
+
executionId: execution.id,
|
|
299
|
+
duration: Date.now() - startTime,
|
|
300
|
+
errorMessage: execution.status !== 'success'
|
|
301
|
+
? execution.data?.resultData?.error?.message
|
|
302
|
+
: undefined,
|
|
303
|
+
metadata: { urlType: 'direct-execution' },
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
catch (error) {
|
|
307
|
+
return {
|
|
308
|
+
triggerId: trigger.nodeId,
|
|
309
|
+
testName: `Payload test (direct execution): ${JSON.stringify(payload).substring(0, 50)}...`,
|
|
310
|
+
input: payload,
|
|
311
|
+
expectedBehavior: 'Workflow executes successfully with payload',
|
|
312
|
+
actualResult: 'error',
|
|
313
|
+
duration: Date.now() - startTime,
|
|
314
|
+
errorMessage: error instanceof Error ? error.message : 'Execution failed',
|
|
315
|
+
metadata: { urlType: 'direct-execution', triedUrls: urls },
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Invoke webhook via REAL HTTP request
|
|
321
|
+
* This tests the actual webhook endpoint, authentication, and payload handling
|
|
322
|
+
*/
|
|
323
|
+
async invokeWebhookHttp(trigger, webhookUrl, payload, startTime) {
|
|
324
|
+
const httpMethod = trigger.configuration.httpMethod || 'POST';
|
|
325
|
+
try {
|
|
326
|
+
// Build headers with authentication if configured
|
|
327
|
+
const headers = {
|
|
328
|
+
'Content-Type': 'application/json',
|
|
329
|
+
};
|
|
330
|
+
// Add authentication headers based on trigger config
|
|
331
|
+
if (trigger.authentication === 'headerAuth' && trigger.configuration.headerAuthName) {
|
|
332
|
+
headers[trigger.configuration.headerAuthName] =
|
|
333
|
+
trigger.configuration.headerAuthValue || 'test-value';
|
|
334
|
+
}
|
|
335
|
+
if (trigger.authentication === 'basicAuth') {
|
|
336
|
+
const username = trigger.configuration.basicAuthUser || '';
|
|
337
|
+
const password = trigger.configuration.basicAuthPassword || '';
|
|
338
|
+
headers['Authorization'] = 'Basic ' + Buffer.from(`${username}:${password}`).toString('base64');
|
|
339
|
+
}
|
|
340
|
+
// Make the actual HTTP request
|
|
341
|
+
const response = await fetch(webhookUrl, {
|
|
342
|
+
method: httpMethod,
|
|
343
|
+
headers,
|
|
344
|
+
body: httpMethod !== 'GET' ? JSON.stringify(payload) : undefined,
|
|
345
|
+
});
|
|
346
|
+
const responseData = await response.text();
|
|
347
|
+
let responseJson;
|
|
348
|
+
try {
|
|
349
|
+
responseJson = JSON.parse(responseData);
|
|
350
|
+
}
|
|
351
|
+
catch {
|
|
352
|
+
responseJson = responseData;
|
|
353
|
+
}
|
|
354
|
+
// Success is 2xx status code
|
|
355
|
+
const isSuccess = response.status >= 200 && response.status < 300;
|
|
356
|
+
return {
|
|
357
|
+
triggerId: trigger.nodeId,
|
|
358
|
+
testName: `HTTP ${httpMethod} webhook: ${webhookUrl}`,
|
|
359
|
+
input: { payload, headers: Object.keys(headers) },
|
|
360
|
+
expectedBehavior: 'Webhook responds successfully to HTTP request',
|
|
361
|
+
actualResult: isSuccess ? 'pass' : 'fail',
|
|
362
|
+
duration: Date.now() - startTime,
|
|
363
|
+
errorMessage: !isSuccess ? `HTTP ${response.status}: ${responseData.substring(0, 200)}` : undefined,
|
|
364
|
+
metadata: {
|
|
365
|
+
httpStatus: response.status,
|
|
366
|
+
responseData: responseJson,
|
|
367
|
+
method: httpMethod,
|
|
368
|
+
url: webhookUrl,
|
|
369
|
+
},
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
catch (error) {
|
|
373
|
+
return {
|
|
374
|
+
triggerId: trigger.nodeId,
|
|
375
|
+
testName: `HTTP ${httpMethod} webhook: ${webhookUrl}`,
|
|
376
|
+
input: payload,
|
|
377
|
+
expectedBehavior: 'Webhook endpoint is reachable',
|
|
378
|
+
actualResult: 'error',
|
|
379
|
+
duration: Date.now() - startTime,
|
|
380
|
+
errorMessage: error instanceof Error ? error.message : 'HTTP request failed',
|
|
381
|
+
metadata: {
|
|
382
|
+
method: httpMethod,
|
|
383
|
+
url: webhookUrl,
|
|
384
|
+
error: error instanceof Error ? error.name : 'Unknown',
|
|
385
|
+
},
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Test webhook without authentication (security test)
|
|
391
|
+
* Verifies that protected webhooks reject unauthenticated requests
|
|
392
|
+
*/
|
|
393
|
+
async testWebhookWithoutAuth(trigger, payload) {
|
|
394
|
+
const startTime = Date.now();
|
|
395
|
+
const webhookUrl = this.getWebhookUrl(trigger);
|
|
396
|
+
if (!webhookUrl) {
|
|
397
|
+
return {
|
|
398
|
+
triggerId: trigger.nodeId,
|
|
399
|
+
testName: 'Unauthenticated access test',
|
|
400
|
+
input: payload,
|
|
401
|
+
expectedBehavior: 'Cannot determine webhook URL',
|
|
402
|
+
actualResult: 'error',
|
|
403
|
+
duration: Date.now() - startTime,
|
|
404
|
+
errorMessage: 'Webhook URL not available',
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
try {
|
|
408
|
+
// Make request WITHOUT authentication headers
|
|
409
|
+
const response = await fetch(webhookUrl, {
|
|
410
|
+
method: trigger.configuration.httpMethod || 'POST',
|
|
411
|
+
headers: { 'Content-Type': 'application/json' },
|
|
412
|
+
body: JSON.stringify(payload),
|
|
413
|
+
});
|
|
414
|
+
// If webhook requires auth, it should reject (4xx status)
|
|
415
|
+
const hasAuth = trigger.authentication && trigger.authentication !== 'none';
|
|
416
|
+
if (hasAuth) {
|
|
417
|
+
// Protected webhook should reject unauthenticated requests
|
|
418
|
+
const rejectedCorrectly = response.status === 401 || response.status === 403;
|
|
419
|
+
return {
|
|
420
|
+
triggerId: trigger.nodeId,
|
|
421
|
+
testName: 'Unauthenticated access blocked',
|
|
422
|
+
input: payload,
|
|
423
|
+
expectedBehavior: 'Protected webhook rejects request without credentials',
|
|
424
|
+
actualResult: rejectedCorrectly ? 'pass' : 'fail',
|
|
425
|
+
duration: Date.now() - startTime,
|
|
426
|
+
errorMessage: !rejectedCorrectly
|
|
427
|
+
? `Expected 401/403 but got ${response.status} - webhook may be vulnerable`
|
|
428
|
+
: undefined,
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
else {
|
|
432
|
+
// Unprotected webhook - just check it responds
|
|
433
|
+
return {
|
|
434
|
+
triggerId: trigger.nodeId,
|
|
435
|
+
testName: 'Public webhook accessible',
|
|
436
|
+
input: payload,
|
|
437
|
+
expectedBehavior: 'Public webhook accepts request',
|
|
438
|
+
actualResult: response.status < 500 ? 'pass' : 'fail',
|
|
439
|
+
duration: Date.now() - startTime,
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
catch (error) {
|
|
444
|
+
return {
|
|
445
|
+
triggerId: trigger.nodeId,
|
|
446
|
+
testName: 'Unauthenticated access test',
|
|
447
|
+
input: payload,
|
|
448
|
+
expectedBehavior: 'Webhook endpoint responds',
|
|
449
|
+
actualResult: 'error',
|
|
450
|
+
duration: Date.now() - startTime,
|
|
451
|
+
errorMessage: error instanceof Error ? error.message : 'Request failed',
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
/**
|
|
456
|
+
* Test webhook with malformed payloads (robustness test)
|
|
457
|
+
*/
|
|
458
|
+
async testWebhookPayloadValidation(trigger) {
|
|
459
|
+
const webhookUrl = this.getWebhookUrl(trigger);
|
|
460
|
+
if (!webhookUrl)
|
|
461
|
+
return [];
|
|
462
|
+
const malformedPayloads = [
|
|
463
|
+
{ name: 'Empty body', payload: null },
|
|
464
|
+
{ name: 'Invalid JSON string', payload: 'not-json' },
|
|
465
|
+
{ name: 'Array instead of object', payload: [1, 2, 3] },
|
|
466
|
+
{ name: 'Nested nulls', payload: { a: { b: null, c: undefined } } },
|
|
467
|
+
{ name: 'Very large payload', payload: { data: 'x'.repeat(10000) } },
|
|
468
|
+
];
|
|
469
|
+
const results = [];
|
|
470
|
+
for (const test of malformedPayloads) {
|
|
471
|
+
const startTime = Date.now();
|
|
472
|
+
try {
|
|
473
|
+
const response = await fetch(webhookUrl, {
|
|
474
|
+
method: trigger.configuration.httpMethod || 'POST',
|
|
475
|
+
headers: { 'Content-Type': 'application/json' },
|
|
476
|
+
body: typeof test.payload === 'string' ? test.payload : JSON.stringify(test.payload),
|
|
477
|
+
});
|
|
478
|
+
// Should not crash (5xx) on bad input - graceful handling expected
|
|
479
|
+
results.push({
|
|
480
|
+
triggerId: trigger.nodeId,
|
|
481
|
+
testName: `Payload validation: ${test.name}`,
|
|
482
|
+
input: test.payload,
|
|
483
|
+
expectedBehavior: 'Webhook handles malformed payload gracefully',
|
|
484
|
+
actualResult: response.status < 500 ? 'pass' : 'fail',
|
|
485
|
+
duration: Date.now() - startTime,
|
|
486
|
+
errorMessage: response.status >= 500 ? `Server error ${response.status}` : undefined,
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
catch (error) {
|
|
490
|
+
results.push({
|
|
491
|
+
triggerId: trigger.nodeId,
|
|
492
|
+
testName: `Payload validation: ${test.name}`,
|
|
493
|
+
input: test.payload,
|
|
494
|
+
expectedBehavior: 'Webhook handles malformed payload gracefully',
|
|
495
|
+
actualResult: 'error',
|
|
496
|
+
duration: Date.now() - startTime,
|
|
497
|
+
errorMessage: error instanceof Error ? error.message : 'Request failed',
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
return results;
|
|
502
|
+
}
|
|
503
|
+
/**
|
|
504
|
+
* Simulate trigger activation
|
|
505
|
+
*/
|
|
506
|
+
async simulateTrigger(trigger, workflowId) {
|
|
507
|
+
const startTime = Date.now();
|
|
508
|
+
// Generate test data based on trigger type
|
|
509
|
+
const testData = this.generateTriggerTestData(trigger);
|
|
510
|
+
try {
|
|
511
|
+
const execution = await this.executeWorkflow(workflowId, testData, {
|
|
512
|
+
waitForCompletion: true,
|
|
513
|
+
timeout: 15000,
|
|
514
|
+
});
|
|
515
|
+
return {
|
|
516
|
+
triggerId: trigger.nodeId,
|
|
517
|
+
testName: `Simulate ${trigger.type} trigger`,
|
|
518
|
+
input: testData,
|
|
519
|
+
expectedBehavior: 'Trigger activates workflow successfully',
|
|
520
|
+
actualResult: execution.status === 'success' ? 'pass' : 'fail',
|
|
521
|
+
executionId: execution.id,
|
|
522
|
+
duration: Date.now() - startTime,
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
catch (error) {
|
|
526
|
+
return {
|
|
527
|
+
triggerId: trigger.nodeId,
|
|
528
|
+
testName: `Simulate ${trigger.type} trigger`,
|
|
529
|
+
input: testData,
|
|
530
|
+
expectedBehavior: 'Trigger activates workflow successfully',
|
|
531
|
+
actualResult: 'error',
|
|
532
|
+
duration: Date.now() - startTime,
|
|
533
|
+
errorMessage: error instanceof Error ? error.message : 'Simulation failed',
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
/**
|
|
538
|
+
* Check if trigger has required configuration
|
|
539
|
+
*/
|
|
540
|
+
hasRequiredTriggerConfig(trigger) {
|
|
541
|
+
if (trigger.type.includes('webhook')) {
|
|
542
|
+
return !!(trigger.configuration.httpMethod && trigger.configuration.path);
|
|
543
|
+
}
|
|
544
|
+
if (trigger.type.includes('cron') || trigger.type.includes('schedule')) {
|
|
545
|
+
return !!(trigger.configuration.rule || trigger.configuration.interval);
|
|
546
|
+
}
|
|
547
|
+
// Default to true for other trigger types
|
|
548
|
+
return true;
|
|
549
|
+
}
|
|
550
|
+
/**
|
|
551
|
+
* Check if trigger is connected to downstream nodes
|
|
552
|
+
*/
|
|
553
|
+
isTriggerConnected(trigger, workflow) {
|
|
554
|
+
return !!workflow.connections[trigger.nodeName];
|
|
555
|
+
}
|
|
556
|
+
/**
|
|
557
|
+
* Generate test data for trigger type
|
|
558
|
+
*/
|
|
559
|
+
generateTriggerTestData(trigger) {
|
|
560
|
+
if (trigger.type.includes('webhook')) {
|
|
561
|
+
return {
|
|
562
|
+
headers: { 'Content-Type': 'application/json' },
|
|
563
|
+
body: { test: true, timestamp: Date.now() },
|
|
564
|
+
query: {},
|
|
565
|
+
};
|
|
566
|
+
}
|
|
567
|
+
if (trigger.type.includes('email')) {
|
|
568
|
+
return {
|
|
569
|
+
from: 'test@example.com',
|
|
570
|
+
subject: 'Test Email',
|
|
571
|
+
text: 'This is a test email',
|
|
572
|
+
date: new Date().toISOString(),
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
if (trigger.type.includes('slack')) {
|
|
576
|
+
return {
|
|
577
|
+
type: 'message',
|
|
578
|
+
channel: '#test',
|
|
579
|
+
user: 'U123456',
|
|
580
|
+
text: 'Test message',
|
|
581
|
+
ts: Date.now().toString(),
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
// Default test data
|
|
585
|
+
return {
|
|
586
|
+
triggered: true,
|
|
587
|
+
timestamp: Date.now(),
|
|
588
|
+
source: 'test',
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
/**
|
|
592
|
+
* Get webhook URL for a trigger
|
|
593
|
+
*
|
|
594
|
+
* n8n webhook URL patterns:
|
|
595
|
+
* - Production (workflow active): /webhook/<path>
|
|
596
|
+
* - Test mode: /webhook-test/<workflowId>/<path>
|
|
597
|
+
* - With UUID path: /webhook/<uuid>
|
|
598
|
+
*
|
|
599
|
+
* Returns both production and test URLs for comprehensive testing
|
|
600
|
+
*/
|
|
601
|
+
getWebhookUrl(trigger, workflowId) {
|
|
602
|
+
if (!trigger.type.includes('webhook')) {
|
|
603
|
+
return null;
|
|
604
|
+
}
|
|
605
|
+
const path = trigger.configuration.path;
|
|
606
|
+
if (!path)
|
|
607
|
+
return null;
|
|
608
|
+
// Remove leading slash if present
|
|
609
|
+
const cleanPath = path.replace(/^\//, '');
|
|
610
|
+
// Return production webhook URL
|
|
611
|
+
// For test mode, use getWebhookTestUrl()
|
|
612
|
+
return `${this.n8nConfig.baseUrl}/webhook/${cleanPath}`;
|
|
613
|
+
}
|
|
614
|
+
/**
|
|
615
|
+
* Get webhook TEST URL for a trigger
|
|
616
|
+
* Test mode webhooks don't require the workflow to be active
|
|
617
|
+
*/
|
|
618
|
+
getWebhookTestUrl(trigger, workflowId) {
|
|
619
|
+
if (!trigger.type.includes('webhook')) {
|
|
620
|
+
return null;
|
|
621
|
+
}
|
|
622
|
+
const path = trigger.configuration.path;
|
|
623
|
+
if (!path)
|
|
624
|
+
return null;
|
|
625
|
+
// Remove leading slash if present
|
|
626
|
+
const cleanPath = path.replace(/^\//, '');
|
|
627
|
+
// n8n test webhook format: /webhook-test/<workflowId>/<path>
|
|
628
|
+
return `${this.n8nConfig.baseUrl}/webhook-test/${workflowId}/${cleanPath}`;
|
|
629
|
+
}
|
|
630
|
+
/**
|
|
631
|
+
* Get all possible webhook URLs for comprehensive testing
|
|
632
|
+
*/
|
|
633
|
+
getAllWebhookUrls(trigger, workflowId) {
|
|
634
|
+
return {
|
|
635
|
+
production: this.getWebhookUrl(trigger, workflowId),
|
|
636
|
+
test: this.getWebhookTestUrl(trigger, workflowId),
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
/**
|
|
640
|
+
* Get trigger type category
|
|
641
|
+
*/
|
|
642
|
+
getTriggerCategory(trigger) {
|
|
643
|
+
for (const [category, types] of Object.entries(TRIGGER_TYPES)) {
|
|
644
|
+
if (types.some(t => trigger.type.includes(t))) {
|
|
645
|
+
return category;
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
return 'other';
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
exports.N8nTriggerTestAgent = N8nTriggerTestAgent;
|
|
652
|
+
//# sourceMappingURL=N8nTriggerTestAgent.js.map
|