Skip to content

Add test skill and pytest suite - #6

Merged
chaksaray merged 2 commits into
developfrom
test/add-pytest-suite
Jul 21, 2026
Merged

Add test skill and pytest suite#6
chaksaray merged 2 commits into
developfrom
test/add-pytest-suite

Conversation

@chaksaray

Copy link
Copy Markdown
Contributor

Summary

  • .claude/skills/test/SKILL.md: a red-green-refactor TDD skill for this repo, styled on Matt Pocock's testing philosophy (test observable behavior, not internals) and adapted to this service: test through the HTTP interface via TestClient, never touch the real network, reset shared module-level state (main._cache, main.limiter) between tests, one behavior per test
  • tests/conftest.py + tests/test_main.py: 14 tests following that skill
  • requirements-dev.txt: adds pytest on top of requirements.txt

Coverage

  • Happy path and 404 for all four routes
  • /mitigation returns only the neutral mitigation object, 404s when a record has none
  • /search matches across all four documented fields, case-insensitively, empty list for no match
  • Rate limiting present on a route (60/minute)
  • All three resilience behaviors from CLAUDE.md: stale-cache-served on refresh failure, hard 500 only when the cache was never populated, no crash-loop on a failed initial fetch

All tests monkeypatch main._refresh_cache, so the suite never touches the real network and runs in ~0.1s.

Test plan

  • python -m pytest -v: 14 passed
  • pre-commit run --all-files passes clean

Note

Not fixed here, out of scope for this ticket: pytest surfaces a DeprecationWarning on @app.on_event("startup") (FastAPI wants lifespan handlers instead). That's the pattern verbatim from the original setup docs, not introduced by this PR; worth its own ticket later.

Test through the HTTP interface, not internals; never touch the real
network; reset the module-level cache and rate limiter between tests;
one behavior per test. Matches this repo's existing research ->
grill-with-doc -> to-spec -> to-tickets -> implement shape, slotted in
during implement.
14 tests, following the new test skill: happy paths and 404s for every
route, mitigation neutrality (only the mitigation object, 404 when a
record has none), search matching across all four documented fields,
rate limiting present on a route, and the three resilience behaviors
from CLAUDE.md (stale-serve on refresh failure, hard-fail only when
the cache was never populated, no crash-loop on a failed initial
fetch). All tests monkeypatch _refresh_cache, no real network access.
@chaksaray
chaksaray merged commit 61813ca into develop Jul 21, 2026
@chaksaray
chaksaray deleted the test/add-pytest-suite branch July 21, 2026 15:11
chaksaray added a commit that referenced this pull request Jul 23, 2026
* Add initial service: fetch, cache, serve AVE records (#1)

Bakes in the resilience pattern (stale-cache-on-refresh-failure,
no crash-loop on startup), rate limiting, and security headers from
the start rather than as follow-up patches, per the reconciled
context brief.

* Add CONTEXT.md, LANGUAGE.md, ARCHITECTURE.md, CLAUDE.md (#2)

One coordinated documentation pass since these four reference each
other: framing and scope (CONTEXT.md), vocabulary (LANGUAGE.md),
structure and design decisions (ARCHITECTURE.md), and session rules
(CLAUDE.md).

* Complete repo: governance docs, changelog, skills, pre-commit, CI security tooling (#4)

* Add community and governance files, flesh out README

SECURITY.md, CONTRIBUTING.md, GOVERNANCE.md, CODE_OF_CONDUCT.md
(Contributor Covenant v2.1, unmodified) didn't exist yet, built fresh
rather than patched. README's deployment section names Render, matching
ARCHITECTURE.md's ADR, rather than the stale Cloud Run reference in an
earlier draft of this brief.

* Add CHANGELOG.md

Framed as a single [1.0.0] initial release rather than an Unreleased
section building on a prior version, since this synthesizes the
service's actual first shipped state rather than patching an existing
changelog.

* Add docker-compose.yml for local development

* Add six custom skills under .claude/skills/

research, grill-with-doc, to-spec, to-tickets, implement, code-review,
following a research -> interrogate -> spec -> tickets -> implement ->
review shape built for this repo's own context.

* Add .pre-commit-config.yaml

trailing-whitespace/end-of-file-fixer/check-yaml/check-json/large-files,
ruff + ruff-format, and two local hooks: no em dashes anywhere, and no
stray mentions of one specific vendor's product name. Ran --all-files
and fixed everything it flagged (ruff-format wrapped two lines in
main.py) before committing.

* Add CodeQL, Dependabot, and OpenSSF Scorecard CI

Three Dependabot ecosystems (pip, docker, github-actions), not just
Python, since the Dockerfile's base image and this repo's own Actions
dependencies need patching too. Scorecard publishes results publicly,
which is what README.md's badge (added in an earlier commit) links to.

* Add test skill and pytest suite (#6)

* Add test skill: red-green-refactor TDD for this repo

Test through the HTTP interface, not internals; never touch the real
network; reset the module-level cache and rate limiter between tests;
one behavior per test. Matches this repo's existing research ->
grill-with-doc -> to-spec -> to-tickets -> implement shape, slotted in
during implement.

* Add pytest suite covering all four routes and the resilience pattern

14 tests, following the new test skill: happy paths and 404s for every
route, mitigation neutrality (only the mitigation object, 404 when a
record has none), search matching across all four documented fields,
rate limiting present on a route, and the three resilience behaviors
from CLAUDE.md (stale-serve on refresh failure, hard-fail only when
the cache was never populated, no crash-loop on a failed initial
fetch). All tests monkeypatch _refresh_cache, no real network access.

* Trigger CodeQL and Scorecard on push to develop too (#15)

Both previously only ran on push to main. Since develop is where work
actually lands first, scanning stopped there meant a window between
merge to develop and the next develop-to-main sync where new code had
no CodeQL or Scorecard coverage at all.

* Fix Dockerfile: copy constants.py, not just main.py (#18)

The build never copied constants.py after main.py started importing
from it, so the container crashed on startup with ModuleNotFoundError
inside Docker even though python3 -m py_compile passed locally (that
check never runs inside the actual container). Verified by building
the image and running it: previously exited immediately with
ModuleNotFoundError: No module named 'constants'; now starts, binds
the port, and serves real data.

* Fix Dockerfile: trailing slash on multi-source COPY destination (#20)

The Dockerfile-missing-constants.py fix (#18) merged with a COPY line
that builds fine locally under BuildKit (which normalizes . to a
directory) but fails on Cloud Build's classic docker builder, which
enforces the Dockerfile spec strictly: a multi-source COPY's
destination must end with /. Reproduced the exact Cloud Build error
locally with DOCKER_BUILDKIT=0, confirmed this fix resolves it under
the same strict builder, and confirmed the resulting image starts and
serves real data (curl to / returns 200).

* Update docs to reflect Cloud Run as the live deployment (#21)

ARCHITECTURE.md's ADR, README.md's Deployment section, and
CHANGELOG.md now say Cloud Run rather than Render, since the first
production deployment landed there instead. Kept the original Render
reasoning in the ADR rather than erasing it, since ADRs record
decisions at the time they were made and the switch itself is part of
the record. Confirmed live: GET / on the deployed *.run.app URL
returns real data (record_count: 59) matching the local verification
from earlier.

* Revert Scorecard to main-only push trigger (#22)

ossf/scorecard-action only supports running against the repo's
default branch: a push to develop failed the action outright with
"validating options: only default branch is supported" /
"Only the default branch main is supported." CodeQL has no such
restriction and keeps running on both main and develop; this reverts
only the Scorecard side of the earlier change.
chaksaray added a commit that referenced this pull request Jul 23, 2026
* Add initial service: fetch, cache, serve AVE records (#1)

Bakes in the resilience pattern (stale-cache-on-refresh-failure,
no crash-loop on startup), rate limiting, and security headers from
the start rather than as follow-up patches, per the reconciled
context brief.

* Add CONTEXT.md, LANGUAGE.md, ARCHITECTURE.md, CLAUDE.md (#2)

One coordinated documentation pass since these four reference each
other: framing and scope (CONTEXT.md), vocabulary (LANGUAGE.md),
structure and design decisions (ARCHITECTURE.md), and session rules
(CLAUDE.md).

* Complete repo: governance docs, changelog, skills, pre-commit, CI security tooling (#4)

* Add community and governance files, flesh out README

SECURITY.md, CONTRIBUTING.md, GOVERNANCE.md, CODE_OF_CONDUCT.md
(Contributor Covenant v2.1, unmodified) didn't exist yet, built fresh
rather than patched. README's deployment section names Render, matching
ARCHITECTURE.md's ADR, rather than the stale Cloud Run reference in an
earlier draft of this brief.

* Add CHANGELOG.md

Framed as a single [1.0.0] initial release rather than an Unreleased
section building on a prior version, since this synthesizes the
service's actual first shipped state rather than patching an existing
changelog.

* Add docker-compose.yml for local development

* Add six custom skills under .claude/skills/

research, grill-with-doc, to-spec, to-tickets, implement, code-review,
following a research -> interrogate -> spec -> tickets -> implement ->
review shape built for this repo's own context.

* Add .pre-commit-config.yaml

trailing-whitespace/end-of-file-fixer/check-yaml/check-json/large-files,
ruff + ruff-format, and two local hooks: no em dashes anywhere, and no
stray mentions of one specific vendor's product name. Ran --all-files
and fixed everything it flagged (ruff-format wrapped two lines in
main.py) before committing.

* Add CodeQL, Dependabot, and OpenSSF Scorecard CI

Three Dependabot ecosystems (pip, docker, github-actions), not just
Python, since the Dockerfile's base image and this repo's own Actions
dependencies need patching too. Scorecard publishes results publicly,
which is what README.md's badge (added in an earlier commit) links to.

* Add test skill and pytest suite (#6)

* Add test skill: red-green-refactor TDD for this repo

Test through the HTTP interface, not internals; never touch the real
network; reset the module-level cache and rate limiter between tests;
one behavior per test. Matches this repo's existing research ->
grill-with-doc -> to-spec -> to-tickets -> implement shape, slotted in
during implement.

* Add pytest suite covering all four routes and the resilience pattern

14 tests, following the new test skill: happy paths and 404s for every
route, mitigation neutrality (only the mitigation object, 404 when a
record has none), search matching across all four documented fields,
rate limiting present on a route, and the three resilience behaviors
from CLAUDE.md (stale-serve on refresh failure, hard-fail only when
the cache was never populated, no crash-loop on a failed initial
fetch). All tests monkeypatch _refresh_cache, no real network access.

* Trigger CodeQL and Scorecard on push to develop too (#15)

Both previously only ran on push to main. Since develop is where work
actually lands first, scanning stopped there meant a window between
merge to develop and the next develop-to-main sync where new code had
no CodeQL or Scorecard coverage at all.

* Fix Dockerfile: copy constants.py, not just main.py (#18)

The build never copied constants.py after main.py started importing
from it, so the container crashed on startup with ModuleNotFoundError
inside Docker even though python3 -m py_compile passed locally (that
check never runs inside the actual container). Verified by building
the image and running it: previously exited immediately with
ModuleNotFoundError: No module named 'constants'; now starts, binds
the port, and serves real data.

* Fix Dockerfile: trailing slash on multi-source COPY destination (#20)

The Dockerfile-missing-constants.py fix (#18) merged with a COPY line
that builds fine locally under BuildKit (which normalizes . to a
directory) but fails on Cloud Build's classic docker builder, which
enforces the Dockerfile spec strictly: a multi-source COPY's
destination must end with /. Reproduced the exact Cloud Build error
locally with DOCKER_BUILDKIT=0, confirmed this fix resolves it under
the same strict builder, and confirmed the resulting image starts and
serves real data (curl to / returns 200).

* Update docs to reflect Cloud Run as the live deployment (#21)

ARCHITECTURE.md's ADR, README.md's Deployment section, and
CHANGELOG.md now say Cloud Run rather than Render, since the first
production deployment landed there instead. Kept the original Render
reasoning in the ADR rather than erasing it, since ADRs record
decisions at the time they were made and the switch itself is part of
the record. Confirmed live: GET / on the deployed *.run.app URL
returns real data (record_count: 59) matching the local verification
from earlier.

* Revert Scorecard to main-only push trigger (#22)

ossf/scorecard-action only supports running against the repo's
default branch: a push to develop failed the action outright with
"validating options: only default branch is supported" /
"Only the default branch main is supported." CodeQL has no such
restriction and keeps running on both main and develop; this reverts
only the Scorecard side of the earlier change.

* add ave api reference url (#23)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant