test_market_engine.py 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. import json
  2. from pathlib import Path
  3. import unittest
  4. from app.market import MarketService
  5. from app.market.models import InquiryRequest
  6. from app.repository import RDFRepository
  7. ROOT = Path(__file__).resolve().parents[1]
  8. HAIER = "https://gesli.example/sample#Haier"
  9. HISENSE = "https://gesli.example/sample#Hisense"
  10. class MarketEngineTest(unittest.TestCase):
  11. def setUp(self):
  12. self.service = MarketService(ROOT, RDFRepository(ROOT))
  13. def test_default_inquiry_builds_three_ranked_solutions(self):
  14. result = self.service.recommend(InquiryRequest(customer=HAIER))
  15. self.assertEqual(
  16. result["pipeline"],
  17. {
  18. "discovered": 9,
  19. "rejected": 6,
  20. "feasible": 3,
  21. "generated_solutions": 3,
  22. },
  23. )
  24. self.assertEqual(result["recommendation"]["solution_type"], "均衡方案")
  25. self.assertEqual(result["recommendation"]["supplier_name"], "马士基青岛分支机构")
  26. self.assertEqual(
  27. {candidate["solution_type"] for candidate in result["candidates"]},
  28. {"经济方案", "均衡方案", "时效方案"},
  29. )
  30. def test_form_only_exposes_routes_with_market_context(self):
  31. options = self.service.form_options()
  32. self.assertEqual(options["origins"], ["青岛港", "青岛胶东机场"])
  33. self.assertEqual(options["destinations"], ["汉堡港", "法兰克福机场", "洛杉矶港"])
  34. self.assertEqual(options["route_types"], ["中转", "直航", "直达"])
  35. self.assertEqual(len(options["routes"]), 3)
  36. def test_customer_profile_changes_recommendation(self):
  37. haier = self.service.recommend(InquiryRequest(customer=HAIER))
  38. hisense = self.service.recommend(InquiryRequest(customer=HISENSE))
  39. self.assertEqual(haier["recommendation"]["solution_type"], "均衡方案")
  40. self.assertEqual(hisense["recommendation"]["solution_type"], "经济方案")
  41. self.assertNotEqual(
  42. haier["recommendation"]["final_quote_amount"],
  43. hisense["recommendation"]["final_quote_amount"],
  44. )
  45. def test_expired_quotes_are_rejected_with_reasons(self):
  46. result = self.service.recommend(InquiryRequest(customer=HAIER))
  47. rejected = {item["quote_no"]: item for item in result["rejected_candidates"]}
  48. self.assertIn("SQ-EGL-20260710-EXPIRED", rejected)
  49. self.assertTrue(
  50. any("报价已于2026-07-18失效" in reason for reason in rejected["SQ-EGL-20260710-EXPIRED"]["reasons"])
  51. )
  52. def test_dangerous_goods_uses_only_specialized_candidates(self):
  53. inquiry = InquiryRequest(
  54. customer="https://gesli.example/sample#Sailun",
  55. dangerous_goods=True,
  56. )
  57. result = self.service.recommend(inquiry)
  58. self.assertEqual(result["pipeline"]["feasible"], 3)
  59. self.assertTrue(
  60. all("危险品" in candidate["product_name"] for candidate in result["candidates"])
  61. )
  62. def test_hamburg_route_builds_three_solutions(self):
  63. result = self.service.recommend(InquiryRequest(
  64. customer="https://gesli.example/sample#Midea",
  65. destination="汉堡港",
  66. requested_departure_date="2026-07-28",
  67. max_transit_days=36,
  68. target_amount=42500,
  69. ))
  70. self.assertEqual(result["pipeline"]["feasible"], 3)
  71. self.assertEqual(result["pipeline"]["rejected"], 1)
  72. def test_time_sensitive_air_customer_gets_express_solution(self):
  73. result = self.service.recommend(InquiryRequest(
  74. customer="https://gesli.example/sample#QingdaoMedTech",
  75. origin="青岛胶东机场",
  76. destination="法兰克福机场",
  77. requested_departure_date="2026-07-27",
  78. max_transit_days=6,
  79. target_amount=68000,
  80. preferred_route_type="直达",
  81. ))
  82. self.assertEqual(result["recommendation"]["solution_type"], "时效方案")
  83. self.assertEqual(result["recommendation"]["supplier_name"], "汉莎货运青岛代理")
  84. def test_sales_response_does_not_expose_supplier_cost(self):
  85. result = self.service.recommend(InquiryRequest(customer=HAIER))
  86. serialized = json.dumps(result, ensure_ascii=False)
  87. self.assertNotIn("supplier_cost", serialized)
  88. self.assertNotIn("supplierCostAmount", serialized)
  89. self.assertIn("cost_risk_status", serialized)
  90. def test_decision_audit_is_recorded_as_rdf_assertions(self):
  91. result = self.service.recommend(InquiryRequest(customer=HAIER))
  92. audit = self.service.get_audit(result["run_id"])
  93. self.assertIsNotNone(audit)
  94. self.assertEqual(audit["candidate_count"], 3)
  95. self.assertGreaterEqual(audit["rdf_assertions"], 9)
  96. if __name__ == "__main__":
  97. unittest.main()