595273277b
add a full coverage of Webdav and Nextcloud
purpose: prepare move to Drives and ensure no regression at all
test scenarios are in docs/plan/BASELINE_TESTS_NC_WEBDAV.md
current existing bugs identified via these tests:
┌──────────┬─────────┬────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Bug │ Surface │ Pin location │
├──────────┼─────────┼────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ G4/G5/K5 │ NC │ AlreadyExists → 500 instead of 412 (handle_move + trashbin restore) │
├──────────┼─────────┼────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ G9 │ NC │ Folder DELETE not row-recursive — orphan descendants stay live │
├──────────┼─────────┼────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ M5/M7 │ Native │ resolve_path_for_user mismatch — PUT writes, GET reads via lenient lookup, MOVE/DELETE can't find │
│ │ │ via strict │
├──────────┼─────────┼────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ M8 │ Native │ COPY discards destination filename — collides with source │
├──────────┼─────────┼────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ N2 │ Native │ LOCK creates the token, mutators don't check it — class-2 advertisement is aspirational │
└──────────┴─────────┴────────────────────────────────────────────────────────────────────────────────────────────────────┘
161 lines
7.7 KiB
Plaintext
161 lines
7.7 KiB
Plaintext
# =============================================================
|
||
# OxiCloud — Baseline: OCS user-info + provisioning
|
||
# =============================================================
|
||
# Group C from BASELINE_TESTS_NC_WEBDAV.md.
|
||
#
|
||
# /ocs/v{1,2}.php/cloud/user is what NC desktop reads after
|
||
# Login Flow v2 to learn its `data.id` — and that exact string
|
||
# is then spliced into every subsequent DAV path
|
||
# (`/remote.php/dav/files/{id}/…`). A regression in this shape
|
||
# breaks 100% of subsequent syncs.
|
||
#
|
||
# Coverage:
|
||
# C1 — GET /ocs/v1.php/cloud/user → statuscode 100 + payload
|
||
# C2 — GET /ocs/v2.php/cloud/user → statuscode 200 + payload
|
||
# C3 — GET /ocs/v1.php/cloud/users/admin (self provisioning)
|
||
# C5 — GET /ocs/v2.php/apps/files_sharing/api/v1/sharees shape
|
||
#
|
||
# Deferred:
|
||
# C4 — admin reading another user's provisioning profile.
|
||
# Needs a second user fixture wired into setup.hurl.
|
||
# Tracked as a TODO in BASELINE_TESTS_NC_WEBDAV.md §7.
|
||
#
|
||
# Setup pattern: this file mints its own app password inline
|
||
# (steps 1–2) so it is self-contained and resilient to test
|
||
# ordering. The mint uses the JWT-authenticated REST API
|
||
# (`POST /api/auth/app-passwords`); the NC-side username
|
||
# returned by the response is exactly the value the NC client
|
||
# would use as the HTTP Basic Auth username.
|
||
# =============================================================
|
||
|
||
|
||
# ─────────────────────────────────────────────────────────────
|
||
# Setup 1 — JWT login (gives us a Bearer token to mint the
|
||
# app password).
|
||
# ─────────────────────────────────────────────────────────────
|
||
POST {{base_url}}/api/auth/login
|
||
Content-Type: application/json
|
||
{ "username": "{{username}}", "password": "{{password}}" }
|
||
|
||
HTTP 200
|
||
[Captures]
|
||
jwt: jsonpath "$.access_token"
|
||
|
||
|
||
# ─────────────────────────────────────────────────────────────
|
||
# Setup 2 — Mint an app password for this test file. The
|
||
# response carries the plaintext password (shown
|
||
# exactly once) and the username to use in Basic Auth.
|
||
# ─────────────────────────────────────────────────────────────
|
||
POST {{base_url}}/api/auth/app-passwords
|
||
Authorization: Bearer {{jwt}}
|
||
Content-Type: application/json
|
||
{ "label": "nc_ocs_user_info hurl test" }
|
||
|
||
HTTP 200
|
||
[Captures]
|
||
nc_username: jsonpath "$.username"
|
||
nc_password: jsonpath "$.password"
|
||
ap_id: jsonpath "$.id"
|
||
[Asserts]
|
||
jsonpath "$.password" matches "^oxicloud-"
|
||
|
||
|
||
# ─────────────────────────────────────────────────────────────
|
||
# C1 — GET /ocs/v1.php/cloud/user
|
||
# data.id is what NC client splices into DAV URLs.
|
||
#
|
||
# Note: `handle_user_info` returns `statuscode: 200`
|
||
# regardless of /v1.php vs /v2.php (unlike capabilities,
|
||
# which switches on ocs_version). C1 and C2 therefore
|
||
# both assert 200 here — this is the actual server
|
||
# behaviour; if either diverges in future, this is the
|
||
# pin that catches it.
|
||
#
|
||
# Hurl JSONPath doesn't accept `-` in dotted form, so the
|
||
# assertion goes against `displayname` (the unhyphenated
|
||
# alias the handler also emits) rather than `display-name`.
|
||
# ─────────────────────────────────────────────────────────────
|
||
GET {{base_url}}/ocs/v1.php/cloud/user?format=json
|
||
[BasicAuth]
|
||
{{nc_username}}: {{nc_password}}
|
||
|
||
HTTP 200
|
||
[Asserts]
|
||
jsonpath "$.ocs.meta.status" == "ok"
|
||
jsonpath "$.ocs.meta.statuscode" == 200
|
||
jsonpath "$.ocs.meta.message" == "OK"
|
||
jsonpath "$.ocs.data.enabled" == true
|
||
jsonpath "$.ocs.data.id" == "{{username}}"
|
||
jsonpath "$.ocs.data.email" == "{{email}}"
|
||
jsonpath "$.ocs.data.displayname" exists
|
||
jsonpath "$.ocs.data.quota.used" exists
|
||
jsonpath "$.ocs.data.quota.total" exists
|
||
jsonpath "$.ocs.data.quota.free" exists
|
||
jsonpath "$.ocs.data.quota.relative" exists
|
||
|
||
|
||
# ─────────────────────────────────────────────────────────────
|
||
# C2 — GET /ocs/v2.php/cloud/user
|
||
# Same payload shape; OCS v2 envelope reports statuscode 200.
|
||
# ─────────────────────────────────────────────────────────────
|
||
GET {{base_url}}/ocs/v2.php/cloud/user?format=json
|
||
[BasicAuth]
|
||
{{nc_username}}: {{nc_password}}
|
||
|
||
HTTP 200
|
||
[Asserts]
|
||
jsonpath "$.ocs.meta.status" == "ok"
|
||
jsonpath "$.ocs.meta.statuscode" == 200
|
||
jsonpath "$.ocs.data.id" == "{{username}}"
|
||
jsonpath "$.ocs.data.email" == "{{email}}"
|
||
|
||
|
||
# ─────────────────────────────────────────────────────────────
|
||
# C3 — GET /ocs/v1.php/cloud/users/{userid} (self lookup)
|
||
# Full provisioning profile: groups, lastLogin, backend.
|
||
# ─────────────────────────────────────────────────────────────
|
||
GET {{base_url}}/ocs/v1.php/cloud/users/{{username}}?format=json
|
||
[BasicAuth]
|
||
{{nc_username}}: {{nc_password}}
|
||
|
||
HTTP 200
|
||
[Asserts]
|
||
jsonpath "$.ocs.meta.statuscode" == 100
|
||
jsonpath "$.ocs.data.id" == "{{username}}"
|
||
jsonpath "$.ocs.data.email" == "{{email}}"
|
||
jsonpath "$.ocs.data.groups" exists
|
||
jsonpath "$.ocs.data.backend" exists
|
||
jsonpath "$.ocs.data.lastLogin" exists
|
||
|
||
|
||
# ─────────────────────────────────────────────────────────────
|
||
# C5 — GET /ocs/v2.php/apps/files_sharing/api/v1/sharees
|
||
# Sharees autocomplete shape — NC desktop uses this to
|
||
# populate the share-dialog. Even with no shares set up
|
||
# yet, the envelope + array slots must exist.
|
||
# ─────────────────────────────────────────────────────────────
|
||
GET {{base_url}}/ocs/v2.php/apps/files_sharing/api/v1/sharees?format=json&search=ad&itemType=file
|
||
[BasicAuth]
|
||
{{nc_username}}: {{nc_password}}
|
||
|
||
HTTP 200
|
||
[Asserts]
|
||
jsonpath "$.ocs.meta.status" == "ok"
|
||
jsonpath "$.ocs.meta.statuscode" == 200
|
||
jsonpath "$.ocs.data.exact" exists
|
||
jsonpath "$.ocs.data.exact.users" exists
|
||
jsonpath "$.ocs.data.exact.groups" exists
|
||
jsonpath "$.ocs.data.users" exists
|
||
|
||
|
||
# ─────────────────────────────────────────────────────────────
|
||
# Teardown — Revoke the app password we minted (captured `ap_id`
|
||
# from the create response, no list+lookup needed).
|
||
# Keeps the test surface clean across re-runs.
|
||
# ─────────────────────────────────────────────────────────────
|
||
DELETE {{base_url}}/api/auth/app-passwords/{{ap_id}}
|
||
Authorization: Bearer {{jwt}}
|
||
|
||
HTTP 200
|