change ids
All checks were successful
Build and Publish / Metadata (push) Successful in 3s
Build and Publish / BuildAndDeployAmd64 (push) Successful in 50s
Build and Publish / BuildAndDeployArm64 (push) Successful in 4m25s

This commit is contained in:
matst80
2025-10-10 21:50:18 +00:00
parent b0e6c8eca8
commit e48a2590bd
13 changed files with 312 additions and 510 deletions

View File

@@ -1,167 +1,155 @@
package main
import (
"crypto/rand"
"encoding/binary"
mrand "math/rand"
"encoding/json"
"fmt"
"testing"
)
// TestEncodeDecodeBase62RoundTrip verifies encodeBase62/decodeBase62 are inverse.
func TestEncodeDecodeBase62RoundTrip(t *testing.T) {
mrand.Seed(42)
for i := 0; i < 1000; i++ {
// Random 64-bit value
v := mrand.Uint64()
s := encodeBase62(v)
dec, ok := decodeBase62(s)
if !ok {
t.Fatalf("decodeBase62 failed for %d encoded=%s", v, s)
}
if dec != v {
t.Fatalf("round trip mismatch: have %d got %d (encoded=%s)", v, dec, s)
}
}
// Explicit zero test
if s := encodeBase62(0); s != "0" {
t.Fatalf("expected encodeBase62(0) == \"0\", got %q", s)
}
if v, ok := decodeBase62("0"); !ok || v != 0 {
t.Fatalf("decodeBase62(0) unexpected result v=%d ok=%v", v, ok)
}
}
// TestNewCartIDUniqueness generates a number of IDs and checks for duplicates.
func TestNewCartIDUniqueness(t *testing.T) {
const n = 10000
// TestNewCartIdUniqueness generates many ids and checks for collisions.
func TestNewCartIdUniqueness(t *testing.T) {
const n = 20000
seen := make(map[string]struct{}, n)
for i := 0; i < n; i++ {
id, err := NewCartID()
id, err := NewCartId()
if err != nil {
t.Fatalf("NewCartID error: %v", err)
t.Fatalf("NewCartId error: %v", err)
}
s := id.String()
if _, exists := seen[s]; exists {
t.Fatalf("duplicate CartID generated: %s", s)
t.Fatalf("duplicate id encountered: %s", s)
}
seen[s] = struct{}{}
if id.IsZero() {
t.Fatalf("NewCartID returned zero value")
if s == "" {
t.Fatalf("empty string representation for id %d", id)
}
if len(s) > 11 {
t.Fatalf("encoded id length exceeds 11 chars: %s (%d)", s, len(s))
}
if id == 0 {
// We force regeneration on zero, extremely unlikely but test guards intent.
t.Fatalf("zero id generated (should be regenerated)")
}
}
}
// TestParseCartIDValidation tests parsing of valid and invalid base62 strings.
func TestParseCartIDValidation(t *testing.T) {
id, err := NewCartID()
if err != nil {
t.Fatalf("NewCartID error: %v", err)
}
parsed, ok := ParseCartID(id.String())
// TestParseCartIdRoundTrip ensures parse -> string -> parse is stable.
func TestParseCartIdRoundTrip(t *testing.T) {
id := MustNewCartId()
txt := id.String()
parsed, ok := ParseCartId(txt)
if !ok {
t.Fatalf("ParseCartID failed for valid id %s", id)
t.Fatalf("ParseCartId failed for valid text %q", txt)
}
if parsed.raw != id.raw {
t.Fatalf("parsed raw mismatch: %d vs %d", parsed.raw, id.raw)
}
if _, ok := ParseCartID(""); ok {
t.Fatalf("expected empty string to be invalid")
}
// Invalid char ('-')
if _, ok := ParseCartID("abc-123"); ok {
t.Fatalf("expected invalid chars to fail parse")
}
// Overly long ( >16 )
if _, ok := ParseCartID("1234567890abcdefg"); ok {
t.Fatalf("expected overly long string to fail parse")
if parsed != id {
t.Fatalf("round trip mismatch: original=%d parsed=%d txt=%s", id, parsed, txt)
}
}
// TestFallbackDeterminism ensures fallback hashing is deterministic.
func TestFallbackDeterminism(t *testing.T) {
inputs := []string{
"legacy-cart-1",
"legacy-cart-2",
"UPPER_lower_123",
"🚀unicode", // unicode bytes (will hash byte sequence)
// TestParseCartIdInvalid covers invalid inputs.
func TestParseCartIdInvalid(t *testing.T) {
invalid := []string{
"", // empty
" ", // space
"01234567890abc", // >11 chars
"!!!!", // invalid chars
"-underscore-", // invalid chars
"abc_def", // underscore invalid for base62
"0123456789ABCD", // 14 chars
}
for _, in := range inputs {
a := FallbackFromString(in)
b := FallbackFromString(in)
if a.raw != b.raw || a.String() != b.String() {
t.Fatalf("fallback mismatch for %q: %+v vs %+v", in, a, b)
for _, s := range invalid {
if _, ok := ParseCartId(s); ok {
t.Fatalf("expected parse failure for %q", s)
}
}
// Distinct inputs should almost always differ; sample check
a := FallbackFromString("distinct-A")
b := FallbackFromString("distinct-B")
if a.raw == b.raw {
t.Fatalf("unexpected identical fallback hashes for distinct inputs")
}
// TestMustParseCartIdPanics verifies panic behavior for invalid input.
func TestMustParseCartIdPanics(t *testing.T) {
defer func() {
if r := recover(); r == nil {
t.Fatalf("expected panic for invalid MustParseCartId input")
}
}()
_ = MustParseCartId("not*base62")
}
// TestJSONMarshalUnmarshalCartId verifies JSON round trip.
func TestJSONMarshalUnmarshalCartId(t *testing.T) {
id := MustNewCartId()
data, err := json.Marshal(struct {
Cart CartId `json:"cart"`
}{Cart: id})
if err != nil {
t.Fatalf("marshal error: %v", err)
}
var out struct {
Cart CartId `json:"cart"`
}
if err := json.Unmarshal(data, &out); err != nil {
t.Fatalf("unmarshal error: %v", err)
}
if out.Cart != id {
t.Fatalf("JSON round trip mismatch: have %d got %d", id, out.Cart)
}
}
// TestCanonicalizeIncomingBehavior covers main control flow branches.
func TestCanonicalizeIncomingBehavior(t *testing.T) {
// Empty => new id
id1, generated, err := CanonicalizeIncoming("")
if err != nil || !generated || id1.IsZero() {
t.Fatalf("CanonicalizeIncoming empty failed: id=%v gen=%v err=%v", id1, generated, err)
// TestBase62LengthBound checks worst-case length (near max uint64).
func TestBase62LengthBound(t *testing.T) {
// Largest uint64
const maxU64 = ^uint64(0)
s := encodeBase62(maxU64)
if len(s) > 11 {
t.Fatalf("max uint64 encoded length > 11: %d (%s)", len(s), s)
}
// Valid base62 => parse; no generation
id2, gen2, err := CanonicalizeIncoming(id1.String())
if err != nil || gen2 || id2.raw != id1.raw {
t.Fatalf("CanonicalizeIncoming parse mismatch: id2=%v gen2=%v err=%v", id2, gen2, err)
}
// Legacy-like random containing invalid chars -> fallback
fallbackInput := "legacy\x00\x00padding"
id3, gen3, err := CanonicalizeIncoming(fallbackInput)
if err != nil || gen3 {
t.Fatalf("CanonicalizeIncoming fallback unexpected: id3=%v gen3=%v err=%v", id3, gen3, err)
}
// Deterministic fallback
id4, _, _ := CanonicalizeIncoming(fallbackInput)
if id3.raw != id4.raw {
t.Fatalf("fallback canonicalization not deterministic")
dec, ok := decodeBase62(s)
if !ok || dec != maxU64 {
t.Fatalf("decode failed for max uint64: ok=%v dec=%d want=%d", ok, dec, maxU64)
}
}
// TestUpgradeLegacyCartId ensures mapping of old CartId is stable.
func TestUpgradeLegacyCartId(t *testing.T) {
var legacy CartId
copy(legacy[:], []byte("legacy-123456789")) // 15 bytes + padding
up1 := UpgradeLegacyCartId(legacy)
up2 := UpgradeLegacyCartId(legacy)
if up1.raw != up2.raw {
t.Fatalf("UpgradeLegacyCartId not deterministic: %v vs %v", up1, up2)
// TestZeroEncoding ensures zero value encodes to "0" and parses back.
func TestZeroEncoding(t *testing.T) {
if s := encodeBase62(0); s != "0" {
t.Fatalf("encodeBase62(0) expected '0', got %q", s)
}
if up1.String() != up2.String() {
t.Fatalf("UpgradeLegacyCartId string mismatch: %s vs %s", up1, up2)
v, ok := decodeBase62("0")
if !ok || v != 0 {
t.Fatalf("decodeBase62('0') failed: ok=%v v=%d", ok, v)
}
if _, ok := ParseCartId("0"); !ok {
t.Fatalf("ParseCartId(\"0\") should succeed")
}
}
// BenchmarkNewCartID gives a rough idea of generation cost.
func BenchmarkNewCartID(b *testing.B) {
// TestSequentialParse ensures sequentially generated ids parse correctly.
func TestSequentialParse(t *testing.T) {
for i := 0; i < 1000; i++ {
id := MustNewCartId()
txt := id.String()
parsed, ok := ParseCartId(txt)
if !ok || parsed != id {
t.Fatalf("sequential parse mismatch: idx=%d orig=%d parsed=%d txt=%s", i, id, parsed, txt)
}
}
}
// BenchmarkNewCartId measures generation performance.
func BenchmarkNewCartId(b *testing.B) {
for i := 0; i < b.N; i++ {
if _, err := NewCartID(); err != nil {
b.Fatalf("error: %v", err)
if _, err := NewCartId(); err != nil {
b.Fatalf("NewCartId error: %v", err)
}
}
}
// BenchmarkEncodeBase62 measures encode speed in isolation.
// BenchmarkEncodeBase62 measures encoding performance.
func BenchmarkEncodeBase62(b *testing.B) {
// Random sample of values
// Precompute sample values
samples := make([]uint64, 1024)
for i := range samples {
var buf [8]byte
if _, err := rand.Read(buf[:]); err != nil {
b.Fatalf("rand: %v", err)
}
samples[i] = binary.BigEndian.Uint64(buf[:])
// Spread bits without crypto randomness overhead
samples[i] = (uint64(i) << 53) ^ (uint64(i) * 0x9E3779B185EBCA87)
}
b.ResetTimer()
var sink string
@@ -171,30 +159,27 @@ func BenchmarkEncodeBase62(b *testing.B) {
_ = sink
}
// BenchmarkDecodeBase62 measures decode speed.
// BenchmarkDecodeBase62 measures decoding performance.
func BenchmarkDecodeBase62(b *testing.B) {
// Pre-encode
encoded := make([]string, 1024)
for i := range encoded {
encoded[i] = encodeBase62(uint64(i)<<32 | uint64(i))
encoded[i] = encodeBase62((uint64(i) << 32) | uint64(i))
}
b.ResetTimer()
var sum uint64
for i := 0; i < b.N; i++ {
v, ok := decodeBase62(encoded[i%len(encoded)])
if !ok {
b.Fatalf("decode failed")
b.Fatalf("decode failure for %s", encoded[i%len(encoded)])
}
sum ^= v
}
_ = sum
}
// Removed TestLookupNDeterminism (ring-based ownership deprecated)
// Removed TestRingFingerprintChanges (ring-based ownership deprecated)
// Removed TestRingDiffHosts (ring-based ownership deprecated)
// TestRingLookupConsistency ensures direct Lookup and LookupID are aligned.
// Removed TestRingLookupConsistency (ring-based ownership deprecated)
// ExampleCartIdString documents usage of CartId string form.
func ExampleCartId_string() {
id := MustNewCartId()
fmt.Println(len(id.String()) <= 11) // outputs true
// Output: true
}