Skip to content

Commit

Permalink
feat(integration-tests): adding boilerplate for integration tests (#15)
Browse files Browse the repository at this point in the history
* feat(integration-tests): adding boilerplate for integration tests

* feat(integration-tests): ensure database is reset between tests

* feat(integration-tests): add compose absed test to CI

* feat(integration-tests): move to go 1.22.5

* feat(integration-tests): add test env file for fake creds
  • Loading branch information
logan-bobo authored Jul 25, 2024
1 parent 141f7bc commit 75349a3
Show file tree
Hide file tree
Showing 9 changed files with 173 additions and 18 deletions.
5 changes: 5 additions & 0 deletions .envtest
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
SERVER_PORT="8080"
PG_CONN="postgres://url_short:password@db-test:5432/url_short?sslmode=disable"
JWT_SECRET="mY+gjoSSg9qeEE0J3mDIlEkp3cEMk0sRReoNkOnLmnGYiWj0/2K0zl9cj1e8QJ34
LHc0sHPkhqATCfB9ENHxNQ=="
RDB_CONN="redis://redis:6379/0"
16 changes: 7 additions & 9 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,18 @@ jobs:
uses: golangci/[email protected]
with:
version: latest
# TODO: Move to use compose based tests
unit-test:
test:
needs: [fmt, lint]
name: unit-test
name: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Go environment
uses: actions/[email protected]
with:
go-version: '1.22.3'
- run: go test ./...
- name: Build test container
run: make build/test
- name: Run Tests
run: make test
build:
needs: unit-test
needs: test
name: build
runs-on: ubuntu-latest
steps:
Expand Down
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ RUN apk upgrade

FROM base AS builder

RUN apk add --update go=1.22.4-r0
RUN apk add --update go=1.22.5-r0

WORKDIR /build

Expand All @@ -15,7 +15,7 @@ RUN go build -o main .

FROM base AS tester

RUN apk add --update go=1.22.4-r0
RUN apk add --update go=1.22.5-r0

RUN wget -O- -nv https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s v1.59.1

Expand Down
8 changes: 5 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
COMPOSE_TEST_FILE="${PWD}/docker-compose-test.yaml"

fmt:
COMPOSE_FILE=${COMPOSE_TEST_FILE} docker compose run --rm --remove-orphans api go fmt
COMPOSE_FILE=${COMPOSE_TEST_FILE} docker compose run --rm --remove-orphans api-test go fmt
.PHONY:fmt

lint: fmt
COMPOSE_FILE=${COMPOSE_TEST_FILE} docker compose run --rm --remove-orphans api golangci-lint run -v
COMPOSE_FILE=${COMPOSE_TEST_FILE} docker compose run --rm --remove-orphans api-test golangci-lint run -v
.PHONY:lint

build:
Expand All @@ -25,7 +25,9 @@ stop:
.PHONY:stop

test:
COMPOSE_FILE=${COMPOSE_TEST_FILE} docker compose run --rm --remove-orphans api go test ./...
COMPOSE_FILE=${COMPOSE_TEST_FILE} docker compose up -d
COMPOSE_FILE=${COMPOSE_TEST_FILE} docker compose run --rm --remove-orphans api-test go test ./...
COMPOSE_FILE=${COMPOSE_TEST_FILE} docker compose down
.PHONY:test

migrate:
Expand Down
25 changes: 22 additions & 3 deletions docker-compose-test.yaml
Original file line number Diff line number Diff line change
@@ -1,10 +1,29 @@
version: '3.1'

services:
api:
api-test:
image: url-short:test
env_file:
- .env
- .envtest
volumes:
- ./:/opt/url-short

db-test:
image: postgres:16-alpine
restart: always
environment:
PGDATA: /var/lib/postgresql/data/pgdata
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
user: postgres
volumes:
- pgdatatest:/var/lib/postgresql/data
- ./bin/local-init.sql:/docker-entrypoint-initdb.d/local-init.sql
ports:
- 5002:5432
healthcheck:
test: ["CMD-SHELL", "pg_isready"]
interval: 2s
timeout: 5s
retries: 10
volumes:
pgdatatest:
5 changes: 5 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,16 @@ go 1.22.4
require (
github.com/golang-jwt/jwt/v5 v5.2.1
github.com/lib/pq v1.10.9
github.com/pressly/goose/v3 v3.21.1
github.com/redis/go-redis/v9 v9.5.3
golang.org/x/crypto v0.24.0
)

require (
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/mfridman/interpolate v0.0.2 // indirect
github.com/sethvargo/go-retry v0.2.4 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/sync v0.7.0 // indirect
)
46 changes: 46 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,59 @@ github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mfridman/interpolate v0.0.2 h1:pnuTK7MQIxxFz1Gr+rjSIx9u7qVjf5VOoM/u6BbAxPY=
github.com/mfridman/interpolate v0.0.2/go.mod h1:p+7uk6oE07mpE/Ik1b8EckO0O4ZXiGAfshKBWLUM9Xg=
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pressly/goose/v3 v3.21.1 h1:5SSAKKWej8LVVzNLuT6KIvP1eFDuPvxa+B6H0w78buQ=
github.com/pressly/goose/v3 v3.21.1/go.mod h1:sqthmzV8PitchEkjecFJII//l43dLOCzfWh8pHEe+vE=
github.com/redis/go-redis/v9 v9.5.3 h1:fOAp1/uJG+ZtcITgZOfYFmTKPE7n4Vclj1wZFgRciUU=
github.com/redis/go-redis/v9 v9.5.3/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/sethvargo/go-retry v0.2.4 h1:T+jHEQy/zKJf5s95UkguisicE0zuF9y7+/vgz08Ocec=
github.com/sethvargo/go-retry v0.2.4/go.mod h1:1afjQuvh7s4gflMObvjLPaWgluLLyhA1wmVZ6KLpICw=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI=
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4=
modernc.org/libc v1.41.0 h1:g9YAc6BkKlgORsUWj+JwqoB1wU3o4DE3bM3yvA3k+Gk=
modernc.org/libc v1.41.0/go.mod h1:w0eszPsiXoOnoMJgrXjglgLuDy/bt5RR4y3QzUUeodY=
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E=
modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E=
modernc.org/sqlite v1.29.6 h1:0lOXGrycJPptfHDuohfYgNqoe4hu+gYuN/pKgY5XjS4=
modernc.org/sqlite v1.29.6/go.mod h1:S02dvcmm7TnTRvGhv8IGYyLnIt7AS2KPaB1F/71p75U=
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
80 changes: 80 additions & 0 deletions handlers_test.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,47 @@
package main

import (
"bytes"
"context"
"database/sql"
"encoding/json"
"errors"
"net/http"
"net/http/httptest"
"os"
"testing"

"github.com/pressly/goose/v3"

"url-short/internal/database"
)

func resetDB(db *sql.DB) error {
provider, err := goose.NewProvider(
goose.DialectPostgres,
db,
os.DirFS("./sql/schema/"),
)

if err != nil {
return errors.New("can not create goose provider")
}

_, err = provider.DownTo(context.Background(), 0)

if err != nil {
return errors.New("can not reset database")
}

_, err = provider.Up(context.Background())

if err != nil {
return errors.New("could not run migrations")
}

return nil
}

func TestHealthEndpoint(t *testing.T) {
t.Run("test healthz endpoint", func(t *testing.T) {
request, _ := http.NewRequest(http.MethodGet, "/api/v1/healthz", nil)
Expand All @@ -16,6 +51,9 @@ func TestHealthEndpoint(t *testing.T) {

apiCfg.healthz(response, request)

// its not a good idea to use a stuct we already define in our code
// this could introduce a subtle bug where a test could pass because
// we incorrectly altered this struct
got := HealthResponse{}
err := json.NewDecoder(response.Body).Decode(&got)

Expand All @@ -32,3 +70,45 @@ func TestHealthEndpoint(t *testing.T) {
}
})
}

func TestPostUser(t *testing.T) {
dbURL := os.Getenv("PG_CONN")
db, err := sql.Open("postgres", dbURL)

if err != nil {
t.Errorf("can not open database connection")
}

err = resetDB(db)

if err != nil {
t.Errorf("could not resetDB %q", err)
}

dbQueries := database.New(db)

t.Run("test user creation", func(t *testing.T) {
requestJSON := []byte(`{"email": "[email protected]", "password": "test"}`)
request, _ := http.NewRequest(http.MethodPost, "/api/v1/users", bytes.NewBuffer(requestJSON))
request.Header.Set("Content-Type", "application/json")

response := httptest.NewRecorder()

apiCfg := apiConfig{
DB: dbQueries,
}

apiCfg.postAPIUsers(response, request)

got := APIUsersResponse{}
err := json.NewDecoder(response.Body).Decode(&got)

if err != nil {
t.Errorf("unable to parse response %q into %q", response.Body, got)
}

if got.Email != "[email protected]" {
t.Errorf("unexpected email in response, got %q, wanted %q", got.Email, "[email protected]")
}
})
}
2 changes: 1 addition & 1 deletion sql/schema/004_url_long_not_unique.sql
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ DROP CONSTRAINT urls_long_url_key;
-- +goose Down
ALTER TABLE urls
ADD CONSTRAINT urls_long_url_key
UNIQUE (long_url)
UNIQUE (long_url);

0 comments on commit 75349a3

Please sign in to comment.