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:
parent
2f0753f00e
commit
297db74fee
2 changed files with 58 additions and 10 deletions
|
|
@ -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"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue