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
This commit is contained in:
+140
@@ -0,0 +1,140 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user