Files
ComfyAssets-ComfyUI_PromptM…/tests/test_ingestion_resolver_profile.py
Vito Sansevero 1d730d8e97 fix(scan): link the traced prompt when the pair resolver cannot decide
The output scan skipped every file the pair resolver called ambiguous or
unresolved, so workflows where several samplers share one prompt (a base
pass, an upscale pass and a detailer) never reached the Library from a
scan. It now links the positive prompt the shared parser traces for
those files, the prompt the inspector and the legacy pages show. Files
the resolver decides are stored as before, and graphs without a
recognized sampler keep the legacy extractor (#252).

Read-only scans of a 38,602-file output folder into temporary databases:
- links: 1,500 before, 7,954 after; none of the 1,500 dropped or changed;
- of the added links that differ from the pre-redesign scan, 1,275
  replace a number, a LoRA tag, a fragment or a truncated prompt that
  scan stored.
2026-10-19 20:22:53 -07:00

93 lines
3.6 KiB
Python

"""Ingestion keeps the default resolver profile; only inspection routes moved (D1).
The live monitor and the admin scan write pairs and links to the user's
database, so their resolver semantics stay byte-for-byte today's. f03 (506
executable nodes) separates the profiles: the default profile's 500-node gate
leaves it unresolved, while the inspection profile resolves it.
"""
import logging
import unittest
from pathlib import Path
from unittest.mock import Mock, patch
from utils import prompt_pair_resolver as resolver
try:
from tests import inspection_parity as parity
except ImportError: # pragma: no cover - discover -s tests puts tests/ first
import inspection_parity as parity
GATED = "Resolution limit reached"
def _f03_metadata():
return {"prompt": parity.build("f03")["prompt"]}
class _Spy:
"""Wrap the real resolver, recording each call's profile and outcome."""
def __init__(self):
self.calls = []
def __call__(self, metadata, **kwargs):
result = resolver.resolve_prompt_pair(metadata, **kwargs)
self.calls.append((kwargs.get("profile", resolver.DEFAULT_PROFILE), result))
return result
def assert_default_and_gated(self, case):
case.assertTrue(self.calls, "the ingestion path resolved the metadata")
for profile, result in self.calls:
case.assertIs(profile, resolver.DEFAULT_PROFILE)
case.assertEqual((result["state"], result["reason"]), ("unresolved", GATED))
class TestIngestionKeepsDefaultProfile(unittest.TestCase):
def test_f03_separates_the_profiles(self):
metadata = _f03_metadata()
default = resolver.resolve_prompt_pair(metadata)
self.assertEqual((default["state"], default["reason"]), ("unresolved", GATED))
inspection = resolver.resolve_prompt_pair(
metadata, profile=resolver.INSPECTION_PROFILE
)
self.assertEqual(inspection["state"], "resolved")
def test_live_monitor_lookup_and_linking_use_the_default_profile(self):
from utils.image_monitor import ImageGenerationHandler
handler = ImageGenerationHandler.__new__(ImageGenerationHandler)
handler.db_manager = Mock()
handler.db_manager.get_prompt_by_hash.return_value = None
handler.logger = logging.getLogger("test.ingestion-profile")
spy, metadata = _Spy(), _f03_metadata()
with patch("utils.image_monitor.resolve_prompt_pair", spy):
handler._find_prompt_from_metadata(metadata)
handler.link_image_to_prompt("/out/f03.png", {"id": 1}, metadata)
self.assertEqual(len(spy.calls), 2)
spy.assert_default_and_gated(self)
handler.db_manager.link_image_to_prompt.assert_called_once()
def test_admin_scan_ingestion_uses_the_default_profile(self):
from py.api import admin
mixin = admin.AdminRoutesMixin.__new__(admin.AdminRoutesMixin)
mixin.db = Mock()
mixin.db.get_prompt_by_hash.return_value = None
mixin.db.save_prompt.return_value = 7
spy = _Spy()
with patch.object(admin, "resolve_prompt_pair", spy):
status = mixin._ingest_scanned_file(Path("/out/f03.png"), _f03_metadata())
spy.assert_default_and_gated(self)
# The resolver leaves f03 unresolved; the scan links the prompt the
# browser parser traces, as the inspector shows it.
self.assertEqual(status, "added")
self.assertEqual(
mixin.db.save_prompt.call_args.args[0], "a red fox in snow, detailed fur"
)
mixin.db.link_image_to_prompt.assert_called_once()
if __name__ == "__main__":
unittest.main()