118 lines
5.4 KiB
Python
118 lines
5.4 KiB
Python
from pathlib import Path
|
|
|
|
target = Path(r'C:\wechat_rpa\test_protocol_review_assistant.py')
|
|
source = target.read_text(encoding='utf-8')
|
|
assert 'class ProtocolSharedCoordinatorTests' not in source
|
|
extra = '''
|
|
class ProtocolSharedCoordinatorTests(unittest.TestCase):
|
|
"""Actual durable coordinator + protocol queue, with no notification worker."""
|
|
make_bot = ProtocolReviewAssistantTests.make_bot
|
|
|
|
def setUp(self):
|
|
import time
|
|
from review_assistant import ReviewAssistantCoordinator
|
|
ProtocolReviewAssistantTests.setUp(self)
|
|
self.base_time = time.time()
|
|
self.db.messages[0]['send_time'] = self.base_time - 10
|
|
settings = {'review_assistant_accounts': {ACCOUNT: {
|
|
'enabled': True, 'recipient_conv_id': 'M:300'}}}
|
|
self.coordinator = ReviewAssistantCoordinator(
|
|
root=self.root, account=ACCOUNT, pid=1, db=self.db,
|
|
settings_loader=lambda: settings, log=self.logs.append)
|
|
self.addCleanup(self.coordinator.close)
|
|
self.enterContext(mock.patch.object(self.coordinator, 'enqueue_notifications'))
|
|
self.bot = self.make_bot()
|
|
|
|
def add(self, rowid, *, self_reply=False, seconds=None, text='synthetic question'):
|
|
item = message(rowid, self=self_reply, text=text)
|
|
item['send_time'] = self.base_time + (rowid if seconds is None else seconds)
|
|
self.db.add(item)
|
|
return item
|
|
|
|
def review(self):
|
|
self.add(2)
|
|
self.bot.poll_once()
|
|
state = self.bot._pending[self.key]
|
|
self.assertTrue(state['assistant_review_id'])
|
|
return state
|
|
|
|
def test_durable_manual_resolution_and_new_risk_get_distinct_episode(self):
|
|
original_id = self.review()['assistant_review_id']
|
|
self.add(3, self_reply=True, text='phone manual answer')
|
|
self.bot.poll_once()
|
|
self.assertFalse(self.coordinator.has_pending(CONV))
|
|
self.assertTrue(self.coordinator.resumed(CONV))
|
|
self.assertNotIn(self.key, self.bot._pending)
|
|
def risky(context):
|
|
self.bot._pending[self.key].update(judge_risk='high', judge_mode='arbitrate')
|
|
return 'new risky answer'
|
|
self.bot.generate = risky
|
|
self.add(4)
|
|
self.bot.poll_once()
|
|
state = self.bot._pending[self.key]
|
|
self.assertTrue(state['awaiting_review'])
|
|
self.assertFalse(state.get('approved'))
|
|
self.assertNotEqual(state['assistant_review_id'], original_id)
|
|
self.assertTrue(self.coordinator.has_pending(CONV))
|
|
self.assertFalse(self.coordinator.resumed(CONV))
|
|
self.assertFalse(self.sender.calls)
|
|
|
|
def test_durable_completion_survives_protocol_save_failure_without_new_episode(self):
|
|
import sqlite3
|
|
state = self.review()
|
|
original_id = state['assistant_review_id']
|
|
self.add(3, self_reply=True, text='phone manual answer')
|
|
self.db.events.clear()
|
|
with mock.patch.object(self.bot, '_save', side_effect=OSError('synthetic full disk')):
|
|
with self.assertRaises(OSError):
|
|
self.bot._reconcile_assistant_review(self.key, state)
|
|
self.assertIs(self.bot._pending[self.key], state)
|
|
self.assertTrue(self.coordinator.resumed(CONV))
|
|
restored = self.make_bot()
|
|
restored.poll_once()
|
|
self.assertNotIn(self.key, restored._pending)
|
|
with sqlite3.connect(self.coordinator.path) as conn:
|
|
rows = conn.execute('SELECT id,status FROM episodes').fetchall()
|
|
self.assertEqual(rows, [(original_id, 'completed')])
|
|
self.assertFalse(self.sender.calls)
|
|
|
|
def test_actual_store_late_phone_sync_preserves_newer_question(self):
|
|
self.review()
|
|
newer = self.add(4, text='newer customer question')
|
|
self.bot.poll_once()
|
|
self.assertTrue(self.bot._pending[self.key]['awaiting_review'])
|
|
self.add(5, self_reply=True, seconds=3, text='late synced phone reply')
|
|
self.bot.poll_once()
|
|
self.assertEqual(len(self.sender.calls), 1)
|
|
self.assertEqual(self.sender.calls[0][0], newer['dedup_key'])
|
|
self.assertEqual(self.generate.call_args.args[0]['last_message']['rowid'], 4)
|
|
self.assertTrue(self.coordinator.resumed(CONV))
|
|
|
|
def test_local_approval_closes_pending_without_future_global_review_bypass(self):
|
|
self.review()
|
|
self.bot.approve_pending_replies([self.key])
|
|
self.bot.poll_once()
|
|
self.assertEqual(len(self.sender.calls), 1)
|
|
self.assertFalse(self.coordinator.has_pending(CONV))
|
|
self.assertFalse(self.coordinator.resumed(CONV))
|
|
self.add(1000)
|
|
self.bot.poll_once()
|
|
self.assertTrue(self.bot._pending[self.key]['awaiting_review'])
|
|
self.assertEqual(len(self.sender.calls), 1)
|
|
|
|
def test_real_store_bot_receipt_is_not_a_manual_approval(self):
|
|
state = self.review()
|
|
self.coordinator.record_automated_send(state, {'status': 'confirmed', 'serverId': '3'})
|
|
self.add(3, self_reply=True, text='synthetic draft')
|
|
self.bot.poll_once()
|
|
self.assertTrue(self.coordinator.has_pending(CONV))
|
|
self.assertFalse(self.coordinator.resumed(CONV))
|
|
self.assertIs(self.bot._pending[self.key], state)
|
|
self.assertFalse(self.sender.calls)
|
|
|
|
|
|
'''
|
|
source = source.replace("if __name__ == '__main__':\n", extra + "if __name__ == '__main__':\n")
|
|
target.write_text(source, encoding='utf-8', newline='\n')
|
|
print('Added five actual shared-coordinator protocol integration tests')
|