feat: complete T-530 cmhub URL normalization

This commit is contained in:
chengma
2026-07-06 10:31:36 +08:00
parent 42dd8a993f
commit b99019e954
14 changed files with 142 additions and 31 deletions
+22
View File
@@ -516,6 +516,28 @@ class AITests(TempDirMixin, unittest.TestCase):
self.assertEqual("https://cmhub.example.com/api/v1/models", calls[0][1])
self.assertEqual("title-standard", models[0]["alias"])
def test_fetch_cmhub_models_404_returns_clear_base_url_error(self):
calls = []
def fake_request(method, url, **kwargs):
calls.append((method, url, kwargs))
return _RequestsResponse({"detail": "notfound"}, status_code=404)
with mock.patch("app.ai.requests.request", side_effect=fake_request):
with self.assertRaises(ai.CMHubError) as raised:
ai.fetch_cmhub_models(
"https://cmhub.example.com/api/v1/",
"sk-cmhub-secret",
)
self.assertEqual("GET", calls[0][0])
self.assertEqual("https://cmhub.example.com/api/v1/models", calls[0][1])
self.assertEqual("not_found", raised.exception.code)
self.assertEqual(404, raised.exception.status)
message = str(raised.exception)
self.assertIn("cmhub 接口不存在", message)
self.assertIn("/api/v1/models", message)
self.assertNotIn("notfound", message)
def test_fetch_cmhub_balance_returns_points(self):
calls = []
+28
View File
@@ -64,6 +64,34 @@ class AppConfigTests(TempDirMixin, unittest.TestCase):
self.assert_removed(temp_dir)
def test_cmhub_base_url_normalizes_to_gateway_root(self):
cases = {
"https://cmhub.example.com/": "https://cmhub.example.com",
"https://cmhub.example.com/api": "https://cmhub.example.com",
"https://cmhub.example.com/api/v1/": "https://cmhub.example.com",
"https://cmhub.example.com/some/path?x=1": "https://cmhub.example.com",
"http://localhost:8000/api/v1": "http://localhost:8000",
"localhost:8000/api/v1": "localhost:8000",
}
for raw, expected in cases.items():
with self.subTest(raw=raw):
self.assertEqual(expected, appconfig.normalize_cmhub_base_url(raw))
self.assertEqual(
expected + "/api/v1/models",
appconfig.cmhub_request_url(raw, "/api/v1/models"),
)
with self.make_temp_dir() as temp_dir:
config_path = os.path.join(temp_dir, "config.json")
config = appconfig.default_config()
config["ai"]["cmhub"]["base_url"] = "https://cmhub.example.com/api/v1/"
saved = appconfig.save_config(config, path=config_path)
self.assertEqual("https://cmhub.example.com", saved["ai"]["cmhub"]["base_url"])
loaded = appconfig.load_config(config_path)
self.assertEqual("https://cmhub.example.com", loaded["ai"]["cmhub"]["base_url"])
self.assert_removed(temp_dir)
def test_config_rejects_sensitive_fields(self):
with self.make_temp_dir() as temp_dir:
config_path = os.path.join(temp_dir, "config.json")
+3 -1
View File
@@ -818,13 +818,14 @@ class GuiTests(TempDirMixin, unittest.TestCase):
self.assertTrue(tab.model_picker_panel.isHidden())
self.assertTrue(tab.direct_role_panel.isHidden())
self.assertFalse(tab.cmhub_panel.isHidden())
self.assertIn("不要带 /api", tab.cmhub_base_url_hint_label.text())
self.assertEqual("https://cmhub.old", tab.cmhub_base_url_edit.text())
self.assertEqual("sk-old-secret", tab.cmhub_api_key_edit.text())
self.assertEqual(QLineEdit.Password, tab.cmhub_api_key_edit.echoMode())
self.assertEqual("title-old", tab.cmhub_title_alias_combo.currentData())
self.assertEqual("image-old", tab.cmhub_image_alias_combo.currentData())
tab.cmhub_base_url_edit.setText("https://cmhub.example.com")
tab.cmhub_base_url_edit.setText("https://cmhub.example.com/api/v1/")
tab.cmhub_api_key_edit.setText("sk-new-secret")
tab.cmhub_connect_timeout_spin.setValue(12)
tab.cmhub_check_balance_checkbox.setChecked(True)
@@ -859,6 +860,7 @@ class GuiTests(TempDirMixin, unittest.TestCase):
saved = appconfig.load_config(cfg["config_path"])
self.assertEqual("cmhub", saved["ai"]["backend"])
self.assertEqual("https://cmhub.example.com", saved["ai"]["cmhub"]["base_url"])
self.assertEqual("https://cmhub.example.com", tab.cmhub_base_url_edit.text())
self.assertEqual("title-standard", saved["ai"]["cmhub"]["title_alias"])
self.assertEqual("image-standard", saved["ai"]["cmhub"]["image_alias"])
self.assertEqual(12, saved["ai"]["cmhub"]["connect_timeout"])