Files
pepperplus-cb/test/integration/api/endpoints/test_program_endpoint.py
2025-11-12 18:04:39 +01:00

132 lines
4.1 KiB
Python

import json
from unittest.mock import AsyncMock
import pytest
from fastapi import FastAPI
from fastapi.testclient import TestClient
from control_backend.api.v1.endpoints import program
from control_backend.schemas.message import Message
from control_backend.schemas.program import Program
@pytest.fixture
def app():
"""Create a FastAPI app with the /program route and mock socket."""
app = FastAPI()
app.include_router(program.router)
return app
@pytest.fixture
def client(app):
"""Create a TestClient."""
return TestClient(app)
def make_valid_program_dict():
"""Helper to create a valid Program JSON structure."""
return {
"phases": [
{
"id": "phase1",
"name": "basephase",
"nextPhaseId": "phase2",
"phaseData": {
"norms": [{"id": "n1", "name": "norm", "value": "be nice"}],
"goals": [
{"id": "g1", "name": "goal", "description": "test goal", "achieved": False}
],
"triggers": [
{
"id": "t1",
"label": "trigger",
"type": "keyword",
"value": ["stop", "exit"],
}
],
},
}
]
}
def test_receive_program_success(client):
"""Valid Program JSON should be parsed and sent through the socket."""
mock_pub_socket = AsyncMock()
client.app.state.endpoints_pub_socket = mock_pub_socket
program_dict = make_valid_program_dict()
message_body = json.dumps(program_dict)
msg = Message(message=message_body)
# Act
response = client.post("/program", json=msg.model_dump())
# Assert
assert response.status_code == 202
assert response.json() == {"status": "Program parsed"}
# Verify socket call (don't compare raw JSON string)
mock_pub_socket.send_multipart.assert_awaited_once()
args, kwargs = mock_pub_socket.send_multipart.await_args
assert args[0][0] == b"program"
sent_bytes = args[0][1]
# Decode sent bytes and compare actual structures
sent_obj = json.loads(sent_bytes.decode())
expected_obj = Program.model_validate_json(message_body).model_dump()
assert sent_obj == expected_obj
def test_receive_program_invalid_json(client):
"""Invalid JSON string (not parseable) should trigger HTTP 400."""
mock_pub_socket = AsyncMock()
client.app.state.endpoints_pub_socket = mock_pub_socket
bad_json_str = "{invalid json}"
msg = Message(message=bad_json_str)
response = client.post("/program", json=msg.model_dump())
assert response.status_code == 400
assert response.json()["detail"] == "Not a valid program"
mock_pub_socket.send_multipart.assert_not_called()
def test_receive_program_invalid_deep_structure(client):
"""Valid JSON shape but invalid deep nested data should still raise 400."""
mock_pub_socket = AsyncMock()
client.app.state.endpoints_pub_socket = mock_pub_socket
# Structurally correct Program, but with missing elements
bad_program = {
"phases": [
{
"id": "phase1",
"name": "deepfail",
"nextPhaseId": "phase2",
"phaseData": {
"norms": [
{"id": "n1", "name": "norm"} # Missing "value"
],
"goals": [
{"id": "g1", "name": "goal", "description": "desc", "achieved": False}
],
"triggers": [
{"id": "t1", "label": "trigger", "type": "keyword", "value": ["start"]}
],
},
}
]
}
msg = Message(message=json.dumps(bad_program))
response = client.post("/program", json=msg.model_dump())
assert response.status_code == 400
assert response.json()["detail"] == "Not a valid program"
mock_pub_socket.send_multipart.assert_not_called()