Files
kite/.planning/phases/12.1-fix-nextcloud-root-listing-and-sync-visibility/12.1-VALIDATION.md
T
curo1305 779b05086b test(12.1-04): add opt-in live Nextcloud smoke test suite
- backend/tests/test_nextcloud_live.py: three read-only live tests behind
  live_nextcloud marker (adapter root metadata, sanitized diagnostic, connection-ID
  browse as testuser@docuvault.example with IDOR/admin negatives)
- backend/pytest.ini: register live_nextcloud marker; addopts excludes it from
  ordinary runs so CI never contacts Nextcloud without explicit selection
- 12.1-VALIDATION.md: live test contract, commands, skip behaviour, sanitized
  7-of-10 probe result, and threat references T-12.1-16 through T-12.1-20
- Manifest status: unconfirmed — Task 2 exact-name gate pending owner decision
2026-06-22 09:02:42 +02:00

141 lines
6.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 0103 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