carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package console
2
3import (
4 "context"
5 "errors"
6 "os"
7 "strings"
8 "sync/atomic"
9 "testing"
10 "time"
11
12 "github.com/carlosframework/platform/internal/domains"
13 "github.com/carlosframework/platform/internal/plans"
14 "github.com/carlosframework/platform/internal/release"
15)
16
17const exampleCatalogPath = "../plans/testdata/example-catalog.json"
18
19func examplePlanCatalog(t *testing.T) *plans.Catalog {
20 t.Helper()
21 c, err := plans.LoadCatalog(exampleCatalogPath)
22 if err != nil {
23 t.Fatal(err)
24 }
25 return c
26}
27
28// bumpedCatalog is the example catalog with free's running_things set to n
29// under a new version.
30func bumpedCatalog(t *testing.T, version string, n string) *plans.Catalog {
31 t.Helper()
32 b, err := os.ReadFile(exampleCatalogPath)
33 if err != nil {
34 t.Fatal(err)
35 }
36 s := strings.Replace(string(b), `"version": "2026-10-20-v1"`, `"version": "`+version+`"`, 1)
37 s = strings.Replace(s, `"running_things": 3,`, `"running_things": `+n+`,`, 1)
38 c, err := plans.ParseCatalog([]byte(s))
39 if err != nil {
40 t.Fatal(err)
41 }
42 return c
43}
44
45// planAccount adds a console account with a valid sqid and returns it.
46func planAccount(t *testing.T, fx *consoleFixture, id int64) *Account {
47 t.Helper()
48 sqid := domains.Sqid(id)
49 if err := fx.st.CreateAccount(id, sqid, "acct", "owner"+sqid+"@example.com", time.Unix(1_700_000_000, 0)); err != nil {
50 t.Fatal(err)
51 }
52 acct, err := fx.st.AccountBySqid(sqid)
53 if err != nil || acct == nil {
54 t.Fatalf("account %s: %v", sqid, err)
55 }
56 return acct
57}
58
59func readLimits(t *testing.T, fx *consoleFixture, sqid string) (*plans.Record, string) {
60 t.Helper()
61 r, etag, err := plans.GetLimits(context.Background(), fx.rel, sqid)
62 if err != nil {
63 t.Fatal(err)
64 }
65 return r, etag
66}
67
68func TestPlanLimitsPassOffWithoutACatalog(t *testing.T) {
69 fx := newConsoleFixture(t)
70 acct := planAccount(t, fx, 501)
71 fx.srv.planLimitsPass(context.Background(), planNow)
72 if r, _ := readLimits(t, fx, acct.Sqid); r != nil {
73 t.Fatalf("wrote a record with no catalog: %+v", r)
74 }
75 if row, _ := fx.st.AccountPlan(acct.ID); row != nil {
76 t.Fatalf("inserted a plan row with no catalog: %+v", row)
77 }
78}
79
80func TestPlanLimitsPassPublishesDefaults(t *testing.T) {
81 fx := newConsoleFixture(t)
82 fx.srv.cfg.PlanCatalog = examplePlanCatalog(t)
83 acct := planAccount(t, fx, 501)
84 fx.srv.planLimitsPass(context.Background(), planNow)
85 r, _ := readLimits(t, fx, acct.Sqid)
86 if r == nil {
87 t.Fatal("no record published")
88 }
89 if r.Plan != "free" || r.Source != PlanSourceDefault || r.Limits.RunningThings != 3 || r.CatalogID != fx.srv.cfg.PlanCatalog.ID() {
90 t.Fatalf("record %+v", r)
91 }
92 row, _ := fx.st.AccountPlan(acct.ID)
93 if row.PublishedRevision != row.Revision || row.PublishedCatalog != fx.srv.cfg.PlanCatalog.ID() {
94 t.Fatalf("row not marked published: %+v", row)
95 }
96}
97
98func TestPlanLimitsPassRepublishesOnCatalogChange(t *testing.T) {
99 fx := newConsoleFixture(t)
100 fx.srv.cfg.PlanCatalog = examplePlanCatalog(t)
101 acct := planAccount(t, fx, 501)
102 fx.srv.planLimitsPass(context.Background(), planNow)
103 next := bumpedCatalog(t, "2026-10-21-v2", "4")
104 fx.srv.cfg.PlanCatalog = next
105 fx.srv.planLimitsPass(context.Background(), planNow)
106 r, etag := readLimits(t, fx, acct.Sqid)
107 if r.CatalogID != next.ID() || r.Limits.RunningThings != 4 {
108 t.Fatalf("not republished: %+v", r)
109 }
110 fx.srv.planLimitsPass(context.Background(), planNow)
111 if _, again := readLimits(t, fx, acct.Sqid); again != etag {
112 t.Fatal("an up-to-date record was rewritten")
113 }
114}
115
116func TestPlanLimitsPassSkipsAnOrphanedAssignment(t *testing.T) {
117 fx := newConsoleFixture(t)
118 fx.srv.cfg.PlanCatalog = examplePlanCatalog(t)
119 orphan := planAccount(t, fx, 501)
120 other := planAccount(t, fx, 502)
121 fx.srv.planLimitsPass(context.Background(), planNow)
122 _, before := readLimits(t, fx, orphan.Sqid)
123 if _, err := fx.st.SetAccountPlan(orphan.ID, "gold", nil, PlanSourceOperator, nil, "r", "ops@example.com", planNow); err != nil {
124 t.Fatal(err)
125 }
126 if _, err := fx.st.SetAccountPlan(other.ID, "personal", nil, PlanSourceOperator, nil, "r", "ops@example.com", planNow); err != nil {
127 t.Fatal(err)
128 }
129 fx.srv.planLimitsPass(context.Background(), planNow)
130 if _, after := readLimits(t, fx, orphan.Sqid); after != before {
131 t.Fatal("the orphan's last good record was replaced")
132 }
133 if row, _ := fx.st.AccountPlan(orphan.ID); row.PublishedRevision == row.Revision {
134 t.Fatal("the orphan was marked published")
135 }
136 if r, _ := readLimits(t, fx, other.Sqid); r.Plan != "personal" {
137 t.Fatalf("the other account was not published: %+v", r)
138 }
139}
140
141func TestPublishNeverOverwritesANewerRecord(t *testing.T) {
142 fx := newConsoleFixture(t)
143 cat := examplePlanCatalog(t)
144 fx.srv.cfg.PlanCatalog = cat
145 acct := planAccount(t, fx, 501)
146 newer := &plans.Record{Account: acct.Sqid, CatalogID: cat.ID(), Plan: "personal", Source: PlanSourceOperator, Revision: 5}
147 if _, err := plans.PutLimits(context.Background(), fx.rel, newer, ""); err != nil {
148 t.Fatal(err)
149 }
150 for range 3 {
151 if _, err := fx.st.SetAccountPlan(acct.ID, "free", nil, PlanSourceOperator, nil, "r", "ops@example.com", planNow); err != nil {
152 t.Fatal(err)
153 }
154 }
155 row, _ := fx.st.AccountPlan(acct.ID)
156 if err := fx.srv.publishAccountLimits(context.Background(), *row, planNow); err != nil {
157 t.Fatal(err)
158 }
159 if r, _ := readLimits(t, fx, acct.Sqid); r.Revision != 5 || r.Plan != "personal" {
160 t.Fatalf("newer record overwritten: %+v", r)
161 }
162 if row, _ := fx.st.AccountPlan(acct.ID); row.PublishedRevision != 3 {
163 t.Fatalf("published_revision %d, want 3", row.PublishedRevision)
164 }
165}
166
167// planFailingPuts is a bucket whose next n Puts fail.
168type planFailingPuts struct {
169 release.Store
170 n atomic.Int32
171}
172
173func (f *planFailingPuts) Put(ctx context.Context, key string, body []byte, ifMatch string) (string, error) {
174 if f.n.Add(-1) >= 0 {
175 return "", errors.New("bucket unavailable")
176 }
177 return f.Store.Put(ctx, key, body, ifMatch)
178}
179
180func TestPublishRetriesAfterABucketFailure(t *testing.T) {
181 fx := newConsoleFixture(t)
182 fx.srv.cfg.PlanCatalog = examplePlanCatalog(t)
183 acct := planAccount(t, fx, 501)
184 fp := &planFailingPuts{Store: fx.rel}
185 fx.srv.cfg.Releases = fp
186 if _, err := fx.st.EnsureDefaultPlans("free", planNow); err != nil {
187 t.Fatal(err)
188 }
189 fp.n.Store(1000) // every Put fails during the first pass
190 fx.srv.planLimitsPass(context.Background(), planNow)
191 if row, _ := fx.st.AccountPlan(acct.ID); row.PublishedRevision != 0 {
192 t.Fatal("marked published although the write failed")
193 }
194 fp.n.Store(0)
195 fx.srv.planLimitsPass(context.Background(), planNow)
196 if r, _ := readLimits(t, fx, acct.Sqid); r == nil {
197 t.Fatal("not published on the next pass")
198 }
199}
200
201func TestEffectiveLimits(t *testing.T) {
202 fx := newConsoleFixture(t)
203 acct := planAccount(t, fx, 501)
204 if l, err := fx.srv.EffectiveLimits(acct); l != nil || err != nil {
205 t.Fatalf("no catalog: %+v, %v", l, err)
206 }
207 fx.srv.cfg.PlanCatalog = examplePlanCatalog(t)
208 l, err := fx.srv.EffectiveLimits(acct)
209 if err != nil || l == nil || l.RunningThings != 3 {
210 t.Fatalf("default: %+v, %v", l, err)
211 }
212 if _, err := fx.st.SetAccountPlan(acct.ID, "personal", map[string]int{"apps": 1}, PlanSourceOperator, nil, "r", "ops@example.com", planNow); err != nil {
213 t.Fatal(err)
214 }
215 l, err = fx.srv.EffectiveLimits(acct)
216 if err != nil || l.RunningThings != 30 {
217 t.Fatalf("personal + apps: %+v, %v", l, err)
218 }
219}
220
221// Orphaned assignments must not starve the rest: with a batch of two and
222// the two lowest accounts orphaned, one pass still reaches the third.
223func TestPlanLimitsPassOrphansDoNotStarveLaterAccounts(t *testing.T) {
224 fx := newConsoleFixture(t)
225 fx.srv.cfg.PlanCatalog = examplePlanCatalog(t)
226 defer func(n int) { planLimitsPassBatch = n }(planLimitsPassBatch)
227 planLimitsPassBatch = 2
228 a := planAccount(t, fx, 501)
229 b := planAccount(t, fx, 502)
230 c := planAccount(t, fx, 503)
231 for _, acct := range []*Account{a, b} {
232 if _, err := fx.st.SetAccountPlan(acct.ID, "gold", nil, PlanSourceOperator, nil, "r", "ops@example.com", planNow); err != nil {
233 t.Fatal(err)
234 }
235 }
236 fx.srv.planLimitsPass(context.Background(), planNow)
237 if r, _ := readLimits(t, fx, c.Sqid); r == nil {
238 t.Fatal("orphaned accounts starved a later valid account")
239 }
240}
241