"""Real GPU/API smoke test, run on CMP. Uses and revokes a disposable device.""" import hashlib import io import json import os from pathlib import Path import time import zipfile import httpx from device_auth import DeviceStore root = Path(os.environ.get('AUDIOBOOK_SERVICE_ROOT', '/home/sevenhill/apps/aletheia-audiobook')) base = 'https://argus.kusoft.xyz/aletheia-tts' with httpx.Client(timeout=60) as client: for attempt in range(30): try: health = client.get(base + '/health') if health.status_code == 200: break except httpx.TransportError: pass time.sleep(2) else: raise RuntimeError('Public receiver health endpoint is unavailable') store = DeviceStore(root / 'audiobook-jobs/jobs.db3') response = client.post(base + '/v1/devices/enroll', json={'code': store.create_code(), 'label': 'Deployment smoke test'}) response.raise_for_status() device = response.json() headers = {'Authorization': 'Bearer ' + device['token']} job = None try: text = 'Это проверка изготовления аудиокниги. Приёмник получил книгу от приложения и читает её вслух. Готовая запись возвращается в библиотеку.' book_format = os.environ.get('AUDIOBOOK_SMOKE_FORMAT', 'epub') if book_format == 'epub': archive = io.BytesIO() with zipfile.ZipFile(archive, 'w') as epub: epub.writestr('mimetype', 'application/epub+zip') epub.writestr('META-INF/container.xml', '') chapters = int(os.environ.get('AUDIOBOOK_SMOKE_CHAPTERS', '1')) manifest = ''.join(f'' for i in range(chapters)) spine = ''.join(f'' for i in range(chapters)) epub.writestr('OEBPS/content.opf', 'cmp-smokeПроверка CMPru' + manifest + '' + spine + '') for i in range(chapters): body = text if chapters == 1 else text + f' Раздел номер {i + 1}.' epub.writestr(f'OEBPS/chapter{i}.xhtml', 'Проверка

Проверка ' + str(i + 1) + '

' + body + '

') book = archive.getvalue() else: book = ('
<p>Проверка</p>

' + text + '

').encode() response = client.post(base + '/v1/audiobooks', headers=headers, data={'title': 'Проверка CMP', 'author': 'Aletheia'}, files={'book': ('test.' + book_format, book, 'application/epub+zip' if book_format == 'epub' else 'application/xml')}) response.raise_for_status() job = response.json()['id'] path = base + '/v1/audiobooks/' + job assert client.get(path).status_code == 401 deadline = time.monotonic() + 1200 previous = None while time.monotonic() < deadline: response = client.get(path, headers=headers) response.raise_for_status() status = response.json() state = (status['status'], status['stage'], status['processedCharacters'], status['totalCharacters']) if state != previous: print(json.dumps({'state': state}, ensure_ascii=False), flush=True) previous = state if status['status'] == 'ready': response = client.get(path + '/file', headers=headers) response.raise_for_status() assert b'ftyp' in response.content[:32] and status['durationMs'] > 0 output = root / 'smoke-audiobook.m4a' output.write_bytes(response.content) print(json.dumps({'result': 'PASS', 'bytes': len(response.content), 'durationMs': status['durationMs'], 'sha256': hashlib.sha256(response.content).hexdigest()}), flush=True) break if status['status'] in ('failed', 'cancelled'): raise RuntimeError(status) time.sleep(3) else: raise TimeoutError('Synthesis exceeded smoke test timeout') finally: if job: client.delete(base + '/v1/audiobooks/' + job, headers=headers) store.revoke(device['deviceId'])