@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.
Files changed (78) hide show
  1. package/Dockerfile +10 -0
  2. package/LICENSE +21 -0
  3. package/README.md +323 -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/vapi-tool-contract/README.md +67 -0
  61. package/examples/vapi-tool-contract/backend-503.case.json +113 -0
  62. package/examples/vapi-tool-contract/empty-orders.case.json +113 -0
  63. package/examples/vapi-tool-contract/observed-results.json +234 -0
  64. package/examples/vapi-tool-contract/success.case.json +118 -0
  65. package/examples/vapi-tool-contract/workflow.json +143 -0
  66. package/examples/vapi-tool-contract/wrong-id.json +143 -0
  67. package/examples/version-matrix/README.md +65 -0
  68. package/examples/version-matrix/observed-results.json +307 -0
  69. package/examples/version-matrix/upgrade-check.yml +32 -0
  70. package/package.json +41 -4
  71. package/src/action.mjs +108 -0
  72. package/src/case-draft.mjs +163 -0
  73. package/src/local-bundle.mjs +154 -0
  74. package/src/mock.mjs +72 -0
  75. package/src/report.mjs +73 -0
  76. package/src/runner.mjs +111 -0
  77. package/src/version.mjs +3 -0
  78. package/src/workflow.mjs +140 -0
@@ -0,0 +1,75 @@
1
+ # Verify PROPFIND sends its XML body after an n8n upgrade
2
+
3
+ **The reported Nextcloud workflow sends its complete XML body on n8n 2.41.7. Keep that behavior as a repeatable check.**
4
+
5
+ [KroetschVialutions reported #39395](https://github.com/n8n-io/n8n/issues/39395) with a three-node workflow on n8n 2.38.4 / HTTP Request 4.5: a PROPFIND request silently omitted its raw XML body. The author included an unclosed `<invalid>` element to make a received body visibly fail XML parsing.
6
+
7
+ Upstream [PR #24151](https://github.com/n8n-io/n8n/pull/24151) added WebDAV-method support. The [2.38.4 implementation](https://github.com/n8n-io/n8n/blob/n8n%402.38.4/packages/nodes-base/nodes/HttpRequest/V3/HttpRequestV3.node.ts#L458) restricted raw-body serialization to PATCH/POST/PUT/GET. The [2.41.7 implementation](https://github.com/n8n-io/n8n/blob/n8n%402.41.7/packages/nodes-base/nodes/HttpRequest/V3/HttpRequestV3.node.ts#L460) sends a PROPFIND body.
8
+
9
+ ## What this checks
10
+
11
+ [`workflow.json`](workflow.json) comes from the author's public reproduction. Its HTTP Request parameters, node version and diagnostic XML are preserved. Instance metadata is removed and node IDs are replaced with local labels. The Code-node fixture supplies synthetic values; n8n-check redirects the HTTP URL to a local mock.
12
+
13
+ [`case.json`](case.json) checks:
14
+
15
+ - Exactly **one PROPFIND request** to the mock route.
16
+ - The **complete 698-character XML body**, including newlines and the author's diagnostic marker.
17
+ - The HTTP Request node's exact output from the declared JSON mock response.
18
+
19
+ The real n8n engine executes the HTTP node. This is an **HTTP transmission contract**: the mock captures the body and returns a declared response. Nextcloud authentication, XML parsing, requested properties and live instance behavior have their own integration checks. Remove the deliberate `<invalid>` marker when testing a real property query; update the case's expected body when changing the XML.
20
+
21
+ ## Run the upgrade check
22
+
23
+ **[Compare the unchanged export on n8n 2.38.4 and 2.41.7](../version-matrix/)** using the v0.1.4 Action. The measured older engine omits the body; 2.41.7 preserves all 698 characters. The commands below run the default 2.41.7 image.
24
+
25
+ With Git and Docker:
26
+
27
+ ```sh
28
+ git clone --branch v0.1.4 https://github.com/blucca/n8n-check.git
29
+ cd n8n-check
30
+ docker build -t n8n-check .
31
+
32
+ docker run --rm --network none -v "$PWD:/work" n8n-check \
33
+ examples/propfind-body/workflow.json examples/propfind-body/case.json \
34
+ --out /work/results/propfind
35
+ # PASSED ... n8n 2.41.7; 6 checks; exit 0
36
+ ```
37
+
38
+ On Linux, add `--user "$(id -u):$(id -g)"` when your user ID differs from the image's default 1000. Windows users can run these commands in WSL.
39
+
40
+ The Dockerfile pins n8n 2.41.7. The container uses loopback-only networking; fixture values replace the input Code node and all HTTP traffic goes to the local mock. Reports appear in `results/propfind/report.json` and `junit.xml`.
41
+
42
+ ### Check your own export and installed runtime
43
+
44
+ From this repository checkout, with the [CLI installed](../../README.md#install-the-cli-from-github):
45
+
46
+ ```sh
47
+ n8n-check /path/to/nextcloud-export.json examples/propfind-body/case.json \
48
+ --n8n /path/to/n8n --allow-network --out results/propfind-local
49
+ ```
50
+
51
+ Use the same `Code in JavaScript` input boundary and `HTTP Request` node names, or update those names in the case. This case expects the diagnostic XML from the report verbatim. `--n8n` selects the installed runtime you want to verify; its version is recorded in the report. `--allow-network` selects trusted local execution on the host; the declared HTTP mock still captures this request locally. The Docker command above provides network isolation.
52
+
53
+ ## Confirm the test catches a missing body
54
+
55
+ [`no-body-control.json`](no-body-control.json) changes exactly one HTTP parameter: `sendBody` becomes `false`. It is an intentional negative control using the same n8n version.
56
+
57
+ ```sh
58
+ docker run --rm --network none -v "$PWD:/work" n8n-check \
59
+ examples/propfind-body/no-body-control.json examples/propfind-body/case.json \
60
+ --out /work/results/propfind-no-body
61
+ # FAILED ... exact body: expected XML, actual null; exit 1
62
+ ```
63
+
64
+ The workflow execution itself still succeeds. The request-body assertion detects the omitted business payload.
65
+
66
+ ## Recorded results
67
+
68
+ [Observed JSON](observed-results.json) includes the original-export independent run and runs of both published exports, with versions, exact request bodies and check results. These runs used n8n-check 0.1.2, n8n 2.41.7 and a Linux namespace with only the loopback interface.
69
+
70
+ | Workflow | n8n execution | Requests | XML body | Check |
71
+ |---|---|---|---|---|
72
+ | Reported parameters | Success | 1 PROPFIND | All 698 characters | 6/6, exit 0 |
73
+ | `sendBody=false` control | Success | 1 PROPFIND | Empty (`null`) | Body assertion fails, exit 1 |
74
+
75
+ The published fixture and recorded results are maintained by [blucca](https://github.com/blucca), an AI-operated software practice. Original reproduction credit: [KroetschVialutions, n8n #39395](https://github.com/n8n-io/n8n/issues/39395).
@@ -0,0 +1,51 @@
1
+ {
2
+ "version": 1,
3
+ "name": "Issue 39395 - PROPFIND transmits its XML body",
4
+ "input": {
5
+ "node": "Code in JavaScript",
6
+ "items": [
7
+ {
8
+ "nextcloudInstance": "example.invalid",
9
+ "userId": "user_id",
10
+ "auth": "Basic synthetic-placeholder",
11
+ "webDavPath": "fixture"
12
+ }
13
+ ]
14
+ },
15
+ "mocks": [
16
+ {
17
+ "node": "HTTP Request",
18
+ "url": "/dav",
19
+ "routes": [
20
+ {
21
+ "method": "PROPFIND",
22
+ "path": "/dav",
23
+ "responses": [
24
+ {
25
+ "status": 200,
26
+ "json": {
27
+ "received": true
28
+ }
29
+ }
30
+ ],
31
+ "expect": {
32
+ "count": 1,
33
+ "bodies": [
34
+ "<?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>"
35
+ ]
36
+ }
37
+ }
38
+ ]
39
+ }
40
+ ],
41
+ "assertions": [
42
+ {
43
+ "node": "HTTP Request",
44
+ "equals": [
45
+ {
46
+ "received": true
47
+ }
48
+ ]
49
+ }
50
+ ]
51
+ }
@@ -0,0 +1,96 @@
1
+ {
2
+ "name": "PropFind Test",
3
+ "nodes": [
4
+ {
5
+ "parameters": {},
6
+ "type": "n8n-nodes-base.manualTrigger",
7
+ "typeVersion": 1,
8
+ "position": [
9
+ 0,
10
+ 0
11
+ ],
12
+ "id": "manual-trigger",
13
+ "name": "When clicking ‘Execute workflow’"
14
+ },
15
+ {
16
+ "parameters": {
17
+ "method": "PROPFIND",
18
+ "url": "=https://{{$json.nextcloudInstance}}/remote.php/dav/files/{{$json.userId}}/{{$json.webDavPath}}",
19
+ "sendHeaders": true,
20
+ "headerParameters": {
21
+ "parameters": [
22
+ {
23
+ "name": "OCS-APIRequest",
24
+ "value": "true"
25
+ },
26
+ {
27
+ "name": "Depth",
28
+ "value": "1"
29
+ },
30
+ {
31
+ "name": "Authorization",
32
+ "value": "={{$json.auth}}"
33
+ },
34
+ {
35
+ "name": "x-requested-with",
36
+ "value": "XMLHttpRequest"
37
+ }
38
+ ]
39
+ },
40
+ "sendBody": false,
41
+ "contentType": "raw",
42
+ "rawContentType": "application/xml",
43
+ "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>",
44
+ "options": {}
45
+ },
46
+ "type": "n8n-nodes-base.httpRequest",
47
+ "typeVersion": 4.5,
48
+ "position": [
49
+ 448,
50
+ 0
51
+ ],
52
+ "id": "propfind-request",
53
+ "name": "HTTP Request"
54
+ },
55
+ {
56
+ "parameters": {
57
+ "jsCode": "return {\n nextcloudInstance: \"nextcloud.instance\", // just domain, no protocol\n userId: \"user_id\",\n auth: \"Basic app_password\",\n webDavPath: \"webdav_path\",\n}"
58
+ },
59
+ "type": "n8n-nodes-base.code",
60
+ "typeVersion": 2,
61
+ "position": [
62
+ 224,
63
+ 0
64
+ ],
65
+ "id": "fixture-input",
66
+ "name": "Code in JavaScript"
67
+ }
68
+ ],
69
+ "connections": {
70
+ "When clicking ‘Execute workflow’": {
71
+ "main": [
72
+ [
73
+ {
74
+ "node": "Code in JavaScript",
75
+ "type": "main",
76
+ "index": 0
77
+ }
78
+ ]
79
+ ]
80
+ },
81
+ "Code in JavaScript": {
82
+ "main": [
83
+ [
84
+ {
85
+ "node": "HTTP Request",
86
+ "type": "main",
87
+ "index": 0
88
+ }
89
+ ]
90
+ ]
91
+ }
92
+ },
93
+ "settings": {
94
+ "executionOrder": "v1"
95
+ }
96
+ }
@@ -0,0 +1,307 @@
1
+ {
2
+ "description": "Actual n8n 2.41.7 executions of the public reproduction from n8n issue 39395, using local HTTP capture and synthetic fixture inputs. The original export was independently rerun, then both published exports were checked in loopback-only namespaces.",
3
+ "sourceIssue": "https://github.com/n8n-io/n8n/issues/39395",
4
+ "upstreamFix": "https://github.com/n8n-io/n8n/pull/24151",
5
+ "caseSha256": "e37304adb5eacfad5ca773a77510505a8c3c89f1be451854d3b61e63a7029add",
6
+ "scope": "Exact PROPFIND method, request count, raw XML body and declared mock response. Live Nextcloud authentication, XML parsing and property lookup are separate integration checks.",
7
+ "runs": [
8
+ {
9
+ "description": "Original issue export, independent run",
10
+ "workflow": "original issue export",
11
+ "workflowSha256": "e3c4cdacb788a24fa32a20eba16ec0bb9c11f52386805bc6e962a4a76581333d",
12
+ "runnerVersion": "0.1.2",
13
+ "n8nVersion": "2.41.7",
14
+ "nodeVersion": "v26.10.0",
15
+ "startedAt": "2026-10-06T12:16:15.159Z",
16
+ "durationMs": 9355,
17
+ "network": {
18
+ "mode": "loopback-only",
19
+ "enforced": true,
20
+ "interfaces": [
21
+ "lo"
22
+ ]
23
+ },
24
+ "status": "passed",
25
+ "exitCode": 0,
26
+ "preparation": {
27
+ "inputNode": "Code in JavaScript",
28
+ "changes": [
29
+ {
30
+ "node": "Code in JavaScript",
31
+ "change": "replace with fixture JSON items"
32
+ },
33
+ {
34
+ "node": "HTTP Request",
35
+ "change": "route URL to local mock; remove credential references; authentication=none"
36
+ }
37
+ ],
38
+ "omittedNodes": [
39
+ "When clicking ‘Execute workflow’"
40
+ ]
41
+ },
42
+ "checks": [
43
+ {
44
+ "name": "n8n execution succeeds",
45
+ "passed": true,
46
+ "actual": {
47
+ "cliExitCode": 0,
48
+ "lastNode": "HTTP Request",
49
+ "executionFound": true
50
+ }
51
+ },
52
+ {
53
+ "name": "HTTP Request: output 0",
54
+ "passed": true,
55
+ "expected": [
56
+ {
57
+ "received": true
58
+ }
59
+ ],
60
+ "actual": [
61
+ {
62
+ "received": true
63
+ }
64
+ ]
65
+ },
66
+ {
67
+ "name": "mock: all requests match routes",
68
+ "passed": true,
69
+ "actual": []
70
+ },
71
+ {
72
+ "name": "mock: request limits and server",
73
+ "passed": true,
74
+ "actual": []
75
+ },
76
+ {
77
+ "name": "HTTP Request: PROPFIND /dav request count",
78
+ "passed": true,
79
+ "expected": 1,
80
+ "actual": 1
81
+ },
82
+ {
83
+ "name": "HTTP Request: PROPFIND /dav request bodies",
84
+ "passed": true,
85
+ "expected": [
86
+ "<?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>"
87
+ ],
88
+ "actual": [
89
+ "<?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>"
90
+ ]
91
+ }
92
+ ],
93
+ "requests": [
94
+ {
95
+ "node": "HTTP Request",
96
+ "method": "PROPFIND",
97
+ "path": "/dav",
98
+ "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>",
99
+ "matched": true,
100
+ "status": 200,
101
+ "response": {
102
+ "received": true
103
+ }
104
+ }
105
+ ],
106
+ "relationToPublishedExport": "HTTP parameters, node names, types, versions and connections match workflow.json; instance metadata and node IDs were cleaned for publication."
107
+ },
108
+ {
109
+ "description": "Published metadata-cleaned export",
110
+ "workflow": "workflow.json",
111
+ "workflowSha256": "f365021255ae250b287848de1bb14c47ed2a244127957268044643ab956156e8",
112
+ "runnerVersion": "0.1.2",
113
+ "n8nVersion": "2.41.7",
114
+ "nodeVersion": "v26.10.0",
115
+ "startedAt": "2026-10-06T12:19:46.611Z",
116
+ "durationMs": 10565,
117
+ "network": {
118
+ "mode": "loopback-only",
119
+ "enforced": true,
120
+ "interfaces": [
121
+ "lo"
122
+ ]
123
+ },
124
+ "status": "passed",
125
+ "exitCode": 0,
126
+ "preparation": {
127
+ "inputNode": "Code in JavaScript",
128
+ "changes": [
129
+ {
130
+ "node": "Code in JavaScript",
131
+ "change": "replace with fixture JSON items"
132
+ },
133
+ {
134
+ "node": "HTTP Request",
135
+ "change": "route URL to local mock; remove credential references; authentication=none"
136
+ }
137
+ ],
138
+ "omittedNodes": [
139
+ "When clicking ‘Execute workflow’"
140
+ ]
141
+ },
142
+ "checks": [
143
+ {
144
+ "name": "n8n execution succeeds",
145
+ "passed": true,
146
+ "actual": {
147
+ "cliExitCode": 0,
148
+ "lastNode": "HTTP Request",
149
+ "executionFound": true
150
+ }
151
+ },
152
+ {
153
+ "name": "HTTP Request: output 0",
154
+ "passed": true,
155
+ "expected": [
156
+ {
157
+ "received": true
158
+ }
159
+ ],
160
+ "actual": [
161
+ {
162
+ "received": true
163
+ }
164
+ ]
165
+ },
166
+ {
167
+ "name": "mock: all requests match routes",
168
+ "passed": true,
169
+ "actual": []
170
+ },
171
+ {
172
+ "name": "mock: request limits and server",
173
+ "passed": true,
174
+ "actual": []
175
+ },
176
+ {
177
+ "name": "HTTP Request: PROPFIND /dav request count",
178
+ "passed": true,
179
+ "expected": 1,
180
+ "actual": 1
181
+ },
182
+ {
183
+ "name": "HTTP Request: PROPFIND /dav request bodies",
184
+ "passed": true,
185
+ "expected": [
186
+ "<?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>"
187
+ ],
188
+ "actual": [
189
+ "<?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>"
190
+ ]
191
+ }
192
+ ],
193
+ "requests": [
194
+ {
195
+ "node": "HTTP Request",
196
+ "method": "PROPFIND",
197
+ "path": "/dav",
198
+ "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>",
199
+ "matched": true,
200
+ "status": 200,
201
+ "response": {
202
+ "received": true
203
+ }
204
+ }
205
+ ]
206
+ },
207
+ {
208
+ "description": "Intentional negative control: sendBody=false",
209
+ "workflow": "no-body-control.json",
210
+ "workflowSha256": "250f9dc350fbfd9bf35efe72ec24bbf1dd90a407b61c673e9cfbc88df7cc94bb",
211
+ "runnerVersion": "0.1.2",
212
+ "n8nVersion": "2.41.7",
213
+ "nodeVersion": "v26.10.0",
214
+ "startedAt": "2026-10-06T12:19:46.611Z",
215
+ "durationMs": 10625,
216
+ "network": {
217
+ "mode": "loopback-only",
218
+ "enforced": true,
219
+ "interfaces": [
220
+ "lo"
221
+ ]
222
+ },
223
+ "status": "failed",
224
+ "exitCode": 1,
225
+ "preparation": {
226
+ "inputNode": "Code in JavaScript",
227
+ "changes": [
228
+ {
229
+ "node": "Code in JavaScript",
230
+ "change": "replace with fixture JSON items"
231
+ },
232
+ {
233
+ "node": "HTTP Request",
234
+ "change": "route URL to local mock; remove credential references; authentication=none"
235
+ }
236
+ ],
237
+ "omittedNodes": [
238
+ "When clicking ‘Execute workflow’"
239
+ ]
240
+ },
241
+ "checks": [
242
+ {
243
+ "name": "n8n execution succeeds",
244
+ "passed": true,
245
+ "actual": {
246
+ "cliExitCode": 0,
247
+ "lastNode": "HTTP Request",
248
+ "executionFound": true
249
+ }
250
+ },
251
+ {
252
+ "name": "HTTP Request: output 0",
253
+ "passed": true,
254
+ "expected": [
255
+ {
256
+ "received": true
257
+ }
258
+ ],
259
+ "actual": [
260
+ {
261
+ "received": true
262
+ }
263
+ ]
264
+ },
265
+ {
266
+ "name": "mock: all requests match routes",
267
+ "passed": true,
268
+ "actual": []
269
+ },
270
+ {
271
+ "name": "mock: request limits and server",
272
+ "passed": true,
273
+ "actual": []
274
+ },
275
+ {
276
+ "name": "HTTP Request: PROPFIND /dav request count",
277
+ "passed": true,
278
+ "expected": 1,
279
+ "actual": 1
280
+ },
281
+ {
282
+ "name": "HTTP Request: PROPFIND /dav request bodies",
283
+ "passed": false,
284
+ "expected": [
285
+ "<?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>"
286
+ ],
287
+ "actual": [
288
+ null
289
+ ]
290
+ }
291
+ ],
292
+ "requests": [
293
+ {
294
+ "node": "HTTP Request",
295
+ "method": "PROPFIND",
296
+ "path": "/dav",
297
+ "body": null,
298
+ "matched": true,
299
+ "status": 200,
300
+ "response": {
301
+ "received": true
302
+ }
303
+ }
304
+ ]
305
+ }
306
+ ]
307
+ }
@@ -0,0 +1,96 @@
1
+ {
2
+ "name": "PropFind Test",
3
+ "nodes": [
4
+ {
5
+ "parameters": {},
6
+ "type": "n8n-nodes-base.manualTrigger",
7
+ "typeVersion": 1,
8
+ "position": [
9
+ 0,
10
+ 0
11
+ ],
12
+ "id": "manual-trigger",
13
+ "name": "When clicking ‘Execute workflow’"
14
+ },
15
+ {
16
+ "parameters": {
17
+ "method": "PROPFIND",
18
+ "url": "=https://{{$json.nextcloudInstance}}/remote.php/dav/files/{{$json.userId}}/{{$json.webDavPath}}",
19
+ "sendHeaders": true,
20
+ "headerParameters": {
21
+ "parameters": [
22
+ {
23
+ "name": "OCS-APIRequest",
24
+ "value": "true"
25
+ },
26
+ {
27
+ "name": "Depth",
28
+ "value": "1"
29
+ },
30
+ {
31
+ "name": "Authorization",
32
+ "value": "={{$json.auth}}"
33
+ },
34
+ {
35
+ "name": "x-requested-with",
36
+ "value": "XMLHttpRequest"
37
+ }
38
+ ]
39
+ },
40
+ "sendBody": true,
41
+ "contentType": "raw",
42
+ "rawContentType": "application/xml",
43
+ "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>",
44
+ "options": {}
45
+ },
46
+ "type": "n8n-nodes-base.httpRequest",
47
+ "typeVersion": 4.5,
48
+ "position": [
49
+ 448,
50
+ 0
51
+ ],
52
+ "id": "propfind-request",
53
+ "name": "HTTP Request"
54
+ },
55
+ {
56
+ "parameters": {
57
+ "jsCode": "return {\n nextcloudInstance: \"nextcloud.instance\", // just domain, no protocol\n userId: \"user_id\",\n auth: \"Basic app_password\",\n webDavPath: \"webdav_path\",\n}"
58
+ },
59
+ "type": "n8n-nodes-base.code",
60
+ "typeVersion": 2,
61
+ "position": [
62
+ 224,
63
+ 0
64
+ ],
65
+ "id": "fixture-input",
66
+ "name": "Code in JavaScript"
67
+ }
68
+ ],
69
+ "connections": {
70
+ "When clicking ‘Execute workflow’": {
71
+ "main": [
72
+ [
73
+ {
74
+ "node": "Code in JavaScript",
75
+ "type": "main",
76
+ "index": 0
77
+ }
78
+ ]
79
+ ]
80
+ },
81
+ "Code in JavaScript": {
82
+ "main": [
83
+ [
84
+ {
85
+ "node": "HTTP Request",
86
+ "type": "main",
87
+ "index": 0
88
+ }
89
+ ]
90
+ ]
91
+ }
92
+ },
93
+ "settings": {
94
+ "executionOrder": "v1"
95
+ }
96
+ }