diff --git a/napalm_yealink/__pycache__/yealink.cpython-312.pyc b/napalm_yealink/__pycache__/yealink.cpython-312.pyc index dee9270..8da1279 100644 Binary files a/napalm_yealink/__pycache__/yealink.cpython-312.pyc and b/napalm_yealink/__pycache__/yealink.cpython-312.pyc differ diff --git a/napalm_yealink/yealink.py b/napalm_yealink/yealink.py index 4e848a6..97f4237 100644 --- a/napalm_yealink/yealink.py +++ b/napalm_yealink/yealink.py @@ -21,14 +21,13 @@ import requests from requests.exceptions import RequestException from napalm.base.exceptions import ConnectionClosedException, ConnectionException -from napalm_device_types import AccessPointDriver, FingerprintRule +from napalm_device_types import FingerprintRule, PhoneDriver from napalm_device_types.models import HealthMetricsDict -class YealinkDriver(AccessPointDriver): +class YealinkDriver(PhoneDriver): """NAPALM driver for Yealink IP phones (T-series, W-series, CP-series, VP-series).""" - TYPE_LABEL = "Phone" VENDOR = "Yealink" DRIVER_NAME = "yealink" diff --git a/tests/__pycache__/test_fingerprint.cpython-312-pytest-9.0.3.pyc b/tests/__pycache__/test_fingerprint.cpython-312-pytest-9.0.3.pyc deleted file mode 100644 index 5849672..0000000 Binary files a/tests/__pycache__/test_fingerprint.cpython-312-pytest-9.0.3.pyc and /dev/null differ diff --git a/tests/test_fingerprint.py b/tests/test_fingerprint.py index 0808f94..43e99dd 100644 --- a/tests/test_fingerprint.py +++ b/tests/test_fingerprint.py @@ -1,6 +1,6 @@ """Fingerprint tests for YealinkDriver.""" -from napalm_device_types import DeviceTypeDriver, FingerprintRule +from napalm_device_types import DeviceTypeDriver, PhoneDriver, role_keys_of from napalm_yealink import YealinkDriver @@ -24,9 +24,18 @@ def test_snmp_oid_prefix(): assert YealinkDriver.SNMP_OBJECT_ID_PREFIX == "1.3.6.1.4.1.37403" -def test_http_fingerprint_mandatory_yealink(): - mandatory = [r for r in YealinkDriver.HTTP_FINGERPRINT if r.mandatory] - assert any(r.pattern == "yealink" for r in mandatory) +def test_yealink_pattern_is_decisive_but_not_mandatory(): + """A mandatory rule disqualifies the driver whenever the pattern is absent. + + Some Yealink models answer / with a redirect to their API page, whose body + carries no "yealink" anywhere — making the rule mandatory would rule out a + real Yealink phone. It carries decisive weight instead, so a page that does + say "yealink" still wins by a wide margin. + """ + yealink_rules = [r for r in YealinkDriver.HTTP_FINGERPRINT if r.pattern == "yealink"] + assert yealink_rules, "the vendor pattern must be present at all" + assert all(not r.mandatory for r in yealink_rules) + assert max(r.weight for r in yealink_rules) >= 10.0 def test_fingerprint_matches_t58_title(): @@ -36,8 +45,16 @@ def test_fingerprint_matches_t58_title(): assert all(r.pattern in combined for r in mandatory) -def test_fingerprint_does_not_match_cisco(): - """Cisco-Seite enthält kein 'yealink' → mandatory Pattern fehlt.""" +def test_fingerprint_scores_nothing_on_a_cisco_page(): + """Without a mandatory rule, what keeps a Cisco phone out is that none of + Yealink's patterns match it at all — so the driver contributes zero score + and never wins.""" combined = "cisco ip phone cp-8841 " - mandatory = [r for r in YealinkDriver.HTTP_FINGERPRINT if r.mandatory] - assert not all(r.pattern in combined for r in mandatory) + assert not any(r.pattern in combined for r in YealinkDriver.HTTP_FINGERPRINT) + + +def test_declares_the_phone_role_not_access_point(): + """A desk phone used to inherit AccessPointDriver for want of anywhere + better, which put it in netOrk's wireless page and AP profile pickers.""" + assert issubclass(YealinkDriver, PhoneDriver) + assert role_keys_of(YealinkDriver) == ["phone"]