test_mcp_servers.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. """
  2. MCP 服务器测试模块
  3. 测试内容:
  4. 1. 格式编码工具(纯函数测试,零依赖)
  5. 2. MCP 服务器集成测试(需要数据库连接)
  6. 运行方式:
  7. cd SmartBizTrip
  8. python -m tests.test_mcp_servers
  9. """
  10. import unittest
  11. import json
  12. import sys
  13. import os
  14. from datetime import date, datetime, timedelta
  15. from decimal import Decimal
  16. # 确保能导入 SmartBizTrip 模块
  17. sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
  18. # ==================== 1. 格式编码工具测试(纯函数,零依赖) ====================
  19. class TestFormatEncoder(unittest.TestCase):
  20. """测试 default_encoder 函数和 DateEncoder 类"""
  21. def setUp(self):
  22. from utils.format import default_encoder, DateEncoder
  23. self.default_encoder_func = default_encoder
  24. self.date_encoder_cls = DateEncoder
  25. def test_encode_datetime(self):
  26. """测试 datetime 编码"""
  27. dt = datetime(2025, 7, 30, 14, 30, 0)
  28. result = self.default_encoder_func(dt)
  29. self.assertEqual(result, '2025-07-30 14:30:00')
  30. def test_encode_date(self):
  31. """测试 date 编码"""
  32. d = date(2025, 7, 30)
  33. result = self.default_encoder_func(d)
  34. self.assertEqual(result, '2025-07-30')
  35. def test_encode_timedelta(self):
  36. """测试 timedelta 编码"""
  37. td = timedelta(hours=5, minutes=30)
  38. result = self.default_encoder_func(td)
  39. self.assertEqual(result, '5:30:00')
  40. def test_encode_decimal(self):
  41. """测试 Decimal 编码"""
  42. dec = Decimal('123.45')
  43. result = self.default_encoder_func(dec)
  44. self.assertIsInstance(result, float)
  45. self.assertAlmostEqual(result, 123.45)
  46. def test_encode_unknown(self):
  47. """测试未知类型原样返回"""
  48. obj = "hello"
  49. result = self.default_encoder_func(obj)
  50. self.assertEqual(result, "hello")
  51. def test_date_encoder_json_dumps(self):
  52. """测试 DateEncoder 类与 json.dumps 配合使用"""
  53. data = {
  54. "date": date(2025, 7, 30),
  55. "datetime": datetime(2025, 7, 30, 14, 30, 0),
  56. "value": Decimal('99.99')
  57. }
  58. result = json.dumps(data, cls=self.date_encoder_cls)
  59. parsed = json.loads(result)
  60. self.assertEqual(parsed["date"], "2025-07-30")
  61. self.assertEqual(parsed["datetime"], "2025-07-30 14:30:00")
  62. self.assertEqual(parsed["value"], 99.99)
  63. # ==================== 2. MCP 服务器集成测试(需要数据库) ====================
  64. @unittest.skipUnless(os.getenv("RUN_ONLINE_TESTS") == "1", "需要数据库;设置 RUN_ONLINE_TESTS=1 后运行")
  65. class TestWeatherMCPIntegration(unittest.TestCase):
  66. """集成测试:天气 MCP 服务器"""
  67. @classmethod
  68. def setUpClass(cls):
  69. from mcp_server.mcp_weather_server import WeatherService
  70. cls.service = WeatherService()
  71. def test_query_weather_single_day(self):
  72. """使用 insert_may01_data.sql 中的成都天气样例。"""
  73. result = self.service.query_weather("成都", "2026-05-01", "2026-05-01")
  74. parsed = json.loads(result)
  75. self.assertEqual(parsed["status"], "success")
  76. self.assertGreater(len(parsed["data"]), 0)
  77. first = parsed["data"][0]
  78. self.assertEqual(first["city"], "成都")
  79. self.assertEqual(first["fx_date"], "2026-05-01")
  80. self.assertEqual(first["temp_max"], 28)
  81. self.assertEqual(first["temp_min"], 18)
  82. def test_query_weather_date_range(self):
  83. """范围查询只返回已有日期的数据,不会补造缺失日期。"""
  84. result = self.service.query_weather("成都", "2026-04-29", "2026-05-03")
  85. parsed = json.loads(result)
  86. self.assertEqual(parsed["status"], "success")
  87. self.assertEqual([day["fx_date"] for day in parsed["data"]], ["2026-05-01"])
  88. for day in parsed["data"]:
  89. self.assertEqual(day["city"], "成都")
  90. self.assertGreaterEqual(day["fx_date"], "2026-04-29")
  91. self.assertLessEqual(day["fx_date"], "2026-05-03")
  92. # 同一城市,排除样例日期后应返回无数据。
  93. outside = self.service.query_weather("成都", "2026-05-02", "2026-05-03")
  94. self.assertEqual(json.loads(outside)["status"], "no_data")
  95. def test_query_weather_chengdu(self):
  96. """测试成都天气查询"""
  97. result = self.service.query_weather("成都", "2026-05-01", "2026-05-01")
  98. parsed = json.loads(result)
  99. self.assertEqual(parsed["status"], "success")
  100. self.assertGreater(len(parsed["data"]), 0)
  101. self.assertEqual(parsed["data"][0]["city"], "成都")
  102. def test_query_weather_no_data(self):
  103. """测试无数据情况"""
  104. result = self.service.query_weather("火星", "2026-05-01", "2026-05-01")
  105. parsed = json.loads(result)
  106. self.assertEqual(parsed["status"], "no_data")
  107. # def test_weather_mcp_server_config(self):
  108. # """验证天气 MCP 服务器配置"""
  109. # from mcp_server.mcp_weather_server import create_weather_mcp_server
  110. # server = create_weather_mcp_server()
  111. # self.assertEqual(server.name, "WeatherTools")
  112. @unittest.skipUnless(os.getenv("RUN_ONLINE_TESTS") == "1", "需要数据库;设置 RUN_ONLINE_TESTS=1 后运行")
  113. class TestTicketMCPIntegration(unittest.TestCase):
  114. """集成测试:票务 MCP 服务器"""
  115. @classmethod
  116. def setUpClass(cls):
  117. from mcp_server.mcp_ticket_server import TicketService
  118. cls.service = TicketService()
  119. def test_query_train(self):
  120. """测试高铁票查询——北京到成都 2026-05-01"""
  121. result = self.service.query_train("北京", "成都", "2026-05-01")
  122. parsed = json.loads(result)
  123. self.assertEqual(parsed["status"], "success")
  124. self.assertGreater(len(parsed["data"]), 0)
  125. first = parsed["data"][0]
  126. self.assertEqual(first["departure_city"], "北京")
  127. self.assertEqual(first["arrival_city"], "成都")
  128. self.assertIn("train_number", first)
  129. def test_query_train_with_seat(self):
  130. """测试带座位类型的高铁票查询——北京到成都 二等座"""
  131. result = self.service.query_train("北京", "成都", "2026-05-01", "二等座")
  132. parsed = json.loads(result)
  133. self.assertEqual(parsed["status"], "success")
  134. self.assertGreater(len(parsed["data"]), 0)
  135. for item in parsed["data"]:
  136. self.assertEqual(item["seat_type"], "二等座")
  137. def test_query_train_no_data(self):
  138. """测试无数据情况——北京到上海无数据"""
  139. result = self.service.query_train("北京", "上海", "2026-05-01")
  140. parsed = json.loads(result)
  141. self.assertEqual(parsed["status"], "no_data")
  142. def test_query_flight(self):
  143. """测试航班查询——北京到成都 2026-05-01"""
  144. result = self.service.query_flight("北京", "成都", "2026-05-01")
  145. parsed = json.loads(result)
  146. self.assertEqual(parsed["status"], "success")
  147. self.assertGreater(len(parsed["data"]), 0)
  148. first = parsed["data"][0]
  149. self.assertEqual(first["departure_city"], "北京")
  150. self.assertEqual(first["arrival_city"], "成都")
  151. def test_query_flight_with_cabin(self):
  152. """测试带舱位类型的航班查询——头等舱"""
  153. result = self.service.query_flight("北京", "成都", "2026-05-01", "头等舱")
  154. parsed = json.loads(result)
  155. self.assertEqual(parsed["status"], "success")
  156. self.assertGreater(len(parsed["data"]), 0)
  157. for item in parsed["data"]:
  158. self.assertEqual(item["cabin_type"], "头等舱")
  159. def test_query_event(self):
  160. """测试会展门票查询——成都企业数字化转型峰会 成都 2026-05-01"""
  161. result = self.service.query_event("成都", "成都企业数字化转型峰会", "2026-05-01")
  162. parsed = json.loads(result)
  163. self.assertEqual(parsed["status"], "success")
  164. self.assertGreater(len(parsed["data"]), 0)
  165. first = parsed["data"][0]
  166. self.assertEqual(first["event_name"], "成都企业数字化转型峰会")
  167. self.assertEqual(first["city"], "成都")
  168. self.assertEqual(first["venue"], "成都世纪城新国际会展中心")
  169. def test_query_event_dengzaiqi(self):
  170. """测试会展门票查询——西部智能制造大会 成都"""
  171. result = self.service.query_event("成都", "西部智能制造大会", "2026-05-01")
  172. parsed = json.loads(result)
  173. self.assertEqual(parsed["status"], "success")
  174. self.assertGreater(len(parsed["data"]), 0)
  175. self.assertEqual(parsed["data"][0]["venue"], "中国西部国际博览城")
  176. def test_query_event_no_data(self):
  177. """测试无数据情况——不存在的活动名称"""
  178. result = self.service.query_event("成都", "不存在的企业峰会", "2026-05-01")
  179. parsed = json.loads(result)
  180. self.assertEqual(parsed["status"], "no_data")
  181. # def test_ticket_mcp_server_config(self):
  182. # """验证票务 MCP 服务器配置"""
  183. # from mcp_server.mcp_ticket_server import create_ticket_mcp_server
  184. # server = create_ticket_mcp_server()
  185. # self.assertEqual(server.name, "TicketTools")
  186. @unittest.skipUnless(os.getenv("RUN_ONLINE_TESTS") == "1", "需要数据库;设置 RUN_ONLINE_TESTS=1 后运行")
  187. class TestTripMCPIntegration(unittest.TestCase):
  188. """集成测试:差旅 MCP 服务器"""
  189. @classmethod
  190. def setUpClass(cls):
  191. from mcp_server.mcp_trip_server import TripService
  192. cls.service = TripService()
  193. def test_query_car_rental(self):
  194. """测试商务用车查询——成都 2026-05-01"""
  195. result = self.service.query_car_rental("成都", "成都", "2026-05-01")
  196. parsed = json.loads(result)
  197. self.assertEqual(parsed["status"], "success")
  198. self.assertGreater(len(parsed["data"]), 0)
  199. first = parsed["data"][0]
  200. self.assertEqual(first["pickup_city"], "成都")
  201. self.assertEqual(first["return_city"], "成都")
  202. def test_query_car_rental_with_type(self):
  203. """测试带车型的商务用车查询——SUV"""
  204. result = self.service.query_car_rental("成都", "成都", "2026-05-01", "SUV")
  205. parsed = json.loads(result)
  206. self.assertEqual(parsed["status"], "success")
  207. self.assertGreater(len(parsed["data"]), 0)
  208. for item in parsed["data"]:
  209. self.assertEqual(item["car_type"], "SUV")
  210. def test_query_car_rental_no_data(self):
  211. """测试无数据情况——北京无商务用车数据"""
  212. result = self.service.query_car_rental("北京", "北京", "2026-05-01")
  213. parsed = json.loads(result)
  214. self.assertEqual(parsed["status"], "no_data")
  215. def test_query_insurance(self):
  216. """测试保险查询(全部)"""
  217. result = self.service.query_insurance()
  218. parsed = json.loads(result)
  219. self.assertEqual(parsed["status"], "success")
  220. self.assertGreater(len(parsed["data"]), 0)
  221. def test_query_insurance_with_type(self):
  222. """测试带类型的保险查询——综合型"""
  223. result = self.service.query_insurance("综合型")
  224. parsed = json.loads(result)
  225. self.assertEqual(parsed["status"], "success")
  226. self.assertGreater(len(parsed["data"]), 0)
  227. for item in parsed["data"]:
  228. self.assertEqual(item["insurance_type"], "综合型")
  229. def test_query_insurance_jingwai(self):
  230. """测试境外型保险查询"""
  231. result = self.service.query_insurance("境外型")
  232. parsed = json.loads(result)
  233. self.assertEqual(parsed["status"], "success")
  234. self.assertGreater(len(parsed["data"]), 0)
  235. for item in parsed["data"]:
  236. self.assertEqual(item["insurance_type"], "境外型")
  237. # def test_trip_mcp_server_config(self):
  238. # """验证差旅 MCP 服务器配置"""
  239. # from mcp_server.mcp_trip_server import create_trip_mcp_server
  240. # server = create_trip_mcp_server()
  241. # self.assertEqual(server.name, "TripTools")
  242. if __name__ == "__main__":
  243. unittest.main(verbosity=2)