"""WP-0: Tests for the Gradio application — build_app constructs successfully.""" from __future__ import annotations import gradio as gr class TestBuildApp: def test_build_app_returns_blocks(self): from app.main import build_app demo = build_app() assert isinstance(demo, gr.Blocks) def test_build_app_has_expected_components(self): from app.main import build_app demo = build_app() # Blocks should be a non-empty layout tree assert demo is not None def test_build_app_idempotent(self): from app.main import build_app demo1 = build_app() demo2 = build_app() assert isinstance(demo1, gr.Blocks) assert isinstance(demo2, gr.Blocks) class TestProcessNegativeWP13: """WP-13 D5: process_negative runs green with affine/flat_guard wiring.""" def test_process_negative_on_fixture(self): import numpy as np from PIL import Image from app.main import process_negative data = np.load("synth/fixtures/case_000.npz") scan = data["scan"].astype(np.float32) upload = Image.fromarray((scan * 255.0).clip(0, 255).astype(np.uint8)) out = process_negative( upload, film_stock="Generic", physics_weight=1.0, perceptual_weight=0.5, num_candidates=3, include_deep_prior=False, full_res_export=False, scan_type="Positive", scan_calibration="auto-exposed", auto_trim=False, ) # 11-tuple return assert len(out) == 11 positive_pil, recombined_pil, img_a, img_b, status, best_state, ranked, gallery, conf, fa, fb = out assert img_a is not None and img_b is not None assert ranked is not None and len(ranked) >= 1 assert "Error" not in (status or "") # Affine ranking path should produce finite hybrid scores assert np.isfinite(ranked[0].score.total_loss) # Best must not be a flat solid pair when structured candidates exist from app.scoring import _is_flat_pair obs = np.asarray(positive_pil.convert("RGB"), dtype=np.float32) / 255.0 # Use preprocessed observed from best_state if available if best_state and best_state.get("observed_rgb") is not None: obs = best_state["observed_rgb"] a = ranked[0].separation.image_a b = ranked[0].separation.image_b # With guard on, if anything non-flat exists best should not be flat assert not _is_flat_pair(a, b, obs) or len(ranked) == 1 class TestAsymmetricWP14: """WP-14 D3: checkbox OFF byte-identical; ON offline asym_sub + note.""" def _upload(self): import numpy as np from PIL import Image data = np.load("synth/fixtures/case_000.npz") scan = data["scan"].astype(np.float32) return Image.fromarray((scan * 255.0).clip(0, 255).astype(np.uint8)) def test_checkbox_off_byte_identical_population(self): """Default path (asym OFF) matches explicit asymmetric_recovery=False.""" import os from app.main import process_negative upload = self._upload() kwargs = dict( film_stock="Generic", physics_weight=1.0, perceptual_weight=0.5, num_candidates=3, include_deep_prior=False, full_res_export=False, scan_type="Positive", scan_calibration="auto-exposed", auto_trim=False, ) # Ensure no accidental key influence old = os.environ.pop("REPLICATE_API_TOKEN", None) try: out_default = process_negative(upload, **kwargs) out_off = process_negative(upload, **kwargs, asymmetric_recovery=False) finally: if old is not None: os.environ["REPLICATE_API_TOKEN"] = old ranked_d = out_default[6] ranked_o = out_off[6] assert ranked_d is not None and ranked_o is not None ids_d = [r.candidate_id for r in ranked_d] ids_o = [r.candidate_id for r in ranked_o] assert ids_d == ids_o # No asym candidates when off assert "asym_sub" not in ids_d assert "asym_fill" not in ids_d # Scores byte-identical for a, b in zip(ranked_d, ranked_o): assert a.candidate_id == b.candidate_id assert abs(a.score.total_loss - b.score.total_loss) < 1e-12 def test_checkbox_on_offline_asym_sub_no_network(self): """ON without keys → asym_sub present; offline note; no network attempted.""" import os from unittest.mock import patch from app.main import process_negative, ASYM_CONSENT upload = self._upload() old_r = os.environ.pop("REPLICATE_API_TOKEN", None) old_a = os.environ.pop("ANTHROPIC_API_KEY", None) try: with patch("app.asymmetric._replicate_inpaint") as mock_inpaint: mock_inpaint.side_effect = AssertionError("network must not be called") out = process_negative( upload, film_stock="Generic", physics_weight=1.0, perceptual_weight=0.5, num_candidates=3, include_deep_prior=False, full_res_export=False, scan_type="Positive", scan_calibration="auto-exposed", auto_trim=False, asymmetric_recovery=True, asymmetric_anchor="Auto", ) mock_inpaint.assert_not_called() finally: if old_r is not None: os.environ["REPLICATE_API_TOKEN"] = old_r if old_a is not None: os.environ["ANTHROPIC_API_KEY"] = old_a status = out[4] or "" ranked = out[6] assert ranked is not None ids = [r.candidate_id for r in ranked] assert "asym_sub" in ids, f"asym_sub missing from {ids}" assert "asym_fill" not in ids assert "offline" in status.lower() or "no REPLICATE" in status or ASYM_CONSENT[:40] in status assert "Asymmetric recovery" in status