Files
wildminder-ComfyUI-KEEP/tests/test_baseline_audit.py
T
WildAi 4803f74e43 chore(tools): remove the machine-local paths from the tracked files
The tools named a local drive, a local interpreter and local ComfyUI trees.
They now take the tree from the command line or from the environment only -
a machine-local default would make a scan run against the wrong tree - and
the usage examples are interpreter agnostic. The exported example workflow
no longer carries the absolute path of the machine it was exported on.
2026-10-24 22:44:06 +03:00

289 lines
12 KiB
Python

"""P0.1 - baseline audit tests.
These tests assert the *recorded baseline* against the **current code**, not
against the audit document: the document is only checked for the two required
signatures, as the plan requires.
Recorded baseline (.dev/docs/audit/2026-10-22-baseline-import.md):
* the package **imports** through ComfyUI's own loader (spec_from_file_location
+ alias, no server) - the swallow-and-stub blocks of B9 hide the real errors;
* the **model build** fails at the eager basicsr arch scan with
``ImportError: cannot import name 'T' from 're'`` (Python 3.13 removed
``re.T``), which the ``DummyArchRegistry`` fallback in ``modules/utils.py``
converts into ``ARCH_REGISTRY.get('KEEP') is None`` and therefore into
``RuntimeError: KEEP architecture class not found in ARCH_REGISTRY.``.
**P2.1 moved the baseline one step forward** (the plan's "documented
next-failure progression"): ``from re import T`` is gone, so the eager arch
scan failed at the *diffusers* import (B2) instead.
**P2.2 moved it one more step forward**: the two diffusers symbols are
vendored, so the vendored tree imports cleanly, the *real* ``ARCH_REGISTRY``
is used and the KEEP architecture is built. The model build now fails in the
weight-loading step (the probe uses a stub weight file that is not a real
checkpoint). The historical signatures stay recorded in the audit document;
the live assertions below follow the current tree.
"""
from __future__ import annotations
import importlib.util
import json
import os
import subprocess
import sys
from pathlib import Path
import pytest
REPO_ROOT = Path(__file__).resolve().parent.parent
PROBE = REPO_ROOT / "tools" / "baseline_probe.py"
AUDIT_DOC = REPO_ROOT / ".dev" / "docs" / "audit" / "2026-10-22-baseline-import.md"
DEPS_DIR = REPO_ROOT / "modules" / "deps"
#: The audit artifacts are development documentation: they are not shipped with
#: the pack and may be absent from a working tree.
pytestmark = pytest.mark.skipif(
not AUDIT_DOC.exists(),
reason=f"the audit artifact {AUDIT_DOC.name} is not in the working tree",
)
def _run_probe(tmp_path: Path, *extra: str) -> dict:
"""Run the baseline probe in a fresh interpreter and return its report.
A subprocess is used on purpose: the probe reproduces the *real* import
behaviour of the package (including the ``sys.path`` mutation of B4 and the
partially imported ``basicsr`` package), which must not leak into the
pytest session.
The probe exits with ``2`` **only** when it could not find a ComfyUI tree
(``tools/baseline_probe.py:329``); that case is reported in the JSON report
under ``"fatal"`` and must lead to a *skip*, not to a failed assertion -
see :func:`_skip_if_fatal`.
"""
report_path = tmp_path / "baseline-report.json"
result = subprocess.run(
[sys.executable, str(PROBE), "--json", str(report_path), *extra],
cwd=str(REPO_ROOT),
capture_output=True,
text=True,
timeout=600,
)
assert report_path.is_file(), "baseline probe did not write its report"
report = json.loads(report_path.read_text(encoding="utf-8"))
if "fatal" in report:
# no ComfyUI tree: the probe exits 2 on purpose, the caller skips.
assert result.returncode == 2, (
f"baseline probe reported a fatal error but exited "
f"rc={result.returncode}\n"
f"stdout:\n{result.stdout}\nstderr:\n{result.stderr}"
)
return report
assert result.returncode == 0, (
f"baseline probe failed (rc={result.returncode})\n"
f"stdout:\n{result.stdout}\nstderr:\n{result.stderr}"
)
return report
def _skip_if_fatal(report: dict) -> None:
"""Skip the test when the probe had no ComfyUI tree to work with."""
if "fatal" in report:
pytest.skip(
"baseline probe could not locate a ComfyUI source tree: "
f"{report['fatal']} (set COMFYUI_ROOT to point at one)"
)
@pytest.fixture(scope="module")
def baseline_report(tmp_path_factory):
tmp_path = tmp_path_factory.mktemp("baseline")
report = _run_probe(tmp_path)
_skip_if_fatal(report)
return report
@pytest.mark.unit
def test_package_imports_through_comfyui_loader(baseline_report):
"""The package imports cleanly today - the failure is at model-build time."""
assert baseline_report["import"]["ok"] is True, baseline_report["import"]
assert baseline_report["import"]["error"] is None
# the three compatibility-guaranteed node ids are registered
# (P4.4: the pack registers as a V3 extension - the ids live in the
# schemas of the node classes)
assert baseline_report["v3_node_ids"] == [
"KEEP_FaceUpscaleImage",
"KEEP_ModelLoader",
"KEEP_ProcessImageSequence",
], baseline_report["v3_node_ids"]
assert baseline_report["v3_display_names"]["KEEP_ModelLoader"] == (
"Load KEEP Models"
), baseline_report["v3_display_names"]
@pytest.mark.unit
def test_utils_exposes_the_real_arch_registry(baseline_report):
"""(a) modules/utils.py exposes the **real** vendored ARCH_REGISTRY.
Since P2.2 the vendored tree imports completely, so the
``DummyArchRegistry`` fallback of ``modules/utils.py`` is not used any
more and the registry knows the ``KEEP`` architecture. Since P2.5 the
registry lives inside the node package (``..deps.basicsr``) and never
shadows an installed ``basicsr``.
"""
arch_registry = baseline_report["arch_registry"]
assert arch_registry["is_stub"] is False, arch_registry
assert arch_registry["class_name"] == "Registry", arch_registry
assert arch_registry["module"].endswith(
".modules.deps.basicsr.utils.registry"
), arch_registry
assert arch_registry["get_keep"] is True, arch_registry
# the fallback block is still in the source (P2.7 removes it) but it did
# not fire - the import of the vendored tree succeeded
assert "utils_arch_registry_dummy" not in baseline_report["stub_summary"]["fired"]
@pytest.mark.unit
def test_vendored_keep_arch_import_succeeds_after_p22():
"""(b) importing the vendored ``keep_arch`` succeeds (B2 closed).
P2.1 moved the failure to the ``diffusers`` import, P2.2 vendored the two
symbols into ``modules/deps/basicsr/archs/_diffusers_compat.py`` and P2.5
turned the vendored tree into a package of the node package - the arch is
imported relatively and needs neither the (unusable) diffusers package
nor a ``sys.path`` entry.
"""
import importlib
module = importlib.import_module(
"ComfyUI_KEEP.modules.deps.basicsr.archs.keep_arch"
)
assert module.__name__.endswith(".modules.deps.basicsr.archs.keep_arch")
# both symbols come from the vendored compat module, not from diffusers
assert module.FeedForward.__module__.endswith(
".modules.deps.basicsr.archs._diffusers_compat"
)
assert module.AdaLayerNorm.__module__.endswith(
".modules.deps.basicsr.archs._diffusers_compat"
)
# the vendored packages are not top-level packages
assert "basicsr" not in sys.modules
assert "facelib" not in sys.modules
@pytest.mark.unit
def test_eager_arch_scan_succeeds_after_p22(baseline_report):
"""The eager arch scan of the vendored ``basicsr`` is clean again.
P2.1 moved the failure to the diffusers import, P2.2 vendored those two
symbols - so the scan over ``basicsr/archs/*_arch.py`` succeeds.
"""
arch_scan = baseline_report["arch_scan"]
assert arch_scan["ok"] is True, arch_scan
assert arch_scan["error"] is None, arch_scan
@pytest.mark.unit
def test_model_build_fails_not_the_import(baseline_report):
"""Acceptance: the failure mode is *model-build*, not import.
The build is attempted with a stubbed weight file present, so a missing
weight cannot explain the failure. Since P2.2 the vendored arch imports
and the architecture is built - the build now fails later, while loading
the *stub* weight file (which is not a real checkpoint).
"""
model_build = baseline_report["model_build"]
assert model_build["ok"] is False, model_build
assert model_build["stub_weight_present"] is True, model_build
error = model_build["error"]
# the import worked - the failure is not an ImportError any more
assert error["type"] != "ImportError", model_build
# ... and it happens *after* the architecture was built, in the weight
# loading step of the loader
assert "keep_model_loader.py" in error["traceback"], model_build
assert "torch.load" in error["traceback"], model_build
@pytest.mark.unit
def test_diffusers_symbols_are_unusable_in_this_env(baseline_report):
"""B2: the vendored diffusers import cannot be satisfied in this env."""
diffusers = baseline_report["diffusers"]
assert diffusers["ok"] is False, diffusers
assert diffusers["error"]["type"] == "ImportError", diffusers
assert "cached_download" in diffusers["error"]["message"]
@pytest.mark.unit
def test_the_pip_shim_is_gone(tmp_path):
"""B10 is closed in P2.1: the vendored pip shim no longer exists.
``modules/deps/basicsr/utils/video_util.py`` (the module that ran
``pip.main([...])`` at import time) was deleted, so importing the package
must not call pip - not even when ``ffmpeg`` is unavailable.
"""
report = _run_probe(tmp_path, "--simulate-missing-ffmpeg")
_skip_if_fatal(report)
pip_shim = report["pip_shim"]
assert pip_shim["pip_main_calls"] == [], pip_shim
assert pip_shim["video_util_imported"] is False, pip_shim
assert pip_shim["ffmpeg_importable"] is False, pip_shim
assert not (REPO_ROOT / "modules" / "deps" / "basicsr" / "utils" / "video_util.py").exists()
@pytest.mark.unit
def test_vendored_deps_are_not_shadowed_globally(baseline_report):
"""B4 is closed in P2.5: the vendored tree no longer shadows anything.
``modules/deps`` is not on ``sys.path`` any more and neither
``basicsr`` nor ``facelib`` is a top-level module - the vendored tree is
imported relatively from the node package.
"""
shadowing = baseline_report["sys_path_shadowing"]
assert shadowing["deps_path_on_sys_path"] is False, shadowing
assert shadowing["basicsr_in_sys_modules"] is False, shadowing
assert shadowing["basicsr_utils_in_sys_modules"] is False, shadowing
assert shadowing["facelib_in_sys_modules"] is False, shadowing
@pytest.mark.unit
def test_fatal_probe_report_leads_to_a_skip_not_a_failure(monkeypatch):
"""On a host without a ComfyUI tree the probe exits 2 with a 'fatal' report.
The fixture must then **skip** with a reason (the documented behaviour);
before the fix the helper asserted ``rc == 0`` first, so the skip was
unreachable and the P0.1 tests failed hard on such a host.
"""
spec = importlib.util.spec_from_file_location("keep_baseline_probe", PROBE)
probe = importlib.util.module_from_spec(spec)
spec.loader.exec_module(probe)
# no ComfyUI tree at all: COMFYUI_ROOT points at a tree that does not exist
monkeypatch.setenv("COMFYUI_ROOT", "definitely-not-a-comfyui-tree")
report = probe.probe()
assert "fatal" in report, report
assert probe.main([]) == 2, "the probe must exit 2 when it has no ComfyUI tree"
# the fixture helper turns that report into a skip
with pytest.raises(pytest.skip.Exception) as excinfo:
_skip_if_fatal(report)
assert "ComfyUI source tree" in str(excinfo.value)
@pytest.mark.unit
def test_audit_doc_contains_both_signatures():
"""The audit document must record both signatures of the baseline."""
assert AUDIT_DOC.is_file(), f"missing audit document: {AUDIT_DOC}"
text = AUDIT_DOC.read_text(encoding="utf-8")
assert "cannot import name 'T' from 're'" in text, "missing B1 signature"
assert "DummyArchRegistry" in text, "missing B9/stub signature"
assert "KEEP architecture class not found in ARCH_REGISTRY." in text, (
"missing model-build failure signature"
)