ではstore構造体にtodoManagerインターフェイスを実装していきます。
func (s *store) projectTodo(t todo) (string, error) {
return "", nil
}
テーブル定義
まずは保存用のテーブルを定義します。シンプルに以下の感じでいいでしょう。
package schema
var schemas = map[int]string{
0: `
...
`,
1: `
BEGIN;
CREATE TABLE todo (
id UUID NOT NULL PRIMARY KEY,
title STRING NOT NULL,
description STRING NOT NULL
);
UPDATE schema_version SET md_curr = false, md_update = EXTRACT(EPOCH FROM current_timestamp)::INT WHERE md_curr = true;
INSERT INTO schema_version VALUES (1);
COMMIT;
`,
}
スキーマのバージョンを更新するクエリにも注目してみてください。
store構造体のテスト
以下のようにテストを書いていきます。
package main
import "testing"
func TestStore_ProjectTodo(t *testing.T) {
t.Run("project a new todo", func(t *testing.T) {})
}
テスト用データベースのセットアップ
テスト時にはローカルにCockroachDBのインスタンスを立ち上げ、ダミーのDBに対してテストを実行していきたいと思います。
なのでセットアップ用に以下のユーティリティ関数、変数を用意します。
var testDB = fmt.Sprintf("test-%s", uuid.New().String())
func openDB(t *testing.T) *sql.DB {
connStr := "postgres://root@localhost:26257/?sslmode=disable"
db, err := sql.Open("postgres", connStr)
require.NoError(t, err)
_, err = db.Exec(fmt.Sprintf(`CREATE DATABASE IF NOT EXISTS "%s"; USE "%s"`, testDB, testDB))
require.NoError(t, err)
return db
}
func closeDB(t *testing.T, db *sql.DB) {
require.NoError(t, db.Close())
}
テスト実行のたびにデータベースを作成することになるためパフォーマンスはよいとは言えませんが、問題となる規模になるまではこれで十分でしょう。
ローカルでのCockroachDB起動のため以下のdocker-compose.yaml
を用意します。
version: '3'
services:
cockroachdb:
image: cockroachdb/cockroach
container_name: cockroachdb
ports:
- "26257:26257"
- "8080:8080"
command: ["start", "--insecure"]
テストの実行
準備が整ったためテストを書きます。
一気に書いてしまいます。以下のような感じです。
func TestStore_ProjectTodo(t *testing.T) {
t.Run("project a new todo", func(t *testing.T) {
db := openDB(t)
defer closeDB(t, db)
sut, err := newStore(db, defaultSchemaVersion)
require.NoError(t, err)
input := todo{
title: "foo title",
description: "foo description",
}
id, err := sut.projectTodo(input)
require.NoError(t, err)
var got todo
require.NoError(t, db.QueryRow(
`SELECT title, description FROM todo WHERE id = $1`,
id,
).Scan(&got.title, &got.description))
assert.Equal(t, input, got)
})
}
main.go
でデフォルトのスキーマバージョンを更新しておくのもお忘れなく。
var (
gitHash = "overriden at compile time"
- defaultSchemaVersion = 0
+ defaultSchemaVersion = 1
)
ではdocker-composeでローカル環境を立ち上げて、テストを実行してみます。
$ docker-compose up
Creating network "qiita-advent-calendar-2019_default" with the default driver
Creating cockroachdb ... done
Attaching to cockroachdb
...
$ make test
go test -v -cover -timeout 30s ./...
=== RUN TestServer_CreateTodo
=== RUN TestServer_CreateTodo/project_a_new_todo
=== RUN TestServer_CreateTodo/error_in_projection
--- PASS: TestServer_CreateTodo (0.00s)
--- PASS: TestServer_CreateTodo/project_a_new_todo (0.00s)
--- PASS: TestServer_CreateTodo/error_in_projection (0.00s)
=== RUN TestStore_ProjectTodo
=== RUN TestStore_ProjectTodo/project_a_new_todo
--- FAIL: TestStore_ProjectTodo (0.21s)
--- FAIL: TestStore_ProjectTodo/project_a_new_todo (0.21s)
require.go:794:
Error Trace: store_test.go:47
Error: Received unexpected error:
pq: error in argument for $1: could not parse string "" as uuid
Test: TestStore_ProjectTodo/project_a_new_todo
FAIL
coverage: 22.9% of statements
FAIL github.com/KentaKudo/qiita-advent-calendar-2019 0.897s
? github.com/KentaKudo/qiita-advent-calendar-2019/internal/pb/service [no test files]
? github.com/KentaKudo/qiita-advent-calendar-2019/internal/schema [no test files]
FAIL
make: *** [test] Error 1
失敗です。いいですね。
store.projectTodoの実装
ではいよいよstore.projectTodo
を以下のように実装します。
func (s *store) projectTodo(t todo) (string, error) {
id := uuid.New().String()
if _, err := s.db.Exec(
`INSERT INTO todo (id, title, description) VALUES ($1, $2, $3)`,
id, t.title, t.description,
); err != nil {
return "", err
}
return id, nil
}
再度テストを実行。
$ make test
go test -v -cover -timeout 30s ./...
=== RUN TestServer_CreateTodo
=== RUN TestServer_CreateTodo/project_a_new_todo
=== RUN TestServer_CreateTodo/error_in_projection
--- PASS: TestServer_CreateTodo (0.00s)
--- PASS: TestServer_CreateTodo/project_a_new_todo (0.00s)
--- PASS: TestServer_CreateTodo/error_in_projection (0.00s)
=== RUN TestStore_ProjectTodo
=== RUN TestStore_ProjectTodo/project_a_new_todo
--- PASS: TestStore_ProjectTodo (0.55s)
--- PASS: TestStore_ProjectTodo/project_a_new_todo (0.55s)
PASS
coverage: 24.7% of statements
ok github.com/KentaKudo/qiita-advent-calendar-2019 1.269s coverage: 24.7% of statements
? github.com/KentaKudo/qiita-advent-calendar-2019/internal/pb/service [no test files]
? github.com/KentaKudo/qiita-advent-calendar-2019/internal/schema [no test files]
通りました:)
main.goの更新
最後にmain.go
内でserver構造体に依存注入するようにしましょう。
利便性のためnewServer()
関数も定義しておきます。
func newServer(todoMgr todoManager) *server {
return &server{
todoMgr: todoMgr,
}
}
func main() {
app := cli.App(appName, appDesc)
...
app.Action = func() {
...
store, err := newStore(db, *schemaVersion)
if err != nil {
log.WithError(err).Fatalln("init store")
}
lis, err := net.Listen("tcp", net.JoinHostPort("", strconv.Itoa(*grpcPort)))
if err != nil {
log.Fatalln("init gRPC server:", err)
}
defer lis.Close()
gSrv := initialiseGRPCServer(newServer(store))
...
}
if err := app.Run(os.Args); err != nil {
log.WithError(err).Fatal("app run")
}
}
CircleCI設定の更新
忘れていました。CircleCI上でもCockroachDBを立ち上げてテストできるようにします。
$ git diff
diff --git a/.circleci/config.yml b/.circleci/config.yml
index 52436a0..fbeabaa 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -4,6 +4,8 @@ jobs:
working_directory: ~/qiita-advent-calendar-2019
docker:
- image: circleci/golang:1
+ - image: cockroachdb/cockroach
+ command: ["start", "--insecure"]
steps:
- checkout
- run: make all
@@ -12,6 +14,8 @@ jobs:
working_directory: ~/qiita-advent-calendar-2019
docker:
- image: circleci/golang:1
+ - image: cockroachdb/cockroach
+ command: ["start", "--insecure"]
steps:
- checkout
- run: make all
ついでに
やっぱりインテグレーションテストもしてみたかったのでBloomRPCでデバッグしてみました。
デプロイをしなおしたらport-forwardでポッドを繋いで、
$ kubectl -n qiita scale --replicas=0 deployment qiita-advent-calendar-2019
$ kubectl -n qiita scale --replicas=1 deployment qiita-advent-calendar-2019
$ kubectl -n qiita port-forward qiita-advent-calendar-2019-5bc6786c75-jwl5r 8090:8090
Forwarding from 127.0.0.1:8090 -> 8090
Forwarding from [::1]:8090 -> 8090
BloomRPCでリクエストの送信。
DBの覗き見。
$ kubectl -n qiita exec -it cockroachdb-0 -- /cockroach/cockroach sql --url postgres://root@localhost:26257 --insecure
...
root@localhost:26257/defaultdb> use qiita_advent_calendar_2019_db;
SET
Time: 998.634µs
root@localhost:26257/qiita_advent_calendar_2019_db> select * from todo;
id | title | description
+--------------------------------------+------------------+-------------------------------------------------+
8644af1a-d16a-4b92-911f-19e03905dffe | wash your hands! | wash your hands when you get back from outside!
(1 row)
Time: 26.989411ms
いいですねいいいですね。
というわけでCreateTodo
エンドポイントの実装が完了しました。
盛りだくさんでしたがいかがでしたか?
明日からはトピックがまた変わります。お楽しみに。