Files
DemonGatanjieu-Anomalous_Mo…/tests/test_activity_log.py
T
DemonGatanjieuandClaude Opus 5.5 7b09909e5b test: publish the plugin's tests, one command to run them
tests/ (Python backend tests, .mjs frontend rule tests) is no longer
ignored. tools/run_tests.mjs runs the structure check, every
tests/*.mjs and the Python tests with $PYTHON, the portable
python_embeded or python. docs/guides/testing.md says how to run and
write them; README links it. Developer notes no longer point at 18 test
files that were removed earlier (AGENTS.md table, backend, frontend and
update-guide notes).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 17:47:35 +08:00

92 lines
4.2 KiB
Python

import asyncio
import json
from pathlib import Path
import sys
import tempfile
import unittest
from unittest import mock
from aiohttp import web
from aiohttp.test_utils import make_mocked_request
PLUGIN = Path(__file__).resolve().parents[1]
sys.path[:0] = [str(PLUGIN), str(PLUGIN.parents[1])]
from api import activity_log # noqa: E402
class ActivityLogTests(unittest.TestCase):
def setUp(self):
self.directory = tempfile.TemporaryDirectory()
self.path = str(Path(self.directory.name, 'activity_log.json'))
self.patch = mock.patch.object(activity_log, 'log_path', return_value=self.path)
self.patch.start()
activity_log._entries = None
def tearDown(self):
self.patch.stop()
activity_log._entries = None
self.directory.cleanup()
def test_entries_are_newest_first_bounded_and_saved(self):
with mock.patch.object(activity_log, 'MAX_ENTRIES', 3):
for index in range(5):
activity_log.add_entry('file', 'recipe_save', f'r{index}')
self.assertEqual([e['target'] for e in activity_log.list_entries()], ['r4', 'r3', 'r2'])
activity_log._entries = None # read back from disk
self.assertEqual([e['target'] for e in activity_log.list_entries()], ['r4', 'r3', 'r2'])
self.assertEqual([e['target'] for e in activity_log.list_entries(source='canvas')], [])
activity_log.clear()
self.assertEqual(json.loads(Path(self.path).read_text()), [])
def test_log_stays_under_its_byte_limit(self):
with mock.patch.object(activity_log, 'MAX_LOG_BYTES', 20_000):
for index in range(200):
activity_log.add_entry('file', 'note_save', f'{index}-' + 'x' * 300)
self.assertLessEqual(Path(self.path).stat().st_size, 20_000)
self.assertTrue(activity_log.list_entries()[0]['target'].startswith('199-'))
def test_long_values_are_shortened(self):
entry = activity_log.add_entry('file', 'note_save', 'x' * 1000)
self.assertLessEqual(len(entry['target']), activity_log.MAX_VALUE_CHARS + 1)
def test_failure_responses_are_not_recorded(self):
ok = web.json_response({'success': True})
self.assertFalse(activity_log._failed(ok))
for bad in (web.json_response({'status': 'error'}), web.json_response({'success': False}),
web.json_response({'ok': True}, status=400)):
self.assertTrue(activity_log._failed(bad))
def test_middleware_records_successful_writes_only(self):
async def run(path, status, payload):
request = make_mocked_request('POST', path, headers={'Content-Type': 'application/json'})
request._read_bytes = json.dumps(payload).encode()
async def handler(_request):
await _request.json()
return web.json_response({'success': status < 300}, status=status)
await activity_log.activity_middleware(request, handler)
asyncio.run(run('/anomalous/delete_recipe', 200, {'filename': 'folder/My Recipe.json'}))
asyncio.run(run('/anomalous/delete_recipe', 500, {'filename': 'broken.json'}))
asyncio.run(run('/anomalous/folders', 200, {'name': 'not a write route'}))
entries = activity_log.list_entries()
self.assertEqual([(e['action'], e['target']) for e in entries], [('recipe_delete', 'My Recipe.json')])
def test_canvas_post_keeps_known_change_kinds(self):
async def post(body):
request = make_mocked_request('POST', '/anomalous/activity', headers={'Content-Type': 'application/json'})
request._read_bytes = json.dumps(body).encode()
return await activity_log.api_post_activity(request)
bad = asyncio.run(post({'changes': [{'kind': 'script', 'node': 'x'}]}))
self.assertEqual(bad.status, 400)
good = asyncio.run(post({'changes': [{'kind': 'changed', 'node': 'KSampler #3', 'widget': 'steps',
'before': '20', 'after': '30'}], 'total': 4, 'workflow': 'a.json'}))
self.assertEqual(good.status, 200)
entry = activity_log.list_entries()[0]
self.assertEqual((entry['source'], entry['target'], entry['detail']['total']), ('canvas', 'a.json', 4))
if __name__ == '__main__':
unittest.main()