Files
anchorage/internal/pkg/ids/ids_test.go
T
57_Wolve 12bf35caf8 anchorage v1.0 initial tree
Greenfield Go multi-tenant IPFS Pinning Service wire-compatible with the
IPFS Pinning Services API spec. Paired 1:1 with Kubo over localhost RPC,
clustered via embedded NATS JetStream, Postgres source-of-truth with
RLS-enforced tenancy, Fiber + huma v2 for the HTTP surface, Authentik
OIDC for session login with kid-rotated HS256 JWT API tokens.

Feature-complete against the 22-milestone build plan, including the
ship-it v1.0 gap items:

  * admin CLIs: drain/uncordon, maintenance, mint-token, rotate-key,
    prune-denylist, rebalance --dry-run, cache-stats, cluster-presences
  * TTL leader election via NATS KV, fence tokens, JetStream dedup
  * rebalancer (plan/apply split), reconciler, requeue sweeper
  * ristretto caches with NATS-backed cross-node invalidation
    (placements live-nodes + token denylist)
  * maintenance watchdog for stuck cluster-pause flag
  * Prometheus /metrics with CIDR ACL, HTTP/pin/scheduler/cache gauges
  * rate limiting: session (10/min) + anonymous global (120/min)
  * integration tests: rebalance, refcount multi-org, RLS belt
  * goreleaser (tar + deb/rpm/apk + Alpine Docker) targeting Gitea

Stack: Cobra/Viper, Fiber v2 + huma v2, embedded NATS JetStream,
pgx/sqlc/golang-migrate, ristretto, TypeID, prometheus/client_golang,
testcontainers-go.
2026-04-16 18:13:36 -05:00

145 lines
3.8 KiB
Go

package ids_test
import (
"strings"
"testing"
"anchorage/internal/pkg/ids"
)
func TestNewConstructorsReturnCorrectPrefixes(t *testing.T) {
tests := []struct {
name string
gen func(t *testing.T) (string, string)
prefix string
}{
{"org", func(t *testing.T) (string, string) {
id, err := ids.NewOrg()
if err != nil {
t.Fatalf("NewOrg: %v", err)
}
return id.Prefix(), id.String()
}, ids.OrgPrefixToken},
{"user", func(t *testing.T) (string, string) {
id, err := ids.NewUser()
if err != nil {
t.Fatalf("NewUser: %v", err)
}
return id.Prefix(), id.String()
}, ids.UserPrefixToken},
{"pin", func(t *testing.T) (string, string) {
id, err := ids.NewPin()
if err != nil {
t.Fatalf("NewPin: %v", err)
}
return id.Prefix(), id.String()
}, ids.PinPrefixToken},
{"token", func(t *testing.T) (string, string) {
id, err := ids.NewToken()
if err != nil {
t.Fatalf("NewToken: %v", err)
}
return id.Prefix(), id.String()
}, ids.TokenPrefixToken},
{"node", func(t *testing.T) (string, string) {
id, err := ids.NewNode()
if err != nil {
t.Fatalf("NewNode: %v", err)
}
return id.Prefix(), id.String()
}, ids.NodePrefixToken},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
prefix, str := tt.gen(t)
if prefix != tt.prefix {
t.Errorf("Prefix() = %q, want %q", prefix, tt.prefix)
}
if !strings.HasPrefix(str, tt.prefix+"_") {
t.Errorf("String() = %q, want prefix %q", str, tt.prefix+"_")
}
})
}
}
func TestMustConstructorsDoNotPanicInHappyPath(t *testing.T) {
// Smoke-test: they must return the same prefix as the error-returning
// counterpart, and not panic under normal operation.
if p := ids.MustNewOrg().Prefix(); p != ids.OrgPrefixToken {
t.Errorf("MustNewOrg prefix = %q", p)
}
if p := ids.MustNewUser().Prefix(); p != ids.UserPrefixToken {
t.Errorf("MustNewUser prefix = %q", p)
}
if p := ids.MustNewPin().Prefix(); p != ids.PinPrefixToken {
t.Errorf("MustNewPin prefix = %q", p)
}
if p := ids.MustNewToken().Prefix(); p != ids.TokenPrefixToken {
t.Errorf("MustNewToken prefix = %q", p)
}
if p := ids.MustNewNode().Prefix(); p != ids.NodePrefixToken {
t.Errorf("MustNewNode prefix = %q", p)
}
}
func TestIDsAreUniquePerCall(t *testing.T) {
// UUIDv7 suffixes must differ between calls even back-to-back.
a := ids.MustNewPin().String()
b := ids.MustNewPin().String()
if a == b {
t.Fatalf("two consecutive NewPin() calls produced the same id: %s", a)
}
}
func TestIDsAreTimeOrdered(t *testing.T) {
// UUIDv7 is time-ordered, so later-generated IDs should sort after
// earlier ones lexicographically. This is the property that gives
// Postgres index locality on (org_id, id desc).
prev := ids.MustNewPin().String()
for i := 0; i < 32; i++ {
next := ids.MustNewPin().String()
if next <= prev {
t.Fatalf("UUIDv7 ordering violated: %q followed %q", next, prev)
}
prev = next
}
}
func TestParseRoundTrip(t *testing.T) {
orig := ids.MustNewOrg()
parsed, err := ids.ParseOrg(orig.String())
if err != nil {
t.Fatalf("ParseOrg: %v", err)
}
if parsed.String() != orig.String() {
t.Errorf("round-trip changed value: %q -> %q", orig, parsed)
}
}
func TestParseRejectsWrongPrefix(t *testing.T) {
// An org ID string must not parse as a pin ID.
orgStr := ids.MustNewOrg().String()
if _, err := ids.ParsePin(orgStr); err == nil {
t.Errorf("ParsePin accepted an org id %q", orgStr)
}
}
func TestParseRejectsGarbage(t *testing.T) {
tests := []string{
"",
"not-a-typeid",
"org_",
"_01h7rfxv6qefr4twn2jg5p4k3z",
"org_too-short",
"org_01h7rfxv6qefr4twn2jg5p4k3Z", // uppercase base32 char: invalid
}
for _, s := range tests {
t.Run(s, func(t *testing.T) {
if _, err := ids.ParseOrg(s); err == nil {
t.Errorf("ParseOrg(%q) should have failed", s)
}
})
}
}