From 213afec6b354af931661640314a98e4ecb009834 Mon Sep 17 00:00:00 2001 From: curo1305 Date: Fri, 22 May 2026 09:10:27 +0200 Subject: [PATCH] =?UTF-8?q?docs(01-02):=20complete=20Wave=200=20test=20sca?= =?UTF-8?q?ffolds=20plan=20=E2=80=94=20SUMMARY,=20STATE,=20ROADMAP?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Create 01-02-SUMMARY.md: 19 total xfail tests across 5 files, 3 task commits documented, no deviations - STATE.md: advance to plan 3/5, update progress to 40%, record decisions for async_client naming and xfail(strict=False) pattern - ROADMAP.md: mark 01-02-PLAN.md complete, update progress table to 2/5 --- .planning/ROADMAP.md | 4 +- .planning/STATE.md | 22 +-- .../01-02-SUMMARY.md | 167 ++++++++++++++++++ 3 files changed, 181 insertions(+), 12 deletions(-) create mode 100644 .planning/phases/01-infrastructure-foundation/01-02-SUMMARY.md diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index 1a15c4f..af222af 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -28,7 +28,7 @@ _Last updated: 2026-05-21_ **Plans**: 5 plans - [x] 01-01-PLAN.md — Docker Compose service topology + Postgres init + Pydantic Settings + requirements -- [ ] 01-02-PLAN.md — Wave 0 test scaffolds (xfail/skip stubs) + async pytest fixtures +- [x] 01-02-PLAN.md — Wave 0 test scaffolds (xfail/skip stubs) + async pytest fixtures - [ ] 01-03-PLAN.md — SQLAlchemy ORM models + async engine + Alembic async migration (incl. alembic upgrade head) - [ ] 01-04-PLAN.md — StorageBackend ABC + MinIO backend + rewritten async services/storage.py - [ ] 01-05-PLAN.md — Lifespan + /health + API cutover + Celery worker + walking-skeleton e2e verify @@ -110,7 +110,7 @@ _Last updated: 2026-05-21_ | Phase | Plans Complete | Status | Completed | |-------|----------------|--------|-----------| -| 1. Infrastructure Foundation | 1/5 | In Progress | - | +| 1. Infrastructure Foundation | 2/5 | In Progress | - | | 2. Users & Authentication | 0/? | Not started | - | | 3. Document Migration & Multi-User Isolation | 0/? | Not started | - | | 4. Folders, Sharing, Quotas & Document UX | 0/? | Not started | - | diff --git a/.planning/STATE.md b/.planning/STATE.md index 7ed8d74..69ca398 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -4,13 +4,13 @@ milestone: v1.0 milestone_name: milestone current_phase: 1 status: executing -last_updated: "2026-05-22T06:59:23Z" +last_updated: "2026-05-22T07:10:00Z" progress: total_phases: 5 completed_phases: 0 total_plans: 5 - completed_plans: 1 - percent: 20 + completed_plans: 2 + percent: 40 --- # Project State @@ -24,7 +24,7 @@ progress: | Phase | Name | Status | |---|---|---| -| 1 | Infrastructure Foundation | In Progress (1/5 plans) | +| 1 | Infrastructure Foundation | In Progress (2/5 plans) | | 2 | Users & Authentication | Not Started | | 3 | Document Migration & Multi-User Isolation | Not Started | | 4 | Folders, Sharing, Quotas & Document UX | Not Started | @@ -33,10 +33,10 @@ progress: ## Current Position Phase: 1 (Infrastructure Foundation) — EXECUTING -Plan: 2 of 5 +Plan: 3 of 5 **Phase:** 01-infrastructure-foundation -**Plan:** 01-01 COMPLETE → advancing to 01-02 -**Progress:** ██░░░░░░░░ 20% +**Plan:** 01-02 COMPLETE → advancing to 01-03 +**Progress:** ████░░░░░░ 40% ## Performance Metrics @@ -45,7 +45,7 @@ Plan: 2 of 5 | Phases complete | 0 / 5 | | Requirements mapped | 54 / 54 | | Plans written | 5 (Phase 1) | -| Plans complete | 1 | +| Plans complete | 2 | ## Accumulated Context @@ -64,6 +64,8 @@ Plan: 2 of 5 | Two-DSN PostgreSQL strategy | DATABASE_URL (docuvault_app, DML only) + DATABASE_MIGRATE_URL (docuvault_migrate, DDL only); celery-worker gets only DATABASE_URL | | MinIO healthcheck via mc ready local | curl removed from MinIO Docker image since Oct 2023; mc is the correct in-container healthcheck tool | | pydantic-settings v2 SettingsConfigDict | SettingsConfigDict API used (not deprecated class Config form) for env var config | +| async_client fixture name | Distinct from legacy sync `client` fixture to avoid collision; both coexist until Plan 05 | +| xfail(strict=False) for Wave 0 | All pre-implementation scaffolds use strict=False so unexpected passes don't break CI | ### Open Questions @@ -82,6 +84,6 @@ _Updated at each phase transition._ | Field | Value | |---|---| -| Last session | 2026-05-22 — Executed 01-01-PLAN.md (Compose + Config Foundation) | -| Next action | Execute 01-02-PLAN.md (Wave 0 test scaffolds + async pytest fixtures) | +| Last session | 2026-05-22 — Executed 01-02-PLAN.md (Wave 0 test scaffolds + async fixtures) | +| Next action | Execute 01-03-PLAN.md (SQLAlchemy ORM models + Alembic async migration) | | Pending decisions | See Open Questions above | diff --git a/.planning/phases/01-infrastructure-foundation/01-02-SUMMARY.md b/.planning/phases/01-infrastructure-foundation/01-02-SUMMARY.md new file mode 100644 index 0000000..bba3b96 --- /dev/null +++ b/.planning/phases/01-infrastructure-foundation/01-02-SUMMARY.md @@ -0,0 +1,167 @@ +--- +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)