Files
VoiceStudio/tests/test_longform_limits.py
Palash DebnathandClaude Opus 4.8 18e4c2347a fix(longform): correctness + robustness fixes from adversarial review (#418)
* fix(longform): correctness + robustness fixes from adversarial review

Fixes the confirmed findings from a multi-agent review of the convergence:

HIGH (correctness/output):
- MP3 + cover produced a corrupt file (-map 2:v -c:v copy is invalid for mp3).
  Cover art is now embedded for M4B only; mp3 skips it (m4b is the cover format).
- Chapter cache key omitted ref_text — editing only a profile's ref_text served
  stale audio. ref_text is now part of the voice signature.
- Preview wrote audiobook_cache/ but the render reads longform_cache/ (rename
  missed in PR 5) → cache-warming silently broke. Unified to longform_cache/.

Robustness (DoS/OOM guards):
- /audiobook/import caps upload at 64 MB; epub_to_chapter_script bounds per-entry
  (25 MB) and cumulative (300 MB) uncompressed reads (zip-bomb guard).
- /longform/render rejects > 10,000 chapters (422).

Frontend leaks:
- StoriesEditor.removeTrack revokes the line's preview blob URL.
- AudiobookTab revokes the cover blob URL on replace/unmount.

Deferred fast-follows (also from review): render-cache disk eviction; restoring
the standalone chapter cue-sheet export (needs chapter times in the done event).

Tests: mp3-drops-cover, epub entry/total caps, import + chapter-count limits;
updated the cache-hit test for the 4-field voice sig. 70 backend + 334 frontend green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test(longform): pass EPUB caps as params, not monkeypatch (CI import-path fix)

The cap tests monkeypatched module constants, but in the full-suite CI context
the module loads under a different import path so the patch missed the function
(it used the real 300 MB cap → tests failed). epub_to_chapter_script now takes
max_entry_bytes/max_total_bytes kwargs (default to the constants); tests pass
small values directly — deterministic regardless of import path.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 16:31:13 +05:30

41 lines
1.2 KiB
Python

"""Input bounds on the longform endpoints (review findings #4/#5).
Direct handler calls (no main/torch import). Caps are monkeypatched small so the
tests stay cheap.
"""
from __future__ import annotations
import asyncio
import io
import pytest
from fastapi import HTTPException, UploadFile
import api.routers.audiobook as ab
from api.routers.audiobook import (
LongformChapter,
LongformRenderRequest,
LongformSpan,
audiobook_import,
longform_render,
)
def test_import_rejects_oversize(monkeypatch):
monkeypatch.setattr(ab, "_IMPORT_MAX_BYTES", 10)
up = UploadFile(io.BytesIO(b"x" * 50), filename="big.txt")
with pytest.raises(HTTPException) as ei:
asyncio.run(audiobook_import(up))
assert ei.value.status_code == 400
def test_longform_render_rejects_too_many_chapters(monkeypatch):
monkeypatch.setattr(ab, "_MAX_CHAPTERS", 1)
req = LongformRenderRequest(chapters=[
LongformChapter(title="A", spans=[LongformSpan(text="hi")]),
LongformChapter(title="B", spans=[LongformSpan(text="yo")]),
])
with pytest.raises(HTTPException) as ei:
asyncio.run(longform_render(req))
assert ei.value.status_code == 422