package main import ( "os" "testing" "github.com/spf13/cobra" "github.com/spf13/viper" ) // TestRootCommand tests the basic structure and flags of the root command func TestRootCommand(t *testing.T) { // Reset viper for clean test state viper.Reset() tests := []struct { name string args []string wantErr bool validate func(t *testing.T, cmd *cobra.Command) }{ { name: "help flag", args: []string{"--help"}, wantErr: false, validate: func(t *testing.T, cmd *cobra.Command) { // Help should be available if !cmd.HasAvailableFlags() { t.Error("Expected command to have available flags") } }, }, { name: "size flag", args: []string{"--size", "128"}, wantErr: false, validate: func(t *testing.T, cmd *cobra.Command) { if viper.GetInt("size") != 128 { t.Errorf("Expected size=128, got %d", viper.GetInt("size")) } }, }, { name: "format flag svg", args: []string{"--format", "svg"}, wantErr: false, validate: func(t *testing.T, cmd *cobra.Command) { if viper.GetString("format") != "svg" { t.Errorf("Expected format=svg, got %s", viper.GetString("format")) } }, }, { name: "format flag png", args: []string{"--format", "png"}, wantErr: false, validate: func(t *testing.T, cmd *cobra.Command) { if viper.GetString("format") != "png" { t.Errorf("Expected format=png, got %s", viper.GetString("format")) } }, }, { name: "invalid format", args: []string{"--format", "invalid"}, wantErr: true, validate: func(t *testing.T, cmd *cobra.Command) { // Should not reach here on error }, }, { name: "padding flag", args: []string{"--padding", "0.15"}, wantErr: false, validate: func(t *testing.T, cmd *cobra.Command) { if viper.GetFloat64("padding") != 0.15 { t.Errorf("Expected padding=0.15, got %f", viper.GetFloat64("padding")) } }, }, { name: "color-saturation flag", args: []string{"--color-saturation", "0.8"}, wantErr: false, validate: func(t *testing.T, cmd *cobra.Command) { if viper.GetFloat64("color-saturation") != 0.8 { t.Errorf("Expected color-saturation=0.8, got %f", viper.GetFloat64("color-saturation")) } }, }, { name: "bg-color flag", args: []string{"--bg-color", "#ffffff"}, wantErr: false, validate: func(t *testing.T, cmd *cobra.Command) { if viper.GetString("bg-color") != "#ffffff" { t.Errorf("Expected bg-color=#ffffff, got %s", viper.GetString("bg-color")) } }, }, { name: "hue-restrictions flag", args: []string{"--hue-restrictions", "0,120,240"}, wantErr: false, validate: func(t *testing.T, cmd *cobra.Command) { hues := viper.GetStringSlice("hue-restrictions") expected := []string{"0", "120", "240"} if len(hues) != len(expected) { t.Errorf("Expected %d hue restrictions, got %d", len(expected), len(hues)) } for i, hue := range expected { if i >= len(hues) || hues[i] != hue { t.Errorf("Expected hue[%d]=%s, got %s", i, hue, hues[i]) } } }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Reset viper for each test viper.Reset() // Create a fresh root command for each test cmd := &cobra.Command{ Use: "jdenticon", Short: "Generate identicons from any input string", } // Re-initialize flags initTestFlags(cmd) // Set args and execute cmd.SetArgs(tt.args) err := cmd.Execute() if (err != nil) != tt.wantErr { t.Errorf("Execute() error = %v, wantErr %v", err, tt.wantErr) return } if !tt.wantErr && tt.validate != nil { tt.validate(t, cmd) } }) } } // initTestFlags initializes flags for testing (similar to root init()) func initTestFlags(cmd *cobra.Command) { // Basic flags shared across commands var formatFlag FormatFlag = FormatSVG // Default to SVG cmd.PersistentFlags().IntP("size", "s", 200, "Size of the identicon in pixels") cmd.PersistentFlags().VarP(&formatFlag, "format", "f", `Output format ("png" or "svg")`) cmd.PersistentFlags().Float64P("padding", "p", 0.08, "Padding as percentage of icon size (0.0-0.5)") // Color configuration flags cmd.PersistentFlags().Float64("color-saturation", 0.5, "Saturation for colored shapes (0.0-1.0)") cmd.PersistentFlags().Float64("grayscale-saturation", 0.0, "Saturation for grayscale shapes (0.0-1.0)") cmd.PersistentFlags().String("bg-color", "", "Background color (hex format, e.g., #ffffff)") // Advanced configuration flags cmd.PersistentFlags().StringSlice("hue-restrictions", nil, "Restrict hues to specific degrees (0-360), e.g., --hue-restrictions=0,120,240") cmd.PersistentFlags().String("color-lightness", "0.4,0.8", "Color lightness range as min,max (0.0-1.0)") cmd.PersistentFlags().String("grayscale-lightness", "0.3,0.9", "Grayscale lightness range as min,max (0.0-1.0)") cmd.PersistentFlags().Int("png-supersampling", 8, "PNG supersampling factor (1-16)") // Bind flags to viper viper.BindPFlag("size", cmd.PersistentFlags().Lookup("size")) viper.BindPFlag("format", cmd.PersistentFlags().Lookup("format")) viper.BindPFlag("padding", cmd.PersistentFlags().Lookup("padding")) viper.BindPFlag("color-saturation", cmd.PersistentFlags().Lookup("color-saturation")) viper.BindPFlag("grayscale-saturation", cmd.PersistentFlags().Lookup("grayscale-saturation")) viper.BindPFlag("bg-color", cmd.PersistentFlags().Lookup("bg-color")) viper.BindPFlag("hue-restrictions", cmd.PersistentFlags().Lookup("hue-restrictions")) viper.BindPFlag("color-lightness", cmd.PersistentFlags().Lookup("color-lightness")) viper.BindPFlag("grayscale-lightness", cmd.PersistentFlags().Lookup("grayscale-lightness")) viper.BindPFlag("png-supersampling", cmd.PersistentFlags().Lookup("png-supersampling")) } // TestPopulateConfigFromFlags tests the configuration building logic func TestPopulateConfigFromFlags(t *testing.T) { tests := []struct { name string setup func() wantErr bool validate func(t *testing.T, config interface{}, size int) }{ { name: "default config", setup: func() { viper.Reset() viper.Set("size", 200) viper.Set("format", "svg") viper.Set("png-supersampling", 8) }, wantErr: false, validate: func(t *testing.T, config interface{}, size int) { if size != 200 { t.Errorf("Expected size=200, got %d", size) } }, }, { name: "custom config", setup: func() { viper.Reset() viper.Set("size", 128) viper.Set("padding", 0.12) viper.Set("color-saturation", 0.7) viper.Set("bg-color", "#000000") viper.Set("png-supersampling", 8) }, wantErr: false, validate: func(t *testing.T, config interface{}, size int) { if size != 128 { t.Errorf("Expected size=128, got %d", size) } }, }, { name: "invalid size", setup: func() { viper.Reset() viper.Set("size", -1) }, wantErr: true, validate: nil, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { tt.setup() config, size, err := populateConfigFromFlags() if (err != nil) != tt.wantErr { t.Errorf("populateConfigFromFlags() error = %v, wantErr %v", err, tt.wantErr) return } if !tt.wantErr && tt.validate != nil { tt.validate(t, config, size) } }) } } // TestGetFormatFromViper tests format flag validation func TestGetFormatFromViper(t *testing.T) { tests := []struct { name string formatValue string wantFormat FormatFlag wantErr bool }{ { name: "svg format", formatValue: "svg", wantFormat: FormatSVG, wantErr: false, }, { name: "png format", formatValue: "png", wantFormat: FormatPNG, wantErr: false, }, { name: "invalid format", formatValue: "invalid", wantFormat: FormatSVG, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { viper.Reset() viper.Set("format", tt.formatValue) format, err := getFormatFromViper() if (err != nil) != tt.wantErr { t.Errorf("getFormatFromViper() error = %v, wantErr %v", err, tt.wantErr) return } if format != tt.wantFormat { t.Errorf("getFormatFromViper() = %v, want %v", format, tt.wantFormat) } }) } } // TestMain sets up and tears down for tests func TestMain(m *testing.M) { // Setup code := m.Run() // Teardown viper.Reset() os.Exit(code) }