from __future__ import annotations from fnmatch import fnmatchcase from pathlib import Path import re import tempfile import tomllib import unittest from urllib.parse import urlsplit from ai_triage.api_routing import ApiTeamRoutingError, load_api_team_router class ApiTeamRoutingTests(unittest.TestCase): def _mapping_file(self, directory: str, content: str) -> Path: path = Path(directory) / "api-team-map.toml" path.write_text(content, encoding="utf-8") return path def test_matches_request_path_without_query_string(self) -> None: with tempfile.TemporaryDirectory() as directory: router = load_api_team_router( self._mapping_file( directory, """ version = 1 [[routes]] path_pattern = "/api/v2025/cloud/v1beta2/tidbs/*" team = "premium" """, ), {"premium": "SOE - Essential & Premium & TiOperator"}, ) team = router.resolve( "https://us-west-2.example.invalid/api/v2025/cloud/v1beta2/" "tidbs/123:resetRootPassword?orgId=456" ) self.assertEqual("premium", team) def test_more_specific_path_rule_wins(self) -> None: with tempfile.TemporaryDirectory() as directory: router = load_api_team_router( self._mapping_file( directory, """ version = 1 [[routes]] path_pattern = "/api/v2025/cloud/v1beta2/tidbs/*" team = "premium" [[routes]] path_pattern = "/api/v2025/cloud/v1beta2/tidbs/*/premiumMigrations*" team = "tidb_x_tools" """, ), { "premium": "SOE - Essential & Premium & TiOperator", "tidb_x_tools": "SOE - Data Integration", }, ) team = router.resolve( "/api/v2025/cloud/v1beta2/tidbs/123/premiumMigrations:validate" ) self.assertEqual("tidb_x_tools", team) def test_unmatched_path_returns_none_for_ai_fallback(self) -> None: with tempfile.TemporaryDirectory() as directory: router = load_api_team_router( self._mapping_file( directory, """ version = 1 [[routes]] path_pattern = "/api/v1/orgs/*/api_keys/*" team = "account" """, ), {"account": "SOE - Billing/Account"}, ) team = router.resolve("https://example.invalid/unknown/path") self.assertIsNone(team) def test_rejects_team_missing_from_live_oncall_catalog(self) -> None: with tempfile.TemporaryDirectory() as directory: path = self._mapping_file( directory, """ version = 1 [[routes]] path_pattern = "/api/v1/orgs/*" team = "invented_team" """, ) with self.assertRaisesRegex(ApiTeamRoutingError, "invented_team"): load_api_team_router(path, {"account": "SOE - Billing/Account"}) def test_rejects_patterns_containing_query_strings(self) -> None: with tempfile.TemporaryDirectory() as directory: path = self._mapping_file( directory, """ version = 1 [[routes]] path_pattern = "/tidbs/*?orgId=*" team = "premium" """, ) with self.assertRaisesRegex(ApiTeamRoutingError, "path_pattern"): load_api_team_router( path, {"premium": "SOE - Essential & Premium & TiOperator"}, ) def test_project_mapping_routes_representative_current_yaml_paths(self) -> None: router = load_api_team_router( Path(__file__).parents[1] / "routing" / "api-team-map.toml", { "account": "SOE - Billing/Account", "global_control": "SOE - Global & Dedicated", "o11y": "SOE - O11Y", "premium": "SOE - Essential & Premium & TiOperator", "security": "SOE - Security", "tidb_x_tools": "SOE - Data Integration", }, ) cases = { ( "https://us-west-2.staging.shared.aws.tidbcloud.com/" "api/v2025/cloud/v1beta2/tidbs/123:resetRootPassword?orgId=456" ): "premium", ( "https://cloud.staging.tidbapi.com/v1beta2/" "tidbs/123/imports:checkSource" ): "tidb_x_tools", ( "https://dedicated.staging.tidbapi.com/v1beta1/" "clusters/123:resetRootPassword" ): "global_control", ( "https://cloud.staging.tidbapi.com/v1beta2/" "tidbs/123/dualLayerDataEncryption" ): "security", "https://example.invalid/not-in-current-map": None, } for request_url, expected in cases.items(): with self.subTest(request_url=request_url): self.assertEqual(expected, router.resolve(request_url)) def test_every_project_mapping_rule_comes_from_a_current_testcase_path( self, ) -> None: project_root = Path(__file__).parents[2] mapping_path = ( project_root / "ai_triage" / "routing" / "api-team-map.toml" ) testcase_root = project_root / "scenario_testcases" if not testcase_root.is_dir(): self.skipTest("standalone deployment has no scenario_testcases source tree") with mapping_path.open("rb") as file: patterns = [ route["path_pattern"] for route in tomllib.load(file)["routes"] ] current_paths: set[str] = set() for testcase in testcase_root.glob("*.yaml"): content = testcase.read_text(encoding="utf-8") base_match = re.search( r"(?m)^\s+base_path:\s*(?P\S+)\s*$", content, ) base_path = ( base_match.group("base") if base_match is not None and base_match.group("base") != "null" else "" ) for raw_path in re.findall( r'"path"\s*:\s*"(?P[^"]+)"', content, ): parsed = urlsplit(raw_path) path = parsed.path if not parsed.scheme and base_path: path = base_path.rstrip("/") + "/" + path.lstrip("/") current_paths.add(path) stale = [ pattern for pattern in patterns if not any(fnmatchcase(path, pattern) for path in current_paths) ] self.assertEqual([], stale) if __name__ == "__main__": unittest.main()