Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 29 additions & 2 deletions .github/workflows/integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ on:
push:
branches: [main]

permissions:
contents: read

jobs:
detect-integration-changes:
runs-on: ubuntu-latest
Expand Down Expand Up @@ -38,20 +41,44 @@ jobs:
env:
# Compose can be slow on runners (Kafka + topic init + server build)
INTEGRATION_READY_TIMEOUT: 120s
# BuildKit + compose build: better layer reuse; GHA cache requires buildx (see below).
DOCKER_BUILDKIT: 1
COMPOSE_DOCKER_CLI_BUILD: 1

steps:
- name: Checkout repo
uses: actions/checkout@v4

- name: Cache Docker images
uses: ScribeMD/docker-cache@0.5.0
with:
key: docker-cache-${{ runner.os }}-${{ hashFiles('docker-compose.yml', 'docker-compose.dev.yml', 'testing/integration/docker-compose.integration.yml', 'testing/integration/docker-compose.ci-cache.yml') }}

# Enables type=gha BuildKit cache used in testing/integration/docker-compose.ci-cache.yml
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache: true

# make integration = docker compose up + go test -tags=integration (see testing/Makefile)
- name: Pull and run services
run: |
docker compose \
--project-directory "${{ github.workspace }}" \
-f docker-compose.dev.yml \
-f testing/integration/docker-compose.integration.yml \
-f testing/integration/docker-compose.ci-cache.yml \
up -d --build

# make integration = docker compose up + go test -tags=integration (see testing/Makefile).
# On GitHub Actions, Makefile adds docker-compose.ci-cache.yml for BuildKit GHA layer cache.
- name: Run integration tests
run: make integration
run: |
cd testing/integration
go test -tags=integration -count=1 -parallel 8 -v ./...

- name: Tear down Compose
if: always()
Expand Down
17 changes: 13 additions & 4 deletions testing/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,26 @@ _TESTING_ABS := $(abspath $(dir $(lastword $(MAKEFILE_LIST))))
REPO_ROOT := $(abspath $(_TESTING_ABS)/..)
COMPOSE_BASE := $(REPO_ROOT)/docker-compose.dev.yml
COMPOSE_INT := $(REPO_ROOT)/testing/integration/docker-compose.integration.yml
COMPOSE_CI_CACHE := $(REPO_ROOT)/testing/integration/docker-compose.ci-cache.yml
COMPOSE_LOAD := $(REPO_ROOT)/testing/load/docker-compose.load.yml
INTEGRATION_DIR := $(REPO_ROOT)/testing/integration
LOAD_DIR := $(REPO_ROOT)/testing/load

# GitHub Actions sets GITHUB_ACTIONS=true — use BuildKit GHA cache (type=gha) for image builds.
ifeq ($(GITHUB_ACTIONS),true)
COMPOSE_EXTRA := -f $(COMPOSE_CI_CACHE)
else
COMPOSE_EXTRA :=
endif

# Default to running all integration tests if no filter is provided
TEST ?= all

.PHONY: integration load teardown teardown-integration teardown-load _up_integration _up_load

# Tear down Compose stacks (absolute -f paths; works from any cwd).
teardown-integration:
docker compose --project-directory "$(REPO_ROOT)" -f $(COMPOSE_BASE) -f $(COMPOSE_INT) down
docker compose --project-directory "$(REPO_ROOT)" -f $(COMPOSE_BASE) -f $(COMPOSE_INT) $(COMPOSE_EXTRA) down

teardown-load:
docker compose --project-directory "$(REPO_ROOT)" -f $(COMPOSE_BASE) -f $(COMPOSE_LOAD) down
Expand All @@ -30,11 +38,11 @@ teardown: teardown-integration teardown-load
# make integration TEST=create (event creation flow only)
integration: _up_integration
ifeq ($(TEST),register)
cd $(INTEGRATION_DIR) && go test -tags=integration -v -run TestRegistration ./...
cd $(INTEGRATION_DIR) && go test -tags=integration -count=1 -parallel 8 -v -run TestRegistration ./...
else ifeq ($(TEST),create)
cd $(INTEGRATION_DIR) && go test -tags=integration -v -run TestEventCreation ./...
cd $(INTEGRATION_DIR) && go test -tags=integration -count=1 -parallel 8 -v -run TestEventCreation ./...
else
cd $(INTEGRATION_DIR) && go test -tags=integration -v ./...
cd $(INTEGRATION_DIR) && go test -tags=integration -count=1 -parallel 8 -v ./...
endif

# Flood the registration endpoint with concurrent requests via k6
Expand All @@ -49,6 +57,7 @@ _up_integration:
--project-directory "$(REPO_ROOT)" \
-f $(COMPOSE_BASE) \
-f $(COMPOSE_INT) \
$(COMPOSE_EXTRA) \
up -d --build

# Spin up the stack with mock Clark for load tests (leaves containers running)
Expand Down
3 changes: 3 additions & 0 deletions testing/integration/creation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
// TestEventCreationFlow_CreateAndRetrieve creates an event and verifies it can be
// fetched back with matching fields.
func TestEventCreationFlow_CreateAndRetrieve(t *testing.T) {
t.Parallel()
body := defaultEvent("Create And Retrieve Test", 50)
eventID := createEvent(t, "admin-create-retrieve", body)

Expand Down Expand Up @@ -42,6 +43,7 @@ func TestEventCreationFlow_CreateAndRetrieve(t *testing.T) {
// TestEventCreationFlow_Update creates an event, patches the name and location,
// then verifies the updated values are returned.
func TestEventCreationFlow_Update(t *testing.T) {
t.Parallel()
eventID := createEvent(t, "admin-update", defaultEvent("Update Test Original", 50))

patch, _ := json.Marshal(map[string]interface{}{
Expand Down Expand Up @@ -83,6 +85,7 @@ func TestEventCreationFlow_Update(t *testing.T) {
// TestEventCreationFlow_Delete creates an event, deletes it, and verifies a
// subsequent GET returns 404. Only draft or closed events may be deleted (see DeleteEventByID).
func TestEventCreationFlow_Delete(t *testing.T) {
t.Parallel()
body := defaultEvent("Delete Test", 50)
body["status"] = "draft"
eventID := createEvent(t, "admin-delete", body)
Expand Down
16 changes: 16 additions & 0 deletions testing/integration/docker-compose.ci-cache.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# CI-only: enable BuildKit GitHub Actions cache for custom images (see .github/workflows/integration.yml).
# Loaded when CI=true (GitHub Actions sets this automatically). Safe to omit locally.
services:
server:
build:
cache_from:
- type=gha,scope=server
cache_to:
- type=gha,mode=max,scope=server

mock-clark:
build:
cache_from:
- type=gha,scope=mock-clark
cache_to:
- type=gha,mode=max,scope=mock-clark
3 changes: 3 additions & 0 deletions testing/integration/registration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ func isRegistrationSubmitted(status int) bool {
// TestRegistrationFlow_BasicRegistration creates an event, registers a user,
// and verifies the registration is accepted by the Kafka consumer.
func TestRegistrationFlow_BasicRegistration(t *testing.T) {
t.Parallel()
eventID := createEvent(t, "admin-basic-reg", defaultEvent("Basic Registration Test", 100))

status, requestID := registerForEvent(eventID, "user-basic-reg", "Alice Smith", "alice@example.com")
Expand All @@ -37,6 +38,7 @@ func TestRegistrationFlow_BasicRegistration(t *testing.T) {
// TestRegistrationFlow_DuplicateRegistration verifies that registering the same
// user for the same event twice is rejected with 409.
func TestRegistrationFlow_DuplicateRegistration(t *testing.T) {
t.Parallel()
eventID := createEvent(t, "admin-dup-reg", defaultEvent("Duplicate Registration Test", 100))

// first registration
Expand All @@ -60,6 +62,7 @@ func TestRegistrationFlow_DuplicateRegistration(t *testing.T) {
// TestRegistrationFlow_CapacityFull creates an event with capacity 1, fills it,
// then verifies a second user's registration is rejected as capacity_full.
func TestRegistrationFlow_CapacityFull(t *testing.T) {
t.Parallel()
eventID := createEvent(t, "admin-cap-full", defaultEvent("Capacity Full Test", 1))

// register first user — should be accepted
Expand Down
Loading