Improve controller behavior and update project requirements

This commit is contained in:
2026-09-30 15:42:36 +08:00
parent 55374356ed
commit 071f06d365
52 changed files with 2205 additions and 335 deletions
@@ -44,6 +44,60 @@ def test_library_and_frame_readback(tmp_path):
mobile.dispatch('content.play', {**item, 'revision': 'old'})
def test_progressive_preview_and_revision_cleanup(tmp_path):
import base64
from io import BytesIO
from PIL import Image
app = create_app(project_root=tmp_path, driver_kind='mock')
mobile = app.state.mobile_control
assert mobile.identity()['protocol_minor'] == 1
assert 'library_progressive' in mobile.identity()['capabilities']
created = mobile.templates.create('预览清理', {
'version': 1, 'width': 64, 'height': 64,
'pixelRgb': base64.b64encode(bytes(64 * 64 * 3)).decode('ascii'), 'elements': [],
})
summary = mobile.dispatch('library.summary', {})
items = mobile.dispatch('library.list', {})['items']
assert summary == {'library_revision': mobile.dispatch('library.list', {})['library_revision'], 'item_count': len(items)}
item = next(item for item in items if item['id'] == created['id'])
small = base64.b64decode(mobile.dispatch('library.preview', item)['data_base64'])
large = base64.b64decode(mobile.dispatch('library.thumbnail', item)['data_base64'])
assert len(small) < len(large)
with Image.open(BytesIO(small)) as image:
assert image.format == 'PNG' and image.size == (16, 16)
with Image.open(BytesIO(large)) as image:
assert image.format == 'PNG' and image.size == (64, 64)
old_file = mobile.preview_cache.root / mobile.preview_cache.name(item)
assert old_file.read_bytes() == small
mobile.templates.rename(created['id'], '预览清理新版', created['revision'])
assert mobile.dispatch('library.summary', {})['library_revision'] != summary['library_revision']
assert not old_file.exists()
with pytest.raises(RpcError, match='CONFLICT'):
mobile.dispatch('library.preview', item)
empty = mobile.animations.create('空动图')
empty_item = next(row for row in mobile.dispatch('library.list', {})['items'] if row['id'] == empty['id'])
with pytest.raises(RpcError, match='CONFLICT'):
mobile.dispatch('library.preview', empty_item)
def test_user_animation_preview_reads_persisted_first_frame(tmp_path):
import base64
from io import BytesIO
from PIL import Image
mobile = create_app(project_root=tmp_path, driver_kind='mock').state.mobile_control
scene = {'version': 1, 'width': 64, 'height': 64,
'pixelRgb': base64.b64encode(bytes((12, 34, 56)) * (64 * 64)).decode('ascii'), 'elements': []}
created = mobile.animations.create_from_frames('用户动图', [{'scene': scene, 'duration_ms': 100}])
item = next(row for row in mobile.dispatch('library.list', {})['items'] if row['id'] == created['id'])
small = base64.b64decode(mobile.dispatch('library.preview', item)['data_base64'])
large = base64.b64decode(mobile.dispatch('library.thumbnail', item)['data_base64'])
with Image.open(BytesIO(small)) as image:
assert image.size == (16, 16) and image.getpixel((0, 0)) == (12, 34, 56)
with Image.open(BytesIO(large)) as image:
assert image.size == (64, 64) and image.getpixel((0, 0)) == (12, 34, 56)
def test_forbidden_operation_and_settings_fields(tmp_path):
mobile = create_app(project_root=tmp_path, driver_kind='mock').state.mobile_control
with pytest.raises(RpcError, match='UNSUPPORTED_CAPABILITY'):