import pytest from hermes_thermal.policy import LEVELS, SCALE, ThermalPolicy, scaled def run(p, temps, t0=0.0, dt=1.0): t, out = t0, [] for temp in temps: t += dt out.append(p.update(temp, t)) return out, t def test_cool_stays_ok(): out, _ = run(ThermalPolicy(), [55, 60, 65, 71.9]) assert set(out) == {'ok'} def test_heating_escalates_at_once_even_several_levels(): p = ThermalPolicy() assert p.update(73, 1.0) == 'warm' assert p.update(78, 2.0) == 'hot' assert p.update(90, 3.0) == 'critical' # straight to the top if it asks for it assert ThermalPolicy().update(84, 1.0) == 'critical' def test_cooling_needs_the_margin_and_the_dwell_and_goes_one_step_at_a_time(): p = ThermalPolicy(dwell_s=20.0, margin_c=3.0) p.update(84, 0.0) # critical assert p.update(75, 5.0) == 'critical' # cooler, but inside the dwell assert p.update(75, 25.0) == 'hot' # dwell over, 75 < 82-3: one step down assert p.update(75, 26.0) == 'hot' # not another step at once assert p.update(73, 46.0) == 'warm' # 73 < 77-3 assert p.update(70, 47.0) == 'warm' assert p.update(68, 67.0) == 'ok' # 68 < 72-3 def test_a_temperature_hovering_at_a_threshold_does_not_flap(): p = ThermalPolicy(dwell_s=20.0, margin_c=3.0) out, _ = run(p, [72.5, 71.5, 72.2, 71.0, 72.1, 70.0, 72.3] * 6, dt=1.0) assert out[0] == 'warm' and set(out) == {'warm'} # never drops back to ok while it stays within the margin def test_a_reheat_during_recovery_escalates_again(): p = ThermalPolicy(dwell_s=10.0) p.update(84, 0.0) assert p.update(70, 20.0) == 'hot' assert p.update(83, 21.0) == 'critical' def test_scaling_respects_floors_and_never_exceeds_normal(): assert scaled(15.0, 'ok', 2.0) == 15.0 assert scaled(15.0, 'hot', 2.0) == pytest.approx(15 * 0.45) assert scaled(15.0, 'critical', 4.0) == 4.0 # the floor wins over 15*0.2 = 3 assert scaled(1.0, 'critical', 2.0) == 1.0 # a floor above normal must not raise the rate assert [scaled(30.0, lv, 8.0) for lv in LEVELS] == sorted([scaled(30.0, lv, 8.0) for lv in LEVELS], reverse=True) assert SCALE['ok'] == 1.0