Files
thamanyah/tests/README.md
T
FahdShalhoub 6f9e04ee97
Build, Push and Deploy CMS / build-push-deploy (push) Successful in 2m35s
Build, Push and Deploy Discovery / build-push-deploy (push) Successful in 1m55s
Deploy Infrastructure / pulumi-up (push) Successful in 2s
FEAT: Discovery Video Service Subscription
2026-08-29 16:50:58 +03:00

108 lines
4.8 KiB
Markdown

# tests
BDD scenarios for the video upload feature, written in Gherkin and run by
[gobdd](https://github.com/go-bdd/gobdd).
The suite is **black box**: it speaks nothing but HTTP to a running `cms`, so
the same scenarios run against the local docker-compose stack and against a
deployed environment. It is its own Go module (`thamanyah/tests`) and imports
nothing from `cms/` — the API is the contract under test, not the code.
The upload to storage is a plain `PUT` to the presigned URL the API hands out,
with no AWS SDK involved, because that is exactly what a real client does.
```
features/*.feature the scenarios, in Gherkin
suite_test.go the gobdd suite: step registration + per-scenario world
steps_test.go what each step does
client_test.go HTTP plumbing and the wire types
```
## Running
The suite needs a live `cms` with its database migrated and its buckets
provisioned. From the repo root:
```bash
docker compose up -d # localstack + infra + cms
docker exec cms ./cms migrate # see "Migrations" below
cd tests && go test ./...
```
If nothing is serving, the suite **skips** rather than fails, so `go test ./...`
in a fresh checkout stays green.
Point it at another environment with `CMS_BASE_URL` (default
`http://localhost:8081`):
```bash
CMS_BASE_URL=https://cms.example.com go test -v ./...
```
Use `-v` to see the Gherkin: gobdd nests a Go subtest per feature, scenario and
step, so the step text shows up in the test output.
### Migrations
`docker-compose.yml` starts `cms` in server mode only. The migrate container
that runs `./cms migrate` to completion before the service starts exists in the
ECS task definition (`runMigrations` in `infrastructure/main.go`), not in
compose — so a freshly created local database has no schema and every scenario
fails with `relation "categories" does not exist`. Run `docker exec cms ./cms
migrate` once after the stack first comes up.
## What the scenarios cover
Both steps of the upload flow, end to end:
- listing the categories a video can be filed under, and the shape of that list
- issuing an upload slot for MP4 and QuickTime, including that the storage key
is generated rather than taken from the client's file name
- refusing a slot for every other media type, and for a body that is not JSON
- uploading the file to the presigned URL and registering it: the record comes
back `processing`, under the key from the slot, with a transcoding job id and
the categories and metadata that were sent
- refusing to register: a file that was never uploaded, a missing title, no
category, and a storage key this API never issued
- answering 404 for a category id no category has, kept distinct from the 500
the category lookup itself failing produces
- answering 409 for a key that was already registered, rather than queueing a
second transcoding job for the same file
- announcing a video on the catalogue topic once its job comes back COMPLETE,
carrying its id, title, playback URL and the **names** of its categories —
and announcing nothing for a job that ended in an error
- the read side building its catalogue from those announcements: the video
turns up in discovery under the id cms issued, a video still transcoding is
not there at all, and a video announced a second time is updated rather than
colliding with its own row
The publication scenarios read `tests-catalogue-events`, the suite's **own**
queue on the catalogue topic — not discovery's. A consumer destroys what it
reads, so sharing discovery's queue would have the suite and the running
service race for every announcement and each see about half. Delivery is raw,
so a message body is the announcement with no SNS envelope.
The ingestion scenarios need `discovery` up as well as `cms`; they read it over
HTTP at `DISCOVERY_BASE_URL` (default `http://localhost:8080`).
Like the job-state publisher they stand in for AWS, so they are the second
place the suite reaches for the AWS SDK; everything a real client does still
goes over plain HTTP.
## Adding a scenario
Write it in `features/`. If it needs a step that does not exist yet, add the
function to `steps_test.go` and register it in `TestVideoUpload`. Two things to
know about gobdd:
- Step patterns are matched **unanchored**, and the first registered pattern
that matches wins. Anchor every pattern with `^…$`, as the existing ones do,
or a new step can silently shadow an old one.
- State moves between steps through the `*world` in the gobdd context, not
package variables — gobdd clones the context between the `Background` and the
scenario, so the world is held by pointer.
Scenarios write real rows and real objects, and nothing cleans up after them.
That is fine against LocalStack, which is disposable; think twice before
pointing the suite at anything that is not.