fix: close platform-check silent-success loophole — raise WebsiteError on None list, missing account, or stale platform after update
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user