Files
Artificial-Sweetener-Simple…/tests/test_comfy_integration_managed_server.py
T

159 lines
4.8 KiB
Python

# SimpleSyrup - workflow-focused ComfyUI extensions for image generation
# Copyright (C) 2026 Artificial Sweetener and contributors
# SPDX-License-Identifier: AGPL-3.0-or-later
"""Test context-managed startup, readiness, and exact cleanup coordination."""
from __future__ import annotations
import json
from pathlib import Path
from typing import cast
import pytest
from tools.comfy_api import JsonObject
from tools.comfy_integration import managed_server
from tools.comfy_integration.artifacts import IntegrationArtifacts
from tools.comfy_integration.managed_server import ManagedComfyServer
from tools.comfy_integration.server_process import ComfyServerCommand
class _FakeProcess:
"""Expose the process boundary required by the lifecycle coordinator."""
pid = 4242
def __init__(self) -> None:
"""Initialize as live and un-stopped."""
self.running = True
self.stop_calls = 0
@property
def is_running(self) -> bool:
"""Return current fake process liveness."""
return self.running
def stop(self) -> None:
"""Record cleanup and transition to stopped."""
self.stop_calls += 1
self.running = False
class _FakeClient:
"""Retain the selected loopback endpoint."""
def __init__(self, base_url: str) -> None:
"""Record the endpoint supplied by the lifecycle owner."""
self.base_url = base_url
def _configure(
monkeypatch: pytest.MonkeyPatch,
process: _FakeProcess,
*,
readiness_error: BaseException | None = None,
) -> None:
"""Replace external lifecycle boundaries with deterministic fakes."""
monkeypatch.setattr(managed_server, "select_unused_loopback_port", lambda: 8299)
monkeypatch.setattr(
"tools.comfy_integration.managed_server.WindowsComfyProcess.start",
lambda command, stdout_path, stderr_path: process,
)
monkeypatch.setattr(managed_server, "LoopbackComfyClient", _FakeClient)
def ready(
client: object, live: object, required: object, *, timeout: float
) -> JsonObject:
"""Return metadata or the configured readiness failure."""
del client, live, required, timeout
if readiness_error is not None:
raise readiness_error
return {"ready": True}
monkeypatch.setattr(managed_server, "wait_for_server", ready)
def test_context_stops_exact_created_process_after_body_failure(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
"""Guarantee owned cleanup when workflow execution raises."""
process = _FakeProcess()
_configure(monkeypatch, process)
artifacts = IntegrationArtifacts(tmp_path)
with pytest.raises(ValueError, match="workflow failed"):
with ManagedComfyServer(
comfy_root=Path("<COMFY_ROOT>"),
artifacts=artifacts,
required_node_ids=frozenset({"Required.Node"}),
) as running:
assert running.port == 8299
assert cast(_FakeClient, running.client).base_url == "http://127.0.0.1:8299"
raise ValueError("workflow failed")
assert process.stop_calls == 1
record: object = json.loads((artifacts.root / "run.json").read_text("utf-8"))
assert isinstance(record, dict)
assert record["parent_pid"] == 4242
def test_readiness_failure_stops_process_before_propagation(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
"""Clean the created tree when startup never becomes usable."""
process = _FakeProcess()
_configure(monkeypatch, process, readiness_error=TimeoutError("not ready"))
artifacts = IntegrationArtifacts(tmp_path)
with pytest.raises(TimeoutError, match="not ready"):
with ManagedComfyServer(
comfy_root=Path("<COMFY_ROOT>"),
artifacts=artifacts,
required_node_ids=frozenset(),
):
pytest.fail("unreachable")
assert process.stop_calls == 1
def test_context_passes_optional_launch_arguments_to_command_owner(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
"""Route CPU-only registration probes through the canonical lifecycle."""
process = _FakeProcess()
_configure(monkeypatch, process)
artifacts = IntegrationArtifacts(tmp_path)
captured: list[tuple[str, ...]] = []
def start(command: ComfyServerCommand, **kwargs: object) -> _FakeProcess:
"""Capture the exact immutable launch arguments."""
del kwargs
captured.append(command.launch_arguments)
return process
monkeypatch.setattr(
"tools.comfy_integration.managed_server.WindowsComfyProcess.start",
start,
)
with ManagedComfyServer(
comfy_root=Path("<COMFY_ROOT>"),
artifacts=artifacts,
required_node_ids=frozenset(),
launch_arguments=("--cpu",),
):
pass
assert captured == [("--cpu",)]