Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions src/backend/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,13 +161,30 @@ async def handle_chat():
"""Unified chat endpoint - routes messages to appropriate handlers based on intent."""
data = await request.get_json()

if not data:
return jsonify({
"action_type": "error",
"message": "Request body is required",
"data": {},
"conversation_id": ""
}), 400
Comment thread
Ragini-Microsoft marked this conversation as resolved.

# Extract request fields
conversation_id = data.get("conversation_id") or str(uuid.uuid4())
user_id = data.get("user_id", "anonymous")
message = data.get("message", "")
action = data.get("action")
payload = data.get("payload", {})

# Validate that message content is not empty when no action is specified
if not action and not message.strip():
Comment thread
Ragini-Microsoft marked this conversation as resolved.
return jsonify({
"action_type": "error",
"message": "Message content must not be empty",
"data": {},
"conversation_id": conversation_id
}), 400

selected_products = data.get("selected_products", [])
brief_data = data.get("brief", {})

Expand Down
124 changes: 60 additions & 64 deletions src/tests/test_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,46 @@ async def test_health_check_api(client):

@pytest.mark.asyncio
async def test_chat_missing_message(client):
"""Test chat endpoint with missing message still returns response (no validation)."""
"""Test chat endpoint rejects missing/empty message with 400."""
response = await client.post(
"/api/chat",
json={"conversation_id": "test-conv"}
)

assert response.status_code == 400
data = await response.get_json()
assert data["action_type"] == "error"
assert "empty" in data["message"].lower()


@pytest.mark.asyncio
async def test_chat_empty_body(client):
"""Test chat endpoint rejects empty request body with 400."""
response = await client.post(
"/api/chat",
data="",
headers={"Content-Type": "application/json"}
)

assert response.status_code == 400


@pytest.mark.asyncio
async def test_chat_whitespace_message(client):
"""Test chat endpoint rejects whitespace-only message with 400."""
response = await client.post(
"/api/chat",
json={"conversation_id": "test-conv", "message": " "}
)

assert response.status_code == 400
data = await response.get_json()
assert data["action_type"] == "error"


@pytest.mark.asyncio
async def test_chat_empty_message_with_action_allowed(client):
"""Test chat endpoint allows empty message when action is specified."""
with patch("app.get_routing_service") as mock_routing, \
patch("app.get_cosmos_service") as mock_cosmos, \
patch("app.get_orchestrator") as mock_orch:
Expand All @@ -88,10 +127,10 @@ async def test_chat_missing_message(client):

response = await client.post(
"/api/chat",
json={"conversation_id": "test-conv"}
json={"conversation_id": "test-conv", "action": "confirm_brief", "message": ""}
)

# API doesn't validate missing message - routes to handler with empty message
# Action-based requests bypass message validation
assert response.status_code in [200, 500]


Expand Down Expand Up @@ -187,36 +226,15 @@ async def test_chat_cosmos_failure(client):

@pytest.mark.asyncio
async def test_parse_brief_missing_text(client):
"""Test chat endpoint with missing message still processes (no validation)."""
with patch("app.get_routing_service") as mock_routing, \
patch("app.get_cosmos_service") as mock_cosmos, \
patch("app.get_orchestrator") as mock_orch:

from services.routing_service import Intent, RoutingResult, ConversationState
mock_routing_service = MagicMock()
mock_routing_service.classify_intent = MagicMock(return_value=RoutingResult(
intent=Intent.PARSE_BRIEF,
confidence=0.5
))
mock_routing_service.derive_state_from_conversation = MagicMock(return_value=ConversationState())
mock_routing.return_value = mock_routing_service

mock_cosmos_service = AsyncMock()
mock_cosmos_service.get_conversation = AsyncMock(return_value=None)
mock_cosmos_service.add_message_to_conversation = AsyncMock()
mock_cosmos.return_value = mock_cosmos_service

mock_orchestrator = AsyncMock()
mock_orchestrator.parse_brief = AsyncMock(return_value=(MagicMock(model_dump=lambda: {}), None, False))
mock_orch.return_value = mock_orchestrator

response = await client.post(
"/api/chat",
json={"conversation_id": "test-conv"}
)
"""Test chat endpoint rejects missing message with 400."""
response = await client.post(
"/api/chat",
json={"conversation_id": "test-conv"}
)

# API doesn't validate missing message - routes to handler with empty message
assert response.status_code in [200, 500]
assert response.status_code == 400
data = await response.get_json()
assert data["action_type"] == "error"


@pytest.mark.asyncio
Expand Down Expand Up @@ -864,39 +882,17 @@ async def test_regenerate_content_success(client, sample_creative_brief_dict):

@pytest.mark.asyncio
async def test_regenerate_content_missing_modification_request(client, sample_creative_brief_dict):
"""Test regeneration without message still routes (no validation)."""
with patch("app.get_routing_service") as mock_routing, \
patch("app.get_cosmos_service") as mock_cosmos, \
patch("app.get_orchestrator") as mock_orch:

from services.routing_service import Intent, RoutingResult, ConversationState
mock_routing_service = MagicMock()
mock_routing_service.classify_intent = MagicMock(return_value=RoutingResult(
intent=Intent.PARSE_BRIEF,
confidence=0.5
))
mock_routing_service.derive_state_from_conversation = MagicMock(return_value=ConversationState())
mock_routing.return_value = mock_routing_service

mock_cosmos_service = AsyncMock()
mock_cosmos_service.get_conversation = AsyncMock(return_value=None)
mock_cosmos_service.add_message_to_conversation = AsyncMock()
mock_cosmos.return_value = mock_cosmos_service

mock_orchestrator = AsyncMock()
mock_orchestrator.parse_brief = AsyncMock(return_value=(MagicMock(model_dump=lambda: {}), None, False))
mock_orch.return_value = mock_orchestrator

response = await client.post(
"/api/chat",
json={
"conversation_id": "test-conv"
# Missing message - no validation in backend
}
)
"""Test regeneration rejects missing message with 400."""
response = await client.post(
"/api/chat",
json={
"conversation_id": "test-conv"
}
)

# Backend doesn't validate missing message
assert response.status_code in [200, 500]
assert response.status_code == 400
data = await response.get_json()
assert data["action_type"] == "error"


@pytest.mark.asyncio
Expand Down
Loading