SerpentStack test
Run and interpret tests for backend (pytest + testcontainers with real Postgres) and frontend (vitest). Use when: running test suites, debugging test failures, checking coverage, writing new tests, or understanding the test infrastructure.
install
source · Clone the upstream repo
git clone https://github.com/Benja-Pauls/SerpentStack
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/Benja-Pauls/SerpentStack "$T" && mkdir -p ~/.claude/skills && cp -r "$T/.skills/test" ~/.claude/skills/benja-pauls-serpentstack-test && rm -rf "$T"
manifest:
.skills/test/SKILL.mdsource content
Test
Run and interpret tests for SerpentStack backend and frontend.
Backend Tests (pytest)
Run All Tests
cd backend && uv run pytest
Run a Specific File
cd backend && uv run pytest tests/test_health.py
Run a Specific Test
cd backend && uv run pytest tests/test_health.py::test_health_check -v
Run with Coverage
cd backend && uv run pytest --cov=app --cov-report=term-missing
Look at the
Miss column to find lines without test coverage.
Test Infrastructure
Tests use a real PostgreSQL instance via testcontainers — a Docker container is spun up automatically per test session. This ensures tests exercise the same database features (UUID columns, JSONB,
ON CONFLICT) used in production.
Each test gets an
AsyncSession bound to an outer transaction with join_transaction_mode="create_savepoint". This means when route handlers call await db.commit(), SQLAlchemy commits a savepoint — not the real transaction. The fixture rolls back the outer transaction after each test, keeping tests fully isolated. The async httpx.AsyncClient uses dependency_overrides to inject this test session.
Requirement: Docker must be running for tests to pass.
Interpreting pytest Output
- PASSED: test is green, no action needed.
- FAILED: look for the
summary line, then scroll up to the full assertion error.FAILED - ERROR: a fixture or setup function raised an exception -- not a test logic failure but an infrastructure issue.
When a test fails:
- Read the
message. It shows expected vs actual values.AssertionError - Read the test function to understand what it asserts.
- Read the source code being tested to understand the actual behavior.
- Determine if the test is wrong (update the test) or the source is wrong (fix the source).
- Re-run the specific test to confirm the fix:
.uv run pytest <path>::<test_name> -v
Common pytest Failures
| Pattern | Meaning | Action |
|---|---|---|
| Endpoint validation rejected the request | Check request payload against Pydantic schema |
| Missing table or column | Run |
| Missing or misnamed test fixture | Check files |
fails | Response shape changed | Check if endpoint returns a wrapper object () |
or container timeout | Docker is not running | Start Docker Desktop, then re-run tests |
| Docker daemon unreachable | Check works, restart Docker if needed |
| not set or test not | Verify in — do NOT add (it's redundant with auto mode) |
/ in sync context | Accidentally called sync code in async path | Ensure all DB operations use with |
Frontend Tests (vitest)
Run All Tests
cd frontend && npm test
Run a Specific File
cd frontend && npm test -- tests/App.test.tsx
Run in Watch Mode
cd frontend && npm run test:watch
Run with Coverage
cd frontend && npm test -- --coverage
Interpreting vitest Output
- Look for lines marked with a red X for failures.
- The diff output shows
vsExpected
values.Received - For component tests, check if the rendered output matches expectations.
When a test fails:
- Read the test name to understand the expected behavior.
- Read the diff to see what diverged.
- Read the component or function source to understand actual behavior.
- Fix either the source (if behavior is wrong) or the test (if expectations are outdated).
- Re-run:
.npm test -- tests/path/to/file.test.tsx
Common vitest Failures
| Pattern | Meaning | Action |
|---|---|---|
| Component did not render the expected element | Check the component JSX and query selectors |
| Mock not set up correctly | Verify calls match the import path |
| State update outside of act wrapper | Wrap async operations in or |
| Snapshot mismatch | Component output changed | Review the diff; update snapshot with if intentional |
Full Suite
make test # Runs backend + frontend tests make lint # Runs ruff + ESLint
Test Strategy
- Write tests alongside new code. Every new endpoint gets at least one happy-path and one error test.
- Every new component gets a render test and an interaction test.
- Run the full suite before opening a PR:
.make test && make lint