| """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") |
|
|
| |
| 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], "...") |
|
|
| |
| 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" |
|
|
| |
| class P: |
| 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") |
|
|
| |
| 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]) |
|
|
| |
| 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") |
|
|