Add deterministic article search to MCP
This commit is contained in:
@@ -7,6 +7,7 @@ from roll_calculation import (
|
||||
CalculationRequest,
|
||||
CalculationState,
|
||||
_canonical_name,
|
||||
article_production_site,
|
||||
calculate_roll,
|
||||
get_article,
|
||||
modify_calculation,
|
||||
@@ -446,6 +447,128 @@ class RollCalculationTests(unittest.TestCase):
|
||||
)
|
||||
self.assertEqual(result["status"], "success")
|
||||
|
||||
def test_search_exact_visible_name_keeps_duplicate_article_numbers(self):
|
||||
result = ArticleRepository.load().search("Bfix NSP 4900, 5,00 x 40 m")
|
||||
|
||||
self.assertEqual(result["status"], "search_results")
|
||||
self.assertEqual(result["total_matches"], 4)
|
||||
self.assertEqual(result["candidates"][:2], [
|
||||
{
|
||||
"article_number": "180205",
|
||||
"name": "Bfix NSP 4900, 5,00 x 40 m",
|
||||
"width_m": 5.0,
|
||||
"production_site": None,
|
||||
},
|
||||
{
|
||||
"article_number": "8180205",
|
||||
"name": "Bfix NSP 4900, 5,00 x 40 m",
|
||||
"width_m": 5.0,
|
||||
"production_site": "Malaysia",
|
||||
},
|
||||
])
|
||||
|
||||
def test_article_number_production_site_rule_only_identifies_malaysia(self):
|
||||
self.assertEqual(article_production_site("8180205"), "Malaysia")
|
||||
self.assertIsNone(article_production_site("180205"))
|
||||
|
||||
def test_search_candidates_expose_site_without_changing_order_or_resolve(self):
|
||||
repository = ArticleRepository.load()
|
||||
candidates = repository.search(
|
||||
"Bfix NSP 4900, 5,00 x 40 m"
|
||||
)["candidates"]
|
||||
|
||||
self.assertEqual(
|
||||
[candidate["article_number"] for candidate in candidates],
|
||||
["180205", "8180205", "182815", "206900"],
|
||||
)
|
||||
self.assertEqual(
|
||||
[candidate["production_site"] for candidate in candidates],
|
||||
[None, "Malaysia", None, None],
|
||||
)
|
||||
resolved = repository.resolve("8180205", None)
|
||||
self.assertEqual(resolved["status"], "resolved")
|
||||
self.assertNotIn("production_site", resolved["article"])
|
||||
|
||||
def test_search_bentofix_alias_matches_bfix_without_resolving_one_article(self):
|
||||
repository = ArticleRepository.load()
|
||||
|
||||
full_name = repository.search("Bentofix NSP 4900")
|
||||
abbreviated = repository.search("Bfix NSP 4900")
|
||||
|
||||
self.assertEqual(full_name["candidates"], abbreviated["candidates"])
|
||||
self.assertGreater(full_name["total_matches"], 2)
|
||||
self.assertIn("180205", [
|
||||
candidate["article_number"] for candidate in full_name["candidates"]
|
||||
])
|
||||
|
||||
def test_search_partial_name_returns_related_products_without_resolving_one(self):
|
||||
result = ArticleRepository.load().search("NSP 4900")
|
||||
|
||||
self.assertEqual(result["status"], "search_results")
|
||||
self.assertGreater(result["total_matches"], 4)
|
||||
self.assertIn("180205", [
|
||||
candidate["article_number"] for candidate in result["candidates"]
|
||||
])
|
||||
self.assertIn("206900", [
|
||||
candidate["article_number"] for candidate in result["candidates"]
|
||||
])
|
||||
|
||||
def test_search_qualifiers_and_dimensions_narrow_real_data_results(self):
|
||||
repository = ArticleRepository.load()
|
||||
base = repository.search("Bfix NSP 4900")
|
||||
dimensions = repository.search("Bfix NSP 4900, 5,00 x 40 m")
|
||||
qualified = repository.search("Bfix NSP 4900 GRI GCL3")
|
||||
|
||||
self.assertLess(dimensions["total_matches"], base["total_matches"])
|
||||
self.assertEqual(
|
||||
[candidate["article_number"] for candidate in qualified["candidates"]],
|
||||
["206900"],
|
||||
)
|
||||
|
||||
def test_search_existing_family_aliases_are_equivalent(self):
|
||||
repository = ArticleRepository.load()
|
||||
|
||||
self.assertEqual(
|
||||
repository.search("Secutex R 1801")["candidates"],
|
||||
repository.search("StEx R 1801")["candidates"],
|
||||
)
|
||||
self.assertEqual(
|
||||
repository.search("Secugrid 40/40 Q6")["candidates"],
|
||||
repository.search("Sgrid 40/40 Q6")["candidates"],
|
||||
)
|
||||
|
||||
def test_search_is_stable_and_handles_empty_and_unknown_queries(self):
|
||||
repository = ArticleRepository.load()
|
||||
|
||||
self.assertEqual(
|
||||
repository.search("NSP 4900"), repository.search("NSP 4900")
|
||||
)
|
||||
self.assertEqual(repository.search(" "), {
|
||||
"status": "search_not_found",
|
||||
"query": "",
|
||||
"total_matches": 0,
|
||||
"candidates": [],
|
||||
})
|
||||
self.assertEqual(
|
||||
repository.search("not a RollCalc product")["status"],
|
||||
"search_not_found",
|
||||
)
|
||||
|
||||
def test_search_reports_total_when_the_stable_result_list_is_capped(self):
|
||||
result = ArticleRepository.load().search("Secugrid")
|
||||
|
||||
self.assertEqual(result["status"], "search_results")
|
||||
self.assertGreater(result["total_matches"], len(result["candidates"]))
|
||||
self.assertEqual(len(result["candidates"]), 20)
|
||||
|
||||
def test_search_does_not_change_existing_resolve_ambiguity(self):
|
||||
repository = ArticleRepository.load()
|
||||
|
||||
self.assertEqual(
|
||||
repository.resolve(None, "Bentofix NSP 4900")["status"],
|
||||
"article_ambiguous",
|
||||
)
|
||||
|
||||
def test_name_only_matching_uses_deterministic_bfix_alias(self):
|
||||
result = self.calculate_fixture({
|
||||
"article_name_hint": "Bentofix Alias Product",
|
||||
|
||||
Reference in New Issue
Block a user