diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3dc225f..b978ee6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -38,31 +38,6 @@ jobs: name: Integration Tests runs-on: ubuntu-latest - services: - redis: - image: redis:7-alpine - ports: - - 6379:6379 - options: >- - --health-cmd "redis-cli ping" - --health-interval 10s - --health-timeout 5s - --health-retries 5 - - postgres: - image: postgres:16-alpine - env: - POSTGRES_DB: quotient_test - POSTGRES_USER: postgres - POSTGRES_PASSWORD: postgres - ports: - - 5432:5432 - options: >- - --health-cmd pg_isready - --health-interval 10s - --health-timeout 5s - --health-retries 5 - steps: - name: Checkout code uses: actions/checkout@v7 @@ -78,21 +53,20 @@ jobs: - name: Install dependencies run: go mod download + - name: Set up Docker Compose + uses: docker/setup-compose-action@v2 + - name: Run integration tests - # -p 1 runs one package binary at a time. These packages share a single - # Postgres database and a single Redis instance, and they use fixed - # names for both (the `tasks` and `results` lists, one schema). Running - # them concurrently lets one package consume the other's queued tasks - # and reset its rows. - run: go test -v -race -p 1 ./tests/integration/... ./engine/... -timeout 10m + run: make test-integration env: REDIS_HOST: localhost - REDIS_PORT: 6379 + REDIS_PORT: 6380 + REDIS_PASSWORD: redis_password POSTGRES_HOST: localhost POSTGRES_PORT: 5432 - POSTGRES_DB: quotient_test - POSTGRES_USER: postgres - POSTGRES_PASSWORD: postgres + POSTGRES_DB: engine + POSTGRES_USER: engineuser + POSTGRES_PASSWORD: postgres_password security-scanner: name: Security Scanner diff --git a/Makefile b/Makefile index bfb2c86..eb933a6 100644 --- a/Makefile +++ b/Makefile @@ -1,37 +1,20 @@ -.PHONY: test test-unit test-integration test-coverage clean test-deps-start test-deps-stop test-deps-wait +.PHONY: test test-unit test-integration test-coverage clean test-deps-start test-deps-stop test: - go test -race -p 1 ./... + go test -v -race -p 1 ./... test-unit: - go test -race -short ./engine/... ./www/... ./runner/... + go test -v -race -short ./engine/... ./www/... ./runner/... -test-integration: test-deps-start test-deps-wait - POSTGRES_PASSWORD=postgres go test -race ./tests/integration/... -timeout 10m +test-integration: test-deps-start + go test -v -race -p 1 ./tests/integration/... ./engine/... -timeout 10m @$(MAKE) test-deps-stop test-deps-start: - @docker ps -q -f name=quotient-test-postgres | grep -q . || \ - docker run -d --name quotient-test-postgres \ - -e POSTGRES_PASSWORD=postgres \ - -e POSTGRES_DB=quotient_test \ - -p 5432:5432 \ - postgres:16-alpine - @docker ps -q -f name=quotient-test-redis | grep -q . || \ - docker run -d --name quotient-test-redis \ - -p 6379:6379 \ - redis:7-alpine - -test-deps-wait: - @echo "Waiting for Postgres..." - @until docker exec quotient-test-postgres pg_isready -U postgres > /dev/null 2>&1; do sleep 1; done - @echo "Waiting for Redis..." - @until docker exec quotient-test-redis redis-cli ping > /dev/null 2>&1; do sleep 1; done - @echo "Services ready." + docker compose up test-redis test-db --wait -d test-deps-stop: - @docker stop quotient-test-postgres quotient-test-redis 2>/dev/null || true - @docker rm quotient-test-postgres quotient-test-redis 2>/dev/null || true + docker compose down test-redis test-db test-coverage: go test -race -p 1 -coverprofile=coverage.out ./... diff --git a/docker-compose.base.yml b/docker-compose.base.yml new file mode 100644 index 0000000..1a84a9f --- /dev/null +++ b/docker-compose.base.yml @@ -0,0 +1,27 @@ +services: + db-base: + image: postgres:17 + env_file: + - .env + healthcheck: + test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"] + interval: 5s + timeout: 5s + retries: 5 + start_period: 3s + networks: + - quotient_network + + redis-base: + image: redis:alpine + env_file: + - .env + command: ["redis-server", "--requirepass", "${REDIS_PASSWORD:?REDIS_PASSWORD variable is not set}"] + healthcheck: + test: ["CMD-SHELL", "redis-cli -a $$REDIS_PASSWORD ping | grep -q PONG"] + interval: 5s + timeout: 5s + retries: 5 + start_period: 3s + networks: + - quotient_network \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index dff04f5..a9975f9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,20 +1,21 @@ services: + test-db: + extends: + file: docker-compose.base.yml + service: db-base + profiles: + - test + ports: + - "127.0.0.1:5432:5432" + db: + extends: + file: docker-compose.base.yml + service: db-base container_name: quotient_database - image: postgres:17 restart: always - env_file: - - .env - networks: - - quotient_network volumes: - db_data:/var/lib/postgresql/data - healthcheck: - test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"] - interval: 5s - timeout: 5s - retries: 5 - start_period: 3s server: container_name: quotient_server @@ -38,23 +39,24 @@ services: - ./engine:/app/engine - ./static:/app/static + test-redis: + extends: + file: docker-compose.base.yml + service: redis-base + profiles: + - test + ports: + # Don't conflict with actual redis instance + - "127.0.0.1:6380:6379" + redis: - image: redis:alpine - container_name: quotient_redis + extends: + file: docker-compose.base.yml + service: redis-base restart: always - env_file: - - .env + container_name: quotient_redis ports: - "127.0.0.1:6379:6379" - networks: - - quotient_network - command: ["redis-server", "--requirepass", "${REDIS_PASSWORD:?REDIS_PASSWORD variable is not set}"] - healthcheck: - test: ["CMD-SHELL", "redis-cli -a $$REDIS_PASSWORD ping | grep -q PONG"] - interval: 5s - timeout: 5s - retries: 5 - start_period: 3s runner: build: diff --git a/tests/testutil/testutil.go b/tests/testutil/testutil.go index c7ea421..40f8b75 100644 --- a/tests/testutil/testutil.go +++ b/tests/testutil/testutil.go @@ -1,6 +1,7 @@ package testutil import ( + "cmp" "fmt" "os" "sync/atomic" @@ -30,17 +31,13 @@ func (r *RedisContainer) Close() error { func startRedis(t *testing.T) *RedisContainer { t.Helper() - host := os.Getenv("REDIS_HOST") - if host == "" { - host = "localhost" - } - port := os.Getenv("REDIS_PORT") - if port == "" { - port = "6379" - } + host := cmp.Or(os.Getenv("REDIS_HOST"), "localhost") + port := cmp.Or(os.Getenv("REDIS_PORT"), "6380") + password := cmp.Or(os.Getenv("REDIS_PASSWORD"), "redis_password") client := redis.NewClient(&redis.Options{ - Addr: fmt.Sprintf("%s:%s", host, port), + Addr: fmt.Sprintf("%s:%s", host, port), + Password: password, }) return &RedisContainer{Client: client} @@ -60,26 +57,11 @@ func (p *PostgresContainer) Close() error { func startPostgres(t *testing.T) *PostgresContainer { t.Helper() - host := os.Getenv("POSTGRES_HOST") - if host == "" { - host = "localhost" - } - port := os.Getenv("POSTGRES_PORT") - if port == "" { - port = "5432" - } - dbname := os.Getenv("POSTGRES_DB") - if dbname == "" { - dbname = "quotient_test" - } - user := os.Getenv("POSTGRES_USER") - if user == "" { - user = "postgres" - } - password := os.Getenv("POSTGRES_PASSWORD") - if password == "" { - password = "postgres" - } + host := cmp.Or(os.Getenv("POSTGRES_HOST"), "localhost") + port := cmp.Or(os.Getenv("POSTGRES_PORT"), "5432") + dbname := cmp.Or(os.Getenv("POSTGRES_DB"), "engine") + user := cmp.Or(os.Getenv("POSTGRES_USER"), "engineuser") + password := cmp.Or(os.Getenv("POSTGRES_PASSWORD"), "postgres_password") connString := fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=disable", host, port, user, password, dbname)