Skip to content

Commit 2daa515

Browse files
committed
MySQL testing
1 parent c6fcbd2 commit 2daa515

File tree

4 files changed

+172
-2
lines changed

4 files changed

+172
-2
lines changed

.travis.yml

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
language: go
22
sudo: false
33
before_script:
4-
- psql -c 'create database travis_ci_test;' -U postgres
5-
- psql -c 'CREATE EXTENSION IF NOT EXISTS postgis; CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; CREATE EXTENSION IF NOT EXISTS "pg_trgm";' -U postgres -d travis_ci_test
4+
- psql -c 'create database sol_test;' -U postgres
5+
- psql -c 'CREATE EXTENSION IF NOT EXISTS postgis; CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; CREATE EXTENSION IF NOT EXISTS "pg_trgm";' -U postgres -d sol_test
6+
- mysql -e 'create database sol_test;'

mysql/README.md

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# MySQL
2+
3+
The MySQL dialect uses the [github.com/go-sql-driver/mysql](https://github.com/go-sql-driver/mysql) driver, which [passes the compatibility test suite](https://github.com/golang/go/wiki/SQLDrivers).
4+
5+
By default, this MySQL dialect will parse `DATE` and `DATETIME` columns into `[]byte` or `string` types. Support for `time.Time` must be explicitly enabled by adding the `parseTime=true` parameter to the connection string.
6+
7+
### Testing
8+
9+
A valid MySQL connection string should be set on the environmental variable `SOL_TEST_MYSQL`. An example:
10+
11+
user:pass@tcp(host:port)/db?parseTime=true
12+
13+
This variable can be given inline:
14+
15+
SOL_TEST_MYSQL="user:pass@tcp(host:port)/db?parseTime=true" go test -run=TestMySQL
16+
17+
If the environmental variable is not given, it will default to a [Travis CI](https://docs.travis-ci.com/user/database-setup/#MySQL) connection string, which will likely panic on your local system.
18+
19+
#### Docker
20+
21+
Docker hub provides an [official MySQL image](https://hub.docker.com/_/mysql/). A container can be started with:
22+
23+
docker run -p 3306:3306 --name mysql -e "MYSQL_ROOT_PASSWORD=" -e "MYSQL_ALLOW_EMPTY_PASSWORD=yes" -e "MYSQL_DATABASE=sol_test" -d mysql:latest
24+
25+
26+
Tests can then be running by supplying the host IP of the default Docker in the environmental variable credential string, such as:
27+
28+
SOL_TEST_MYSQL="root@tcp(host:3306)/sol_test?parseTime=true" go test

mysql/mysql_test.go

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package mysql
2+
3+
import (
4+
"os"
5+
"sync"
6+
"testing"
7+
8+
_ "github.com/go-sql-driver/mysql"
9+
10+
"github.com/aodin/sol"
11+
)
12+
13+
const travisCI = "root@tcp(127.0.0.1:3306)/sol_test?parseTime=true"
14+
15+
var conn *sol.DB
16+
var once sync.Once
17+
18+
// getConn returns a MySQL connection pool
19+
func getConn(t *testing.T) *sol.DB {
20+
// Check if an ENV VAR has been set, otherwise, use travis
21+
credentials := os.Getenv("SOL_TEST_MYSQL")
22+
if credentials == "" {
23+
credentials = travisCI
24+
}
25+
26+
once.Do(func() {
27+
var err error
28+
if conn, err = sol.Open("mysql", credentials); err != nil {
29+
t.Fatalf("Failed to open connection: %s", err)
30+
}
31+
conn.SetMaxOpenConns(20)
32+
})
33+
return conn
34+
}
35+
36+
func TestMySQL(t *testing.T) {
37+
conn := getConn(t)
38+
defer conn.Close()
39+
sol.IntegrationTest(t, conn)
40+
}

tester.go

+101
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@ import (
66
"runtime"
77
"strings"
88
"testing"
9+
"time"
910

1011
"github.com/aodin/sol/dialect"
12+
"github.com/aodin/sol/types"
1113
)
1214

1315
// callerInfo returns a string containing the file and line number of the
@@ -104,3 +106,102 @@ func (t *tester) SQL(stmt Compiles, expect string, ps ...interface{}) {
104106
func NewTester(t *testing.T, d dialect.Dialect) *tester {
105107
return &tester{t: t, dialect: d}
106108
}
109+
110+
// IntegrationTest runs a large, neutral dialect test
111+
func IntegrationTest(t *testing.T, conn *DB) {
112+
// Perform all tests in a transaction
113+
// TODO What features should be testing outside of a transaction?
114+
tx, err := conn.Begin()
115+
if err != nil {
116+
t.Fatalf("Creating a new transaction should not error: %s", err)
117+
}
118+
defer tx.Rollback()
119+
120+
// CREATE TABLE
121+
// TODO foreign keys
122+
testusers := Table("testusers",
123+
Column("id", types.Integer()),
124+
Column("email", types.Varchar().Limit(256).NotNull()),
125+
Column("is_admin", types.Boolean().NotNull()),
126+
Column("created_at", types.Timestamp()),
127+
PrimaryKey("id"),
128+
Unique("email"),
129+
)
130+
131+
type testuser struct {
132+
ID int64
133+
Email string
134+
IsAdmin bool
135+
CreatedAt time.Time
136+
}
137+
138+
if err = tx.Query(testusers.Create()); err != nil {
139+
t.Fatalf("CREATE TABLE should not error: %s", err)
140+
}
141+
142+
// INSERT by struct
143+
// Truncate the time.Time field to avoid significant digit errors
144+
admin := testuser{
145+
ID: 1,
146+
147+
IsAdmin: true,
148+
CreatedAt: time.Now().UTC().Truncate(time.Second),
149+
}
150+
151+
if err = tx.Query(testusers.Insert().Values(admin)); err != nil {
152+
t.Fatalf("INSERT should not fail %s", err)
153+
}
154+
155+
// SELECT
156+
var selected testuser
157+
if err = tx.Query(
158+
testusers.Select().Where(testusers.C("id").Equals(admin.ID)),
159+
&selected,
160+
); err != nil {
161+
t.Fatalf("SELECT should not fail: %s", err)
162+
}
163+
164+
if selected != admin {
165+
t.Errorf(
166+
"Unequal testusers: have %+v, want %+v",
167+
selected, admin,
168+
)
169+
}
170+
171+
// UPDATE
172+
if err = tx.Query(
173+
testusers.Update().Values(
174+
Values{"is_admin": false},
175+
).Where(testusers.C("id").Equals(admin.ID)),
176+
); err != nil {
177+
t.Fatalf("UPDATE should not fail: %s", err)
178+
}
179+
180+
var updated testuser
181+
if err = tx.Query(
182+
testusers.Select().Where(testusers.C("id").Equals(admin.ID)),
183+
&updated,
184+
); err != nil {
185+
t.Fatalf("SELECT should not fail: %s", err)
186+
}
187+
188+
selected.IsAdmin = false
189+
if updated != selected {
190+
t.Errorf(
191+
"Unequal testusers: have %+v, want %+v",
192+
updated, selected,
193+
)
194+
}
195+
196+
// DELETE
197+
if err = tx.Query(
198+
testusers.Delete().Where(testusers.C("email").Equals(admin.Email)),
199+
); err != nil {
200+
t.Fatalf("DELETE should not fail: %s", err)
201+
}
202+
203+
// DROP TABLE
204+
if err = tx.Query(testusers.Drop()); err != nil {
205+
t.Fatalf("DROP TABLE should not fail %s", err)
206+
}
207+
}

0 commit comments

Comments
 (0)