Files
ComfyAssets-ComfyUI_PromptM…/tests/test_db_path_config.py
T
Vito 763e6b37b1 feat(history): record durable ComfyUI execution attempts (#212)
* feat(history): normalize bounded execution evidence (#209)

* fix(history): bound lifecycle kind checks before hashing (#209)

* feat(history): persist execution attempts with restore fencing (#209)

* test(api): bootstrap standalone backup restore fixtures (#209)

* feat(history): isolate bounded asynchronous ledger writes (#209)

* feat(history): observe actual queue attempts without altering execution (#209)

* fix(database): honor package-scoped configuration (#211)

* test(history): exercise real cached and headless executions (#209)

* fix(history): reject unsupported uniqueness and isolate package fixtures (#209)

* test(history): support Python 3.10 package fixture cleanup (#209)
2026-10-17 16:42:07 -07:00

154 lines
6.1 KiB
Python

"""
Tests for issue #119: Database path configuration.
Verifies that _resolve_db_path() correctly reads from config, resolves
relative paths against the extension root, and handles absolute paths.
"""
import os
import sys
import tempfile
import types
import unittest
from unittest.mock import patch
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import database.operations as operations
from database.operations import _resolve_db_path
EXTENSION_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
class TestResolveDbPath(unittest.TestCase):
"""Test _resolve_db_path() database path resolution."""
def test_package_qualified_config_precedes_unrelated_standalone_config(
self,
) -> None:
"""A custom-node package must use its own configured disposable database."""
with tempfile.TemporaryDirectory() as directory:
configured = os.path.join(directory, "owned", "prompts.db")
package_config = types.ModuleType("owned_promptmanager.py.config")
package_config.PromptManagerConfig = types.SimpleNamespace(
DEFAULT_DB_PATH=configured
)
standalone_config = types.ModuleType("py.config")
standalone_config.PromptManagerConfig = types.SimpleNamespace(
DEFAULT_DB_PATH=os.path.join(directory, "unrelated.db")
)
with (
patch.dict(
operations.__dict__,
{"__package__": "owned_promptmanager.database", "__spec__": None},
),
patch.dict(
sys.modules,
{
"owned_promptmanager.py.config": package_config,
"py.config": standalone_config,
},
),
):
resolved = _resolve_db_path(None)
self.assertEqual(resolved, configured)
self.assertTrue(os.path.isdir(os.path.dirname(configured)))
def test_explicit_absolute_path_used_as_is(self):
"""An explicit absolute path should be returned unchanged."""
with tempfile.TemporaryDirectory() as tmpdir:
path = os.path.join(tmpdir, "custom.db")
result = _resolve_db_path(path)
self.assertEqual(result, path)
def test_explicit_relative_path_resolved_to_extension_root(self):
"""An explicit relative path should resolve against the extension root."""
result = _resolve_db_path("data/my.db")
expected = os.path.join(EXTENSION_ROOT, "data", "my.db")
self.assertEqual(result, expected)
def test_none_falls_back_to_default_without_config(self):
"""Without config (outside ComfyUI), should default to prompts.db."""
# py.config requires ComfyUI's server module, so import fails naturally
result = _resolve_db_path(None)
expected = os.path.join(EXTENSION_ROOT, "prompts.db")
self.assertEqual(result, expected)
def test_none_reads_config_when_available(self):
"""None should read DEFAULT_DB_PATH from config when importable."""
with tempfile.TemporaryDirectory() as tmpdir:
custom_path = os.path.join(tmpdir, "prompts.db")
fake_config_mod = types.ModuleType("py.config")
fake_pm_config = type(
"PromptManagerConfig", (), {"DEFAULT_DB_PATH": custom_path}
)
fake_config_mod.PromptManagerConfig = fake_pm_config
with patch.dict(sys.modules, {"py.config": fake_config_mod}):
result = _resolve_db_path(None)
self.assertEqual(result, custom_path)
def test_config_relative_path_resolved_to_extension_root(self):
"""A relative path from config should resolve against extension root."""
fake_config_mod = types.ModuleType("py.config")
fake_pm_config = type(
"PromptManagerConfig", (), {"DEFAULT_DB_PATH": "custom_data/prompts.db"}
)
fake_config_mod.PromptManagerConfig = fake_pm_config
with patch.dict(sys.modules, {"py.config": fake_config_mod}):
result = _resolve_db_path(None)
expected = os.path.join(EXTENSION_ROOT, "custom_data", "prompts.db")
self.assertEqual(result, expected)
def test_result_is_always_absolute(self):
"""Result should always be an absolute path."""
result = _resolve_db_path("prompts.db")
self.assertTrue(os.path.isabs(result))
def test_parent_directory_created(self):
"""Parent directories should be created for custom paths."""
with tempfile.TemporaryDirectory() as tmpdir:
nested = os.path.join(tmpdir, "deep", "nested", "prompts.db")
result = _resolve_db_path(nested)
self.assertEqual(result, nested)
self.assertTrue(os.path.isdir(os.path.join(tmpdir, "deep", "nested")))
class TestPromptDatabaseUsesConfig(unittest.TestCase):
"""Test that PromptDatabase() picks up the resolved path."""
@patch("database.operations._resolve_db_path")
def test_constructor_calls_resolve_with_none(self, mock_resolve):
"""PromptDatabase() should call _resolve_db_path with None."""
with tempfile.NamedTemporaryFile(delete=False, suffix=".db") as f:
mock_resolve.return_value = f.name
try:
from database.operations import PromptDatabase
PromptDatabase()
mock_resolve.assert_called_once_with(None)
finally:
os.unlink(f.name)
@patch("database.operations._resolve_db_path")
def test_constructor_passes_explicit_path(self, mock_resolve):
"""PromptDatabase(path) should call _resolve_db_path with that path."""
with tempfile.NamedTemporaryFile(delete=False, suffix=".db") as f:
mock_resolve.return_value = f.name
try:
from database.operations import PromptDatabase
PromptDatabase("/explicit/path.db")
mock_resolve.assert_called_once_with("/explicit/path.db")
finally:
os.unlink(f.name)
if __name__ == "__main__":
unittest.main()