From 2efce93a3749c2dfd7844ebe36091b05bf9ecd06 Mon Sep 17 00:00:00 2001 From: Pim Hutting Date: Tue, 28 Oct 2025 14:17:07 +0100 Subject: [PATCH] fix: made beliefs a dict of lists [ Before it was a list of a dict of lists of lists of strings now it is a dict of lists of lists of strings as prescribed by architecture (knowledge base) *also added some tests, but will have to add some more ] [ref]: N25B-206 --- .../behaviours/continuous_collect.py | 27 ++++-- .../agents/mock_agents/belief_text_mock.py | 5 +- .../bdi/behaviours/test_continuous_collect.py | 90 +++++++++++++++++++ 3 files changed, 110 insertions(+), 12 deletions(-) create mode 100644 test/unit/agents/bdi/behaviours/test_continuous_collect.py 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 510a380..dbb3bcd 100644 --- a/src/control_backend/agents/belief_collector/behaviours/continuous_collect.py +++ b/src/control_backend/agents/belief_collector/behaviours/continuous_collect.py @@ -1,7 +1,7 @@ import json import logging from spade.behaviour import CyclicBehaviour -from spade.message import Message +from spade.agent import Message from control_backend.core.config import settings logger = logging.getLogger(__name__) @@ -62,20 +62,29 @@ class ContinuousBeliefCollector(CyclicBehaviour): Expected payload: { "type": "belief_extraction_text", - "beliefs": [ - {"user_said": [["hello"],["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"]]} + } """ - beliefs = payload.get("beliefs", []) - if not isinstance(beliefs, list): - logger.warning("BeliefCollector: 'beliefs' is not a list: %r", beliefs) + beliefs = payload.get("beliefs", dict) + + if not beliefs: + logger.info("BeliefCollector: no beliefs to process.") + return + + if not isinstance(beliefs, dict): + logger.warning("BeliefCollector: 'beliefs' is not a dict: %r", beliefs) + return + + if not all(isinstance(v, list) for v in beliefs.values()): + logger.warning("BeliefCollector: 'beliefs' values are not all lists: %r", beliefs) return logger.info("BeliefCollector: forwarding %d beliefs.", len(beliefs)) - for b in beliefs: - logger.info(" - %s", b) + for belief_name, belief_lists in beliefs.items(): + for args in belief_lists: + logger.info(" - %s %s", belief_name, " ".join(map(str, args))) await self._send_beliefs_to_bdi(beliefs, origin=origin) 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 2c761ff..05a6a13 100644 --- a/src/control_backend/agents/mock_agents/belief_text_mock.py +++ b/src/control_backend/agents/mock_agents/belief_text_mock.py @@ -12,9 +12,8 @@ class BeliefTextAgent(Agent): # Send multiple beliefs in one JSON payload payload = { "type": "belief_extraction_text", - "beliefs": [ - {"user_said": [["hello"],["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 new file mode 100644 index 0000000..0a19cb3 --- /dev/null +++ b/test/unit/agents/bdi/behaviours/test_continuous_collect.py @@ -0,0 +1,90 @@ +import json +import logging +from unittest.mock import MagicMock, AsyncMock, call + +import pytest + +from control_backend.agents.belief_collector.behaviours.continuous_collect import ContinuousBeliefCollector + +@pytest.fixture +def mock_agent(mocker): + """Fixture to create a mock Agent.""" + agent = MagicMock() + agent.jid = "belief_collector_agent@test" + return agent + +@pytest.fixture +def continuous_collector(mock_agent, mocker): + """Fixture to create an instance of ContinuousBeliefCollector with a mocked agent.""" + # Patch asyncio.sleep to prevent tests from actually waiting + mocker.patch("asyncio.sleep", return_value=None) + + collector = ContinuousBeliefCollector() + collector.agent = mock_agent + # Mock the receive method, we will control its return value in each test + collector.receive = AsyncMock() + return collector + +@pytest.mark.asyncio +async def test_run_no_message_received(continuous_collector, mocker): + """ + Test that when no message is received, _process_message is not called. + """ + # Arrange + continuous_collector.receive.return_value = None + mocker.patch.object(continuous_collector, "_process_message") + + # Act + await continuous_collector.run() + + # Assert + continuous_collector._process_message.assert_not_called() + +@pytest.mark.asyncio +async def test_run_message_received(continuous_collector, mocker): + """ + Test that when a message is received, _process_message is called with that message. + """ + # Arrange + mock_msg = MagicMock() + continuous_collector.receive.return_value = mock_msg + mocker.patch.object(continuous_collector, "_process_message") + + # Act + await continuous_collector.run() + + # Assert + continuous_collector._process_message.assert_awaited_once_with(mock_msg) + +@pytest.mark.asyncio +async def test_process_message_invalid(continuous_collector, mocker): + """ + Test that when an invalid JSON message is received, a warning is logged and processing stops. + """ + # Arrange + invalid_json = "this is not json" + msg = MagicMock() + msg.body = invalid_json + msg.sender = "belief_text_agent_mock@test" + + logger_mock = mocker.patch("control_backend.agents.belief_collector.behaviours.continuous_collect.logger") + + # Act + await continuous_collector._process_message(msg) + + # Assert + logger_mock.warning.assert_called_once() + +def test_get_sender_from_message(continuous_collector): + """ + Test that _sender_node correctly extracts the sender node from the message JID. + """ + # Arrange + msg = MagicMock() + msg.sender = "agent_node@host/resource" + + # Act + sender_node = continuous_collector._sender_node(msg) + + # Assert + assert sender_node == "agent_node" \ No newline at end of file