test(12.1): verify live cloud browse integration

Phase 12.1 close — docs, version, and security evidence:
- Bump version 0.2.5 → 0.2.6 in backend/main.py, frontend/package.json
- CLAUDE.md: update current state to Phase 12.1 complete v0.2.6
- README.md: add Phase 12.1 corrections, live smoke variable names, Phase 13/14 exclusions
- RUNBOOK.md: add live smoke invocation, warning semantics table, fixture reconciliation procedure
- SECURITY.md: add Phase 12.1 Plan 04 threat table (T-12.1-16..20, T-12.1-SC) and gate evidence
- 12.1-VALIDATION.md: mark Plan 04 complete with final gate evidence
This commit is contained in:
curo1305
2026-06-22 09:46:25 +02:00
parent 6d294ea7e6
commit 46b7ea6c12
7 changed files with 105 additions and 5 deletions
+48
View File
@@ -734,6 +734,54 @@ WHERE cc.id IS NULL;
- **Credential fields:** `credentials_enc` is never included in browse API responses. Verify with: `grep -r "credentials_enc" backend/api/cloud/` — must only appear in connection create/update service code, never in response schemas.
- **SSRF guard:** `storage/cloud_utils.validate_cloud_url` blocks RFC1918, link-local, file://, and cloud metadata IPs. All WebDAV/Nextcloud URLs pass through this guard before any connection is established.
### Phase 12.1 Live Smoke Tests (Opt-In, Read-Only)
The `live_nextcloud` pytest marker selects opt-in tests that use real Nextcloud credentials. These tests are **excluded from ordinary CI runs** (`addopts = -m "not live_nextcloud"` in `pytest.ini`).
**Running the live smoke:**
```bash
# Set in .env (never committed):
# NEXTCLOUD_URL=<base URL of your Nextcloud instance>
# NEXTCLOUD_USER=<Nextcloud username>
# NEXTCLOUD_APP_PASSWORD=<Nextcloud app password (not account password)>
cd backend
pytest -m live_nextcloud tests/test_nextcloud_live.py
```
**What the live tests do:**
- `test_nextcloud_adapter_read_only_root_metadata` — invokes production adapter directly via `PROPFIND`; asserts no byte/mutation method exists; verifies item ownership identity
- `test_nextcloud_root_diagnostic` — sanitized count/kind diagnostic; prints total, expected-match count, missing expected names, unexpected count (names withheld); exits successfully even if candidate manifest has drift
- `test_nextcloud_connection_id_browse_as_designated_user` — end-to-end browse via `GET /api/cloud/connections/{id}/items` in isolated test DB; asserts foreign-user 403/404 and admin 403/404
- `test_nextcloud_expected_root_manifest` — exact set and kind equality against owner-confirmed fixture (requires `-k expected_root_manifest` or running all live tests)
**What they do NOT do:**
- Never download file bytes
- Never issue PUT/POST/PATCH/DELETE/MKCOL/MOVE/COPY
- Never print credential values, full URLs, or unexpected provider names
- Never change MinIO objects, quota, or provider content
- Missing credentials produce a descriptive skip, not a failure
**Fixture reconciliation procedure:**
If the expected root manifest (`backend/tests/fixtures/cloud/nextcloud_expected_root.json`) drifts:
1. Run `pytest -m live_nextcloud -k root_diagnostic` and read the sanitized output (missing expected names only)
2. Compare the missing names with the Nextcloud web UI (do not print the unexpected names)
3. Update the fixture with the owner-confirmed correct names and set `owner_confirmed: true`
4. Never record unexpected live names in any committed artifact
### Phase 12 / 12.1 Warning Semantics
A `warning` freshness state in the browse response means the last reconciliation was incomplete or failed. **The UI shows a warning indicator; cached rows remain visible.** This is not an error — it is honest state:
| State | Meaning | UI behavior |
|-------|---------|-------------|
| `fresh` | Last `complete=True` listing succeeded | Green freshness indicator |
| `refreshing` | Background refresh in progress | Spinner |
| `warning` | Last listing was incomplete or failed | Warning banner; cached rows visible |
| `stale` | Transport error during load | Error message; no freshness indicator |
`complete=False` from any provider ALWAYS produces `warning`, never `fresh`. `last_refreshed_at` is not advanced for incomplete listings.
### Phase 12 Deferred Items
| Item | Current state | Future path |