1from ucml import pricepaths as pp2from ucml import config as C34H = 3600.056def test_transient_removed():7 # A -> B (6h) -> A : B is a transient, path collapses to a single level A8 ts = [0.0, 10 * H, 16 * H]9 pr = [100.0, 110.0, 100.0]10 s = pp.sustain(ts, pr, last_seen=100 * H)11 assert [p for _, p in s.levels] == [100.0]12 assert s.n_transients == 11314def test_sustained_change_kept():15 ts = [0.0, 10 * H, 10 * H + 3 * 86400]16 pr = [100.0, 110.0, 100.0]17 s = pp.sustain(ts, pr, last_seen=20 * 86400)18 assert [p for _, p in s.levels] == [100.0, 110.0, 100.0]19 assert s.n_transients == 02021def test_null_dropped_and_counted():22 s = pp.sustain([0.0, H, 2 * H], [20000.0, None, 19000.0], last_seen=10 * 86400)23 assert s.n_null == 1 and [p for _, p in s.levels] == [20000.0, 19000.0]2425def test_classify_cut_rise_minor():26 ev = pp.classify([(0.0, 20000.0), (1.0, 19000.0), (2.0, 18950.0), (3.0, 21000.0)])27 kinds = [e["kind"] for e in ev]28 assert kinds == ["entry", "cut", "minor", "rise"]29 assert abs(ev[1]["change_pct"] + 0.05) < 1e-93031def test_oscillation_flag_threshold():32 ts, pr = [], []33 t = 0.034 for k in range(8):35 ts.append(t); pr.append(100.0 if k % 2 == 0 else 110.0); t += 6 * H36 s = pp.sustain(ts, pr, last_seen=t)37 assert s.n_transients >= C.OSCILLATION_MAX_TRANSIENTS38