import unittest from tools.maintain_remote_address import MaintainRemoteAddressTool from tools.query_remote_address_list import QueryRemoteAddressListTool from tools.save_remote_address import SaveRemoteAddressTool from app import GatewayApp from public_gateway import PublicGatewayApp from services.output_presenter import OutputPresenter class RemoteAddressToolTest(unittest.TestCase): def test_tools_are_registered_on_base_backend(self): local = GatewayApp().registered_tool_names() public = PublicGatewayApp(None, None).registered_tool_names() self.assertEqual(local, public) self.assertIn('query_remote_address_list', local) self.assertIn('maintain_remote_address', local) self.assertIn('save_remote_address', local) self.assertEqual('base', QueryRemoteAddressListTool.backend) self.assertEqual('base', MaintainRemoteAddressTool.backend) self.assertEqual('base', SaveRemoteAddressTool.backend) def test_query_posts_filters_to_base_route(self): client = FakeClient() result = QueryRemoteAddressListTool(api_client=client).call( address_no=' PY12 ', remote_name='美国', status=1, postcode='10001', page=2, limit=20, request_id='rq_remote_list', ) self.assertEqual({'ok': True}, result) self.assertEqual('query_remote_address_list', client.calls[0][0]) self.assertEqual('/mcp/tools/queryRemoteAddressList', client.calls[0][1]) self.assertEqual({ 'page': 2, 'limit': 20, 'address_no': 'PY12', 'remote_name': '美国', 'postcode': '10001', 'status': 1, }, client.calls[0][2]) def test_maintain_without_number_prepares_create(self): client = FakeClient() MaintainRemoteAddressTool(api_client=client).call(request_id='rq_maintain') self.assertEqual('/mcp/tools/maintainRemoteAddress', client.calls[0][1]) self.assertEqual({'page': 1, 'limit': 20}, client.calls[0][2]) def test_presenter_hides_temp_key(self): columns = [ {'key': key, 'name': name} for key, name in OutputPresenter.REMOTE_ADDRESS_DETAIL_COLUMNS ] record = {key: '' for key, _name in OutputPresenter.REMOTE_ADDRESS_DETAIL_COLUMNS} result = OutputPresenter().present('maintain_remote_address', { 'code': 'MCP_0000', 'msg': 'success', 'data': { 'summary': '准备新增地址库', 'mode_text': '新增', 'address_no': '', 'remote_name': '', 'status_text': '', 'temp_key': 'secret-temp-key', 'columns': columns, 'records': [record], 'tips': [], }, 'meta': {'request_id': 'rq_maintain', 'page': 1, 'limit': 20, 'total': 0}, }) rendered = str(result) self.assertNotIn('secret-temp-key', rendered) self.assertNotIn('temp_key', rendered) def test_save_create_posts_details(self): client = FakeClient() SaveRemoteAddressTool(api_client=client).call( remote_name=' 美西偏远 ', status=1, details=[{ 'country': 'US', 'province': 'CA', 'start_postcode': '90001', 'end_postcode': '90099', }], request_id='rq_save_remote', ) self.assertEqual('save_remote_address', client.calls[0][0]) self.assertEqual('/mcp/tools/saveRemoteAddress', client.calls[0][1]) self.assertEqual({ 'remote_name': '美西偏远', 'status': 1, 'details': [{ 'country': 'US', 'province': 'CA', 'start_postcode': '90001', 'end_postcode': '90099', }], }, client.calls[0][2]) def test_save_rejects_end_postcode_without_start(self): tool = SaveRemoteAddressTool(api_client=FakeClient()) with self.assertRaises(ValueError): tool.call( remote_name='美西偏远', status=1, details=[{'country': 'US', 'end_postcode': '90099'}], ) def test_save_rejects_non_ascii_postcode(self): tool = SaveRemoteAddressTool(api_client=FakeClient()) with self.assertRaises(ValueError): tool.call( remote_name='美西偏远', status=1, details=[{'country': 'US', 'start_postcode': '九万'}], ) def test_query_rejects_overlong_text(self): tool = QueryRemoteAddressListTool(api_client=FakeClient()) with self.assertRaises(ValueError): tool.call(address_no='P' * 21) with self.assertRaises(ValueError): tool.call(country='USUSUSUSUSU') def test_save_edit_rejects_details(self): tool = SaveRemoteAddressTool(api_client=FakeClient()) with self.assertRaises(ValueError): tool.call( address_no='PY12', remote_name='美西偏远', status=0, details=[{'country': 'US'}], ) def test_presenter_shows_saved_address(self): result = OutputPresenter().present('save_remote_address', { 'code': 'MCP_0000', 'msg': 'success', 'data': { 'summary': '已新增地址库 PY12 美西偏远,状态启用', 'mode_text': '新增', 'address_no': 'PY12', 'remote_name': '美西偏远', 'status_text': '启用', }, 'meta': {'request_id': 'rq_save_remote'}, }) rendered = str(result) self.assertIn('PY12', rendered) self.assertIn('美西偏远', rendered) self.assertNotIn('temp_key', rendered) def test_presenter_keeps_missing_detail_message(self): result = OutputPresenter().present('save_remote_address', { 'code': 'MCP_1401', 'msg': '地址库没有详情,请导入', 'data': {}, 'meta': {'request_id': 'rq_save_remote'}, }) self.assertIn('地址库没有详情,请导入', str(result)) class FakeClient: def __init__(self): self.calls = [] def call_tool(self, name, route, payload, request_id): self.calls.append((name, route, payload, request_id)) return {'ok': True} if __name__ == '__main__': unittest.main()