Skip to content

Backend: Tests

Test Strategy

The tests are organized in layers and verify both business logic and API behavior. The goal is to detect errors early in small units and to safeguard API integrity across end-to-end paths. In addition to unit tests, there are controller and integration tests, as well as a special comparison between the OpenAPI specification and the router definition.

Additionally, tests are added when fixing bugs to prevent regressions and ensure these issues do not reoccur in the future.

Test Types and Coverage

Unit Tests

Unit tests focus on pure logic without external dependencies. These include, for example, recurrence calculations for tasks/events, limits for room–user assignments, validator logic, and helper functions in services (QR, renderer, logger, JWT). These tests run quickly and are ideal for the local development cycle.

Controller Tests

Controller tests verify the API layer with mocked or real databases, including routing, request binding, validation, and status codes. They cover endpoints such as user, room, display, schedule, check-in, and QR, and validate typical error cases (e.g., missing permissions, invalid UUIDs, conflict cases).

Router/OpenAPI Tests

A dedicated test compares the routes defined in the OpenAPI spec with the actually registered Gin routes. This ensures that the specification is always up to date and that no endpoints are “forgotten.”

  • Test: app/backend/internal/api/v1/router/router_openapi_test.go

Integration Tests

Integration tests run with real dependencies (Postgres, Redis) and verify realistic end-to-end paths. These tests use the build tag integration and require a running Redis instance as well as a test Postgres database.

Test Databases and Isolation

SQLite is used as an in-memory DB and is suitable for fast unit and controller tests. Postgres tests create an isolated schema per run so that tests can run in parallel without interfering with each other. The schema is created per test run and dropped again after completion.

  • SQLite: in-memory (fast, no Docker required)
  • Postgres: separate schemas per test run (internal/testutil)

Important Environment Variables for Tests

Selecting the Test Database

The TEST_DB variable can be used to restrict the DB selection.

  • TEST_DB=sqlite only SQLite tests
  • TEST_DB=postgres only Postgres tests
  • without TEST_DB, both DBs are tested

Postgres Test DB

The default values are defined in internal/testutil/db.go, but can be overridden via environment variables.

  • POSTGRES_TEST_HOST (default: localhost)
  • POSTGRES_TEST_PORT (default: 5432)
  • POSTGRES_TEST_USER (default: test_user)
  • POSTGRES_TEST_PASSWORD (default: test_password)
  • POSTGRES_TEST_DATABASE (default: test_db)
  • POSTGRES_TEST_SSLMODE (default: disable)

Redis for Integration Tests

Integration tests require a Redis instance. The address is set via redis_addr.

  • redis_addr=localhost:6379
  • optional: redis_password, redis_db

Execution

Standard Run (Unit + Controller)

1
2
3
cd app/backend

go test ./... -count=1

Testing Only One Database

1
2
3
4
5
# SQLite only
go test ./... -count=1 -run Test -v

# Postgres only
TEST_DB=postgres go test ./... -count=1

Integration Tests

cd app/backend

# Start Postgres test DB
docker compose -f docker-compose.test.yml up -d

# Start Redis
docker run --name redis_rooms -p 6379:6379 -d redis

# Run integration tests
redis_addr=localhost:6379 go test ./... -tags=integration -count=1

Specific Package or Test

1
2
3
4
5
# specific package
go test ./internal/api/v1/controller/room -v -count=1

# single test
go test ./internal/api/v1/controller/room -run TestGetRooms -v -count=1

Debugging Notes

  • -count=1 disables the Go test cache and helps with reproducible runs.
  • -v provides detailed logs and helps with troubleshooting.
  • For OpenAPI errors, first run swag init -g cmd/rooms/main.go -o api.