"""Tests for ws-api WebSocket plugin""" import os import sys import json import time import threading import pytest from pathlib import Path PLUGIN_DIR = Path(__file__).parent.parent / "store" / "NebulaShell" / "ws-api" sys.path.insert(0, str(PLUGIN_DIR)) import importlib.util spec = importlib.util.spec_from_file_location("ws_api_main", str(PLUGIN_DIR / "main.py")) main_module = importlib.util.module_from_spec(spec) spec.loader.exec_module(main_module) WsApi = main_module.WsApi class TestWsApi: def test_lifecycle(self): api = WsApi() api.init() api.start() assert api._running is True api.stop() assert api._running is False def test_get_info(self): api = WsApi() info = api.get_info() assert "host" in info assert "port" in info assert "running" in info assert "websockets_available" in info def test_register_handler(self): api = WsApi() results = [] api.register_handler("custom", lambda data, ctx: results.append(data)) assert "custom" in api._handlers def test_default_host_port(self): api = WsApi() assert api._host == "127.0.0.1" assert api._port == 8081 class TestWsApiDispatch: @pytest.mark.asyncio async def test_ping_pong(self): api = WsApi() class FakeWs: def __init__(self): self.sent = [] self.remote_address = ("127.0.0.1", 12345) async def send(self, msg): self.sent.append(json.loads(msg)) ws = FakeWs() await api._dispatch(ws, '{"type":"ping"}', "test") assert len(ws.sent) == 1 assert ws.sent[0] == {"type": "pong"} @pytest.mark.asyncio async def test_invalid_json(self): api = WsApi() class FakeWs: def __init__(self): self.sent = [] self.remote_address = ("127.0.0.1", 12345) async def send(self, msg): self.sent.append(json.loads(msg)) ws = FakeWs() await api._dispatch(ws, "not json", "test") assert len(ws.sent) == 1 assert ws.sent[0]["type"] == "error" @pytest.mark.asyncio async def test_subscribe(self): api = WsApi() class FakeWs: def __init__(self): self.sent = [] self.remote_address = ("127.0.0.1", 12345) async def send(self, msg): self.sent.append(json.loads(msg)) ws = FakeWs() await api._dispatch(ws, '{"type":"subscribe","topic":"news"}', "test") assert "news" in api._connections assert len(api._connections["news"]) == 1 @pytest.mark.asyncio async def test_unsubscribe(self): api = WsApi() api._connections["test-topic"] = {"addr1"} class FakeWs: def __init__(self): self.sent = [] self.remote_address = ("127.0.0.1", 12345) async def send(self, msg): self.sent.append(json.loads(msg)) ws = FakeWs() await api._dispatch(ws, '{"type":"unsubscribe","topic":"test-topic"}', "addr1") assert "test-topic" not in api._connections or len(api._connections["test-topic"]) == 0 @pytest.mark.asyncio async def test_custom_handler(self): api = WsApi() results = [] def handler(data, ctx): results.append((data, ctx)) return {"processed": True} api.register_handler("my_action", handler) class FakeWs: def __init__(self): self.sent = [] self.remote_address = ("127.0.0.1", 12345) async def send(self, msg): self.sent.append(json.loads(msg)) ws = FakeWs() await api._dispatch(ws, '{"type":"my_action","value":42}', "test") assert len(results) == 1 assert results[0][0]["value"] == 42 assert len(ws.sent) == 1 assert ws.sent[0]["type"] == "my_action_response" assert ws.sent[0]["data"]["processed"] is True if __name__ == '__main__': pytest.main([__file__, '-v'])