Files
darth-veitcher-comfydv/tests/conftest.py
T
darth-veitcher 130c57f88b Add comprehensive pytest test suite and fix FormatString node issues
- Fixed Jinja2 regex pattern to handle spaces around filters ({{ var | filter }})
- Added null safety in IS_CHANGED method for empty/None templates
- Restored dynamic output functionality for variable chaining
- Created 47 comprehensive tests with 78% coverage on format_string.py
- Set up test infrastructure with ComfyUI dependency mocking
- Used importlib to bypass package imports during testing
- Added conditional imports in root __init__.py for test compatibility
- Configured pytest in pyproject.toml (removed outdated pytest.ini)
- Added pytest and pytest-cov to dev dependencies
- Updated .gitignore for test artifacts
- Removed deprecated model_unload.py
2025-11-05 11:59:31 +00:00

217 lines
5.5 KiB
Python

"""
Pytest configuration and fixtures for comfydv tests.
This file sets up mocks for ComfyUI dependencies before any test imports.
"""
import os
import sys
import pytest
# Add src directory to Python path so we can import modules
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src"))
def pytest_configure(config):
"""
Pytest hook that runs before test collection.
Install all ComfyUI mocks here before any imports happen.
"""
# Mock comfy module
class MockInterruptProcessingException(Exception):
pass
class MockModelManagement:
InterruptProcessingException = MockInterruptProcessingException
comfy_module = type(sys)("comfy")
comfy_module.model_management = MockModelManagement
sys.modules["comfy"] = comfy_module
sys.modules["comfy.model_management"] = MockModelManagement
# Mock server module
class MockRoutes:
@staticmethod
def post(path):
def decorator(func):
return func
return decorator
@staticmethod
def get(path):
def decorator(func):
return func
return decorator
class MockPromptServer:
def __init__(self):
self.routes = MockRoutes()
MockPromptServer.instance = MockPromptServer()
server_module = type(sys)("server")
server_module.PromptServer = MockPromptServer
sys.modules["server"] = server_module
# Mock folder_paths module
class MockFolderPaths:
@staticmethod
def get_output_directory():
return "/tmp/comfydv_test"
sys.modules["folder_paths"] = MockFolderPaths
# Mock aiohttp module
class MockWeb:
@staticmethod
def json_response(data):
return data
aiohttp_module = type(sys)("aiohttp")
aiohttp_module.web = MockWeb
sys.modules["aiohttp"] = aiohttp_module
# Keep these classes for type hints/documentation
class MockRoutes:
"""Mock ComfyUI routes for testing."""
@staticmethod
def post(path):
"""Mock POST route decorator."""
def decorator(func):
return func
return decorator
@staticmethod
def get(path):
"""Mock GET route decorator."""
def decorator(func):
return func
return decorator
class MockPromptServer:
"""Mock ComfyUI PromptServer for testing."""
def __init__(self):
self.routes = MockRoutes()
instance = None
MockPromptServer.instance = MockPromptServer()
class MockFolderPaths:
"""Mock ComfyUI folder_paths module for testing."""
@staticmethod
def get_output_directory():
"""Return temporary directory for testing."""
return "/tmp/comfydv_test"
class MockWeb:
"""Mock aiohttp.web for testing."""
@staticmethod
def json_response(data):
"""Mock json_response method."""
return data
class MockInterruptProcessingException(Exception):
"""Mock ComfyUI InterruptProcessingException."""
pass
class MockModelManagement:
"""Mock ComfyUI model_management module."""
InterruptProcessingException = MockInterruptProcessingException
# Install mocks in sys.modules FIRST, before any other imports
# This is critical to prevent ImportErrors from comfydv modules
comfy_module = type(sys)("comfy")
comfy_module.model_management = MockModelManagement
sys.modules["comfy"] = comfy_module
sys.modules["comfy.model_management"] = MockModelManagement
server_module = type(sys)("server")
server_module.PromptServer = MockPromptServer
sys.modules["server"] = server_module
sys.modules["folder_paths"] = MockFolderPaths
aiohttp_module = type(sys)("aiohttp")
aiohttp_module.web = MockWeb
sys.modules["aiohttp"] = aiohttp_module
@pytest.fixture
def format_string_class():
"""Fixture to provide a fresh FormatString class for each test."""
# Import the module directly, bypassing __init__.py which has ComfyUI dependencies
import importlib.util
import os
spec = importlib.util.spec_from_file_location(
"format_string",
os.path.join(
os.path.dirname(__file__), "..", "src", "comfydv", "format_string.py"
),
)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
FormatString = module.FormatString
# Reset class state before each test
FormatString.node_configs = {}
FormatString.RETURN_TYPES = ("STRING", "STRING")
FormatString.RETURN_NAMES = ("formatted_string", "saved_file_path")
FormatString.OUTPUT_IS_LIST = (False, False)
return FormatString
@pytest.fixture
def sample_templates():
"""Fixture providing sample templates for testing."""
return {
"simple_one_var": "Hello {name}",
"simple_two_vars": "Hello {name}, you are {age} years old",
"simple_three_vars": "{greeting} {name}, you are {age}",
"jinja2_simple": "Hello {{ name }}",
"jinja2_filter": "Hello {{ name | upper }}",
"jinja2_multiple_filters": "{{ first | upper }} {{ last | lower }}",
"jinja2_datetime": "Current time: {{ now() }}",
"jinja2_with_math": "Result: {{ value * 2 }}",
"mixed": "Hello {name}, today is {{ date }}",
"no_vars": "Hello World",
}
@pytest.fixture
def sample_data():
"""Fixture providing sample data for template rendering."""
return {
"name": "Alice",
"age": "30",
"greeting": "Hi",
"first": "John",
"last": "Doe",
"date": "2025-11-05",
"value": 5,
}