From 13d844c130b7d55c8c598842de59391d9c66bc67 Mon Sep 17 00:00:00 2001 From: Ethan Werner Date: Tue, 30 Jun 2026 00:29:48 +0400 Subject: initial commit --- snowflake_test.go | 379 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 379 insertions(+) create mode 100644 snowflake_test.go (limited to 'snowflake_test.go') diff --git a/snowflake_test.go b/snowflake_test.go new file mode 100644 index 0000000..e8c5e7c --- /dev/null +++ b/snowflake_test.go @@ -0,0 +1,379 @@ +package snowflake + +import ( + "encoding/json" + "testing" +) + +func TestNew(t *testing.T) { + s, err := New() + if err != nil { + t.Fatalf("New() returned error: %v", err) + } + if s.IsZero() { + t.Error("New() returned zero Snowflake") + } +} + +func TestNewUnique(t *testing.T) { + seen := make(map[string]bool, 1000) + + for i := 0; i < 1000; i++ { + s, err := New() + if err != nil { + t.Fatalf("New() returned error: %v", err) + } + str := s.String() + if seen[str] { + t.Fatalf("duplicate ID generated: %s", str) + } + seen[str] = true + } +} + +func TestIsZero(t *testing.T) { + zero := Zero() + if !zero.IsZero() { + t.Error("Zero Snowflake should be zero") + } + + nonZero, err := New() + if err != nil { + t.Fatalf("New() returned error: %v", err) + } + if nonZero.IsZero() { + t.Error("New Snowflake should not be zero") + } +} + +func TestStringRoundTrip(t *testing.T) { + original, err := New() + if err != nil { + t.Fatalf("New() returned error: %v", err) + } + str := original.String() + parsed, err := FromString(str) + if err != nil { + t.Fatalf("FromString failed: %v", err) + } + + if parsed != original { + t.Errorf("round-trip mismatch: original=%+v, parsed=%+v", original, parsed) + } +} + +func TestStringRoundTripMany(t *testing.T) { + for i := 0; i < 100; i++ { + original, err := New() + if err != nil { + t.Fatalf("New() returned error: %v", err) + } + str := original.String() + parsed, err := FromString(str) + if err != nil { + t.Fatalf("iteration %d: FromString failed: %v", i, err) + } + if parsed != original { + t.Fatalf("iteration %d: round-trip mismatch", i) + } + } +} + +func TestFromStringInvalidChars(t *testing.T) { + _, err := FromString("!!invalid!!") + if err != ErrInvalidEncoding { + t.Errorf("expected ErrInvalidEncoding, got: %v", err) + } +} + +func TestFromStringEmpty(t *testing.T) { + s, err := FromString("") + if err != nil { + t.Errorf("unexpected error for empty string: %v", err) + } + if !s.IsZero() { + t.Error("empty string should produce zero Snowflake") + } +} + +func TestFromStringTooLong(t *testing.T) { + _, err := FromString("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") + if err != ErrInvalidEncoding { + t.Errorf("expected ErrInvalidEncoding for overflow, got: %v", err) + } +} + +func TestHexString(t *testing.T) { + s, err := New() + if err != nil { + t.Fatalf("New() returned error: %v", err) + } + hex := s.Hex() + + if len(hex) != IDByteSize*2 { + t.Errorf("expected hex length %d, got %d", IDByteSize*2, len(hex)) + } + + for _, c := range hex { + if !((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f')) { + t.Errorf("invalid hex character: %c", c) + } + } +} + +func TestZeroString(t *testing.T) { + zero := Zero() + str := zero.String() + if str != "0" { + t.Errorf("Zero Snowflake should stringify to %q, got: %q", "0", str) + } + + parsed, err := FromString(str) + if err != nil { + t.Fatalf("FromString(%q) failed: %v", str, err) + } + if !parsed.IsZero() { + t.Error("FromString(Zero().String()) should round-trip to zero") + } +} + +func TestHexRoundTrip(t *testing.T) { + original, err := New() + if err != nil { + t.Fatalf("New() returned error: %v", err) + } + hex := original.Hex() + parsed, err := FromHex(hex) + if err != nil { + t.Fatalf("FromHex failed: %v", err) + } + if parsed != original { + t.Error("hex round-trip mismatch") + } +} + +func TestFromHexInvalid(t *testing.T) { + _, err := FromHex("xyz") + if err == nil { + t.Error("expected error for invalid hex") + } +} + +func TestFromHexWrongLength(t *testing.T) { + _, err := FromHex("01") + if err != ErrInvalidIDLength { + t.Errorf("expected ErrInvalidIDLength, got: %v", err) + } +} + +func TestMarshalText(t *testing.T) { + s, err := New() + if err != nil { + t.Fatalf("New() returned error: %v", err) + } + text, err := s.MarshalText() + if err != nil { + t.Fatalf("MarshalText failed: %v", err) + } + + var decoded Snowflake + err = decoded.UnmarshalText(text) + if err != nil { + t.Fatalf("UnmarshalText failed: %v", err) + } + + if decoded != s { + t.Error("text marshal/unmarshal mismatch") + } +} + +func TestUnmarshalTextInvalid(t *testing.T) { + var s Snowflake + err := s.UnmarshalText([]byte("!!!")) + if err == nil { + t.Error("expected error for invalid text") + } +} + +func TestMarshalJSON(t *testing.T) { + s, err := New() + if err != nil { + t.Fatalf("New() returned error: %v", err) + } + data, err := s.MarshalJSON() + if err != nil { + t.Fatalf("MarshalJSON failed: %v", err) + } + + var decoded Snowflake + err = json.Unmarshal(data, &decoded) + if err != nil { + t.Fatalf("json.Unmarshal failed: %v", err) + } + + if decoded != s { + t.Error("JSON marshal/unmarshal mismatch") + } +} + +func TestUnmarshalJSONMissingQuotes(t *testing.T) { + var s Snowflake + err := s.UnmarshalJSON([]byte(`invalid`)) + if err != ErrInvalidEncoding { + t.Errorf("expected ErrInvalidEncoding, got: %v", err) + } +} + +func TestUnmarshalJSONEmpty(t *testing.T) { + var s Snowflake + err := s.UnmarshalJSON([]byte(`""`)) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if !s.IsZero() { + t.Error("empty JSON string should produce zero Snowflake") + } +} + +func TestUnmarshalJSONInvalidInner(t *testing.T) { + var s Snowflake + err := s.UnmarshalJSON([]byte(`"!!!"`)) + if err != ErrInvalidEncoding { + t.Errorf("expected ErrInvalidEncoding for invalid inner content, got: %v", err) + } +} + +func TestDriverValue(t *testing.T) { + s, err := New() + if err != nil { + t.Fatalf("New() returned error: %v", err) + } + val, err := s.Value() + if err != nil { + t.Fatalf("Value failed: %v", err) + } + + bytes, ok := val.([]byte) + if !ok { + t.Fatal("Value should return []byte") + } + + if len(bytes) != IDByteSize { + t.Errorf("expected byte length %d, got %d", IDByteSize, len(bytes)) + } +} + +func TestDriverScan(t *testing.T) { + original, err := New() + if err != nil { + t.Fatalf("New() returned error: %v", err) + } + bytes := make([]byte, IDByteSize) + copy(bytes, original.bytes[:]) + + var s Snowflake + err = s.Scan(bytes) + if err != nil { + t.Fatalf("Scan failed: %v", err) + } + + if s != original { + t.Error("Scan produced mismatched value") + } +} + +func TestDriverScanString(t *testing.T) { + original, err := New() + if err != nil { + t.Fatalf("New() returned error: %v", err) + } + bytes := make([]byte, IDByteSize) + copy(bytes, original.bytes[:]) + + var s Snowflake + err = s.Scan(string(bytes)) + if err != nil { + t.Fatalf("Scan(string) failed: %v", err) + } + + if s != original { + t.Error("Scan(string) produced mismatched value") + } +} + +func TestDriverScanNil(t *testing.T) { + var s Snowflake + err := s.Scan(nil) + if err != nil { + t.Fatalf("Scan(nil) failed: %v", err) + } + + if !s.IsZero() { + t.Error("Scan(nil) should produce zero Snowflake") + } +} + +func TestDriverScanWrongLength(t *testing.T) { + var s Snowflake + err := s.Scan([]byte{1, 2, 3}) + if err != ErrInvalidIDLength { + t.Errorf("expected ErrInvalidIDLength, got: %v", err) + } +} + +func TestDriverScanZeroBytes(t *testing.T) { + var s Snowflake + zeroBytes := make([]byte, IDByteSize) + err := s.Scan(zeroBytes) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if !s.IsZero() { + t.Error("Scan with all-zero bytes should produce zero Snowflake") + } +} + +func TestDriverScanUnsupportedType(t *testing.T) { + var s Snowflake + err := s.Scan(42) + if err == nil { + t.Error("expected error for unsupported type") + } +} + +func TestJSONEmbedding(t *testing.T) { + type Wrapper struct { + Name string `json:"name"` + ID Snowflake `json:"id"` + Ref *Snowflake `json:"ref,omitempty"` + } + + s, err := New() + if err != nil { + t.Fatalf("New() returned error: %v", err) + } + w := Wrapper{ + Name: "test", + ID: s, + Ref: &s, + } + + data, err := json.Marshal(&w) + if err != nil { + t.Fatalf("marshal failed: %v", err) + } + + var decoded Wrapper + err = json.Unmarshal(data, &decoded) + if err != nil { + t.Fatalf("unmarshal failed: %v", err) + } + + if decoded.ID != s { + t.Error("embedded ID mismatch") + } + + if decoded.Ref == nil || *decoded.Ref != s { + t.Error("embedded ref mismatch") + } +} -- cgit v1.3.1