SahilCodevally's picture
Init commit
e1c010e
Raw History Blame Contribute Delete
4.13 kB
"""
Tests for the Language Reasoning Module — CommandReasoner.
Uses mocked OpenAI client to test interpretation logic without API calls.
"""
import json
from unittest.mock import MagicMock, patch
import pytest
from src.language.reasoner import CommandReasoner
class TestCommandReasoner:
"""Tests for CommandReasoner.interpret() method."""
@pytest.fixture
def sample_detections(self) -> list[dict]:
"""Sample detection results for testing."""
return [
{"label": "bottle", "confidence": 0.92, "bbox": [100, 150, 200, 350]},
{"label": "box", "confidence": 0.85, "bbox": [400, 300, 550, 500]},
{"label": "cup", "confidence": 0.78, "bbox": [250, 200, 320, 300]},
]
def test_fallback_mode_without_api_key(self, sample_detections):
"""Reasoner without API key should use fallback mode."""
reasoner = CommandReasoner(provider="openai", api_key="", model="gpt-4o")
result = reasoner.interpret(
"Move the bottle to the box", sample_detections
)
assert isinstance(result, dict)
assert "action" in result
assert "source" in result
assert "destination" in result
def test_fallback_extracts_action_verb(self, sample_detections):
"""Fallback mode should detect the action verb from the command."""
reasoner = CommandReasoner(provider="openai", api_key="", model="gpt-4o")
result = reasoner.interpret(
"inspect the cup carefully", sample_detections
)
assert result["action"] == "inspect"
def test_fallback_matches_labels(self, sample_detections):
"""Fallback mode should match detected labels from the command."""
reasoner = CommandReasoner(provider="openai", api_key="", model="gpt-4o")
result = reasoner.interpret(
"Move the bottle to the box", sample_detections
)
assert result["source"] == "bottle"
assert result["destination"] == "box"
def test_empty_command_returns_error(self, sample_detections):
"""Empty command should return an error result."""
reasoner = CommandReasoner(provider="openai", api_key="", model="gpt-4o")
result = reasoner.interpret("", sample_detections)
assert result["action"] == "error"
assert "error" in result
@patch("src.language.reasoner.CommandReasoner._init_client")
def test_llm_interpretation(self, mock_init, sample_detections):
"""Test successful LLM-based interpretation with mocked client."""
reasoner = CommandReasoner(provider="openai", api_key="test-key", model="gpt-4o")
# Mock the OpenAI client response
mock_response = MagicMock()
mock_response.choices = [MagicMock()]
mock_response.choices[0].message.content = json.dumps(
{"action": "move", "source": "bottle", "destination": "box"}
)
mock_client = MagicMock()
mock_client.chat.completions.create.return_value = mock_response
reasoner.client = mock_client
result = reasoner.interpret(
"Move the bottle to the box", sample_detections
)
assert result["action"] == "move"
assert result["source"] == "bottle"
assert result["destination"] == "box"
def test_parse_response_with_markdown_fences(self):
"""Parser should handle markdown code fences from LLM."""
reasoner = CommandReasoner(provider="openai", api_key="", model="gpt-4o")
raw = '```json\n{"action": "pick_up", "source": "cup", "destination": null}\n```'
result = reasoner._parse_response(raw)
assert result["action"] == "pick_up"
assert result["source"] == "cup"
assert result["destination"] is None
def test_parse_response_invalid_json(self):
"""Parser should return error dict for invalid JSON."""
reasoner = CommandReasoner(provider="openai", api_key="", model="gpt-4o")
result = reasoner._parse_response("this is not json")
assert result["action"] == "unknown"
assert "error" in result