mirror of
https://github.com/jokob-sk/NetAlertX.git
synced 2026-10-03 03:05:04 -04:00
106 lines
4.5 KiB
Python
106 lines
4.5 KiB
Python
"""
|
|
Regression coverage for GitHub issue #1818: print_scan_stats() (device_handling.py)
|
|
undercounted "down_alerts"/"new_down_alerts" whenever a device's stored MAC and its
|
|
CurrentScan MAC differed only by case.
|
|
|
|
DevicesView exposes devMac as LOWER(IFNULL(devMac, '')) - a function-derived VIEW
|
|
column, not a bare table column. print_scan_stats()'s down-alert subqueries compared
|
|
it against CurrentScan.scanMac as `devMac = scanMac` (DevicesView column on the LEFT),
|
|
which - unlike a bare COLLATE NOCASE column comparison - loses NOCASE collation and
|
|
becomes a case-sensitive byte comparison. Reversing the operand order to
|
|
`scanMac = devMac` restores NOCASE collation (SQLite gives precedence to the left
|
|
operand's column collation; scanMac is a real COLLATE-free CurrentScan column but the
|
|
comparison still resolves via devMac's declared NOCASE affinity once devMac is on the
|
|
right, verified empirically against a real SQLite instance built from the actual
|
|
DevicesView definition - see server/scan/device_handling.py print_scan_stats()).
|
|
|
|
This function is diagnostic/verbose-log-only (mylog("verbose"/"trace", ...), no other
|
|
consumer) - it does not drive real "Device Down" notifications, which go through
|
|
session_events.insert_events() -> presence.current_scan_presence_condition() instead
|
|
and were already safe (scanMac on the left there). This test covers the diagnostic
|
|
counter only.
|
|
"""
|
|
|
|
import sys
|
|
import os
|
|
|
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
|
|
from db_test_helpers import ( # noqa: E402
|
|
DummyDB,
|
|
make_device_dict,
|
|
insert_device_from_dict,
|
|
make_current_scan_dict,
|
|
insert_current_scan_row_from_dict,
|
|
)
|
|
|
|
import scan.device_handling as device_handling # noqa: E402
|
|
from scan.device_handling import print_scan_stats # noqa: E402
|
|
|
|
|
|
def _run_stats(conn, monkeypatch):
|
|
"""Call print_scan_stats() and return the logged down_alerts/new_down_alerts
|
|
counts, parsed straight out of its own mylog("verbose", ...) calls - this
|
|
exercises the real function/query rather than a re-derived copy of it."""
|
|
logged = {}
|
|
|
|
def fake_mylog(level, parts):
|
|
"""Stand in for logger.mylog: parse the down_alerts/new_down_alerts
|
|
counts straight out of print_scan_stats()'s own log lines."""
|
|
text = "".join(parts) if isinstance(parts, list) else str(parts)
|
|
if "Down Alerts" in text and "New Down Alerts" not in text:
|
|
logged["down_alerts"] = int(text.rsplit(":", 1)[1].strip())
|
|
elif "New Down Alerts" in text:
|
|
logged["new_down_alerts"] = int(text.rsplit(":", 1)[1].strip())
|
|
|
|
monkeypatch.setattr(device_handling, "mylog", fake_mylog)
|
|
|
|
db = DummyDB(conn)
|
|
print_scan_stats(db)
|
|
return logged
|
|
|
|
|
|
class TestScanStatsMacCase:
|
|
def test_matching_mac_different_case_is_not_a_false_down_alert(self, scan_db, monkeypatch):
|
|
"""Device stored lowercase, present this cycle under an uppercase MAC
|
|
(e.g. a plugin like FREEBOX reporting uppercase) must not be counted
|
|
as down - this is the exact scenario from issue #1818."""
|
|
device = make_device_dict(
|
|
mac="54:ef:44:9a:b6:7f",
|
|
devAlertDown=1,
|
|
devCanSleep=0,
|
|
devPresentLastScan=1,
|
|
)
|
|
insert_device_from_dict(scan_db, device)
|
|
|
|
current_scan = make_current_scan_dict(mac="54:EF:44:9A:B6:7F")
|
|
insert_current_scan_row_from_dict(scan_db, current_scan)
|
|
|
|
stats = _run_stats(scan_db, monkeypatch)
|
|
|
|
assert stats["down_alerts"] == 0
|
|
assert stats["new_down_alerts"] == 0
|
|
|
|
def test_truly_absent_device_is_still_counted_as_down(self, scan_db, monkeypatch):
|
|
"""Sanity check: a device genuinely missing from CurrentScan this
|
|
cycle must still be counted - the fix must not mask real down alerts.
|
|
|
|
An unrelated CurrentScan row is seeded too: print_scan_stats() GROUPs
|
|
BY scanSourcePlugin, so an empty CurrentScan produces zero result rows
|
|
and print_scan_stats() falls back to its hardcoded all-zero stub - that
|
|
would make this assertion pass for the wrong reason."""
|
|
device = make_device_dict(
|
|
mac="54:ef:44:9a:b6:7f",
|
|
devAlertDown=1,
|
|
devCanSleep=0,
|
|
devPresentLastScan=1,
|
|
)
|
|
insert_device_from_dict(scan_db, device)
|
|
|
|
other_scan = make_current_scan_dict(mac="11:22:33:44:55:66")
|
|
insert_current_scan_row_from_dict(scan_db, other_scan)
|
|
|
|
stats = _run_stats(scan_db, monkeypatch)
|
|
|
|
assert stats["down_alerts"] == 1
|
|
assert stats["new_down_alerts"] == 1
|