package jdenticon import ( "errors" "strings" "testing" ) // TestIsValidHexColor tests the hex color validation helper function. func TestIsValidHexColor(t *testing.T) { tests := []struct { name string color string expected bool }{ // Valid cases { name: "empty string (transparent)", color: "", expected: true, }, { name: "valid 3-digit lowercase", color: "#fff", expected: true, }, { name: "valid 3-digit uppercase", color: "#FFF", expected: true, }, { name: "valid 3-digit mixed case", color: "#Fa3", expected: true, }, { name: "valid 6-digit lowercase", color: "#ffffff", expected: true, }, { name: "valid 6-digit uppercase", color: "#FFFFFF", expected: true, }, { name: "valid 6-digit mixed case", color: "#Ff00Aa", expected: true, }, { name: "valid with numbers", color: "#123456", expected: true, }, { name: "valid 3-digit with numbers", color: "#123", expected: true, }, // Invalid cases { name: "missing hash prefix", color: "ffffff", expected: false, }, { name: "too short", color: "#ff", expected: false, }, { name: "too long", color: "#fffffff", expected: false, }, { name: "invalid hex characters", color: "#gggggg", expected: false, }, { name: "invalid hex characters in 3-digit", color: "#ggg", expected: false, }, { name: "just hash", color: "#", expected: false, }, { name: "double hash", color: "##ffffff", expected: false, }, { name: "color name", color: "red", expected: false, }, { name: "color name with hash", color: "#red", expected: false, }, { name: "4-digit hex", color: "#1234", expected: false, }, { name: "5-digit hex", color: "#12345", expected: false, }, { name: "7-digit hex", color: "#1234567", expected: false, }, { name: "8-digit hex (RGBA)", color: "#12345678", expected: false, }, { name: "with spaces", color: "# ffffff", expected: false, }, { name: "with special characters", color: "#ffffff!", expected: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := isValidHexColor(tt.color) if result != tt.expected { t.Errorf("isValidHexColor(%q) = %v, expected %v", tt.color, result, tt.expected) } }) } } // TestWithBackgroundColor tests the WithBackgroundColor config option. func TestWithBackgroundColor(t *testing.T) { tests := []struct { name string color string expectError bool expectedError string }{ // Valid cases { name: "empty string (transparent)", color: "", expectError: false, }, { name: "valid 3-digit hex", color: "#fff", expectError: false, }, { name: "valid 6-digit hex", color: "#ffffff", expectError: false, }, { name: "valid mixed case", color: "#FfAa00", expectError: false, }, { name: "valid with numbers", color: "#123456", expectError: false, }, // Invalid cases { name: "missing hash", color: "ffffff", expectError: true, expectedError: "must be a valid hex color in #RGB or #RRGGBB format", }, { name: "invalid hex characters", color: "#gggggg", expectError: true, expectedError: "must be a valid hex color in #RGB or #RRGGBB format", }, { name: "too short", color: "#ff", expectError: true, expectedError: "must be a valid hex color in #RGB or #RRGGBB format", }, { name: "too long", color: "#fffffff", expectError: true, expectedError: "must be a valid hex color in #RGB or #RRGGBB format", }, { name: "color name", color: "red", expectError: true, expectedError: "must be a valid hex color in #RGB or #RRGGBB format", }, { name: "4-digit hex", color: "#1234", expectError: true, expectedError: "must be a valid hex color in #RGB or #RRGGBB format", }, { name: "8-digit hex (RGBA)", color: "#12345678", expectError: true, expectedError: "must be a valid hex color in #RGB or #RRGGBB format", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { option := WithBackgroundColor(tt.color) config := DefaultConfig() err := option(&config) if tt.expectError { if err == nil { t.Errorf("WithBackgroundColor(%q) expected error but got none", tt.color) return } // Check that it's the right error type var invalidInput *ErrInvalidInput if !errors.As(err, &invalidInput) { t.Errorf("WithBackgroundColor(%q) error type = %T, expected *ErrInvalidInput", tt.color, err) return } // Check error message contains expected text if tt.expectedError != "" { if !strings.Contains(err.Error(), tt.expectedError) { t.Errorf("WithBackgroundColor(%q) error = %q, expected to contain %q", tt.color, err.Error(), tt.expectedError) } } // Check field name if invalidInput.Field != "background_color" { t.Errorf("WithBackgroundColor(%q) error field = %q, expected %q", tt.color, invalidInput.Field, "background_color") } // Check value is captured if invalidInput.Value != tt.color { t.Errorf("WithBackgroundColor(%q) error value = %q, expected %q", tt.color, invalidInput.Value, tt.color) } } else { if err != nil { t.Errorf("WithBackgroundColor(%q) unexpected error: %v", tt.color, err) return } // Check that the color was set if config.BackgroundColor != tt.color { t.Errorf("WithBackgroundColor(%q) config.BackgroundColor = %q, expected %q", tt.color, config.BackgroundColor, tt.color) } } }) } } // TestConfigValidateBackgroundColor tests that Config.Validate() validates background colors. func TestConfigValidateBackgroundColor(t *testing.T) { tests := []struct { name string color string expectError bool expectedError string }{ { name: "valid empty color", color: "", expectError: false, }, { name: "valid 3-digit hex", color: "#fff", expectError: false, }, { name: "valid 6-digit hex", color: "#ffffff", expectError: false, }, { name: "invalid color", color: "invalid-color", expectError: true, expectedError: "must be a valid hex color in #RGB or #RRGGBB format", }, { name: "missing hash", color: "ffffff", expectError: true, expectedError: "must be a valid hex color in #RGB or #RRGGBB format", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { config := DefaultConfig() config.BackgroundColor = tt.color err := config.Validate() if tt.expectError { if err == nil { t.Errorf("Config.Validate() with BackgroundColor=%q expected error but got none", tt.color) return } // Check that it's the right error type var invalidInput *ErrInvalidInput if !errors.As(err, &invalidInput) { t.Errorf("Config.Validate() with BackgroundColor=%q error type = %T, expected *ErrInvalidInput", tt.color, err) return } // Check error message contains expected text if tt.expectedError != "" { if !strings.Contains(err.Error(), tt.expectedError) { t.Errorf("Config.Validate() with BackgroundColor=%q error = %q, expected to contain %q", tt.color, err.Error(), tt.expectedError) } } } else { if err != nil { t.Errorf("Config.Validate() with BackgroundColor=%q unexpected error: %v", tt.color, err) } } }) } } // TestToEngineColorConfigBackgroundColor tests that toEngineColorConfig handles background colors. // Since validation is handled by Config.Validate(), this test focuses on the successful conversion path. func TestToEngineColorConfigBackgroundColor(t *testing.T) { tests := []struct { name string color string expectError bool expectedError string }{ { name: "valid empty color", color: "", expectError: false, }, { name: "valid 3-digit hex", color: "#fff", expectError: false, }, { name: "valid 6-digit hex", color: "#ffffff", expectError: false, }, { name: "invalid color caught by Validate()", color: "invalid-color", expectError: true, expectedError: "invalid configuration", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { config := DefaultConfig() // Directly set the background color to bypass WithBackgroundColor validation config.BackgroundColor = tt.color _, err := config.toEngineColorConfig() if tt.expectError { if err == nil { t.Errorf("toEngineColorConfig() with BackgroundColor=%q expected error but got none", tt.color) return } // Check error message contains expected text (from c.Validate() call) if tt.expectedError != "" { if !strings.Contains(err.Error(), tt.expectedError) { t.Errorf("toEngineColorConfig() with BackgroundColor=%q error = %q, expected to contain %q", tt.color, err.Error(), tt.expectedError) } } } else { if err != nil { t.Errorf("toEngineColorConfig() with BackgroundColor=%q unexpected error: %v", tt.color, err) } } }) } } // TestConfigureFunctionWithBackgroundColor tests the Configure function with background color options. func TestConfigureFunctionWithBackgroundColor(t *testing.T) { tests := []struct { name string color string expectError bool expectedError string }{ { name: "valid color through Configure", color: "#ffffff", expectError: false, }, { name: "invalid color through Configure", color: "invalid", expectError: true, expectedError: "configuration option failed", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { config, err := Configure(WithBackgroundColor(tt.color)) if tt.expectError { if err == nil { t.Errorf("Configure(WithBackgroundColor(%q)) expected error but got none", tt.color) return } if tt.expectedError != "" { if !strings.Contains(err.Error(), tt.expectedError) { t.Errorf("Configure(WithBackgroundColor(%q)) error = %q, expected to contain %q", tt.color, err.Error(), tt.expectedError) } } } else { if err != nil { t.Errorf("Configure(WithBackgroundColor(%q)) unexpected error: %v", tt.color, err) return } if config.BackgroundColor != tt.color { t.Errorf("Configure(WithBackgroundColor(%q)) config.BackgroundColor = %q, expected %q", tt.color, config.BackgroundColor, tt.color) } } }) } }