carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1package domains_test
2
3import (
4 "strings"
5 "testing"
6
7 "github.com/carlosframework/platform/internal/domains"
8 "github.com/carlosframework/platform/internal/release"
9)
10
11func TestSqidGolden(t *testing.T) {
12 // FROZEN 2026-08-02 (first green run of the id+23² base-23 scheme). If
13 // this test fails you are re-homing every platform domain ever minted —
14 // ops's sqid is baked into every <app>.bab.<apps-domain> URL in the
15 // wild. Do not "fix" the expectations; fix your code.
16 golden := map[int64]string{
17 1: "bab", // ops — the built-in account, on every deployment
18 2: "bac",
19 23: "bba",
20 24: "bbb",
21 529: "caa",
22 1000: "cxn",
23 }
24 goldenSeen := map[string]int64{}
25 for id, want := range golden {
26 s := domains.Sqid(id)
27 if s != want {
28 t.Errorf("Sqid(%d) = %q, want frozen %q", id, s, want)
29 }
30 if len(s) < 3 {
31 t.Errorf("Sqid(%d)=%q shorter than 3", id, s)
32 }
33 for _, c := range s {
34 if !strings.ContainsRune("abcdefghjkmnpqrstuvwxyz", c) {
35 t.Errorf("Sqid(%d)=%q uses %q outside the frozen alphabet", id, s, c)
36 }
37 }
38 if prev, dup := goldenSeen[s]; dup {
39 t.Errorf("collision: %d and %d both %q", prev, id, s)
40 }
41 goldenSeen[s] = id
42 }
43}
44
45// TestValidSqid pins the predicate the CLI's -account-id gate rests on. The
46// case that matters is the i/l/o rows: release.ValidAccount (^[a-z]{3,16}$)
47// accepts every one of them, because it gates bucket path segments and every
48// account NAME is a legal segment. ValidSqid must not — "ops" passing here
49// is `carlos ship -account-id ops` writing to apps/ops/..., exiting 0, and
50// serving nothing, when the real ops account is Sqid(1) = "bab".
51func TestValidSqid(t *testing.T) {
52 for _, tc := range []struct {
53 s string
54 want bool
55 }{
56 {"bab", true}, // ops, the built-in account
57 {"zzz", true}, // never minted here, but shaped like something Sqid emits
58 {"baf", true}, //
59 {"abc", true}, // the 3-char floor sqidOffset guarantees
60 {"abcdefghjkmnpqrs", true}, // 16, Sqid's buffer ceiling
61
62 // The lookalikes the alphabet drops. Each of these passes
63 // release.ValidAccount and is therefore exactly the bug.
64 {"ops", false}, // o
65 {"oil", false}, // o, i, l
66 {"ill", false}, // i, l
67 {"kass", true}, // a real account NAME that IS sqid-shaped: see below
68 {"amadan", true},
69
70 {"ab", false}, // under the 3 floor
71 {"abcdefghjkmnpqrst", false}, // over the 16 ceiling
72 {"", false}, //
73 {"BAB", false}, // uppercase is not the alphabet
74 {"ba1", false}, // digits are not the alphabet
75 {"ba-b", false}, // nor is a hyphen
76 {"ba b", false}, // nor a space (callers must trim first)
77 } {
78 if got := domains.ValidSqid(tc.s); got != tc.want {
79 t.Errorf("ValidSqid(%q) = %v, want %v", tc.s, got, tc.want)
80 }
81 }
82
83 // The honest limit of a shape check, stated so nobody mistakes it for
84 // more: a NAME made only of alphabet letters ("kass", "amadan") is
85 // indistinguishable from a sqid by shape alone, and passes. -account-id
86 // is matched exactly against the account space, so such a value simply
87 // resolves to nothing rather than to the wrong account — the failure is
88 // a 404, not a silent write under a phantom account. It is the i/l/o
89 // names, and only those, that a shape check can catch for free.
90
91 // Every sqid the encoder can actually emit must satisfy the predicate:
92 // the two are frozen together, and this is the direction that would
93 // break first if the alphabet or the buffer ever changed.
94 for id := int64(1); id < 20000; id++ {
95 if s := domains.Sqid(id); !domains.ValidSqid(s) {
96 t.Fatalf("Sqid(%d) = %q, which ValidSqid rejects", id, s)
97 }
98 }
99}
100
101// The frozen alphabet omits i, l and o, so no id encodes to "legacy" — which is
102// what lets release.LegacySource use that word as a sentinel for the
103// account-less key space in `carlos accounts migrate -from legacy`. If the
104// alphabet ever gains an "l", the sentinel silently becomes a real account's
105// sqid and the verb starts reading one as the other. Fail here first.
106//
107// The emitted character set is derived from Sqid itself rather than read off
108// the constant: this test lives outside the package, and asserting on what the
109// encoder actually produces is the stronger check anyway.
110func TestLegacySourceIsNotASqid(t *testing.T) {
111 emitted := map[rune]bool{}
112 for id := int64(1); id < 20000; id++ {
113 for _, r := range domains.Sqid(id) {
114 emitted[r] = true
115 }
116 }
117 if len(emitted) != 23 {
118 t.Fatalf("saw %d distinct characters over 20k ids, want the full 23 — widen the range", len(emitted))
119 }
120 for _, r := range release.LegacySource {
121 if !emitted[r] {
122 return // a character the encoder can never emit: no id can collide
123 }
124 }
125 t.Fatalf("every character of %q is one the sqid encoder emits — the sentinel can collide with a real account sqid",
126 release.LegacySource)
127}
128