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