package api import ( "math" "testing" ) func TestProportionDelta_ReproducesTheDiscoverCase(t *testing.T) { // The comparison that motivated #2495: Discover taste-matched (59 plays, // 15.3% skip) vs random-unheard (70 plays, 28.6%). A 13.3pp gap that the old // card rendered as a confident coloured number, sitting at p ≈ 0.06. d := proportionDelta(0.153, 59, 0.286, 70) if d == nil { t.Fatal("expected a delta for two real samples") } if math.Abs(d.DeltaPP-(-13.3)) > 0.1 { t.Errorf("DeltaPP = %.2f, want ≈ -13.3", d.DeltaPP) } // This is the assertion the whole task exists for: at these sample sizes the // margin swallows the difference. if d.Distinguishable { t.Errorf("13.3pp on n=59/70 reported as distinguishable (margin %.2f) — "+ "this is exactly the false confidence #2495 set out to remove", d.MarginPP) } if d.MarginPP <= 13.3 { t.Errorf("MarginPP = %.2f, expected it to exceed the 13.3pp delta", d.MarginPP) } } // Same effect size, ~10x the volume: now it is real. Proves the flag tracks // sample size rather than just the size of the gap. func TestProportionDelta_SameGapBecomesDistinguishableWithVolume(t *testing.T) { d := proportionDelta(0.153, 600, 0.286, 700) if d == nil { t.Fatal("expected a delta") } if !d.Distinguishable { t.Errorf("13.3pp on n=600/700 should be distinguishable (margin %.2f)", d.MarginPP) } } func TestProportionDelta_SignAndDirection(t *testing.T) { // Surface skips MORE than baseline -> positive delta (worse for skip rate). worse := proportionDelta(0.40, 500, 0.25, 500) if worse == nil || worse.DeltaPP <= 0 { t.Fatalf("expected a positive delta, got %+v", worse) } better := proportionDelta(0.10, 500, 0.25, 500) if better == nil || better.DeltaPP >= 0 { t.Fatalf("expected a negative delta, got %+v", better) } } func TestProportionDelta_EmptySamples(t *testing.T) { if d := proportionDelta(0.2, 0, 0.3, 100); d != nil { t.Errorf("n1=0 produced a delta: %+v", d) } if d := proportionDelta(0.2, 100, 0.3, 0); d != nil { t.Errorf("n2=0 produced a delta: %+v", d) } } // Two degenerate rates have zero standard error, which would report a margin of // 0 and therefore "distinguishable" for a delta of exactly 0. Reporting nothing // is the honest answer. func TestProportionDelta_DegenerateRates(t *testing.T) { if d := proportionDelta(0, 50, 0, 50); d != nil { t.Errorf("both rates 0 produced a delta: %+v", d) } if d := proportionDelta(1, 50, 1, 50); d != nil { t.Errorf("both rates 1 produced a delta: %+v", d) } // One degenerate side is still informative — the other side carries variance. if d := proportionDelta(0, 200, 0.3, 200); d == nil { t.Error("one degenerate rate should still yield a delta") } } func TestMeanDelta(t *testing.T) { // Completion is bimodal, so ~0.16 variance (sd ≈ 0.4) is realistic. const v = 0.16 thin := meanDelta(0.82, v, 59, 0.54, v, 70) if thin == nil { t.Fatal("expected a delta") } if math.Abs(thin.DeltaPP-28.0) > 0.1 { t.Errorf("DeltaPP = %.2f, want ≈ 28.0", thin.DeltaPP) } // 28pp is large enough to survive even a wide margin at this n. if !thin.Distinguishable { t.Errorf("28pp on n=59/70 with sd 0.4 should be distinguishable (margin %.2f)", thin.MarginPP) } // A small completion gap at the same volume should not be. small := meanDelta(0.56, v, 59, 0.54, v, 70) if small == nil { t.Fatal("expected a delta") } if small.Distinguishable { t.Errorf("2pp on n=59/70 reported as distinguishable (margin %.2f)", small.MarginPP) } } // A sample variance needs at least two observations per side. func TestMeanDelta_NeedsTwoObservations(t *testing.T) { if d := meanDelta(0.8, 0.1, 1, 0.5, 0.1, 100); d != nil { t.Errorf("n1=1 produced a delta: %+v", d) } if d := meanDelta(0.8, 0.1, 100, 0.5, 0.1, 1); d != nil { t.Errorf("n2=1 produced a delta: %+v", d) } } func TestMeanDelta_ZeroVarianceBothSides(t *testing.T) { if d := meanDelta(0.8, 0, 50, 0.5, 0, 50); d != nil { t.Errorf("zero variance on both sides produced a delta: %+v", d) } } func TestSampleVariance(t *testing.T) { // Observations 0, 1: mean 0.5, sample variance 0.5. if got := sampleVariance(1.0, 1.0, 2); math.Abs(got-0.5) > 1e-9 { t.Errorf("sampleVariance = %v, want 0.5", got) } // Identical observations -> zero variance, and must not go negative through // floating-point cancellation. if got := sampleVariance(4.0, 4.0, 4); got != 0 { t.Errorf("identical observations gave variance %v, want 0", got) } if got := sampleVariance(0, 0, 1); got != 0 { t.Errorf("n=1 gave variance %v, want 0", got) } } // Clamping matters: a negative variance would become NaN in the square root and // propagate into the JSON as a null-ish number. func TestSampleVariance_NeverNegative(t *testing.T) { // sqSum slightly below sum²/n, as cancellation can produce. if got := sampleVariance(10.0, 24.999999999, 4); got < 0 { t.Errorf("variance went negative: %v", got) } } func TestNewDelta_BoundaryCountsAsDistinguishable(t *testing.T) { d := newDelta(5.0, 5.0) if !d.Distinguishable { t.Error("a delta exactly equal to its margin should count as distinguishable") } d = newDelta(4.999, 5.0) if d.Distinguishable { t.Error("a delta just inside its margin should not count as distinguishable") } }