agentleFS
vstorm-covstorm-co/agenticos/.claude/skills/backend-tests/SKILL.md

backend-tests

Skill241 starsChanged 30 days agoApache-2.0View on GitHub

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.

  • Reads credentials

1 other project uses a version of this.

Adopted by

Most recently taken up by

The file

We split this skill into the rules it gives an agent

3 rules. About 0 are common, 2 are distinctive, and 1 is very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈2

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 1

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    When listing files for one pytest run, keep the tests/integration/ files together: an integration file, then a plain tests/ file, then …

    line 68
  2. Distinctive:

    The docstring says why it matters: when the name cannot.

    line 89
  3. Distinctive:

    No test for a mock: If removing the implementation still passes, delete it.

    line 92

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Backend tests — four layers and a 100% gate

Read docs/testing.md and the ## Testing section of AGENTS.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.

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

LayerPathUse when
Unittests/test_*.pyOne module, deps mocked at the repository boundary. Most tests
Integrationtests/integration/A CHECK, a cascade, a partial unique index, tenant isolation in the schema
APItests/api/A route is wired to the right permission and returns the right status
E2Efrontend/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

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 worker needs no server and no variable of its own. Its guarantees are tested against the real background_jobs table and real child processes, with the misbehaving kinds in tests/worker_handlers.py; a service that queues a job is unit-tested by patching enqueue where it is imported (the queued_fires fixture in tests/test_agent_triggers.py). docs/testing.md#the-workers-tests has which file covers what.

When listing files for one pytest run, keep the tests/integration/ files together: an integration file, then a plain tests/ file, then another integration file loses tests/integration/conftest.py for the last one and it errors with fixture 'db' not found. Whole directories, as CI runs them, are unaffected.

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 (make test starts a throwaway Postgres on :5433; for a scoped run, make docker-test-db and pass POSTGRES_HOST=localhost POSTGRES_PORT=5433 POSTGRES_USER=postgres POSTGRES_PASSWORD=postgres), and refuses any database whose name contains neither test nor ci, or that is not a plain identifier — it truncates the populated tables between tests 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.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.