fix(avatar): prevent takeover loops and isolate settings

This commit is contained in:
stefanfeng
2026-08-27 11:30:12 +08:00
parent ef58c5f2d2
commit 46d42b7d98
14 changed files with 848 additions and 62 deletions
@@ -103,6 +103,7 @@ def test_avatar_permission_settings_default_and_persist(authorization_context):
assert initial["data"] == {
"avatarId": context["avatar"].id,
"permissions": ["friend", "chat"],
"takeoverReplyDelaySeconds": 180,
}
updated = client.put(
@@ -115,6 +116,7 @@ def test_avatar_permission_settings_default_and_persist(authorization_context):
reloaded = client.get(endpoint, headers=context["owner_headers"]).json()
assert reloaded["data"]["permissions"] == ["friend", "publish", "interact", "takeover"]
assert reloaded["data"]["takeoverReplyDelaySeconds"] == 180
def test_avatar_permission_settings_allow_all_disabled(authorization_context):
@@ -156,3 +158,57 @@ def test_avatar_permission_settings_validate_owner_and_permissions(authorization
unauthenticated = client.get(endpoint)
assert unauthenticated.status_code == 401
def test_takeover_delay_minimum_and_single_active_avatar_per_owner(authorization_context):
from database import SessionLocal
from models import Avatar
context = authorization_context
endpoint = f"/api/avatar/{context['avatar'].id}/permission-settings"
invalid = client.put(
endpoint,
headers=context["owner_headers"],
json={"permissions": ["chat"], "takeoverReplyDelaySeconds": 2},
).json()
assert invalid["code"] == 400
second_avatar_id = f"second-{context['suffix']}"
db = SessionLocal()
try:
db.add(
Avatar(
id=second_avatar_id,
owner_id=context["owner"].huihui_user_id,
name="第二个分身",
status="active",
config={"authorizationPermissions": ["chat", "takeover"]},
)
)
db.commit()
finally:
db.close()
try:
updated = client.put(
endpoint,
headers=context["owner_headers"],
json={"permissions": ["chat", "takeover"], "takeoverReplyDelaySeconds": 3},
).json()
assert updated["code"] == 200
assert updated["data"]["takeoverReplyDelaySeconds"] == 3
assert updated["data"]["disabledAvatarIds"] == [second_avatar_id]
db = SessionLocal()
try:
second = db.query(Avatar).filter(Avatar.id == second_avatar_id).one()
assert "takeover" not in second.config["authorizationPermissions"]
finally:
db.close()
finally:
db = SessionLocal()
try:
db.query(Avatar).filter(Avatar.id == second_avatar_id).delete()
db.commit()
finally:
db.close()