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  }