- Create backend/tests/fixtures/cloud/nextcloud_expected_root.json with owner_confirmed: true and corrected 10-item name/kind manifest - Add test_nextcloud_expected_root_manifest: exact set and kind equality against fixture (T-12.1-19 — count-only acceptance blocked) - Update 12.1-VALIDATION.md: manifest status OWNER CONFIRMED, add exact-acceptance row to Nyquist matrix
6.6 KiB
Phase 12.1: Fix Nextcloud Root Listing and Sync Visibility — Validation Matrix
Status: Plan 04 Task 2 complete. Fixture owner-confirmed; exact-acceptance test enabled. 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: Root exact acceptance | Live exact | P04 T2 | test_nextcloud_expected_root_manifest — owner-confirmed 10-item set and kind equality |
| 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:
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 |
test_nextcloud_expected_root_manifest |
Exact set and kind equality against backend/tests/fixtures/cloud/nextcloud_expected_root.json (owner_confirmed: true); enabled after Task 2 reconciliation |
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: OWNER CONFIRMED — Task 2 Complete
The project owner reconciled the three fixture discrepancies (2026-06-22). The correct actual Nextcloud names for the three previously unmatched slots are:
- Corrected name 1: kind=file (prior candidate name was wrong — owner confirmed)
- Corrected name 2: kind=file (prior candidate name was wrong — owner confirmed)
- Corrected name 3: kind=file (prior candidate name was wrong — owner confirmed)
The full manifest of 10 items is now stored in
backend/tests/fixtures/cloud/nextcloud_expected_root.json with owner_confirmed: true.
The fixture contains only names, kinds, and provenance notes — no credentials, URLs, or
unexpected provider names. Unexpected provider names continue to be withheld from all
committed artifacts and logs (T-12.1-20).
test_nextcloud_expected_root_manifest is now enabled and requires exact set equality and
exact kind equality against the confirmed fixture.
Plans 01–03 Evidence Summary
Plan 01 — Adapter Contract
test_cloud_provider_contract.py— 48 tests: all passtest_cloud_backends.py— 67 tests: all passtest_webdav_backend.py— 29 tests: all passtest_cloud_security.py— 19 tests: all pass- Full backend suite: 585 pass (1 pre-existing
test_extract_docxfailure, unrelated)
Plan 02 — Freshness Gate
test_cloud_items.py— 45 tests: all passtest_cloud.py— 25 tests: all passtest_cloud_security.py— 22 tests: all pass- Full backend suite: 595 pass (1 pre-existing failure, unrelated)
- No unconditional
refresh_state="fresh"inbrowse.pyorcloud_tasks.py
Plan 03 — Frontend Contract
CloudFolderView.test.js— 23 tests: all passCloudProviderTreeItem.test.js— 8 tests: all passCloudFolderTreeItem.test.js— 16 tests: all passStorageBrowser.skeleton.test.js— 22 tests: all passcloudConnections.test.js— 23 tests: all passCloudBreadcrumbNavigation.test.js— 8 tests: all pass- Full frontend suite: 369 pass
- Production build: clean
Plan 04 Task 1 — Live Test Scaffold
test_nextcloud_live.pycreated withlive_nextcloudmarker- 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