carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package economics
2
3import (
4 "encoding/json"
5 "testing"
6)
7
8func TestKeys(t *testing.T) {
9 if got := ScenarioKey("launch-pricing"); got != "economics/scenarios/launch-pricing.json" {
10 t.Fatalf("ScenarioKey = %q", got)
11 }
12 if got := BillKey("2026-08"); got != "economics/bills/2026-08.json" {
13 t.Fatalf("BillKey = %q", got)
14 }
15 if ActiveKey != "economics/active.json" {
16 t.Fatalf("ActiveKey = %q", ActiveKey)
17 }
18}
19
20func TestDefaultScenarioValidates(t *testing.T) {
21 sc := DefaultScenario()
22 if err := sc.Validate(); err != nil {
23 t.Fatalf("default scenario invalid: %v", err)
24 }
25 if sc.Mode != "cost-plus" || sc.Margin != 0.8 {
26 t.Fatalf("default = mode %q margin %v", sc.Mode, sc.Margin)
27 }
28 if _, ok := sc.RateCard.Boxes[""]; !ok {
29 t.Fatal("default rate card must carry a \"\" default box spec")
30 }
31}
32
33func TestValidateRejects(t *testing.T) {
34 cases := []func(*Scenario){
35 func(s *Scenario) { s.Margin = 1.0 }, // price would be infinite
36 func(s *Scenario) { s.Margin = -0.1 },
37 func(s *Scenario) { s.Mode = "surge" },
38 func(s *Scenario) { s.Allocation.Policy = "vibes" },
39 func(s *Scenario) { s.Allocation.Policy = "blend"; s.Allocation.CPUWeight = 1.5 },
40 func(s *Scenario) { s.Name = "Bad Name!" },
41 func(s *Scenario) { s.RateCard.Boxes = nil },
42 }
43 for i, mutate := range cases {
44 sc := DefaultScenario()
45 sc.Name = "test"
46 mutate(&sc)
47 if err := sc.Validate(); err == nil {
48 t.Fatalf("case %d: mutation validated", i)
49 }
50 }
51}
52
53func TestValidScenarioName(t *testing.T) {
54 for name, want := range map[string]bool{
55 "launch-pricing": true, "v2": true, "": false, "UP": false,
56 "has space": false, "-lead": false, "trail-": false,
57 "waaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaay-too-long": false,
58 } {
59 if got := ValidScenarioName(name); got != want {
60 t.Fatalf("ValidScenarioName(%q) = %v", name, got)
61 }
62 }
63}
64
65func TestDefaultScenarioV2Knobs(t *testing.T) {
66 sc := DefaultScenario()
67 if sc.Sheet.BoxMonthServerUSD != 5 || sc.Sheet.BoxMonthDeviceUSD != 1 {
68 t.Fatalf("box prices = %v/%v", sc.Sheet.BoxMonthServerUSD, sc.Sheet.BoxMonthDeviceUSD)
69 }
70 if sc.FleetBreak.MinBoxes != 10 || sc.FleetBreak.Discount != 0.4 {
71 t.Fatalf("fleet break = %+v", sc.FleetBreak)
72 }
73 if sc.Pool.RevenueFrac != 0.10 || sc.Pool.TopupUSD != 200 {
74 t.Fatalf("pool = %+v", sc.Pool)
75 }
76 if err := sc.Validate(); err != nil {
77 t.Fatal(err)
78 }
79}
80
81func TestValidateRejectsV2(t *testing.T) {
82 cases := []func(*Scenario){
83 func(s *Scenario) { s.FleetBreak.Discount = 1.0 },
84 func(s *Scenario) { s.FleetBreak.Discount = -0.1 },
85 func(s *Scenario) { s.FleetBreak.MinBoxes = -1 },
86 func(s *Scenario) { s.Pool.RevenueFrac = 1.5 },
87 func(s *Scenario) { s.Pool.TopupUSD = -5 },
88 func(s *Scenario) { s.Sheet.MemGBHourUSD = -0.01 },
89 }
90 for i, mutate := range cases {
91 sc := DefaultScenario()
92 sc.Name = "test"
93 mutate(&sc)
94 if err := sc.Validate(); err == nil {
95 t.Fatalf("case %d validated", i)
96 }
97 }
98}
99
100func TestOldScenarioStillParses(t *testing.T) {
101 old := `{"schema":1,"name":"x","rate_card":{"boxes":{"":{"hour_usd":0.02,"cores":2,"mem_gb":2}}},"allocation":{"policy":"cpu"},"mode":"cost-plus","margin":0.8}`
102 var sc Scenario
103 if err := json.Unmarshal([]byte(old), &sc); err != nil {
104 t.Fatal(err)
105 }
106 if sc.Pool.RevenueFrac != 0 || sc.FleetBreak.MinBoxes != 0 {
107 t.Fatalf("zero-defaults broken: %+v %+v", sc.Pool, sc.FleetBreak)
108 }
109}
110