Viewing files at commit 1c9b819ab86bCommit detailsView latest

Browse files
tests/test_reporting_golden.py 141 lines · 6.1 KB · python
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()