Skip to content

Commit 55b898b

Browse files
Merge branch 'master' into fix/resource-delete-atomic-deprovision-guard-2026-06-04
2 parents 8edd54b + 53e312b commit 55b898b

1 file changed

Lines changed: 67 additions & 0 deletions

File tree

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package handlers
2+
3+
// admin_allowed_tiers_registry_test.go — rule-18 drift guard.
4+
//
5+
// adminAllowedTiers (admin_customers.go) is a hand-maintained closed set of
6+
// the tiers an admin may set on a team. The capabilities-contract regression
7+
// (a hardcoded slice that silently dropped hobby_plus for 3h) is the bug class
8+
// this guards: a NEW tier added to plans.yaml must be consciously filed as
9+
// either admin-settable OR explicitly excluded — never silently forgotten.
10+
//
11+
// Per rule 18 the test iterates the LIVE plans registry rather than a
12+
// hand-typed tier list, so adding a tier to plans.yaml reds CI until someone
13+
// makes the decision.
14+
15+
import (
16+
"testing"
17+
18+
"instant.dev/internal/plans"
19+
)
20+
21+
func TestAdminAllowedTiers_StaysInSyncWithPlanRegistry(t *testing.T) {
22+
// Tiers that are deliberately NOT admin-settable, each with the reason.
23+
// Adding a tier here is the conscious "this is not an admin headline tier"
24+
// decision the guard forces.
25+
adminExcluded := map[string]string{
26+
"anonymous": "unclaimed default; not a settable account tier",
27+
"growth": "upsell-only API tier; admin UI exposes headline tiers only",
28+
"hobby_plus": "upsell-only API tier; admin UI exposes headline tiers only",
29+
"hobby_plus_yearly": "yearly variant of an upsell-only tier",
30+
"hobby_yearly": "yearly billing variant; admin sets the monthly headline tier",
31+
"pro_yearly": "yearly billing variant; admin sets the monthly headline tier",
32+
"team_yearly": "yearly billing variant; admin sets the monthly headline tier",
33+
}
34+
35+
all := plans.Default().All()
36+
if len(all) == 0 {
37+
t.Fatal("plans registry is empty — cannot validate adminAllowedTiers drift")
38+
}
39+
40+
for tier := range all {
41+
settable := adminAllowedTiers[tier]
42+
_, excluded := adminExcluded[tier]
43+
switch {
44+
case settable && excluded:
45+
t.Errorf("tier %q is BOTH admin-settable and in the exclude set — contradiction; "+
46+
"remove it from one (admin_customers.go / this test)", tier)
47+
case !settable && !excluded:
48+
t.Errorf("tier %q exists in plans.yaml but is neither in adminAllowedTiers nor the "+
49+
"documented exclude set — file it on one side (rule 18)", tier)
50+
}
51+
}
52+
53+
// Belt: no stale entry in adminAllowedTiers that no longer exists in the
54+
// registry (a removed/renamed tier left behind).
55+
for tier := range adminAllowedTiers {
56+
if _, ok := all[tier]; !ok {
57+
t.Errorf("adminAllowedTiers contains %q which is not present in plans.yaml", tier)
58+
}
59+
}
60+
// Belt: no stale exclude entry either.
61+
for tier := range adminExcluded {
62+
if _, ok := all[tier]; !ok {
63+
t.Errorf("adminExcluded test set contains %q which is not present in plans.yaml — "+
64+
"drop the stale entry", tier)
65+
}
66+
}
67+
}

0 commit comments

Comments
 (0)