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()