Files
go-jdenticon/jdenticon/config_test.go
Kevin McIntyre f84b511895 init
2025-06-18 01:00:00 -04:00

437 lines
11 KiB
Go

package jdenticon
import (
"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) {
tests := []struct {
name string
color string
wantErr bool
expected string
}{
{"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, ""},
}
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
}
if result != tt.expected {
t.Errorf("Expected %s, got %s", tt.expected, result)
}
})
}
}
func TestConfigGetHue(t *testing.T) {
tests := []struct {
name string
restrictions []float64
input float64
expected float64
}{
{"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},
}
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)
}
})
}
}
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)
}
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)
}
}
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")
}
}
// Helper function for floating point comparison
func abs(x float64) float64 {
if x < 0 {
return -x
}
return x
}