package jdenticon import ( "strings" "testing" ) func TestComputeHash(t *testing.T) { tests := []struct { name string input interface{} expected string // Known SHA-1 hash values }{ { name: "empty string", input: "", expected: "da39a3ee5e6b4b0d3255bfef95601890afd80709", }, { name: "simple string", input: "hello", expected: "aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d", }, { name: "email address", input: "user@example.com", expected: "63a710569261a24b3766275b7000ce8d7b32e2f7", }, { name: "nil input", input: nil, expected: "da39a3ee5e6b4b0d3255bfef95601890afd80709", // Same as empty string }, { name: "integer input", input: 123, expected: "40bd001563085fc35165329ea1ff5c5ecbdbbeef", // SHA-1 of "123" }, { name: "byte slice input", input: []byte("test"), expected: "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := ComputeHash(tt.input) if result != tt.expected { t.Errorf("ComputeHash(%v) = %s, want %s", tt.input, result, tt.expected) } }) } } func TestHashValue(t *testing.T) { // Test that HashValue produces the same result as ComputeHash for strings testStrings := []string{"", "hello", "user@example.com", "test123"} for _, str := range testStrings { hashValue := HashValue(str) computeHash := ComputeHash(str) if hashValue != computeHash { t.Errorf("HashValue(%s) = %s, but ComputeHash(%s) = %s", str, hashValue, str, computeHash) } } } func TestIsValidHash(t *testing.T) { tests := []struct { name string input string expected bool }{ { name: "valid long hash", input: "da39a3ee5e6b4b0d3255bfef95601890afd80709", expected: true, }, { name: "valid minimum length", input: "da39a3ee5e6", expected: true, }, { name: "too short", input: "da39a3ee5e", expected: false, }, { name: "invalid character", input: "da39a3ee5e6g4b0d3255bfef95601890afd80709", expected: false, }, { name: "uppercase valid", input: "DA39A3EE5E6B4B0D3255BFEF95601890AFD80709", expected: true, }, { name: "empty string", input: "", expected: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := isValidHash(tt.input) if result != tt.expected { t.Errorf("isValidHash(%s) = %v, want %v", tt.input, result, tt.expected) } }) } } func TestParseHex(t *testing.T) { hash := "da39a3ee5e6b4b0d3255bfef95601890afd80709" tests := []struct { name string startPosition int octets int expected int }{ { name: "first character", startPosition: 0, octets: 1, expected: 0xd, // 'd' in hex }, { name: "two characters", startPosition: 0, octets: 2, expected: 0xda, }, { name: "middle position", startPosition: 10, octets: 1, expected: 0x6, // '6' at position 10 }, { name: "negative index", startPosition: -1, octets: 1, expected: 0x9, // last character '9' }, { name: "negative index multiple chars", startPosition: -2, octets: 2, expected: 0x09, // last two characters "09" }, { name: "out of bounds", startPosition: 100, octets: 1, expected: 0, }, { name: "zero octets reads to end", startPosition: -7, octets: 0, expected: 0xfd80709, // Last 7 characters "fd80709" }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result, err := parseHex(hash, tt.startPosition, tt.octets) if err != nil { // For out of bounds case, we expect an error but return 0 if tt.expected == 0 && tt.name == "out of bounds" { // This is expected return } t.Errorf("parseHex(%s, %d, %d) unexpected error: %v", hash, tt.startPosition, tt.octets, err) return } if result != tt.expected { t.Errorf("parseHex(%s, %d, %d) = %d, want %d", hash, tt.startPosition, tt.octets, result, tt.expected) } }) } } func TestParseHexErrors(t *testing.T) { tests := []struct { name string hash string startPosition int octets int expectError bool errorContains string }{ { name: "invalid hex character", hash: "da39g3ee5e6b4b0d3255bfef95601890afd80709", startPosition: 4, octets: 1, expectError: true, errorContains: "failed to parse hex", }, { name: "out of bounds positive", hash: "da39a3ee", startPosition: 10, octets: 1, expectError: true, errorContains: "out of bounds", }, { name: "out of bounds negative", hash: "da39a3ee", startPosition: -20, octets: 1, expectError: true, errorContains: "out of bounds", }, { name: "valid hex", hash: "da39a3ee5e6b4b0d3255bfef95601890afd80709", startPosition: 0, octets: 2, expectError: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result, err := parseHex(tt.hash, tt.startPosition, tt.octets) if tt.expectError { if err == nil { t.Errorf("parseHex(%s, %d, %d) expected error, got nil", tt.hash, tt.startPosition, tt.octets) return } if tt.errorContains != "" && !strings.Contains(err.Error(), tt.errorContains) { t.Errorf("parseHex(%s, %d, %d) error = %v, want error containing %s", tt.hash, tt.startPosition, tt.octets, err, tt.errorContains) } } else { if err != nil { t.Errorf("parseHex(%s, %d, %d) unexpected error: %v", tt.hash, tt.startPosition, tt.octets, err) } // For valid case, just ensure no error and result >= 0 if result < 0 { t.Errorf("parseHex(%s, %d, %d) = %d, want >= 0", tt.hash, tt.startPosition, tt.octets, result) } } }) } } func TestParseHash(t *testing.T) { tests := []struct { name string input string expectError bool expected []byte }{ { name: "valid hex string", input: "da39a3ee", expectError: false, expected: []byte{0xda, 0x39, 0xa3, 0xee}, }, { name: "with 0x prefix", input: "0xda39a3ee", expectError: false, expected: []byte{0xda, 0x39, 0xa3, 0xee}, }, { name: "with 0X prefix", input: "0Xda39a3ee", expectError: false, expected: []byte{0xda, 0x39, 0xa3, 0xee}, }, { name: "uppercase hex", input: "DA39A3EE", expectError: false, expected: []byte{0xda, 0x39, 0xa3, 0xee}, }, { name: "empty string", input: "", expectError: true, expected: nil, }, { name: "odd length", input: "da39a3e", expectError: true, expected: nil, }, { name: "invalid character", input: "da39g3ee", expectError: true, expected: nil, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result, err := ParseHash(tt.input) if tt.expectError { if err == nil { t.Errorf("ParseHash(%s) expected error, got nil", tt.input) } } else { if err != nil { t.Errorf("ParseHash(%s) unexpected error: %v", tt.input, err) } if len(result) != len(tt.expected) { t.Errorf("ParseHash(%s) length = %d, want %d", tt.input, len(result), len(tt.expected)) } for i, b := range result { if i < len(tt.expected) && b != tt.expected[i] { t.Errorf("ParseHash(%s)[%d] = %x, want %x", tt.input, i, b, tt.expected[i]) } } } }) } } func TestExtractHue(t *testing.T) { // Test with a known hash to ensure consistent behavior with JavaScript hash := "da39a3ee5e6b4b0d3255bfef95601890afd80709" hue := ExtractHue(hash) // Verify it's in [0,1] range if hue < 0.0 || hue > 1.0 { t.Errorf("ExtractHue(%s) = %f, want value in [0,1] range", hash, hue) } // Verify it matches manual calculation expectedValue, err := parseHex(hash, -7, 0) if err != nil { t.Fatalf("parseHex failed: %v", err) } expectedHue := float64(expectedValue) / 0xfffffff if hue != expectedHue { t.Errorf("ExtractHue(%s) = %f, want %f", hash, hue, expectedHue) } // Test error cases - should return 0.0 for JavaScript compatibility t.Run("invalid hash", func(t *testing.T) { invalidHash := "invalid-hash-with-non-hex-chars" hue := ExtractHue(invalidHash) if hue != 0.0 { t.Errorf("ExtractHue with invalid hash should return 0.0, got %f", hue) } }) t.Run("short hash", func(t *testing.T) { shortHash := "short" hue := ExtractHue(shortHash) if hue != 0.0 { t.Errorf("ExtractHue with short hash should return 0.0, got %f", hue) } }) } func TestExtractShapeIndex(t *testing.T) { hash := "da39a3ee5e6b4b0d3255bfef95601890afd80709" tests := []struct { position int expected int }{ {1, 0xa}, // 'a' at position 1 {2, 0x3}, // '3' at position 2 {4, 0xa}, // 'a' at position 4 } for _, tt := range tests { result := ExtractShapeIndex(hash, tt.position) if result != tt.expected { t.Errorf("ExtractShapeIndex(%s, %d) = %d, want %d", hash, tt.position, result, tt.expected) } } // Test error cases - should return 0 for JavaScript compatibility t.Run("invalid hash", func(t *testing.T) { invalidHash := "invalid-hash-with-non-hex-chars" result := ExtractShapeIndex(invalidHash, 0) if result != 0 { t.Errorf("ExtractShapeIndex with invalid hash should return 0, got %d", result) } }) t.Run("out of bounds position", func(t *testing.T) { result := ExtractShapeIndex(hash, 100) if result != 0 { t.Errorf("ExtractShapeIndex with out of bounds position should return 0, got %d", result) } }) } func TestExtractColorIndex(t *testing.T) { hash := "da39a3ee5e6b4b0d3255bfef95601890afd80709" // Test modulo behavior availableColors := 5 position := 8 rawValue, err := parseHex(hash, position, 1) if err != nil { t.Fatalf("parseHex failed: %v", err) } expected := rawValue % availableColors result := ExtractColorIndex(hash, position, availableColors) if result != expected { t.Errorf("ExtractColorIndex(%s, %d, %d) = %d, want %d", hash, position, availableColors, result, expected) } // Test with zero availableColors result = ExtractColorIndex(hash, position, 0) if result != rawValue { t.Errorf("ExtractColorIndex(%s, %d, 0) = %d, want %d", hash, position, result, rawValue) } // Test error cases - should return 0 for JavaScript compatibility t.Run("invalid hash", func(t *testing.T) { invalidHash := "invalid-hash-with-non-hex-chars" result := ExtractColorIndex(invalidHash, 0, 5) if result != 0 { t.Errorf("ExtractColorIndex with invalid hash should return 0, got %d", result) } }) t.Run("out of bounds position", func(t *testing.T) { result := ExtractColorIndex(hash, 100, 5) if result != 0 { t.Errorf("ExtractColorIndex with out of bounds position should return 0, got %d", result) } }) } func TestExtractRotation(t *testing.T) { hash := "da39a3ee5e6b4b0d3255bfef95601890afd80709" tests := []struct { position int expected int }{ {0, 0xd}, // 'd' at position 0 {1, 0xa}, // 'a' at position 1 {5, 0x3}, // '3' at position 5 } for _, tt := range tests { result := ExtractRotation(hash, tt.position) if result != tt.expected { t.Errorf("ExtractRotation(%s, %d) = %d, want %d", hash, tt.position, result, tt.expected) } } // Test error cases - should return 0 for JavaScript compatibility t.Run("invalid hash", func(t *testing.T) { invalidHash := "invalid-hash-with-non-hex-chars" result := ExtractRotation(invalidHash, 0) if result != 0 { t.Errorf("ExtractRotation with invalid hash should return 0, got %d", result) } }) t.Run("out of bounds position", func(t *testing.T) { result := ExtractRotation(hash, 100) if result != 0 { t.Errorf("ExtractRotation with out of bounds position should return 0, got %d", result) } }) }