--- phase: 05-cloud-storage-backends plan: 01 subsystem: testing tags: [pytest, cloud-storage, google-drive, onedrive, webdav, cryptography, msal, xfail, nyquist] # Dependency graph requires: - phase: 04-folders-sharing-quotas provides: conftest.py fixture patterns (auth_user, admin_user, db_session, async_client) provides: - 19 xfail test stubs in backend/tests/test_cloud.py (CLOUD-01..07, SSRF D-17, IDOR SEC-08) - 4 cloud fixtures in backend/tests/conftest.py (mock_google_drive_creds, mock_onedrive_creds, mock_webdav_client, cloud_connection_factory) - 6 new PyPI package pins in backend/requirements.txt - 8 new Settings fields in backend/config.py for cloud OAuth and credential encryption affects: [05-02, 05-03, 05-04, 05-05, 05-06, 05-07, 05-08] # Tech tracking tech-stack: added: - cryptography>=41.0.0 - google-auth-oauthlib>=1.3.1 - google-api-python-client>=2.196.0 - msal>=1.36.0 - webdavclient3>=3.14.7 - cachetools>=5.3.0 patterns: - xfail(strict=False) Wave 0 Nyquist scaffolding — stubs xfail, never fail, until implementation turns them green - pytest_asyncio.fixture factory pattern for cloud_connection_factory (inner async function) - MagicMock with explicit method wiring (mock_webdav_client) key-files: created: - backend/tests/test_cloud.py - .planning/phases/05-cloud-storage-backends/deferred-items.md modified: - backend/requirements.txt - backend/config.py - backend/tests/conftest.py - .env.example key-decisions: - "frontend_url reused from Phase 2 — field already declared in Settings; no duplicate added" - "test_ssrf_validation parametrized with 5 cases (4 blocked + 1 valid) to match D-17 threat register" - "cloud_connection_factory accepts session as first arg (not fixture-injected) for flexibility across test patterns" patterns-established: - "Wave 0 stub body: only pytest.xfail('not implemented yet') — no assertions; strict=False prevents xpass CI breakage" - "Cloud fixture naming convention: mock_{provider}_creds for credential dicts, cloud_connection_factory for ORM rows" requirements-completed: - CLOUD-01 - CLOUD-02 - CLOUD-03 - CLOUD-04 - CLOUD-05 - CLOUD-06 - CLOUD-07 # Metrics duration: 5min completed: 2026-05-28 --- # Phase 5 Plan 01: Wave 0 Nyquist Scaffold Summary **19 xfail test stubs for CLOUD-01..07 + SSRF + IDOR, 4 cloud conftest fixtures, 6 new PyPI package pins (cryptography/google-auth-oauthlib/msal/webdavclient3), and 8 new cloud Settings fields** ## Performance - **Duration:** 5 min - **Started:** 2026-05-28T18:47:26Z - **Completed:** 2026-05-28T18:52:27Z - **Tasks:** 3 - **Files modified:** 5 ## Accomplishments - Added 6 Phase 5 package dependencies (cryptography, google-auth-oauthlib, google-api-python-client, msal, webdavclient3, cachetools) with exact version pins to requirements.txt - Added 8 new Settings fields to config.py (cloud_creds_key, google OAuth, OneDrive OAuth, backend_url, frontend_url already present) - Created test_cloud.py with 19 xfail stubs covering all Phase 5 requirements and security invariants - Added 4 cloud-specific conftest fixtures (mock_google_drive_creds, mock_onedrive_creds, mock_webdav_client, cloud_connection_factory) without modifying any existing fixture ## Task Commits 1. **Task 1: Add Phase 5 packages to requirements.txt and settings to config.py** - `a052ed4` (feat) 2. **Task 2: Create test_cloud.py with all 15 xfail stubs** - `231dfcd` (test) 3. **Task 3: Add cloud fixtures to conftest.py** - `b53ea86` (feat) ## Files Created/Modified - `/Users/nik/Documents/Progamming/document_scanner/backend/requirements.txt` - Added 6 cloud package pins - `/Users/nik/Documents/Progamming/document_scanner/backend/config.py` - Added Cloud Storage (Phase 5) settings block with 7 new fields (frontend_url reused) - `/Users/nik/Documents/Progamming/document_scanner/backend/tests/test_cloud.py` - Created with 15 named test stubs (19 collected including parametrize variants) - `/Users/nik/Documents/Progamming/document_scanner/backend/tests/conftest.py` - Appended 4 cloud fixtures - `/Users/nik/Documents/Progamming/document_scanner/.env.example` - Appended Cloud Storage Backends section ## Decisions Made - `frontend_url` was already declared in Settings (Phase 2, password reset links) — no duplicate field added; the plan noted "shared field" - `test_ssrf_validation` was parametrized with 5 cases (4 RFC-1918/loopback blocked + 1 valid URL that should pass) matching D-17 threat register, resulting in 19 total collected tests vs 15 named stubs - `cloud_connection_factory` returns an inner async function that takes `session` as first argument, giving callers full control over which session to use (not locked to db_session fixture) ## Deviations from Plan None - plan executed exactly as written. ## Issues Encountered **Pre-existing (out of scope):** `tests/test_extractor.py::test_extract_docx` fails with `ModuleNotFoundError: No module named 'docx'` — python-docx is not installed in the local Python 3.9.6 environment. This failure was present before Plan 05-01 started and is unrelated to Phase 5 changes. Documented in `deferred-items.md`. Resolution: install python-docx locally or run tests inside the Docker container where all requirements.txt packages are installed. ## User Setup Required Phase 5 introduces new environment variables for cloud OAuth. Add to `.env` before testing cloud provider connections: ``` CLOUD_CREDS_KEY=<32+ byte random key — generate with: python3 -c "import secrets; print(secrets.token_urlsafe(32))"> GOOGLE_CLIENT_ID= GOOGLE_CLIENT_SECRET= ONEDRIVE_CLIENT_ID= ONEDRIVE_CLIENT_SECRET= ONEDRIVE_TENANT_ID=common BACKEND_URL=http://localhost:8000 FRONTEND_URL=http://localhost:5173 ``` See `.env.example` for full documentation. All new settings have safe defaults so the app boots without cloud credentials configured. ## Next Phase Readiness - Wave 0 Nyquist scaffold complete: all 19 stubs xfail cleanly, full suite at 172 passed / 43 xfailed - Plan 05-02 can immediately begin implementing `cloud_utils.py` (HKDF encryption) — `test_credential_round_trip` and `test_credentials_enc_not_exposed` are ready to turn green - `cloud_connection_factory` fixture ready for use in Plans 05-03 through 05-06 integration tests ## Known Stubs All 15 named stubs (19 with parametrize) in `backend/tests/test_cloud.py` are intentional scaffolding. Each will be promoted to a real test by the plan that implements the corresponding behavior: | Stub | Plan to resolve | |------|----------------| | test_credential_round_trip, test_credentials_enc_not_exposed | 05-02 | | test_connect_google_drive, test_oauth_callback_valid_state/invalid_state, test_webdav_connect_validates, test_ssrf_validation, test_ssrf_link_local | 05-03 | | test_invalid_grant_sets_requires_reauth, test_disconnect_deletes_credentials | 05-04 | | test_cloud_upload_no_presigned, test_factory_returns_correct_backend | 05-05 | | test_connection_status_display, test_admin_cannot_see_credentials, test_cross_user_idor | 05-06 | ## Self-Check: PASSED Files verified present: - backend/tests/test_cloud.py: FOUND - backend/tests/conftest.py: FOUND (contains cloud_connection_factory) - backend/requirements.txt: FOUND (contains cryptography) - backend/config.py: FOUND (contains cloud_creds_key) Commits verified: - a052ed4: feat(05-01): add Phase 5 cloud storage packages and config settings — FOUND - 231dfcd: test(05-01): create test_cloud.py with all 15 Phase 5 xfail stubs — FOUND - b53ea86: feat(05-01): add Phase 5 cloud fixtures to conftest.py — FOUND --- *Phase: 05-cloud-storage-backends* *Completed: 2026-05-28*