Files
darth-veitcher-comfydv/tests/conftest.py
T
James VeitchandClaude Sonnet 4.6 4c64e2b6da feat(chat): I-commit — model→STRING, OLLAMA_HOST docker support, dynamic test fixtures
- OllamaChatCompletion.model changed from COMBO to plain STRING so it can
  receive a wired value from OllamaLoadModel without a separate model_name
  socket (removes dual-input UX confusion and COMBO validation errors)
- Removed model_name optional input — model STRING is the single entry point
- _load_default_models() tries OLLAMA_HOST env var → host.docker.internal →
  localhost so COMBO nodes pre-populate on server start inside Docker
- docker-compose.dev.yml: OLLAMA_HOST=http://host.docker.internal:11434 so
  model dropdowns are populated at startup without a manual Refresh click
- JS: refreshModelWidget handles both COMBO and STRING widget types; keeps
  user-typed/wired value when it is already a known model
- Tests: first_generative_model fixture dynamically selects the first
  installed model instead of hardcoding embeddinggemma:latest (which is an
  embedding-only model that rejects /api/generate with HTTP 400)
- Tests: test_multi_turn_receives_context passes think=False to prevent
  Qwen3 thinking-mode models from returning empty content fields
- Screenshots retaken: ollama_chat.png, ollama_lifecycle.png, ollama_workflow.png

All 80 unit + 9 integration tests pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RWysE6jYn4YjNorLBQ1cXb
2026-06-29 12:49:53 +01:00

169 lines
4.7 KiB
Python

"""
Pytest configuration and fixtures for comfydv tests.
This file sets up mocks for ComfyUI dependencies before any test imports.
"""
import os
import socket
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):
"""Install ComfyUI mocks into sys.modules before any test collection."""
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
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
class MockFolderPaths:
@staticmethod
def get_output_directory():
return "/tmp/comfydv_test"
sys.modules["folder_paths"] = MockFolderPaths
# ---------------------------------------------------------------------------
# Ollama fixtures (used by @pytest.mark.integration tests)
# ---------------------------------------------------------------------------
@pytest.fixture(scope="session")
def ollama_host():
return "http://localhost:11434"
@pytest.fixture(scope="session")
def ollama_available():
try:
sock = socket.create_connection(("localhost", 11434), timeout=2.0)
sock.close()
return True
except OSError:
return False
@pytest.fixture
def skip_if_no_ollama(ollama_available):
if not ollama_available:
pytest.skip(
"Ollama not reachable at localhost:11434 — start Ollama to run integration tests"
)
@pytest.fixture(scope="session")
def first_generative_model(ollama_host, ollama_available):
"""Return the first model available from Ollama, skipping embedding-only models.
Used by lifecycle tests that call /api/generate — embedding models like
embeddinggemma reject that endpoint with HTTP 400.
"""
if not ollama_available:
pytest.skip("Ollama not reachable at localhost:11434")
import asyncio
from comfydv.ollama import _fetch_models
models = asyncio.run(_fetch_models(ollama_host))
if not models:
pytest.skip("No models installed in Ollama")
return models[0]
# ---------------------------------------------------------------------------
# Existing ComfyUI node fixtures
# ---------------------------------------------------------------------------
@pytest.fixture
def format_string_class():
"""Provide a fresh FormatString class for each test."""
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
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,
}