Merge branch 'dev' into feat/cb2ri-gestures
This commit is contained in:
63
test/unit/api/v1/endpoints/test_logs_endpoint.py
Normal file
63
test/unit/api/v1/endpoints/test_logs_endpoint.py
Normal file
@@ -0,0 +1,63 @@
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from fastapi import FastAPI
|
||||
from fastapi.testclient import TestClient
|
||||
from starlette.responses import StreamingResponse
|
||||
|
||||
from control_backend.api.v1.endpoints import logs
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def client():
|
||||
"""TestClient with logs router included."""
|
||||
app = FastAPI()
|
||||
app.include_router(logs.router)
|
||||
return TestClient(app)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_log_stream_endpoint_lines(client):
|
||||
"""Call /logs/stream with a mocked ZMQ socket to cover all lines."""
|
||||
|
||||
# Dummy socket to mock ZMQ behavior
|
||||
class DummySocket:
|
||||
def __init__(self):
|
||||
self.subscribed = []
|
||||
self.connected = False
|
||||
self.recv_count = 0
|
||||
|
||||
def subscribe(self, topic):
|
||||
self.subscribed.append(topic)
|
||||
|
||||
def connect(self, addr):
|
||||
self.connected = True
|
||||
|
||||
async def recv_multipart(self):
|
||||
# Return one message, then stop generator
|
||||
if self.recv_count == 0:
|
||||
self.recv_count += 1
|
||||
return (b"INFO", b"test message")
|
||||
else:
|
||||
raise StopAsyncIteration
|
||||
|
||||
dummy_socket = DummySocket()
|
||||
|
||||
# Patch Context.instance().socket() to return dummy socket
|
||||
with patch("control_backend.api.v1.endpoints.logs.Context.instance") as mock_context:
|
||||
mock_context.return_value.socket.return_value = dummy_socket
|
||||
|
||||
# Call the endpoint directly
|
||||
response = await logs.log_stream()
|
||||
assert isinstance(response, StreamingResponse)
|
||||
|
||||
# Fetch one chunk from the generator
|
||||
gen = response.body_iterator
|
||||
chunk = await gen.__anext__()
|
||||
if isinstance(chunk, bytes):
|
||||
chunk = chunk.decode("utf-8")
|
||||
assert "data:" in chunk
|
||||
|
||||
# Optional: assert subscribe/connect were called
|
||||
assert dummy_socket.subscribed # at least some log levels subscribed
|
||||
assert dummy_socket.connected # connect was called
|
||||
45
test/unit/api/v1/endpoints/test_message_endpoint.py
Normal file
45
test/unit/api/v1/endpoints/test_message_endpoint.py
Normal file
@@ -0,0 +1,45 @@
|
||||
import json
|
||||
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from control_backend.api.v1.endpoints import message
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def client():
|
||||
"""FastAPI TestClient for the message router."""
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
app.include_router(message.router)
|
||||
return TestClient(app)
|
||||
|
||||
|
||||
def test_receive_message_post(client, monkeypatch):
|
||||
"""Test POST /message endpoint sends message to pub socket."""
|
||||
|
||||
# Dummy pub socket to capture sent messages
|
||||
class DummyPubSocket:
|
||||
def __init__(self):
|
||||
self.sent = []
|
||||
|
||||
async def send_multipart(self, msg):
|
||||
self.sent.append(msg)
|
||||
|
||||
dummy_socket = DummyPubSocket()
|
||||
|
||||
# Patch app.state.endpoints_pub_socket
|
||||
client.app.state.endpoints_pub_socket = dummy_socket
|
||||
|
||||
data = {"message": "Hello world"}
|
||||
response = client.post("/message", json=data)
|
||||
|
||||
assert response.status_code == 202
|
||||
assert response.json() == {"status": "Message received"}
|
||||
|
||||
# Ensure the message was sent via pub_socket
|
||||
assert len(dummy_socket.sent) == 1
|
||||
topic, body = dummy_socket.sent[0]
|
||||
parsed = json.loads(body.decode("utf-8"))
|
||||
assert parsed["message"] == "Hello world"
|
||||
16
test/unit/api/v1/endpoints/test_router.py
Normal file
16
test/unit/api/v1/endpoints/test_router.py
Normal file
@@ -0,0 +1,16 @@
|
||||
from fastapi.routing import APIRoute
|
||||
|
||||
from control_backend.api.v1.router import api_router # <--- corrected import
|
||||
|
||||
|
||||
def test_router_includes_expected_paths():
|
||||
"""Ensure api_router includes main router prefixes."""
|
||||
routes = [r for r in api_router.routes if isinstance(r, APIRoute)]
|
||||
paths = [r.path for r in routes]
|
||||
|
||||
# Ensure at least one route under each prefix exists
|
||||
assert any(p.startswith("/robot") for p in paths)
|
||||
assert any(p.startswith("/message") for p in paths)
|
||||
assert any(p.startswith("/sse") for p in paths)
|
||||
assert any(p.startswith("/logs") for p in paths)
|
||||
assert any(p.startswith("/program") for p in paths)
|
||||
24
test/unit/api/v1/endpoints/test_sse_endpoint.py
Normal file
24
test/unit/api/v1/endpoints/test_sse_endpoint.py
Normal file
@@ -0,0 +1,24 @@
|
||||
import pytest
|
||||
from fastapi import FastAPI
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from control_backend.api.v1.endpoints import sse
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def app():
|
||||
app = FastAPI()
|
||||
app.include_router(sse.router)
|
||||
return app
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def client(app):
|
||||
return TestClient(app)
|
||||
|
||||
|
||||
def test_sse_route_exists(client):
|
||||
"""Minimal smoke test to ensure /sse route exists and responds."""
|
||||
response = client.get("/sse")
|
||||
# Since implementation is not done, we only assert it doesn't crash
|
||||
assert response.status_code == 200
|
||||
Reference in New Issue
Block a user