Files
Oxicloud/tests/api/webdav_nested_move_cascade.hurl
T
Edouard Vanbelle cf1479d113 test(webdav): cover nested folder move
this test is a duplicated with the move in litmus test suite
    it has been added because not everybody do have litmus installed
    and no clue if litmus will be kept in the future
2026-06-30 22:55:49 +02:00

165 lines
8.7 KiB
Plaintext

# =============================================================
# OxiCloud — WebDAV: nested-folder MOVE descendant cascade
# =============================================================
# Regression guard for the litmus `copymove → move_coll`
# scenario: when a parent folder is renamed (or moved), every
# DESCENDANT folder row must have its `path` / `lpath` columns
# rewritten by the AFTER cascade trigger
# `trg_folders_cascade_path` so that path-keyed lookups
# (WebDAV, CalDAV, CardDAV, and a handful of REST endpoints)
# resolve the descendant at its new location.
#
# Why this needs a dedicated test:
# * REST API tests overwhelmingly use folder IDs, not paths —
# a stale `folders.path` column is invisible to `WHERE id =
# $1` lookups. So they can't catch this regression even when
# they MOVE.
# * Existing WebDAV tests are flat: MOVE a file, or MKCOL +
# DELETE on a single-level folder. None combine "MOVE a
# folder that has folder descendants" with "look the
# descendant up by its post-move path".
# * litmus's `copymove → move_coll` IS this test, but litmus
# isn't installed on every contributor's machine — it lives
# on the CI side only. This Hurl scenario runs in every
# standard `just api-test`.
#
# Bug shape it catches: the `UPDATE OF <cols>` column list on
# `trg_folders_cascade_path` must include `name` AND `parent_id`
# (not just `path, lpath, drive_id`) — otherwise the AFTER
# trigger never fires on the rename SQL `UPDATE folders SET
# name = $1` or the move SQL `UPDATE folders SET parent_id =
# $1`, descendant rows stay at their pre-move path, and any
# subsequent path-keyed lookup of a descendant returns 404.
#
# What this test does:
# 1. Login (admin).
# 2. MKCOL /webdav/regress-cascade-a/
# 3. MKCOL /webdav/regress-cascade-a/b/ (descendant folder)
# 4. PUT /webdav/regress-cascade-a/b/leaf.txt (leaf file)
# 5. MOVE /webdav/regress-cascade-a/ → /webdav/regress-cascade-c/
# 6. DELETE /webdav/regress-cascade-c/b/leaf.txt ← path-based file
# lookup at the
# new descendant
# location
# 7. DELETE /webdav/regress-cascade-c/b/ ← path-based
# descendant
# folder lookup
# (this is the
# one that 404s
# when the bug
# is present)
# 8. DELETE /webdav/regress-cascade-c/ (cleanup root)
#
# Steps 6 and 7 are the load-bearing assertions; without the
# cascade, the descendant's `path` column is still
# `Personal/regress-cascade-a/b` and both DELETEs return 404.
# =============================================================
# ─────────────────────────────────────────────────────────────
# Step 1 — Login, capture JWT.
# ─────────────────────────────────────────────────────────────
POST {{base_url}}/api/auth/login
Content-Type: application/json
{ "username": "{{username}}", "password": "{{password}}" }
HTTP 200
[Captures]
token: jsonpath "$.access_token"
# ─────────────────────────────────────────────────────────────
# Step 2 — MKCOL the parent collection. Fresh names; 201 expected.
# ─────────────────────────────────────────────────────────────
MKCOL {{base_url}}/webdav/regress-cascade-a/
Authorization: Bearer {{token}}
HTTP 201
# ─────────────────────────────────────────────────────────────
# Step 3 — MKCOL the descendant collection inside the parent.
# This is the folder row whose `path` column the
# cascade trigger must rewrite when the parent is
# renamed in Step 5.
# ─────────────────────────────────────────────────────────────
MKCOL {{base_url}}/webdav/regress-cascade-a/b/
Authorization: Bearer {{token}}
HTTP 201
# ─────────────────────────────────────────────────────────────
# Step 4 — PUT a leaf file inside the descendant. We use it in
# Step 6 to verify the post-move file lookup works
# (files resolve via their parent folder's `path`, so
# this branch caught fire too when the cascade was
# broken — even though `storage.files` has no `path`
# column of its own).
# ─────────────────────────────────────────────────────────────
PUT {{base_url}}/webdav/regress-cascade-a/b/leaf.txt
Authorization: Bearer {{token}}
Content-Type: text/plain
```
nested cascade regression probe
```
HTTP 201
# ─────────────────────────────────────────────────────────────
# Step 5 — MOVE the parent collection. The SQL the service
# issues is `UPDATE storage.folders SET name = $1, ...`
# on the parent row (intra-drive same-parent rename).
# The BEFORE trigger `trg_folders_path` rewrites the
# parent's own `path`; the AFTER trigger
# `trg_folders_cascade_path` must fire to rewrite
# every descendant folder's `path` / `lpath`.
#
# RFC 4918 §9.9.4: destination is fresh → 201 Created.
# ─────────────────────────────────────────────────────────────
MOVE {{base_url}}/webdav/regress-cascade-a/
Authorization: Bearer {{token}}
Destination: {{base_url}}/webdav/regress-cascade-c/
HTTP 201
# ─────────────────────────────────────────────────────────────
# Step 6 — Resolve the leaf FILE by its post-move path. The
# DELETE handler's resolver joins `storage.files`
# against `storage.folders` on `folder_id`, then
# filters `fo.path = 'Personal/regress-cascade-c/b'`.
# That match depends on the descendant folder's
# `path` column having been cascade-rewritten in
# Step 5.
# ─────────────────────────────────────────────────────────────
DELETE {{base_url}}/webdav/regress-cascade-c/b/leaf.txt
Authorization: Bearer {{token}}
HTTP 204
# ─────────────────────────────────────────────────────────────
# Step 7 — Resolve the descendant FOLDER by its post-move path.
# This is the assertion that was failing as litmus
# test 10. Lookup SQL: `SELECT … FROM storage.folders
# WHERE path = 'Personal/regress-cascade-c/b' …`.
# Without the cascade, the row still has path
# `Personal/regress-cascade-a/b` → 0 rows → 404.
# ─────────────────────────────────────────────────────────────
DELETE {{base_url}}/webdav/regress-cascade-c/b/
Authorization: Bearer {{token}}
HTTP 204
# ─────────────────────────────────────────────────────────────
# Step 8 — Cleanup: DELETE the moved root so subsequent test
# runs start clean even on a non-pristine DB.
# ─────────────────────────────────────────────────────────────
DELETE {{base_url}}/webdav/regress-cascade-c/
Authorization: Bearer {{token}}
HTTP 204