Skip to main content

backend-tests

Write or extend the backend Python test suite — a unit test for a service, an integration test against a real Postgres, an API test through the FastAPI app, or the test a bug fix needs. Also for "coverage is failing", "make test is red", and deciding which of the four layers a test belongs in. Uses anyio (not pytest-asyncio) and a 100% coverage gate on the platform layer.

インストールへ移動

ソース情報

リポジトリ
vstorm-co/agenticos
ソースの最終更新活動
2026年9月13日 00:06
検出された SKILL.md の言語
英語
スター
37
フォーク
7

インストール方法

デフォルトでは、最初にソースを確認する Prompt が選択されています。直接コマンドに切り替えるか、ローカルコピーをダウンロードすることもできます。

ソースファイルを確認

インストールを決める前に、SKILL.md と SkillsMP に表示されている付属ファイルをお読みください。

ファイルエクスプローラー
3 ファイル

SKILL.md を表示中

SKILL.md
ソースの指示 · 読み取り専用プレビュー
name
backend-tests
description
Write or extend the backend Python test suite — a unit test for a service, an integration test against a real Postgres, an API test through the FastAPI app, or the test a bug fix needs. Also for "coverage is failing", "make test is red", and deciding which of the four layers a test belongs in. Uses anyio (not pytest-asyncio) and a 100% coverage gate on the platform layer.
# Backend tests — four layers and a 100% gate **Read `docs/testing.md`** and the `## Testing` section of `CLAUDE.md`; both are current. `.claude/rules/testing.md` has the naming and fixture shapes. This file is where a test goes and what actually breaks. ```bash make test-fast # no coverage — the write-run-write loop make test # backend + the 100% gate on the platform layer make test-integration # only the tests that need a real database make test-cov # HTML at backend/htmlcov/index.html make coverage-all # includes template-inherited code (informational) make test-migrations # the whole chain forwards and back against Postgres make check # every CI job except e2e — before opening a PR ``` ## Which layer | Layer | Path | Use when | |---|---|---| | **Unit** | `tests/test_*.py` | One module, deps mocked at the repository boundary. Most tests | | **Integration** | `tests/integration/` | A `CHECK`, a cascade, a partial unique index, tenant isolation in the schema | | **API** | `tests/api/` | A route is wired to the right permission and returns the right status | | **E2E** | `frontend/e2e/` | See the `e2e-tests` skill | A mock cannot tell you whether a constraint rejects a row. If the assertion is about the schema, it is an integration test or it is worthless. ## anyio, not pytest-asyncio ```python import pytest pytestmark = pytest.mark.anyio # module top ``` There is no `asyncio_mode` and no `@pytest.mark.asyncio`. The `anyio_backend` fixture in `tests/conftest.py` pins `asyncio` because that is what uvicorn uses. ## Fixtures worth knowing `tests/conftest.py`: - **`client`** — `httpx.AsyncClient` over `ASGITransport(app=app)`. Use this, never Starlette's `TestClient`. It overrides `get_db_session` and `get_redis`, and clears `app.dependency_overrides` afterwards. - **`mock_db_session`** — an `AsyncMock`. Mock repositories, **never** the service under test. - **`mock_redis`**, **`api_key_headers`**. The conftest also sets `POSTGRES_DB` to `<base>_p<pid>` *before* anything imports `app.core.config`. Do not move or weaken either half: running the unit suite against a checkout with a populated `.env` used to empty the development database, and a constant name meant two runs on one machine dropping each other's tables (#189). The same block seeds Prefect, which reads `backend/.env` on its own account: `PREFECT_API_URL` **empty** so that calling a `@flow` starts a temporary server rather than reaching for the one `make dev` names (#536), and `PREFECT_HOME` at a directory belonging to the tests so that server's SQLite database is not a developer's `~/.prefect`. `tests/test_prefect_test_environment.py` pins both, and `docs/testing.md#prefect-and-why-no-test-reaches-a-server` explains why an empty assignment is the shape that works. `tests/integration/conftest.py` **creates that database for the session and drops it afterwards**, so two concurrent runs need nothing passed to them — `make test` and `uv run pytest tests/integration` are safe while another run is going. It skips the whole module when no database is reachable, and **refuses any database whose name contains neither `test` nor `ci`, or that is not a plain identifier** — it calls `drop_all` unconditionally and drops the database itself at the end. ## A test earns its place by failing when the behaviour changes - **Name the behaviour, not the function.** `test_a_failed_run_still_records_its_cost`, not `test_finish`. - **Assert the consequence.** Not "the repository was called" but "the cost written was $2.00". - **The docstring says why it matters** when the name cannot. - **Cover the refusal.** Most of this platform's value is in what it refuses: a cross-tenant read, an ungranted scope, a second decision on a decided approval. - **No test for a mock.** If removing the implementation still passes, delete it. ## Invariants to test directly Tenant isolation (including when the caller owns the row) · permission scopes and grants · budget checked *before* the model request and recorded even when the run fails · spec validation refused at publish, never at run time · no plaintext secret in any response, log or audit entry · channel mentions running as the sender · what a parser claims it reads vs what the pipeline routes · narrowing a rule on a field already stored as JSONB. For parser/routing changes, read the `rag-knowledge` skill; for stored JSONB validation changes, read `agent-spec` and `alembic-migration`. ## Depth - `references/coverage-gate.md` — what is held to 100%, how the config selects it, and the two ways a module drops out silently. - `references/patterns.md` — worked service, API and integration tests.
GitHubで見る