# Phase 12.1: Fix Nextcloud Root Listing and Sync Visibility — Validation Matrix **Status:** Plan 04 Task 1 complete. Awaiting Task 2 (owner fixture reconciliation). **Updated:** 2026-06-22 --- ## Nyquist Validation Matrix | Requirement | Test type | Coverage | Evidence | |------------|-----------|----------|---------| | CONN-04: Connection-ID IDOR | Security-negative | All plans | `test_foreign_user_cannot_browse_cloud_item`, `test_admin_cannot_browse_cloud_connection` | | CLOUD-01: Provider-neutral browse | Contract suite | P01 | `test_cloud_provider_contract.py` — 48 parametrized tests across all 4 providers | | CLOUD-01: Root listing correct | Live smoke | P04 | `test_nextcloud_root_diagnostic` — sanitized counts and kind breakdown | | CLOUD-01: Connection-ID API browse | End-to-end live | P04 | `test_nextcloud_connection_id_browse_as_designated_user` | | CACHE-01: Freshness truthful | TDD GREEN | P02 | `test_incomplete_listing_never_marks_folder_fresh`, `test_browse_complete_false_never_sets_fresh` | | SYNC-01: Frontend normalized shape | TDD GREEN | P03 | `CloudFolderView.test.js` — kind, provider_item_id, verbatim freshness | --- ## Live Test Contract (Plan 04, Task 1) ### Marker ``` live_nextcloud ``` Tests tagged with this marker are excluded from ordinary pytest runs via `addopts = -m "not live_nextcloud"` in `backend/pytest.ini`. Select explicitly with: ```bash cd backend && pytest -m live_nextcloud tests/test_nextcloud_live.py ``` ### Required environment variables (values in ignored `.env` — never committed) ``` NEXTCLOUD_URL # Nextcloud server base URL NEXTCLOUD_USER # Nextcloud username NEXTCLOUD_APP_PASSWORD # Nextcloud app password ``` If any variable is absent, all live tests skip with a message naming only the variable keys. ### Tests included | Test | Purpose | |------|---------| | `test_nextcloud_adapter_read_only_root_metadata` | Verifies the production adapter lists root via canonical four-argument signature, returns CloudListing, items carry trusted caller identity, no byte/mutation method is accessible | | `test_nextcloud_root_diagnostic` | Sanitized count/kind/match diagnostic — nonblocking while manifest is unconfirmed | | `test_nextcloud_connection_id_browse_as_designated_user` | End-to-end browse via `GET /api/cloud/connections/{id}/items` as `testuser@docuvault.example` in isolated test DB; asserts foreign-user and admin denial | ### Read-only / no-mutation contract The network guard integrated in the test design blocks these HTTP methods before dispatch: `GET` (byte content), `PUT`, `POST`, `PATCH`, `DELETE`, `MKCOL`, `MOVE`, `COPY`. Only `PROPFIND`, `OPTIONS`, and `HEAD` are permitted. Mutation methods are asserted absent on the adapter object. No MinIO, quota, or object-storage change is made. ### Threat coverage | Threat ID | Mitigation | Test | |-----------|-----------|------| | T-12.1-16 | Credentials/full URL excluded from output | `_assert_no_secrets_in_string` on captured output; no values in parametrize IDs, assertions, or exceptions | | T-12.1-17 | No byte download or mutation | `_NetworkMethodGuard`; mutation method absence assertion | | T-12.1-18 | Designated regular user only; IDOR/admin negatives | `test_nextcloud_connection_id_browse_as_designated_user` | | T-12.1-19 | Count-only acceptance blocked | Exact name gate deferred to Task 2 checkpoint | | T-12.1-20 | Unexpected names never printed | `print(f"Unexpected item count: {unexpected_count} (names withheld)")` | --- ## Sanitized Probe Result (from 12.1-RESEARCH.md, pre-Plan-04) | Check | Result | |-------|--------| | Endpoint reachable | Yes | | Authentication accepted | Yes | | HTTP status | 207 Multi-Status | | Direct root children returned | 10 | | Exact supplied-name matches | 7 of 10 | | Supplied names not exactly matched | `Manual Nextcloud.png`, `Nextcloud Intro.mp4`, `Template credits.md` | | Additional returned-name count | 3; names withheld | | Byte download or mutation | None | --- ## Manifest Status: UNCONFIRMED — Task 2 Pending The exact-name acceptance gate is blocked. The three supplier-expected names that were not exactly matched in the prior probe are: - `Manual Nextcloud.png` - `Nextcloud Intro.mp4` - `Template credits.md` The project owner must confirm (via Task 2 checkpoint) which source is authoritative before the fixture `backend/tests/fixtures/cloud/nextcloud_expected_root.json` is created and `test_nextcloud_expected_root_manifest` is enabled. Unexpected provider names returned for the 3 unmatched slots are withheld here and must not appear in any committed artifact, log, or response. --- ## Plans 01–03 Evidence Summary ### Plan 01 — Adapter Contract - `test_cloud_provider_contract.py` — 48 tests: all pass - `test_cloud_backends.py` — 67 tests: all pass - `test_webdav_backend.py` — 29 tests: all pass - `test_cloud_security.py` — 19 tests: all pass - Full backend suite: 585 pass (1 pre-existing `test_extract_docx` failure, unrelated) ### Plan 02 — Freshness Gate - `test_cloud_items.py` — 45 tests: all pass - `test_cloud.py` — 25 tests: all pass - `test_cloud_security.py` — 22 tests: all pass - Full backend suite: 595 pass (1 pre-existing failure, unrelated) - No unconditional `refresh_state="fresh"` in `browse.py` or `cloud_tasks.py` ### Plan 03 — Frontend Contract - `CloudFolderView.test.js` — 23 tests: all pass - `CloudProviderTreeItem.test.js` — 8 tests: all pass - `CloudFolderTreeItem.test.js` — 16 tests: all pass - `StorageBrowser.skeleton.test.js` — 22 tests: all pass - `cloudConnections.test.js` — 23 tests: all pass - `CloudBreadcrumbNavigation.test.js` — 8 tests: all pass - Full frontend suite: 369 pass - Production build: clean ### Plan 04 Task 1 — Live Test Scaffold - `test_nextcloud_live.py` created with `live_nextcloud` marker - Marker excluded from default runs via `pytest.ini` - Three live tests: adapter metadata, sanitized root diagnostic, connection-ID end-to-end - Task 2 (exact-name acceptance) deferred to checkpoint — fixture not yet created