282 lines
9.4 KiB
Python
282 lines
9.4 KiB
Python
"""Tests for Modal-Sync settings discovery."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
|
|
def test_settings_discovers_comfyui_root_from_custom_nodes_install_path(
|
|
settings_module: Any,
|
|
monkeypatch: Any,
|
|
tmp_path: Path,
|
|
) -> None:
|
|
"""The extension should infer the ComfyUI root from its install location."""
|
|
comfyui_root = tmp_path / "ComfyUI"
|
|
custom_node_repo = comfyui_root / "custom_nodes" / "ComfyUI-Modal"
|
|
custom_node_repo.mkdir(parents=True)
|
|
(comfyui_root / "main.py").write_text("print('main')\n", encoding="utf-8")
|
|
(comfyui_root / "nodes.py").write_text("NODE_CLASS_MAPPINGS = {}\n", encoding="utf-8")
|
|
|
|
monkeypatch.setattr(settings_module, "__file__", str(custom_node_repo / "settings.py"))
|
|
monkeypatch.delenv("COMFYUI_ROOT", raising=False)
|
|
monkeypatch.delenv("COMFY_MODAL_COMFYUI_ROOT", raising=False)
|
|
|
|
resolved = settings_module._discover_comfyui_root(custom_node_repo)
|
|
|
|
assert resolved == comfyui_root.resolve()
|
|
|
|
|
|
def test_settings_prefers_modal_specific_comfyui_root_env(
|
|
settings_module: Any,
|
|
monkeypatch: Any,
|
|
tmp_path: Path,
|
|
) -> None:
|
|
"""The Modal-specific ComfyUI root env var should override path inference."""
|
|
env_root = tmp_path / "alt-comfyui"
|
|
env_root.mkdir()
|
|
|
|
monkeypatch.setenv("COMFY_MODAL_COMFYUI_ROOT", str(env_root))
|
|
monkeypatch.delenv("COMFYUI_ROOT", raising=False)
|
|
|
|
resolved = settings_module._discover_comfyui_root(tmp_path / "repo")
|
|
|
|
assert resolved == env_root.resolve()
|
|
|
|
|
|
def test_settings_reads_modal_gpu_override(
|
|
settings_module: Any,
|
|
monkeypatch: Any,
|
|
) -> None:
|
|
"""The Modal GPU type should be configurable via environment variable."""
|
|
monkeypatch.setenv("COMFY_MODAL_GPU", "L40S")
|
|
settings_module.get_settings.cache_clear()
|
|
try:
|
|
settings = settings_module.get_settings()
|
|
finally:
|
|
settings_module.get_settings.cache_clear()
|
|
|
|
assert settings.modal_gpu == "L40S"
|
|
|
|
|
|
def test_settings_cache_tracks_execution_mode_env_changes(
|
|
settings_module: Any,
|
|
monkeypatch: Any,
|
|
) -> None:
|
|
"""Changing Modal execution mode should not leave callers with stale cached settings."""
|
|
settings_module.get_settings.cache_clear()
|
|
try:
|
|
monkeypatch.setenv("COMFY_MODAL_EXECUTION_MODE", "local")
|
|
local_settings = settings_module.get_settings()
|
|
|
|
monkeypatch.setenv("COMFY_MODAL_EXECUTION_MODE", "remote")
|
|
remote_settings = settings_module.get_settings()
|
|
finally:
|
|
settings_module.get_settings.cache_clear()
|
|
|
|
assert local_settings.execution_mode == "local"
|
|
assert local_settings.sync_custom_nodes is False
|
|
assert remote_settings.execution_mode == "remote"
|
|
assert remote_settings.sync_custom_nodes is True
|
|
|
|
|
|
def test_settings_enable_gpu_memory_snapshot_defaults_true(
|
|
settings_module: Any,
|
|
monkeypatch: Any,
|
|
) -> None:
|
|
"""GPU memory snapshots should be enabled by default."""
|
|
monkeypatch.delenv("COMFY_MODAL_ENABLE_GPU_MEMORY_SNAPSHOT", raising=False)
|
|
settings_module.get_settings.cache_clear()
|
|
try:
|
|
settings = settings_module.get_settings()
|
|
finally:
|
|
settings_module.get_settings.cache_clear()
|
|
|
|
assert settings.enable_gpu_memory_snapshot is True
|
|
|
|
|
|
def test_settings_reads_modal_container_scaling_overrides(
|
|
settings_module: Any,
|
|
monkeypatch: Any,
|
|
) -> None:
|
|
"""Modal autoscaling knobs should be configurable via environment variables."""
|
|
monkeypatch.setenv("COMFY_MODAL_MIN_CONTAINERS", "1")
|
|
monkeypatch.setenv("COMFY_MODAL_MAX_CONTAINERS", "6")
|
|
monkeypatch.setenv("COMFY_MODAL_BUFFER_CONTAINERS", "2")
|
|
settings_module.get_settings.cache_clear()
|
|
try:
|
|
settings = settings_module.get_settings()
|
|
finally:
|
|
settings_module.get_settings.cache_clear()
|
|
|
|
assert settings.min_containers == 1
|
|
assert settings.max_containers == 6
|
|
assert settings.buffer_containers == 2
|
|
|
|
|
|
def test_settings_reads_proactive_warmup_override(
|
|
settings_module: Any,
|
|
monkeypatch: Any,
|
|
) -> None:
|
|
"""Proactive remote warmup should be configurable via environment variable."""
|
|
monkeypatch.setenv("COMFY_MODAL_ENABLE_PROACTIVE_WARMUP", "false")
|
|
settings_module.get_settings.cache_clear()
|
|
try:
|
|
settings = settings_module.get_settings()
|
|
finally:
|
|
settings_module.get_settings.cache_clear()
|
|
|
|
assert settings.enable_proactive_warmup is False
|
|
|
|
|
|
def test_settings_reads_remote_cancel_grace_override(
|
|
settings_module: Any,
|
|
monkeypatch: Any,
|
|
) -> None:
|
|
"""The post-interrupt remote wait should be configurable."""
|
|
monkeypatch.setenv("COMFY_MODAL_REMOTE_CANCEL_GRACE_SECONDS", "0.25")
|
|
settings_module.get_settings.cache_clear()
|
|
try:
|
|
settings = settings_module.get_settings()
|
|
finally:
|
|
settings_module.get_settings.cache_clear()
|
|
|
|
assert settings.remote_cancel_grace_seconds == 0.25
|
|
|
|
|
|
def test_settings_reads_remote_cancel_restart_override(
|
|
settings_module: Any,
|
|
monkeypatch: Any,
|
|
) -> None:
|
|
"""The remote post-cancel restart timeout should be configurable."""
|
|
monkeypatch.setenv("COMFY_MODAL_REMOTE_CANCEL_RESTART_SECONDS", "0.75")
|
|
settings_module.get_settings.cache_clear()
|
|
try:
|
|
settings = settings_module.get_settings()
|
|
finally:
|
|
settings_module.get_settings.cache_clear()
|
|
|
|
assert settings.remote_cancel_restart_seconds == 0.75
|
|
|
|
|
|
def test_settings_defaults_interrupt_dict_name_from_app_name(
|
|
settings_module: Any,
|
|
monkeypatch: Any,
|
|
) -> None:
|
|
"""The shared interrupt dict name should default to one derived from the app name."""
|
|
monkeypatch.setenv("COMFY_MODAL_APP_NAME", "my-modal-app")
|
|
settings_module.get_settings.cache_clear()
|
|
try:
|
|
settings = settings_module.get_settings()
|
|
finally:
|
|
settings_module.get_settings.cache_clear()
|
|
|
|
assert settings.interrupt_dict_name == "my-modal-app-interrupts"
|
|
|
|
|
|
def test_settings_reads_interrupt_dict_name_override(
|
|
settings_module: Any,
|
|
monkeypatch: Any,
|
|
) -> None:
|
|
"""The shared interrupt dict name should be overridable explicitly."""
|
|
monkeypatch.setenv("COMFY_MODAL_INTERRUPT_DICT_NAME", "custom-interrupt-store")
|
|
settings_module.get_settings.cache_clear()
|
|
try:
|
|
settings = settings_module.get_settings()
|
|
finally:
|
|
settings_module.get_settings.cache_clear()
|
|
|
|
assert settings.interrupt_dict_name == "custom-interrupt-store"
|
|
|
|
|
|
def test_settings_defaults_node_cache_dict_name_from_app_name(
|
|
settings_module: Any,
|
|
monkeypatch: Any,
|
|
) -> None:
|
|
"""The shared node-cache dict should default to one derived from the app name."""
|
|
monkeypatch.setenv("COMFY_MODAL_APP_NAME", "my-modal-app")
|
|
settings_module.get_settings.cache_clear()
|
|
try:
|
|
settings = settings_module.get_settings()
|
|
finally:
|
|
settings_module.get_settings.cache_clear()
|
|
|
|
assert settings.node_output_cache_dict_name == "my-modal-app-node-cache"
|
|
|
|
|
|
def test_settings_defaults_session_bridge_dict_name_from_app_name(
|
|
settings_module: Any,
|
|
monkeypatch: Any,
|
|
) -> None:
|
|
"""The durable session-bridge dict should default to one derived from the app name."""
|
|
monkeypatch.setenv("COMFY_MODAL_APP_NAME", "my-modal-app")
|
|
settings_module.get_settings.cache_clear()
|
|
try:
|
|
settings = settings_module.get_settings()
|
|
finally:
|
|
settings_module.get_settings.cache_clear()
|
|
|
|
assert settings.session_bridge_dict_name == "my-modal-app-session-bridges"
|
|
|
|
|
|
def test_settings_defaults_node_cache_max_bytes_to_five_mib(
|
|
settings_module: Any,
|
|
monkeypatch: Any,
|
|
) -> None:
|
|
"""The distributed node-cache size cap should default to 5 MiB."""
|
|
monkeypatch.delenv("COMFY_MODAL_NODE_CACHE_MAX_BYTES", raising=False)
|
|
settings_module.get_settings.cache_clear()
|
|
try:
|
|
settings = settings_module.get_settings()
|
|
finally:
|
|
settings_module.get_settings.cache_clear()
|
|
|
|
assert settings.node_output_cache_max_bytes == 5 * 1024 * 1024
|
|
|
|
|
|
def test_settings_reads_node_cache_overrides(
|
|
settings_module: Any,
|
|
monkeypatch: Any,
|
|
) -> None:
|
|
"""The shared node-cache dict name and size limit should be configurable."""
|
|
monkeypatch.setenv("COMFY_MODAL_NODE_CACHE_DICT_NAME", "custom-node-cache")
|
|
monkeypatch.setenv("COMFY_MODAL_NODE_CACHE_MAX_BYTES", "123456")
|
|
settings_module.get_settings.cache_clear()
|
|
try:
|
|
settings = settings_module.get_settings()
|
|
finally:
|
|
settings_module.get_settings.cache_clear()
|
|
|
|
assert settings.node_output_cache_dict_name == "custom-node-cache"
|
|
assert settings.node_output_cache_max_bytes == 123456
|
|
|
|
|
|
def test_settings_reads_session_bridge_dict_name_override(
|
|
settings_module: Any,
|
|
monkeypatch: Any,
|
|
) -> None:
|
|
"""The durable session-bridge dict name should be configurable."""
|
|
monkeypatch.setenv("COMFY_MODAL_SESSION_BRIDGE_DICT_NAME", "custom-session-bridges")
|
|
settings_module.get_settings.cache_clear()
|
|
try:
|
|
settings = settings_module.get_settings()
|
|
finally:
|
|
settings_module.get_settings.cache_clear()
|
|
|
|
assert settings.session_bridge_dict_name == "custom-session-bridges"
|
|
|
|
|
|
def test_settings_reads_terminate_container_on_error_override(
|
|
settings_module: Any,
|
|
monkeypatch: Any,
|
|
) -> None:
|
|
"""Remote crash teardown should be configurable via environment variable."""
|
|
monkeypatch.setenv("COMFY_MODAL_TERMINATE_CONTAINER_ON_ERROR", "false")
|
|
settings_module.get_settings.cache_clear()
|
|
try:
|
|
settings = settings_module.get_settings()
|
|
finally:
|
|
settings_module.get_settings.cache_clear()
|
|
|
|
assert settings.terminate_container_on_error is False
|