carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package applog
2
3import (
4 "errors"
5 "strings"
6 "testing"
7)
8
9// TestFamilyBuildersRefuseBadSegments: no builder in the apps/<sqid>/<app>/
10// families can produce a key from an empty or invalid account or app, or an
11// empty or slash-bearing box or hour. The empty app is the regression: an
12// app-less route was filed under its account with app "", and the errors
13// shipper wrote apps/bab//errors/..., which hostops.Apps refuses to walk.
14func TestFamilyBuildersRefuseBadSegments(t *testing.T) {
15 type build func(acct, app, box, hour string) (string, error)
16 builders := map[string]build{}
17 for _, fam := range []Family{FamilyLogs, FamilyErrors, FamilyAnalytics} {
18 builders["FamilyPrefix/"+string(fam)] = func(acct, app, _, _ string) (string, error) { return FamilyPrefix(fam, acct, app) }
19 builders["FamilyBoxPrefix/"+string(fam)] = func(acct, app, box, _ string) (string, error) { return FamilyBoxPrefix(fam, acct, app, box) }
20 builders["FamilyHourPrefix/"+string(fam)] = func(acct, app, box, hour string) (string, error) {
21 return FamilyHourPrefix(fam, acct, app, box, hour)
22 }
23 builders["FamilyChunkKey/"+string(fam)] = func(acct, app, box, hour string) (string, error) {
24 return FamilyChunkKey(fam, acct, app, box, hour, 1, 2)
25 }
26 }
27 builders["Prefix"] = func(acct, app, _, _ string) (string, error) { return Prefix(acct, app) }
28 builders["BoxPrefix"] = func(acct, app, box, _ string) (string, error) { return BoxPrefix(acct, app, box) }
29 builders["HourPrefix"] = func(acct, app, box, hour string) (string, error) { return HourPrefix(acct, app, box, hour) }
30 builders["ChunkKey"] = func(acct, app, box, hour string) (string, error) { return ChunkKey(acct, app, box, hour, 1, 2) }
31
32 // Each case breaks exactly one segment. A box or hour case only applies
33 // to builders that take that segment: a prefix above the box level
34 // never sees the box.
35 cases := []struct {
36 name string
37 acct, app, box, hr string
38 needsBox, needsHour bool
39 }{
40 {"empty app", "bab", "", "box1", "2026-10-16-06", false, false},
41 {"empty account", "", "web", "box1", "2026-10-16-06", false, false},
42 {"invalid app", "bab", "Web/x", "box1", "2026-10-16-06", false, false},
43 {"invalid account", "b", "web", "box1", "2026-10-16-06", false, false},
44 {"empty box", "bab", "web", "", "2026-10-16-06", true, false},
45 {"slash in box", "bab", "web", "a/b", "2026-10-16-06", true, false},
46 {"empty hour", "bab", "web", "box1", "", false, true},
47 {"slash in hour", "bab", "web", "box1", "2026/10", false, true},
48 }
49 for name, b := range builders {
50 usesBox := !strings.HasPrefix(name, "FamilyPrefix") && name != "Prefix"
51 usesHour := strings.Contains(name, "Hour") || strings.Contains(name, "ChunkKey")
52 for _, c := range cases {
53 if (c.needsBox && !usesBox) || (c.needsHour && !usesHour) {
54 continue
55 }
56 key, err := b(c.acct, c.app, c.box, c.hr)
57 if !errors.Is(err, ErrBadKeySegment) || key != "" {
58 t.Errorf("%s with %s = (%q, %v), want (\"\", ErrBadKeySegment)", name, c.name, key, err)
59 }
60 }
61 }
62}
63
64// TestFamilyKeysByteIdentical pins every builder's output for valid names:
65// the refusal must not move a single byte of a key that was already good.
66func TestFamilyKeysByteIdentical(t *testing.T) {
67 m := must(t)
68 for _, c := range []struct{ got, want string }{
69 {m(FamilyPrefix(FamilyErrors, "bab", "web")), "apps/bab/web/errors/"},
70 {m(FamilyBoxPrefix(FamilyErrors, "bab", "web", "leo-climbing")), "apps/bab/web/errors/leo-climbing/"},
71 {m(FamilyHourPrefix(FamilyErrors, "bab", "web", "leo-climbing", "2026-10-16-06")), "apps/bab/web/errors/leo-climbing/2026-10-16-06/"},
72 {m(FamilyChunkKey(FamilyErrors, "bab", "web", "leo-climbing", "2026-10-16-06", 1792015319229, 257)), "apps/bab/web/errors/leo-climbing/2026-10-16-06/0001792015319229-257.jsonl.gz"},
73 {m(FamilyPrefix(FamilyAnalytics, "bde", "ledgerpay")), "apps/bde/ledgerpay/analytics/"},
74 {m(FamilyBoxPrefix(FamilyAnalytics, "bde", "ledgerpay", "box-a")), "apps/bde/ledgerpay/analytics/box-a/"},
75 {m(Prefix("bde", "ledgerpay")), "apps/bde/ledgerpay/logs/"},
76 {m(BoxPrefix("bde", "ledgerpay", "box1")), "apps/bde/ledgerpay/logs/box1/"},
77 {m(HourPrefix("bde", "ledgerpay", "box1", "2026-08-10-21")), "apps/bde/ledgerpay/logs/box1/2026-08-10-21/"},
78 {m(ChunkKey("bde", "ledgerpay", "box1", "2026-08-10-21", 41822, 8123)), "apps/bde/ledgerpay/logs/box1/2026-08-10-21/0000000000041822-8123.jsonl.gz"},
79 } {
80 if c.got != c.want {
81 t.Errorf("key = %q, want %q", c.got, c.want)
82 }
83 }
84}
85