437 lines
11 KiB
Go
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
|
|
} |