@blucca/n8n-check 0.0.0-stage → 0.1.6

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.
Files changed (70) hide show
  1. package/Dockerfile +10 -0
  2. package/LICENSE +21 -0
  3. package/README.md +319 -2
  4. package/bin/n8n-check.mjs +62 -0
  5. package/examples/agregado-enrichment/README.md +68 -0
  6. package/examples/agregado-enrichment/n8n-synthetic +11 -0
  7. package/examples/agregado-enrichment/observed-results.json +806 -0
  8. package/examples/agregado-enrichment/prepare.mjs +39 -0
  9. package/examples/identity-projection/README.md +39 -0
  10. package/examples/identity-projection/case.json +33 -0
  11. package/examples/identity-projection/observed-results.json +117 -0
  12. package/examples/identity-projection/workflow.json +71 -0
  13. package/examples/identity-projection/wrong-id.json +71 -0
  14. package/examples/morsof-invoice-history/README.md +77 -0
  15. package/examples/morsof-invoice-history/UPSTREAM-LICENSE +21 -0
  16. package/examples/morsof-invoice-history/n8n-datatable.cjs +28 -0
  17. package/examples/morsof-invoice-history/observed-results.json +113 -0
  18. package/examples/morsof-invoice-history/prepare.mjs +57 -0
  19. package/examples/morsof-invoice-history/replay.mjs +35 -0
  20. package/examples/morsof-invoice-history/run.sh +19 -0
  21. package/examples/object-body/broken.json +74 -0
  22. package/examples/object-body/case.json +81 -0
  23. package/examples/object-body/fixed.json +74 -0
  24. package/examples/object-body/observed-results.json +449 -0
  25. package/examples/object-body/retry-case.json +112 -0
  26. package/examples/object-body/retry.json +77 -0
  27. package/examples/propfind-body/README.md +75 -0
  28. package/examples/propfind-body/case.json +51 -0
  29. package/examples/propfind-body/no-body-control.json +96 -0
  30. package/examples/propfind-body/observed-results.json +307 -0
  31. package/examples/propfind-body/workflow.json +96 -0
  32. package/examples/raw-response/README.md +71 -0
  33. package/examples/raw-response/file-case.json +54 -0
  34. package/examples/raw-response/json-body.json +58 -0
  35. package/examples/raw-response/json-case.json +52 -0
  36. package/examples/raw-response/observed-results.json +383 -0
  37. package/examples/raw-response/raw-file.json +88 -0
  38. package/examples/raw-response/raw-json.json +58 -0
  39. package/examples/raw-response/raw-text.json +58 -0
  40. package/examples/raw-response/text-case.json +50 -0
  41. package/examples/retry-isolation/README.md +71 -0
  42. package/examples/retry-isolation/direct-case.json +112 -0
  43. package/examples/retry-isolation/direct.json +101 -0
  44. package/examples/retry-isolation/editor-template.json +168 -0
  45. package/examples/retry-isolation/http-batching-case.json +112 -0
  46. package/examples/retry-isolation/http-batching.json +107 -0
  47. package/examples/retry-isolation/loop-case.json +117 -0
  48. package/examples/retry-isolation/loop.json +133 -0
  49. package/examples/retry-isolation/observed-results.json +155 -0
  50. package/examples/silent-filter/README.md +57 -0
  51. package/examples/silent-filter/broken.json +96 -0
  52. package/examples/silent-filter/case.json +120 -0
  53. package/examples/silent-filter/fixed.json +96 -0
  54. package/examples/silent-filter/observed-results.json +273 -0
  55. package/examples/stop-after/README.md +66 -0
  56. package/examples/stop-after/broken.json +134 -0
  57. package/examples/stop-after/case.json +34 -0
  58. package/examples/stop-after/fixed.json +134 -0
  59. package/examples/stop-after/observed-results.json +162 -0
  60. package/examples/version-matrix/README.md +65 -0
  61. package/examples/version-matrix/observed-results.json +307 -0
  62. package/examples/version-matrix/upgrade-check.yml +32 -0
  63. package/package.json +41 -4
  64. package/src/action.mjs +108 -0
  65. package/src/case-draft.mjs +163 -0
  66. package/src/mock.mjs +72 -0
  67. package/src/report.mjs +73 -0
  68. package/src/runner.mjs +111 -0
  69. package/src/version.mjs +3 -0
  70. 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,65 @@
1
+ # Check the workflow before upgrading n8n
2
+
3
+ **Keep the workflow and expected behavior fixed. Change the n8n version. Inspect what changes.**
4
+
5
+ n8n-check's Action accepts `n8n-version`, starting with v0.1.4. A GitHub matrix runs your case against the release you deploy today and the release you want to adopt. Each job produces its own version-labelled summary and JSON/JUnit artifacts.
6
+
7
+ ## Use your own workflow
8
+
9
+ 1. Export the workflow and [build a case](https://blucca.github.io/n8n-check/) with synthetic inputs, HTTP responses, and expected behavior.
10
+ 2. Copy [`upgrade-check.yml`](upgrade-check.yml) to `.github/workflows/n8n-upgrade.yml` in your repository.
11
+ 3. Set the two `matrix.n8n` versions and the `workflow` / `case` file paths.
12
+ 4. Commit and inspect both jobs. `fail-fast: false` preserves the other result when one version fails.
13
+
14
+ The supplied workflow treats every failed contract as a failed job. Both artifact names include the version, so the reports stay separate. Review a mismatch against your intended behavior before changing the expectation. A failing deployed baseline identifies an existing workflow or engine behavior worth addressing before the upgrade.
15
+
16
+ The HTTP responses remain controlled local fixtures. Real credentials, provider behavior, triggers, and production data remain separate integration checks. Node types and parameters must exist in each selected n8n release. Data Table initialization adapters in other examples retain their own explicit version bounds.
17
+
18
+ ## A real version difference: a missing PROPFIND body
19
+
20
+ The author of [n8n #39395](https://github.com/n8n-io/n8n/issues/39395) supplied a Nextcloud workflow on **2.38.4**, where PROPFIND silently drops the request's XML body. Upstream [PR #24151](https://github.com/n8n-io/n8n/pull/24151) adds body handling for this method; **2.41.7** preserves it.
21
+
22
+ The [workflow and exact-body case](../propfind-body/) use the author's HTTP Request 4.5 parameters and complete 698-character diagnostic XML. The fixture replaces connection information with synthetic values and redirects the URL to a local capture server. This compares the actual released engines with the same export and expected body.
23
+
24
+ ### Observed on October 7, 2026
25
+
26
+ | Same export and expected XML | n8n 2.38.4 | n8n 2.41.7 |
27
+ |---|---|---|
28
+ | Engine execution | Success | Success |
29
+ | PROPFIND requests | 1 | 1 |
30
+ | Captured body | Empty (`null`) | All 698 characters |
31
+ | Contract | **5/6 · exit 1** | **6/6 · exit 0** |
32
+
33
+ [Actual GitHub matrix run](https://github.com/blucca/n8n-check/actions/runs/37524798259) · [Versioned JSON record, requests and assertions](observed-results.json)
34
+
35
+ Both jobs used n8n-check 0.1.4, the same file hashes, and loopback-only networking. The record also includes a separate n8n **2.37.9** smoke check: the object-valued JSON request case passes 6/6 with two complete requests and outputs. This checks the selected runtime path; each project's own nodes and contracts supply its upgrade coverage.
36
+
37
+ To run the recorded comparison in your fork:
38
+
39
+ 1. Fork n8n-check and enable Actions.
40
+ 2. Choose **Compare n8n versions on a real PROPFIND regression → Run workflow**.
41
+ 3. Inspect the contract step summary and download `propfind-n8n-2.38.4` and `propfind-n8n-2.41.7`.
42
+
43
+ [Comparison workflow source](../../.github/workflows/compare-versions.yml)
44
+
45
+ This demonstration allows the known older-version contract failure, then asserts the specific missing-body difference. The copyable `upgrade-check.yml` above keeps ordinary strict pass/fail gating for your project.
46
+
47
+ ## Local version selection
48
+
49
+ ```sh
50
+ git clone --branch v0.1.4 https://github.com/blucca/n8n-check.git
51
+ cd n8n-check
52
+ docker build --build-arg N8N_VERSION=2.38.4 -t n8n-check:2.38.4 .
53
+ docker run --rm --network none -v "$PWD:/work" n8n-check:2.38.4 \
54
+ examples/propfind-body/workflow.json examples/propfind-body/case.json \
55
+ --out /work/results/2.38.4
56
+ # Expected: exit 1, with the request-body assertion failing.
57
+ ```
58
+
59
+ Repeat with `2.41.7` for the complete-body contract. Linux users with a UID other than 1000 can add `--user "$(id -u):$(id -g)"` to `docker run`.
60
+
61
+ Runtime images come from `ghcr.io/n8n-io/n8n`. The default is 2.41.7; supply an exact available release. Every report records the actual `n8nVersion`, `runnerVersion`, and `nodeVersion`.
62
+
63
+ ---
64
+
65
+ **Have a client upgrade to ship?** The [$650 implementation pilot](https://blucca.github.io/n8n-release-checks/) supplies an agreed fixture pack and handoff for one workflow or slice. Send the deployed version, target version, workflow purpose, and release date to [belgialucca@gmail.com](mailto:belgialucca@gmail.com?subject=n8n%20upgrade%20check).
@@ -0,0 +1,307 @@
1
+ {
2
+ "recordedAt": "2026-10-06T20:17:00Z",
3
+ "sourceCommit": "77e496f65105d5633374a65f63da990ab76ff4de",
4
+ "runUrl": "https://github.com/blucca/n8n-check/actions/runs/37524798259",
5
+ "sourceIssue": "https://github.com/n8n-io/n8n/issues/39395",
6
+ "inputs": {
7
+ "examples/propfind-body/workflow.json": "f365021255ae250b287848de1bb14c47ed2a244127957268044643ab956156e8",
8
+ "examples/propfind-body/case.json": "e37304adb5eacfad5ca773a77510505a8c3c89f1be451854d3b61e63a7029add"
9
+ },
10
+ "runs": [
11
+ {
12
+ "name": "Issue 39395 - PROPFIND transmits its XML body",
13
+ "startedAt": "2026-10-06T20:14:31.865Z",
14
+ "n8nVersion": "2.38.4",
15
+ "runnerVersion": "0.1.4",
16
+ "nodeVersion": "v26.7.0",
17
+ "network": {
18
+ "mode": "loopback-only",
19
+ "enforced": true,
20
+ "interfaces": [
21
+ "lo"
22
+ ]
23
+ },
24
+ "status": "failed",
25
+ "exitCode": 1,
26
+ "checks": [
27
+ {
28
+ "name": "n8n execution succeeds",
29
+ "passed": true,
30
+ "actual": {
31
+ "cliExitCode": 0,
32
+ "lastNode": "HTTP Request",
33
+ "executionFound": true
34
+ }
35
+ },
36
+ {
37
+ "name": "HTTP Request: output 0",
38
+ "passed": true,
39
+ "expected": [
40
+ {
41
+ "received": true
42
+ }
43
+ ],
44
+ "actual": [
45
+ {
46
+ "received": true
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
+ "name": "HTTP Request: PROPFIND /dav request count",
62
+ "passed": true,
63
+ "expected": 1,
64
+ "actual": 1
65
+ },
66
+ {
67
+ "name": "HTTP Request: PROPFIND /dav request bodies",
68
+ "passed": false,
69
+ "expected": [
70
+ "<?xml version=\"1.0\"?>\n<invalid>\n<d:propfind xmlns:d=\"DAV:\" xmlns:oc=\"http://owncloud.org/ns\" xmlns:nc=\"http://nextcloud.org/ns\">\n <d:prop>\n <d:creationdate />\n <nc:upload_time />\n <d:getlastmodified />\n <d:getetag />\n <d:getcontenttype />\n <d:resourcetype />\n <d:quota-used-bytes />\n <d:quota-available-bytes />\n <d:displayname />\n <nc:has-preview />\n <oc:id />\n <oc:fileid />\n <oc:permissions />\n <oc:size />\n <oc:favorite />\n <oc:tags />\n <oc:comments-unread />\n <oc:owner-id />\n <oc:owner-display-name />\n <oc:share-types />\n </d:prop>\n</d:propfind>"
71
+ ],
72
+ "actual": [
73
+ null
74
+ ]
75
+ }
76
+ ],
77
+ "requests": [
78
+ {
79
+ "node": "HTTP Request",
80
+ "method": "PROPFIND",
81
+ "path": "/dav",
82
+ "body": null,
83
+ "matched": true,
84
+ "status": 200,
85
+ "response": {
86
+ "received": true
87
+ }
88
+ }
89
+ ]
90
+ },
91
+ {
92
+ "name": "Issue 39395 - PROPFIND transmits its XML body",
93
+ "startedAt": "2026-10-06T20:14:26.062Z",
94
+ "n8nVersion": "2.41.7",
95
+ "runnerVersion": "0.1.4",
96
+ "nodeVersion": "v26.7.0",
97
+ "network": {
98
+ "mode": "loopback-only",
99
+ "enforced": true,
100
+ "interfaces": [
101
+ "lo"
102
+ ]
103
+ },
104
+ "status": "passed",
105
+ "exitCode": 0,
106
+ "checks": [
107
+ {
108
+ "name": "n8n execution succeeds",
109
+ "passed": true,
110
+ "actual": {
111
+ "cliExitCode": 0,
112
+ "lastNode": "HTTP Request",
113
+ "executionFound": true
114
+ }
115
+ },
116
+ {
117
+ "name": "HTTP Request: output 0",
118
+ "passed": true,
119
+ "expected": [
120
+ {
121
+ "received": true
122
+ }
123
+ ],
124
+ "actual": [
125
+ {
126
+ "received": true
127
+ }
128
+ ]
129
+ },
130
+ {
131
+ "name": "mock: all requests match routes",
132
+ "passed": true,
133
+ "actual": []
134
+ },
135
+ {
136
+ "name": "mock: request limits and server",
137
+ "passed": true,
138
+ "actual": []
139
+ },
140
+ {
141
+ "name": "HTTP Request: PROPFIND /dav request count",
142
+ "passed": true,
143
+ "expected": 1,
144
+ "actual": 1
145
+ },
146
+ {
147
+ "name": "HTTP Request: PROPFIND /dav request bodies",
148
+ "passed": true,
149
+ "expected": [
150
+ "<?xml version=\"1.0\"?>\n<invalid>\n<d:propfind xmlns:d=\"DAV:\" xmlns:oc=\"http://owncloud.org/ns\" xmlns:nc=\"http://nextcloud.org/ns\">\n <d:prop>\n <d:creationdate />\n <nc:upload_time />\n <d:getlastmodified />\n <d:getetag />\n <d:getcontenttype />\n <d:resourcetype />\n <d:quota-used-bytes />\n <d:quota-available-bytes />\n <d:displayname />\n <nc:has-preview />\n <oc:id />\n <oc:fileid />\n <oc:permissions />\n <oc:size />\n <oc:favorite />\n <oc:tags />\n <oc:comments-unread />\n <oc:owner-id />\n <oc:owner-display-name />\n <oc:share-types />\n </d:prop>\n</d:propfind>"
151
+ ],
152
+ "actual": [
153
+ "<?xml version=\"1.0\"?>\n<invalid>\n<d:propfind xmlns:d=\"DAV:\" xmlns:oc=\"http://owncloud.org/ns\" xmlns:nc=\"http://nextcloud.org/ns\">\n <d:prop>\n <d:creationdate />\n <nc:upload_time />\n <d:getlastmodified />\n <d:getetag />\n <d:getcontenttype />\n <d:resourcetype />\n <d:quota-used-bytes />\n <d:quota-available-bytes />\n <d:displayname />\n <nc:has-preview />\n <oc:id />\n <oc:fileid />\n <oc:permissions />\n <oc:size />\n <oc:favorite />\n <oc:tags />\n <oc:comments-unread />\n <oc:owner-id />\n <oc:owner-display-name />\n <oc:share-types />\n </d:prop>\n</d:propfind>"
154
+ ]
155
+ }
156
+ ],
157
+ "requests": [
158
+ {
159
+ "node": "HTTP Request",
160
+ "method": "PROPFIND",
161
+ "path": "/dav",
162
+ "body": "<?xml version=\"1.0\"?>\n<invalid>\n<d:propfind xmlns:d=\"DAV:\" xmlns:oc=\"http://owncloud.org/ns\" xmlns:nc=\"http://nextcloud.org/ns\">\n <d:prop>\n <d:creationdate />\n <nc:upload_time />\n <d:getlastmodified />\n <d:getetag />\n <d:getcontenttype />\n <d:resourcetype />\n <d:quota-used-bytes />\n <d:quota-available-bytes />\n <d:displayname />\n <nc:has-preview />\n <oc:id />\n <oc:fileid />\n <oc:permissions />\n <oc:size />\n <oc:favorite />\n <oc:tags />\n <oc:comments-unread />\n <oc:owner-id />\n <oc:owner-display-name />\n <oc:share-types />\n </d:prop>\n</d:propfind>",
163
+ "matched": true,
164
+ "status": 200,
165
+ "response": {
166
+ "received": true
167
+ }
168
+ }
169
+ ]
170
+ }
171
+ ],
172
+ "additionalRuntimeSmoke": {
173
+ "name": "Two render requests preserve object options",
174
+ "startedAt": "2026-10-06T20:12:16.116Z",
175
+ "n8nVersion": "2.37.9",
176
+ "runnerVersion": "0.1.4",
177
+ "nodeVersion": "v26.5.1",
178
+ "network": {
179
+ "mode": "loopback-only",
180
+ "enforced": true,
181
+ "interfaces": [
182
+ "lo"
183
+ ]
184
+ },
185
+ "status": "passed",
186
+ "exitCode": 0,
187
+ "checks": [
188
+ {
189
+ "name": "n8n execution succeeds",
190
+ "passed": true,
191
+ "actual": {
192
+ "cliExitCode": 0,
193
+ "lastNode": "Render",
194
+ "executionFound": true
195
+ }
196
+ },
197
+ {
198
+ "name": "Render: output 0",
199
+ "passed": true,
200
+ "expected": [
201
+ {
202
+ "id": "render-42"
203
+ },
204
+ {
205
+ "id": "render-43"
206
+ }
207
+ ],
208
+ "actual": [
209
+ {
210
+ "id": "render-42"
211
+ },
212
+ {
213
+ "id": "render-43"
214
+ }
215
+ ]
216
+ },
217
+ {
218
+ "name": "mock: all requests match routes",
219
+ "passed": true,
220
+ "actual": []
221
+ },
222
+ {
223
+ "name": "mock: request limits and server",
224
+ "passed": true,
225
+ "actual": []
226
+ },
227
+ {
228
+ "name": "Render: POST /renders request count",
229
+ "passed": true,
230
+ "expected": 2,
231
+ "actual": 2
232
+ },
233
+ {
234
+ "name": "Render: POST /renders request bodies",
235
+ "passed": true,
236
+ "expected": [
237
+ {
238
+ "shortId": 42,
239
+ "renderOptions": {
240
+ "color": "#112233",
241
+ "fontSize": 31
242
+ }
243
+ },
244
+ {
245
+ "shortId": 43,
246
+ "renderOptions": {
247
+ "color": "#aabbcc",
248
+ "fontSize": 47
249
+ }
250
+ }
251
+ ],
252
+ "actual": [
253
+ {
254
+ "shortId": 42,
255
+ "renderOptions": {
256
+ "color": "#112233",
257
+ "fontSize": 31
258
+ }
259
+ },
260
+ {
261
+ "shortId": 43,
262
+ "renderOptions": {
263
+ "color": "#aabbcc",
264
+ "fontSize": 47
265
+ }
266
+ }
267
+ ]
268
+ }
269
+ ],
270
+ "requests": [
271
+ {
272
+ "node": "Render",
273
+ "method": "POST",
274
+ "path": "/renders",
275
+ "body": {
276
+ "shortId": 42,
277
+ "renderOptions": {
278
+ "color": "#112233",
279
+ "fontSize": 31
280
+ }
281
+ },
282
+ "matched": true,
283
+ "status": 200,
284
+ "response": {
285
+ "id": "render-42"
286
+ }
287
+ },
288
+ {
289
+ "node": "Render",
290
+ "method": "POST",
291
+ "path": "/renders",
292
+ "body": {
293
+ "shortId": 43,
294
+ "renderOptions": {
295
+ "color": "#aabbcc",
296
+ "fontSize": 47
297
+ }
298
+ },
299
+ "matched": true,
300
+ "status": 200,
301
+ "response": {
302
+ "id": "render-43"
303
+ }
304
+ }
305
+ ]
306
+ }
307
+ }
@@ -0,0 +1,32 @@
1
+ # Save as .github/workflows/n8n-upgrade.yml in your own repository.
2
+ # Replace both releases and both file paths with your project's values.
3
+ name: n8n upgrade contract
4
+ on: [push, pull_request, workflow_dispatch]
5
+ permissions:
6
+ contents: read
7
+ jobs:
8
+ contract:
9
+ name: n8n ${{ matrix.n8n }}
10
+ runs-on: ubuntu-latest
11
+ timeout-minutes: 15
12
+ strategy:
13
+ fail-fast: false
14
+ matrix:
15
+ n8n: ['2.38.4', '2.41.7']
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+ with:
19
+ persist-credentials: false
20
+ - uses: blucca/n8n-check@v0.1.4
21
+ with:
22
+ n8n-version: ${{ matrix.n8n }}
23
+ workflow: workflows/workflow.json
24
+ case: tests/workflow.case.json
25
+ out: results/contract
26
+ - uses: actions/upload-artifact@v4
27
+ if: always()
28
+ with:
29
+ name: n8n-contract-${{ matrix.n8n }}
30
+ path: |
31
+ results/contract/report.json
32
+ results/contract/junit.xml
package/package.json CHANGED
@@ -1,6 +1,43 @@
1
1
  {
2
2
  "name": "@blucca/n8n-check",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.6",
4
+ "description": "Run n8n workflow regression checks with fixtures, local HTTP mocks and JUnit reports.",
5
+ "type": "module",
6
+ "bin": {
7
+ "n8n-check": "bin/n8n-check.mjs"
8
+ },
9
+ "exports": "./src/runner.mjs",
10
+ "files": [
11
+ "bin",
12
+ "src",
13
+ "examples",
14
+ "Dockerfile",
15
+ "README.md",
16
+ "LICENSE"
17
+ ],
18
+ "scripts": {
19
+ "test": "node --test test/*.test.mjs"
20
+ },
21
+ "engines": {
22
+ "node": ">=24"
23
+ },
24
+ "license": "MIT",
25
+ "author": "blucca",
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "git+https://github.com/blucca/n8n-check.git"
29
+ },
30
+ "homepage": "https://github.com/blucca/n8n-check#readme",
31
+ "bugs": "https://github.com/blucca/n8n-check/issues",
32
+ "keywords": [
33
+ "n8n",
34
+ "testing",
35
+ "workflow",
36
+ "regression",
37
+ "junit",
38
+ "mock"
39
+ ],
40
+ "publishConfig": {
41
+ "access": "public"
42
+ }
43
+ }