forked from adamlaska/boulder
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmap_test.go
More file actions
356 lines (326 loc) · 12.7 KB
/
map_test.go
File metadata and controls
356 lines (326 loc) · 12.7 KB
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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
package db
import (
"context"
"database/sql"
"errors"
"fmt"
"testing"
gorp "github.com/go-gorp/gorp/v3"
"github.com/go-sql-driver/mysql"
"github.com/letsencrypt/boulder/core"
"github.com/letsencrypt/boulder/test"
"github.com/letsencrypt/boulder/test/vars"
)
func TestErrDatabaseOpError(t *testing.T) {
testErr := errors.New("computers are cancelled")
testCases := []struct {
name string
err error
expected string
}{
{
name: "error with table",
err: ErrDatabaseOp{
Op: "test",
Table: "testTable",
Err: testErr,
},
expected: fmt.Sprintf("failed to test testTable: %s", testErr),
},
{
name: "error with no table",
err: ErrDatabaseOp{
Op: "test",
Err: testErr,
},
expected: fmt.Sprintf("failed to test: %s", testErr),
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
test.AssertEquals(t, tc.err.Error(), tc.expected)
})
}
}
func TestErrDatabaseOpNoRows(t *testing.T) {
testCases := []struct {
name string
err ErrDatabaseOp
expectedNoRows bool
}{
{
name: "underlying err is sql.ErrNoRows",
err: ErrDatabaseOp{
Op: "test",
Table: "testTable",
Err: sql.ErrNoRows,
},
expectedNoRows: true,
},
{
name: "underlying err is not sql.ErrNoRows",
err: ErrDatabaseOp{
Op: "test",
Table: "testTable",
Err: errors.New("lots of rows. too many rows."),
},
expectedNoRows: false,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
test.AssertEquals(t, tc.err.noRows(), tc.expectedNoRows)
})
}
}
func TestErrDatabaseOpDuplicate(t *testing.T) {
testCases := []struct {
name string
err ErrDatabaseOp
expectDuplicate bool
}{
{
name: "underlying err has duplicate prefix",
err: ErrDatabaseOp{
Op: "test",
Table: "testTable",
Err: errors.New("Error 1062: Duplicate entry detected!!!!!!!"),
},
expectDuplicate: true,
},
{
name: "underlying err doesn't have duplicate prefix",
err: ErrDatabaseOp{
Op: "test",
Table: "testTable",
Err: errors.New("DB forgot to save your data."),
},
expectDuplicate: false,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
test.AssertEquals(t, tc.err.duplicate(), tc.expectDuplicate)
})
}
}
func TestTableFromQuery(t *testing.T) {
// A sample of example queries logged by the SA during Boulder
// unit/integration tests.
testCases := []struct {
query string
expectedTable string
}{
{
query: "SELECT id, jwk, jwk_sha256, contact, agreement, initialIP, createdAt, LockCol, status FROM registrations WHERE jwk_sha256 = ?",
expectedTable: "registrations",
},
{
query: "\n\t\t\t\t\tSELECT orderID, registrationID\n\t\t\t\t\tFROM orderFqdnSets\n\t\t\t\t\tWHERE setHash = ?\n\t\t\t\t\tAND expires > ?\n\t\t\t\t\tORDER BY expires ASC\n\t\t\t\t\tLIMIT 1",
expectedTable: "orderFqdnSets",
},
{
query: "SELECT id, identifierType, identifierValue, registrationID, status, expires, challenges, attempted, token, validationError, validationRecord FROM authz2 WHERE\n\t\t\tregistrationID = :regID AND\n\t\t\tstatus = :status AND\n\t\t\texpires > :validUntil AND\n\t\t\tidentifierType = :dnsType AND\n\t\t\tidentifierValue = :ident\n\t\t\tORDER BY expires ASC\n\t\t\tLIMIT 1 ",
expectedTable: "authz2",
},
{
query: "insert into `registrations` (`id`,`jwk`,`jw k_sha256`,`contact`,`agreement`,`initialIp`,`createdAt`,`LockCol`,`status`) values (null,?,?,?,?,?,?,?,?);",
expectedTable: "`registrations`",
},
{
query: "update `registrations` set `jwk`=?, `jwk_sh a256`=?, `contact`=?, `agreement`=?, `initialIp`=?, `createdAt`=?, `LockCol` =?, `status`=? where `id`=? and `LockCol`=?;",
expectedTable: "`registrations`",
},
{
query: "SELECT COUNT(1) FROM registrations WHERE initialIP = ? AND ? < createdAt AND createdAt <= ?",
expectedTable: "registrations",
},
{
query: "SELECT count(1) FROM orders WHERE registrationID = ? AND created >= ? AND created < ?",
expectedTable: "orders",
},
{
query: " SELECT id, identifierType, identifierValue, registrationID, status, expires, challenges, attempted, token, validationError, validationRecord FROM authz2 WHERE registrationID = ? AND status IN (?,?) AND expires > ? AND identifierType = ? AND identifierValue IN (?)",
expectedTable: "authz2",
},
{
query: "insert into `authz2` (`id`,`identifierType`,`identifierValue`,`registrationID`,`status`,`expires`,`challenges`,`attempted`,`token`,`validationError`,`validationRecord`) values (null,?,?,?,?,?,?,?,?,?,?);",
expectedTable: "`authz2`",
},
{
query: "insert into `orders` (`ID`,`RegistrationID`,`Expires`,`Created`,`Error`,`CertificateSerial`,`BeganProcessing`) values (null,?,?,?,?,?,?)",
expectedTable: "`orders`",
},
{
query: "insert into `orderToAuthz2` (`OrderID`,`AuthzID`) values (?,?);",
expectedTable: "`orderToAuthz2`",
},
{
query: "insert into `requestedNames` (`ID`,`OrderID`,`ReversedName`) values (?,?,?);",
expectedTable: "`requestedNames`",
},
{
query: "UPDATE authz2 SET status = :status, attempted = :attempted, validationRecord = :validationRecord, validationError = :validationError, expires = :expires WHERE id = :id AND status = :pending",
expectedTable: "authz2",
},
{
query: "insert into `precertificates` (`ID`,`Serial`,`RegistrationID`,`DER`,`Issued`,`Expires`) values (null,?,?,?,?,?);",
expectedTable: "`precertificates`",
},
{
query: "INSERT INTO certificateStatus (serial, status, ocspLastUpdated, revokedDate, revokedReason, lastExpirationNagSent, ocspResponse, notAfter, isExpired, issuerID) VALUES (?,?,?,?,?,?,?,?,?,?)",
expectedTable: "certificateStatus",
},
{
query: "INSERT INTO issuedNames (reversedName, serial, notBefore, renewal) VALUES (?, ?, ?, ?);",
expectedTable: "issuedNames",
},
{
query: "insert into `certificates` (`registrationID`,`serial`,`digest`,`der`,`issued`,`expires`) values (?,?,?,?,?,?);",
expectedTable: "`certificates`",
},
{
query: "INSERT INTO certificatesPerName (eTLDPlusOne, time, count) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE count=count+1;",
expectedTable: "certificatesPerName",
},
{
query: "insert into `fqdnSets` (`ID`,`SetHash`,`Serial`,`Issued`,`Expires`) values (null,?,?,?,?);",
expectedTable: "`fqdnSets`",
},
{
query: "UPDATE orders SET certificateSerial = ? WHERE id = ? AND beganProcessing = true",
expectedTable: "orders",
},
{
query: "DELETE FROM orderFqdnSets WHERE orderID = ?",
expectedTable: "orderFqdnSets",
},
{
query: "insert into `serials` (`ID`,`Serial`,`RegistrationID`,`Created`,`Expires`) values (null,?,?,?,?);",
expectedTable: "`serials`",
},
{
query: "UPDATE orders SET beganProcessing = ? WHERE id = ? AND beganProcessing = ?",
expectedTable: "orders",
},
}
for i, tc := range testCases {
t.Run(fmt.Sprintf("testCases.%d", i), func(t *testing.T) {
table := tableFromQuery(tc.query)
test.AssertEquals(t, table, tc.expectedTable)
})
}
}
func testDbMap(t *testing.T) *WrappedMap {
// NOTE(@cpu): We avoid using sa.NewDBMapFromConfig here because it would
// create a cyclic dependency. The `sa` package depends on `db` for
// `WithTransaction`. The `db` package can't depend on the `sa` for creating
// a DBMap. Since we only need a map for simple unit tests we can make our
// own dbMap by hand (how artisanal).
var config *mysql.Config
config, err := mysql.ParseDSN(vars.DBConnSA)
test.AssertNotError(t, err, "parsing DBConnSA DSN")
dbConn, err := sql.Open("mysql", config.FormatDSN())
test.AssertNotError(t, err, "opening DB connection")
dialect := gorp.MySQLDialect{Engine: "InnoDB", Encoding: "UTF8"}
// NOTE(@cpu): We avoid giving a sa.BoulderTypeConverter to the DbMap field to
// avoid the cyclic dep. We don't need to convert any types in the db tests.
dbMap := &gorp.DbMap{Db: dbConn, Dialect: dialect, TypeConverter: nil}
return &WrappedMap{DbMap: dbMap}
}
func TestWrappedMap(t *testing.T) {
mustDbErr := func(err error) ErrDatabaseOp {
var dbOpErr ErrDatabaseOp
test.AssertErrorWraps(t, err, &dbOpErr)
return dbOpErr
}
testWrapper := func(dbMap Executor) {
reg := &core.Registration{}
// Test wrapped Get
_, err := dbMap.Get(reg)
test.AssertError(t, err, "expected err Getting Registration w/o type converter")
dbOpErr := mustDbErr(err)
test.AssertEquals(t, dbOpErr.Op, "get")
test.AssertEquals(t, dbOpErr.Table, "*core.Registration")
test.AssertError(t, dbOpErr.Err, "expected non-nil underlying err")
// Test wrapped Insert
err = dbMap.Insert(reg)
test.AssertError(t, err, "expected err Inserting Registration w/o type converter")
dbOpErr = mustDbErr(err)
test.AssertEquals(t, dbOpErr.Op, "insert")
test.AssertEquals(t, dbOpErr.Table, "*core.Registration")
test.AssertError(t, dbOpErr.Err, "expected non-nil underlying err")
// Test wrapped Update
_, err = dbMap.Update(reg)
test.AssertError(t, err, "expected err Updating Registration w/o type converter")
dbOpErr = mustDbErr(err)
test.AssertEquals(t, dbOpErr.Op, "update")
test.AssertEquals(t, dbOpErr.Table, "*core.Registration")
test.AssertError(t, dbOpErr.Err, "expected non-nil underlying err")
// Test wrapped Delete
_, err = dbMap.Delete(reg)
test.AssertError(t, err, "expected err Deleting Registration w/o type converter")
dbOpErr = mustDbErr(err)
test.AssertEquals(t, dbOpErr.Op, "delete")
test.AssertEquals(t, dbOpErr.Table, "*core.Registration")
test.AssertError(t, dbOpErr.Err, "expected non-nil underlying err")
// Test wrapped Select with a bogus query
_, err = dbMap.Select(reg, "blah")
test.AssertError(t, err, "expected err Selecting Registration w/o type converter")
dbOpErr = mustDbErr(err)
test.AssertEquals(t, dbOpErr.Op, "select")
test.AssertEquals(t, dbOpErr.Table, "*core.Registration (unknown table)")
test.AssertError(t, dbOpErr.Err, "expected non-nil underlying err")
// Test wrapped Select with a valid query
_, err = dbMap.Select(reg, "SELECT id, contact FROM registrationzzz WHERE id > 1;")
test.AssertError(t, err, "expected err Selecting Registration w/o type converter")
dbOpErr = mustDbErr(err)
test.AssertEquals(t, dbOpErr.Op, "select")
test.AssertEquals(t, dbOpErr.Table, "registrationzzz")
test.AssertError(t, dbOpErr.Err, "expected non-nil underlying err")
// Test wrapped SelectOne with a bogus query
err = dbMap.SelectOne(reg, "blah")
test.AssertError(t, err, "expected err SelectOne-ing Registration w/o type converter")
dbOpErr = mustDbErr(err)
test.AssertEquals(t, dbOpErr.Op, "select one")
test.AssertEquals(t, dbOpErr.Table, "*core.Registration (unknown table)")
test.AssertError(t, dbOpErr.Err, "expected non-nil underlying err")
// Test wrapped SelectOne with a valid query
err = dbMap.SelectOne(reg, "SELECT contact FROM doesNotExist WHERE id=1;")
test.AssertError(t, err, "expected err SelectOne-ing Registration w/o type converter")
dbOpErr = mustDbErr(err)
test.AssertEquals(t, dbOpErr.Op, "select one")
test.AssertEquals(t, dbOpErr.Table, "doesNotExist")
test.AssertError(t, dbOpErr.Err, "expected non-nil underlying err")
// Test wrapped Exec
_, err = dbMap.Exec("INSERT INTO whatever (id) VALUES (?) WHERE id = ?", 10)
test.AssertError(t, err, "expected err Exec-ing bad query")
dbOpErr = mustDbErr(err)
test.AssertEquals(t, dbOpErr.Op, "exec")
test.AssertEquals(t, dbOpErr.Table, "whatever")
test.AssertError(t, dbOpErr.Err, "expected non-nil underlying err")
}
// Create a test wrapped map. It won't have a type converted registered.
dbMap := testDbMap(t)
// A top level WrappedMap should operate as expected with respect to wrapping
// database errors.
testWrapper(dbMap)
// Using WithContext on the WrappedMap should return a map that continues to
// operate in the expected fashion.
dbMapWithCtx := dbMap.WithContext(context.Background())
testWrapper(dbMapWithCtx)
// Using Begin to start a transaction with the dbMap should return a
// transaction that continues to operate in the expected fashion.
tx, err := dbMap.Begin()
defer func() { _ = tx.Rollback() }()
test.AssertNotError(t, err, "unexpected error beginning transaction")
testWrapper(tx)
// Using Begin to start a transaction with the dbMap and then using
// WithContext should return a transaction that continues to operate in the
// expected fashion.
tx, err = dbMap.Begin()
defer func() { _ = tx.Rollback() }()
test.AssertNotError(t, err, "unexpected error beginning transaction")
txWithContext := tx.WithContext(context.Background())
testWrapper(txWithContext)
}