import io import math import unittest from unittest.mock import Mock, patch from app import GatewayApp from public_gateway import PublicGatewayApp from services.output_presenter import OutputPresenter from tools.trial_goods_tax import TrialGoodsTaxTool class TrialGoodsTaxToolTest(unittest.TestCase): def test_call_forwards_required_fields(self): client = Mock() client.call_tool.return_value = {'code': 'MCP_0000'} TrialGoodsTaxTool(client).call( sku=' SKU-1 ', departure_country=' CN ', destination_country='us', price=10, qty=2, gross_weight=1.5, box=1, ) client.call_tool.assert_called_once_with( 'trial_goods_tax', '/mcp/tools/trialGoodsTax', { 'sku': 'SKU-1', 'departure_country': 'CN', 'destination_country': 'US', 'price': 10, 'qty': 2, 'gross_weight': 1.5, 'box': 1, }, 'rq_trial_goods_tax', ) def test_call_rejects_country_name(self): tool = TrialGoodsTaxTool(api_client=Mock()) with self.assertRaises(ValueError): tool.call( sku='SKU-1', departure_country='中国', destination_country='US', price=10, qty=1, gross_weight=1, box=1, ) def test_call_rejects_zero_price(self): tool = TrialGoodsTaxTool(api_client=Mock()) with self.assertRaises(ValueError): tool.call( sku='SKU-1', departure_country='CN', destination_country='US', price=0, qty=1, gross_weight=1, box=1, ) def test_call_requires_api_client(self): with self.assertRaises(RuntimeError): TrialGoodsTaxTool().call( sku='SKU-1', departure_country='CN', destination_country='US', price=1, qty=1, gross_weight=1, box=1, ) def test_call_rejects_invalid_fields(self): tool = TrialGoodsTaxTool(api_client=Mock()) cases = [ {'sku': 1}, {'sku': ''}, {'sku': 'S' * 101}, {'departure_country': None}, {'departure_country': 'A'}, {'departure_country': 'ABCD'}, {'departure_country': 'C1'}, {'price': True}, {'qty': '1'}, {'gross_weight': math.inf}, {'box': 1000000001}, ] base = { 'sku': 'SKU-1', 'departure_country': 'CN', 'destination_country': 'USA', 'price': 1, 'qty': 1, 'gross_weight': 1, 'box': 1, } for override in cases: with self.subTest(override=override): payload = dict(base) payload.update(override) with self.assertRaises(ValueError): tool.call(**payload) def test_cli_forwards_trial_fields(self): app = GatewayApp() with patch.object(app, 'call_tool', return_value={'code': 'MCP_0000'}) as call_tool: code = app.run_cli([ 'call', '--tool', 'trial_goods_tax', '--sku', 'SKU-1', '--departure-country', 'CN', '--destination-country', 'US', '--price', '10', '--qty', '2', '--gross-weight', '1.5', '--box', '1', ], stdout=io.StringIO()) self.assertEqual(0, code) self.assertEqual( { 'page': 1, 'limit': 20, 'sku': 'SKU-1', 'departure_country': 'CN', 'destination_country': 'US', 'price': 10.0, 'qty': 2.0, 'gross_weight': 1.5, 'box': 1.0, }, call_tool.call_args[0][1], ) def test_registries_match(self): local = GatewayApp().registered_tool_names() public = PublicGatewayApp(None, None).registered_tool_names() self.assertEqual(local, public) self.assertIn('trial_goods_tax', local) self.assertEqual(42, len(local)) self.assertIn('trial_goods_tax', OutputPresenter.SAFE_TOOLS) def test_presenter_renders_amount(self): result = OutputPresenter().present('trial_goods_tax', { 'code': 'MCP_0000', 'data': { 'estimated_tax': 12.5, 'sku': 'SKU-1', 'departure_country': 'CN', 'destination_country': 'US', }, 'meta': {'request_id': 'rq1'}, }) self.assertFalse(result['is_error']) self.assertEqual(12.5, result['structured_content']['estimated_tax']) self.assertIn('预计关税金额', result['text']) def test_presenter_keeps_safe_failure_message(self): result = OutputPresenter().present('trial_goods_tax', { 'code': 'MCP_1501', 'msg': '未找到已审核商品', 'data': [], 'meta': {'request_id': 'rq1'}, }) self.assertTrue(result['is_error']) self.assertEqual('未找到已审核商品', result['structured_content']['message']) def test_presenter_rejects_invalid_tax_payload(self): presenter = OutputPresenter() valid = { 'estimated_tax': 12, 'sku': 'SKU-1', 'departure_country': 'CN', 'destination_country': 'USA', } ok = presenter.present('trial_goods_tax', { 'code': 'MCP_0000', 'data': valid, }) self.assertFalse(ok['is_error']) self.assertEqual(12, ok['structured_content']['estimated_tax']) cases = [ {'estimated_tax': 1}, dict(valid, estimated_tax=True), dict(valid, estimated_tax='12'), dict(valid, estimated_tax=math.nan), dict(valid, sku=1), dict(valid, sku=' '), dict(valid, departure_country=1), dict(valid, departure_country='C1'), dict(valid, departure_country='cn'), dict(valid, departure_country='A'), dict(valid, departure_country='ABCD'), dict(valid, destination_country='us'), ] for data in cases: with self.subTest(data=data): result = presenter.present('trial_goods_tax', { 'code': 'MCP_0000', 'data': data, }) self.assertTrue(result['is_error']) self.assertNotIn('SKU-1', result['text'])