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 "math"
5 "testing"
6
7 "github.com/carlosframework/platform/internal/usage"
8)
9
10func approx(t *testing.T, name string, got, want float64) {
11 t.Helper()
12 if math.Abs(got-want) > 1e-9 {
13 t.Fatalf("%s = %v, want %v", name, got, want)
14 }
15}
16
17// A 2-core/2GB box at $0.10/h with no EBS, 100h period, one tenant using
18// 50 core-hours (25% of 200 available) and 100 GB-hours (50% of 200).
19func testBox() (RateCard, BoxLoad) {
20 rc := RateCard{Boxes: map[string]BoxSpec{"": {HourUSD: 0.10, Cores: 2, MemGB: 2}}}
21 b := BoxLoad{Box: "box1", Hours: 100, ByAccount: map[string]BoxAccountLoad{
22 "bab": {CPUCoreHours: 50, MemGBHours: 100},
23 }}
24 return rc, b
25}
26
27func TestAllocateBoxPolicies(t *testing.T) {
28 rc, b := testBox()
29 per, idle, total := AllocateBox(rc, Allocation{Policy: "cpu"}, b)
30 approx(t, "total", total, 10.0)
31 approx(t, "cpu share", per["bab"], 2.5) // 25% of $10
32 approx(t, "cpu idle", idle, 7.5)
33
34 per, idle, _ = AllocateBox(rc, Allocation{Policy: "mem"}, b)
35 approx(t, "mem share", per["bab"], 5.0) // 50% of $10
36 approx(t, "mem idle", idle, 5.0)
37
38 per, idle, _ = AllocateBox(rc, Allocation{Policy: "blend", CPUWeight: 0.5}, b)
39 approx(t, "blend share", per["bab"], 3.75) // (0.5*25% + 0.5*50%) of $10
40 approx(t, "blend idle", idle, 6.25)
41}
42
43func TestAllocateBoxEBSAndFallbackSpec(t *testing.T) {
44 rc := RateCard{
45 Boxes: map[string]BoxSpec{"": {HourUSD: 0.10, Cores: 2, MemGB: 2, EBSGB: 73}},
46 EBSGBMonthUSD: 0.10,
47 }
48 b := BoxLoad{Box: "unlisted-box", Hours: 730, ByAccount: nil}
49 _, idle, total := AllocateBox(rc, Allocation{Policy: "cpu"}, b)
50 // 730h of $0.10 = $73, plus 73GB*$0.10/GB-month * (730/730) = $7.30.
51 approx(t, "total", total, 80.3)
52 approx(t, "all idle", idle, 80.3)
53}
54
55func TestPriceAccountCostPlusEightyMarginIsFiveX(t *testing.T) {
56 sc := DefaultScenario()
57 sc.RateCard.EgressGBUSD = 0.10
58 sc.RateCard.RequestsPer1kUSD = 0
59 sc.RateCard.StorageGBMonthUSD = 0
60 sc.FreeTier = FreeTier{} // no free tier: pure margin check
61 meters := map[string]float64{usage.MeterEgressGB: 10} // $1 of egress cost
62 ae := PriceAccount(sc, "bab", meters, 0)
63 approx(t, "cost", ae.CostUSD, 1.0)
64 approx(t, "revenue", ae.RevenueUSD, 5.0) // 80% margin = 5x cost
65 approx(t, "margin", ae.MarginFrac, 0.8)
66 if ae.FreeTierPos != "paying" {
67 t.Fatalf("pos = %q", ae.FreeTierPos)
68 }
69}
70
71func TestPriceAccountFreeTierSubtraction(t *testing.T) {
72 sc := DefaultScenario()
73 sc.Mode = "rate-sheet"
74 sc.Sheet = RateSheet{AppMonthUSD: 5, EgressGBUSD: 0.15}
75 sc.FreeTier = FreeTier{Apps: 1, EgressGB: 10}
76 meters := map[string]float64{usage.MeterAppMonths: 2, usage.MeterEgressGB: 14}
77 ae := PriceAccount(sc, "bab", meters, 0)
78 // Billable: 1 app-month, 4 GB egress = $5 + $0.60.
79 approx(t, "revenue", ae.RevenueUSD, 5.6)
80 if ae.FreeTierPos != "partial" {
81 t.Fatalf("pos = %q", ae.FreeTierPos)
82 }
83
84 inside := PriceAccount(sc, "tiny", map[string]float64{usage.MeterAppMonths: 1, usage.MeterEgressGB: 2}, 0)
85 approx(t, "inside revenue", inside.RevenueUSD, 0)
86 if inside.FreeTierPos != "inside" {
87 t.Fatalf("inside pos = %q", inside.FreeTierPos)
88 }
89}
90
91func TestPriceAccountComputeFraction(t *testing.T) {
92 sc := DefaultScenario()
93 sc.Mode = "cost-plus"
94 sc.Margin = 0.5
95 sc.RateCard.EgressGBUSD, sc.RateCard.RequestsPer1kUSD, sc.RateCard.StorageGBMonthUSD = 0, 0, 0
96 sc.FreeTier = FreeTier{MemGBHours: 100}
97 meters := map[string]float64{usage.MeterMemGBHours: 400}
98 // $8 allocated, 300/400 billable -> $6 billable cost -> $12 revenue at 50%.
99 ae := PriceAccount(sc, "bab", meters, 8)
100 approx(t, "cost", ae.CostUSD, 8)
101 approx(t, "revenue", ae.RevenueUSD, 12)
102}
103
104func TestProject(t *testing.T) {
105 approx(t, "project", Project(10, 10, 30), 30)
106 approx(t, "zero covered", Project(10, 0, 30), 0)
107}
108
109func TestPriceAccountFleetBoxes(t *testing.T) {
110 sc := DefaultScenario()
111 sc.Mode = "rate-sheet"
112 sc.Sheet = RateSheet{BoxMonthServerUSD: 5, BoxMonthDeviceUSD: 1}
113 sc.FreeTier = FreeTier{}
114 // 2 server boxes + 5 device boxes, full month.
115 meters := map[string]float64{
116 usage.MeterFleetBoxHoursServer: 2 * 730,
117 usage.MeterFleetBoxHoursDevice: 5 * 730,
118 }
119 ae := PriceAccount(sc, "bae", meters, 0)
120 approx(t, "fleet revenue", ae.RevenueUSD, 2*5+5*1) // $15
121}
122
123func TestPriceAccountFleetVolumeBreak(t *testing.T) {
124 sc := DefaultScenario()
125 sc.Mode = "rate-sheet"
126 sc.Sheet = RateSheet{BoxMonthServerUSD: 5}
127 sc.FleetBreak = FleetBreak{MinBoxes: 10, Discount: 0.4}
128 sc.FreeTier = FreeTier{}
129 meters := map[string]float64{usage.MeterFleetBoxHoursServer: 12 * 730}
130 ae := PriceAccount(sc, "bae", meters, 0)
131 approx(t, "discounted", ae.RevenueUSD, 12*5*0.6) // $36
132 // 9.99 avg boxes: no break.
133 meters[usage.MeterFleetBoxHoursServer] = 9.99 * 730
134 ae = PriceAccount(sc, "bae", meters, 0)
135 approx(t, "no break", ae.RevenueUSD, 9.99*5)
136}
137
138func TestPriceAccountFleetAddsInCostPlusToo(t *testing.T) {
139 sc := DefaultScenario()
140 sc.Mode = "cost-plus"
141 sc.Margin = 0.5
142 sc.Sheet.BoxMonthServerUSD = 5
143 sc.RateCard.EgressGBUSD = 0.10
144 sc.RateCard.RequestsPer1kUSD, sc.RateCard.StorageGBMonthUSD = 0, 0
145 sc.FreeTier = FreeTier{}
146 meters := map[string]float64{
147 usage.MeterEgressGB: 10, // $1 cost -> $2 cost-plus revenue
148 usage.MeterFleetBoxHoursServer: 1 * 730, // + $5 box-month
149 }
150 ae := PriceAccount(sc, "bae", meters, 0)
151 approx(t, "combined", ae.RevenueUSD, 2+5)
152}
153
154func TestPriceAccountMemOverage(t *testing.T) {
155 sc := DefaultScenario()
156 sc.Mode = "rate-sheet"
157 sc.Sheet = RateSheet{MemGBHourUSD: 0.01}
158 sc.FreeTier = FreeTier{MemGBHours: 100}
159 meters := map[string]float64{usage.MeterMemGBHours: 400}
160 ae := PriceAccount(sc, "bab", meters, 0)
161 approx(t, "overage", ae.RevenueUSD, 3.0) // 300 over * $0.01
162}
163