feat(dyndns): add get_ddns_status() for os-ddclient enabled/running state
CI / test (3.10) (push) Failing after 8s
CI / test (3.11) (push) Failing after 7s
CI / test (3.12) (push) Failing after 7s
CI / test (3.9) (push) Failing after 7s

Verified against a live OPNsense 24.7 instance: the service id is
"ddclient" but the REST module is "dyndns" (/api/ddclient/* all 404).
Scoped to enabled/running only -- no ddclient/dyndns account was
configured on the test device to verify a per-account "registered IP"
shape against, so that comparison is deliberately left out rather than
guessed.
This commit is contained in:
Christian Manivong
2026-07-15 13:52:20 +02:00
parent 26470676ce
commit c8caa14176
2 changed files with 87 additions and 0 deletions
+60
View File
@@ -1060,6 +1060,66 @@ class TestGetCertificates:
driver._post = lambda path, data=None: (_ for _ in ()).throw(Exception("404"))
assert driver.get_certificates() == []
# ---------------------------------------------------------------------------
# get_ddns_status()
#
# Endpoints verified 2026-07-15 against a live OPNsense 24.7 instance: the
# service id is "ddclient" (get_services()) but the API module is "dyndns",
# not "ddclient" -- /api/ddclient/* all 404, /api/dyndns/service/status and
# /api/dyndns/settings/get are the real paths. Scoped to enabled/running
# only: no live ddclient/dyndns account was configured on the test device
# to verify a per-account "registered IP" shape against, so that comparison
# is deliberately not implemented here (would be guesswork).
# ---------------------------------------------------------------------------
DYNDNS_STATUS_RESPONSE = {"status": "running"}
DYNDNS_SETTINGS_ENABLED = {"ddclient": {"general": {"enabled": "1"}}}
DYNDNS_SETTINGS_DISABLED = {"ddclient": {"general": {"enabled": "0"}}}
class TestGetDdnsStatus:
def _fake_get(self, status_response, settings_response):
def fake_get(path):
if "service/status" in path:
return status_response
return settings_response
return fake_get
def test_calls_expected_endpoints(self, driver):
calls = []
def fake_get(path):
calls.append(path)
if "service/status" in path:
return DYNDNS_STATUS_RESPONSE
return DYNDNS_SETTINGS_ENABLED
driver._get = fake_get
driver.get_ddns_status()
assert calls == ["/api/dyndns/service/status", "/api/dyndns/settings/get"]
def test_enabled_and_running(self, driver):
driver._get = self._fake_get(DYNDNS_STATUS_RESPONSE, DYNDNS_SETTINGS_ENABLED)
result = driver.get_ddns_status()
assert result == {"enabled": True, "running": True}
def test_enabled_but_not_running(self, driver):
driver._get = self._fake_get({"status": "stopped"}, DYNDNS_SETTINGS_ENABLED)
result = driver.get_ddns_status()
assert result == {"enabled": True, "running": False}
def test_disabled(self, driver):
driver._get = self._fake_get(DYNDNS_STATUS_RESPONSE, DYNDNS_SETTINGS_DISABLED)
result = driver.get_ddns_status()
assert result == {"enabled": False, "running": True}
def test_api_error_returns_none(self, driver):
driver._get = lambda path: (_ for _ in ()).throw(Exception("404"))
assert driver.get_ddns_status() is None
# ---------------------------------------------------------------------------
# _post()
# ---------------------------------------------------------------------------