package data import ( "context" "os" "path/filepath" "strings" "testing" "kra/internal/config" "gopkg.in/yaml.v3" ) func TestDatabaseConnectionConfigured(t *testing.T) { tests := []struct { name string config *config.Database want bool }{ {name: "missing config", config: nil, want: false}, {name: "empty database", config: &config.Database{Driver: "mysql"}, want: false}, {name: "named database", config: &config.Database{Driver: "mysql", Name: "kra"}, want: true}, {name: "standalone DSN", config: &config.Database{Driver: "sqlite", Source: "file:kra.db"}, want: true}, {name: "generated DSN still requires a name", config: &config.Database{Driver: "mysql", Source: "root@tcp(localhost)/", Host: "localhost"}, want: false}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { if got := databaseConnectionConfigured(test.config); got != test.want { t.Fatalf("databaseConnectionConfigured() = %v, want %v", got, test.want) } }) } } func TestInitializationStateTracksRealDatabaseConnection(t *testing.T) { data := &Data{} initialized, err := data.IsInitialized(context.Background()) if err != nil { t.Fatalf("IsInitialized() error = %v", err) } if initialized { t.Fatal("bootstrap state must require initialization") } data.databaseReady.Store(true) initialized, err = data.IsInitialized(context.Background()) if err != nil { t.Fatalf("IsInitialized() error = %v", err) } if !initialized { t.Fatal("an active configured database must not require initialization") } } func TestPersistDatabaseConfigPreservesTemplateAndRotatesJWTKey(t *testing.T) { root := t.TempDir() configPath := filepath.Join(root, "config.yaml") original := `server: http: addr: 0.0.0.0:8000 data: database: driver: mysql name: old redis: addr: 127.0.0.1:6379 admin: jwt: signing_key: old-key issuer: kra zap: director: logs extension: preserved: true ` if err := os.WriteFile(configPath, []byte(original), 0o600); err != nil { t.Fatal(err) } runtime := config.NewStore(&config.Config{Data: &config.Data{}, Admin: &config.Admin{ConfigPath: configPath}}) data := &Data{runtime: runtime} if err := data.persistDatabaseConfig(&config.Database{Driver: "mysql", Host: "db", Port: "3306", User: "root", Password: "secret", Name: "kra", Config: "parseTime=True"}, "new-key"); err != nil { t.Fatal(err) } raw, err := os.ReadFile(configPath) if err != nil { t.Fatal(err) } var value map[string]any if err = yaml.Unmarshal(raw, &value); err != nil { t.Fatal(err) } admin := value["admin"].(map[string]any) jwt := admin["jwt"].(map[string]any) if jwt["signing_key"] != "new-key" || jwt["issuer"] != "kra" { t.Fatalf("unexpected jwt config: %#v", jwt) } dataConfig := value["data"].(map[string]any) database := dataConfig["database"].(map[string]any) if database["name"] != "kra" || database["password"] != "secret" { t.Fatalf("unexpected database config: %#v", database) } if _, ok := dataConfig["redis"]; !ok { t.Fatalf("redis config was removed: %#v", dataConfig) } if _, ok := value["extension"]; !ok || !strings.Contains(string(raw), "preserved: true") { t.Fatalf("extension config was removed: %s", raw) } }