FEAT: Update Status Of Video Row On Media Convert Update
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
---
|
||||
description: The ATDD loop this repo develops features with — write the Gherkin scenario first, watch it fail, write just enough code in cms/ to pass it, re-run, move to the next scenario. Use when adding or changing an endpoint, a validation rule, an error response, or any user-visible behaviour in cms/.
|
||||
---
|
||||
|
||||
## The loop
|
||||
|
||||
One scenario at a time. Never write two scenarios' worth of code in one pass.
|
||||
|
||||
1. **Write the scenario** in `tests/features/*.feature`, in the language of the
|
||||
feature rather than of HTTP where you can. Name it for the behaviour
|
||||
(`Refusing to register a video whose file was never uploaded`), not the
|
||||
mechanism.
|
||||
2. **Run it and watch it fail.** `cd tests && go test -count=1 -v ./...`
|
||||
Confirm it fails for the reason you expect. A scenario that passes before
|
||||
you have written any code is testing nothing — go back to step 1.
|
||||
3. **Write just enough code in `cms/`** to turn that one scenario green.
|
||||
Not the next scenario's code. Not a generalisation you have no test for.
|
||||
4. **Re-run the suite** — the whole file, not just the new scenario. Green
|
||||
means done; a scenario that used to pass and now does not means the new
|
||||
code changed published behaviour, which is a decision, not an accident.
|
||||
5. **Next scenario.** Repeat until the feature is described.
|
||||
|
||||
Only then tidy: extract helpers, rename, split files. The suite is what makes
|
||||
that safe, so do it with the tests green and re-run after.
|
||||
|
||||
## Where things go
|
||||
|
||||
Tests are **black box**. `tests/` is its own Go module (`thamanyah/tests`) that
|
||||
imports nothing from `cms/` and speaks only HTTP. Do not add `_test.go` files
|
||||
inside `cms/` — if a behaviour cannot be reached through the API, that is a
|
||||
finding about the design, not a reason to reach past it.
|
||||
|
||||
| file | what goes in it |
|
||||
|---|---|
|
||||
| `tests/features/*.feature` | the scenarios |
|
||||
| `tests/suite_test.go` | step registration, the per-scenario `world` |
|
||||
| `tests/steps_test.go` | what each step does |
|
||||
| `tests/client_test.go` | HTTP plumbing and wire types |
|
||||
|
||||
Production code keeps to the seams in `CLAUDE.md`: wire structs in
|
||||
`internal/api`, domain types in `internal/models`, HTTP in `internal/handlers`,
|
||||
SQL in `internal/db/repositories`, AWS in `internal/services`. "Just enough
|
||||
code" still lands on the right seam — a handler that runs its own SQL is not
|
||||
minimal, it is misplaced.
|
||||
|
||||
## Running it
|
||||
|
||||
```bash
|
||||
docker compose up -d # localstack + infra + cms (needs LOCALSTACK_AUTH_TOKEN)
|
||||
docker exec cms ./cms migrate # see Migrations below
|
||||
cd tests && go test -count=1 -v ./...
|
||||
```
|
||||
|
||||
`-count=1` defeats Go's test caching, which otherwise hides a re-run against a
|
||||
changed service. `-v` prints the Gherkin: gobdd nests a subtest per feature,
|
||||
scenario and step, so the step text appears in the output.
|
||||
|
||||
`CMS_BASE_URL` (default `http://localhost:8081`) points the suite elsewhere. If
|
||||
nothing is serving, the suite skips rather than fails.
|
||||
|
||||
**Migrations.** Compose starts `cms` in server mode only — the migrate
|
||||
container exists in the ECS task definition, not in `docker-compose.yml`. A new
|
||||
migration is **not** applied by `docker compose watch` rebuilding the image.
|
||||
Run `docker exec cms ./cms migrate` after adding one, or every scenario fails
|
||||
on `relation "…" does not exist`.
|
||||
|
||||
**The spec.** Changing a handler, its annotations, or an `internal/api` struct
|
||||
means regenerating the OpenAPI spec, or the build drifts from the published
|
||||
contract:
|
||||
|
||||
```bash
|
||||
$(go env GOPATH)/bin/swag init --generalInfo main.go --dir ./ --parseInternal --output ./docs
|
||||
```
|
||||
|
||||
## Reading a red run
|
||||
|
||||
gobdd does **not** stop a scenario at the first failing step. A step that fails
|
||||
leaves the later steps running against stale state, so one real failure
|
||||
produces a cascade of noisy ones. **Read the first failure in a scenario and
|
||||
ignore the rest** until it is fixed.
|
||||
|
||||
`cannot find step definition for step: …` is a legitimate red: the scenario is
|
||||
written and the step is not. Add the function to `steps_test.go`, register it
|
||||
in `TestVideoUpload`, and run again.
|
||||
|
||||
## Writing steps
|
||||
|
||||
- **Anchor every pattern** with `^…$`. gobdd matches unanchored and the
|
||||
first registered pattern that matches wins, so an unanchored pattern can
|
||||
silently shadow another step.
|
||||
- State moves between steps through the `*world` in the gobdd context, never
|
||||
package variables. It is held by **pointer** because gobdd clones the context
|
||||
between the `Background` and the scenario steps.
|
||||
- A `Scenario Outline` runs every example row in **one** world. Steps must
|
||||
overwrite state, never accumulate it.
|
||||
- Reuse a step before writing a new one. Two steps that differ only in wording
|
||||
are two ways for the feature file to drift.
|
||||
|
||||
## Asserting errors
|
||||
|
||||
Errors are RFC 9457 Problem Details on `application/problem+json`. `title` is
|
||||
held identical across every occurrence of a given problem so clients can branch
|
||||
on it — so **assert the exact title**, not just the status. A new error
|
||||
condition means a new stable title, and a new `@Failure` annotation on the
|
||||
handler.
|
||||
|
||||
When a status alone would be ambiguous, distinguish the cases in code rather
|
||||
than in the assertion: a client's mistake and a server fault reaching the same
|
||||
`writeProblem` call is the bug, and a sentinel error in
|
||||
`internal/handlers/errors.go` plus `errors.Is` at the call site is how this repo
|
||||
separates them.
|
||||
|
||||
## `@known-gap`
|
||||
|
||||
Tag a scenario `@known-gap` when it pins what the service does **today** and
|
||||
that differs from what it is documented to do. It still passes — it is a record,
|
||||
not a failure — and it carries a comment saying what the contract promises and
|
||||
what changing it would take. Fixing the code means changing the scenario in the
|
||||
same commit and dropping the tag.
|
||||
|
||||
Do not use the tag to park a scenario you could not make pass.
|
||||
Reference in New Issue
Block a user