Initial release: Go Jdenticon library v0.1.0
- Core library with SVG and PNG generation - CLI tool with generate and batch commands - Cross-platform path handling for Windows compatibility - Comprehensive test suite with integration tests
This commit is contained in:
@@ -1,437 +1,462 @@
|
||||
package jdenticon
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDefaultConfig(t *testing.T) {
|
||||
config := DefaultConfig()
|
||||
|
||||
// Test default values match jdenticon-js
|
||||
if len(config.HueRestrictions) != 0 {
|
||||
t.Errorf("Expected no hue restrictions, got %v", config.HueRestrictions)
|
||||
}
|
||||
|
||||
expectedColorRange := [2]float64{0.4, 0.8}
|
||||
if config.ColorLightnessRange != expectedColorRange {
|
||||
t.Errorf("Expected color lightness range %v, got %v", expectedColorRange, config.ColorLightnessRange)
|
||||
}
|
||||
|
||||
expectedGrayscaleRange := [2]float64{0.3, 0.9}
|
||||
if config.GrayscaleLightnessRange != expectedGrayscaleRange {
|
||||
t.Errorf("Expected grayscale lightness range %v, got %v", expectedGrayscaleRange, config.GrayscaleLightnessRange)
|
||||
}
|
||||
|
||||
if config.ColorSaturation != 0.5 {
|
||||
t.Errorf("Expected color saturation 0.5, got %f", config.ColorSaturation)
|
||||
}
|
||||
|
||||
if config.GrayscaleSaturation != 0.0 {
|
||||
t.Errorf("Expected grayscale saturation 0.0, got %f", config.GrayscaleSaturation)
|
||||
}
|
||||
|
||||
if config.BackgroundColor != "" {
|
||||
t.Errorf("Expected empty background color, got %s", config.BackgroundColor)
|
||||
}
|
||||
|
||||
if config.Padding != 0.08 {
|
||||
t.Errorf("Expected padding 0.08, got %f", config.Padding)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithHueRestrictions(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
hues []float64
|
||||
wantErr bool
|
||||
}{
|
||||
{"valid hues", []float64{0, 90, 180, 270}, false},
|
||||
{"single hue", []float64{120}, false},
|
||||
{"empty slice", []float64{}, false},
|
||||
{"negative hue", []float64{-10}, true},
|
||||
{"hue too large", []float64{360}, true},
|
||||
{"mixed valid/invalid", []float64{120, 400}, true},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
config, err := Configure(WithHueRestrictions(tt.hues))
|
||||
|
||||
if tt.wantErr {
|
||||
if err == nil {
|
||||
t.Errorf("Expected error for hues %v, got none", tt.hues)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(config.HueRestrictions) != len(tt.hues) {
|
||||
t.Errorf("Expected %d hue restrictions, got %d", len(tt.hues), len(config.HueRestrictions))
|
||||
}
|
||||
|
||||
for i, hue := range tt.hues {
|
||||
if config.HueRestrictions[i] != hue {
|
||||
t.Errorf("Expected hue %f at index %d, got %f", hue, i, config.HueRestrictions[i])
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithLightnessRanges(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
min float64
|
||||
max float64
|
||||
wantErr bool
|
||||
}{
|
||||
{"valid range", 0.2, 0.8, false},
|
||||
{"full range", 0.0, 1.0, false},
|
||||
{"equal values", 0.5, 0.5, false},
|
||||
{"min > max", 0.8, 0.2, true},
|
||||
{"negative min", -0.1, 0.5, true},
|
||||
{"max > 1", 0.5, 1.1, true},
|
||||
{"both invalid", -0.1, 1.1, true},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run("color_"+tt.name, func(t *testing.T) {
|
||||
config, err := Configure(WithColorLightnessRange(tt.min, tt.max))
|
||||
|
||||
if tt.wantErr {
|
||||
if err == nil {
|
||||
t.Errorf("Expected error for range [%f, %f], got none", tt.min, tt.max)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
expected := [2]float64{tt.min, tt.max}
|
||||
if config.ColorLightnessRange != expected {
|
||||
t.Errorf("Expected range %v, got %v", expected, config.ColorLightnessRange)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("grayscale_"+tt.name, func(t *testing.T) {
|
||||
config, err := Configure(WithGrayscaleLightnessRange(tt.min, tt.max))
|
||||
|
||||
if tt.wantErr {
|
||||
if err == nil {
|
||||
t.Errorf("Expected error for range [%f, %f], got none", tt.min, tt.max)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
expected := [2]float64{tt.min, tt.max}
|
||||
if config.GrayscaleLightnessRange != expected {
|
||||
t.Errorf("Expected range %v, got %v", expected, config.GrayscaleLightnessRange)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithSaturation(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
saturation float64
|
||||
wantErr bool
|
||||
}{
|
||||
{"valid saturation", 0.5, false},
|
||||
{"zero saturation", 0.0, false},
|
||||
{"max saturation", 1.0, false},
|
||||
{"negative saturation", -0.1, true},
|
||||
{"saturation > 1", 1.1, true},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run("color_"+tt.name, func(t *testing.T) {
|
||||
config, err := Configure(WithColorSaturation(tt.saturation))
|
||||
|
||||
if tt.wantErr {
|
||||
if err == nil {
|
||||
t.Errorf("Expected error for saturation %f, got none", tt.saturation)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if config.ColorSaturation != tt.saturation {
|
||||
t.Errorf("Expected saturation %f, got %f", tt.saturation, config.ColorSaturation)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("grayscale_"+tt.name, func(t *testing.T) {
|
||||
config, err := Configure(WithGrayscaleSaturation(tt.saturation))
|
||||
|
||||
if tt.wantErr {
|
||||
if err == nil {
|
||||
t.Errorf("Expected error for saturation %f, got none", tt.saturation)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if config.GrayscaleSaturation != tt.saturation {
|
||||
t.Errorf("Expected saturation %f, got %f", tt.saturation, config.GrayscaleSaturation)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithBackgroundColor(t *testing.T) {
|
||||
// TestIsValidHexColor tests the hex color validation helper function.
|
||||
func TestIsValidHexColor(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
color string
|
||||
wantErr bool
|
||||
expected string
|
||||
expected bool
|
||||
}{
|
||||
{"empty color", "", false, ""},
|
||||
{"3-char hex", "#fff", false, "#fff"},
|
||||
{"4-char hex with alpha", "#ffff", false, "#ffff"},
|
||||
{"6-char hex", "#ffffff", false, "#ffffff"},
|
||||
{"8-char hex with alpha", "#ffffff80", false, "#ffffff80"},
|
||||
{"lowercase", "#abc123", false, "#abc123"},
|
||||
{"uppercase", "#ABC123", false, "#ABC123"},
|
||||
{"invalid format", "ffffff", true, ""},
|
||||
{"invalid chars", "#gggggg", true, ""},
|
||||
{"too short", "#ff", true, ""},
|
||||
{"5-char hex", "#fffff", true, ""},
|
||||
{"7-char hex", "#fffffff", true, ""},
|
||||
{"9-char hex", "#fffffffff", true, ""},
|
||||
// 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) {
|
||||
config, err := Configure(WithBackgroundColor(tt.color))
|
||||
|
||||
if tt.wantErr {
|
||||
if err == nil {
|
||||
t.Errorf("Expected error for color %s, got none", tt.color)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if config.BackgroundColor != tt.expected {
|
||||
t.Errorf("Expected color %s, got %s", tt.expected, config.BackgroundColor)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithPadding(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
padding float64
|
||||
wantErr bool
|
||||
}{
|
||||
{"valid padding", 0.08, false},
|
||||
{"zero padding", 0.0, false},
|
||||
{"max padding", 0.5, false},
|
||||
{"negative padding", -0.1, true},
|
||||
{"padding > 0.5", 0.6, true},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
config, err := Configure(WithPadding(tt.padding))
|
||||
|
||||
if tt.wantErr {
|
||||
if err == nil {
|
||||
t.Errorf("Expected error for padding %f, got none", tt.padding)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if config.Padding != tt.padding {
|
||||
t.Errorf("Expected padding %f, got %f", tt.padding, config.Padding)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigureMultipleOptions(t *testing.T) {
|
||||
config, err := Configure(
|
||||
WithHueRestrictions([]float64{120, 240}),
|
||||
WithColorSaturation(0.7),
|
||||
WithPadding(0.1),
|
||||
WithBackgroundColor("#ff0000"),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(config.HueRestrictions) != 2 || config.HueRestrictions[0] != 120 || config.HueRestrictions[1] != 240 {
|
||||
t.Errorf("Expected hue restrictions [120, 240], got %v", config.HueRestrictions)
|
||||
}
|
||||
|
||||
if config.ColorSaturation != 0.7 {
|
||||
t.Errorf("Expected color saturation 0.7, got %f", config.ColorSaturation)
|
||||
}
|
||||
|
||||
if config.Padding != 0.1 {
|
||||
t.Errorf("Expected padding 0.1, got %f", config.Padding)
|
||||
}
|
||||
|
||||
if config.BackgroundColor != "#ff0000" {
|
||||
t.Errorf("Expected background color #ff0000, got %s", config.BackgroundColor)
|
||||
}
|
||||
|
||||
// Check that other values are still default
|
||||
expectedColorRange := [2]float64{0.4, 0.8}
|
||||
if config.ColorLightnessRange != expectedColorRange {
|
||||
t.Errorf("Expected default color lightness range %v, got %v", expectedColorRange, config.ColorLightnessRange)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseColor(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
expected string
|
||||
wantErr bool
|
||||
}{
|
||||
{"empty", "", "", false},
|
||||
{"3-char to 6-char", "#abc", "#aabbcc", false},
|
||||
{"4-char to 8-char", "#abcd", "#aabbccdd", false},
|
||||
{"6-char unchanged", "#abcdef", "#abcdef", false},
|
||||
{"8-char unchanged", "#abcdef80", "#abcdef80", false},
|
||||
{"invalid format", "abcdef", "", true},
|
||||
{"invalid length", "#abcde", "", true},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result, err := ParseColor(tt.input)
|
||||
|
||||
if tt.wantErr {
|
||||
if err == nil {
|
||||
t.Errorf("Expected error for input %s, got none", tt.input)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
result := isValidHexColor(tt.color)
|
||||
if result != tt.expected {
|
||||
t.Errorf("Expected %s, got %s", tt.expected, result)
|
||||
t.Errorf("isValidHexColor(%q) = %v, expected %v", tt.color, result, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigGetHue(t *testing.T) {
|
||||
// TestWithBackgroundColor tests the WithBackgroundColor config option.
|
||||
func TestWithBackgroundColor(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
restrictions []float64
|
||||
input float64
|
||||
expected float64
|
||||
name string
|
||||
color string
|
||||
expectError bool
|
||||
expectedError string
|
||||
}{
|
||||
{"no restrictions", nil, 0.5, 0.5},
|
||||
{"single restriction", []float64{180}, 0.5, 0.5},
|
||||
{"multiple restrictions", []float64{0, 120, 240}, 0.0, 0.0},
|
||||
{"multiple restrictions mid", []float64{0, 120, 240}, 0.5, 120.0 / 360.0},
|
||||
{"multiple restrictions high", []float64{0, 120, 240}, 0.99, 240.0 / 360.0},
|
||||
// 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.HueRestrictions = tt.restrictions
|
||||
|
||||
result := config.GetHue(tt.input)
|
||||
|
||||
// Allow small floating point differences
|
||||
if abs(result-tt.expected) > 0.001 {
|
||||
t.Errorf("Expected hue %f, got %f", tt.expected, result)
|
||||
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)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigGetLightness(t *testing.T) {
|
||||
config := DefaultConfig()
|
||||
|
||||
// Test color lightness
|
||||
colorLight := config.GetColorLightness(0.0)
|
||||
if colorLight != 0.4 {
|
||||
t.Errorf("Expected min color lightness 0.4, got %f", colorLight)
|
||||
// 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",
|
||||
},
|
||||
}
|
||||
|
||||
colorLight = config.GetColorLightness(1.0)
|
||||
if colorLight != 0.8 {
|
||||
t.Errorf("Expected max color lightness 0.8, got %f", colorLight)
|
||||
}
|
||||
|
||||
colorLight = config.GetColorLightness(0.5)
|
||||
expected := 0.4 + 0.5*(0.8-0.4)
|
||||
if abs(colorLight-expected) > 0.001 {
|
||||
t.Errorf("Expected mid color lightness %f, got %f", expected, colorLight)
|
||||
}
|
||||
|
||||
// Test grayscale lightness
|
||||
grayLight := config.GetGrayscaleLightness(0.0)
|
||||
if grayLight != 0.3 {
|
||||
t.Errorf("Expected min grayscale lightness 0.3, got %f", grayLight)
|
||||
}
|
||||
|
||||
grayLight = config.GetGrayscaleLightness(1.0)
|
||||
if abs(grayLight-0.9) > 0.001 {
|
||||
t.Errorf("Expected max grayscale lightness 0.9, got %f", grayLight)
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigureFailing(t *testing.T) {
|
||||
// Test that Configure fails on invalid options
|
||||
_, err := Configure(
|
||||
WithHueRestrictions([]float64{400}), // Invalid hue
|
||||
WithColorSaturation(0.5), // Valid option after invalid
|
||||
)
|
||||
|
||||
if err == nil {
|
||||
t.Error("Expected Configure to fail with invalid hue restriction")
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function for floating point comparison
|
||||
func abs(x float64) float64 {
|
||||
if x < 0 {
|
||||
return -x
|
||||
}
|
||||
return x
|
||||
}
|
||||
Reference in New Issue
Block a user