"""The shipped IDE must use the same public OmniRoute catalog as every member.""" import io import json import unittest from unittest.mock import patch import ide_catalog as c def row(mid, name, source="omniroute", capabilities=("chat",)): return {"id": mid, "turkservis": {"display_name": name, "source": source, "capabilities": list(capabilities)}} class MemberCatalog(unittest.TestCase): def test_metadata_classifies_only_public_models(self): public = [row("plain", "Plain"), row("team", "Team")] metadata = [{"id": "plain", "kind": "model"}, {"id": "team", "kind": "combo"}, {"id": "internal", "kind": "combo"}] replies = [io.BytesIO(json.dumps({"data": public}).encode()), io.BytesIO(json.dumps({"models": metadata}).encode())] with patch.object(c, "ayar_oku", return_value={"enabled": True, "kapsam": "ide"}), \ patch.object(c, "anahtar_oku", return_value="test-key"), \ patch.object(c, "ide_yapilandirma", return_value=("https://example.test/v1", "", [])), \ patch.object(c.urllib.request, "urlopen", side_effect=replies): status, _, _, selected = c.yukle() self.assertEqual(status, "tamam") self.assertEqual({m["id"]: m["kind"] for m in selected}, {"plain": "model", "team": "combo"}) def test_empty_catalog_is_a_refresh_failure(self): with patch.object(c, "ayar_oku", return_value={"enabled": True, "kapsam": "ide"}), \ patch.object(c, "anahtar_oku", return_value="test-key"), \ patch.object(c, "ide_yapilandirma", return_value=("https://example.test/v1", "", [])), \ patch.object(c.urllib.request, "urlopen", return_value=io.BytesIO(b'{"data": []}')): with self.assertRaises(ValueError): c.yukle() def test_public_omniroute_models_do_not_need_private_local_configuration(self): rows = [row("9router-nano-x", "9R · nano/x"), row("agnes-flat", "Agnes Flat"), row("Ajan-Arac", "Ajan-Arac"), row("image", "image", capabilities=("image",))] self.assertEqual({m["id"] for m in c.sec(rows, [])}, {"9router-nano-x", "agnes-flat", "Ajan-Arac"}) def test_direct_route_is_never_added_beside_omniroute(self): rows = [row("9router-nano-x-direct", "9R · nano/x", "9router"), row("9router-nano-x", "9R · nano/x")] self.assertEqual([m["id"] for m in c.sec(rows, ["nano/x"], "hepsi")], ["9router-nano-x"]) def test_model_order_uses_combo_metadata_and_preserves_every_model(self): models = [{"slug": "gpt-z", "display_name": "GPT Z", "priority": 1}, {"slug": "ide/bekci-kurnaz", "display_name": "kurnaz", "priority": 2}, {"slug": "ide/Z-Team", "display_name": "Z-Team", "priority": 5}, {"slug": "ide/9router-cc-opus", "display_name": "9R · cc/opus", "priority": 3}, {"slug": "ide/A-Team", "display_name": "A-Team", "priority": 4}] extras = [{"slug": m["slug"], "kind": "combo" if m["slug"].endswith("Team") else "model"} for m in models if m["slug"].startswith("ide/")] c.sirala_codex(models, extras) self.assertEqual([m["slug"] for m in models], ["gpt-z", "ide/A-Team", "ide/Z-Team", "ide/bekci-kurnaz", "ide/9router-cc-opus"]) self.assertEqual([m["priority"] for m in models], [1, 2, 3, 4, 5]) def test_subscription_models_keep_their_own_order_priority_and_name(self): models = [{"slug": "ide/GPT-6-Astra", "display_name": "GPT-6-Astra", "priority": 3}, {"slug": "gpt-6-astra", "display_name": "GPT-6-Astra", "priority": 2}, {"slug": "ide/A-Team", "display_name": "A-Team", "priority": 0}, {"slug": "gpt-6.1-sol", "display_name": "GPT-6.1-Sol", "priority": 1}, {"slug": "codex-auto-review", "display_name": "Codex Auto Review", "priority": 43}] models.append({"slug": "ide/GPT-6-1-Sol", "display_name": "GPT-6-1-Sol", "priority": 2}) extras = [{"slug": "ide/GPT-6-Astra", "kind": "combo"}, {"slug": "ide/A-Team", "kind": "combo"}, {"slug": "ide/GPT-6-1-Sol", "kind": "combo"}] originals = [dict(m) for m in models if not m["slug"].startswith("ide/")] c.sirala_codex(models, extras) self.assertEqual([m for m in models if not m["slug"].startswith("ide/")], originals) self.assertEqual([m["slug"] for m in models], ["gpt-6-astra", "gpt-6.1-sol", "codex-auto-review", "ide/A-Team", "ide/GPT-6-1-Sol", "ide/GPT-6-Astra"]) self.assertEqual([m["priority"] for m in models[3:]], [44, 45, 46]) self.assertEqual([m["display_name"] for m in models[4:]], ["GPT-6-1-Sol (AITURK)", "GPT-6-Astra (AITURK)"]) def test_description_line_tells_what_the_entry_is_and_fits_a_phone_row(self): yedek = {"kind": "combo", "model": "Yedek", "strategy": "priority", "members": [ {"kind": "model", "model": "nano/z-ai/glm-5.3-flash-uncensored"}, {"kind": "model", "model": "nano/abliteration-ai/abliterated-model-large-v2"}]} takim = {"topology": {"kind": "combo", "strategy": "priority", "members": [ {"kind": "combo", "model": "Takim-Hizli", "strategy": "fusion", "members": [ {"kind": "model", "model": "ag/gemini-3.8-flash-high"}, {"kind": "model", "model": "cc/claude-sonnet-5-5"}]}, yedek]}} self.assertEqual(c.aciklama("Takim-Hizli", "Takim-Hizli", "combo", takim), "Takım: gemini+sonnet") sirali = {"topology": {"kind": "combo", "strategy": "priority", "members": [ {"kind": "model", "model": "cx/gpt-6-astra"}, {"kind": "model", "model": "cc/claude-opus-5-5"}, {"kind": "model", "model": "cc/claude-sonnet-5-5"}, {"kind": "model", "model": "ag/gemini-3.8-flash-high"}]}} self.assertEqual(c.aciklama("Ajan-Arac", "Ajan-Arac", "combo", sirali), "Sıralı: astra>opus>sonnet +1") # An unresolved first member stays visible instead of being hidden behind the fallback. eksik = {"topology": {"kind": "combo", "strategy": "priority", "members": [{"kind": "combo", "model": None}, yedek]}} self.assertEqual(c.aciklama("GPT-6-Sol", "GPT-6-Sol", "combo", eksik), "Sıralı: ?>glm>abliterated") donen = {"topology": {"kind": "combo", "strategy": "round-robin", "members": [yedek, yedek, yedek]}} self.assertEqual(c.aciklama("Takim-Sira", "Takim-Sira", "combo", donen), "Dönüşümlü: 3 üye") self.assertEqual(c.aciklama("X", "X", "combo", None), "Takım") self.assertEqual(c.aciklama("9router-cc-claude-opus-5-5", "9R · cc/claude-opus-5-5", "model"), "Tek model · Claude") self.assertEqual(c.aciklama("9router-nano-qwen-qwen3.8-27b-uncensored-thinking", "9R · nano/qwen3.8-27b-uncensored:thinking", "model"), "NanoGPT · sansürsüz · düşünen") self.assertEqual(c.aciklama("bekci-kurnaz", "kurnaz", "model"), "Tek model · Bekçi") # 9Router aliases: cc = Claude Code, cx = OpenAI Codex, ag = Antigravity. self.assertEqual(c.aciklama("9router-cx-gpt-6-astra", "9R · cx/gpt-6-astra", "model"), "Tek model · Codex") self.assertEqual(c.aciklama("9router-ag-gemini-3.8-flash-high", "9R · ag/gemini-3.8-flash-high", "model"), "Tek model · Antigravity") uzun = {"topology": {"kind": "combo", "strategy": "priority", "members": [ {"kind": "model", "model": f"x/model{chr(97 + i) * 9}"} for i in range(8)]}} self.assertLessEqual(len(c.aciklama("U", "U", "combo", uzun)), c.ACIKLAMA_SINIRI) for ad in ("Takim-Hizli", "9R · cc/claude-opus-5-5"): self.assertNotIn(ad, c.aciklama(ad, ad, "combo" if "Takim" in ad else "model", takim)) def test_picker_names_are_combo_name_or_short_provider_slash_model(self): # User decision, 10 Oct 2026: no "9R" mark; combos by name; single models as provider/model. rows = [("Takim-Hizli", "Takim-Hizli", "combo"), ("9router-cc-claude-opus-5-5", "9R · cc/claude-opus-5-5", "model"), ("9router-cc-claude-opus-4-8", "9R · cc/claude-opus-4-8", "model"), ("9router-cx-gpt-6-astra", "9R · cx/gpt-6-astra", "model"), ("9router-ag-gemini-3.8-flash-high", "9R · ag/gemini-3.8-flash-high", "model"), ("9router-nano-z-ai-glm-5.3-flash-uncensored", "9R · nano/glm-5.3-flash-uncensored", "model"), ("agnes-agnes-2.0-flash", "Agnes 2.0 Flash", "model"), ("agnes-agnes-2.0-flash-high", "agnes-2.0-flash-high", "model"), ("bekci-bekci", "bekci", "model"), ("bekci-kurnaz", "kurnaz", "model"), ("hf-deepseek-ai-DeepSeek-V3", "hf/DeepSeek V3", "model"), ("no-think-9router-cc-claude-opus-5-5", "no-think/claude-opus-5-5", "model"), ("no-think-9router-ag-claude-opus-4-6-thinking", "no-think/claude-opus-4-6-thinking", "model")] adlar = c.adlandir([(k, a) for k, a, _ in rows], turler={k: t for k, _, t in rows}) self.assertEqual([adlar[k] for k, _, _ in rows], ["Takim-Hizli", "cc/opus-5-5", "cc/opus-4-8", "cx/gpt-6-astra", "agy/gemini-3.8-flash-high", "nano/glm-5.3-flash-uncensored", "agnes/agnes-2.0-flash", "agnes/agnes-2.0-flash-high", "bekci/bekci", "bekci/kurnaz", "hf/DeepSeek-V3", "cc/opus-5-5 (no-think)", "agy/opus-4-6-thinking (no-think)"]) self.assertFalse([a for a in adlar.values() if "9R" in a]) # Added models sort after the combos by provider then model. models = [{"slug": "ide/" + k, "display_name": adlar[k], "priority": 0} for k, _, _ in rows] c.sirala_codex(models, [{"slug": "ide/" + k, "kind": t} for k, _, t in rows]) gorunen = [m["display_name"] for m in models] self.assertEqual(gorunen[0], "Takim-Hizli") self.assertEqual(gorunen[1:], sorted(gorunen[1:], key=lambda a: tuple(p.casefold() for p in a.partition("/")[::2]))) if __name__ == "__main__": unittest.main()