114 lines
5.1 KiB
Markdown
114 lines
5.1 KiB
Markdown
# Plan: Full Test Coverage Across All Namespaces
|
|
|
|
## Context
|
|
|
|
5,555 tests at 92% line coverage, but several namespaces have zero or minimal test files. Goal: every namespace with real code gets dedicated tests.
|
|
|
|
Current state:
|
|
| Namespace | Source files | Test files | Gap |
|
|
|-----------|-------------|------------|-----|
|
|
| **ccw** | 314 | 0 | 41 table models + 308 doc modules, all generated |
|
|
| **cms** | 161 | 1 | ~120 generated table models (helpers already tested) |
|
|
| **bcda** | 12 | 1 | client.py, store.py, log.py at 0% coverage |
|
|
| **aco** | 73 | 16 | table/ (1063 classes), dag.py, rex/ untested |
|
|
| **bib** | 10 | 4 | client.py, spider.py, translate.py untested |
|
|
| **pfs** | 12 | 2 | pipe.py untested |
|
|
| **rex** | 7 | 3 | format.py, pipe.py untested |
|
|
| **bls** | 2 | 0 | All empty files — skip |
|
|
|
|
## Files to Create (14 files)
|
|
|
|
### Phase 1: BCDA (highest value — complex untested logic)
|
|
|
|
**`tests/bcda/test_log.py`** — JsonlHandler emit, extras, exc_info, setup() idempotency (~6 tests)
|
|
|
|
**`tests/bcda/conftest.py`** — MockTransport fixtures for auth + counting responses
|
|
|
|
**`tests/bcda/test_client.py`** — Full client coverage via MockTransport (~20 tests)
|
|
- Auth: token extraction, expiry timing, refresh on 401
|
|
- Retry: 5xx backoff, 429 rate-limit, connection error, max retries
|
|
- Export flow: start_export (202+Content-Location), poll loop, download (plain + gzip)
|
|
- Edge cases: JobExpiredError on 410, metadata (no auth)
|
|
- Patch `time.sleep` to zero via `retry_interval=0.0` and `poll_interval=0.0`
|
|
|
|
**`tests/bcda/test_store.py`** — Store with `fsspec.filesystem("memory")` (~12 tests)
|
|
- Parquet I/O: read missing → empty, append + read back
|
|
- `_current_state` dedup: keeps last row per key
|
|
- Job state: append, get, list, filter by status, KeyError
|
|
- File state: append, get, dedup by URL
|
|
|
|
### Phase 2: Parametrized table tests (bulk coverage, fast)
|
|
|
|
**`tests/ccw/__init__.py`** — empty package marker
|
|
|
|
**`tests/ccw/test_tables.py`** — Discover all `SQLTable` subclasses in `ccw.table` (~200 parametrized tests)
|
|
- `__schema__ == "ccw"`, `__tablename__` set, fields non-empty, `model()` with no args succeeds
|
|
- Also test `ccw.docs` modules: `name`, `label`, `type`, `length`, `source` attributes present
|
|
|
|
**`tests/cms/test_tables.py`** — Same pattern for `cms.table.__all__` (~480 parametrized)
|
|
- Iterate `__all__`, validate `__schema__`/`__tablename__` set, fields present, instantiable
|
|
|
|
**`tests/aco/test_tables.py`** — Same pattern for `aco.table` submodules (~300 parametrized)
|
|
- Validate `qualified_name()` returns `schema.tablename`, `column_names()` non-empty
|
|
|
|
### Phase 3: Remaining modules
|
|
|
|
**`tests/aco/test_dag.py`** — Graph builder + renderers (~12 tests)
|
|
- `build_graph`: nodes from exprs, edges from inputs, external node detection
|
|
- `Graph.schemas()`, `Graph.filter()`
|
|
- `to_dot`: valid DOT with subgraph clusters
|
|
- `to_mermaid`: starts with "graph LR", has nodes + edges
|
|
- `to_html`: contains `<script>`, has Cytoscape elements JSON
|
|
|
|
**`tests/bib/test_client.py`** — Trivial (~3 tests)
|
|
- `connect(":memory:")` returns Store, COLLECTIONS has expected keys
|
|
|
|
**`tests/bib/test_spider.py`** — Pure regex functions (~12 tests)
|
|
- `classify_url`: each URL pattern returns correct name, unknown → None
|
|
- Reference extraction regex patterns (CFR, FR, section)
|
|
|
|
**`tests/bib/test_translate.py`** — Translator parsing (~15 tests)
|
|
- Mock `_fetch` to return canned HTML/JSON
|
|
- Each translator: URL parsing, field extraction, tag assignment
|
|
|
|
**`tests/pfs/test_pipe.py`** — Column maps + structural (~5 tests)
|
|
- `_RVU_COLUMNS`/`_GPCI_COLUMNS` have expected keys, no duplicate targets
|
|
|
|
**`tests/rex/test_format.py`** — Stub coverage (~5 tests)
|
|
- Readers raise NotImplementedError, Format model instantiates
|
|
|
|
## What We Skip
|
|
|
|
- **bls** — all files are empty (0 lines of code)
|
|
- **aco/lake/sync.py** — requires DuckDB + Databricks context, integration-level
|
|
- **aco/lake/unity.py** — wraps Databricks SDK, can't mock meaningfully
|
|
- **bib/sync.py, bib/ingest.py** — require Zotero 61-table EAV schema, integration-level
|
|
- **rex/express.py** — all NotImplementedError stubs, already tracked by test_ast_coverage.py
|
|
|
|
## Key Patterns to Reuse
|
|
|
|
- `httpx.MockTransport` + `_counting_transport()` from `tests/api/test_base.py`
|
|
- `SQLTable` base class at `src/aco/table/base.py` (has `qualified_name()`, `column_names()`)
|
|
- `Pipeline` + `Expr` from `src/aco/pipe/base.py` for dag tests
|
|
- `fsspec.filesystem("memory")` for bcda/store tests
|
|
|
|
## Implementation Order
|
|
|
|
1. `tests/bcda/test_log.py` (simplest, builds confidence)
|
|
2. `tests/bcda/conftest.py` + `tests/bcda/test_client.py` (high-value)
|
|
3. `tests/bcda/test_store.py` (high-value)
|
|
4. `tests/ccw/` + `tests/cms/test_tables.py` + `tests/aco/test_tables.py` (bulk parametrized)
|
|
5. `tests/aco/test_dag.py` (pure functions)
|
|
6. `tests/bib/test_client.py` + `test_spider.py` + `test_translate.py`
|
|
7. `tests/pfs/test_pipe.py` + `tests/rex/test_format.py`
|
|
|
|
## Verification
|
|
|
|
```bash
|
|
uv run ruff check tests/
|
|
uv run pytest tests/ -v --tb=short
|
|
uv run pytest --cov=aco --cov=api --cov=bcda --cov=bib --cov=ccw --cov=cms --cov=pfs --cov=rex --cov-report=term-missing tests/
|
|
```
|
|
|
|
Estimated: ~1,100 new tests, bringing total to ~6,650+.
|