Skip to content

Commit

Permalink
Merge pull request #4 from ryichk/add-test-code
Browse files Browse the repository at this point in the history
テストコードを追加
  • Loading branch information
ryichk authored Dec 31, 2024
2 parents a9986e4 + b5faf84 commit 4180e4e
Show file tree
Hide file tree
Showing 16 changed files with 709 additions and 0 deletions.
2 changes: 2 additions & 0 deletions api/.golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ linters:
linters-settings:
govet:
enable-all: true
disable:
- fieldalignment

issues:
exclude-use-default: false
30 changes: 30 additions & 0 deletions api/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,58 @@ require (
github.com/MicahParks/keyfunc/v3 v3.3.5
github.com/go-playground/validator/v10 v10.23.0
github.com/golang-jwt/jwt/v5 v5.2.1
github.com/golang-migrate/migrate/v4 v4.18.1
github.com/jackc/pgx/v5 v5.7.2
github.com/labstack/echo/v4 v4.13.3
github.com/labstack/gommon v0.4.2
github.com/ory/dockertest/v3 v3.11.0
)

require (
dario.cat/mergo v1.0.0 // indirect
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
github.com/MicahParks/jwkset v0.5.19 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/containerd/continuity v0.4.3 // indirect
github.com/docker/cli v26.1.4+incompatible // indirect
github.com/docker/docker v27.2.0+incompatible // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jackc/puddle/v2 v2.2.2 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/lib/pq v1.10.9 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/moby/term v0.5.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0 // indirect
github.com/opencontainers/runc v1.1.13 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
go.uber.org/atomic v1.7.0 // indirect
golang.org/x/crypto v0.31.0 // indirect
golang.org/x/net v0.33.0 // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/sys v0.28.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/time v0.8.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)
131 changes: 131 additions & 0 deletions api/go.sum

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions api/internal/db/query/test_truncate.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-- name: TruncateAllTables :exec
TRUNCATE TABLE
app.todos
CASCADE;
1 change: 1 addition & 0 deletions api/internal/db/query/todo.sql
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
-- name: ListTodos :many
SELECT
id,
title,
note,
done,
Expand Down
25 changes: 25 additions & 0 deletions api/internal/handler/hello_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package handler

import (
"net/http"
"net/http/httptest"
"testing"

"github.com/ryichk/todolist/api/internal/model"
"github.com/ryichk/todolist/api/internal/testutil"
)

func TestHello(t *testing.T) {
e := testutil.SetupEcho()
req := httptest.NewRequest(http.MethodGet, "/", nil)
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)
queries := model.New()
h := NewHandler(testDBPool, queries)
if err := h.Hello(c); err != nil {
t.Errorf("Hello() error = %v", err)
}
if rec.Code != http.StatusOK {
t.Errorf("Hello() rec.Code = %v, want %v", rec.Code, http.StatusOK)
}
}
26 changes: 26 additions & 0 deletions api/internal/handler/testmain_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package handler

import (
"os"
"testing"

"github.com/jackc/pgx/v5/pgxpool"
"github.com/ory/dockertest/v3"

"github.com/ryichk/todolist/api/internal/testdb"
"github.com/ryichk/todolist/api/internal/testutil"
)

var testDBPool *pgxpool.Pool

func TestMain(m *testing.M) {
var dockerPool *dockertest.Pool
var dockerResource *dockertest.Resource
dockerPool, dockerResource, testDBPool = testutil.SetupTestDB()

code := m.Run()

testdb.CloseTestContainer(dockerPool, dockerResource)

os.Exit(code)
}
101 changes: 101 additions & 0 deletions api/internal/handler/todo_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package handler

import (
"bytes"
"context"
"encoding/json"
"log"
"net/http"
"net/http/httptest"
"testing"

"github.com/labstack/echo/v4"
"github.com/ryichk/todolist/api/internal/model"
"github.com/ryichk/todolist/api/internal/testutil"
)

func TestListTodos(t *testing.T) {
e := testutil.SetupEcho()
queries := model.New()
h := NewHandler(testDBPool, queries)

ctx := context.Background()

userID := testutil.SeedTestUserID()
conn, err := queries.AcquireConnection(ctx, testDBPool, userID)
if err != nil {
log.Fatalf("failed to acquire connection: %v", err)
}

todo := testutil.SetupTestTodo(ctx, queries, conn)

t.Run("Todo一覧を取得できる", func(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/todos", nil)
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)
testutil.SetAuthContext(c)

if err := h.ListTodos(c); err != nil {
t.Errorf("Handler.ListTodos() error = %v", err)
}
if rec.Code != http.StatusOK {
t.Errorf("Handler.ListTodos() got http status = %v, want %v", rec.Code, http.StatusOK)
}
var got []model.ListTodosRow
if err := json.NewDecoder(rec.Body).Decode(&got); err != nil {
t.Errorf("Handler.ListTodos() error decoding response body: %v", err)
}
if got[0].ID != todo.ID {
t.Errorf("Handler.ListTodos() got ID = %v, want %v", got[0].ID, todo.ID)
}
if got[0].Title != todo.Title {
t.Errorf("Handler.ListTodos() got Title = %v, want %v", got[0].Title, todo.Title)
}
if got[0].Note != todo.Note {
t.Errorf("Handler.ListTodos() got Note = %v, want %v", got[0].Note, todo.Note)
}
})

testutil.Teardown(ctx, queries, conn)
}

func TestCreateTodo(t *testing.T) {
e := testutil.SetupEcho()
queries := model.New()
h := NewHandler(testDBPool, queries)
ctx := context.Background()
userID := testutil.SeedTestUserID()
conn, err := queries.AcquireConnection(ctx, testDBPool, userID)
if err != nil {
log.Fatalf("failed to acquire connection: %v", err)
}

t.Run("Todoを作成できる", func(t *testing.T) {
requestBody, _ := json.Marshal(map[string]interface{}{
"title": "テストタイトル",
"note": "テストメモ",
})
req := httptest.NewRequest(http.MethodPost, "/todos", bytes.NewReader(requestBody))
req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)
testutil.SetAuthContext(c)

if err := h.CreateTodo(c); err != nil {
t.Errorf("Handler.CreateTodo() error = %v", err)
}
if rec.Code != http.StatusCreated {
t.Errorf("Handler.CreateTodo() got http status = %v, want %v", rec.Code, http.StatusCreated)
}
var got map[string]string
if err := json.NewDecoder(rec.Body).Decode(&got); err != nil {
t.Errorf("Handler.CreateTodo() error decoding response body: %v", err)
}
successMessage := "Successfully created a todo"
if got["message"] != successMessage {
t.Errorf("Handler.CreateTodo() got message = %v, want %v", got["message"], successMessage)
}
})

testutil.Teardown(ctx, queries, conn)
}
21 changes: 21 additions & 0 deletions api/internal/model/test_truncate.sql.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions api/internal/model/todo.sql.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions api/internal/testdata/testdata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"user_id": "90000000-0000-0000-0000-000000000000",
"user_id_str": "90000000-0000-0000-0000-000000000000"
}
109 changes: 109 additions & 0 deletions api/internal/testdb/testdb.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package testdb

import (
"context"
"fmt"
"log"
"os"
"time"

"github.com/golang-migrate/migrate/v4"
_ "github.com/golang-migrate/migrate/v4/database/postgres"
_ "github.com/golang-migrate/migrate/v4/source/file"
"github.com/jackc/pgx/v5/pgxpool"
"github.com/ory/dockertest/v3"
"github.com/ory/dockertest/v3/docker"
)

func CreateTestContainer(postgresUser, postgresPassword, postgresDB string) (*dockertest.Pool, *dockertest.Resource) {
dockerPool, err := dockertest.NewPool("")
if err != nil {
log.Fatalf("Could not construct pool: %v", err)
}
dockerPool.MaxWait = 5 * time.Second

runOptions := &dockertest.RunOptions{
Repository: "postgres",
Tag: "17",
Env: []string{
fmt.Sprintf("POSTGRES_USER=%s", postgresUser),
fmt.Sprintf("POSTGRES_PASSWORD=%s", postgresPassword),
fmt.Sprintf("POSTGRES_DB=%s", postgresDB),
"listen_addresses='*'",
},
}

resource, err := dockerPool.RunWithOptions(runOptions,
func(config *docker.HostConfig) {
config.AutoRemove = true
config.RestartPolicy = docker.RestartPolicy{
Name: "no",
}
},
)
if err != nil {
log.Fatalf("Could not start resource: %v", err)
}

return dockerPool, resource
}

func ConnectTestDB(dockerPool *dockertest.Pool, resource *dockertest.Resource, postgresUser, postgresPassword, postgresDB string) *pgxpool.Pool {
databaseURL := getDatabaseURL(resource, postgresUser, postgresPassword, postgresDB)

var pool *pgxpool.Pool

if retryErr := dockerPool.Retry(func() error {
var err error
pool, err = pgxpool.New(context.Background(), databaseURL)
if err != nil {
return err
}
if err := pool.Ping(context.Background()); err != nil {
return err
}
return nil
}); retryErr != nil {
log.Fatalf("Could not connect to Docker: %v", retryErr)
}

return pool
}

func MigrateTestDB(resource *dockertest.Resource, postgresUser, postgresPassword, postgresDB string) {
databaseURL := getDatabaseURL(resource, postgresUser, postgresPassword, postgresDB)
mig, err := migrate.New("file://../db/migration", databaseURL)
if err != nil {
log.Fatal(err)
}
if err := mig.Up(); err != nil {
if err.Error() == "no change" {
log.Println("No change")
} else {
log.Fatal(err)
}
}
}

func CloseTestContainer(dockerPool *dockertest.Pool, resource *dockertest.Resource) {
if err := dockerPool.Purge(resource); err != nil {
log.Fatalf("Could not purge resource: %v", err)
}
}

func getDatabaseURL(resource *dockertest.Resource, postgresUser, postgresPassword, postgresDB string) string {
hostname := os.Getenv("DOCKER_HOSTNAME")
if hostname == "" {
hostname = "localhost"
}
port := resource.GetPort("5432/tcp")
databaseURL := fmt.Sprintf("postgres://%s:%s@%s:%s/%s?sslmode=disable",
postgresUser,
postgresPassword,
hostname,
port,
postgresDB,
)

return databaseURL
}
Loading

0 comments on commit 4180e4e

Please sign in to comment.