package economics import ( "encoding/json" "testing" ) func TestKeys(t *testing.T) { if got := ScenarioKey("launch-pricing"); got != "economics/scenarios/launch-pricing.json" { t.Fatalf("ScenarioKey = %q", got) } if got := BillKey("2026-08"); got != "economics/bills/2026-08.json" { t.Fatalf("BillKey = %q", got) } if ActiveKey != "economics/active.json" { t.Fatalf("ActiveKey = %q", ActiveKey) } } func TestDefaultScenarioValidates(t *testing.T) { sc := DefaultScenario() if err := sc.Validate(); err != nil { t.Fatalf("default scenario invalid: %v", err) } if sc.Mode != "cost-plus" || sc.Margin != 0.8 { t.Fatalf("default = mode %q margin %v", sc.Mode, sc.Margin) } if _, ok := sc.RateCard.Boxes[""]; !ok { t.Fatal("default rate card must carry a \"\" default box spec") } } func TestValidateRejects(t *testing.T) { cases := []func(*Scenario){ func(s *Scenario) { s.Margin = 1.0 }, // price would be infinite func(s *Scenario) { s.Margin = -0.1 }, func(s *Scenario) { s.Mode = "surge" }, func(s *Scenario) { s.Allocation.Policy = "vibes" }, func(s *Scenario) { s.Allocation.Policy = "blend"; s.Allocation.CPUWeight = 1.5 }, func(s *Scenario) { s.Name = "Bad Name!" }, func(s *Scenario) { s.RateCard.Boxes = nil }, } for i, mutate := range cases { sc := DefaultScenario() sc.Name = "test" mutate(&sc) if err := sc.Validate(); err == nil { t.Fatalf("case %d: mutation validated", i) } } } func TestValidScenarioName(t *testing.T) { for name, want := range map[string]bool{ "launch-pricing": true, "v2": true, "": false, "UP": false, "has space": false, "-lead": false, "trail-": false, "waaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaay-too-long": false, } { if got := ValidScenarioName(name); got != want { t.Fatalf("ValidScenarioName(%q) = %v", name, got) } } } func TestDefaultScenarioV2Knobs(t *testing.T) { sc := DefaultScenario() if sc.Sheet.BoxMonthServerUSD != 5 || sc.Sheet.BoxMonthDeviceUSD != 1 { t.Fatalf("box prices = %v/%v", sc.Sheet.BoxMonthServerUSD, sc.Sheet.BoxMonthDeviceUSD) } if sc.FleetBreak.MinBoxes != 10 || sc.FleetBreak.Discount != 0.4 { t.Fatalf("fleet break = %+v", sc.FleetBreak) } if sc.Pool.RevenueFrac != 0.10 || sc.Pool.TopupUSD != 200 { t.Fatalf("pool = %+v", sc.Pool) } if err := sc.Validate(); err != nil { t.Fatal(err) } } func TestValidateRejectsV2(t *testing.T) { cases := []func(*Scenario){ func(s *Scenario) { s.FleetBreak.Discount = 1.0 }, func(s *Scenario) { s.FleetBreak.Discount = -0.1 }, func(s *Scenario) { s.FleetBreak.MinBoxes = -1 }, func(s *Scenario) { s.Pool.RevenueFrac = 1.5 }, func(s *Scenario) { s.Pool.TopupUSD = -5 }, func(s *Scenario) { s.Sheet.MemGBHourUSD = -0.01 }, } for i, mutate := range cases { sc := DefaultScenario() sc.Name = "test" mutate(&sc) if err := sc.Validate(); err == nil { t.Fatalf("case %d validated", i) } } } func TestOldScenarioStillParses(t *testing.T) { 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}` var sc Scenario if err := json.Unmarshal([]byte(old), &sc); err != nil { t.Fatal(err) } if sc.Pool.RevenueFrac != 0 || sc.FleetBreak.MinBoxes != 0 { t.Fatalf("zero-defaults broken: %+v %+v", sc.Pool, sc.FleetBreak) } }