init
This commit is contained in:
481
jdenticon/hash_test.go
Normal file
481
jdenticon/hash_test.go
Normal file
@@ -0,0 +1,481 @@
|
||||
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)
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user