diff --git a/src/control_backend/agents/belief_collector/behaviours/continuous_collect.py b/src/control_backend/agents/belief_collector/behaviours/continuous_collect.py index dbb3bcd..b94dbd9 100644 --- a/src/control_backend/agents/belief_collector/behaviours/continuous_collect.py +++ b/src/control_backend/agents/belief_collector/behaviours/continuous_collect.py @@ -67,7 +67,7 @@ class ContinuousBeliefCollector(CyclicBehaviour): } """ - beliefs = payload.get("beliefs", dict) + beliefs = payload.get("beliefs", {}) if not beliefs: logger.info("BeliefCollector: no beliefs to process.") diff --git a/src/control_backend/agents/mock_agents/belief_text_mock.py b/src/control_backend/agents/mock_agents/belief_text_mock.py index 05a6a13..d8a223f 100644 --- a/src/control_backend/agents/mock_agents/belief_text_mock.py +++ b/src/control_backend/agents/mock_agents/belief_text_mock.py @@ -12,8 +12,7 @@ class BeliefTextAgent(Agent): # Send multiple beliefs in one JSON payload payload = { "type": "belief_extraction_text", - "beliefs": {"user_said": [["hello","test"],["Can you help me?"],["stop talking to me"],["No"],["Pepper do a dance"]]} - + "beliefs": {"user_said": [["hello test"],["Can you help me?"],["stop talking to me"],["No"],["Pepper do a dance"]]} } msg = Message(to=to_jid) diff --git a/test/unit/agents/bdi/behaviours/test_continuous_collect.py b/test/unit/agents/bdi/behaviours/test_continuous_collect.py index 0a19cb3..c603048 100644 --- a/test/unit/agents/bdi/behaviours/test_continuous_collect.py +++ b/test/unit/agents/bdi/behaviours/test_continuous_collect.py @@ -87,4 +87,123 @@ def test_get_sender_from_message(continuous_collector): sender_node = continuous_collector._sender_node(msg) # Assert - assert sender_node == "agent_node" \ No newline at end of file + assert sender_node == "agent_node" + +@pytest.mark.asyncio +async def test_routes_to_handle_belief_text_by_type(continuous_collector, mocker): + msg = MagicMock() + msg.body = json.dumps({"type": "belief_extraction_text", "beliefs": {"user_said": [["hi"]]}}) + msg.sender = "anyone@test" + spy = mocker.patch.object(continuous_collector, "_handle_belief_text", new=AsyncMock()) + await continuous_collector._process_message(msg) + spy.assert_awaited_once() + +@pytest.mark.asyncio +async def test_routes_to_handle_belief_text_by_sender(continuous_collector, mocker): + msg = MagicMock() + msg.body = json.dumps({"beliefs": {"user_said": [["hi"]]}}) # no type + msg.sender = "belief_text_agent_mock@test" + spy = mocker.patch.object(continuous_collector, "_handle_belief_text", new=AsyncMock()) + await continuous_collector._process_message(msg) + spy.assert_awaited_once() + +@pytest.mark.asyncio +async def test_routes_to_handle_emo_text(continuous_collector, mocker): + msg = MagicMock() + msg.body = json.dumps({"type": "emotion_extraction_text"}) + msg.sender = "anyone@test" + spy = mocker.patch.object(continuous_collector, "_handle_emo_text", new=AsyncMock()) + await continuous_collector._process_message(msg) + spy.assert_awaited_once() + +@pytest.mark.asyncio +async def test_unrecognized_message_logs_info(continuous_collector, mocker): + msg = MagicMock() + msg.body = json.dumps({"type": "something_else"}) + msg.sender = "x@test" + logger_mock = mocker.patch("control_backend.agents.belief_collector.behaviours.continuous_collect.logger") + await continuous_collector._process_message(msg) + logger_mock.info.assert_any_call( + "BeliefCollector: unrecognized message (sender=%s, type=%r). Ignoring.", "x", "something_else" + ) + + +@pytest.mark.asyncio +async def test_belief_text_no_beliefs(continuous_collector, mocker): + msg_payload = {"type": "belief_extraction_text"} # no 'beliefs' + logger_mock = mocker.patch("control_backend.agents.belief_collector.behaviours.continuous_collect.logger") + await continuous_collector._handle_belief_text(msg_payload, "origin_node") + logger_mock.info.assert_any_call("BeliefCollector: no beliefs to process.") + +@pytest.mark.asyncio +async def test_belief_text_beliefs_not_dict(continuous_collector, mocker): + payload = {"type": "belief_extraction_text", "beliefs": ["not", "a", "dict"]} + logger_mock = mocker.patch("control_backend.agents.belief_collector.behaviours.continuous_collect.logger") + await continuous_collector._handle_belief_text(payload, "origin") + logger_mock.warning.assert_any_call("BeliefCollector: 'beliefs' is not a dict: %r", ["not", "a", "dict"]) + +@pytest.mark.asyncio +async def test_belief_text_values_not_lists(continuous_collector, mocker): + payload = {"type": "belief_extraction_text", "beliefs": {"user_said": "not-a-list"}} + logger_mock = mocker.patch("control_backend.agents.belief_collector.behaviours.continuous_collect.logger") + await continuous_collector._handle_belief_text(payload, "origin") + logger_mock.warning.assert_any_call( + "BeliefCollector: 'beliefs' values are not all lists: %r", {"user_said": "not-a-list"} + ) + +@pytest.mark.asyncio +async def test_belief_text_happy_path_logs_items_and_sends(continuous_collector, mocker): + payload = { + "type": "belief_extraction_text", + "beliefs": {"user_said": [["hello", "test"], ["No"]]} + } + # Your code calls self.send(..); patch it (or switch implementation to self.agent.send and patch that) + continuous_collector.send = AsyncMock() + logger_mock = mocker.patch("control_backend.agents.belief_collector.behaviours.continuous_collect.logger") + await continuous_collector._handle_belief_text(payload, "belief_text_agent_mock") + + logger_mock.info.assert_any_call("BeliefCollector: forwarding %d beliefs.", 1) + # and the item logs: + logger_mock.info.assert_any_call(" - %s %s", "user_said", "hello test") + logger_mock.info.assert_any_call(" - %s %s", "user_said", "No") + # make sure we attempted a send + continuous_collector.send.assert_awaited_once() + +@pytest.mark.asyncio +async def test_send_beliefs_noop_on_empty(continuous_collector): + continuous_collector.send = AsyncMock() + await continuous_collector._send_beliefs_to_bdi([], origin="o") + continuous_collector.send.assert_not_awaited() + +@pytest.mark.asyncio +async def test_send_beliefs_sends_json_packet(continuous_collector): + # Patch .send and capture the message body + sent = {} + + async def _fake_send(msg): + sent["body"] = msg.body + sent["to"] = str(msg.to) + + continuous_collector.send = AsyncMock(side_effect=_fake_send) + beliefs = ["user_said hello", "user_said No"] + await continuous_collector._send_beliefs_to_bdi(beliefs, origin="origin_node") + + assert "belief_packet" in json.loads(sent["body"])["type"] + assert json.loads(sent["body"])["beliefs"] == beliefs + +def test_sender_node_no_sender_returns_literal(continuous_collector): + msg = MagicMock() + msg.sender = None + assert continuous_collector._sender_node(msg) == "no_sender" + +def test_sender_node_without_at(continuous_collector): + msg = MagicMock() + msg.sender = "localpartonly" + assert continuous_collector._sender_node(msg) == "localpartonly" + +@pytest.mark.asyncio +async def test_belief_text_coerces_non_strings(continuous_collector, mocker): + payload = {"type": "belief_extraction_text", "beliefs": {"user_said": [["hi", 123]]}} + continuous_collector.send = AsyncMock() + await continuous_collector._handle_belief_text(payload, "origin") + continuous_collector.send.assert_awaited_once()