ai-video-generation / test_app_e2e.py
GitHub Actions
Deploy from 7a84bfa
7362ed8
Raw
History Blame Contribute Delete
2.53 kB
"""End-to-end test through the real app handlers against the REAL Modal backend.
Requires the backend to be deployed:
modal deploy modal_backend/shotcraft_inference.py
Optionally point elsewhere:
SHOTCRAFT_API_URL=https://... python test_app_e2e.py
This spends real GPU time (1 MiniCPM call + 6 FLUX frames) β€” run it for
Slice 4/5 validation, not in a tight loop.
"""
import json
import zipfile
from PIL import Image
import app
from model_runtime import health
h = health()
assert h.get("status") == "ok", f"Backend not reachable: {h}"
print("backend OK β€”", h)
photo = Image.open("examples/demo_product.png")
# Stage 1 handler β€” real MiniCPM
out = app.run_stage1(photo, "Handmade ceramic mugs from Krakow", "Home", "Minimal")
pkg, analysis = out[0], out[1]
fields = list(out[2:-1])
assert len(pkg.shots) == 5 and len(fields) == 40
print("run_stage1 OK β€”", analysis[:60], "...")
# Edit prompt 3 (AC-2) + non-prompt field edit (FR-1.3)
fields[2 * 8 + 7] = ("EDITED: lifestyle close-up of the speckled ceramic mug "
"on a linen tablecloth, morning light")
fields[1 * 8 + 0] = "Renamed Concept"
# Stage 2 handler β€” real FLUX, 5 frames in one backend call
class P: # stub gr.Progress
def __call__(self, *a, **k): pass
gallery, pkg2, regen_state = app.run_stage2(pkg, "Minimal", "1:1", *fields, progress=P())
assert len(gallery) == 5
assert all(img.size == (1024, 1024) for img, _ in gallery)
assert pkg2.shots[1].concept_name == "Renamed Concept"
assert pkg2.shots[2].image_prompt.startswith("EDITED:")
print("run_stage2 OK β€” 5 real frames, edits persisted")
# Regen one (AC-4) β€” only shot 2 changes
import io
before = io.BytesIO(); gallery[1][0].save(before, "PNG")
gallery2, regen_state2 = app.regen_one(pkg2, "Minimal", "1:1", 2, gallery, regen_state)
after = io.BytesIO(); gallery2[1][0].save(after, "PNG")
assert regen_state2[2] == 1
assert before.getvalue() != after.getvalue(), "regen frame should differ"
print("regen_one OK β€” shot 2 rerolled, counter:", regen_state2[2])
# Export (AC-5/AC-6)
path = app.export_zip(pkg2, gallery2, [1, 4], regen_state2)
zf = zipfile.ZipFile(path)
names = sorted(zf.namelist())
m = json.loads(zf.read("selection_manifest.json"))
assert "selection_manifest.json" in names
assert len([n for n in names if n.endswith(".png")]) == 5
assert m["hero_frames"] == [1, 4]
assert m["shots"][2]["image_prompt"].startswith("EDITED:")
assert m["shots"][1]["regen_count"] == 1
print("export_zip OK β€”", names)
print()
print("REAL E2E PASSED")