Files
sentinel-home-ai/fam-core/tests/test_motion_notifier.py

115 lines
3.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import time
from fam_core.motion_notifier.motion_notifier import MotionNotifier
def _cfg(**overrides):
base = {
"enabled": True,
"poll_enabled": False,
"dsm_host": "192.168.50.64",
"dsm_port": 5000,
"dsm_account": "ericwyuan",
"dsm_password": "secret",
"oracle_base_url": "http://oracle.example:5000",
"oracle_token": "tok123",
"heartbeat_interval_sec": 300,
"timeout_sec": 5,
}
base.update(overrides)
return {"motion_notifier": base}
def _make_notifier(monkeypatch, **cfg_overrides):
monkeypatch.setattr(
"fam_core.motion_notifier.motion_notifier.load_config",
lambda: _cfg(**cfg_overrides))
return MotionNotifier()
class _FakeResp:
def __init__(self, status_code=200, payload=None, text=""):
self.status_code = status_code
self._payload = payload or {}
self.text = text
def json(self):
return self._payload
# ----------------------------------------------------------------------
# 心跳跟轮询无关enabled=true 就该跑)
# ----------------------------------------------------------------------
def test_send_heartbeat_success_updates_state(monkeypatch):
n = _make_notifier(monkeypatch)
calls = []
def fake_post(url, json=None, timeout=None):
calls.append((url, json))
return _FakeResp(200, {"status": "ok", "received": 0, "stored": 0})
monkeypatch.setattr("fam_core.motion_notifier.motion_notifier.requests.post", fake_post)
ok = n.send_heartbeat()
assert ok is True
assert n._last_heartbeat_at is not None
assert n._last_heartbeat_error is None
assert calls[0][0] == "http://oracle.example:5000/api/ss/motion"
assert calls[0][1]["events"] == []
assert calls[0][1]["token"] == "tok123"
def test_send_heartbeat_http_error_records_failure(monkeypatch):
n = _make_notifier(monkeypatch)
def fake_post(url, json=None, timeout=None):
return _FakeResp(500, {}, text="boom")
monkeypatch.setattr("fam_core.motion_notifier.motion_notifier.requests.post", fake_post)
ok = n.send_heartbeat()
assert ok is False
assert n._last_heartbeat_error == "HTTP 500"
def test_send_heartbeat_network_exception_records_failure(monkeypatch):
import requests as _requests
n = _make_notifier(monkeypatch)
def fake_post(url, json=None, timeout=None):
raise _requests.RequestException("connection refused")
monkeypatch.setattr("fam_core.motion_notifier.motion_notifier.requests.post", fake_post)
ok = n.send_heartbeat()
assert ok is False
assert "connection refused" in n._last_heartbeat_error
def test_start_runs_heartbeat_thread_even_when_poll_disabled(monkeypatch):
"""核心诉求: 心跳跟"是否轮询 SS"是两回事——poll_enabled=false 时轮询线程
不应该启动,但心跳线程必须照样跑,否则甲骨文永远收不到心跳,
has_motion_in_range_local() 会一直 fail-open省配额的效果就没了。"""
n = _make_notifier(monkeypatch, poll_enabled=False, heartbeat_interval_sec=3600)
monkeypatch.setattr(
"fam_core.motion_notifier.motion_notifier.requests.post",
lambda url, json=None, timeout=None: _FakeResp(200, {"stored": 0}))
try:
n.start()
time.sleep(0.2)
assert n.is_alive() is False # 轮询线程未启动
assert n._hb_thread is not None and n._hb_thread.is_alive()
finally:
n.stop()
def test_start_does_nothing_when_disabled(monkeypatch):
n = _make_notifier(monkeypatch, enabled=False)
n.start()
time.sleep(0.1)
assert n._hb_thread is None
assert n.is_alive() is False
def test_status_includes_heartbeat_fields(monkeypatch):
n = _make_notifier(monkeypatch)
st = n.status()
assert "heartbeat_running" in st
assert "last_heartbeat_at" in st
assert "last_heartbeat_error" in st