| 1 | """Python/JS reporting parity: assert the shared golden fixture.
|
| 2 |
|
| 3 | tests/fixtures/reporting_golden.json is the parity contract between the
|
| 4 | Python formulas (oakbench.stats / oakbench.reporting) and their Cloudflare
|
| 5 | worker ports (cloudflare/reporting.mjs). This module asserts every golden
|
| 6 | case against the REAL Python functions, then — when node is available —
|
| 7 | runs the JS side (node --test cloudflare/reporting_parity.test.mjs) so the
|
| 8 | same numbers are asserted from both implementations.
|
| 9 | """
|
| 10 |
|
| 11 | from __future__ import annotations
|
| 12 |
|
| 13 | import json
|
| 14 | import shutil
|
| 15 | import subprocess
|
| 16 | import sys
|
| 17 | import unittest
|
| 18 | from pathlib import Path
|
| 19 |
|
| 20 | ROOT = Path(__file__).resolve().parents[1]
|
| 21 | sys.path.insert(0, str(ROOT / "scripts"))
|
| 22 |
|
| 23 | from oakbench.reporting import TAIL_PERCENTILE_MIN_SAMPLES, delta_pct, median, tail_latency_summary
|
| 24 | from oakbench.stats import percentile_nearest
|
| 25 |
|
| 26 | GOLDEN_PATH = ROOT / "tests" / "fixtures" / "reporting_golden.json"
|
| 27 | GOLDEN = json.loads(GOLDEN_PATH.read_text())
|
| 28 |
|
| 29 |
|
| 30 | class PercentileNearestGoldenTest(unittest.TestCase):
|
| 31 | def test_all_cases(self) -> None:
|
| 32 | for case in GOLDEN["percentile_nearest"]:
|
| 33 | with self.subTest(values=case["values"], p=case["p"]):
|
| 34 | self.assertEqual(percentile_nearest(case["values"], case["p"]), case["expected"])
|
| 35 |
|
| 36 | def test_covers_empty_single_and_ties(self) -> None:
|
| 37 | """The contract must keep exercising the edge cases it promises."""
|
| 38 | cases = GOLDEN["percentile_nearest"]
|
| 39 | self.assertTrue(any(case["values"] == [] and case["expected"] is None for case in cases))
|
| 40 | self.assertTrue(any(len(case["values"]) == 1 for case in cases))
|
| 41 | self.assertTrue(any(len(set(case["values"])) == 1 and len(case["values"]) > 1 for case in cases))
|
| 42 |
|
| 43 |
|
| 44 | class DeltaPctGoldenTest(unittest.TestCase):
|
| 45 | def test_all_cases(self) -> None:
|
| 46 | for case in GOLDEN["delta_pct"]:
|
| 47 | with self.subTest(base=case["base"], value=case["value"]):
|
| 48 | self.assertEqual(delta_pct(case["base"], case["value"]), case["expected"])
|
| 49 |
|
| 50 | def test_base_zero_guard_documented(self) -> None:
|
| 51 | zero_cases = [case for case in GOLDEN["delta_pct"] if case["base"] == 0]
|
| 52 | self.assertTrue(zero_cases, "fixture must include the base == 0 guard")
|
| 53 | for case in zero_cases:
|
| 54 | self.assertEqual(case["expected"], 0.0)
|
| 55 |
|
| 56 |
|
| 57 | class AggregateGroupGoldenTest(unittest.TestCase):
|
| 58 | def test_expected_matches_python_formulas(self) -> None:
|
| 59 | rows = GOLDEN["aggregate_group"]["rows"]
|
| 60 | expected = GOLDEN["aggregate_group"]["expected"]
|
| 61 | values = [
|
| 62 | row["elapsed_ms"]
|
| 63 | for row in rows
|
| 64 | if row["returncode"] == 0 and row["elapsed_ms"] is not None
|
| 65 | ]
|
| 66 | self.assertEqual(median(values), expected["median"])
|
| 67 | self.assertEqual(percentile_nearest(values, 90), expected["p90"])
|
| 68 | self.assertEqual(percentile_nearest(values, 95), expected["p95"])
|
| 69 | self.assertEqual(percentile_nearest(values, 99), expected["p99"])
|
| 70 | self.assertEqual(len(values), expected["n"])
|
| 71 |
|
| 72 | def test_fixture_exercises_null_preservation(self) -> None:
|
| 73 | rows = GOLDEN["aggregate_group"]["rows"]
|
| 74 | self.assertTrue(any(row["elapsed_ms"] is None for row in rows))
|
| 75 | self.assertTrue(any(row.get("estimated_tokens_total") is None for row in rows))
|
| 76 | self.assertTrue(any(row["returncode"] == 77 for row in rows))
|
| 77 | self.assertTrue(any(row["returncode"] not in (0, 77) for row in rows))
|
| 78 |
|
| 79 |
|
| 80 | class AggregateTailGatingGoldenTest(unittest.TestCase):
|
| 81 | """Tail honesty parity: p95 null below n=20, p99 null below n=100.
|
| 82 |
|
| 83 | Python side asserts oakbench.reporting.tail_latency_summary; the JS side
|
| 84 | asserts the same cases against computeAggregates' gated p95_ms/p99_ms
|
| 85 | (cloudflare/reporting.mjs TAIL_MIN_SAMPLES mirrors
|
| 86 | TAIL_PERCENTILE_MIN_SAMPLES / scripts/benchmark_stats.py).
|
| 87 | """
|
| 88 |
|
| 89 | def test_expected_matches_tail_latency_summary(self) -> None:
|
| 90 | for case in GOLDEN["aggregate_tail_gating"]["cases"]:
|
| 91 | values = case["values"]
|
| 92 | expected = case["expected"]
|
| 93 | with self.subTest(n=len(values)):
|
| 94 | summary = tail_latency_summary(values)
|
| 95 | self.assertEqual(summary["n"], len(values))
|
| 96 | self.assertEqual(summary["p50_ms"], expected["p50"])
|
| 97 | self.assertEqual(summary["p95_ms"], expected["p95"])
|
| 98 | self.assertEqual(summary["p99_ms"], expected["p99"])
|
| 99 | self.assertEqual(summary["tail_supported"], expected["tail_supported"])
|
| 100 |
|
| 101 | def test_fixture_covers_both_gate_boundaries(self) -> None:
|
| 102 | sizes = {len(case["values"]) for case in GOLDEN["aggregate_tail_gating"]["cases"]}
|
| 103 | self.assertIn(TAIL_PERCENTILE_MIN_SAMPLES["p95"], sizes, "n=20 boundary case present")
|
| 104 | self.assertIn(TAIL_PERCENTILE_MIN_SAMPLES["p99"], sizes, "n=100 boundary case present")
|
| 105 | self.assertTrue(any(size < TAIL_PERCENTILE_MIN_SAMPLES["p95"] for size in sizes), "ungated-n case present")
|
| 106 |
|
| 107 |
|
| 108 | class ComparisonGoldenTest(unittest.TestCase):
|
| 109 | def test_expected_matches_python_formulas(self) -> None:
|
| 110 | comparison = GOLDEN["comparison"]
|
| 111 | oak_median = median(comparison["oak_values"])
|
| 112 | git_median = median(comparison["git_values"])
|
| 113 | self.assertEqual(oak_median, comparison["expected_oak_median"])
|
| 114 | self.assertEqual(git_median, comparison["expected_git_median"])
|
| 115 | self.assertEqual(oak_median / git_median, comparison["expected_ratio"])
|
| 116 | self.assertEqual(delta_pct(git_median, oak_median), comparison["expected_delta_pct"])
|
| 117 |
|
| 118 |
|
| 119 | class NodeParityTest(unittest.TestCase):
|
| 120 | """Run the JS side of the contract when node is installed."""
|
| 121 |
|
| 122 | def test_node_suite_passes(self) -> None:
|
| 123 | node = shutil.which("node")
|
| 124 | if node is None:
|
| 125 | self.skipTest("node absent")
|
| 126 | proc = subprocess.run(
|
| 127 | [node, "--test", "cloudflare/reporting_parity.test.mjs"],
|
| 128 | cwd=ROOT,
|
| 129 | capture_output=True,
|
| 130 | text=True,
|
| 131 | timeout=120,
|
| 132 | )
|
| 133 | self.assertEqual(
|
| 134 | proc.returncode,
|
| 135 | 0,
|
| 136 | f"node --test failed:\nSTDOUT:\n{proc.stdout}\nSTDERR:\n{proc.stderr}",
|
| 137 | )
|
| 138 |
|
| 139 |
|
| 140 | if __name__ == "__main__":
|
| 141 | unittest.main()
|