SPB Git forge
15commits 1branches 0releases
29.7 MBsize
maindefault branch
11 days agolast push
TypeScript 36.3% Python 31.8% Go 18% JavaScript 9.8% Shell 1.9% SQL 1.4% CSS 0.5%
6.0 KB · 166 lines python
Raw Blame History
1import math23import pytest45from internetpressure.config import ConfigError, build_config, load_file_config6from internetpressure.engine import scoring as S789def test_robust_z_basic_and_clipping():10    assert S.robust_z(30, 20, 2) == 5.011    assert S.robust_z(200, 20, 2) == 8.0  # clipped high12    assert S.robust_z(0, 20, 2) == -3.0  # clipped low13    assert S.robust_z(None, 20, 2) is None14    assert S.robust_z(20, None, 2) is None151617def test_robust_z_mad_floor_prevents_explosion():18    # ultra-tight baseline (MAD 0.01 ms on a 20 ms median): floor = max(1.0, 0.05*20=1.0) → z uses 1.019    assert S.robust_z(25, 20, 0.01) == 5.020    # relative floor on large medians21    assert S.robust_z(1100, 1000, 1) == pytest.approx(2.0)  # floor 50 ms222324def test_mad_from_iqr():25    assert S.mad_from_iqr(10, 20) == pytest.approx(7.413)26    assert S.mad_from_iqr(None, 20) is None272829def test_z_to_stress_ramp():30    assert S.z_to_stress(None) == 031    assert S.z_to_stress(0.5) == 032    assert S.z_to_stress(1.0) == 033    assert S.z_to_stress(3.5) == pytest.approx(0.5)34    assert S.z_to_stress(6) == 135    assert S.z_to_stress(8) == 1363738def test_rate_stress():39    assert S.rate_stress(0.0, 0.0) == 040    assert S.rate_stress(0.25, 0.0) == 1.041    assert S.rate_stress(0.125, 0.0) == pytest.approx(0.5)42    # a noisy target with a 10 % baseline failure rate needs 30 % excess for full stress43    assert S.rate_stress(0.25, 0.10) == pytest.approx(0.5)44    assert S.rate_stress(0.05, 0.10) == 0454647def test_ratio_stress():48    assert S.ratio_stress(100, 100) == 049    assert S.ratio_stress(150, 100) == 050    assert S.ratio_stress(375, 100) == pytest.approx(0.5)51    assert S.ratio_stress(1000, 100) == 152    assert S.ratio_stress(10, 0) == 0535455def test_saturate_bounds_and_shape():56    assert S.saturate(0) == 057    assert S.saturate(1) == pytest.approx(100)58    mid = S.saturate(0.5)59    assert 55 < mid < 75  # concave: half stress is well above 5060    assert S.saturate(0.1) < S.saturate(0.2) < S.saturate(0.5)61    assert S.saturate(0.5, k=0) == 50626364def test_coverage_factor():65    assert S.coverage_factor(0, 24) == 066    assert S.coverage_factor(2, 24) == 0  # below 20 %67    assert S.coverage_factor(24, 24) == 168    assert 0 < S.coverage_factor(12, 24) < 169    assert S.coverage_factor(5, 0) == 1707172def test_weighted_stress_and_breadth():73    pairs = [74        S.PairStress("a", 1.0, 2.0),75        S.PairStress("b", 0.0, 1.0),76        S.PairStress("c", 0.5, 1.0),77        S.PairStress("d", 0.9, 0.0),  # zero weight ignored78    ]79    stress, tw, n = S.weighted_stress(pairs)80    assert n == 3 and tw == 4.081    assert stress == pytest.approx((2.0 + 0 + 0.5) / 4.0)82    assert S.breadth(pairs, threshold=0.4) == pytest.approx(3.0 / 4.0)83    assert S.weighted_stress([]) == (0.0, 0.0, 0)848586def test_component_score_renormalises_missing_signals():87    w = {"a": 0.5, "b": 0.3, "c": 0.2}88    assert S.component_score({}, w) == 089    full = S.component_score({"a": 1, "b": 1, "c": 1}, w)90    assert full == pytest.approx(100)91    # only "a" observed at full stress → still 100 (renormalised), not 5092    assert S.component_score({"a": 1}, w) == pytest.approx(100)93    assert S.component_score({"a": 0, "b": 0, "c": 0}, w) == 0949596def test_global_score_weights_from_config():97    cfg = load_file_config()98    comps = {c: 50.0 for c in cfg.weights}99    score, contrib = S.global_score(comps, cfg.weights)100    assert score == pytest.approx(50.0)101    assert sum(contrib.values()) == pytest.approx(50.0)102    # missing routing → renormalised over the rest103    comps["routing"] = None104    score2, contrib2 = S.global_score(comps, cfg.weights)105    assert score2 == pytest.approx(50.0)106    assert "routing" not in contrib2107108109def test_global_score_example_from_spec():110    weights = {"routing": .25, "latency": .20, "dns": .15, "availability": .15, "http_tls": .10, "path": .10,111               "corroboration": .05}112    comps = {"routing": 57, "latency": 44, "dns": 19, "availability": 37, "http_tls": 28, "path": 51, "corroboration": 0}113    score, _ = S.global_score(comps, weights)114    assert score == pytest.approx(57 * .25 + 44 * .2 + 19 * .15 + 37 * .15 + 28 * .1 + 51 * .1)115116117def test_confidence_monotone():118    low = S.confidence_score(probes=1, probe_regions=1, signals_agreeing=1, samples=2, min_samples=24)119    high = S.confidence_score(probes=8, probe_regions=4, signals_agreeing=3, samples=100, min_samples=24,120                              duration_s=1200, bgp_corroborated=True, external_corroborated=True)121    assert 0 < low < high <= 1.0122123124def test_velocity_and_acceleration():125    pts = [(t * 60.0, 20 + t * 0.5) for t in range(0, 30)]  # +0.5/min = +30/h126    v, a = S.velocity(pts)127    assert v == pytest.approx(30.0, rel=1e-3)128    assert a == pytest.approx(0.0, abs=1e-6)129    pts2 = [(t * 60.0, 20 + 0.02 * t * t) for t in range(0, 30)]  # accelerating130    v2, a2 = S.velocity(pts2)131    assert v2 > 0 and a2 > 0132    assert S.velocity(pts[:3]) == (None, None)133134135def test_volatility_and_trend():136    assert S.volatility([1, 1, 1]) == 0137    assert S.volatility([1, 2]) is None138    assert S.volatility([10, 20, 30]) == pytest.approx(10.0)139    assert S.trend_word(0.4) == "stable"140    assert S.trend_word(5) == "rising"141    assert S.trend_word(-5) == "falling"142    assert S.trend_word(None) == "stable"143144145def test_config_validation_rejects_bad_weights():146    cfg = load_file_config()147    raw = dict(cfg.raw)148    raw["pressure_weights"] = {**cfg.weights, "routing": 0.5}149    with pytest.raises(ConfigError):150        build_config(raw)151    raw["pressure_weights"] = {k: v for k, v in cfg.weights.items() if k != "path"}152    with pytest.raises(ConfigError):153        build_config(raw)154155156def test_levels_from_config():157    cfg = load_file_config()158    assert cfg.level_for(5)[0] == "calm"159    assert cfg.level_for(10)[0] == "calm"160    assert cfg.level_for(10.1)[0] == "normal"161    assert cfg.level_for(42.7)[0] == "stressed"162    assert cfg.level_for(99)[0] == "extreme"163    assert cfg.level_for(None)[0] == "unknown"164    assert not math.isnan(cfg.importance_weight(5))165    assert cfg.importance_weight(5) > cfg.importance_weight(1)166