test(api): inject disposable databases before API startup (#194)
This commit is contained in:
+3
-3
@@ -264,12 +264,12 @@ class PromptManagerAPI(
|
||||
db (PromptDatabase): Database connection and operations handler
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize the PromptManager API with database connection and cleanup."""
|
||||
def __init__(self, db: PromptDatabase | None = None) -> None:
|
||||
"""Initialize the API, using an injected database before any cleanup."""
|
||||
self.logger = get_logger("prompt_manager.api")
|
||||
self.logger.info("Initializing PromptManager API")
|
||||
|
||||
self.db = PromptDatabase()
|
||||
self.db = db if db is not None else PromptDatabase()
|
||||
self._cached_output_dir = None # Lazy-cached by _find_comfyui_output_dir()
|
||||
self._html_cache = {} # Cached HTML file contents keyed by path
|
||||
self._gallery_cache = {} # dict: path_str -> (files, timestamp)
|
||||
|
||||
+1
-2
@@ -31,8 +31,7 @@ class APITestCase(AioHTTPTestCase):
|
||||
app = web.Application()
|
||||
routes = web.RouteTableDef()
|
||||
|
||||
self.api = PromptManagerAPI()
|
||||
self.api.db = PromptDatabase(self._temp_db.name)
|
||||
self.api = PromptManagerAPI(db=PromptDatabase(self._temp_db.name))
|
||||
self.api.add_routes(routes)
|
||||
app.router.add_routes(routes)
|
||||
return app
|
||||
|
||||
@@ -70,8 +70,7 @@ class AdminAPITestCase(AioHTTPTestCase):
|
||||
db_path = os.path.join(self.tmpdir, "prompts.db")
|
||||
app = web.Application()
|
||||
routes = web.RouteTableDef()
|
||||
self.api = PromptManagerAPI()
|
||||
self.api.db = PromptDatabase(db_path)
|
||||
self.api = PromptManagerAPI(db=PromptDatabase(db_path))
|
||||
self.api.add_routes(routes)
|
||||
app.router.add_routes(routes)
|
||||
return app
|
||||
|
||||
@@ -119,8 +119,7 @@ class AutotagAPITestCase(AioHTTPTestCase):
|
||||
|
||||
app = web.Application()
|
||||
routes = web.RouteTableDef()
|
||||
self.api = PromptManagerAPI()
|
||||
self.api.db = PromptDatabase(self._temp_db.name)
|
||||
self.api = PromptManagerAPI(db=PromptDatabase(self._temp_db.name))
|
||||
self.api._get_all_output_dirs = lambda: [self.output_dir]
|
||||
self.api.add_routes(routes)
|
||||
app.router.add_routes(routes)
|
||||
|
||||
@@ -45,8 +45,7 @@ class BackupRestoreTestCase(AioHTTPTestCase):
|
||||
|
||||
app = web.Application()
|
||||
routes = web.RouteTableDef()
|
||||
self.api = PromptManagerAPI()
|
||||
self.api.db = PromptDatabase(self.db_path)
|
||||
self.api = PromptManagerAPI(db=PromptDatabase(self.db_path))
|
||||
self.api.add_routes(routes)
|
||||
app.router.add_routes(routes)
|
||||
return app
|
||||
|
||||
+12
-6
@@ -322,7 +322,9 @@ class TestLongJobLock(unittest.TestCase):
|
||||
"""_acquire_long_job: one job per family at a time, released on exit."""
|
||||
|
||||
def setUp(self):
|
||||
self.api = PromptManagerAPI()
|
||||
database = MagicMock()
|
||||
database.cleanup_duplicates.return_value = 0
|
||||
self.api = PromptManagerAPI(db=database)
|
||||
|
||||
def _run(self, coro):
|
||||
import asyncio
|
||||
@@ -373,8 +375,9 @@ class CoreRoutesTestCase(AioHTTPTestCase):
|
||||
self.tmpdir = tempfile.mkdtemp()
|
||||
app = web.Application()
|
||||
routes = web.RouteTableDef()
|
||||
self.api = PromptManagerAPI()
|
||||
self.api.db = PromptDatabase(os.path.join(self.tmpdir, "prompts.db"))
|
||||
self.api = PromptManagerAPI(
|
||||
db=PromptDatabase(os.path.join(self.tmpdir, "prompts.db"))
|
||||
)
|
||||
self.api.add_routes(routes)
|
||||
app.router.add_routes(routes)
|
||||
return app
|
||||
@@ -565,8 +568,9 @@ class TestInstanceHelpers(FolderPathsFixture):
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.api = PromptManagerAPI()
|
||||
self.api.db = PromptDatabase(os.path.join(self.tmpdir, "prompts.db"))
|
||||
self.api = PromptManagerAPI(
|
||||
db=PromptDatabase(os.path.join(self.tmpdir, "prompts.db"))
|
||||
)
|
||||
self.addCleanup(self.api.db.close_all) # runs before the rmtree cleanup
|
||||
orig_dirs = list(GalleryConfig.MONITORING_DIRECTORIES)
|
||||
self.addCleanup(setattr, GalleryConfig, "MONITORING_DIRECTORIES", orig_dirs)
|
||||
@@ -668,7 +672,9 @@ class TestInstanceHelpers(FolderPathsFixture):
|
||||
class TestMetadataParsing(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.api = PromptManagerAPI()
|
||||
database = MagicMock()
|
||||
database.cleanup_duplicates.return_value = 0
|
||||
self.api = PromptManagerAPI(db=database)
|
||||
self.tmpdir = tempfile.mkdtemp()
|
||||
self.addCleanup(shutil.rmtree, self.tmpdir, True)
|
||||
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
"""API fixtures never open the configured database before installing test state."""
|
||||
|
||||
import unittest.mock
|
||||
|
||||
from py.api import PromptManagerAPI
|
||||
|
||||
|
||||
class TestAPIDatabaseIsolation(unittest.TestCase):
|
||||
def test_injected_database_never_constructs_the_configured_database(self):
|
||||
database = unittest.mock.MagicMock()
|
||||
database.cleanup_duplicates.return_value = 0
|
||||
with (
|
||||
unittest.mock.patch(
|
||||
"py.api.PromptDatabase", side_effect=AssertionError("default DB opened")
|
||||
) as factory,
|
||||
unittest.mock.patch("py.api.get_logger"),
|
||||
):
|
||||
api = PromptManagerAPI(db=database)
|
||||
self.assertIs(api.db, database)
|
||||
factory.assert_not_called()
|
||||
database.cleanup_duplicates.assert_called_once_with()
|
||||
|
||||
def test_falsey_injected_database_is_still_used(self):
|
||||
database = unittest.mock.MagicMock()
|
||||
database.__bool__.return_value = False
|
||||
database.cleanup_duplicates.return_value = 0
|
||||
with (
|
||||
unittest.mock.patch(
|
||||
"py.api.PromptDatabase", side_effect=AssertionError("default DB opened")
|
||||
) as factory,
|
||||
unittest.mock.patch("py.api.get_logger"),
|
||||
):
|
||||
api = PromptManagerAPI(db=database)
|
||||
self.assertIs(api.db, database)
|
||||
factory.assert_not_called()
|
||||
|
||||
def test_default_runtime_construction_still_uses_the_configured_factory(self):
|
||||
database = unittest.mock.MagicMock()
|
||||
database.cleanup_duplicates.return_value = 2
|
||||
with (
|
||||
unittest.mock.patch(
|
||||
"py.api.PromptDatabase", return_value=database
|
||||
) as factory,
|
||||
unittest.mock.patch("py.api.get_logger") as logger,
|
||||
):
|
||||
api = PromptManagerAPI()
|
||||
self.assertIs(api.db, database)
|
||||
factory.assert_called_once_with()
|
||||
logger.return_value.info.assert_any_call(
|
||||
"Startup cleanup: removed 2 duplicate prompts"
|
||||
)
|
||||
|
||||
def test_injected_cleanup_failure_is_logged_without_falling_back_to_default(self):
|
||||
database = unittest.mock.MagicMock()
|
||||
database.cleanup_duplicates.side_effect = RuntimeError("test cleanup")
|
||||
with (
|
||||
unittest.mock.patch(
|
||||
"py.api.PromptDatabase", side_effect=AssertionError("default DB opened")
|
||||
) as factory,
|
||||
unittest.mock.patch("py.api.get_logger") as logger,
|
||||
):
|
||||
api = PromptManagerAPI(db=database)
|
||||
self.assertIs(api.db, database)
|
||||
factory.assert_not_called()
|
||||
logger.return_value.error.assert_called_once_with(
|
||||
"Startup cleanup failed: test cleanup"
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -64,8 +64,7 @@ class ImageAPITestCase(AioHTTPTestCase):
|
||||
app = web.Application()
|
||||
routes = web.RouteTableDef()
|
||||
|
||||
self.api = PromptManagerAPI()
|
||||
self.api.db = PromptDatabase(self._temp_db.name)
|
||||
self.api = PromptManagerAPI(db=PromptDatabase(self._temp_db.name))
|
||||
self.api._get_all_output_dirs = lambda: [self.output_dir]
|
||||
self.api._find_comfyui_output_dir = lambda: str(self.output_dir)
|
||||
self.api.add_routes(routes)
|
||||
|
||||
@@ -104,8 +104,9 @@ class LoggingAPITestCase(AioHTTPTestCase):
|
||||
|
||||
app = web.Application()
|
||||
routes = web.RouteTableDef()
|
||||
self.api = PromptManagerAPI()
|
||||
self.api.db = PromptDatabase(os.path.join(self.tmpdir, "prompts.db"))
|
||||
self.api = PromptManagerAPI(
|
||||
db=PromptDatabase(os.path.join(self.tmpdir, "prompts.db"))
|
||||
)
|
||||
self.api._get_logger_manager = lambda: self.manager
|
||||
self.api.add_routes(routes)
|
||||
app.router.add_routes(routes)
|
||||
|
||||
@@ -76,8 +76,7 @@ class LoraAPITestCase(AioHTTPTestCase):
|
||||
|
||||
app = web.Application()
|
||||
routes = web.RouteTableDef()
|
||||
self.api = PromptManagerAPI()
|
||||
self.api.db = PromptDatabase(self._temp_db.name)
|
||||
self.api = PromptManagerAPI(db=PromptDatabase(self._temp_db.name))
|
||||
self.api.add_routes(routes)
|
||||
app.router.add_routes(routes)
|
||||
return app
|
||||
|
||||
@@ -38,8 +38,7 @@ class PromptAPITestCase(AioHTTPTestCase):
|
||||
app = web.Application()
|
||||
routes = web.RouteTableDef()
|
||||
|
||||
self.api = PromptManagerAPI()
|
||||
self.api.db = PromptDatabase(self._temp_db.name)
|
||||
self.api = PromptManagerAPI(db=PromptDatabase(self._temp_db.name))
|
||||
self.api.add_routes(routes)
|
||||
app.router.add_routes(routes)
|
||||
return app
|
||||
|
||||
@@ -299,7 +299,9 @@ class TestGetAllOutputDirsFiltersRoots(GalleryRootTestCase):
|
||||
super().setUp()
|
||||
from py.api import PromptManagerAPI
|
||||
|
||||
self.api = PromptManagerAPI()
|
||||
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)
|
||||
self.addCleanup(self._restore_dirs)
|
||||
|
||||
@@ -9,7 +9,7 @@ import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
from unittest.mock import MagicMock
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
@@ -26,7 +26,16 @@ class TestFindComfyuiOutputDir(unittest.TestCase):
|
||||
"""Test _find_comfyui_output_dir() respects user-configured directories."""
|
||||
|
||||
def setUp(self):
|
||||
self.api = PromptManagerAPI()
|
||||
roots = patch.object(
|
||||
GalleryConfig,
|
||||
"allowed_gallery_parents",
|
||||
return_value=[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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user