* feat(db): add pair schema and stage restore migrations for #193 * feat(database): persist reusable ordered prompt pairs (#193) * feat(api): expose reusable pairs and atomic metadata enrichment (#193) * feat(metadata): resolve canonical prompt pairs with bounded provenance (#193) * feat(scan): enrich image pairs without guessing or duplicate links (#193) * fix(database): preserve pair tags during global maintenance (#193) * feat(tracking): enrich live image pairs and cancel scans safely (#193) * feat(nodes): encode paired prompts and count cached recipe runs (#193) * fix(database): validate restored pair ownership and provenance (#193) * feat(admin): report pair scan outcomes and cancellation (#193) * feat(ui): add paired prompt editor and explicit recipe library for #193 * fix(autocomplete): include reusable pair metadata vocabulary (#193) * fix(pairs): honor recent sorting and conservative scan outcomes (#193) * fix(ui): protect paired draft saves and clarify negative variants (#193) * fix(metadata): preserve bounded paired execution evidence (#193) * fix(tracking): follow actual pair output roles and prefer execution evidence (#193) * docs(pairs): document live validation and retain renderer screenshots (#193) * test(api): inject disposable databases before API startup (#194) * test(api): canonicalize portable temporary gallery roots (#194) * docs(ui): prototype branching negative variant history (#193) * docs(ui): show selected negative beneath positive in timeline concept (#193) * docs: retain approved timeline for later gallery redesign (#193) * test(db): close restore fixture connections before cleanup (#194) * test(db): reuse existing handle guards in all pair fixtures (#194) * chore: clarify rollback test and intentional exception handling (#193)
143 lines
5.6 KiB
Python
143 lines
5.6 KiB
Python
"""
|
|
Tests for _find_comfyui_output_dir() — verifies that user-configured
|
|
directories take priority over auto-detection, and that the cache is
|
|
invalidated when the setting changes.
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
from pathlib import Path
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
|
|
# Mock ComfyUI's server module before importing anything that touches config
|
|
_mock_server = MagicMock()
|
|
_mock_server.PromptServer.instance.routes = MagicMock()
|
|
sys.modules.setdefault("server", _mock_server)
|
|
|
|
from py.api import PromptManagerAPI
|
|
from py.config import GalleryConfig
|
|
|
|
|
|
class TestFindComfyuiOutputDir(unittest.TestCase):
|
|
"""Test _find_comfyui_output_dir() respects user-configured directories."""
|
|
|
|
def setUp(self):
|
|
roots = patch.object(
|
|
GalleryConfig,
|
|
"allowed_gallery_parents",
|
|
return_value=[os.path.normcase(os.path.realpath(tempfile.gettempdir()))],
|
|
)
|
|
roots.start()
|
|
self.addCleanup(roots.stop)
|
|
database = MagicMock()
|
|
database.cleanup_duplicates.return_value = 0
|
|
self.api = PromptManagerAPI(db=database)
|
|
self.api._cached_output_dir = None
|
|
self._orig_dirs = list(GalleryConfig.MONITORING_DIRECTORIES)
|
|
|
|
def tearDown(self):
|
|
GalleryConfig.MONITORING_DIRECTORIES = self._orig_dirs
|
|
|
|
def test_configured_directory_takes_priority(self):
|
|
"""When a valid directory is configured, it should be used instead of
|
|
auto-detect."""
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
GalleryConfig.MONITORING_DIRECTORIES = [tmpdir]
|
|
self.api._cached_output_dir = None
|
|
|
|
result = self.api._find_comfyui_output_dir()
|
|
|
|
self.assertEqual(result, str(Path(tmpdir).resolve()))
|
|
|
|
def test_nonexistent_configured_directory_falls_through(self):
|
|
"""When the configured directory doesn't exist, fall back to auto-detection."""
|
|
GalleryConfig.MONITORING_DIRECTORIES = ["/nonexistent/fake/path/output"]
|
|
self.api._cached_output_dir = None
|
|
|
|
# Should not return the nonexistent path — will either find
|
|
# ComfyUI output via auto-detect or return None
|
|
result = self.api._find_comfyui_output_dir()
|
|
|
|
self.assertNotEqual(result, "/nonexistent/fake/path/output")
|
|
|
|
def test_empty_config_uses_auto_detection(self):
|
|
"""When MONITORING_DIRECTORIES is empty, auto-detection should be used."""
|
|
GalleryConfig.MONITORING_DIRECTORIES = []
|
|
self.api._cached_output_dir = None
|
|
|
|
# Should not raise — auto-detection may or may not find a dir
|
|
result = self.api._find_comfyui_output_dir()
|
|
# Result is either a valid path or None
|
|
if result is not None:
|
|
self.assertTrue(Path(result).is_dir())
|
|
|
|
def test_cache_returns_same_value(self):
|
|
"""After first lookup, cached value should be returned."""
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
GalleryConfig.MONITORING_DIRECTORIES = [tmpdir]
|
|
self.api._cached_output_dir = None
|
|
|
|
result1 = self.api._find_comfyui_output_dir()
|
|
# Change config — should NOT affect result because of cache
|
|
GalleryConfig.MONITORING_DIRECTORIES = ["/some/other/path"]
|
|
result2 = self.api._find_comfyui_output_dir()
|
|
|
|
self.assertEqual(result1, result2)
|
|
|
|
def test_cache_invalidation_picks_up_new_directory(self):
|
|
"""After clearing the cache, _find_comfyui_output_dir should use new config."""
|
|
with tempfile.TemporaryDirectory() as tmpdir1:
|
|
with tempfile.TemporaryDirectory() as tmpdir2:
|
|
GalleryConfig.MONITORING_DIRECTORIES = [tmpdir1]
|
|
self.api._cached_output_dir = None
|
|
|
|
result1 = self.api._find_comfyui_output_dir()
|
|
self.assertEqual(result1, str(Path(tmpdir1).resolve()))
|
|
|
|
# Simulate what save_settings does: update config + invalidate cache
|
|
GalleryConfig.MONITORING_DIRECTORIES = [tmpdir2]
|
|
self.api._cached_output_dir = None
|
|
|
|
result2 = self.api._find_comfyui_output_dir()
|
|
self.assertEqual(result2, str(Path(tmpdir2).resolve()))
|
|
|
|
def test_configured_directory_is_resolved(self):
|
|
"""Configured paths with symlinks or .. should be resolved to absolute."""
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
# Use a relative-ish path with ..
|
|
parent = str(Path(tmpdir).parent)
|
|
basename = Path(tmpdir).name
|
|
dotdot_path = os.path.join(parent, ".", basename)
|
|
|
|
GalleryConfig.MONITORING_DIRECTORIES = [dotdot_path]
|
|
self.api._cached_output_dir = None
|
|
|
|
result = self.api._find_comfyui_output_dir()
|
|
|
|
self.assertEqual(result, str(Path(tmpdir).resolve()))
|
|
|
|
|
|
class TestOutputDirectoryFixturePortability(unittest.TestCase):
|
|
def test_aliased_temporary_parent_uses_the_real_gallery_allowlist(self):
|
|
with tempfile.TemporaryDirectory() as directory:
|
|
parent = Path(directory)
|
|
(parent / "alias").mkdir()
|
|
(parent / "actual").mkdir()
|
|
aliased = str(parent / "alias" / ".." / "actual")
|
|
with patch("tempfile.gettempdir", return_value=aliased):
|
|
case = TestFindComfyuiOutputDir(
|
|
"test_configured_directory_takes_priority"
|
|
)
|
|
result = unittest.TestResult()
|
|
case.run(result)
|
|
self.assertEqual(result.errors, [])
|
|
self.assertEqual(result.failures, [])
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|