@blucca/n8n-check 0.0.0-stage → 0.1.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/Dockerfile +10 -0
- package/LICENSE +21 -0
- package/README.md +323 -2
- package/bin/n8n-check.mjs +62 -0
- package/examples/agregado-enrichment/README.md +68 -0
- package/examples/agregado-enrichment/n8n-synthetic +11 -0
- package/examples/agregado-enrichment/observed-results.json +806 -0
- package/examples/agregado-enrichment/prepare.mjs +39 -0
- package/examples/identity-projection/README.md +39 -0
- package/examples/identity-projection/case.json +33 -0
- package/examples/identity-projection/observed-results.json +117 -0
- package/examples/identity-projection/workflow.json +71 -0
- package/examples/identity-projection/wrong-id.json +71 -0
- package/examples/morsof-invoice-history/README.md +77 -0
- package/examples/morsof-invoice-history/UPSTREAM-LICENSE +21 -0
- package/examples/morsof-invoice-history/n8n-datatable.cjs +28 -0
- package/examples/morsof-invoice-history/observed-results.json +113 -0
- package/examples/morsof-invoice-history/prepare.mjs +57 -0
- package/examples/morsof-invoice-history/replay.mjs +35 -0
- package/examples/morsof-invoice-history/run.sh +19 -0
- package/examples/object-body/broken.json +74 -0
- package/examples/object-body/case.json +81 -0
- package/examples/object-body/fixed.json +74 -0
- package/examples/object-body/observed-results.json +449 -0
- package/examples/object-body/retry-case.json +112 -0
- package/examples/object-body/retry.json +77 -0
- package/examples/propfind-body/README.md +75 -0
- package/examples/propfind-body/case.json +51 -0
- package/examples/propfind-body/no-body-control.json +96 -0
- package/examples/propfind-body/observed-results.json +307 -0
- package/examples/propfind-body/workflow.json +96 -0
- package/examples/raw-response/README.md +71 -0
- package/examples/raw-response/file-case.json +54 -0
- package/examples/raw-response/json-body.json +58 -0
- package/examples/raw-response/json-case.json +52 -0
- package/examples/raw-response/observed-results.json +383 -0
- package/examples/raw-response/raw-file.json +88 -0
- package/examples/raw-response/raw-json.json +58 -0
- package/examples/raw-response/raw-text.json +58 -0
- package/examples/raw-response/text-case.json +50 -0
- package/examples/retry-isolation/README.md +71 -0
- package/examples/retry-isolation/direct-case.json +112 -0
- package/examples/retry-isolation/direct.json +101 -0
- package/examples/retry-isolation/editor-template.json +168 -0
- package/examples/retry-isolation/http-batching-case.json +112 -0
- package/examples/retry-isolation/http-batching.json +107 -0
- package/examples/retry-isolation/loop-case.json +117 -0
- package/examples/retry-isolation/loop.json +133 -0
- package/examples/retry-isolation/observed-results.json +155 -0
- package/examples/silent-filter/README.md +57 -0
- package/examples/silent-filter/broken.json +96 -0
- package/examples/silent-filter/case.json +120 -0
- package/examples/silent-filter/fixed.json +96 -0
- package/examples/silent-filter/observed-results.json +273 -0
- package/examples/stop-after/README.md +66 -0
- package/examples/stop-after/broken.json +134 -0
- package/examples/stop-after/case.json +34 -0
- package/examples/stop-after/fixed.json +134 -0
- package/examples/stop-after/observed-results.json +162 -0
- package/examples/vapi-tool-contract/README.md +67 -0
- package/examples/vapi-tool-contract/backend-503.case.json +113 -0
- package/examples/vapi-tool-contract/empty-orders.case.json +113 -0
- package/examples/vapi-tool-contract/observed-results.json +234 -0
- package/examples/vapi-tool-contract/success.case.json +118 -0
- package/examples/vapi-tool-contract/workflow.json +143 -0
- package/examples/vapi-tool-contract/wrong-id.json +143 -0
- package/examples/version-matrix/README.md +65 -0
- package/examples/version-matrix/observed-results.json +307 -0
- package/examples/version-matrix/upgrade-check.yml +32 -0
- package/package.json +41 -4
- package/src/action.mjs +108 -0
- package/src/case-draft.mjs +163 -0
- package/src/local-bundle.mjs +154 -0
- package/src/mock.mjs +72 -0
- package/src/report.mjs +73 -0
- package/src/runner.mjs +111 -0
- package/src/version.mjs +3 -0
- package/src/workflow.mjs +140 -0
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
{
|
|
2
|
+
"description": "Synthetic full Sheets-to-Slack exports; only the fixture and business transformation run. Real n8n, loopback-only network.",
|
|
3
|
+
"files": {
|
|
4
|
+
"fixed.json": "a20e75366252c5cd165821eeca8b54671b69df51242fe6e6c3f847bc7d651ec6",
|
|
5
|
+
"broken.json": "899bb0f39ce8f494b9f44e9b4b79874f93065b4bc379fab970aad90ee907ed14",
|
|
6
|
+
"case.json": "81fe5dcca0b261ca97fb7cff1d27ce5a177ffbb61aeec7c75cb1c63fb28f0ded"
|
|
7
|
+
},
|
|
8
|
+
"runs": {
|
|
9
|
+
"fixed": {
|
|
10
|
+
"runnerVersion": "0.1.5",
|
|
11
|
+
"n8nVersion": "2.41.7",
|
|
12
|
+
"nodeVersion": "v26.10.0",
|
|
13
|
+
"startedAt": "2026-10-06T22:13:13.515Z",
|
|
14
|
+
"status": "passed",
|
|
15
|
+
"exitCode": 0,
|
|
16
|
+
"checks": [
|
|
17
|
+
{
|
|
18
|
+
"name": "n8n execution succeeds",
|
|
19
|
+
"passed": true,
|
|
20
|
+
"actual": {
|
|
21
|
+
"cliExitCode": 0,
|
|
22
|
+
"lastNode": "Prepare notification",
|
|
23
|
+
"executionFound": true
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"name": "Prepare notification: output 0",
|
|
28
|
+
"passed": true,
|
|
29
|
+
"expected": [
|
|
30
|
+
{
|
|
31
|
+
"invoiceId": "INV-42",
|
|
32
|
+
"text": "Invoice INV-42: $650.00"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"invoiceId": "INV-43",
|
|
36
|
+
"text": "Invoice INV-43: $120.50"
|
|
37
|
+
}
|
|
38
|
+
],
|
|
39
|
+
"actual": [
|
|
40
|
+
{
|
|
41
|
+
"invoiceId": "INV-42",
|
|
42
|
+
"text": "Invoice INV-42: $650.00"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"invoiceId": "INV-43",
|
|
46
|
+
"text": "Invoice INV-43: $120.50"
|
|
47
|
+
}
|
|
48
|
+
]
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"name": "mock: all requests match routes",
|
|
52
|
+
"passed": true,
|
|
53
|
+
"actual": []
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"name": "mock: request limits and server",
|
|
57
|
+
"passed": true,
|
|
58
|
+
"actual": []
|
|
59
|
+
}
|
|
60
|
+
],
|
|
61
|
+
"network": {
|
|
62
|
+
"mode": "loopback-only",
|
|
63
|
+
"enforced": true,
|
|
64
|
+
"interfaces": [
|
|
65
|
+
"lo"
|
|
66
|
+
]
|
|
67
|
+
},
|
|
68
|
+
"preparation": {
|
|
69
|
+
"inputNode": "Read invoices",
|
|
70
|
+
"changes": [
|
|
71
|
+
{
|
|
72
|
+
"node": "Read invoices",
|
|
73
|
+
"change": "replace with fixture JSON items"
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"node": "Prepare notification",
|
|
77
|
+
"change": "stop this branch after the node; remove outgoing connections; retain independently reachable branches"
|
|
78
|
+
}
|
|
79
|
+
],
|
|
80
|
+
"omittedNodes": [
|
|
81
|
+
"Start",
|
|
82
|
+
"Notify team"
|
|
83
|
+
]
|
|
84
|
+
},
|
|
85
|
+
"requests": []
|
|
86
|
+
},
|
|
87
|
+
"broken": {
|
|
88
|
+
"runnerVersion": "0.1.5",
|
|
89
|
+
"n8nVersion": "2.41.7",
|
|
90
|
+
"nodeVersion": "v26.10.0",
|
|
91
|
+
"startedAt": "2026-10-06T22:13:22.755Z",
|
|
92
|
+
"status": "failed",
|
|
93
|
+
"exitCode": 1,
|
|
94
|
+
"checks": [
|
|
95
|
+
{
|
|
96
|
+
"name": "n8n execution succeeds",
|
|
97
|
+
"passed": true,
|
|
98
|
+
"actual": {
|
|
99
|
+
"cliExitCode": 0,
|
|
100
|
+
"lastNode": "Prepare notification",
|
|
101
|
+
"executionFound": true
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
"name": "Prepare notification: output 0",
|
|
106
|
+
"passed": false,
|
|
107
|
+
"expected": [
|
|
108
|
+
{
|
|
109
|
+
"invoiceId": "INV-42",
|
|
110
|
+
"text": "Invoice INV-42: $650.00"
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
"invoiceId": "INV-43",
|
|
114
|
+
"text": "Invoice INV-43: $120.50"
|
|
115
|
+
}
|
|
116
|
+
],
|
|
117
|
+
"actual": [
|
|
118
|
+
{
|
|
119
|
+
"invoiceId": "INV-42",
|
|
120
|
+
"text": "Invoice INV-42: $650.00"
|
|
121
|
+
}
|
|
122
|
+
]
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
"name": "mock: all requests match routes",
|
|
126
|
+
"passed": true,
|
|
127
|
+
"actual": []
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
"name": "mock: request limits and server",
|
|
131
|
+
"passed": true,
|
|
132
|
+
"actual": []
|
|
133
|
+
}
|
|
134
|
+
],
|
|
135
|
+
"network": {
|
|
136
|
+
"mode": "loopback-only",
|
|
137
|
+
"enforced": true,
|
|
138
|
+
"interfaces": [
|
|
139
|
+
"lo"
|
|
140
|
+
]
|
|
141
|
+
},
|
|
142
|
+
"preparation": {
|
|
143
|
+
"inputNode": "Read invoices",
|
|
144
|
+
"changes": [
|
|
145
|
+
{
|
|
146
|
+
"node": "Read invoices",
|
|
147
|
+
"change": "replace with fixture JSON items"
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
"node": "Prepare notification",
|
|
151
|
+
"change": "stop this branch after the node; remove outgoing connections; retain independently reachable branches"
|
|
152
|
+
}
|
|
153
|
+
],
|
|
154
|
+
"omittedNodes": [
|
|
155
|
+
"Start",
|
|
156
|
+
"Notify team"
|
|
157
|
+
]
|
|
158
|
+
},
|
|
159
|
+
"requests": []
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Vapi tool calls: preserve the caller, surface the backend failure
|
|
2
|
+
|
|
3
|
+
**Two tool calls in one request. Two different customers. Every response keeps its own `toolCallId`.**
|
|
4
|
+
|
|
5
|
+
This synthetic backend check looks up customer orders through a real n8n HTTP Request node. It checks outgoing customer/ID pairs, response serialization, and the complete Vapi `results` envelope.
|
|
6
|
+
|
|
7
|
+
| Scenario | Backend responses | Expected tool response |
|
|
8
|
+
| --- | --- | --- |
|
|
9
|
+
| `success` | Two HTTP 200 responses with different orders | Two distinct IDs and the correct serialized order data |
|
|
10
|
+
| `backend-503` | Customer A succeeds; customer B returns 503 | A retains its result; B receives an explicit `error` string |
|
|
11
|
+
| `empty-orders` | Customer B returns `{"orders":[]}` | B receives the successful string `"{\"orders\":[]}"` |
|
|
12
|
+
| `wrong-id.json` with the unchanged success case | Both backend requests succeed | **FAIL**: the second result incorrectly repeats the first tool-call ID |
|
|
13
|
+
|
|
14
|
+
## The workflow
|
|
15
|
+
|
|
16
|
+
`Vapi webhook → Split tool calls → Lookup orders → Build Vapi response → Respond to Vapi`
|
|
17
|
+
|
|
18
|
+
- [`workflow.json`](workflow.json) is an importable synthetic workflow with a Webhook input and Respond to Webhook configured for HTTP 200.
|
|
19
|
+
- Each case injects the Webhook node's `body.message` JSON and stops after **Build Vapi response**. The real Split, HTTP Request, and response-building nodes execute; the fixture replaces webhook delivery and the response node sits beyond this test boundary.
|
|
20
|
+
- HTTP Request uses **Include Response Headers and Status** and **Never Error** to preserve the backend's status for response mapping. Local HTTP mocks return the declared 200/503 responses.
|
|
21
|
+
- Response construction follows n8n item pairing back to each split tool call. Each route checks exactly one request and its exact customer/`toolCallId` body.
|
|
22
|
+
- Success uses `JSON.stringify` for a single-line `result`. HTTP 503 maps to `error`, preserving the sibling success. Empty order data stays an explicit successful result.
|
|
23
|
+
|
|
24
|
+
## Run from your browser
|
|
25
|
+
|
|
26
|
+
1. [Fork n8n-check](https://github.com/blucca/n8n-check/fork), then enable **Actions** in your fork.
|
|
27
|
+
2. Open **Try a Vapi tool-call contract → Run workflow**.
|
|
28
|
+
3. Choose `backend-503` with `workflow` to check a successful result beside an explicit tool error. Choose `success` with `wrong-id` to see the regression turn the job red.
|
|
29
|
+
4. Read the check summary; download `vapi-tool-contract-results` for JSON/JUnit.
|
|
30
|
+
|
|
31
|
+
This Action runs the selected case with the released **v0.1.7** runner and **n8n 2.41.7**, using loopback-only Docker networking and synthetic data. GitHub Actions usage follows your account plan. [Action source](../../.github/workflows/try-vapi-tool-contract.yml).
|
|
32
|
+
|
|
33
|
+
## Run with real n8n
|
|
34
|
+
|
|
35
|
+
From the repository root, with Node.js 24+ and n8n installed:
|
|
36
|
+
|
|
37
|
+
```sh
|
|
38
|
+
# Optional: select an existing installation.
|
|
39
|
+
# export N8N_BINARY=/absolute/path/to/n8n
|
|
40
|
+
for scenario in success backend-503 empty-orders; do
|
|
41
|
+
node bin/n8n-check.mjs examples/vapi-tool-contract/workflow.json \
|
|
42
|
+
"examples/vapi-tool-contract/$scenario.case.json" \
|
|
43
|
+
--allow-network --out "temp/vapi-tool-contract/$scenario" || exit $?
|
|
44
|
+
done
|
|
45
|
+
|
|
46
|
+
# Negative control: expect exit 1 and a failed Build Vapi response assertion.
|
|
47
|
+
node bin/n8n-check.mjs examples/vapi-tool-contract/wrong-id.json \
|
|
48
|
+
examples/vapi-tool-contract/success.case.json \
|
|
49
|
+
--allow-network --out temp/vapi-tool-contract/wrong-id
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
The bundled source and fixtures use synthetic values. The local command enables host networking; the runner redirects both HTTP routes to its loopback mock. Use the repository's [Docker route](../../README.md#try-a-failure-then-its-fix) for an isolated runtime. Run local CLI cases sequentially: n8n's task broker uses a shared default port.
|
|
53
|
+
|
|
54
|
+
Recorded execution: **n8n 2.41.7, n8n-check 0.1.6**, three passing cases, each with eight checks and two HTTP requests. The wrong-ID control retains successful execution and both correct backend requests while failing the output assertion. [`observed-results.json`](observed-results.json) contains compact check results, mock traces, and the actual emitted envelopes.
|
|
55
|
+
|
|
56
|
+
**[Completed GitHub Actions run](https://github.com/blucca/n8n-check/actions/runs/37558788945):** `backend-503`, 8/8 checks, two HTTP requests, loopback-only networking. The result preserves customer A’s order and returns customer B’s tool error.
|
|
57
|
+
|
|
58
|
+
## Verified Vapi contract and scope
|
|
59
|
+
|
|
60
|
+
Official references, read 2026-10-07:
|
|
61
|
+
|
|
62
|
+
- [Custom tools — request and response format](https://docs.vapi.ai/tools/custom-tools): `message.toolCallList[].function.name` and object-valued `function.arguments`; `results[]` associates each response with `toolCallId`; `result` and `error` are single-line strings. Each entry uses one of those fields. Multiple results may appear in any order. The server returns HTTP 200 for successful and tool-error envelopes.
|
|
63
|
+
- [Server URL events — tool calls](https://docs.vapi.ai/server-url/events): documents tool-call delivery and response correlation. Its introductory flattened `name`/`parameters` example is a separate input shape; this fixture follows the custom-tools function/arguments example above.
|
|
64
|
+
|
|
65
|
+
This pack covers one tool, `lookupOrders`, a non-empty batch with unique call IDs, object-valued arguments, synthetic customer IDs, and backend HTTP responses containing JSON. The assertions preserve this sample's response order as well as the Vapi ID mapping. HTTP error handling here covers returned statuses; transport timeouts/DNS errors, malformed tool arguments, webhook authentication/delivery, and actual HTTP 200 response delivery require additional integration cases. The sample's Webhook response configuration is supplied for importing and subsequent endpoint testing.
|
|
66
|
+
|
|
67
|
+
Voice/audio, Twilio delivery, ElevenLabs speech, model decisions, live Vapi acceptance, and production credentials are separate live-agent acceptance work. A buyer's export, tool schema, example call trace, and agreed behavior provide the next test inputs.
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"name": "Vapi tool contract: backend-503",
|
|
4
|
+
"input": {
|
|
5
|
+
"node": "Vapi webhook",
|
|
6
|
+
"items": [
|
|
7
|
+
{
|
|
8
|
+
"body": {
|
|
9
|
+
"message": {
|
|
10
|
+
"type": "tool-calls",
|
|
11
|
+
"toolCallList": [
|
|
12
|
+
{
|
|
13
|
+
"id": "call-order-A",
|
|
14
|
+
"type": "function",
|
|
15
|
+
"function": {
|
|
16
|
+
"name": "lookupOrders",
|
|
17
|
+
"arguments": {
|
|
18
|
+
"customerId": "customer-A"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"id": "call-order-B",
|
|
24
|
+
"type": "function",
|
|
25
|
+
"function": {
|
|
26
|
+
"name": "lookupOrders",
|
|
27
|
+
"arguments": {
|
|
28
|
+
"customerId": "customer-B"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
]
|
|
37
|
+
},
|
|
38
|
+
"stopAfter": "Build Vapi response",
|
|
39
|
+
"mocks": [
|
|
40
|
+
{
|
|
41
|
+
"node": "Lookup orders",
|
|
42
|
+
"url": "/lookup/{{ $json.customerId }}",
|
|
43
|
+
"routes": [
|
|
44
|
+
{
|
|
45
|
+
"method": "POST",
|
|
46
|
+
"path": "/lookup/customer-A",
|
|
47
|
+
"responses": [
|
|
48
|
+
{
|
|
49
|
+
"status": 200,
|
|
50
|
+
"json": {
|
|
51
|
+
"orders": [
|
|
52
|
+
{
|
|
53
|
+
"id": "order-A-101",
|
|
54
|
+
"status": "shipped"
|
|
55
|
+
}
|
|
56
|
+
]
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
],
|
|
60
|
+
"expect": {
|
|
61
|
+
"count": 1,
|
|
62
|
+
"bodies": [
|
|
63
|
+
{
|
|
64
|
+
"customerId": "customer-A",
|
|
65
|
+
"toolCallId": "call-order-A"
|
|
66
|
+
}
|
|
67
|
+
]
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"method": "POST",
|
|
72
|
+
"path": "/lookup/customer-B",
|
|
73
|
+
"responses": [
|
|
74
|
+
{
|
|
75
|
+
"status": 503,
|
|
76
|
+
"json": {
|
|
77
|
+
"message": "upstream overloaded"
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
],
|
|
81
|
+
"expect": {
|
|
82
|
+
"count": 1,
|
|
83
|
+
"bodies": [
|
|
84
|
+
{
|
|
85
|
+
"customerId": "customer-B",
|
|
86
|
+
"toolCallId": "call-order-B"
|
|
87
|
+
}
|
|
88
|
+
]
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
]
|
|
92
|
+
}
|
|
93
|
+
],
|
|
94
|
+
"assertions": [
|
|
95
|
+
{
|
|
96
|
+
"node": "Build Vapi response",
|
|
97
|
+
"equals": [
|
|
98
|
+
{
|
|
99
|
+
"results": [
|
|
100
|
+
{
|
|
101
|
+
"toolCallId": "call-order-A",
|
|
102
|
+
"result": "{\"orders\":[{\"id\":\"order-A-101\",\"status\":\"shipped\"}]}"
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
"toolCallId": "call-order-B",
|
|
106
|
+
"error": "Order service temporarily unavailable. Please try again later."
|
|
107
|
+
}
|
|
108
|
+
]
|
|
109
|
+
}
|
|
110
|
+
]
|
|
111
|
+
}
|
|
112
|
+
]
|
|
113
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"name": "Vapi tool contract: empty-orders",
|
|
4
|
+
"input": {
|
|
5
|
+
"node": "Vapi webhook",
|
|
6
|
+
"items": [
|
|
7
|
+
{
|
|
8
|
+
"body": {
|
|
9
|
+
"message": {
|
|
10
|
+
"type": "tool-calls",
|
|
11
|
+
"toolCallList": [
|
|
12
|
+
{
|
|
13
|
+
"id": "call-order-A",
|
|
14
|
+
"type": "function",
|
|
15
|
+
"function": {
|
|
16
|
+
"name": "lookupOrders",
|
|
17
|
+
"arguments": {
|
|
18
|
+
"customerId": "customer-A"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"id": "call-order-B",
|
|
24
|
+
"type": "function",
|
|
25
|
+
"function": {
|
|
26
|
+
"name": "lookupOrders",
|
|
27
|
+
"arguments": {
|
|
28
|
+
"customerId": "customer-B"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
]
|
|
37
|
+
},
|
|
38
|
+
"stopAfter": "Build Vapi response",
|
|
39
|
+
"mocks": [
|
|
40
|
+
{
|
|
41
|
+
"node": "Lookup orders",
|
|
42
|
+
"url": "/lookup/{{ $json.customerId }}",
|
|
43
|
+
"routes": [
|
|
44
|
+
{
|
|
45
|
+
"method": "POST",
|
|
46
|
+
"path": "/lookup/customer-A",
|
|
47
|
+
"responses": [
|
|
48
|
+
{
|
|
49
|
+
"status": 200,
|
|
50
|
+
"json": {
|
|
51
|
+
"orders": [
|
|
52
|
+
{
|
|
53
|
+
"id": "order-A-101",
|
|
54
|
+
"status": "shipped"
|
|
55
|
+
}
|
|
56
|
+
]
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
],
|
|
60
|
+
"expect": {
|
|
61
|
+
"count": 1,
|
|
62
|
+
"bodies": [
|
|
63
|
+
{
|
|
64
|
+
"customerId": "customer-A",
|
|
65
|
+
"toolCallId": "call-order-A"
|
|
66
|
+
}
|
|
67
|
+
]
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"method": "POST",
|
|
72
|
+
"path": "/lookup/customer-B",
|
|
73
|
+
"responses": [
|
|
74
|
+
{
|
|
75
|
+
"status": 200,
|
|
76
|
+
"json": {
|
|
77
|
+
"orders": []
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
],
|
|
81
|
+
"expect": {
|
|
82
|
+
"count": 1,
|
|
83
|
+
"bodies": [
|
|
84
|
+
{
|
|
85
|
+
"customerId": "customer-B",
|
|
86
|
+
"toolCallId": "call-order-B"
|
|
87
|
+
}
|
|
88
|
+
]
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
]
|
|
92
|
+
}
|
|
93
|
+
],
|
|
94
|
+
"assertions": [
|
|
95
|
+
{
|
|
96
|
+
"node": "Build Vapi response",
|
|
97
|
+
"equals": [
|
|
98
|
+
{
|
|
99
|
+
"results": [
|
|
100
|
+
{
|
|
101
|
+
"toolCallId": "call-order-A",
|
|
102
|
+
"result": "{\"orders\":[{\"id\":\"order-A-101\",\"status\":\"shipped\"}]}"
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
"toolCallId": "call-order-B",
|
|
106
|
+
"result": "{\"orders\":[]}"
|
|
107
|
+
}
|
|
108
|
+
]
|
|
109
|
+
}
|
|
110
|
+
]
|
|
111
|
+
}
|
|
112
|
+
]
|
|
113
|
+
}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
{
|
|
2
|
+
"n8nVersion": "2.41.7",
|
|
3
|
+
"runnerVersion": "0.1.6",
|
|
4
|
+
"networkMode": "host-network",
|
|
5
|
+
"boundary": "Fixture at Vapi webhook; real Split tool calls, Lookup orders HTTP Request and Build Vapi response; stop before Respond to Vapi. Local synthetic HTTP mocks.",
|
|
6
|
+
"runs": [
|
|
7
|
+
{
|
|
8
|
+
"scenario": "success",
|
|
9
|
+
"workflow": "workflow.json",
|
|
10
|
+
"case": "success.case.json",
|
|
11
|
+
"startedAt": "2026-10-07T01:44:22.166Z",
|
|
12
|
+
"status": "passed",
|
|
13
|
+
"exitCode": 0,
|
|
14
|
+
"checksPassed": 8,
|
|
15
|
+
"checksTotal": 8,
|
|
16
|
+
"failedChecks": [],
|
|
17
|
+
"requests": [
|
|
18
|
+
{
|
|
19
|
+
"node": "Lookup orders",
|
|
20
|
+
"method": "POST",
|
|
21
|
+
"path": "/lookup/customer-A",
|
|
22
|
+
"body": {
|
|
23
|
+
"customerId": "customer-A",
|
|
24
|
+
"toolCallId": "call-order-A"
|
|
25
|
+
},
|
|
26
|
+
"status": 200
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"node": "Lookup orders",
|
|
30
|
+
"method": "POST",
|
|
31
|
+
"path": "/lookup/customer-B",
|
|
32
|
+
"body": {
|
|
33
|
+
"customerId": "customer-B",
|
|
34
|
+
"toolCallId": "call-order-B"
|
|
35
|
+
},
|
|
36
|
+
"status": 200
|
|
37
|
+
}
|
|
38
|
+
],
|
|
39
|
+
"actualResponse": {
|
|
40
|
+
"results": [
|
|
41
|
+
{
|
|
42
|
+
"toolCallId": "call-order-A",
|
|
43
|
+
"result": "{\"orders\":[{\"id\":\"order-A-101\",\"status\":\"shipped\"}]}"
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"toolCallId": "call-order-B",
|
|
47
|
+
"result": "{\"orders\":[{\"id\":\"order-B-202\",\"status\":\"processing\"}]}"
|
|
48
|
+
}
|
|
49
|
+
]
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"scenario": "backend-503",
|
|
54
|
+
"workflow": "workflow.json",
|
|
55
|
+
"case": "backend-503.case.json",
|
|
56
|
+
"startedAt": "2026-10-07T01:44:47.031Z",
|
|
57
|
+
"status": "passed",
|
|
58
|
+
"exitCode": 0,
|
|
59
|
+
"checksPassed": 8,
|
|
60
|
+
"checksTotal": 8,
|
|
61
|
+
"failedChecks": [],
|
|
62
|
+
"requests": [
|
|
63
|
+
{
|
|
64
|
+
"node": "Lookup orders",
|
|
65
|
+
"method": "POST",
|
|
66
|
+
"path": "/lookup/customer-A",
|
|
67
|
+
"body": {
|
|
68
|
+
"customerId": "customer-A",
|
|
69
|
+
"toolCallId": "call-order-A"
|
|
70
|
+
},
|
|
71
|
+
"status": 200
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
"node": "Lookup orders",
|
|
75
|
+
"method": "POST",
|
|
76
|
+
"path": "/lookup/customer-B",
|
|
77
|
+
"body": {
|
|
78
|
+
"customerId": "customer-B",
|
|
79
|
+
"toolCallId": "call-order-B"
|
|
80
|
+
},
|
|
81
|
+
"status": 503
|
|
82
|
+
}
|
|
83
|
+
],
|
|
84
|
+
"actualResponse": {
|
|
85
|
+
"results": [
|
|
86
|
+
{
|
|
87
|
+
"toolCallId": "call-order-A",
|
|
88
|
+
"result": "{\"orders\":[{\"id\":\"order-A-101\",\"status\":\"shipped\"}]}"
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
"toolCallId": "call-order-B",
|
|
92
|
+
"error": "Order service temporarily unavailable. Please try again later."
|
|
93
|
+
}
|
|
94
|
+
]
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"scenario": "empty-orders",
|
|
99
|
+
"workflow": "workflow.json",
|
|
100
|
+
"case": "empty-orders.case.json",
|
|
101
|
+
"startedAt": "2026-10-07T01:44:29.107Z",
|
|
102
|
+
"status": "passed",
|
|
103
|
+
"exitCode": 0,
|
|
104
|
+
"checksPassed": 8,
|
|
105
|
+
"checksTotal": 8,
|
|
106
|
+
"failedChecks": [],
|
|
107
|
+
"requests": [
|
|
108
|
+
{
|
|
109
|
+
"node": "Lookup orders",
|
|
110
|
+
"method": "POST",
|
|
111
|
+
"path": "/lookup/customer-A",
|
|
112
|
+
"body": {
|
|
113
|
+
"customerId": "customer-A",
|
|
114
|
+
"toolCallId": "call-order-A"
|
|
115
|
+
},
|
|
116
|
+
"status": 200
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
"node": "Lookup orders",
|
|
120
|
+
"method": "POST",
|
|
121
|
+
"path": "/lookup/customer-B",
|
|
122
|
+
"body": {
|
|
123
|
+
"customerId": "customer-B",
|
|
124
|
+
"toolCallId": "call-order-B"
|
|
125
|
+
},
|
|
126
|
+
"status": 200
|
|
127
|
+
}
|
|
128
|
+
],
|
|
129
|
+
"actualResponse": {
|
|
130
|
+
"results": [
|
|
131
|
+
{
|
|
132
|
+
"toolCallId": "call-order-A",
|
|
133
|
+
"result": "{\"orders\":[{\"id\":\"order-A-101\",\"status\":\"shipped\"}]}"
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
"toolCallId": "call-order-B",
|
|
137
|
+
"result": "{\"orders\":[]}"
|
|
138
|
+
}
|
|
139
|
+
]
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
"scenario": "wrong-id",
|
|
144
|
+
"workflow": "wrong-id.json",
|
|
145
|
+
"case": "success.case.json",
|
|
146
|
+
"startedAt": "2026-10-07T01:44:56.484Z",
|
|
147
|
+
"status": "failed",
|
|
148
|
+
"exitCode": 1,
|
|
149
|
+
"checksPassed": 7,
|
|
150
|
+
"checksTotal": 8,
|
|
151
|
+
"failedChecks": [
|
|
152
|
+
{
|
|
153
|
+
"name": "Build Vapi response: output 0",
|
|
154
|
+
"passed": false,
|
|
155
|
+
"expected": [
|
|
156
|
+
{
|
|
157
|
+
"results": [
|
|
158
|
+
{
|
|
159
|
+
"toolCallId": "call-order-A",
|
|
160
|
+
"result": "{\"orders\":[{\"id\":\"order-A-101\",\"status\":\"shipped\"}]}"
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
"toolCallId": "call-order-B",
|
|
164
|
+
"result": "{\"orders\":[{\"id\":\"order-B-202\",\"status\":\"processing\"}]}"
|
|
165
|
+
}
|
|
166
|
+
]
|
|
167
|
+
}
|
|
168
|
+
],
|
|
169
|
+
"actual": [
|
|
170
|
+
{
|
|
171
|
+
"results": [
|
|
172
|
+
{
|
|
173
|
+
"toolCallId": "call-order-A",
|
|
174
|
+
"result": "{\"orders\":[{\"id\":\"order-A-101\",\"status\":\"shipped\"}]}"
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
"toolCallId": "call-order-A",
|
|
178
|
+
"result": "{\"orders\":[{\"id\":\"order-B-202\",\"status\":\"processing\"}]}"
|
|
179
|
+
}
|
|
180
|
+
]
|
|
181
|
+
}
|
|
182
|
+
]
|
|
183
|
+
}
|
|
184
|
+
],
|
|
185
|
+
"requests": [
|
|
186
|
+
{
|
|
187
|
+
"node": "Lookup orders",
|
|
188
|
+
"method": "POST",
|
|
189
|
+
"path": "/lookup/customer-A",
|
|
190
|
+
"body": {
|
|
191
|
+
"customerId": "customer-A",
|
|
192
|
+
"toolCallId": "call-order-A"
|
|
193
|
+
},
|
|
194
|
+
"status": 200
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
"node": "Lookup orders",
|
|
198
|
+
"method": "POST",
|
|
199
|
+
"path": "/lookup/customer-B",
|
|
200
|
+
"body": {
|
|
201
|
+
"customerId": "customer-B",
|
|
202
|
+
"toolCallId": "call-order-B"
|
|
203
|
+
},
|
|
204
|
+
"status": 200
|
|
205
|
+
}
|
|
206
|
+
],
|
|
207
|
+
"actualResponse": {
|
|
208
|
+
"results": [
|
|
209
|
+
{
|
|
210
|
+
"toolCallId": "call-order-A",
|
|
211
|
+
"result": "{\"orders\":[{\"id\":\"order-A-101\",\"status\":\"shipped\"}]}"
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
"toolCallId": "call-order-A",
|
|
215
|
+
"result": "{\"orders\":[{\"id\":\"order-B-202\",\"status\":\"processing\"}]}"
|
|
216
|
+
}
|
|
217
|
+
]
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
],
|
|
221
|
+
"ci": {
|
|
222
|
+
"url": "https://github.com/blucca/n8n-check/actions/runs/37558788945",
|
|
223
|
+
"sourceCommit": "fe78330",
|
|
224
|
+
"scenario": "backend-503",
|
|
225
|
+
"workflow": "workflow.json",
|
|
226
|
+
"n8nVersion": "2.41.7",
|
|
227
|
+
"runnerVersion": "0.1.6",
|
|
228
|
+
"networkMode": "loopback-only",
|
|
229
|
+
"checksPassed": 8,
|
|
230
|
+
"checksTotal": 8,
|
|
231
|
+
"requests": 2,
|
|
232
|
+
"exitCode": 0
|
|
233
|
+
}
|
|
234
|
+
}
|