"""HTTP-Client gegen einen echten lokalen Server.""" from __future__ import annotations import aiohttp import pytest from custom_components.ninstrip.api import ( NinstripAuthError, NinstripBusyError, NinstripClient, NinstripConnectionError, NinstripRequestError, NinstripResponseError, ) from tests_unit.conftest import FakeStrip from tests_unit.samples import INFO_K1Y, PRESETS_K1Y, SETTINGS_COMPILED, STATE_K1Y, TOKEN def client_for(strip: FakeStrip, session: aiohttp.ClientSession, **kwargs: float) -> NinstripClient: return NinstripClient(session, strip.host, TOKEN, port=strip.port, **kwargs) async def test_zustand_schickt_das_token_roh(strip: FakeStrip, session) -> None: strip.reply("GET", "/api/v1/device/state", payload=STATE_K1Y) zustand = await client_for(strip, session).get_state() assert zustand == STATE_K1Y assert strip.calls[-1].headers["Authorization"] == TOKEN # kein "Bearer" async def test_set_state_schickt_felder_und_gibt_die_antwort_zurueck( strip: FakeStrip, session ) -> None: antwort = {**STATE_K1Y, "brightness": 55} # die Antwort weicht von der Anfrage ab strip.reply("POST", "/api/v1/device/state", payload=antwort) ergebnis = await client_for(strip, session).set_state(brightness=78, transition=2500) assert ergebnis == antwort assert strip.calls[-1].body == {"brightness": 78, "transition": 2500} async def test_unbekannte_felder_bleiben_erhalten(strip: FakeStrip, session) -> None: strip.reply("GET", "/api/v1/device/state", payload={**STATE_K1Y, "kuenftig": 7}) assert (await client_for(strip, session).get_state())["kuenftig"] == 7 async def test_info_settings_und_presets(strip: FakeStrip, session) -> None: strip.reply("GET", "/api/v1/system/info", payload=INFO_K1Y) strip.reply("GET", "/api/v1/system/settings", payload=SETTINGS_COMPILED) strip.reply("GET", "/api/v1/device/presets", payload=PRESETS_K1Y) client = client_for(strip, session) assert await client.get_info() == INFO_K1Y assert await client.get_settings() == SETTINGS_COMPILED assert await client.get_presets() == PRESETS_K1Y async def test_sacn_wird_als_settings_typ_geschrieben(strip: FakeStrip, session) -> None: strip.reply("PUT", "/api/v1/system/settings", payload={"status": "ok"}) await client_for(strip, session).set_sacn(universe=7, start_slot=4) assert strip.calls[-1].body == {"type": "sacn", "universe": 7, "start_slot": 4} async def test_szenen_anwenden_und_sichern(strip: FakeStrip, session) -> None: strip.reply("PUT", "/api/v1/device/presets/3/apply", payload={"status": "ok"}) strip.reply("PUT", "/api/v1/device/presets/3", payload={"status": "ok"}) client = client_for(strip, session) await client.apply_preset(3) assert strip.calls[-1].path == "/api/v1/device/presets/3/apply" await client.save_preset(3, "Abend") assert strip.calls[-1].body == {"name": "Abend"} await client.save_preset(3) assert strip.calls[-1].body == {} async def test_befehle_liegen_verschachtelt(strip: FakeStrip, session) -> None: strip.reply("PUT", "/api/v1/system/command", payload={"ok": True}) client = client_for(strip, session) await client.reboot() assert strip.calls[-1].body == {"command": {"type": "reboot"}} strip.reply("PUT", "/api/v1/system/command", status=202, payload={"status": "updating"}) await client.fw_update("http://192.168.1.50:8123/api/ninstrip/firmware/k3y") assert strip.calls[-1].body == { "command": { "type": "fw_update", "url": "http://192.168.1.50:8123/api/ninstrip/firmware/k3y", } } async def test_401_ist_ein_auth_fehler(strip: FakeStrip, session) -> None: strip.reply("GET", "/api/v1/system/settings", status=401, payload={"error": "Token fehlt"}) with pytest.raises(NinstripAuthError): await client_for(strip, session).get_settings() async def test_409_ist_beschaeftigt_mit_meldung(strip: FakeStrip, session) -> None: strip.reply( "PUT", "/api/v1/system/command", status=409, payload={"error": "Update laeuft schon"} ) with pytest.raises(NinstripBusyError) as err: await client_for(strip, session).fw_update("http://192.168.1.50:8123/x") assert err.value.status == 409 assert err.value.message == "Update laeuft schon" async def test_400_ist_ein_anfragefehler_kein_beschaeftigt(strip: FakeStrip, session) -> None: strip.reply("POST", "/api/v1/device/state", status=400, payload={"error": "brightness"}) with pytest.raises(NinstripRequestError) as err: await client_for(strip, session).set_state(brightness=0) assert not isinstance(err.value, NinstripBusyError) assert (err.value.status, err.value.message) == (400, "brightness") async def test_fehlertext_ohne_json_kommt_roh(strip: FakeStrip, session) -> None: strip.reply("POST", "/api/v1/device/state", status=500, raw="peng") with pytest.raises(NinstripRequestError) as err: await client_for(strip, session).set_state(on=True) assert err.value.message == "peng" async def test_json_fehlertext_ohne_error_feld_kommt_roh(strip: FakeStrip, session) -> None: strip.reply("POST", "/api/v1/device/state", status=500, payload={"detail": "zu hell"}) with pytest.raises(NinstripRequestError) as err: await client_for(strip, session).set_state(on=True) assert "detail" in err.value.message async def test_erfolg_ohne_json_ist_ein_antwortfehler(strip: FakeStrip, session) -> None: strip.reply("GET", "/api/v1/device/state", raw="") with pytest.raises(NinstripResponseError): await client_for(strip, session).get_state() async def test_json_array_als_erfolg_ist_ein_antwortfehler(strip: FakeStrip, session) -> None: strip.reply("GET", "/api/v1/device/state", raw="[1, 2, 3]") with pytest.raises(NinstripResponseError): await client_for(strip, session).get_state() @pytest.mark.parametrize("fehlt", ["on", "brightness", "segment_count", "pixel_count"]) async def test_zustand_ohne_pflichtfeld_ist_ein_antwortfehler( strip: FakeStrip, session, fehlt: str ) -> None: strip.reply( "GET", "/api/v1/device/state", payload={k: v for k, v in STATE_K1Y.items() if k != fehlt} ) with pytest.raises(NinstripResponseError): await client_for(strip, session).get_state() @pytest.mark.parametrize("fehlt", ["mac", "product", "fw_version"]) async def test_info_ohne_pflichtfeld_ist_ein_antwortfehler( strip: FakeStrip, session, fehlt: str ) -> None: strip.reply( "GET", "/api/v1/system/info", payload={k: v for k, v in INFO_K1Y.items() if k != fehlt} ) with pytest.raises(NinstripResponseError): await client_for(strip, session).get_info() async def test_zeitueberschreitung_ist_ein_verbindungsfehler(strip: FakeStrip, session) -> None: strip.reply("GET", "/api/v1/device/state", payload=STATE_K1Y, delay=0.5) with pytest.raises(NinstripConnectionError): await client_for(strip, session, timeout=0.05).get_state() async def test_abgewiesene_verbindung_ist_ein_verbindungsfehler(session) -> None: client = NinstripClient(session, "127.0.0.1", TOKEN, port=1) with pytest.raises(NinstripConnectionError): await client.get_state() async def test_host_gibt_die_eingestellte_adresse(session) -> None: assert NinstripClient(session, "192.168.2.238", TOKEN, port=3333).host == "192.168.2.238" async def test_befehl_ohne_antwortkoerper_geht_durch(strip: FakeStrip, session) -> None: strip.reply("PUT", "/api/v1/system/command", status=204, raw="") await client_for(strip, session).reboot()