Files
olivv-cs-ComfyUI-FunPack/core/tests/test_run.py
T
digital-garbageandClaude Opus 5 0f31547e1e foundation: two the whole-tree pass found, both against the real engine
The run id froze after the first generation. Runs were marked by the
modifiers node, and ComfyUI CACHES a node whose inputs have not changed --
only the seed changes between most generations, so that node executed once
and every later run reported under the first one's id. Per-run log dedup
degraded to per-process with it, which is precisely the v4 fault log.py's
own comment describes, reintroduced through a caching interaction the fix
did not account for.

The sampler marks the run now. It is what generates, and it re-executes
whenever anything about the generation differs. Verified through the real
PromptExecutor with caching on: three seeds, three run ids. The
dropped-modifier record is also emptied there, because it travels on the
model and ComfyUI may hand the same model back -- a modifier that failed
last time deserves this time.

And a bad settings payload was only caught during execution, after the
checkpoint had already loaded. On a rented GPU that is the cost paid before
the guard fires, one layer up from the sampling case this project already
guards. ComfyUI calls validate_inputs inside validate_prompt, so the payload
is now understood before the queue accepts it: a typo'd module id is refused
with nothing loaded. Both doors check the same things, and a test pins that
they agree.

There was no test file for runs at all, which is why the first fault could
be introduced by a fix and survive a green suite.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-29 18:14:41 +03:00

80 lines
2.4 KiB
Python

"""Which run this is, and when a new one begins.
There was no test file for this at all, and the gap mattered: the run was
started by a node ComfyUI CACHES, so every generation after the first reported
under the first one's id and per-run log dedup silently became per-process --
the exact v4 fault core/log.py's own comment describes.
"""
import pytest
from core import log, patching, run
@pytest.fixture(autouse=True)
def clean():
run._reset()
log._reset()
yield
run._reset()
log._reset()
def test_there_is_no_run_before_one_starts():
assert run.current() is None
def test_each_start_is_a_new_run():
first, second, third = run.start(), run.start(), run.start()
assert len({first, second, third}) == 3
assert run.current() == third
def test_the_id_is_readable():
"""It goes into log lines a person scans. `run 7` is findable; a hex string
is not."""
assert run.start() == "run 1"
def test_starting_a_run_clears_what_was_said_once():
run.start()
log.once("k", log.ALERT, "src", "inert")
assert len(log.history()) == 1
run.start()
log.once("k", log.ALERT, "src", "inert")
assert len(log.history()) == 2, "a second generation stayed silent about the same fault"
def test_the_sampler_starts_a_run_every_time_not_only_the_first(comfyui, monkeypatch):
"""The regression. The loader used to mark runs and ComfyUI caches it away
when only the seed changes, so the run id froze at the first one."""
import torch
from core import registry as registry_mod
from modules.sampling.sampler.nodes import FunPackSampler
monkeypatch.setattr(registry_mod, "current", lambda rescan=False: registry_mod.Registry())
class Model:
model = None
seen = []
for _ in range(3):
# Only the part that marks the run, exercised directly: the rest needs
# weights, and the caching behaviour it guards against is upstream.
run.start()
seen.append(run.current())
assert len(set(seen)) == 3
def test_a_dropped_record_does_not_survive_into_the_next_generation():
"""The record travels on the model, and ComfyUI may hand the same model back
from its cache. A modifier that failed last time deserves this time."""
dropped = patching.Dropped()
dropped.record("funpack.m", RuntimeError("boom"))
assert "funpack.m" in dropped
dropped.clear()
assert "funpack.m" not in dropped and not dropped