import json import os import sqlite3 import tempfile import unittest from types import SimpleNamespace from unittest.mock import patch from app.services import avatar_service class AvatarSquareAuthorizationTests(unittest.TestCase): def setUp(self): fd, self.db_path = tempfile.mkstemp(suffix=".db") os.close(fd) connection = sqlite3.connect(self.db_path) connection.executescript(""" CREATE TABLE users ( huihui_user_id TEXT, nickname TEXT, avatar_url TEXT, huihui_token TEXT ); CREATE TABLE avatars ( id TEXT, owner_id TEXT, name TEXT, display_name TEXT, description TEXT, photo_url TEXT, config TEXT, status TEXT ); """) connection.execute( "INSERT INTO users VALUES (?, ?, ?, ?)", ("huihui-7", "主人", "/owner.jpg", "huihui-token"), ) connection.commit() connection.close() avatar_service._engine = None avatar_service._SessionLocal = None self.path_patch = patch.object(avatar_service.settings, "AVATAR_DB_PATH", self.db_path) self.path_patch.start() def tearDown(self): self.path_patch.stop() if avatar_service._engine is not None: avatar_service._engine.dispose() avatar_service._engine = None avatar_service._SessionLocal = None os.unlink(self.db_path) def _insert_avatar(self, permissions, *, status="active", token=None): connection = sqlite3.connect(self.db_path) connection.execute( "INSERT INTO avatars VALUES (?, ?, ?, ?, ?, ?, ?, ?)", ( "avatar-7", "huihui-7", "avatar", "小会", "语气友好,表达简洁", "/avatar.jpg", json.dumps({ "authorizationPermissions": permissions, "replyStyle": "warm", "responseLength": "short", }), status, ), ) if token is not None: connection.execute( "UPDATE users SET huihui_token = ? WHERE huihui_user_id = ?", (token, "huihui-7"), ) connection.commit() connection.close() def test_interact_permission_exposes_only_requested_square_actions(self): self._insert_avatar(["chat", "interact"]) permissions = avatar_service.get_square_interaction_permissions("avatar-7") self.assertEqual( permissions, frozenset({"like", "collect", "comment", "reply"}), ) self.assertNotIn("forward", permissions) def test_missing_permission_inactive_avatar_or_missing_token_denies_execution(self): scenarios = [ (["chat"], "active", "huihui-token"), (["interact"], "inactive", "huihui-token"), (["interact"], "active", ""), ] for permissions, status, token in scenarios: with self.subTest(permissions=permissions, status=status, token=token): connection = sqlite3.connect(self.db_path) connection.execute("DELETE FROM avatars") connection.commit() connection.close() self._insert_avatar(permissions, status=status, token=token) self.assertEqual( avatar_service.get_square_interaction_permissions("avatar-7"), frozenset(), ) def test_delegated_avatar_identity_is_recognized_without_matching_normal_users(self): delegated = SimpleNamespace(account="__avatar__:avatar-7") normal = SimpleNamespace(account="13800000000") self.assertTrue(avatar_service.is_delegated_avatar_user(delegated)) self.assertEqual(avatar_service.delegated_avatar_id(delegated), "avatar-7") self.assertFalse(avatar_service.is_delegated_avatar_user(normal)) self.assertEqual(avatar_service.delegated_avatar_id(normal), "") def test_response_length_maps_to_scheduler_comment_limits(self): self.assertEqual(avatar_service._word_count_range({"responseLength": "short"}), (10, 35)) self.assertEqual(avatar_service._word_count_range({"responseLength": "long"}), (30, 80)) self.assertEqual(avatar_service._word_count_range({"responseLength": "unknown"}), (20, 60)) if __name__ == "__main__": unittest.main()