li / test /test_inline_task_delivery.py
xiaolongmr
fix: serve inline task images separately from lightweight polling JSON
ee2d10d
Raw History Blame Contribute Delete
4.87 kB
import asyncio
import base64
import json
from pathlib import Path
from tempfile import TemporaryDirectory
import unittest
from unittest import mock
from fastapi import FastAPI
from fastapi.testclient import TestClient
import api.image_tasks as image_tasks_module
from services.image_task_service import ImageTaskService, _collect_image_urls, _inline_image
class InlineTaskDeliveryTests(unittest.TestCase):
def make_service(self, root):
service = ImageTaskService(root / 'tasks.json', retention_days_getter=lambda: 0)
service._tasks['owner:result'] = {
'id': 'result', 'owner_id': 'owner', 'status': 'running',
'base_url': 'https://site.test',
}
return service
def finish(self, service, payload=b'\x89PNG\r\n\x1a\n' + b'x' * 200000):
encoded = base64.b64encode(payload).decode('ascii')
with mock.patch('services.image_task_service.proxied_image_url', side_effect=lambda url: url):
service._update_task('owner:result', status='success', data=[
{'url': 'data:image/png;base64,' + encoded, 'revised_prompt': 'test'},
])
public = service.list_tasks({'id': 'owner'}, ['result'])['items'][0]
return payload, public, service._tasks['owner:result']['inline_result_token']
def test_poll_is_small_and_complete_image_survives_database_reload(self):
with TemporaryDirectory() as directory:
service = self.make_service(Path(directory))
payload, public, token = self.finish(service)
self.assertLess(len(json.dumps(public)), 1500)
self.assertNotIn('b64_json', public['data'][0])
self.assertEqual(public['data'][0]['url'], f'https://site.test/image-task-results/{token}/0')
self.assertEqual(service.get_inline_result(token, 0), (payload, 'image/png'))
restored = ImageTaskService(service.path, retention_days_getter=lambda: 0)
self.assertEqual(restored.get_inline_result(token, 0), (payload, 'image/png'))
def test_foreign_users_cannot_discover_capability_and_invalid_requests_fail(self):
with TemporaryDirectory() as directory:
service = self.make_service(Path(directory))
_, _, token = self.finish(service)
self.assertEqual(service.list_tasks({'id': 'foreign'}, ['result'])['items'], [])
self.assertIsNone(service.get_inline_result('x' * 43, 0))
self.assertIsNone(service.get_inline_result(token, -1))
self.assertIsNone(service.get_inline_result(token, 2))
def test_capability_rotates_when_image_is_replaced(self):
with TemporaryDirectory() as directory:
service = self.make_service(Path(directory))
_, _, old = self.finish(service, b'old')
payload, _, new = self.finish(service, b'new')
self.assertNotEqual(old, new)
self.assertIsNone(service.get_inline_result(old, 0))
self.assertEqual(service.get_inline_result(new, 0)[0], payload)
def test_image_endpoint_does_not_use_network_mount_or_event_loop(self):
with TemporaryDirectory() as directory:
service = self.make_service(Path(directory))
payload, _, token = self.finish(service)
original = service.get_inline_result
def off_loop(*args):
with self.assertRaises(RuntimeError):
asyncio.get_running_loop()
return original(*args)
with mock.patch.object(image_tasks_module, 'image_task_service', service), \
mock.patch.object(service, 'get_inline_result', side_effect=off_loop):
app = FastAPI()
app.include_router(image_tasks_module.create_router())
with TestClient(app) as client:
response = client.get(f'/image-task-results/{token}/0')
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, payload)
self.assertEqual(response.headers['content-type'], 'image/png')
self.assertEqual(response.headers['x-content-type-options'], 'nosniff')
missing = client.get('/image-task-results/' + 'x' * 43 + '/0')
self.assertEqual(missing.status_code, 404)
self.assertEqual(missing.headers['cache-control'], 'no-store')
def test_inline_image_is_not_copied_to_usage_log_urls(self):
self.assertEqual(_collect_image_urls([{'url': 'data:image/png;base64,eA=='}]), [])
self.assertEqual(_collect_image_urls([{'url': 'https://site/image.png'}]), ['https://site/image.png'])
def test_active_svg_content_is_not_served_from_public_image_endpoint(self):
self.assertIsNone(_inline_image({'url': 'data:image/svg+xml;base64,eA=='}))