test(url-scheme): WLED route-level integration + IPv6 regression
TestWLEDSchemeInference in test_devices_routes covers the POST/PUT create-and-update flow with a stubbed WLED provider so the infer_http_scheme integration hop has end-to-end coverage instead of just the unit tests. test_url_scheme grows public IPv6 (Cloudflare / Google / Quad9 DNS), bracketed-form, and ULA cases. Adds an explicit pin for the Python ipaddress documentation-prefix quirk (2001:db8::/32 is is_private, so it routes to http:// even though some audits colloquially call it "public").
This commit is contained in:
@@ -342,6 +342,93 @@ class TestPairDevice:
|
||||
assert resp.status_code == 422
|
||||
|
||||
|
||||
class TestWLEDSchemeInference:
|
||||
"""End-to-end pin for the URL-scheme inference applied to WLED devices.
|
||||
|
||||
The :func:`infer_http_scheme` helper has its own exhaustive unit
|
||||
coverage in :file:`tests/test_url_scheme.py`; this class adds the
|
||||
*integration* hop that REVIEW_TODO calls out — POST/PUT a bare host
|
||||
and assert the stored device carries the inferred scheme so a future
|
||||
refactor of the route handler can't quietly drop the call.
|
||||
|
||||
The WLED provider is stubbed out so the test does not need a real
|
||||
device on the network — we just need ``validate_device`` to return a
|
||||
successful payload.
|
||||
"""
|
||||
|
||||
@pytest.fixture
|
||||
def _stub_wled_validate(self, monkeypatch):
|
||||
async def fake_validate(self, url): # noqa: ARG001 — provider self
|
||||
return {"led_count": 30}
|
||||
|
||||
from ledgrab.core.devices.wled_provider import WLEDDeviceProvider
|
||||
|
||||
monkeypatch.setattr(WLEDDeviceProvider, "validate_device", fake_validate)
|
||||
return fake_validate
|
||||
|
||||
def test_create_wled_with_bare_private_ip_normalises_to_http(
|
||||
self, client, device_store, _stub_wled_validate
|
||||
):
|
||||
resp = client.post(
|
||||
"/api/v1/devices",
|
||||
json={
|
||||
"name": "WLED desk",
|
||||
"device_type": "wled",
|
||||
"url": "192.168.1.42",
|
||||
"led_count": 30,
|
||||
},
|
||||
)
|
||||
assert resp.status_code == 201, resp.text
|
||||
device_id = resp.json()["id"]
|
||||
assert device_store.get_device(device_id).url == "http://192.168.1.42"
|
||||
|
||||
def test_create_wled_with_public_host_normalises_to_https(
|
||||
self, client, device_store, _stub_wled_validate
|
||||
):
|
||||
resp = client.post(
|
||||
"/api/v1/devices",
|
||||
json={
|
||||
"name": "WLED cloud",
|
||||
"device_type": "wled",
|
||||
"url": "wled.example.com",
|
||||
"led_count": 30,
|
||||
},
|
||||
)
|
||||
assert resp.status_code == 201, resp.text
|
||||
device_id = resp.json()["id"]
|
||||
assert device_store.get_device(device_id).url == "https://wled.example.com"
|
||||
|
||||
def test_create_wled_strips_trailing_slash_then_infers(
|
||||
self, client, device_store, _stub_wled_validate
|
||||
):
|
||||
resp = client.post(
|
||||
"/api/v1/devices",
|
||||
json={
|
||||
"name": "WLED rack",
|
||||
"device_type": "wled",
|
||||
"url": "wled-rack.local/",
|
||||
"led_count": 30,
|
||||
},
|
||||
)
|
||||
assert resp.status_code == 201, resp.text
|
||||
device_id = resp.json()["id"]
|
||||
assert device_store.get_device(device_id).url == "http://wled-rack.local"
|
||||
|
||||
def test_update_wled_with_bare_host_normalises_url(self, client, device_store):
|
||||
existing = device_store.create_device(
|
||||
name="WLED desk",
|
||||
url="http://192.168.1.42",
|
||||
led_count=30,
|
||||
device_type="wled",
|
||||
)
|
||||
resp = client.put(
|
||||
f"/api/v1/devices/{existing.id}",
|
||||
json={"url": "10.0.0.5"},
|
||||
)
|
||||
assert resp.status_code == 200, resp.text
|
||||
assert device_store.get_device(existing.id).url == "http://10.0.0.5"
|
||||
|
||||
|
||||
class TestPairThenCreateFlow:
|
||||
"""End-to-end coverage: pair, then persist; assert the token is
|
||||
encrypted at rest and decrypted in to_config(), and that the API
|
||||
|
||||
Reference in New Issue
Block a user