-
Notifications
You must be signed in to change notification settings - Fork 83
/
dbtest.go
164 lines (133 loc) · 4.14 KB
/
dbtest.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
// Package dbtest contains supporting code for running tests that hit the DB.
package dbtest
import (
"bufio"
"bytes"
"context"
"fmt"
"testing"
"time"
"github.com/go-olive/olive/business/data/dbschema"
"github.com/go-olive/olive/business/sys/database"
"github.com/go-olive/olive/foundation/docker"
"github.com/jmoiron/sqlx"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
// Success and failure markers.
const (
Success = "\u2713"
Failed = "\u2717"
)
// StartDB starts a database instance.
func StartDB() (*docker.Container, error) {
image := "postgres:14-alpine"
port := "5432"
args := []string{"-e", "POSTGRES_PASSWORD=postgres"}
return docker.StartContainer(image, port, args...)
}
// StopDB stops a running database instance.
func StopDB(c *docker.Container) {
docker.StopContainer(c.ID)
}
// NewUnit creates a test database inside a Docker container. It creates the
// required table structure but the database is otherwise empty. It returns
// the database to use as well as a function to call at the end of the test.
func NewUnit(t *testing.T, c *docker.Container, dbName string) (*zap.SugaredLogger, *sqlx.DB, func()) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
dbM, err := database.Open(database.Config{
User: "postgres",
Password: "postgres",
Host: c.Host,
Name: "postgres",
DisableTLS: true,
})
if err != nil {
t.Fatalf("Opening database connection: %v", err)
}
t.Log("Waiting for database to be ready ...")
if err := database.StatusCheck(ctx, dbM); err != nil {
t.Fatalf("status check database: %v", err)
}
t.Log("Database ready")
if _, err := dbM.ExecContext(context.Background(), "CREATE DATABASE "+dbName); err != nil {
t.Fatalf("creating database %s: %v", dbName, err)
}
dbM.Close()
// =========================================================================
db, err := database.Open(database.Config{
User: "postgres",
Password: "postgres",
Host: c.Host,
Name: dbName,
DisableTLS: true,
})
if err != nil {
t.Fatalf("Opening database connection: %v", err)
}
t.Log("Migrate and seed database ...")
if err := dbschema.Migrate(ctx, db); err != nil {
docker.DumpContainerLogs(t, c.ID)
t.Fatalf("Migrating error: %s", err)
}
if err := dbschema.Seed(ctx, db); err != nil {
docker.DumpContainerLogs(t, c.ID)
t.Fatalf("Seeding error: %s", err)
}
t.Log("Ready for testing ...")
var buf bytes.Buffer
encoder := zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig())
writer := bufio.NewWriter(&buf)
log := zap.New(
zapcore.NewCore(encoder, zapcore.AddSync(writer), zapcore.DebugLevel)).
Sugar()
// teardown is the function that should be invoked when the caller is done
// with the database.
teardown := func() {
t.Helper()
db.Close()
log.Sync()
writer.Flush()
fmt.Println("******************** LOGS ********************")
fmt.Print(buf.String())
fmt.Println("******************** LOGS ********************")
}
return log, db, teardown
}
// Test owns state for running and shutting down tests.
type Test struct {
DB *sqlx.DB
Log *zap.SugaredLogger
Teardown func()
t *testing.T
}
// NewIntegration creates a database, seeds it, constructs an authenticator.
func NewIntegration(t *testing.T, c *docker.Container, dbName string) *Test {
log, db, teardown := NewUnit(t, c, dbName)
test := Test{
DB: db,
Log: log,
t: t,
Teardown: teardown,
}
return &test
}
// StringPointer is a helper to get a *string from a string. It is in the tests
// package because we normally don't want to deal with pointers to basic types
// but it's useful in some tests.
func StringPointer(s string) *string {
return &s
}
// IntPointer is a helper to get a *int from a int. It is in the tests package
// because we normally don't want to deal with pointers to basic types but it's
// useful in some tests.
func IntPointer(i int) *int {
return &i
}
// BoolPointer is a helper to get a *bool from a bool. It is in the tests package
// because we normally don't want to deal with pointers to basic types but it's
// useful in some tests.
func BoolPointer(b bool) *bool {
return &b
}