Skip to content

Commit e29d69e

Browse files
committed
Initial commit with mysql tests
0 parents  commit e29d69e

File tree

2 files changed

+234
-0
lines changed

2 files changed

+234
-0
lines changed

docker-compose.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
version: '3'
2+
services:
3+
mysql:
4+
image: mysql
5+
environment:
6+
MYSQL_ROOT_PASSWORD: root
7+
MYSQL_DATABASE: gosqltest
8+
ports:
9+
- 3306:3306

sqltest/sql_test.go

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
package sqltest
2+
3+
import (
4+
"database/sql"
5+
"math/rand"
6+
"net"
7+
"os/exec"
8+
"regexp"
9+
"strconv"
10+
"strings"
11+
"sync"
12+
"testing"
13+
14+
_ "github.com/go-sql-driver/mysql"
15+
_ "github.com/ziutek/mymysql/godrv"
16+
)
17+
18+
const TablePrefix = "gosqltest_"
19+
20+
type mysqlDB struct {
21+
driver string
22+
connectionString string
23+
container string
24+
once sync.Once
25+
running bool
26+
}
27+
28+
type pqDB struct {
29+
}
30+
31+
type oracleDB struct {
32+
}
33+
34+
type params struct {
35+
dbType Tester
36+
*testing.T
37+
*sql.DB
38+
}
39+
40+
type Tester interface {
41+
RunTest(*testing.T, func(params))
42+
}
43+
44+
var (
45+
goMysqlDB Tester = &mysqlDB{driver: "mysql", container: "mysql", connectionString: "root:root@/gosqltest"}
46+
myMysqlDB Tester = &mysqlDB{driver: "mymysql", container: "mysql", connectionString: "gosqltest/root/root"}
47+
pq Tester = &pqDB{}
48+
oracle Tester = &oracleDB{}
49+
)
50+
51+
func (m *mysqlDB) Running() bool {
52+
m.once.Do(func() {
53+
c, err := net.Dial("tcp", "localhost:3306")
54+
if err == nil {
55+
m.running = true
56+
c.Close()
57+
}
58+
})
59+
return m.running
60+
}
61+
62+
func startContainer(t *testing.T, c string) {
63+
cmd := exec.Command("docker-compose", "up", "-d", c)
64+
if err := cmd.Start(); err != nil {
65+
t.Fatalf("could not start %s using docker-compose: %v", c, err)
66+
}
67+
}
68+
69+
func stopContainer(t *testing.T, c string) {
70+
cmd := exec.Command("docker-compose", "down", c)
71+
if err := cmd.Start(); err != nil {
72+
t.Fatalf("could not stop %s using docker-compose: %v", c, err)
73+
}
74+
}
75+
76+
func (m *mysqlDB) RunTest(t *testing.T, fn func(params)) {
77+
// startContainer(t, m.container)
78+
// defer stopContainer(t, m.container)
79+
// wait 60 seconds for db to start
80+
// <-time.After(60 * time.Second)
81+
82+
if !m.Running() {
83+
t.Logf("skipping test; no MySQL running on localhost:3306")
84+
return
85+
}
86+
db, err := sql.Open(m.driver, m.connectionString)
87+
if err != nil {
88+
t.Fatalf("error connecting: %v", err)
89+
}
90+
91+
params := params{m, t, db}
92+
93+
// Drop all tables in the test database.
94+
rows, err := db.Query("SHOW TABLES")
95+
if err != nil {
96+
t.Fatalf("failed to enumerate tables: %v", err)
97+
}
98+
for rows.Next() {
99+
var table string
100+
if rows.Scan(&table) == nil &&
101+
strings.HasPrefix(strings.ToLower(table), strings.ToLower(TablePrefix)) {
102+
params.mustExec("DROP TABLE " + table)
103+
}
104+
}
105+
106+
fn(params)
107+
}
108+
109+
func (p *pqDB) RunTest(t *testing.T, fn func(params)) {
110+
//TODO
111+
}
112+
113+
func (o *oracleDB) RunTest(t *testing.T, fn func(params)) {
114+
//TODO
115+
}
116+
117+
func (t params) mustExec(sql string, args ...interface{}) sql.Result {
118+
res, err := t.DB.Exec(sql, args...)
119+
if err != nil {
120+
t.Fatalf("Error running %q: %v", sql, err)
121+
}
122+
return res
123+
}
124+
125+
func testPreparedStmt(t params) {
126+
t.mustExec("CREATE TABLE " + TablePrefix + "t (count INT)")
127+
sel, err := t.Prepare("SELECT count FROM " + TablePrefix + "t ORDER BY count DESC")
128+
if err != nil {
129+
t.Fatalf("prepare 1: %v", err)
130+
}
131+
ins, err := t.Prepare(t.q("INSERT INTO " + TablePrefix + "t (count) VALUES (?)"))
132+
if err != nil {
133+
t.Fatalf("prepare 2: %v", err)
134+
}
135+
136+
for n := 1; n <= 3; n++ {
137+
if _, err := ins.Exec(n); err != nil {
138+
t.Fatalf("insert(%d) = %v", n, err)
139+
}
140+
}
141+
142+
const nRuns = 10
143+
ch := make(chan bool)
144+
for i := 0; i < nRuns; i++ {
145+
go func() {
146+
defer func() {
147+
ch <- true
148+
}()
149+
for j := 0; j < 10; j++ {
150+
count := 0
151+
if err := sel.QueryRow().Scan(&count); err != nil && err != sql.ErrNoRows {
152+
t.Errorf("Query: %v", err)
153+
return
154+
}
155+
if _, err := ins.Exec(rand.Intn(100)); err != nil {
156+
t.Errorf("Insert: %v", err)
157+
return
158+
}
159+
}
160+
}()
161+
}
162+
for i := 0; i < nRuns; i++ {
163+
<-ch
164+
}
165+
}
166+
167+
var qrx = regexp.MustCompile(`\?`)
168+
169+
// q converts "?" characters to $1, $2, $n on postgres, :1, :2, :n on Oracle
170+
func (t params) q(sql string) string {
171+
var pref string
172+
switch t.dbType {
173+
case pq:
174+
pref = "$"
175+
case oracle:
176+
pref = ":"
177+
default:
178+
return sql
179+
}
180+
n := 0
181+
return qrx.ReplaceAllStringFunc(sql, func(string) string {
182+
n++
183+
return pref + strconv.Itoa(n)
184+
})
185+
}
186+
187+
func testTxQuery(t params) {
188+
tx, err := t.Begin()
189+
if err != nil {
190+
t.Fatal(err)
191+
}
192+
defer tx.Rollback()
193+
194+
_, err = t.DB.Exec("create table " + TablePrefix + "foo (id integer primary key, name varchar(50))")
195+
if err != nil {
196+
t.Logf("cannot drop table "+TablePrefix+"foo: %s", err)
197+
}
198+
199+
_, err = tx.Exec(t.q("insert into "+TablePrefix+"foo (id, name) values(?,?)"), 1, "bob")
200+
if err != nil {
201+
t.Fatal(err)
202+
}
203+
204+
r, err := tx.Query(t.q("select name from "+TablePrefix+"foo where id = ?"), 1)
205+
if err != nil {
206+
t.Fatal(err)
207+
}
208+
defer r.Close()
209+
210+
if !r.Next() {
211+
if r.Err() != nil {
212+
t.Fatal(err)
213+
}
214+
t.Fatal("expected one rows")
215+
}
216+
217+
var name string
218+
err = r.Scan(&name)
219+
if err != nil {
220+
t.Fatal(err)
221+
}
222+
}
223+
224+
func TestTxQuery_GoMySQL(t *testing.T) { goMysqlDB.RunTest(t, testTxQuery) }
225+
func TestTxQuery_MyMySQL(t *testing.T) { myMysqlDB.RunTest(t, testTxQuery) }

0 commit comments

Comments
 (0)