Files
FahdShalhoub db50e015bf
Build, Push and Deploy Discovery / build-push-deploy (push) Successful in 5m2s
FEAT: Swicthed QUERY Method To GET
2026-08-30 10:56:51 +03:00
..
2026-08-29 17:56:11 +03:00
2026-08-30 10:56:51 +03:00
2026-08-29 17:56:11 +03:00

tests

BDD scenarios for the video upload feature, written in Gherkin and run by 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:

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):

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

  • searching that catalogue over GET /api/videos: finding a video by a word in its title and by a part-typed one, narrowing to a category and being left out of another, several categories meaning "any of", paging by cursor so the pages tile the results exactly once, refusing a cursor no search issued, and capping a page size that asks for the whole catalogue

Search scenarios give every video they catalogue a nonce in its title, and assert relative to the video they created ("returns that video") rather than on absolute counts. Nothing cleans up between runs, so a fixed title accumulates a copy per run and a count would stop meaning anything. The paging scenario shares one nonce across its videos so that searching for it matches that run's cohort and nothing else.

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.