from datetime import datetime, timedelta, timezone import pytest from scripts.benchmark.pressure_thresholds import evaluate_pressure_thresholds, learn_pressure_thresholds CUTOFF = datetime(2026, 10, 8, 12, tzinfo=timezone.utc) def history(device, noisy=False): start = CUTOFF - timedelta(days=15) return [{"device_id": device, "time": start + timedelta(minutes=15 * index), "monitored_value": 30 + (index % 96) / 20 + ((index // 96 % 3) - 1) * (4 if noisy else 0.1)} for index in range(1440)] def test_thresholds_follow_device_noise_after_removing_daily_pattern(): result = learn_pressure_thresholds(history("quiet") + history("noisy", True), ["quiet", "noisy"], CUTOFF) assert result["status"] == "ready" assert result["devices"]["quiet"]["relative_drop_percent"] == 3 assert result["devices"]["noisy"]["relative_drop_percent"] > 30 assert result["devices"]["quiet"]["absolute_drop_m"] == 0.5 def test_short_warmup_is_not_treated_as_a_fitted_adaptive_rule(): rows = history("P")[-4:] result = learn_pressure_thresholds(rows, ["P"], CUTOFF) assert result["status"] == "history_not_ready" assert result["required_samples_per_device"] == 1440 assert result["available_samples"] == {"P": 4} def test_event_or_future_reading_is_rejected(): with pytest.raises(ValueError, match="future"): learn_pressure_thresholds(history("P") + [{"device_id": "P", "time": CUTOFF, "monitored_value": 10}], ["P"], CUTOFF) def test_missing_grid_point_prevents_fitting(): assert learn_pressure_thresholds(history("P")[:-1], ["P"], CUTOFF)["status"] == "history_not_ready" @pytest.mark.parametrize("value", [0, -1, float("nan"), float("inf")]) def test_invalid_pressure_samples_are_rejected(value): rows = history("P") rows[0]["monitored_value"] = value with pytest.raises(ValueError, match="positive finite"): learn_pressure_thresholds(rows, ["P"], CUTOFF) def test_normal_daily_pattern_is_not_confused_with_a_burst(): fitted = learn_pressure_thresholds(history("P"), ["P"], CUTOFF) normal = [{"device_id": "P", "time": CUTOFF + timedelta(minutes=15 * i), "monitored_value": 30 + i / 20} for i in range(20)] assert not evaluate_pressure_thresholds(normal, fitted)["raised"] burst = [{**row, "monitored_value": row["monitored_value"] - 3} for row in normal] event = evaluate_pressure_thresholds(burst, fitted) assert event["raised"] assert event["signals"][0]["first_signal_at"] == (CUTOFF + timedelta(minutes=15)).isoformat() def test_absolute_floor_and_consecutive_sample_gap_are_enforced(): baseline = [{**row, "monitored_value": 5} for row in history("P")] fitted = learn_pressure_thresholds(baseline, ["P"], CUTOFF) small = [{"device_id": "P", "time": CUTOFF + timedelta(minutes=15 * i), "monitored_value": 4.8} for i in range(2)] assert not evaluate_pressure_thresholds(small, fitted)["raised"] separated = [{"device_id": "P", "time": CUTOFF + timedelta(minutes=30 * i), "monitored_value": 4} for i in range(2)] assert not evaluate_pressure_thresholds(separated, fitted)["raised"]