test_export_pending_outbound_tools.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. import unittest
  2. from app import GatewayApp
  3. from public_gateway import PublicGatewayApp
  4. from tools.export_pending_outbound_orders import ExportPendingOutboundOrdersTool
  5. from tools.list_pending_outbound_export_filter_options import (
  6. ListPendingOutboundExportFilterOptionsTool,
  7. )
  8. class RecordingApiClient:
  9. def __init__(self):
  10. self.calls = []
  11. def call_tool(self, tool_code, route_path, payload, request_id):
  12. self.calls.append((tool_code, route_path, payload, request_id))
  13. return {'code': 'MCP_0000', 'data': {'file_url': 'https://files.test/order.xlsx'}}
  14. def list_enabled_tools(self):
  15. return {
  16. 'code': 'MCP_0000',
  17. 'data': {
  18. 'tool_codes': [
  19. 'export_pending_outbound_orders',
  20. 'list_pending_outbound_export_filter_options',
  21. ],
  22. },
  23. }
  24. class PublicSessionStore:
  25. def get(self, gateway_session_id):
  26. return {'mcp_token': 'MT_test'} if gateway_session_id == 'GWS_test' else None
  27. class PublicApiClient:
  28. def list_enabled_tools(self, token):
  29. return {
  30. 'code': 'MCP_0000',
  31. 'data': {
  32. 'tool_codes': [
  33. 'export_pending_outbound_orders',
  34. 'list_pending_outbound_export_filter_options',
  35. ],
  36. },
  37. }
  38. class ExportPendingOutboundToolsTest(unittest.TestCase):
  39. def test_metadata_uses_add_page_filter_names_and_native_types(self):
  40. schema = ExportPendingOutboundOrdersTool().metadata()['input_schema']
  41. properties = schema['properties']
  42. for field in (
  43. 'number', 'product_type_id', 'product_id', 'order_warehouse_id',
  44. 'receiver_country', 'is_remove', 'address', 'inbound_date', 'status',
  45. 'is_battery', 'is_magnetic', 'is_wood', 'is_other', 'is_fda',
  46. 'is_toy', 'is_ultra_limit', 'is_sensitive', 'is_food', 'no_property',
  47. 'merge_declare_number', 'importer_id', 'packing_type',
  48. ):
  49. self.assertIn(field, properties)
  50. self.assertEqual('array', properties['product_id']['type'])
  51. self.assertEqual('array', properties['status']['type'])
  52. self.assertEqual('string', properties['packing_type']['type'])
  53. self.assertNotIn('property_2', properties)
  54. def test_export_call_preserves_three_page_multi_select_shapes(self):
  55. client = RecordingApiClient()
  56. tool = ExportPendingOutboundOrdersTool(api_client=client)
  57. result = tool.call(
  58. product_id=[12, 13],
  59. status=[30, 40],
  60. is_battery='Y',
  61. packing_type='散货,整柜',
  62. is_remove=0,
  63. request_id='rq_export',
  64. )
  65. self.assertEqual('MCP_0000', result['code'])
  66. self.assertEqual(
  67. {
  68. 'product_id': [12, 13],
  69. 'status': [30, 40],
  70. 'is_battery': 'Y',
  71. 'packing_type': '散货,整柜',
  72. 'is_remove': 0,
  73. },
  74. client.calls[0][2],
  75. )
  76. self.assertEqual('/mcp/tools/exportPendingOutboundOrders', client.calls[0][1])
  77. def test_filter_options_forwards_product_type_dependency_and_bounds_page(self):
  78. client = RecordingApiClient()
  79. tool = ListPendingOutboundExportFilterOptionsTool(api_client=client)
  80. tool.call(
  81. filter_type=' product ',
  82. product_type_id=8,
  83. keyword=' 海运 ',
  84. page=0,
  85. limit=200,
  86. request_id='rq_filters',
  87. )
  88. self.assertEqual(
  89. {
  90. 'filter_type': 'product',
  91. 'product_type_id': 8,
  92. 'keyword': '海运',
  93. 'page': 1,
  94. 'limit': 100,
  95. },
  96. client.calls[0][2],
  97. )
  98. self.assertEqual('/mcp/tools/listPendingOutboundExportFilterOptions', client.calls[0][1])
  99. def test_filter_options_rejects_missing_client_unknown_type_and_invalid_dependency(self):
  100. with self.assertRaisesRegex(RuntimeError, 'api client is required'):
  101. ListPendingOutboundExportFilterOptionsTool().call('country')
  102. tool = ListPendingOutboundExportFilterOptionsTool(api_client=RecordingApiClient())
  103. with self.assertRaisesRegex(ValueError, 'unsupported filter_type'):
  104. tool.call('unknown')
  105. with self.assertRaisesRegex(ValueError, 'product_type_id must be greater than 0'):
  106. tool.call('product', product_type_id=0)
  107. tool.call('country')
  108. def test_export_requires_api_client(self):
  109. with self.assertRaisesRegex(RuntimeError, 'api client is required'):
  110. ExportPendingOutboundOrdersTool().call()
  111. def test_gateways_register_both_tools_when_backend_enables_them(self):
  112. local_names = {
  113. item['name']
  114. for item in GatewayApp(api_client=RecordingApiClient()).list_tools()
  115. }
  116. public_names = {
  117. item['name']
  118. for item in PublicGatewayApp(
  119. PublicSessionStore(), PublicApiClient()
  120. ).list_tools('GWS_test')
  121. }
  122. self.assertIn('export_pending_outbound_orders', local_names)
  123. self.assertIn('list_pending_outbound_export_filter_options', local_names)
  124. self.assertIn('export_pending_outbound_orders', public_names)
  125. self.assertIn('list_pending_outbound_export_filter_options', public_names)
  126. if __name__ == '__main__':
  127. unittest.main()