1 package sa
2
3 import (
4 "context"
5 "database/sql"
6 "errors"
7 "os"
8 "path"
9 "strings"
10 "testing"
11 "time"
12
13 "github.com/go-sql-driver/mysql"
14 "github.com/letsencrypt/boulder/cmd"
15 "github.com/letsencrypt/boulder/config"
16 "github.com/letsencrypt/boulder/test"
17 "github.com/letsencrypt/boulder/test/vars"
18 )
19
20 func TestInvalidDSN(t *testing.T) {
21 _, err := DBMapForTest("invalid")
22 test.AssertError(t, err, "DB connect string missing the slash separating the database name")
23
24 DSN := "policy:password@tcp(boulder-proxysql:6033)/boulder_policy_integration?readTimeout=800ms&writeTimeout=800ms&stringVarThatDoesntExist=%27whoopsidaisies"
25 _, err = DBMapForTest(DSN)
26 test.AssertError(t, err, "Variable does not exist in curated system var list, but didn't return an error and should have")
27
28 DSN = "policy:password@tcp(boulder-proxysql:6033)/boulder_policy_integration?readTimeout=800ms&writeTimeout=800ms&concurrent_insert=2"
29 _, err = DBMapForTest(DSN)
30 test.AssertError(t, err, "Variable is unable to be set in the SESSION scope, but was declared")
31
32 DSN = "policy:password@tcp(boulder-proxysql:6033)/boulder_policy_integration?readTimeout=800ms&writeTimeout=800ms&optimizer_switch=incorrect-quoted-string"
33 _, err = DBMapForTest(DSN)
34 test.AssertError(t, err, "Variable declared with incorrect quoting")
35
36 DSN = "policy:password@tcp(boulder-proxysql:6033)/boulder_policy_integration?readTimeout=800ms&writeTimeout=800ms&concurrent_insert=%272%27"
37 _, err = DBMapForTest(DSN)
38 test.AssertError(t, err, "Integer enum declared, but should not have been quoted")
39 }
40
41 var errExpected = errors.New("expected")
42
43 func TestDbSettings(t *testing.T) {
44
45 oldSetMaxOpenConns := setMaxOpenConns
46 oldSetMaxIdleConns := setMaxIdleConns
47 oldSetConnMaxLifetime := setConnMaxLifetime
48 oldSetConnMaxIdleTime := setConnMaxIdleTime
49 defer func() {
50 setMaxOpenConns = oldSetMaxOpenConns
51 setMaxIdleConns = oldSetMaxIdleConns
52 setConnMaxLifetime = oldSetConnMaxLifetime
53 setConnMaxIdleTime = oldSetConnMaxIdleTime
54 }()
55
56 maxOpenConns := -1
57 maxIdleConns := -1
58 connMaxLifetime := time.Second * 1
59 connMaxIdleTime := time.Second * 1
60
61 setMaxOpenConns = func(db *sql.DB, m int) {
62 maxOpenConns = m
63 oldSetMaxOpenConns(db, maxOpenConns)
64 }
65 setMaxIdleConns = func(db *sql.DB, m int) {
66 maxIdleConns = m
67 oldSetMaxIdleConns(db, maxIdleConns)
68 }
69 setConnMaxLifetime = func(db *sql.DB, c time.Duration) {
70 connMaxLifetime = c
71 oldSetConnMaxLifetime(db, connMaxLifetime)
72 }
73 setConnMaxIdleTime = func(db *sql.DB, c time.Duration) {
74 connMaxIdleTime = c
75 oldSetConnMaxIdleTime(db, connMaxIdleTime)
76 }
77 dsnFile := path.Join(t.TempDir(), "dbconnect")
78 err := os.WriteFile(dsnFile,
79 []byte("sa@tcp(boulder-proxysql:6033)/boulder_sa_integration"),
80 os.ModeAppend)
81 test.AssertNotError(t, err, "writing dbconnect file")
82
83 config := cmd.DBConfig{
84 DBConnectFile: dsnFile,
85 MaxOpenConns: 100,
86 MaxIdleConns: 100,
87 ConnMaxLifetime: config.Duration{Duration: 100 * time.Second},
88 ConnMaxIdleTime: config.Duration{Duration: 100 * time.Second},
89 }
90 _, err = InitWrappedDb(config, nil, nil)
91 if err != nil {
92 t.Errorf("connecting to DB: %s", err)
93 }
94 if maxOpenConns != 100 {
95 t.Errorf("maxOpenConns was not set: expected 100, got %d", maxOpenConns)
96 }
97 if maxIdleConns != 100 {
98 t.Errorf("maxIdleConns was not set: expected 100, got %d", maxIdleConns)
99 }
100 if connMaxLifetime != 100*time.Second {
101 t.Errorf("connMaxLifetime was not set: expected 100s, got %s", connMaxLifetime)
102 }
103 if connMaxIdleTime != 100*time.Second {
104 t.Errorf("connMaxIdleTime was not set: expected 100s, got %s", connMaxIdleTime)
105 }
106 }
107
108
109 func TestNewDbMap(t *testing.T) {
110 const mysqlConnectURL = "policy:password@tcp(boulder-proxysql:6033)/boulder_policy_integration?readTimeout=800ms&writeTimeout=800ms"
111 const expected = "policy:password@tcp(boulder-proxysql:6033)/boulder_policy_integration?clientFoundRows=true&parseTime=true&readTimeout=800ms&writeTimeout=800ms&long_query_time=0.6400000000000001&max_statement_time=0.76&sql_mode=%27STRICT_ALL_TABLES%27"
112 oldSQLOpen := sqlOpen
113 defer func() {
114 sqlOpen = oldSQLOpen
115 }()
116 sqlOpen = func(dbType, connectString string) (*sql.DB, error) {
117 if connectString != expected {
118 t.Errorf("incorrect connection string mangling, want %#v, got %#v", expected, connectString)
119 }
120 return nil, errExpected
121 }
122
123 dbMap, err := DBMapForTest(mysqlConnectURL)
124 if err != errExpected {
125 t.Errorf("got incorrect error. Got %v, expected %v", err, errExpected)
126 }
127 if dbMap != nil {
128 t.Errorf("expected nil, got %v", dbMap)
129 }
130
131 }
132
133 func TestStrictness(t *testing.T) {
134 dbMap, err := DBMapForTest(vars.DBConnSA)
135 if err != nil {
136 t.Fatal(err)
137 }
138 _, err = dbMap.ExecContext(ctx, `insert into orderToAuthz2 set
139 orderID=999999999999999999999999999,
140 authzID=999999999999999999999999999;`)
141 if err == nil {
142 t.Fatal("Expected error when providing out of range value, got none.")
143 }
144 if !strings.Contains(err.Error(), "Out of range value for column") {
145 t.Fatalf("Got wrong type of error: %s", err)
146 }
147 }
148
149 func TestTimeouts(t *testing.T) {
150 dbMap, err := DBMapForTest(vars.DBConnSA + "?max_statement_time=1")
151 if err != nil {
152 t.Fatal("Error setting up DB:", err)
153 }
154
155
156
157 _, err = dbMap.ExecContext(ctx, `SELECT 1 FROM (SELECT SLEEP(5)) as subselect;`)
158 if err == nil {
159 t.Fatal("Expected error when running slow query, got none.")
160 }
161
162
163
164
165 if !strings.Contains(err.Error(), "Error 1969") {
166 t.Fatalf("Got wrong type of error: %s", err)
167 }
168 }
169
170
171
172
173 func TestAutoIncrementSchema(t *testing.T) {
174 dbMap, err := DBMapForTest(vars.DBInfoSchemaRoot)
175 test.AssertNotError(t, err, "unexpected err making NewDbMap")
176
177 var count int64
178 err = dbMap.SelectOne(
179 context.Background(),
180 &count,
181 `SELECT COUNT(*) FROM columns WHERE
182 table_schema LIKE 'boulder%' AND
183 extra LIKE '%auto_increment%' AND
184 data_type != "bigint"`)
185 test.AssertNotError(t, err, "unexpected err querying columns")
186 test.AssertEquals(t, count, int64(0))
187 }
188
189 func TestAdjustMySQLConfig(t *testing.T) {
190 conf := &mysql.Config{}
191 err := adjustMySQLConfig(conf)
192 test.AssertNotError(t, err, "unexpected err setting server variables")
193 test.AssertDeepEquals(t, conf.Params, map[string]string{
194 "sql_mode": "'STRICT_ALL_TABLES'",
195 })
196
197 conf = &mysql.Config{ReadTimeout: 100 * time.Second}
198 err = adjustMySQLConfig(conf)
199 test.AssertNotError(t, err, "unexpected err setting server variables")
200 test.AssertDeepEquals(t, conf.Params, map[string]string{
201 "sql_mode": "'STRICT_ALL_TABLES'",
202 "max_statement_time": "95",
203 "long_query_time": "80",
204 })
205
206 conf = &mysql.Config{
207 ReadTimeout: 100 * time.Second,
208 Params: map[string]string{
209 "max_statement_time": "0",
210 },
211 }
212 err = adjustMySQLConfig(conf)
213 test.AssertNotError(t, err, "unexpected err setting server variables")
214 test.AssertDeepEquals(t, conf.Params, map[string]string{
215 "sql_mode": "'STRICT_ALL_TABLES'",
216 "long_query_time": "80",
217 })
218
219 conf = &mysql.Config{
220 Params: map[string]string{
221 "max_statement_time": "0",
222 },
223 }
224 err = adjustMySQLConfig(conf)
225 test.AssertNotError(t, err, "unexpected err setting server variables")
226 test.AssertDeepEquals(t, conf.Params, map[string]string{
227 "sql_mode": "'STRICT_ALL_TABLES'",
228 })
229 }
230
View as plain text