Skip to content

Commit 03cef8e

Browse files
Secnario 2 sql
Bugfix scenario 2 some code
1 parent 4724b76 commit 03cef8e

3 files changed

Lines changed: 167 additions & 21 deletions

File tree

audit.sql

Lines changed: 49 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -28,25 +28,53 @@ SELECT price_group FROM (
2828

2929
DROP TABLE IF EXISTS priced;
3030
CREATE TABLE priced AS
31+
WITH base AS (
32+
SELECT
33+
li.box, li.date, li.month, li.grouping, li.type, li.kind, li.qty, li.currency,
34+
li.paid, st.ram_gb, st.vcpu,
35+
( SELECT MIN(
36+
( SELECT p.price_monthly FROM prices p
37+
WHERE p.type = cand.type AND p.currency = li.currency
38+
AND p.price_group = (SELECT price_group FROM detected_group)
39+
AND p.effective_from <= li.date
40+
ORDER BY p.effective_from DESC LIMIT 1 )
41+
)
42+
FROM server_types cand
43+
WHERE cand.ram_gb = st.ram_gb AND cand.vcpu >= st.vcpu
44+
) AS cheapest_monthly,
45+
( SELECT MIN(
46+
( SELECT p.price_hourly FROM prices p
47+
WHERE p.type = cand.type AND p.currency = li.currency
48+
AND p.price_group = (SELECT price_group FROM detected_group)
49+
AND p.effective_from <= li.date
50+
ORDER BY p.effective_from DESC LIMIT 1 )
51+
)
52+
FROM server_types cand
53+
WHERE cand.ram_gb = st.ram_gb AND cand.vcpu >= st.vcpu
54+
) AS cheapest_hourly,
55+
( SELECT MAX(p.effective_from) FROM prices p
56+
WHERE p.effective_from <= li.date AND p.effective_from <> '2026-06-15' ) AS epoch
57+
FROM line_items li
58+
JOIN server_types st ON st.type = li.type
59+
),
60+
locked AS (
61+
SELECT *,
62+
MIN(cheapest_monthly) OVER win AS lm_market,
63+
MIN(CASE WHEN kind = 'monthly' THEN paid / qty END) OVER win AS lm_bob,
64+
MIN(cheapest_hourly) OVER win AS lh_market,
65+
MIN(CASE WHEN kind = 'hourly' THEN paid / qty END) OVER win AS lh_bob
66+
FROM base
67+
WINDOW win AS (PARTITION BY box, ram_gb, vcpu, epoch ORDER BY month
68+
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
69+
)
3170
SELECT
32-
li.box, li.date, li.month, li.grouping, li.type, li.kind, li.qty, li.currency,
33-
li.paid,
34-
MIN(
35-
COALESCE(
36-
( SELECT MIN(
37-
( SELECT CASE WHEN li.kind = 'hourly' THEN p.price_hourly ELSE p.price_monthly END
38-
FROM prices p
39-
WHERE p.type = cand.type AND p.currency = li.currency
40-
AND p.price_group = (SELECT price_group FROM detected_group)
41-
AND p.effective_from <= li.date
42-
ORDER BY p.effective_from DESC LIMIT 1 )
43-
)
44-
FROM server_types cand
45-
WHERE cand.ram_gb = st.ram_gb AND cand.vcpu >= st.vcpu
46-
) * li.qty,
47-
li.paid
48-
),
49-
li.paid
50-
) AS optimal
51-
FROM line_items li
52-
JOIN server_types st ON st.type = li.type;
71+
box, date, month, grouping, type, kind, qty, currency, paid,
72+
MIN( COALESCE( (CASE WHEN kind = 'hourly' THEN cheapest_hourly ELSE cheapest_monthly END) * qty, paid ), paid ) AS optimal,
73+
MIN( COALESCE(
74+
( CASE WHEN kind = 'monthly'
75+
THEN COALESCE( min(lm_market, lm_bob), lm_market, lm_bob )
76+
ELSE COALESCE( min(lh_market, lh_bob), lh_market, lh_bob )
77+
END ) * qty,
78+
paid ),
79+
paid ) AS optimal_locked
80+
FROM locked;

tests/fixtures.bash

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,84 @@ grouping,product,description,reference,quantity,from,until,condition,unit,extern
102102
CSV
103103
}
104104

105+
jun2026_scaleup_assets() {
106+
mkdir -p "$1/providers/hetzner"
107+
cp "$ROOT/schema.sql" "$ROOT/audit.sql" "$1/"
108+
cat > "$1/providers/hetzner/prices.csv" <<CSV
109+
type,price_group,currency,effective_from,price_hourly,price_monthly
110+
cx33,eu,eur,2025-10-01,0.0080,4.99
111+
cx33,eu,eur,2026-04-01,0.0104,6.49
112+
cpx32,eu,eur,2025-10-01,0.0224,13.99
113+
CSV
114+
cat > "$1/providers/hetzner/server_types.csv" <<CSV
115+
type,vcpu,ram_gb
116+
cx33,4,8
117+
cpx32,4,8
118+
CSV
119+
}
120+
121+
jun2026_scaleup_invoice() {
122+
cat > "$1" <<CSV
123+
grouping,product,description,reference,quantity,from,until,condition,unit,external id,price,total
124+
"P","CX33 Cloud Server",,,"1.0000",2026-05-01,2026-05-31,,"Months","b1",,"€ 4.9900"
125+
"P","CPX32 Cloud Server",,,"1.0000",2026-06-01,2026-06-30,,"Months","b1",,"€ 13.9900"
126+
CSV
127+
}
128+
129+
jun2026_mixed_kind_invoice() {
130+
cat > "$1" <<CSV
131+
grouping,product,description,reference,quantity,from,until,condition,unit,external id,price,total
132+
"P","CPX31 Cloud Server",,,"240",2026-05-01,2026-05-31,,"Hours","m1",,"€ 8.06"
133+
"P","CPX31 Cloud Server",,,"1.0000",2026-06-01,2026-06-30,,"Months","m1",,"€ 20.9900"
134+
CSV
135+
}
136+
137+
jun2026_reprice_assets() {
138+
mkdir -p "$1/providers/hetzner"
139+
cp "$ROOT/schema.sql" "$ROOT/audit.sql" "$1/"
140+
cat > "$1/providers/hetzner/prices.csv" <<CSV
141+
type,price_group,currency,effective_from,price_hourly,price_monthly
142+
cx33,eu,eur,2025-10-01,0.0080,4.99
143+
cx33,eu,eur,2026-04-01,0.0104,6.49
144+
cx33,eu,eur,2026-06-15,0.0136,8.49
145+
CSV
146+
cat > "$1/providers/hetzner/server_types.csv" <<CSV
147+
type,vcpu,ram_gb
148+
cx33,4,8
149+
CSV
150+
}
151+
152+
jun2026_across_april_invoice() {
153+
cat > "$1" <<CSV
154+
grouping,product,description,reference,quantity,from,until,condition,unit,external id,price,total
155+
"P","CX33 Cloud Server",,,"1.0000",2026-03-01,2026-03-31,,"Months","g1",,"€ 4.9900"
156+
"P","CX33 Cloud Server",,,"1.0000",2026-05-01,2026-05-31,,"Months","g1",,"€ 6.4900"
157+
CSV
158+
}
159+
160+
jun2026_resize_assets() {
161+
mkdir -p "$1/providers/hetzner"
162+
cp "$ROOT/schema.sql" "$ROOT/audit.sql" "$1/"
163+
cat > "$1/providers/hetzner/prices.csv" <<CSV
164+
type,price_group,currency,effective_from,price_hourly,price_monthly
165+
cx22,eu,eur,2026-04-01,0.0060,3.79
166+
cx33,eu,eur,2026-04-01,0.0104,6.49
167+
CSV
168+
cat > "$1/providers/hetzner/server_types.csv" <<CSV
169+
type,vcpu,ram_gb
170+
cx22,2,4
171+
cx33,4,8
172+
CSV
173+
}
174+
175+
jun2026_resize_invoice() {
176+
cat > "$1" <<CSV
177+
grouping,product,description,reference,quantity,from,until,condition,unit,external id,price,total
178+
"P","CX22 Cloud Server",,,"1.0000",2026-05-01,2026-05-31,,"Months","b1",,"€ 3.7900"
179+
"P","CX33 Cloud Server",,,"1.0000",2026-06-01,2026-06-30,,"Months","b1",,"€ 6.4900"
180+
CSV
181+
}
182+
105183
jun2026_solo_assets() {
106184
mkdir -p "$1/providers/hetzner"
107185
cp "$ROOT/schema.sql" "$ROOT/audit.sql" "$1/"

tests/unit.bats

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,46 @@ CSV
168168
[ "$(sqlite3 "$db" "SELECT printf('%.2f', optimal) FROM priced WHERE box='c1';")" = "8.49" ]
169169
}
170170

171+
@test "scoring 2: optimal_locked holds the cheapest rate available since the box's first month, not the risen list" {
172+
a="$BATS_TEST_TMPDIR/lka"; jun2026_assets "$a"
173+
d="$BATS_TEST_TMPDIR/lkd"; mkdir -p "$d"; jun2026_untouched_invoice "$d/i.csv"
174+
db="$BATS_TEST_TMPDIR/lk.db"; build_db "$db" "$a" "$d" >/dev/null
175+
[ "$(sqlite3 "$db" "SELECT printf('%.2f', optimal_locked) FROM priced WHERE month='2026-06';")" = "6.49" ]
176+
[ "$(sqlite3 "$db" "SELECT printf('%.2f', optimal_locked) FROM priced WHERE month='2026-07';")" = "6.49" ]
177+
}
178+
179+
@test "scoring 2: Alice keeps the cheap box she inherited when Bob scales it up, not the pricier market rate" {
180+
a="$BATS_TEST_TMPDIR/sua"; jun2026_scaleup_assets "$a"
181+
d="$BATS_TEST_TMPDIR/sud"; mkdir -p "$d"; jun2026_scaleup_invoice "$d/i.csv"
182+
db="$BATS_TEST_TMPDIR/su.db"; build_db "$db" "$a" "$d" >/dev/null
183+
[ "$(sqlite3 "$db" "SELECT printf('%.2f', optimal_locked) FROM priced WHERE month='2026-05';")" = "4.99" ]
184+
[ "$(sqlite3 "$db" "SELECT printf('%.2f', optimal_locked) FROM priced WHERE month='2026-06';")" = "4.99" ]
185+
}
186+
187+
@test "scoring 2: a box with both hourly and monthly lines keeps units separate — no hourly rate leaks into the monthly lock" {
188+
a="$BATS_TEST_TMPDIR/mka"; jun2026_assets "$a"
189+
d="$BATS_TEST_TMPDIR/mkd"; mkdir -p "$d"; jun2026_mixed_kind_invoice "$d/i.csv"
190+
db="$BATS_TEST_TMPDIR/mk.db"; build_db "$db" "$a" "$d" >/dev/null
191+
[ "$(sqlite3 "$db" "SELECT printf('%.2f', optimal_locked) FROM priced WHERE month='2026-06';")" = "6.49" ]
192+
[ "$(sqlite3 "$db" "SELECT printf('%.2f', optimal_locked) FROM priced WHERE month='2026-05';")" = "2.50" ]
193+
}
194+
195+
@test "scoring 2: the lock resets across a non-grandfathered hike (only 15-Jun-2026 grandfathers), so a pre-April rate is not carried past it" {
196+
a="$BATS_TEST_TMPDIR/gfa"; jun2026_reprice_assets "$a"
197+
d="$BATS_TEST_TMPDIR/gfd"; mkdir -p "$d"; jun2026_across_april_invoice "$d/i.csv"
198+
db="$BATS_TEST_TMPDIR/gf.db"; build_db "$db" "$a" "$d" >/dev/null
199+
[ "$(sqlite3 "$db" "SELECT printf('%.2f', optimal_locked) FROM priced WHERE month='2026-03';")" = "4.99" ]
200+
[ "$(sqlite3 "$db" "SELECT printf('%.2f', optimal_locked) FROM priced WHERE month='2026-05';")" = "6.49" ]
201+
}
202+
203+
@test "scoring 2: resizing a box to a bigger spec resets the lock — a cheap small-box rate is not carried onto the bigger box" {
204+
a="$BATS_TEST_TMPDIR/rza"; jun2026_resize_assets "$a"
205+
d="$BATS_TEST_TMPDIR/rzd"; mkdir -p "$d"; jun2026_resize_invoice "$d/i.csv"
206+
db="$BATS_TEST_TMPDIR/rz.db"; build_db "$db" "$a" "$d" >/dev/null
207+
[ "$(sqlite3 "$db" "SELECT printf('%.2f', optimal_locked) FROM priced WHERE month='2026-05';")" = "3.79" ]
208+
[ "$(sqlite3 "$db" "SELECT printf('%.2f', optimal_locked) FROM priced WHERE month='2026-06';")" = "6.49" ]
209+
}
210+
171211
@test "a lone post-15-Jun invoice still detects its price group from an old locked rate, so the box is priced against the cheaper type (no false 0%)" {
172212
a="$BATS_TEST_TMPDIR/pha"; jun2026_assets "$a"
173213
d="$BATS_TEST_TMPDIR/phd"; mkdir -p "$d"; jun2026_post_hike_only_invoice "$d/i.csv"

0 commit comments

Comments
 (0)