Files
kefu/deploy/review-picker-20260918/backend-before/source-test_review_assistant_contacts.py
T
2026-09-21 10:34:06 +08:00

139 lines
7.3 KiB
Python

"""Recipient discovery regression: synthetic databases, no native calls or sends."""
import contextlib
import json
import os
from pathlib import Path
import sqlite3
import tempfile
import unittest
from unittest import mock
import review_assistant_contacts as contacts
ACCOUNT = '168800000000001'
ACTIVE = {'ready': True, 'account_id': ACCOUNT, 'account_name': 'Test', 'reason': ''}
class ContactDirectoryTest(unittest.TestCase):
def setUp(self):
self.temp = tempfile.TemporaryDirectory()
self.addCleanup(self.temp.cleanup)
self.root = Path(self.temp.name)
self.folder = self.root / ACCOUNT
self.folder.mkdir()
with contextlib.closing(sqlite3.connect(self.folder / 'message.db')) as db, db:
db.execute('CREATE TABLE message_table(conversation_id TEXT,send_time REAL,content TEXT)')
db.executemany('INSERT INTO message_table VALUES(?,?,?)', [
('M:101', 1700000000, 'MESSAGE_BODY_NOT_READ'),
('S:1_2', 1700000010, 'internal'), ('R:10', 1700000020, 'group')])
self.write_user('''CREATE TABLE user_table(id TEXT,name TEXT,real_name TEXT,account TEXT);
INSERT INTO user_table VALUES('168800000000001','托管账号','','');
CREATE TABLE wechat_contactV1(wxid TEXT,gene INTEGER,name TEXT);
INSERT INTO wechat_contactV1 VALUES('wxid_one',101,'同名助理');
INSERT INTO wechat_contactV1 VALUES('wxid_two',999,'同名助理');
CREATE TABLE wx_friend(user_id TEXT,nick_name TEXT,wx_id TEXT);
CREATE TABLE external_user_relation_v3(user_id TEXT,remarks TEXT,real_remarks TEXT);''')
self.patch = mock.patch.object(contacts, 'active_context', return_value=ACTIVE)
self.identity = self.patch.start()
self.addCleanup(self.patch.stop)
contacts._CACHE.clear()
def write_user(self, sql):
with contextlib.closing(sqlite3.connect(self.folder / 'user.db')) as db, db:
db.executescript(sql)
def listing(self, *args, **kwargs):
return contacts.list_recipients(ACCOUNT, *args, cache_roots=[self.root], **kwargs)
def test_directory_contacts_are_visible_but_never_fabricate_send_targets(self):
result = self.listing()
self.assertTrue(result['ready'])
self.assertEqual((result['total'], result['selectable_total'], result['contact_total']), (3, 1, 2))
self.assertEqual({i['id'] for i in result['items'] if i['selectable']}, {'M:101'})
self.assertNotIn('M:999', {i['id'] for i in result['items']})
self.assertNotIn('MESSAGE_BODY_NOT_READ', json.dumps(result))
for item in result['items']:
if not item['selectable']:
self.assertIn('刷新消息库', item['unavailableReason'])
self.assertFalse(contacts.validate_recipient(ACCOUNT,item['id'],cache_roots=[self.root])['ok'])
def test_directory_search_and_pagination_keep_same_names_separate(self):
result = self.listing('同名助理', limit=1)
self.assertEqual(result['total'],2)
self.assertTrue(result['has_more'])
second = self.listing('同名助理',limit=1,offset=1)
self.assertNotEqual(result['items'][0]['id'],second['items'][0]['id'])
self.assertEqual(self.listing('wxid_two')['items'][0]['id'],'contact:wxid_two')
def test_explicit_unique_friend_relation_supplies_name_and_search_aliases(self):
self.write_user("INSERT INTO wx_friend VALUES('101','助理备注','wxid_one');")
result = self.listing()
self.assertEqual(result['total'],2)
target = next(i for i in result['items'] if i['id']=='M:101')
self.assertEqual(target['name'],'助理备注')
self.assertTrue(target['selectable'])
self.assertEqual(self.listing('wxid_one')['items'][0]['id'],'M:101')
self.assertTrue(contacts.validate_recipient(ACCOUNT,'M:101',cache_roots=[self.root])['ok'])
def test_ambiguous_friend_mapping_does_not_merge_contacts(self):
for rows in ["('101','','wxid_one'),('202','','wxid_one')",
"('101','','wxid_one'),('101','','wxid_two')"]:
with self.subTest(rows=rows):
self.write_user('DELETE FROM wx_friend;INSERT INTO wx_friend VALUES'+rows+';')
contacts._CACHE.clear()
result=self.listing()
self.assertEqual(result['contact_total'],2)
self.assertEqual(next(i for i in result['items'] if i['id']=='M:101')['name'],'微信联系人')
def test_friend_relation_without_existing_message_does_not_enable_sending(self):
self.write_user("INSERT INTO wx_friend VALUES('999','助理','wxid_two');")
result=self.listing('wxid_two')
self.assertFalse(result['items'][0]['selectable'])
self.assertFalse(contacts.validate_recipient(ACCOUNT,'M:999',cache_roots=[self.root])['ok'])
def test_remark_and_nickname_both_searchable_for_exact_existing_uid(self):
self.write_user("INSERT INTO user_table VALUES('101','助理昵称','','');INSERT INTO external_user_relation_v3 VALUES('101','助理备注','');")
for query in ['助理昵称','助理备注','M:101']:
self.assertEqual(self.listing(query)['items'][0]['id'],'M:101')
self.assertEqual(self.listing('助理备注')['items'][0]['name'],'助理备注')
def test_legacy_database_without_addressbook_tables_is_supported(self):
self.write_user('DROP TABLE wechat_contactV1;DROP TABLE wx_friend;DROP TABLE external_user_relation_v3;')
self.assertEqual(self.listing()['total'],1)
def test_refresh_invalidates_cache_after_database_change(self):
self.assertEqual(self.listing()['total'],3)
self.write_user("INSERT INTO wechat_contactV1 VALUES('wxid_new',123,'新助理');")
self.assertEqual(self.listing('新助理')['total'],1)
def test_latest_wal_revision_wins_between_account_caches(self):
other=self.root/'other'/ACCOUNT
other.mkdir(parents=True)
from shutil import copyfile
copyfile(self.folder/'message.db',other/'message.db')
copyfile(self.folder/'user.db',other/'user.db')
os.utime(self.folder/'user.db',ns=(100,100))
os.utime(other/'user.db',ns=(200,200))
wal=Path(str(self.folder/'user.db')+'-wal')
wal.write_bytes(b'file-time-only')
os.utime(wal,ns=(300,300))
self.assertEqual(contacts._databases(ACCOUNT,[self.root,self.root/'other'])['user.db'],self.folder/'user.db')
def test_default_roots_include_protocol_cache_before_archived_copy(self):
with mock.patch.object(contacts,'application_data_dir',return_value=self.root):
self.assertEqual(contacts._roots()[0],self.root/'wxwork_reply_cache')
def test_other_account_directory_is_never_read(self):
self.identity.return_value={**ACTIVE,'account_id':'168800000000002'}
with mock.patch.object(contacts,'_contacts') as read:
self.assertFalse(self.listing()['ready'])
read.assert_not_called()
def test_lookup_does_not_change_database_files(self):
before={p.name:p.read_bytes() for p in self.folder.glob('*.db')}
self.listing(); contacts.validate_recipient(ACCOUNT,'M:101',cache_roots=[self.root])
self.assertEqual(before,{p.name:p.read_bytes() for p in self.folder.glob('*.db')})
if __name__=='__main__':
unittest.main()