Files
BountyBoard/internal/ulid/ulid_test.go
T
etalon 976f5238f8 feat: scaffold app skeleton with config, ULID, health endpoints, compose stack (phase 1)
- internal .env loader (real env wins) + strictly validated config struct
- ULID package: 48-bit ms timestamp + 80-bit crypto randomness, sortable
- HTTP server with recovery/request-id/trusted-proxy/access-log middleware
- /healthz, /readyz (pluggable checkers), /metricsz (counter registry)
- multi-stage Dockerfile (alpine, non-root) + compose with healthchecks,
  app bound to 127.0.0.1:8787, mongo unpublished, graceful 15s shutdown

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 18:10:30 +02:00

93 lines
2.1 KiB
Go

package ulid
import (
"sort"
"strings"
"testing"
"time"
)
func TestNewShape(t *testing.T) {
id := New()
if len(id) != Len {
t.Fatalf("len = %d, want %d", len(id), Len)
}
for i := 0; i < len(id); i++ {
if !strings.ContainsRune(alphabet, rune(id[i])) {
t.Fatalf("char %q at %d not in Crockford alphabet", id[i], i)
}
}
if !IsValid(id) {
t.Fatalf("IsValid(%q) = false", id)
}
}
func TestUniqueness(t *testing.T) {
const n = 10000
seen := make(map[string]struct{}, n)
for i := 0; i < n; i++ {
id := New()
if _, dup := seen[id]; dup {
t.Fatalf("duplicate ULID %q after %d ids", id, i)
}
seen[id] = struct{}{}
}
}
func TestLexicographicTimeOrdering(t *testing.T) {
base := time.Now()
var ids []string
for i := 0; i < 50; i++ {
ids = append(ids, At(base.Add(time.Duration(i)*time.Millisecond)))
}
if !sort.StringsAreSorted(ids) {
t.Fatal("ULIDs with increasing timestamps are not lexicographically sorted")
}
}
func TestTimestampRoundTrip(t *testing.T) {
tests := []time.Time{
time.UnixMilli(0),
time.Date(2026, 6, 12, 12, 0, 0, 0, time.UTC),
time.Now(),
}
for _, want := range tests {
id := At(want)
got, err := Timestamp(id)
if err != nil {
t.Fatalf("Timestamp(%q): %v", id, err)
}
if got.UnixMilli() != want.UnixMilli() {
t.Errorf("timestamp = %v, want %v", got.UnixMilli(), want.UnixMilli())
}
}
}
func TestIsValid(t *testing.T) {
tests := []struct {
in string
want bool
}{
{New(), true},
{strings.ToLower(New()), true}, // Crockford decoding is case-insensitive
{"", false},
{"short", false},
{strings.Repeat("0", 25), false},
{strings.Repeat("0", 27), false},
{strings.Repeat("U", 26), false}, // U not in alphabet
{"8" + strings.Repeat("0", 25), false}, // timestamp overflow (>48 bits)
{"7" + strings.Repeat("Z", 25), true},
}
for _, tt := range tests {
if got := IsValid(tt.in); got != tt.want {
t.Errorf("IsValid(%q) = %v, want %v", tt.in, got, tt.want)
}
}
}
func TestTimestampRejectsInvalid(t *testing.T) {
if _, err := Timestamp("definitely-not-a-ulid!!!!!"); err == nil {
t.Fatal("want error")
}
}