--- phase: 01-infrastructure-foundation plan: 02 subsystem: testing tags: - testing - wave-0 - pytest - tdd - xfail dependency_graph: requires: - 01-01 # config.py Pydantic Settings (used in conftest imports) provides: - Wave 0 test scaffolds for Plans 03, 04, 05 - Async pytest fixtures (db_session, async_client) ready for Plan 03+ affects: - backend/tests/conftest.py - backend/tests/test_health.py - backend/tests/test_documents.py - backend/tests/test_storage.py - backend/tests/test_alembic.py tech_stack: added: - pytest-asyncio (async_mode=auto, already in requirements.txt) - httpx.AsyncClient + ASGITransport (test transport) - sqlalchemy.ext.asyncio create_async_engine + async_sessionmaker (test fixtures) - aiosqlite (in-memory SQLite for unit test engine) patterns: - "@pytest_asyncio.fixture for async test fixtures" - "try/except ImportError: pytest.skip() for graceful degradation before Plan 03" - "xfail(strict=False) for Wave 0 scaffolds targeting future plan deliverables" - "AsyncClient + ASGITransport(app=app) + dependency_overrides[get_db] override" key_files: created: - backend/tests/test_storage.py - backend/tests/test_alembic.py modified: - backend/tests/conftest.py - backend/tests/test_health.py - backend/tests/test_documents.py decisions: - "async_client fixture uses NEW name to avoid collision with legacy sync client fixture" - "All new async fixtures degrade gracefully via pytest.skip(ImportError) before Plan 03" - "Sync fixtures (isolated_data_dir, client, sample_txt, sample_pdf) retained verbatim" - "test_alembic.py uses stdlib sqlite3 (not aiosqlite) for table inspection post-migration" - "test_documents.py keeps all 9 original sync tests AND adds 10 async counterparts" metrics: duration: "~9 minutes" completed: "2026-05-22T07:08:49Z" tasks_completed: 3 tasks_total: 3 files_created: 2 files_modified: 3 --- # Phase 1 Plan 02: Wave 0 Test Scaffolds — Summary **One-liner:** Async SQLAlchemy + httpx fixtures added to conftest; 19 xfail Wave 0 tests across 5 files scaffold Plan 03-05 contracts before implementation. ## What Was Built This plan authored all Wave 0 test scaffolds identified in `01-VALIDATION.md` before any implementation lands. Every test that depends on Plan 03-05 code is marked `xfail(strict=False)` so the suite stays green between waves. Later executors remove the xfail markers as their code lands. ### Files Created | File | Purpose | Tests | |------|---------|-------| | `backend/tests/test_storage.py` | Wave 0 STORE-02 unit tests | 6 xfail tests | | `backend/tests/test_alembic.py` | Wave 0 migration integration tests | 2 xfail tests | ### Files Modified | File | Change | Tests Added | |------|--------|-------------| | `backend/tests/conftest.py` | Added `db_session` + `async_client` async fixtures | 0 (fixture-only) | | `backend/tests/test_health.py` | Added `test_health_checks_postgres_and_minio` | 1 xfail | | `backend/tests/test_documents.py` | Added 9 async port tests + 1 persistence test | 10 xfail | ### Total Wave 0 xfail Inventory | File | xfail count | Unblocked by | |------|-------------|--------------| | test_storage.py | 6 | Plan 04 | | test_alembic.py | 2 | Plan 03 | | test_health.py | 1 | Plan 05 | | test_documents.py | 10 | Plan 05 | | **Total** | **19** | — | ### Fixture Inventory (conftest.py) | Fixture | Type | Scope | Status | |---------|------|-------|--------| | `isolated_data_dir` | sync autouse | function | Retained (Plan 05 removes) | | `client` | sync TestClient | function | Retained (Plan 05 removes) | | `sample_txt` | sync | function | Retained | | `sample_pdf` | sync | function | Retained | | `db_session` | async | function | New — skips until Plan 03 | | `async_client` | async | function | New — skips until Plan 03 | ### Test Functions Added **test_storage.py** (all xfail → Plan 04): - `test_object_key_schema` — regex `^[^/]+/[^/]+/{uuid4}(\.ext)?$` against `put_object` return value - `test_filename_not_in_object_key` — asserts "invoice", "Q3", "secret" not in key - `test_storage_backend_abc_methods` — incomplete subclass raises `TypeError` - `test_get_storage_backend_returns_minio` — factory returns `MinIOBackend` - `test_put_object_uses_asyncio_to_thread` — `asyncio.to_thread` was called with `_client.put_object` - `test_minio_backend_health_check_returns_bool` — returns `True` / `False` on ok/exception **test_alembic.py** (all xfail → Plan 03): - `test_migration_creates_all_tables` — all 11 v1 tables: users, quotas, refresh_tokens, folders, documents, topics, document_topics, shares, audit_log, cloud_connections, groups - `test_documents_user_id_nullable` — `PRAGMA table_info(documents)` confirms `notnull=0` **test_health.py** (1 xfail → Plan 05): - `test_health_checks_postgres_and_minio` — asserts `checks.postgres == "ok"` and `checks.minio == "ok"` **test_documents.py** (10 xfail → Plan 05): - `test_upload_txt_no_classify_async` - `test_upload_pdf_no_classify_async` - `test_list_documents_async` - `test_list_documents_filter_by_topic_async` - `test_get_document_async` - `test_get_document_not_found_async` - `test_delete_document_async` - `test_delete_document_not_found_async` - `test_upload_empty_file_async` - `test_upload_persists_to_postgres_and_minio_async` — UUID id regex + GET round-trip ## Commits | Hash | Message | |------|---------| | `1f675fc` | feat(01-02): add async db_session and async_client fixtures to conftest.py | | `27fa0d4` | test(01-02): add Wave 0 scaffolds test_storage.py and test_alembic.py | | `d856a2e` | test(01-02): extend test_health.py and port test_documents.py to async client | ## Deviations from Plan None — plan executed exactly as written. The `async_client` fixture was named `async_client` (not `client`) as specified in the plan action to avoid collision with the legacy sync `client` fixture. This matches the plan's intent. ## Known Stubs None. All test files reference not-yet-existing modules inside `try/except ImportError: pytest.skip()` blocks — this is intentional Wave 0 scaffold behavior, not stubs. ## Threat Flags None. All test data is synthetic (no real PII). aiosqlite DB lives in `tmp_path` and `:memory:` only (T-01-02-01 mitigated). No new network endpoints introduced. ## Self-Check: PASSED Files exist: - `/Users/nik/Documents/Progamming/document_scanner/backend/tests/test_storage.py` — FOUND - `/Users/nik/Documents/Progamming/document_scanner/backend/tests/test_alembic.py` — FOUND - `/Users/nik/Documents/Progamming/document_scanner/backend/tests/conftest.py` — FOUND (modified) - `/Users/nik/Documents/Progamming/document_scanner/backend/tests/test_health.py` — FOUND (modified) - `/Users/nik/Documents/Progamming/document_scanner/backend/tests/test_documents.py` — FOUND (modified) Commits verified in git log: - `1f675fc` — FOUND - `27fa0d4` — FOUND - `d856a2e` — FOUND xfail count: 19 (>= minimum 18 required) Syntax check: PASSED (all 5 files parsed cleanly by Python AST)