package main import ( "bytes" "os" "path/filepath" "strings" "testing" "github.com/spf13/viper" ) func TestGenerateConfigCmd_Flags(t *testing.T) { cmd := generateConfigCmd() flags := cmd.Flags() lang, err := flags.GetString("lang") if err != nil { t.Fatalf("failed to get --lang flag: %v", err) } if lang != "en" { t.Errorf("--lang default = %q, want %q", lang, "en") } output, err := flags.GetString("output") if err != nil { t.Fatalf("failed to get --output flag: %v", err) } if output != "" { t.Errorf("--output default = %q, want empty string", output) } } func TestGenerateConfig_LangEn(t *testing.T) { cmd := generateConfigCmd() var buf bytes.Buffer cmd.SetOut(&buf) cmd.SetErr(&buf) if err := cmd.Flags().Set("lang", "en"); err != nil { t.Fatalf("Set flag failed: %v", err) } if err := cmd.Execute(); err != nil { t.Fatalf("Execute failed: %v", err) } out := buf.String() if !strings.Contains(out, "pg:") { t.Errorf("output missing pg section") } if !strings.Contains(out, "host") { t.Errorf("output missing host key") } if !strings.Contains(out, "PostgreSQL") { t.Errorf("output missing English comment") } } func TestGenerateConfig_LangRu(t *testing.T) { cmd := generateConfigCmd() var buf bytes.Buffer cmd.SetOut(&buf) cmd.SetErr(&buf) if err := cmd.Flags().Set("lang", "ru"); err != nil { t.Fatalf("Set flag failed: %v", err) } if err := cmd.Execute(); err != nil { t.Fatalf("Execute failed: %v", err) } out := buf.String() if !strings.Contains(out, "pg:") { t.Errorf("output missing pg section") } if !strings.Contains(out, "хост") { t.Errorf("output missing Russian comment (хост)") } } func TestGenerateConfig_OutputFile(t *testing.T) { tempDir := t.TempDir() outputPath := filepath.Join(tempDir, "config.yaml") cmd := generateConfigCmd() var buf bytes.Buffer cmd.SetOut(&buf) cmd.SetErr(&buf) if err := cmd.Flags().Set("output", outputPath); err != nil { t.Fatalf("Set flag failed: %v", err) } if err := cmd.Execute(); err != nil { t.Fatalf("Execute failed: %v", err) } data, err := os.ReadFile(outputPath) if err != nil { t.Fatalf("failed to read output file: %v", err) } if len(data) == 0 { t.Errorf("output file is empty") } if buf.Len() != 0 { t.Errorf("stdout not empty when --output set: %q", buf.String()) } } func TestGenerateConfig_Stdout(t *testing.T) { cmd := generateConfigCmd() var buf bytes.Buffer cmd.SetOut(&buf) cmd.SetErr(&buf) if err := cmd.Execute(); err != nil { t.Fatalf("Execute failed: %v", err) } out := buf.String() if out == "" { t.Errorf("stdout empty when no --output") } if !strings.Contains(out, "pg:") { t.Errorf("stdout missing pg section") } } func TestGenerateConfig_AllKeys(t *testing.T) { cmd := generateConfigCmd() var buf bytes.Buffer cmd.SetOut(&buf) cmd.SetErr(&buf) if err := cmd.Execute(); err != nil { t.Fatalf("Execute failed: %v", err) } out := buf.String() requiredKeys := []string{ "host", "port", "user", "password", "database", "sslmode", "exclude_tables", "dir", "retention_days", "cron", "pq_scheme", "classical_scheme", "pq_public_key_path", "classical_public_key_path", "healthz", "log", } for _, key := range requiredKeys { if !strings.Contains(out, key) { t.Errorf("output missing key %q", key) } } } func TestGenerateConfig_RoundTrip(t *testing.T) { tempDir := t.TempDir() outputPath := filepath.Join(tempDir, "config.yaml") cmd := generateConfigCmd() if err := cmd.Flags().Set("output", outputPath); err != nil { t.Fatalf("Set flag failed: %v", err) } if err := cmd.Execute(); err != nil { t.Fatalf("Execute failed: %v", err) } v := viper.New() v.SetConfigFile(outputPath) if err := v.ReadInConfig(); err != nil { t.Fatalf("viper read generated config failed: %v", err) } var cfg struct { PG struct { Host string `mapstructure:"host"` Port int `mapstructure:"port"` User string `mapstructure:"user"` Password string `mapstructure:"password"` Database string `mapstructure:"database"` SSLMode string `mapstructure:"sslmode"` ExcludeTables []string `mapstructure:"exclude_tables"` } `mapstructure:"pg"` Backup struct { Dir string `mapstructure:"dir"` RetentionDays int `mapstructure:"retention_days"` Cron string `mapstructure:"cron"` } `mapstructure:"backup"` PQScheme uint16 `mapstructure:"pq_scheme"` ClassicalScheme uint16 `mapstructure:"classical_scheme"` PQPublicKeyPath string `mapstructure:"pq_public_key_path"` ClassicalPublicKeyPath string `mapstructure:"classical_public_key_path"` Healthz struct { Port int `mapstructure:"port"` } `mapstructure:"healthz"` Log struct { Level string `mapstructure:"level"` } `mapstructure:"log"` } if err := v.Unmarshal(&cfg); err != nil { t.Fatalf("viper unmarshal generated config failed: %v", err) } if cfg.PG.Port != 5432 { t.Errorf("PG.Port = %d, want 5432", cfg.PG.Port) } if cfg.PG.SSLMode != "prefer" { t.Errorf("PG.SSLMode = %q, want prefer", cfg.PG.SSLMode) } if len(cfg.PG.ExcludeTables) != 1 || cfg.PG.ExcludeTables[0] != "e2e_one_time_keys_json" { t.Errorf("PG.ExcludeTables = %v, want [e2e_one_time_keys_json]", cfg.PG.ExcludeTables) } if cfg.Backup.RetentionDays != 180 { t.Errorf("Backup.RetentionDays = %d, want 180", cfg.Backup.RetentionDays) } if cfg.Backup.Cron != "0 0 3 * * *" { t.Errorf("Backup.Cron = %q, want 0 0 3 * * *", cfg.Backup.Cron) } if cfg.PQScheme != 0x0006 { t.Errorf("PQScheme = 0x%04x, want 0x%04x", cfg.PQScheme, 0x0006) } if cfg.ClassicalScheme != 0x0007 { t.Errorf("ClassicalScheme = 0x%04x, want 0x%04x", cfg.ClassicalScheme, 0x0007) } if cfg.Healthz.Port != 8080 { t.Errorf("Healthz.Port = %d, want 8080", cfg.Healthz.Port) } if cfg.Log.Level != "info" { t.Errorf("Log.Level = %q, want info", cfg.Log.Level) } }