--- phase: "13" plan: "01" subsystem: cloud-testing tags: [tdd, red-phase, cloud-mutations, provider-contracts, audit, reconnect] dependency_graph: requires: - "12.1-04 (provider contract foundation)" - "backend/storage/cloud_base.py (CloudResourceAdapter interface)" - "backend/tests/test_cloud_security.py (security negative pattern)" provides: - "Red backend test contracts for all Phase 13 mutation, reconnect, and audit semantics" - "Four-provider mutable-operation parity RED coverage" affects: - "backend/tests/test_cloud_mutations.py" - "backend/tests/test_cloud_reconnect.py" - "backend/tests/test_cloud_audit.py" - "backend/tests/test_cloud_backends.py" - "backend/tests/test_cloud_provider_contract.py" tech_stack: added: [] patterns: - "TDD RED phase — all new tests fail against current codebase" - "Typed kind/reason result body contract (normalized dict pattern)" - "IDOR/admin/credential exclusion pattern from test_cloud_security.py" - "Parametrized four-provider contract pattern from test_cloud_provider_contract.py" key_files: created: - "backend/tests/test_cloud_mutations.py" - "backend/tests/test_cloud_reconnect.py" - "backend/tests/test_cloud_audit.py" modified: - "backend/tests/test_cloud_backends.py" - "backend/tests/test_cloud_provider_contract.py" decisions: - "Typed kind/reason dict bodies required for every mutation outcome — frontend must never infer from raw HTTP status alone" - "Reconnect patches existing CloudConnection row in-place (CONN-01) — creating new row breaks item identity" - "OneDrive _refresh_token must return new credentials dict for service-layer handoff (CONN-02)" - "Audit rows are metadata-only: no access_token, refresh_token, credentials_enc, or raw file bytes allowed in metadata_ JSONB" - "Google Drive full 'drive' scope required for Phase 13 (D-17) — 'drive.file' restricts to app-created items only" - "Conflict normalization happens inside each adapter — no router or service layer pattern-matches raw provider errors" metrics: duration: "8m" completed_date: "2026-06-22" tasks_completed: 2 files_created: 3 files_modified: 2 tests_added_task1: 46 tests_added_task2_new: 128 status: complete --- # Phase 13 Plan 01: Red Backend Contract Suites Summary **One-liner:** Red TDD contracts for Phase 13 cloud mutation, reconnect, credential-refresh persistence, and audit-secrecy semantics across all four providers. ## What Was Built ### Task 1 — Red API and Audit Contracts (3 new test files) **`backend/tests/test_cloud_mutations.py`** (commit: efb5964) Failing integration tests for the Phase 13 mutation endpoint contracts: - Open/preview: authorized-download-only, binary-only preview (D-02, D-18), IDOR block, credential exclusion - Upload: typed `{kind: 'conflict', reason: 'name_collision'}` body required — no silent overwrite (D-03) - Create folder: typed 201 success body with `provider_item_id` and `kind='folder'`; auto-name on collision (D-05) - Rename: typed success body with `kind` field; stale-etag returns `{kind: 'stale', reason: 'item_changed'}` (D-07) - Move: same-connection success; `{kind: 'invalid_destination'}` for self-move and cross-connection (D-08, D-09) - Delete: `{kind: 'deleted', reason: 'trashed'|'permanent'}` typed result (D-11); IDOR block; credential exclusion - Connection-state bodies: `{kind: 'offline'}` for transient outage, `{kind: 'reauth_required'}` for auth failure (D-13, D-15) - `{kind: 'unsupported_operation'}` for read-only providers (D-18) **`backend/tests/test_cloud_reconnect.py`** (commit: efb5964) Failing integration tests for CONN-01 through CONN-03 and D-12 through D-16: - CONN-01: reconnect patches the existing row — row count must not increase; UUID must not change - CONN-02: refreshed credentials are encrypted and stored in `credentials_enc`; no plaintext tokens in the blob - CONN-03: reconnect response never contains `access_token`, `refresh_token`, `credentials_enc`, or `client_secret` - D-12/D-13: explicit health endpoint (`GET /health`) with typed status; explicit test action (`POST /test`) - D-14: reconnect preserves cached CloudItems as stale — `deleted_at` must remain null - D-15: transient provider outage must not erase credentials or delete cached metadata - D-16: disconnect removes `credentials_enc` and all connection-scoped CloudItems; foreign-user IDOR blocked **`backend/tests/test_cloud_audit.py`** (commit: efb5964) Failing audit secrecy and accuracy tests (T-13-02, T-13-05): - Every successful operation (reconnect, open, upload, create-folder, rename, move, delete) must write a typed audit row - Metadata-only rule: no `access_token`, `refresh_token`, `credentials_enc`, raw token prefixes (`ya29.`, `1//`) in metadata_ - False-event prevention: conflicted/rejected operations must NOT write false success audit rows - Admin audit log viewer must scrub any credential leak in existing rows (defense-in-depth) - No base64-encoded binary content in audit metadata (raw file bytes never in audit payloads) ### Task 2 — Provider Contract Extensions (2 modified test files) **`backend/tests/test_cloud_backends.py`** (commit: fd6b561) Added four provider-specific RED classes for mutable operations: - `TestGoogleDriveMutableContract`: create/rename/move/delete/upload async assertions; delete normalizes `{kind: 'deleted', reason: 'trashed'|'permanent'}`; create-folder collision returns `{kind: 'conflict'}`; rename stale etag returns `{kind: 'stale'}`; D-17 expanded Drive scope assertion (`SCOPES` class attribute required) - `TestOneDriveMutableContract`: same method assertions; CONN-02 `_refresh_token` must return new credentials dict for service-layer handoff; delete returns `{kind: 'deleted', reason: 'permanent'}` (no Graph trash API); nextLink SSRF guard (follow only `graph.microsoft.com`) - `TestNextcloudMutableContract`: method assertions; create-folder SSRF guard for redirect attempts; delete defaults to `permanent` - `TestWebDAVMutableContract`: method assertions; delete returns `permanent`; move SSRF validation for Destination header; no `cloud_items` imports in module source (provider-neutral contract) **`backend/tests/test_cloud_provider_contract.py`** (commit: fd6b561) Added two new Phase 13 contract classes parametrized over all four providers: - `TestMutableAdapterContract`: method existence, async enforcement, canonical signatures for `create_folder`, `rename`, `move`, `delete`, `upload_file` — all with caller identity (`connection_id`, `user_id`) where required; no direct `cloud_items` imports in any adapter; no browse-time byte transfer; unsupported capabilities explicitly disclosed in `get_capabilities` (cloud_base.ACTIONS must include mutation verbs) - `TestMutableAdapterResultNormalization`: normalized result dict contract, docstring requirement, conflict normalization method requirement ## Test Results All Phase 13 RED tests fail as expected: ``` test_cloud_mutations.py — 26 tests, all fail (routes do not exist) test_cloud_reconnect.py — 12 tests, all fail (Phase 13 reconnect/health routes absent) test_cloud_audit.py — 8 tests, all fail (Phase 13 audit writes absent) test_cloud_backends.py — 56 prior tests PASS; Phase 13 RED classes fail on first assertion test_cloud_provider_contract.py — prior tests PASS; Phase 13 RED classes fail on method existence ``` ## Deviations from Plan None — plan executed exactly as written. ## Known Stubs None. This plan only creates test files; no implementation code was written. ## Threat Flags No new network endpoints, auth paths, or schema changes introduced. Test files only. ## Self-Check: PASSED - `backend/tests/test_cloud_mutations.py` — FOUND - `backend/tests/test_cloud_reconnect.py` — FOUND - `backend/tests/test_cloud_audit.py` — FOUND - Commit efb5964 — FOUND (Task 1) - Commit fd6b561 — FOUND (Task 2) - All pre-existing tests still pass (56/56 in test_cloud_backends.py prior coverage)