| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404 |
- from __future__ import annotations
- import asyncio
- import inspect
- import unittest
- from unittest.mock import patch
- from data_collector_mcp import app, common_server, modbus_server, s7_server
- class ServerToolTests(unittest.TestCase):
- def test_app_imports_tool_modules_for_registration(self) -> None:
- self.assertIs(app.common_server, common_server)
- self.assertIs(app.modbus_server, modbus_server)
- self.assertIs(app.s7_server, s7_server)
- def test_app_registers_expected_mcp_tools(self) -> None:
- tools = asyncio.run(app.mcp.list_tools())
- tool_names = {tool.name for tool in tools}
- self.assertEqual(
- tool_names,
- {
- "project.list",
- "collector.device_list",
- "collector.device_connect",
- "collector.device_disconnect",
- "collector.device_points",
- "modbus.point_collect_test",
- "s7.raw_read",
- "s7.point_collect_test",
- "s7.connect_scan",
- "collector.modbus_device_create",
- "collector.modbus_device_edit",
- "collector.modbus_point_create",
- "collector.modbus_point_edit",
- "collector.s7_device_create",
- "collector.s7_device_edit",
- "collector.s7_point_create",
- "collector.s7_point_edit",
- },
- )
- def test_project_list_filters_enabled_projects_and_sorts(self) -> None:
- with patch(
- "data_collector_mcp.common_server.load_projects_config",
- return_value=[
- {
- "project_key": "z-prod",
- "project_name": "Prod",
- "base_url": "http://gateway.prod",
- "data_collector_base_url": "http://collector.prod",
- "enabled": True,
- },
- {
- "project_key": "disabled",
- "project_name": "Disabled",
- "base_url": "http://gateway.disabled",
- "data_collector_base_url": "http://collector.disabled",
- "enabled": False,
- },
- {
- "project_key": "a-dev",
- "project_name": "Dev",
- "base_url": "http://gateway.dev",
- "data_collector_base_url": "http://collector.dev",
- "enabled": True,
- },
- ],
- ):
- result = common_server.project_list()
- self.assertEqual(
- result,
- {
- "projects": [
- {
- "project_key": "a-dev",
- "project_name": "Dev",
- "base_url": "http://gateway.dev",
- "data_collector_base_url": "http://collector.dev",
- },
- {
- "project_key": "z-prod",
- "project_name": "Prod",
- "base_url": "http://gateway.prod",
- "data_collector_base_url": "http://collector.prod",
- },
- ],
- "total": 2,
- },
- )
- def test_common_device_tools_forward_to_api(self) -> None:
- with patch("data_collector_mcp.common_server.api_list_devices", return_value={"state": 0}) as list_devices:
- self.assertEqual(common_server.collector_device_list("dev-01", num_points=True), {"state": 0})
- list_devices.assert_called_once_with("dev-01", num_points=True)
- with patch("data_collector_mcp.common_server.api_connect_device", return_value={"state": 0}) as connect:
- self.assertEqual(
- common_server.collector_device_connect("dev-01", device_id=1, device_type="s7"),
- {"state": 0},
- )
- connect.assert_called_once_with("dev-01", device_id=1, device_type="s7")
- with patch("data_collector_mcp.common_server.api_disconnect_device", return_value={"state": 0}) as disconnect:
- self.assertEqual(
- common_server.collector_device_disconnect("dev-01", device_id=1, device_type="modbus"),
- {"state": 0},
- )
- disconnect.assert_called_once_with("dev-01", device_id=1, device_type="modbus")
- with patch("data_collector_mcp.common_server.api_list_device_points", return_value={"state": 0}) as points:
- self.assertEqual(
- common_server.collector_device_points("dev-01", device_id=1, device_type="s7", group_id=2),
- {"state": 0},
- )
- points.assert_called_once_with("dev-01", device_id=1, device_type="s7", group_id=2)
- def test_modbus_device_create_uses_explicit_parameters(self) -> None:
- signature = inspect.signature(modbus_server.collector_modbus_device_create)
- self.assertNotIn("payload", signature.parameters)
- with patch("data_collector_mcp.modbus_server.api_create_modbus_device", return_value={"state": 0}) as api_create:
- result = modbus_server.collector_modbus_device_create(
- project_key="dev-01",
- name="modbus_tcp_1",
- device_type=1,
- ip="127.0.0.1",
- port=5502,
- slave_id=1,
- byte_order=1,
- word_order=1,
- address_base=0,
- group_id=10,
- )
- self.assertEqual(result, {"state": 0})
- api_create.assert_called_once_with(
- "dev-01",
- {
- "name": "modbus_tcp_1",
- "device_type": 1,
- "ip": "127.0.0.1",
- "port": 5502,
- "slave_id": 1,
- "byte_order": 1,
- "word_order": 1,
- "address_base": 0,
- "serial_port": "",
- "timeout": 3,
- "is_persistent": True,
- "baud_rate": 0,
- "data_bit": 0,
- "parity": 0,
- "stop_bit": 0,
- "mode": 0,
- "retry_times": 0,
- "group_id": 10,
- "alarm_interval": 90,
- "collect_interval": 5,
- },
- )
- def test_modbus_device_edit_uses_explicit_parameters(self) -> None:
- signature = inspect.signature(modbus_server.collector_modbus_device_edit)
- self.assertNotIn("payload", signature.parameters)
- with patch("data_collector_mcp.modbus_server.api_edit_modbus_device", return_value={"state": 0}) as api_edit:
- result = modbus_server.collector_modbus_device_edit(
- project_key="dev-01",
- ori_id=1,
- name="modbus_tcp_edited",
- device_type=1,
- ip="127.0.0.1",
- port=5502,
- slave_id=1,
- byte_order=2,
- word_order=2,
- address_offset=1,
- device_group_id=10,
- )
- self.assertEqual(result, {"state": 0})
- api_edit.assert_called_once_with(
- "dev-01",
- {
- "ori_id": 1,
- "name": "modbus_tcp_edited",
- "device_type": 1,
- "ip": "127.0.0.1",
- "port": 5502,
- "slave_id": 1,
- "byte_order": 2,
- "word_order": 2,
- "serial_port": "",
- "timeout": 3,
- "is_persistent": True,
- "baud_rate": 0,
- "data_bit": 0,
- "parity": 0,
- "stop_bit": 0,
- "mode": 0,
- "address_offset": 1,
- "retry_times": 0,
- "device_group_id": 10,
- "alarm_interval": 90,
- "collect_interval": 5,
- },
- )
- def test_modbus_point_edit_uses_explicit_parameters_with_func_code(self) -> None:
- signature = inspect.signature(modbus_server.collector_modbus_point_edit)
- self.assertNotIn("payload", signature.parameters)
- with patch("data_collector_mcp.modbus_server.api_edit_modbus_point", return_value={"state": 0}) as api_edit:
- result = modbus_server.collector_modbus_point_edit(
- project_key="dev-01",
- ori_id=101,
- name="holding_register_uint16_edited",
- address=10,
- data_type="uint16",
- func_code=3,
- point_id="HR_UINT16_EDITED",
- )
- self.assertEqual(result, {"state": 0})
- api_edit.assert_called_once_with(
- "dev-01",
- {
- "ori_id": 101,
- "name": "holding_register_uint16_edited",
- "address": 10,
- "type": "uint16",
- "point_id": "HR_UINT16_EDITED",
- "scale_ratio": 1,
- "value_offset": 0,
- "group_id": 0,
- "invalid_values": "",
- "valid_range_start": None,
- "valid_range_end": None,
- "bit": 0,
- "describe": "",
- "func_code": 3,
- },
- )
- def test_modbus_point_edit_uses_register_type_when_func_code_is_zero(self) -> None:
- with patch("data_collector_mcp.modbus_server.api_edit_modbus_point", return_value={"state": 0}) as api_edit:
- modbus_server.collector_modbus_point_edit(
- project_key="dev-01",
- ori_id=101,
- name="holding_register_uint16_edited",
- address=10,
- data_type="uint16",
- register_type="holding_register",
- )
- payload = api_edit.call_args.args[1]
- self.assertEqual(payload["register_type"], "holding_register")
- self.assertNotIn("func_code", payload)
- def test_s7_device_create_uses_explicit_parameters(self) -> None:
- signature = inspect.signature(s7_server.collector_s7_device_create)
- self.assertNotIn("payload", signature.parameters)
- with patch("data_collector_mcp.s7_server.api_create_s7_device", return_value={"state": 0}) as api_create:
- result = s7_server.collector_s7_device_create(
- project_key="dev-01",
- name="s7_1200_1",
- ip="127.0.0.1",
- rock=0,
- slot=1,
- tsap_conn_type="OP",
- )
- self.assertEqual(result, {"state": 0})
- api_create.assert_called_once_with(
- "dev-01",
- {
- "name": "s7_1200_1",
- "ip": "127.0.0.1",
- "rock": 0,
- "slot": 1,
- "port": 102,
- "device_type": 1,
- "tsap_conn_type": "OP",
- "is_persistent": False,
- "device_group_id": 0,
- "timeout": 3,
- "alarm_interval": 90,
- "collect_interval": 5,
- },
- )
- def test_s7_device_edit_uses_explicit_parameters(self) -> None:
- signature = inspect.signature(s7_server.collector_s7_device_edit)
- self.assertNotIn("payload", signature.parameters)
- with patch("data_collector_mcp.s7_server.api_edit_s7_device", return_value={"state": 0}) as api_edit:
- result = s7_server.collector_s7_device_edit(
- project_key="dev-01",
- ori_id=3,
- name="s7_edited",
- ip="127.0.0.1",
- rock=0,
- slot=1,
- tsap_conn_type="OP",
- )
- self.assertEqual(result, {"state": 0})
- api_edit.assert_called_once_with(
- "dev-01",
- {
- "ori_id": 3,
- "name": "s7_edited",
- "ip": "127.0.0.1",
- "rock": 0,
- "slot": 1,
- "port": 102,
- "device_type": 1,
- "tsap_conn_type": "OP",
- "is_persistent": False,
- "device_group_id": 0,
- "timeout": 3,
- "alarm_interval": 90,
- "collect_interval": 5,
- },
- )
- def test_s7_point_edit_uses_register_area_when_register_type_is_zero(self) -> None:
- signature = inspect.signature(s7_server.collector_s7_point_edit)
- self.assertNotIn("payload", signature.parameters)
- with patch("data_collector_mcp.s7_server.api_edit_s7_point", return_value={"state": 0}) as api_edit:
- s7_server.collector_s7_point_edit(
- project_key="dev-01",
- ori_id=101,
- device_id=3,
- name="db_real",
- address="1.10",
- data_type="float32",
- register_area="DB",
- point_id="DB_REAL",
- )
- payload = api_edit.call_args.args[1]
- self.assertEqual(payload["register_area"], "DB")
- self.assertNotIn("register_type", payload)
- def test_s7_gateway_tools_forward_to_api(self) -> None:
- with patch("data_collector_mcp.s7_server.api_s7_raw_read", return_value={"code": 0}) as raw_read:
- self.assertEqual(
- s7_server.s7_raw_read(
- project_key="dev-01",
- ip="192.168.1.10",
- rock=0,
- slot=1,
- read={"area": "DB", "db": 1, "start": 0, "size": 4},
- ),
- {"code": 0},
- )
- raw_read.assert_called_once_with(
- "dev-01",
- ip="192.168.1.10",
- rock=0,
- slot=1,
- read={"area": "DB", "db": 1, "start": 0, "size": 4},
- device_type="S7TCP",
- port=102,
- tsap_conn_type="PG",
- )
- with patch("data_collector_mcp.s7_server.api_s7_point_collect_test", return_value={"code": 0}) as point_test:
- self.assertEqual(
- s7_server.s7_point_collect_test(
- project_key="dev-01",
- ip="192.168.1.10",
- port=1102,
- rock=0,
- slot=1,
- tsap_conn_type="BASIC",
- points=[{"area": "M", "start": 0, "type": "bool"}],
- ),
- {"code": 0},
- )
- point_test.assert_called_once_with(
- "dev-01",
- ip="192.168.1.10",
- rock=0,
- slot=1,
- points=[{"area": "M", "start": 0, "type": "bool"}],
- device_type="S7TCP",
- port=1102,
- tsap_conn_type="BASIC",
- )
- with patch("data_collector_mcp.s7_server.api_s7_connect_scan", return_value={"code": 0}) as connect_scan:
- self.assertEqual(s7_server.s7_connect_scan("dev-01", ip="192.168.1.10"), {"code": 0})
- connect_scan.assert_called_once_with("dev-01", ip="192.168.1.10")
- if __name__ == "__main__":
- unittest.main()
|