trucking: support full-price control arm in coupon A/B (pct 0 = no code)

CAMPAIGN_COUPON_AB_PCTS="20,30,0" now means 20% / 30% / full-price. The 0 arm
mints no code; pick_coupon_for_email returns ("","") so it renders identically
to a normal-price send, while carriers are still deterministically hash-bucketed
into it (re-hash a converter's email to recover their arm). Even ~33/33/33 split
incl. the control verified over 30k. Adds test_full_price_control_arm; 8/8 pass.
This commit is contained in:
justin 2026-06-21 00:12:30 -05:00
parent 2f0753f00e
commit 297db74fee
2 changed files with 58 additions and 10 deletions

View file

@ -59,6 +59,36 @@ def test_single_arm():
assert btc.pick_coupon_for_email("a@b.com", {40: "ZZZZZ"}) == ("ZZZZZ", "40")
def test_full_price_control_arm():
"""A 0% arm (code "") is a full-price control: returned as ("", "") so the
email renders the normal-price branch, but carriers are still hash-bucketed
evenly across all three arms (so the control is measurable)."""
coupons = {20: "AAAAA", 30: "BBBBB", 0: ""} # 0 = full-price control
counts: Counter = Counter()
arm_for: dict = {}
for i in range(30000):
e = f"user{i}@carrier{i % 500}.com"
code, pct = btc.pick_coupon_for_email(e, coupons)
# The control arm presents identically to a no-coupon send.
if code == "":
assert pct == ""
else:
assert code in ("AAAAA", "BBBBB") and pct in ("20", "30")
# Bucket label: recover arm even for the silent control via re-hash.
h = __import__("hashlib").sha256(e.encode()).hexdigest()
arm = sorted(coupons)[int(h, 16) % 3]
arm_for[e] = arm
counts[arm] += 1
# Stable across re-calls.
assert btc.pick_coupon_for_email(e, coupons) == (code, pct)
total = sum(counts.values())
for arm in (0, 20, 30):
share = counts[arm] / total
assert 0.31 <= share <= 0.353, (arm, share)
# The price helper yields no number for the control arm (blank pct).
assert btc.discounted_price_attribs("mcs150", None, "")["coupon_priceable"] == ""
def test_coupon_attribs_reflects_pct():
a = btc.coupon_attribs("BBBBB", "30")
assert a["coupon_code"] == "BBBBB"