github.com/letsencrypt/boulder@v0.20251208.0/sa/database_test.go (about) 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(foo-database:1337)/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(foo-database:1337)/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(foo-database:1337)/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(foo-database:1337)/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 // TODO(#5248): Add a full db.mockWrappedMap to sa/database tests 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 79 err := os.WriteFile(dsnFile, []byte(vars.DBConnSA), os.ModeAppend) 80 test.AssertNotError(t, err, "writing dbconnect file") 81 82 config := cmd.DBConfig{ 83 DBConnectFile: dsnFile, 84 MaxOpenConns: 100, 85 MaxIdleConns: 100, 86 ConnMaxLifetime: config.Duration{Duration: 100 * time.Second}, 87 ConnMaxIdleTime: config.Duration{Duration: 100 * time.Second}, 88 } 89 _, err = InitWrappedDb(config, nil, nil) 90 if err != nil { 91 t.Errorf("connecting to DB: %s", err) 92 } 93 if maxOpenConns != 100 { 94 t.Errorf("maxOpenConns was not set: expected 100, got %d", maxOpenConns) 95 } 96 if maxIdleConns != 100 { 97 t.Errorf("maxIdleConns was not set: expected 100, got %d", maxIdleConns) 98 } 99 if connMaxLifetime != 100*time.Second { 100 t.Errorf("connMaxLifetime was not set: expected 100s, got %s", connMaxLifetime) 101 } 102 if connMaxIdleTime != 100*time.Second { 103 t.Errorf("connMaxIdleTime was not set: expected 100s, got %s", connMaxIdleTime) 104 } 105 } 106 107 // TODO: Change this to test `newDbMapFromMySQLConfig` instead? 108 func TestNewDbMap(t *testing.T) { 109 const mysqlConnectURL = "policy:password@tcp(boulder-proxysql:6033)/boulder_policy_integration?readTimeout=800ms&writeTimeout=800ms" 110 const expected = "policy:password@tcp(boulder-proxysql:6033)/boulder_policy_integration?clientFoundRows=true&parseTime=true&readTimeout=800ms&writeTimeout=800ms&sql_mode=%27STRICT_ALL_TABLES%27" 111 oldSQLOpen := sqlOpen 112 defer func() { 113 sqlOpen = oldSQLOpen 114 }() 115 sqlOpen = func(dbType, connectString string) (*sql.DB, error) { 116 if connectString != expected { 117 t.Errorf("incorrect connection string mangling, want %#v, got %#v", expected, connectString) 118 } 119 return nil, errExpected 120 } 121 122 dbMap, err := DBMapForTest(mysqlConnectURL) 123 if err != errExpected { 124 t.Errorf("got incorrect error. Got %v, expected %v", err, errExpected) 125 } 126 if dbMap != nil { 127 t.Errorf("expected nil, got %v", dbMap) 128 } 129 130 } 131 132 func TestStrictness(t *testing.T) { 133 dbMap, err := DBMapForTest(vars.DBConnSA) 134 if err != nil { 135 t.Fatal(err) 136 } 137 _, err = dbMap.ExecContext(ctx, `insert into orderToAuthz2 set 138 orderID=999999999999999999999999999, 139 authzID=999999999999999999999999999;`) 140 if err == nil { 141 t.Fatal("Expected error when providing out of range value, got none.") 142 } 143 if !strings.Contains(err.Error(), "Out of range value for column") { 144 t.Fatalf("Got wrong type of error: %s", err) 145 } 146 } 147 148 // TestAutoIncrementSchema tests that all of the tables in the boulder_* 149 // databases that have auto_increment columns use BIGINT for the data type. Our 150 // data is too big for INT. 151 func TestAutoIncrementSchema(t *testing.T) { 152 dbMap, err := DBMapForTest(vars.DBInfoSchemaRoot) 153 test.AssertNotError(t, err, "unexpected err making NewDbMap") 154 155 var count int64 156 err = dbMap.SelectOne( 157 context.Background(), 158 &count, 159 `SELECT COUNT(*) FROM columns WHERE 160 table_schema LIKE 'boulder%' AND 161 extra LIKE '%auto_increment%' AND 162 data_type != "bigint"`) 163 test.AssertNotError(t, err, "unexpected err querying columns") 164 test.AssertEquals(t, count, int64(0)) 165 } 166 167 func TestAdjustMySQLConfig(t *testing.T) { 168 conf := &mysql.Config{} 169 err := adjustMySQLConfig(conf) 170 test.AssertNotError(t, err, "unexpected err setting server variables") 171 test.Assert(t, conf.ParseTime, "ParseTime should be enabled") 172 test.Assert(t, conf.ClientFoundRows, "ClientFoundRows should be enabled") 173 test.AssertDeepEquals(t, conf.Params, map[string]string{"sql_mode": "'STRICT_ALL_TABLES'"}) 174 }