-
Notifications
You must be signed in to change notification settings - Fork 1
/
create.go
146 lines (130 loc) · 4.67 KB
/
create.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
// Package test contains test helper functions dealing with a database.
package test
import (
"database/sql"
"fmt"
"io/ioutil"
"os"
"github.com/jimmc/jraceman/dbrepo"
"github.com/golang/glog"
goldendb "github.com/jimmc/golden/db"
_ "github.com/mattn/go-sqlite3"
)
// Struct TestRecord matches the format of the test table
// created by DbWithTestTable.
type TestRecord struct {
ID string;
N int;
S string;
S2 *string;
}
// DbWIthTestTable creates a test database with one small table
// called test.
func DbWithTestTable() (*sql.DB, error) {
return goldendb.DbWithSetupString(`
CREATE table test(id string, n int, s string, s2 string);
INSERT into test(id, n, s, s2)
values('T1', 1, 'a', 'A'), ('T2', 2, 'b', null), ('T3', 3, 'c', 'C');
`)
}
// ReposEmpty creates an empty test Repos.
// The second return value is the cleanup function. Tests should call
// this function to ensure that everything is properly cleaned up
// at the end of each test.
func ReposEmpty() (*dbrepo.Repos, func(), error) {
// The in-memory database has some quirks that make it harder to use for testing.
// See the comments in ReposEmptyInMemory. Using a TempFile database is still pretty
// fast for our tests, and doesn't have those problems.
// return ReposEmptyInMemory()
return ReposEmptyTempFile()
}
// ReposEmptyInMemory create an empty in-memory Repos for
// use with unit tests.
func ReposEmptyInMemory() (*dbrepo.Repos, func(), error) {
// If we specify ":memory": to sqlite3, each connection opens a new
// in-memory database. This appears to happen when you try to issue
// a query on a connection while another query is still open, which
// makes tests fail with a "no such table" error. According to the FAQ
// (https://github.com/mattn/go-sqlite3/blob/master/README.md#faq)
// a workaround is to file "file::memory:?cache=shared", which will
// make all connections point to the same database.
// Unfortunately, this also means the next time an in-memory database
// is opened, it opens the same database, so the cleanup is not done
// properly. A potential workaround for this problem is given here:
// https://stackoverflow.com/questions/21547616/how-do-i-completely-clear-a-sqlite3-database-without-deleting-the-database-file
nop := func(){}
repos, err := dbrepo.Open("sqlite3:file::memory:?cache=shared")
if err != nil {
return nil, nop, fmt.Errorf("failed to open in-memory database: %w", err)
}
cleanup := func() {
repos.Close()
}
return repos, cleanup, err
}
// ReposEmptyTempFile create an empty Repos database using a temp
// file for use with unit tests.
func ReposEmptyTempFile() (*dbrepo.Repos, func(), error) {
nop := func(){}
tmpf, err := ioutil.TempFile("", "jrdbtest")
if err != nil {
return nil, nop, fmt.Errorf("failed to open tmp file for database: %w", err)
}
tmpf.Close()
dbr, err := dbrepo.Open("sqlite3:file:"+tmpf.Name())
if err != nil {
os.Remove(tmpf.Name())
return nil, nop, fmt.Errorf("failed to open database on tmp file %q: %w",
tmpf.Name(), err)
}
fn := tmpf.Name()
cleanup := func() {
dbr.Close()
os.Remove(fn)
}
return dbr, cleanup, nil
}
// ReposAndLoadFile creates a test Repos with the default
// set of tables, then imports the specified JRaceman-format file.
func ReposAndLoadFile(setupfile string) (*dbrepo.Repos, func(), error) {
repos, cleanup, err := ReposEmpty()
if err != nil {
cleanup()
return nil, nil, fmt.Errorf("failed to open repository: %v", err)
}
err = repos.CreateTables()
if err != nil {
cleanup()
return nil, nil, fmt.Errorf("failed to create repository tables: %v", err)
}
glog.Infof("Importing from %s\n", setupfile)
counts, err := repos.ImportFile(setupfile)
if err != nil {
cleanup()
return nil, nil, fmt.Errorf("error importing from %s: %v", setupfile, err)
}
glog.Infof("Import done: inserted %d, updated %d, unchanged %d records\n",
counts.Inserted(), counts.Updated(), counts.Unchanged())
return repos, cleanup, nil
}
// ReposAndSqlFile creates a test Repos and loads
// the specified SQL file. It does not create the standard tables.
func EmptyReposAndSqlFile(setupfile string) (*dbrepo.Repos, func(), error) {
repos, cleanup, err := ReposEmpty()
if err != nil {
cleanup()
return nil, nil, fmt.Errorf("failed to open repository: %v", err)
}
glog.Infof("Loading SQL from %s\n", setupfile)
db, ok := repos.DB().(*sql.DB)
if !ok {
cleanup()
return nil, nil, fmt.Errorf("repos.DB() is not *sql.DB")
}
err = goldendb.LoadSetupFile(db, setupfile)
if err != nil {
cleanup()
return nil, nil, fmt.Errorf("error importing from %s: %v", setupfile, err)
}
return repos, cleanup, nil
}