testing page updates
This commit is contained in:
+56
-138
@@ -1,177 +1,95 @@
|
|||||||
|
---
|
||||||
|
icon: lucide/flask-conical
|
||||||
|
---
|
||||||
|
|
||||||
# Testing
|
# Testing
|
||||||
|
|
||||||
This page defines an initial, high-level test structure for this repository that is easy to grow over time while keeping local feedback fast.
|
This page describes the current test layout and execution model for this repository.
|
||||||
|
|
||||||
Primary guidance sources:
|
Primary guidance sources:
|
||||||
- [Pytest scaffolding skill](./skills/pytest-scaffolding/SKILL.md)
|
- [Pytest scaffolding skill](./skills/pytest-scaffolding/SKILL.md)
|
||||||
- [FastAPI testing reference](./skills/pytest-scaffolding/references/fastapi-testing.md)
|
- [Pytest docs reference](./skills/pytest-scaffolding/references/pytest-docs.md)
|
||||||
- [FastAPI + uv + Docker skill](./skills/fastapi-uv-docker/SKILL.md)
|
- [FastAPI + uv + Docker skill](./skills/fastapi-uv-docker/SKILL.md)
|
||||||
|
|
||||||
## Goals
|
## Goals
|
||||||
|
|
||||||
1. Keep the default developer test loop fast and deterministic.
|
1. Keep local feedback fast with deterministic tests.
|
||||||
2. Separate cheap tests from expensive tests with clear markers.
|
2. Mirror source modules with focused test groups.
|
||||||
3. Mirror production code layout so new tests are easy to place.
|
3. Keep endpoint and MCP surface checks explicit.
|
||||||
4. Make FastAPI dependency and lifespan testing explicit.
|
4. Make marker usage strict and intentional.
|
||||||
5. Leave clear extension points for database and external integrations.
|
|
||||||
|
|
||||||
## Naming And Layout Conventions
|
## Current Test Layout
|
||||||
|
|
||||||
Use these naming rules consistently:
|
Current tree:
|
||||||
- test files: `test_<subject>.py`
|
|
||||||
- test functions: `test_<behavior>_<expected_result>()`
|
|
||||||
- test classes (optional): `Test<Subject>`
|
|
||||||
- fixtures: `<scope>_<resource>` (for example `session_engine`, `api_client`)
|
|
||||||
|
|
||||||
Target end-state test tree:
|
|
||||||
|
|
||||||
```text
|
```text
|
||||||
tests/
|
tests/
|
||||||
conftest.py
|
__init__.py
|
||||||
unit/
|
conftest.py
|
||||||
test_main.py
|
registry/
|
||||||
test_mcp.py
|
ingest/
|
||||||
catalog/
|
conftest.py
|
||||||
test_server.py
|
test_current_docs.py
|
||||||
skills/
|
test_document.py
|
||||||
test_document_loader.py
|
test_prompt.py
|
||||||
test_document_loader_references.py
|
test_skill.py
|
||||||
web/
|
models/
|
||||||
test_config.py
|
test_document_validation.py
|
||||||
test_docs_mount.py
|
test_prompt_validation.py
|
||||||
integration/
|
test_registry_payload_models.py
|
||||||
api/
|
test_skill_validation.py
|
||||||
test_health_endpoint.py
|
web/
|
||||||
test_app_routes.py
|
conftest.py
|
||||||
catalog/
|
test_endpoint_connections.py
|
||||||
test_prompt_loader_and_catalog.py
|
test_mcp_skills.py
|
||||||
test_step6_tool_fallback.py
|
|
||||||
startup/
|
|
||||||
test_lifespan.py
|
|
||||||
smoke/
|
|
||||||
test_service_boot_and_health.py
|
|
||||||
fixtures/
|
|
||||||
__init__.py
|
|
||||||
factories.py
|
|
||||||
payloads.py
|
|
||||||
resources/
|
|
||||||
prompts/
|
|
||||||
skills/
|
|
||||||
catalog/
|
|
||||||
configs/
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Mapping rule:
|
Source-to-test alignment today:
|
||||||
- mirror `src/personal_mcp/` into `tests/unit/` first.
|
- `src/personal_mcp/registry/ingest/` -> `tests/registry/ingest/`
|
||||||
- add integration modules only where contracts exist (FastAPI routes, DB behavior, external adapters).
|
- `src/personal_mcp/registry/models/` -> `tests/registry/models/`
|
||||||
|
- `src/personal_mcp/web/` and MCP HTTP surface -> `tests/web/`
|
||||||
|
|
||||||
Current to target mapping in this repo:
|
## Markers And Strictness
|
||||||
- `tests/test_document_loader_references.py` -> `tests/unit/skills/test_document_loader_references.py`
|
|
||||||
- `tests/test_prompt_loader_and_catalog.py` -> `tests/integration/catalog/test_prompt_loader_and_catalog.py`
|
|
||||||
- `tests/test_step6_tool_fallback.py` -> `tests/integration/catalog/test_step6_tool_fallback.py`
|
|
||||||
|
|
||||||
## Marker Strategy
|
Configured markers in `pyproject.toml`:
|
||||||
|
- `unit`: fast deterministic tests with no external dependencies
|
||||||
|
- `integration`: framework or component integration tests
|
||||||
|
- `smoke`: thin critical-path checks
|
||||||
|
|
||||||
Use a small marker vocabulary from day one:
|
Pytest runs with `--strict-markers`, so any unregistered marker fails the test run.
|
||||||
- `unit`: pure logic, no DB/network/filesystem side effects.
|
|
||||||
- `integration`: framework wiring and/or DB contract checks.
|
|
||||||
- `smoke`: thin checks for critical paths.
|
|
||||||
- `slow`: expensive tests that should not run in every local loop.
|
|
||||||
- `external`: real third-party calls; typically excluded in CI by default.
|
|
||||||
|
|
||||||
Fast-path expectation:
|
|
||||||
- local default loop should prioritize `unit` (and optionally `smoke`).
|
|
||||||
|
|
||||||
## Fixture Layering
|
## Fixture Layering
|
||||||
|
|
||||||
1. Keep lightweight, global fixtures in `tests/conftest.py`.
|
Fixture placement follows test scope:
|
||||||
2. Add subtree `conftest.py` files only when a test group needs dedicated setup.
|
1. `tests/conftest.py` for cross-suite defaults.
|
||||||
3. Prefer fixture composition over large monolithic fixtures.
|
2. `tests/registry/ingest/conftest.py` for ingest-specific setup.
|
||||||
4. Always clean up FastAPI dependency overrides in fixture teardown.
|
3. `tests/web/conftest.py` for web and endpoint client setup.
|
||||||
|
|
||||||
Suggested early fixtures:
|
Prefer adding fixtures at the narrowest scope that serves more than one test.
|
||||||
- `settings_override`
|
|
||||||
- `api_client`
|
|
||||||
- `tmp_workspace`
|
|
||||||
|
|
||||||
Optional integration fixtures (add when needed):
|
|
||||||
- `session_engine` / `db_session`
|
|
||||||
- `async_engine` / `async_session`
|
|
||||||
|
|
||||||
## FastAPI Testing Structure
|
|
||||||
|
|
||||||
Default route tests:
|
|
||||||
- use `fastapi.testclient.TestClient` for standard route behavior tests.
|
|
||||||
- keep tests as sync `def` unless async behavior must be asserted.
|
|
||||||
|
|
||||||
Async route tests:
|
|
||||||
- use `httpx.AsyncClient` with `ASGITransport` and `@pytest.mark.anyio`.
|
|
||||||
|
|
||||||
Dependency testing:
|
|
||||||
- prefer `app.dependency_overrides` for `Depends(...)` seams.
|
|
||||||
- reset `app.dependency_overrides` after each test/fixture.
|
|
||||||
|
|
||||||
Lifespan behavior:
|
|
||||||
- use `TestClient(app)` as a context manager for startup/shutdown checks.
|
|
||||||
|
|
||||||
## Test Granularity By Layer
|
|
||||||
|
|
||||||
`unit/`:
|
|
||||||
- parser and loader behavior
|
|
||||||
- catalog indexing and filtering logic
|
|
||||||
- utility functions and pure transformations
|
|
||||||
|
|
||||||
`integration/api/`:
|
|
||||||
- endpoint request/response contracts
|
|
||||||
- dependency override behavior
|
|
||||||
- error mapping and status code assertions
|
|
||||||
|
|
||||||
`integration/db/` (future-ready):
|
|
||||||
- transaction boundaries
|
|
||||||
- commit/rollback semantics
|
|
||||||
- async session lifecycle behavior
|
|
||||||
|
|
||||||
`smoke/`:
|
|
||||||
- one request/assertion path per critical workflow
|
|
||||||
|
|
||||||
## Command Baseline
|
## Command Baseline
|
||||||
|
|
||||||
Canonical invocation in this repository:
|
Canonical invocation:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
uv run pytest
|
uv run pytest
|
||||||
```
|
```
|
||||||
|
|
||||||
Recommended execution sequence:
|
Useful filtered runs:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
uv run pytest --collect-only -q
|
uv run pytest --collect-only -q
|
||||||
uv run pytest -m unit -q
|
uv run pytest -m unit -q
|
||||||
uv run pytest -m "unit or smoke" -q
|
uv run pytest -m integration -q
|
||||||
uv run pytest -q
|
uv run pytest -m smoke -q
|
||||||
```
|
```
|
||||||
|
|
||||||
## Extension Plan
|
## Adding New Tests
|
||||||
|
|
||||||
When adding a new feature:
|
When adding coverage:
|
||||||
1. Add or update corresponding `unit` tests first.
|
1. Place tests under the nearest existing module subtree (`registry/` or `web/`).
|
||||||
2. Add an `integration` test only for real boundary/contracts.
|
2. Mirror the source path where practical.
|
||||||
3. Add/adjust fixtures at the narrowest useful scope.
|
3. Reuse existing `conftest.py` files before adding new fixture layers.
|
||||||
4. Add a `smoke` test only for user-critical paths.
|
4. Add markers only when they convey execution intent, and register new markers in `pyproject.toml` first.
|
||||||
|
|
||||||
When the suite grows:
|
This keeps the suite aligned with the current architecture while preserving a fast local test loop.
|
||||||
1. Split slower groups behind `slow` and/or `external` markers.
|
|
||||||
2. Keep `unit` runtime bounded for rapid local feedback.
|
|
||||||
3. Promote shared setup into fixtures only after repeated duplication.
|
|
||||||
|
|
||||||
## Rollout Order
|
|
||||||
|
|
||||||
Use this order to reach the end-state structure with minimal disruption:
|
|
||||||
1. Create `tests/unit/` and `tests/integration/` subtrees.
|
|
||||||
2. Move the 3 existing top-level tests into the mapped target paths.
|
|
||||||
3. Add `tests/unit` modules mirroring uncovered source modules.
|
|
||||||
4. Add API and lifespan integration tests under `tests/integration/api/` and `tests/integration/startup/`.
|
|
||||||
5. Add one smoke test for boot + health path.
|
|
||||||
6. Add marker registration in `pyproject.toml` as marker usage expands.
|
|
||||||
|
|
||||||
This gives a concrete, stable final organization that remains easy to extend as new modules and boundaries are added.
|
|
||||||
Reference in New Issue
Block a user