| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250 |
- package setup
- import (
- "os"
- "path/filepath"
- "strings"
- "testing"
- "time"
- )
- func TestDecideAdminBootstrap(t *testing.T) {
- t.Parallel()
- tests := []struct {
- name string
- totalUsers int64
- adminUsers int64
- should bool
- reason string
- }{
- {
- name: "empty database should create admin",
- totalUsers: 0,
- adminUsers: 0,
- should: true,
- reason: adminBootstrapReasonEmptyDatabase,
- },
- {
- name: "admin exists should skip",
- totalUsers: 10,
- adminUsers: 1,
- should: false,
- reason: adminBootstrapReasonAdminExists,
- },
- {
- name: "users exist without admin should skip",
- totalUsers: 5,
- adminUsers: 0,
- should: false,
- reason: adminBootstrapReasonUsersExistWithoutAdmin,
- },
- }
- for _, tc := range tests {
- tc := tc
- t.Run(tc.name, func(t *testing.T) {
- t.Parallel()
- got := decideAdminBootstrap(tc.totalUsers, tc.adminUsers)
- if got.shouldCreate != tc.should {
- t.Fatalf("shouldCreate=%v, want %v", got.shouldCreate, tc.should)
- }
- if got.reason != tc.reason {
- t.Fatalf("reason=%q, want %q", got.reason, tc.reason)
- }
- })
- }
- }
- func TestSetupDefaultAdminConcurrency(t *testing.T) {
- t.Run("simple mode admin uses higher concurrency", func(t *testing.T) {
- t.Setenv("RUN_MODE", "simple")
- if got := setupDefaultAdminConcurrency(); got != simpleModeAdminConcurrency {
- t.Fatalf("setupDefaultAdminConcurrency()=%d, want %d", got, simpleModeAdminConcurrency)
- }
- })
- t.Run("standard mode keeps existing default", func(t *testing.T) {
- t.Setenv("RUN_MODE", "standard")
- if got := setupDefaultAdminConcurrency(); got != defaultUserConcurrency {
- t.Fatalf("setupDefaultAdminConcurrency()=%d, want %d", got, defaultUserConcurrency)
- }
- })
- }
- func TestNeedsSetupSkipsWhenSkipSetupIsEnabled(t *testing.T) {
- tests := []struct {
- name string
- value string
- }{
- {name: "true", value: "true"},
- {name: "one", value: "1"},
- {name: "yes", value: "yes"},
- {name: "trimmed mixed case true", value: " TrUe "},
- {name: "trimmed mixed case yes", value: " YeS "},
- }
- for _, tc := range tests {
- t.Run(tc.name, func(t *testing.T) {
- t.Setenv("DATA_DIR", t.TempDir())
- t.Setenv("SKIP_SETUP", tc.value)
- if NeedsSetup() {
- t.Fatalf("NeedsSetup() = true, want false when SKIP_SETUP is enabled")
- }
- })
- }
- }
- func TestNeedsSetupFallsBackToFileDetectionWhenSkipSetupIsDisabled(t *testing.T) {
- tests := []struct {
- name string
- skipSetupSet bool
- skipSetup string
- markerFile string
- want bool
- }{
- {
- name: "unset without installation files",
- want: true,
- },
- {
- name: "false without installation files",
- skipSetupSet: true,
- skipSetup: " false ",
- want: true,
- },
- {
- name: "invalid value without installation files",
- skipSetupSet: true,
- skipSetup: "enabled",
- want: true,
- },
- {
- name: "config file exists",
- skipSetupSet: true,
- skipSetup: "false",
- markerFile: ConfigFileName,
- want: false,
- },
- {
- name: "install lock file exists",
- skipSetupSet: true,
- skipSetup: "invalid",
- markerFile: InstallLockFile,
- want: false,
- },
- }
- for _, tc := range tests {
- t.Run(tc.name, func(t *testing.T) {
- dataDir := t.TempDir()
- t.Setenv("DATA_DIR", dataDir)
- if tc.skipSetupSet {
- t.Setenv("SKIP_SETUP", tc.skipSetup)
- } else {
- originalValue, wasSet := os.LookupEnv("SKIP_SETUP")
- if err := os.Unsetenv("SKIP_SETUP"); err != nil {
- t.Fatalf("Unsetenv(SKIP_SETUP) error = %v", err)
- }
- t.Cleanup(func() {
- if wasSet {
- _ = os.Setenv("SKIP_SETUP", originalValue)
- return
- }
- _ = os.Unsetenv("SKIP_SETUP")
- })
- }
- if tc.markerFile != "" {
- if err := os.WriteFile(filepath.Join(dataDir, tc.markerFile), nil, 0o600); err != nil {
- t.Fatalf("WriteFile(%s) error = %v", tc.markerFile, err)
- }
- }
- if got := NeedsSetup(); got != tc.want {
- t.Fatalf("NeedsSetup() = %v, want %v", got, tc.want)
- }
- })
- }
- }
- func TestSetupMigrationTimeout(t *testing.T) {
- t.Run("uses default timeout when unset", func(t *testing.T) {
- cfg := &SetupConfig{}
- if got := cfg.migrationTimeout(); got != 60*time.Second {
- t.Fatalf("migrationTimeout()=%s, want 60s", got)
- }
- })
- t.Run("uses configured timeout", func(t *testing.T) {
- cfg := &SetupConfig{MigrationTimeoutSeconds: 300}
- if got := cfg.migrationTimeout(); got != 300*time.Second {
- t.Fatalf("migrationTimeout()=%s, want 300s", got)
- }
- })
- }
- func TestWriteConfigFileKeepsDefaultUserConcurrency(t *testing.T) {
- t.Setenv("RUN_MODE", "simple")
- t.Setenv("DATA_DIR", t.TempDir())
- if err := writeConfigFile(&SetupConfig{}); err != nil {
- t.Fatalf("writeConfigFile() error = %v", err)
- }
- data, err := os.ReadFile(GetConfigFilePath())
- if err != nil {
- t.Fatalf("ReadFile() error = %v", err)
- }
- if !strings.Contains(string(data), "user_concurrency: 5") {
- t.Fatalf("config missing default user concurrency, got:\n%s", string(data))
- }
- }
- func TestWriteConfigFileIncludesRedisUsername(t *testing.T) {
- t.Setenv("DATA_DIR", t.TempDir())
- if err := writeConfigFile(&SetupConfig{
- Redis: RedisConfig{
- Host: "redis",
- Port: 6379,
- Username: "app-user",
- },
- }); err != nil {
- t.Fatalf("writeConfigFile() error = %v", err)
- }
- data, err := os.ReadFile(GetConfigFilePath())
- if err != nil {
- t.Fatalf("ReadFile() error = %v", err)
- }
- if !strings.Contains(string(data), "username: app-user") {
- t.Fatalf("config missing Redis username, got:\n%s", string(data))
- }
- }
- func TestBuildDatabaseConnectionDSNsUsesPostgresForBootstrap(t *testing.T) {
- cfg := &DatabaseConfig{
- Host: "db",
- Port: 5432,
- User: "sub2api",
- Password: "secret",
- DBName: "sub2api",
- SSLMode: "disable",
- }
- bootstrapDSN, targetDSN := buildDatabaseConnectionDSNs(cfg)
- if !strings.Contains(bootstrapDSN, "dbname=postgres") {
- t.Fatalf("bootstrap DSN = %q, want default postgres database", bootstrapDSN)
- }
- if strings.Contains(bootstrapDSN, "dbname=sub2api") {
- t.Fatalf("bootstrap DSN = %q, should not connect to target database before checking/creating it", bootstrapDSN)
- }
- if !strings.Contains(targetDSN, "dbname=sub2api") {
- t.Fatalf("target DSN = %q, want configured database", targetDSN)
- }
- }
|