diff --git a/net_alerter/test_net_alerter.py b/net_alerter/test_net_alerter.py index ee88560..2db9bce 100644 --- a/net_alerter/test_net_alerter.py +++ b/net_alerter/test_net_alerter.py @@ -15,6 +15,15 @@ sys.path.insert(0, str(Path(__file__).parent)) import net_alerter + +def cleanup_flap_state(): + """Reset flap detection state between tests.""" + net_alerter._flap_window.clear() + net_alerter._interface_recovering = False + if net_alerter._recovery_timer: + net_alerter._recovery_timer.cancel() + net_alerter._recovery_timer = None + def test_hostname_dedup_when_hostname_equals_ip(): """Test that hostname is not repeated when DNS fails (hostname == IP).""" # Simulate a device where hostname lookup returned the IP address itself @@ -32,6 +41,7 @@ def test_hostname_dedup_when_hostname_equals_ip(): def test_hostname_shown_when_different_from_ip(): """Test that hostname is shown separately when it differs from IP.""" + cleanup_flap_state() mac = "88:a2:9e:8e:f2:90" ip = "10.0.0.0" hostname = "mydevice.local" @@ -47,6 +57,7 @@ def test_hostname_shown_when_different_from_ip(): def test_oui_lookup_with_inline_dict(): """Test that OUI lookup returns vendor for known MACs.""" + cleanup_flap_state() # These are real OUI prefixes test_cases = [ ("88:a2:9e", "Apple"), # Apple @@ -66,12 +77,14 @@ def test_oui_lookup_with_inline_dict(): def test_oui_lookup_unknown_vendor(): """Test that unknown OUI returns 'unknown'.""" + cleanup_flap_state() vendor = net_alerter.lookup_oui("aa:bb:cc:dd:ee:ff") assert vendor == "unknown", f"Expected 'unknown' for unknown OUI, got {vendor}" def test_departure_format_dedup(): """Test that departure message also deduplicates hostname and IP.""" + cleanup_flap_state() mac = "88:a2:9e:8e:f2:90" ip = "10.0.0.0" hostname = ip @@ -84,6 +97,7 @@ def test_departure_format_dedup(): def test_infrastructure_ips_never_alert(): """Test that infrastructure IPs (gateway, broadcast, self) don't trigger alerts.""" + cleanup_flap_state() # Reset known_devices net_alerter.known_devices.clear() @@ -113,6 +127,7 @@ def test_infrastructure_ips_never_alert(): def test_departure_debounce_15min(): """Test that departure alerts are debounced for 15 minutes.""" + cleanup_flap_state() net_alerter.known_devices.clear() net_alerter.departure_timers.clear() @@ -150,6 +165,7 @@ def test_departure_debounce_15min(): def test_re_arrival_within_5min_suppresses_alert(): """Test that device re-arrival within 5 min of departure suppresses ARRIVED alert.""" + cleanup_flap_state() net_alerter.known_devices.clear() net_alerter.departure_timers.clear() net_alerter.last_departed_time.clear() @@ -192,6 +208,7 @@ def test_re_arrival_within_5min_suppresses_alert(): def test_dhcp_renewal_dedup_30min(): """Test that DHCP renewal from known device (< 30 min old) doesn't send ARRIVED alert.""" + cleanup_flap_state() net_alerter.known_devices.clear() alert_calls = [] @@ -223,10 +240,12 @@ def test_dhcp_renewal_dedup_30min(): def test_re_arrival_cancels_departure_timer(): """ + cleanup_flap_state() Regression test: device departs → timer starts → device re-arrives BEFORE timer fires. Timer should be cancelled and NO departure alert should be sent. This tests the exact bug scenario: on_arrival() must cancel any pending timer. """ + cleanup_flap_state() net_alerter.known_devices.clear() net_alerter.departure_timers.clear() net_alerter.last_departed_time.clear() @@ -295,6 +314,7 @@ def test_rapid_flap_no_duplicate_arrived_alerts(): - on_arrival() checks for departing flag and silently cancels timer - No duplicate ARRIVED alerts """ + cleanup_flap_state() net_alerter.known_devices.clear() net_alerter.departure_timers.clear() net_alerter.last_departed_time.clear()