From 0030c521d3c8129c81c892e8f32389890b3085c3 Mon Sep 17 00:00:00 2001 From: John Lancaster <32917998+jsl12@users.noreply.github.com> Date: Tue, 23 Jun 2026 20:08:02 -0500 Subject: [PATCH] test enhancements --- docs/step1.md | 116 ++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 85 insertions(+), 31 deletions(-) diff --git a/docs/step1.md b/docs/step1.md index d123283..1a9d3ce 100644 --- a/docs/step1.md +++ b/docs/step1.md @@ -328,7 +328,7 @@ REQ-3 lists six states: `upload`, `queued`, `processing`, `transcribed`, `failed MVP uses SQLite with auto-create-tables at startup. PostgreSQL migration is a post-MVP configuration change. """ - +import contextlib from collections.abc import Generator from sqlmodel import Session, SQLModel, create_engine @@ -356,7 +356,8 @@ def create_all() -> None: SQLModel.metadata.create_all(engine) -def get_session() -> Generator[Session, None, None]: +@contextlib.contextmanager +def get_session() -> Generator[Session]: """Yield a database session and ensure cleanup.""" with Session(engine) as session: yield session @@ -370,6 +371,54 @@ def get_session() -> Generator[Session, None, None]: ## 5. Test Plan +### Pytest Hierarchy Rules (Applied) + +Use the following structure consistently across Step 1 tests: + +- Module names: `test_*.py` +- Class names: `Test*` (group related scenarios) +- Method names: `test_` (keep them short and behavior-focused) +- Shared fixtures: nearest `conftest.py` at needed scope + +Hierarchy pattern used in this step: + +```text +tests/ + conftest.py + test_config.py + TestSettingsLoading + test_loads_from_env + test_requires_api_key + TestProviderSettings + test_defaults_to_openrouter + test_rejects_invalid_value + test_optional_fields_default_to_none + TestPathSettings + test_path_fields_are_path_objects + test_models.py + TestDocumentModel + test_can_be_persisted + test_defaults_are_populated + TestJobModel + test_can_be_created_for_document + test_defaults_are_populated + test_transitions_to_transcribed + test_transitions_to_failed + TestTranscriptModel + test_success_record_persists + test_failure_record_persists + test_job_id_is_unique + TestRelationships + test_document_exposes_jobs + test_job_exposes_transcript + test_db.py + TestSchemaBootstrap + test_create_all_creates_expected_tables + TestSessionFactory + test_get_session_yields_session + test_session_is_closed_after_generator_exit +``` + ### `tests/conftest.py` — Shared Fixtures ```python tests/conftest.py @@ -395,40 +444,45 @@ def session(): yield session ``` -### `tests/test_config.py` — Configuration Tests +### `tests/test_config.py` — Configuration Hierarchy -| Test | What It Verifies | -|------|------------------| -| `test_settings_loads_from_env` | `Settings` constructs successfully when `PROVIDER_API_KEY` is set via env var | -| `test_settings_requires_api_key` | `Settings()` raises `ValidationError` when `PROVIDER_API_KEY` is missing | -| `test_provider_defaults_to_openrouter` | Default provider is `openrouter` when not explicitly set | -| `test_provider_rejects_invalid_value` | Setting `PROVIDER=invalid` raises `ValidationError` | -| `test_optional_fields_default_to_none` | `provider_model` and `provider_base_url` are `None` when unset | -| `test_path_fields_are_path_objects` | `upload_dir` and `prompt_dir` are `Path` instances | +| Class | Method | What It Verifies | +|------|--------|------------------| +| `TestSettingsLoading` | `test_loads_from_env` | `Settings` constructs successfully when `PROVIDER_API_KEY` is set via env var | +| `TestSettingsLoading` | `test_requires_api_key` | `Settings()` raises `ValidationError` when `PROVIDER_API_KEY` is missing | +| `TestProviderSettings` | `test_defaults_to_openrouter` | Default provider is `openrouter` when not explicitly set | +| `TestProviderSettings` | `test_rejects_invalid_value` | Setting `PROVIDER=invalid` raises `ValidationError` | +| `TestProviderSettings` | `test_optional_fields_default_to_none` | `provider_model` and `provider_base_url` are `None` when unset | +| `TestPathSettings` | `test_path_fields_are_path_objects` | `upload_dir` and `prompt_dir` are `Path` instances | -### `tests/test_models.py` — Model & Relationship Tests +### `tests/test_models.py` — Model & Relationship Hierarchy -| Test | What It Verifies | -|------|------------------| -| `test_create_document` | A `Document` can be persisted and read back with correct fields | -| `test_document_defaults` | `id` is auto-generated UUID, `uploaded_at` is populated | -| `test_create_job_with_document` | A `Job` linked to a `Document` via FK persists correctly | -| `test_job_defaults` | Default status is `queued`, `created_at` and `updated_at` are populated | -| `test_job_status_transitions` | Status can be updated from `queued` → `processing` → `transcribed` | -| `test_job_status_to_failed` | Status can be updated from `processing` → `failed` | -| `test_create_transcript_success` | A `Transcript` with `text` set and `error_detail=None` persists correctly | -| `test_create_transcript_failure` | A `Transcript` with `text=None` and `error_detail` set persists correctly | -| `test_document_jobs_relationship` | `document.jobs` returns the linked `Job` list | -| `test_job_transcript_relationship` | `job.transcript` returns the linked `Transcript` | -| `test_transcript_job_id_unique` | Inserting two transcripts with the same `job_id` raises an integrity error | +| Class | Method | What It Verifies | +|------|--------|------------------| +| `TestDocumentModel` | `test_can_be_persisted` | A `Document` can be persisted and read back with correct fields | +| `TestDocumentModel` | `test_defaults_are_populated` | `id` is auto-generated UUID, `uploaded_at` is populated | +| `TestJobModel` | `test_can_be_created_for_document` | A `Job` linked to a `Document` via FK persists correctly | +| `TestJobModel` | `test_defaults_are_populated` | Default status is `queued`, `created_at` and `updated_at` are populated | +| `TestJobModel` | `test_transitions_to_transcribed` | Status can be updated from `queued` → `processing` → `transcribed` | +| `TestJobModel` | `test_transitions_to_failed` | Status can be updated from `processing` → `failed` | +| `TestTranscriptModel` | `test_success_record_persists` | A `Transcript` with `text` set and `error_detail=None` persists correctly | +| `TestTranscriptModel` | `test_failure_record_persists` | A `Transcript` with `text=None` and `error_detail` set persists correctly | +| `TestRelationships` | `test_document_exposes_jobs` | `document.jobs` returns the linked `Job` list | +| `TestRelationships` | `test_job_exposes_transcript` | `job.transcript` returns the linked `Transcript` | +| `TestTranscriptModel` | `test_job_id_is_unique` | Inserting two transcripts with the same `job_id` raises an integrity error | -### `tests/test_db.py` — Database Bootstrap Tests +### `tests/test_db.py` — Database Bootstrap Hierarchy -| Test | What It Verifies | -|------|------------------| -| `test_create_all_creates_tables` | After `create_all()`, the expected tables (`document`, `job`, `transcript`) exist in the database | -| `test_get_session_yields_session` | `get_session()` yields a usable `Session` object | -| `test_session_cleanup_on_exit` | After the generator is exhausted, the session is closed | +| Class | Method | What It Verifies | +|------|--------|------------------| +| `TestSchemaBootstrap` | `test_create_all_creates_expected_tables` | After `create_all()`, the expected tables (`document`, `job`, `transcript`) exist in the database | +| `TestSessionFactory` | `test_get_session_yields_session` | `get_session()` yields a usable `Session` object | +| `TestSessionFactory` | `test_session_is_closed_after_generator_exit` | After the generator is exhausted, the session is closed | + +### Marker Strategy (Step 1) + +- Keep all Step 1 tests as default unit-level tests (no custom marker needed yet). +- When slower integration or external tests are introduced, add explicit markers (for example `integration`, `external`) and keep names unchanged. ---