//go:build mysql package mysql import ( "errors" "os" "strings" "testing" "time" mysqldriver "github.com/go-sql-driver/mysql" ) func TestLoginAuditQueriesAreTenantScoped(t *testing.T) { source, err := os.ReadFile("tenant_security_audit.go") if err != nil { t.Fatal(err) } body := string(source) for _, required := range []string{ "(tenant_id,user_id,username", "(tenant_id,user_id,account_type", "WHERE tenant_id=? AND session_id=?", } { if !strings.Contains(body, required) { t.Fatalf("login audit query is missing tenant scope %q", required) } } } func TestRetryMySQLDeadlockEventuallySucceeds(t *testing.T) { calls := 0 var delays []time.Duration err := retryMySQLDeadlock(func() error { calls++ if calls < 3 { return &mysqldriver.MySQLError{Number: 1213, SQLState: [5]byte{'4', '0', '0', '0', '1'}} } return nil }, func(delay time.Duration) { delays = append(delays, delay) }) if err != nil { t.Fatal(err) } if calls != 3 { t.Fatalf("operation calls=%d want=3", calls) } want := []time.Duration{25 * time.Millisecond, 50 * time.Millisecond} if len(delays) != len(want) || delays[0] != want[0] || delays[1] != want[1] { t.Fatalf("retry delays=%v want=%v", delays, want) } } func TestRetryMySQLDeadlockStopsOnNonRetryableError(t *testing.T) { want := errors.New("invalid update") calls := 0 err := retryMySQLDeadlock(func() error { calls++ return want }, func(time.Duration) { t.Fatal("non-retryable error must not sleep") }) if !errors.Is(err, want) || calls != 1 { t.Fatalf("err=%v calls=%d want=%v/1", err, calls, want) } } func TestRetryMySQLDeadlockStopsAfterFourAttempts(t *testing.T) { calls := 0 err := retryMySQLDeadlock(func() error { calls++ return &mysqldriver.MySQLError{Number: 1205} }, func(time.Duration) {}) if err == nil || calls != 4 { t.Fatalf("err=%v calls=%d want non-nil/4", err, calls) } }