fix: close platform-check silent-success loophole — raise WebsiteError on None list, missing account, or stale platform after update

This commit is contained in:
SmartUp Developer
2026-07-02 23:28:28 +08:00
parent 2c976f4d48
commit a0c96ed5da
2 changed files with 134 additions and 9 deletions
+7 -3
View File
@@ -48,7 +48,7 @@ from app.schemas.website import (
SyncUpstreamModelsResponse,
)
from app.services.website_client import Sub2ApiWebsiteClient, _extract_id
from app.services.website_client import Sub2ApiWebsiteClient, WebsiteError, _extract_id
from app.services.website_sync import (
binding_sources,
sync_binding,
@@ -1826,9 +1826,13 @@ def organize_website_groups(
remote_acc["group_ids"] = new_group_ids
if "platform" in update_payload:
# 主动核对防线:防止子站忽略 platform 修改而发生静默失败
fresh_accs = c.list_accounts() or []
fresh_accs = c.list_accounts()
if fresh_accs is None:
raise WebsiteError("复查失败:拉取远端账号列表返回空,无法核对平台修改是否生效")
fresh_acc = next((a for a in fresh_accs if str(c.extract_id(a)) == str(old_account_id)), None)
if fresh_acc:
if not fresh_acc:
raise WebsiteError(f"复查失败:在远端账号列表中未找到要更新的账号 {old_account_id}")
remote_account_map[str(old_account_id)] = fresh_acc
new_platform = fresh_acc.get("platform")
if str(new_platform).lower() != str(platform).lower():
+121
View File
@@ -582,3 +582,124 @@ def test_organize_groups_corrects_mismatched_platform_instead_of_recreating(db_s
assert updated_accounts[0][0] == "ACC-G1"
assert updated_accounts[0][1]["platform"] == "anthropic"
assert "平台从 openai 修正为 anthropic" in response.items[0].message
def _seed_platform_mismatch(db_session, monkeypatch, list_accounts_fn):
"""公用工厂:搭建平台错配场景并注入可定制的 list_accounts。"""
from app.models.snapshot import UpstreamRateSnapshot
w = Website(
name="W-PlatChk",
site_type="sub2api",
base_url="http://wp",
enabled=True,
auth_config_json="{}",
timeout_seconds=30,
)
u1 = Upstream(name="U-PlatChk", base_url="http://up")
db_session.add_all([w, u1])
db_session.commit()
db_session.refresh(w)
db_session.refresh(u1)
b1 = WebsiteGroupBinding(
website_id=w.id,
target_group_id="TG1",
target_group_name="TG1-Group",
source_groups_json=json.dumps([{"upstream_id": u1.id, "group_id": "G1"}]),
enabled=True,
)
db_session.add(b1)
k1 = UpstreamGeneratedKey(
upstream_id=u1.id,
group_id="G1",
group_name="G1-Group",
key_name="Key-G1",
key_value="sk-g1-secret",
status="imported",
imported_website_id=w.id,
imported_account_id="ACC-G1",
imported_target_group_id="TG1",
)
db_session.add(k1)
snapshot = UpstreamRateSnapshot(
upstream_id=u1.id,
snapshot_json=json.dumps({
"groups": {
"G1": {"group_name": "G1-Group", "rate": 0.1, "platform": "anthropic"}
}
}),
)
db_session.add(snapshot)
db_session.commit()
call_count = {"n": 0}
class MockClient:
def __init__(self, **kwargs): pass
def __enter__(self): return self
def __exit__(self, *a): pass
def get_groups(self, *a, **kw):
return [{"id": "TG1", "name": "TG1-Group"}]
def list_accounts(self):
call_count["n"] += 1
return list_accounts_fn(call_count["n"])
def extract_id(self, val):
return val.get("id") if isinstance(val, dict) else str(val)
def update_account(self, account_id, body):
return {"id": account_id}
monkeypatch.setattr("app.routers.websites.Sub2ApiWebsiteClient", MockClient)
monkeypatch.setattr(
"app.routers.websites.sync_account_priorities_for_website", lambda db, wid: []
)
return w
def test_organize_groups_platform_update_remote_silently_ignores(db_session, monkeypatch):
"""复查时远端仍返回旧平台值 → 必须报错,不能静默认为成功。"""
def list_accounts_fn(call_n):
# 第 1 次初始列表;第 2 次复查:仍是 openai(忽略了 platform 写入)
return [{"id": "ACC-G1", "name": "SmartUp-G1", "group_ids": ["TG1"], "platform": "openai"}]
w = _seed_platform_mismatch(db_session, monkeypatch, list_accounts_fn)
response = organize_website_groups(wid=w.id, db=db_session)
failed_items = [item for item in response.items if item.status == "failed"]
assert len(failed_items) == 1, f"期望 1 个 failed 项,实际:{response.items}"
assert "不支持修改平台属性" in failed_items[0].message
def test_organize_groups_platform_update_list_returns_none(db_session, monkeypatch):
"""复查时 list_accounts() 返回 None → 必须报错,不能静默成功。"""
def list_accounts_fn(call_n):
if call_n == 1:
return [{"id": "ACC-G1", "name": "SmartUp-G1", "group_ids": ["TG1"], "platform": "openai"}]
return None # 复查时列表拉取失败
w = _seed_platform_mismatch(db_session, monkeypatch, list_accounts_fn)
response = organize_website_groups(wid=w.id, db=db_session)
failed_items = [item for item in response.items if item.status == "failed"]
assert len(failed_items) == 1, f"期望 1 个 failed 项,实际:{response.items}"
assert "复查失败" in failed_items[0].message
def test_organize_groups_platform_update_account_missing_from_list(db_session, monkeypatch):
"""复查时账号从列表中消失 → 必须报错,不能静默成功。"""
def list_accounts_fn(call_n):
if call_n == 1:
return [{"id": "ACC-G1", "name": "SmartUp-G1", "group_ids": ["TG1"], "platform": "openai"}]
return [] # 复查时账号消失
w = _seed_platform_mismatch(db_session, monkeypatch, list_accounts_fn)
response = organize_website_groups(wid=w.id, db=db_session)
failed_items = [item for item in response.items if item.status == "failed"]
assert len(failed_items) == 1, f"期望 1 个 failed 项,实际:{response.items}"
assert "复查失败" in failed_items[0].message