feat: 一键整理强制对齐分组逻辑,移除旧托管分组并优化统计消息区分
This commit is contained in:
@@ -163,9 +163,10 @@ def test_organize_groups_full_scenarios(db_session, monkeypatch):
|
||||
|
||||
# 6. 断言结果
|
||||
assert response.success is True
|
||||
# 整理完成:创建账号 2 (k1创建 + k4重建) / 已存在 1 (k3已存在并补齐) / 缺少 Key 1 (G2缺少)
|
||||
assert "创建账号 2" in response.message
|
||||
assert "已存在 1" in response.message
|
||||
# 整理完成:已创建 1 / 已重建 1 / 已存在已迁移 1 / 缺少 Key 1
|
||||
assert "已创建 1" in response.message
|
||||
assert "已重建 1" in response.message
|
||||
assert "已存在已迁移 1" in response.message
|
||||
assert "缺少 Key 1" in response.message
|
||||
|
||||
items = response.items
|
||||
@@ -186,7 +187,7 @@ def test_organize_groups_full_scenarios(db_session, monkeypatch):
|
||||
# TG2 分组下的 G3 (已存在并补齐)
|
||||
item_g3 = next(item for item in items if item.target_group_id == "TG2" and item.source_group_id == "G3")
|
||||
assert item_g3.status == "exists"
|
||||
assert "已补齐目标分组绑定" in item_g3.message
|
||||
assert "已迁移到目标分组" in item_g3.message
|
||||
# 校验是否触发了 update_account 补齐了 TG2
|
||||
assert len(updated_accounts) == 1
|
||||
assert updated_accounts[0][0] == "ACC-G3"
|
||||
@@ -301,8 +302,8 @@ def test_organize_groups_list_accounts_none_conservatively_skips(db_session, mon
|
||||
|
||||
# 断言结果:由于其中有 1 个已导入账号校验失败,response.success 应为 False(有失败项)
|
||||
assert response.success is False
|
||||
# 统计信息中应该有“创建账号 1”与“失败 1”
|
||||
assert "创建账号 1" in response.message
|
||||
# 统计信息中应该有“已创建 1”与“失败 1”
|
||||
assert "已创建 1" in response.message
|
||||
assert "失败 1" in response.message
|
||||
|
||||
items = response.items
|
||||
@@ -324,3 +325,157 @@ def test_organize_groups_list_accounts_none_conservatively_skips(db_session, mon
|
||||
assert k3.imported_website_id == w.id
|
||||
assert k3.imported_account_id == "ACC-G3-EXISTING"
|
||||
assert k3.status == "imported"
|
||||
|
||||
|
||||
def test_organize_groups_force_alignment(db_session, monkeypatch):
|
||||
"""测试强对齐场景:
|
||||
1. 已存在账号在旧 SmartUp 分组,整理后移除旧分组并加入新分组。
|
||||
2. 已存在账号同时有非 SmartUp 分组,整理后保留非 SmartUp 分组。
|
||||
3. 已存在账号已经完全一致,整理不重复调用更新接口。
|
||||
4. 多目标分组、多上游来源时,每个账号按自己的当前绑定关系对齐。
|
||||
"""
|
||||
w = Website(
|
||||
name="W1",
|
||||
site_type="sub2api",
|
||||
base_url="http://w1",
|
||||
enabled=True,
|
||||
auth_config_json="{}",
|
||||
timeout_seconds=30
|
||||
)
|
||||
u1 = Upstream(name="U1", base_url="http://u1")
|
||||
db_session.add_all([w, u1])
|
||||
db_session.commit()
|
||||
db_session.refresh(w)
|
||||
db_session.refresh(u1)
|
||||
|
||||
# 两个绑定关系:
|
||||
# 绑定 1:目标分组 TG1 ↔ G1
|
||||
# 绑定 2:目标分组 TG2 ↔ G2
|
||||
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
|
||||
)
|
||||
b2 = WebsiteGroupBinding(
|
||||
website_id=w.id,
|
||||
target_group_id="TG2",
|
||||
target_group_name="TG2-Group",
|
||||
source_groups_json=json.dumps([{"upstream_id": u1.id, "group_id": "G2"}]),
|
||||
enabled=True
|
||||
)
|
||||
db_session.add_all([b1, b2])
|
||||
db_session.commit()
|
||||
|
||||
# 3. 建立 3 个 Key,对应 3 个不同的远端账号状态
|
||||
# k1: 原本在 TG_old (旧 SmartUp 分组),但根据绑定 1 它现在应该在 TG1
|
||||
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="TG_old", # 旧 SmartUp 分组
|
||||
)
|
||||
# k2: 已经在 TG2 (正确),且同时有非 SmartUp 分组 (999) 和另一个旧 SmartUp 分组 (TG1 - 应该被移除)
|
||||
k2 = UpstreamGeneratedKey(
|
||||
upstream_id=u1.id,
|
||||
group_id="G2",
|
||||
group_name="G2-Group",
|
||||
key_name="Key-G2",
|
||||
key_value="sk-g2-secret",
|
||||
status="imported",
|
||||
imported_website_id=w.id,
|
||||
imported_account_id="ACC-G2",
|
||||
imported_target_group_id="TG2",
|
||||
)
|
||||
db_session.add_all([k1, k2])
|
||||
db_session.commit()
|
||||
|
||||
updated_accounts = []
|
||||
|
||||
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"},
|
||||
{"id": "TG2", "name": "TG2-Group"},
|
||||
]
|
||||
def list_accounts(self):
|
||||
return [
|
||||
{
|
||||
"id": "ACC-G1",
|
||||
"name": "SmartUp-G1",
|
||||
"group_ids": ["TG_old"], # 包含旧 SmartUp 分组 TG_old,没有 TG1
|
||||
},
|
||||
{
|
||||
"id": "ACC-G2",
|
||||
"name": "SmartUp-G2",
|
||||
"group_ids": ["TG2", 999, "TG1"], # TG2 是当前正确分组,999 是非托管分组,TG1 是旧托管分组(应移除)
|
||||
}
|
||||
]
|
||||
def extract_id(self, val):
|
||||
return val.get("id") if isinstance(val, dict) else str(val)
|
||||
def update_account(self, account_id, body):
|
||||
updated_accounts.append((account_id, body))
|
||||
return {"id": account_id, "group_ids": body.get("group_ids")}
|
||||
|
||||
monkeypatch.setattr("app.routers.websites.Sub2ApiWebsiteClient", MockClient)
|
||||
monkeypatch.setattr("app.routers.websites.sync_account_priorities_for_website", lambda db, wid: [])
|
||||
|
||||
# 执行一键整理
|
||||
response = organize_website_groups(wid=w.id, db=db_session)
|
||||
|
||||
assert response.success is True
|
||||
|
||||
# ACC-G1 (G1) 的 group_ids 应变更为 ["TG1"] (TG2 被移除了)
|
||||
# ACC-G2 (G2) 的 group_ids 应变更为 [999, "TG2"] (TG1 被移除了,999 保留)
|
||||
assert len(updated_accounts) == 2
|
||||
|
||||
# 验证 ACC-G1 变更
|
||||
up_g1 = next(item for item in updated_accounts if item[0] == "ACC-G1")
|
||||
assert set(str(g) for g in up_g1[1]["group_ids"]) == {"TG1"}
|
||||
|
||||
# 验证 ACC-G2 变更
|
||||
up_g2 = next(item for item in updated_accounts if item[0] == "ACC-G2")
|
||||
assert set(str(g) for g in up_g2[1]["group_ids"]) == {"999", "TG2"}
|
||||
|
||||
# 验证本地 DB 的标记已被同步更新为正确的 TG1 / TG2
|
||||
db_session.refresh(k1)
|
||||
db_session.refresh(k2)
|
||||
assert k1.imported_target_group_id == "TG1"
|
||||
assert k2.imported_target_group_id == "TG2"
|
||||
|
||||
# 验证结果消息汇总包含 aligned 0, migrated 2 (因为两个都发生了迁移/更新)
|
||||
assert "已存在已迁移 2" in response.message
|
||||
|
||||
# 让我们跑一次完全一致的再次整理,验证不触发 update_account (已对齐)
|
||||
updated_accounts.clear()
|
||||
|
||||
class MockClientAligned(MockClient):
|
||||
def list_accounts(self):
|
||||
return [
|
||||
{
|
||||
"id": "ACC-G1",
|
||||
"name": "SmartUp-G1",
|
||||
"group_ids": ["TG1"],
|
||||
},
|
||||
{
|
||||
"id": "ACC-G2",
|
||||
"name": "SmartUp-G2",
|
||||
"group_ids": [999, "TG2"],
|
||||
}
|
||||
]
|
||||
|
||||
monkeypatch.setattr("app.routers.websites.Sub2ApiWebsiteClient", MockClientAligned)
|
||||
response_aligned = organize_website_groups(wid=w.id, db=db_session)
|
||||
assert response_aligned.success is True
|
||||
# 既然已经一致,不会触发 update_account
|
||||
assert len(updated_accounts) == 0
|
||||
assert "已存在且已对齐 2" in response_aligned.message
|
||||
|
||||
Reference in New Issue
Block a user