package core import ( "bytes" "encoding/json" "testing" "time" ) func TestNewID(t *testing.T) { id := NewID() if id.IsZero() { t.Error("NewID returned zero ID") } } func TestIDUniqueness(t *testing.T) { // Generate multiple IDs and ensure they're unique ids := make(map[ID]bool) for i := 0; i < 1000; i++ { id := NewID() if ids[id] { t.Errorf("Duplicate ID generated: %s", id) } ids[id] = true } } func TestZeroID(t *testing.T) { zero := ID{} if !zero.IsZero() { t.Error("ZeroID is not zero") } id := NewID() if id.IsZero() { t.Error("NewID returned zero ID") } } func TestStringEncoding(t *testing.T) { id := NewID() s := id.String() // Check length if len(s) != 26 { t.Errorf("String length = %d, want 26", len(s)) } // Check all characters are in alphabet for _, c := range s { if !bytes.ContainsRune([]byte(alphabet), c) { t.Errorf("Invalid character in ID string: %c", c) } } } func TestParseID(t *testing.T) { original := NewID() s := original.String() parsed, err := ParseID(s) if err != nil { t.Fatalf("ParseID failed: %v", err) } if !parsed.Equal(original) { t.Errorf("Parsed ID != original. Got %s, want %s", parsed, original) } } func TestParseIDInvalid(t *testing.T) { tests := []struct { name string input string }{ {"too short", "12345"}, {"too long", "12345678901234567890123456789"}, {"invalid char", "IIIIIIIIIIIIIIIIIIIIIIIIII"}, // I not in alphabet {"invalid char", "LLLLLLLLLLLLLLLLLLLLLLLLLL"}, // L not in alphabet {"invalid char", "OOOOOOOOOOOOOOOOOOOOOOOOOO"}, // O not in alphabet {"invalid char", "UUUUUUUUUUUUUUUUUUUUUUUUUU"}, // U not in alphabet } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { _, err := ParseID(tt.input) if err == nil { t.Error("Expected error, got nil") } }) } } func TestEqual(t *testing.T) { id1 := NewID() id2 := NewID() if id1.Equal(id2) { t.Error("Different IDs should not be equal") } if !id1.Equal(id1) { t.Error("ID should equal itself") } // Test via string round-trip parsed, _ := ParseID(id1.String()) if !id1.Equal(parsed) { t.Error("ID should equal parsed version") } } func TestCompare(t *testing.T) { id1 := NewID() time.Sleep(2 * time.Millisecond) // Ensure different timestamp id2 := NewID() // id1 should be less than id2 (earlier timestamp) if id1.Compare(id2) != -1 { t.Error("Earlier ID should be less than later ID") } if id2.Compare(id1) != 1 { t.Error("Later ID should be greater than earlier ID") } if id1.Compare(id1) != 0 { t.Error("ID should be equal to itself") } } func TestMarshalBinary(t *testing.T) { id := NewID() data, err := id.MarshalBinary() if err != nil { t.Fatalf("MarshalBinary failed: %v", err) } if len(data) != 16 { t.Errorf("Binary length = %d, want 16", len(data)) } // Unmarshal and compare var decoded ID if err := decoded.UnmarshalBinary(data); err != nil { t.Fatalf("UnmarshalBinary failed: %v", err) } if !decoded.Equal(id) { t.Error("Unmarshaled ID != original") } } func TestMarshalText(t *testing.T) { id := NewID() text, err := id.MarshalText() if err != nil { t.Fatalf("MarshalText failed: %v", err) } if len(text) != 26 { t.Errorf("Text length = %d, want 26", len(text)) } // Unmarshal and compare var decoded ID if err := decoded.UnmarshalText(text); err != nil { t.Fatalf("UnmarshalText failed: %v", err) } if !decoded.Equal(id) { t.Error("Unmarshaled ID != original") } } func TestJSON(t *testing.T) { type testStruct struct { ID ID `json:"id"` } original := testStruct{ID: NewID()} data, err := json.Marshal(original) if err != nil { t.Fatalf("JSON marshal failed: %v", err) } var decoded testStruct if err := json.Unmarshal(data, &decoded); err != nil { t.Fatalf("JSON unmarshal failed: %v", err) } if !decoded.ID.Equal(original.ID) { t.Error("JSON round-trip changed ID") } } func TestSQLInterfaces(t *testing.T) { id := NewID() // Test Value (driver.Valuer) val, err := id.Value() if err != nil { t.Fatalf("Value failed: %v", err) } str, ok := val.(string) if !ok { t.Fatalf("Value returned %T, want string", val) } if len(str) != 26 { t.Errorf("Value length = %d, want 26", len(str)) } // Test Scan (sql.Scanner) with string var scanned ID if err := scanned.Scan(str); err != nil { t.Fatalf("Scan string failed: %v", err) } if !scanned.Equal(id) { t.Error("Scanned ID != original") } // Test Scan with []byte var scanned2 ID if err := scanned2.Scan([]byte(str)); err != nil { t.Fatalf("Scan []byte failed: %v", err) } if !scanned2.Equal(id) { t.Error("Scanned ID != original") } } func TestScanInvalid(t *testing.T) { tests := []struct { name string input interface{} }{ {"nil", nil}, {"int", 42}, {"invalid string", "not-valid-id-string"}, {"wrong length", "12345"}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { var id ID err := id.Scan(tt.input) if err == nil { t.Error("Expected error, got nil") } }) } } func TestTimestampOrdering(t *testing.T) { // Generate IDs with delays to ensure timestamp ordering var ids []ID for i := 0; i < 10; i++ { ids = append(ids, NewID()) time.Sleep(1 * time.Millisecond) } // Verify they're in order for i := 1; i < len(ids); i++ { if ids[i-1].Compare(ids[i]) != -1 { t.Errorf("ID %d not less than ID %d", i-1, i) } } } func BenchmarkNewID(b *testing.B) { for i := 0; i < b.N; i++ { _ = NewID() } } func BenchmarkString(b *testing.B) { id := NewID() b.ResetTimer() for i := 0; i < b.N; i++ { _ = id.String() } } func BenchmarkParseID(b *testing.B) { id := NewID() s := id.String() b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = ParseID(s) } } func BenchmarkCompare(b *testing.B) { id1 := NewID() id2 := NewID() b.ResetTimer() for i := 0; i < b.N; i++ { _ = id1.Compare(id2) } }