Files
gokayfem-ComfyUI-fal-API/tests/test_errors.py
T
Gökay Aydoğan b75f731abf feat: v2.0.0 — full fal catalog coverage, dynamic nodes, core rewrite (#75)
Auto-generated nodes for the entire live fal catalog (1,391 models →
1,482 total nodes) built at startup from a committed registry, plus a
full rewrite of the core layer. All 87 v1 node keys, inputs and outputs
are preserved — existing workflows load and run unchanged.

New:
- data/fal_registry.json + scripts/build_registry.py: distills fal's
  catalog and per-endpoint OpenAPI schemas into a deterministic registry
- nodes/dynamic/: schema→node factory (typed widgets, tooltips, pricing
  in node help, IMAGE/VIDEO/AUDIO auto-upload, native VIDEO/AUDIO
  outputs, seed -1=random, force_rerun), plus a generic Fal Any Endpoint
  node for arbitrary endpoints
- nodes/utils/: rewritten core — FalApiError surfacing fal's real error
  payloads, queue progress logs, ComfyUI cancel support, retries and
  timeouts everywhere, parallel uploads/downloads, structured logging
- tests/ (45 tests incl. a legacy-key snapshot lock), CI lint+test
  workflow, weekly registry-refresh workflow that opens a PR on changes
- README regenerated from the registry (scripts/build_readme.py)

Fixed:
- GPTImage2 / GPTImage2Edit were defined but never registered
- Topaz video upscale sent a nonexistent API field (desired_increase)
- SeedVR video upscaler had its error handling commented out + wrong
  model label; ProRes enum value mismatch translated at argument level
- 10x `is ""` comparisons, bare excepts, missing HTTP timeouts,
  temp-file leaks, fps=0 crash in LoadVideoURL, duplicate enum entry
- opencv-python & friends missing from pyproject (Registry installs)
- API failures now raise visible errors instead of silently returning
  blank images or "Error:" strings that downstream nodes tried to load

BREAKING (behavioral only): failed API calls raise instead of returning
blank/black images or error strings. Node signatures are unchanged.
2026-07-02 12:46:22 +03:00

80 lines
2.4 KiB
Python

"""Unit tests for fal error extraction and FalApiError formatting."""
from __future__ import annotations
import pytest
class _FakeResponse:
def __init__(self, payload):
self._payload = payload
def json(self):
if isinstance(self._payload, Exception):
raise self._payload
return self._payload
class _FakeHTTPError(Exception):
"""Duck-typed stand-in for fal_client.FalClientHTTPError."""
def __init__(self, message, status_code, payload):
super().__init__(message)
self.status_code = status_code
self.response = _FakeResponse(payload)
def test_error_message_includes_model_and_status(errors_mod):
err = errors_mod.FalApiError("fal-ai/flux/dev", "boom", 422)
assert "fal-ai/flux/dev" in str(err)
assert "boom" in str(err)
assert "422" in str(err)
def test_extract_string_detail(errors_mod):
exc = _FakeHTTPError("HTTP 403", 403, {"detail": "Content policy violation"})
message, status = errors_mod.extract_error_message(exc)
assert message == "Content policy violation"
assert status == 403
def test_extract_validation_list(errors_mod):
exc = _FakeHTTPError(
"HTTP 422", 422,
{"detail": [
{"loc": ["body", "prompt"], "msg": "field required"},
{"loc": ["body", "seed"], "msg": "not an int"},
]},
)
message, status = errors_mod.extract_error_message(exc)
assert "prompt: field required" in message
assert "seed: not an int" in message
assert status == 422
def test_extract_falls_back_to_str(errors_mod):
message, status = errors_mod.extract_error_message(RuntimeError("plain failure"))
assert message == "plain failure"
assert status is None
def test_extract_survives_bad_response_json(errors_mod):
exc = _FakeHTTPError("HTTP 500", 500, ValueError("not json"))
message, status = errors_mod.extract_error_message(exc)
assert message # falls back to str(exc)
assert status == 500
def test_raise_fal_error_chains(errors_mod):
original = RuntimeError("root cause")
with pytest.raises(errors_mod.FalApiError) as excinfo:
errors_mod.raise_fal_error("some-model", original)
assert excinfo.value.__cause__ is original
def test_raise_fal_error_passthrough(errors_mod):
already = errors_mod.FalApiError("m", "msg")
with pytest.raises(errors_mod.FalApiError) as excinfo:
errors_mod.raise_fal_error("other", already)
assert excinfo.value is already