fix(webdav): wire orphaned PATCH tests + fix NC error-mapping and path bugs they caught
webdav_patch.hurl and nc_webdav_patch.hurl existed with real coverage since the original PATCH commits but were never added to tests/api/run.sh, so just api-test/CI silently skipped them. Wire both in, fix nc_webdav_patch.hurl's header-after-[BasicAuth] ordering bug that meant it had never actually passed, and add two new consistency-focused files chaining PATCH operations with cross-protocol/cross-surface verification: - webdav_patch_consistency.hurl: chained overwrites with ETag-change checks, GET/HEAD/PROPFIND cross-protocol agreement, quota-507 leaving the file byte-for-byte unchanged, direct_put_max_bytes prefix/suffix regression coverage. - nc_webdav_patch_consistency.hurl: Editor/Viewer/Outsider permission matrix, cross-surface lock interop, quota-507 via the NC surface. Running these surfaced two real bugs in the NC PATCH handler, both fixed here: - The write step mapped every error (including a legitimate anti-enum permission denial) to a raw 500 instead of AppError::from(e), unlike the plain surface. A Viewer without Update permission got a 500 leak instead of the expected 404. - nc_to_internal_path() didn't strip the leading '/' that chroot.path carries from StoragePath::to_string(), so a LOCK taken via /webdav/ silently failed to block PATCH via /remote.php/dav/ on the same file — the exact-string lock-store lookup never matched. Added a regression unit test.
This commit is contained in:
+54
-27
@@ -17,6 +17,18 @@
|
||||
# 6. PATCH on a directory → 409.
|
||||
# 7. PATCH on a missing resource → 404.
|
||||
# 8. PATCH without X-Update-Range → 400.
|
||||
# 9. If-None-Match precondition failure (tag matches current ETag) → 412.
|
||||
# 10. If-Match with a WEAK (`W/`) form of the current ETag → 412 (RFC 7232
|
||||
# §3.1: If-Match requires a STRONG match; a weak validator in the
|
||||
# request never satisfies it, even if the underlying tag value is
|
||||
# identical — see `if_match_precondition_fails`).
|
||||
#
|
||||
# Hurl gotcha: a triple-backtick ``` multiline body appends a trailing
|
||||
# `\n` the server counts as part of Content-Length — that silently
|
||||
# breaks the exact `end - start + 1` span check on a byte-range PATCH.
|
||||
# Plain-text bodies below use the single-backtick ONELINE string form
|
||||
# (`` `text` ``) instead, which sends exactly the bytes between the
|
||||
# backticks with no injected newline.
|
||||
# =============================================================
|
||||
|
||||
|
||||
@@ -38,9 +50,7 @@ token: jsonpath "$.access_token"
|
||||
PUT {{base_url}}/webdav/patch-probe.txt
|
||||
Authorization: Bearer {{token}}
|
||||
Content-Type: text/plain
|
||||
```
|
||||
0123456789
|
||||
```
|
||||
`0123456789`
|
||||
|
||||
HTTP 201
|
||||
[Captures]
|
||||
@@ -55,9 +65,7 @@ PATCH {{base_url}}/webdav/patch-probe.txt
|
||||
Authorization: Bearer {{token}}
|
||||
X-Update-Range: bytes=3-5
|
||||
Content-Type: text/plain
|
||||
```
|
||||
XYZ
|
||||
```
|
||||
`XYZ`
|
||||
|
||||
HTTP 204
|
||||
[Asserts]
|
||||
@@ -79,11 +87,11 @@ PATCH {{base_url}}/webdav/patch-probe.txt
|
||||
Authorization: Bearer {{token}}
|
||||
X-Update-Range: append
|
||||
Content-Type: text/plain
|
||||
```
|
||||
-APPENDED
|
||||
```
|
||||
`-APPENDED`
|
||||
|
||||
HTTP 204
|
||||
[Captures]
|
||||
current_etag: header "ETag"
|
||||
|
||||
|
||||
GET {{base_url}}/webdav/patch-probe.txt
|
||||
@@ -103,9 +111,7 @@ PATCH {{base_url}}/webdav/patch-probe.txt
|
||||
Authorization: Bearer {{token}}
|
||||
X-Update-Range: bytes=1000-1005
|
||||
Content-Type: text/plain
|
||||
```
|
||||
oops
|
||||
```
|
||||
`oops`
|
||||
|
||||
HTTP 416
|
||||
|
||||
@@ -118,9 +124,7 @@ Authorization: Bearer {{token}}
|
||||
X-Update-Range: bytes=0-2
|
||||
If-Match: "not-the-real-etag"
|
||||
Content-Type: text/plain
|
||||
```
|
||||
NOP
|
||||
```
|
||||
`NOP`
|
||||
|
||||
HTTP 412
|
||||
|
||||
@@ -149,9 +153,7 @@ PATCH {{base_url}}/webdav/patch-probe.txt
|
||||
Authorization: Bearer {{token}}
|
||||
X-Update-Range: bytes=0-2
|
||||
Content-Type: text/plain
|
||||
```
|
||||
NOP
|
||||
```
|
||||
`NOP`
|
||||
|
||||
HTTP 423
|
||||
|
||||
@@ -177,9 +179,7 @@ PATCH {{base_url}}/webdav/patch-probe-dir/
|
||||
Authorization: Bearer {{token}}
|
||||
X-Update-Range: bytes=0-2
|
||||
Content-Type: text/plain
|
||||
```
|
||||
NOP
|
||||
```
|
||||
`NOP`
|
||||
|
||||
HTTP 409
|
||||
|
||||
@@ -191,9 +191,7 @@ PATCH {{base_url}}/webdav/patch-probe-does-not-exist.txt
|
||||
Authorization: Bearer {{token}}
|
||||
X-Update-Range: bytes=0-2
|
||||
Content-Type: text/plain
|
||||
```
|
||||
NOP
|
||||
```
|
||||
`NOP`
|
||||
|
||||
HTTP 404
|
||||
|
||||
@@ -204,13 +202,42 @@ HTTP 404
|
||||
PATCH {{base_url}}/webdav/patch-probe.txt
|
||||
Authorization: Bearer {{token}}
|
||||
Content-Type: text/plain
|
||||
```
|
||||
NOP
|
||||
```
|
||||
`NOP`
|
||||
|
||||
HTTP 400
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Step 11 — If-None-Match precondition failure: the header names the
|
||||
# CURRENT ETag, so the "only if it does NOT match" condition
|
||||
# is violated → 412.
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
PATCH {{base_url}}/webdav/patch-probe.txt
|
||||
Authorization: Bearer {{token}}
|
||||
X-Update-Range: bytes=0-2
|
||||
If-None-Match: {{current_etag}}
|
||||
Content-Type: text/plain
|
||||
`NOP`
|
||||
|
||||
HTTP 412
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Step 12 — If-Match with a WEAK form (`W/`) of the current ETag → 412.
|
||||
# RFC 7232 §3.1 requires If-Match to STRONG-match; a request
|
||||
# carrying a weak validator never satisfies it even when the
|
||||
# underlying tag value is identical.
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
PATCH {{base_url}}/webdav/patch-probe.txt
|
||||
Authorization: Bearer {{token}}
|
||||
X-Update-Range: bytes=0-2
|
||||
If-Match: W/{{current_etag}}
|
||||
Content-Type: text/plain
|
||||
`NOP`
|
||||
|
||||
HTTP 412
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# Cleanup
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user