Files
VraethrDalkr c7c24e02d7 release: v0.10.4 - major refactoring and WanVideoWrapper integration
This release includes comprehensive architecture refactoring, WanVideoWrapper
integration, critical bug fixes, and extensive testing improvements.

Major changes:
- Modular architecture with shared utilities (triple_ksampler/ package)
- WanVideoWrapper integration nodes (TripleWVSampler variants)
- Critical fix for WanVideo sample passing (issue aec0e66)
- Debug logging improvements
- Comprehensive documentation updates
- Test infrastructure improvements (unit/integration/regression separation)
- 314 total tests (204 unit + 110 integration/regression)

See CHANGELOG.md for detailed release notes spanning v0.9.4 through v0.10.4.
2025-11-14 19:18:07 -05:00

813 lines
30 KiB
Python

"""
Unit tests for TripleKSampler parameter validation.
Tests all the error handling scenarios we've implemented to ensure
they raise appropriate ValueErrors with helpful messages.
"""
import os
import sys
from unittest.mock import MagicMock
import pytest
import torch
# Import assertion helper
# Import from conftest (classes are loaded there)
from conftest import COMFYUI_AVAILABLE, TripleKSampler, TripleKSamplerAdvanced
# Import comfy for exception types
try:
import comfy.model_management
except ImportError:
comfy = None
@pytest.mark.skipif(not COMFYUI_AVAILABLE, reason="ComfyUI dependencies not available")
class TestParameterValidation:
"""Test parameter validation for TripleKSampler nodes."""
def setup_method(self):
"""Set up test fixtures."""
self.advanced_node = TripleKSamplerAdvanced()
self.simple_node = TripleKSampler()
# Mock models and inputs with proper ComfyUI compatibility
from unittest.mock import MagicMock
# Create model mocks with required attributes for ComfyUI
self.mock_base_high = MagicMock()
self.mock_base_high.model.model_config.sampling_settings = {
"shift": 1.0,
"multiplier": 1000,
}
self.mock_lightning_high = MagicMock()
self.mock_lightning_high.model.model_config.sampling_settings = {
"shift": 1.0,
"multiplier": 1000,
}
self.mock_lightning_low = MagicMock()
self.mock_lightning_low.model.model_config.sampling_settings = {
"shift": 1.0,
"multiplier": 1000,
}
self.mock_positive = MagicMock()
self.mock_negative = MagicMock()
# Mock latent with tensor-like behavior
import torch
self.mock_latent = {"samples": torch.randn(1, 4, 64, 64)}
# Common valid parameters
self.valid_params = {
"base_high": self.mock_base_high,
"lightning_high": self.mock_lightning_high,
"lightning_low": self.mock_lightning_low,
"positive": self.mock_positive,
"negative": self.mock_negative,
"latent_image": self.mock_latent,
"seed": 42,
"sigma_shift": 5.0,
"base_steps": 3,
"base_quality_threshold": -1,
"base_cfg": 3.5,
"lightning_start": 1,
"lightning_steps": 8,
"lightning_cfg": 1.0,
"base_sampler": "euler",
"base_scheduler": "simple",
"lightning_sampler": "euler",
"lightning_scheduler": "simple",
"switch_strategy": "50% of steps",
"switch_boundary": 0.875,
"switch_step": -1,
}
def test_lightning_steps_too_small(self):
"""Test lightning_steps must be at least 2."""
params = self.valid_params.copy()
params["lightning_steps"] = 1
with pytest.raises(ValueError, match="lightning_steps must be at least 2"):
self.advanced_node.sample(**params)
def test_lightning_start_out_of_range(self):
"""Test lightning_start must be within valid range."""
params = self.valid_params.copy()
# Test negative lightning_start
params["lightning_start"] = -1
with pytest.raises(ValueError, match="lightning_start must be within"):
self.advanced_node.sample(**params)
# Test lightning_start >= lightning_steps
params["lightning_start"] = 8
params["lightning_steps"] = 8
with pytest.raises(ValueError, match="lightning_start must be within"):
self.advanced_node.sample(**params)
def test_switch_step_negative(self):
"""Test switch_step cannot be negative for manual strategy."""
params = self.valid_params.copy()
params["switch_strategy"] = "Manual switch step"
params["switch_step"] = -5 # Invalid negative value
with pytest.raises(ValueError, match="switch_step \\(-5\\) must be >= 0"):
self.advanced_node.sample(**params)
def test_switch_step_out_of_bounds(self):
"""Test switch_step must be within lightning_steps range."""
params = self.valid_params.copy()
params["switch_strategy"] = "Manual switch step"
params["switch_step"] = 10 # Greater than lightning_steps (8)
with pytest.raises(
ValueError, match="switch_step \\(10\\) must be < lightning_steps \\(8\\)"
):
self.advanced_node.sample(**params)
def test_switch_step_less_than_lightning_start(self):
"""Test switch_step cannot be less than lightning_start."""
params = self.valid_params.copy()
params["switch_strategy"] = "Manual switch step"
params["lightning_start"] = 5
params["switch_step"] = 2 # Less than lightning_start
with pytest.raises(ValueError) as exc_info:
self.advanced_node.sample(**params)
error_msg = str(exc_info.value)
assert "switch_step (2) cannot be less than lightning_start (5)" in error_msg
assert "If you want low-noise only, set lightning_start=0 as well" in error_msg
def test_base_steps_too_small_with_lightning_start(self):
"""Test base_steps must be >= 1 when lightning_start > 0."""
params = self.valid_params.copy()
params["base_steps"] = 0
params["lightning_start"] = 2
with pytest.raises(ValueError, match="base_steps must be >= 1 when lightning_start > 0"):
self.advanced_node.sample(**params)
def test_base_steps_zero_with_nonzero_lightning_start(self):
"""Test base_steps=0 only allowed when lightning_start=0."""
params = self.valid_params.copy()
params["base_steps"] = 0
params["lightning_start"] = 1
with pytest.raises(ValueError, match="base_steps must be >= 1 when lightning_start > 0"):
self.advanced_node.sample(**params)
def test_stage1_stage2_skip_with_positive_base_steps(self):
"""Test Stage1+Stage2 skip scenario validation."""
params = self.valid_params.copy()
params["lightning_start"] = 0 # Skip Stage1
params["switch_strategy"] = "Manual switch step"
params["switch_step"] = 0 # Skip Stage2
params["base_steps"] = 5 # Positive base_steps (invalid)
with pytest.raises(
ValueError, match="When skipping both Stage 1 and Stage 2.*base_steps must be -1 or 0"
):
self.advanced_node.sample(**params)
def test_lightning_start_zero_with_positive_base_steps(self):
"""Test base_steps ignored when lightning_start=0."""
params = self.valid_params.copy()
params["lightning_start"] = 0 # Lightning-only mode
params["base_steps"] = 5 # Positive base_steps (will be ignored)
with pytest.raises(ValueError) as exc_info:
self.advanced_node.sample(**params)
error_msg = str(exc_info.value)
assert "Set base_steps=0 or base_steps=-1 for Lightning-only mode" in error_msg
def test_lightning_start_greater_than_switch_point(self):
"""Test lightning_start cannot be greater than computed switch point."""
params = self.valid_params.copy()
params["lightning_start"] = 6
params["switch_strategy"] = "Manual switch step"
params["switch_step"] = 2 # Less than lightning_start
with pytest.raises(ValueError) as exc_info:
self.advanced_node.sample(**params)
error_msg = str(exc_info.value)
assert (
"cannot be greater than switch_step" in error_msg
or "cannot be less than lightning_start" in error_msg
)
def test_valid_parameters_pass(self):
"""Test that valid parameters don't raise exceptions."""
# Use dry_run mode to test parameter validation without actual sampling
params = self.valid_params.copy()
params["dry_run"] = True
# In dry run mode, should interrupt workflow execution
with pytest.raises(comfy.model_management.InterruptProcessingException):
self.advanced_node.sample(**params)
def test_simple_node_delegates_validation(self):
"""Test that simple node inherits validation from advanced node."""
# Simple node parameters (subset of advanced)
simple_params = {
"base_high": self.mock_base_high,
"lightning_high": self.mock_lightning_high,
"lightning_low": self.mock_lightning_low,
"positive": self.mock_positive,
"negative": self.mock_negative,
"latent_image": self.mock_latent,
"seed": 42,
"sigma_shift": 5.0,
"base_steps": 3, # Required but ignored
"base_quality_threshold": -1,
"base_cfg": 3.5,
"lightning_start": 1, # Added required parameter
"lightning_steps": 1, # Invalid: too small
"lightning_cfg": 1.0, # Required but ignored
"sampler_name": "euler",
"scheduler": "simple",
"switch_strategy": "50% of steps",
}
with pytest.raises(ValueError, match="lightning_steps must be at least 2"):
self.simple_node.sample(**simple_params)
@pytest.mark.skipif(not COMFYUI_AVAILABLE, reason="ComfyUI dependencies not available")
class TestEdgeCases:
"""Test edge cases and special scenarios."""
def setup_method(self):
"""Set up test fixtures."""
self.advanced_node = TripleKSamplerAdvanced()
# Set up proper mocks like in TestParameterValidation
from unittest.mock import MagicMock
import torch
self.mock_base_high = MagicMock()
self.mock_base_high.model.model_config.sampling_settings = {
"shift": 1.0,
"multiplier": 1000,
}
self.mock_lightning_high = MagicMock()
self.mock_lightning_high.model.model_config.sampling_settings = {
"shift": 1.0,
"multiplier": 1000,
}
self.mock_lightning_low = MagicMock()
self.mock_lightning_low.model.model_config.sampling_settings = {
"shift": 1.0,
"multiplier": 1000,
}
self.mock_latent = {"samples": torch.randn(1, 4, 64, 64)}
def test_lightning_only_mode_valid(self):
"""Test valid Lightning-only mode configuration."""
params = {
"base_high": self.mock_base_high,
"lightning_high": self.mock_lightning_high,
"lightning_low": self.mock_lightning_low,
"positive": MagicMock(),
"negative": MagicMock(),
"latent_image": self.mock_latent,
"seed": 42,
"sigma_shift": 5.0,
"base_steps": 0, # Valid for lightning-only
"base_quality_threshold": 20, # Add missing parameter
"base_cfg": 3.5,
"lightning_start": 0, # Lightning-only mode
"lightning_steps": 8,
"lightning_cfg": 1.0,
"base_sampler": "euler",
"base_scheduler": "simple",
"lightning_sampler": "euler",
"lightning_scheduler": "simple",
"switch_strategy": "50% of steps",
"switch_boundary": 0.875,
"switch_step": -1,
"dry_run": True, # Use dry run to avoid sampling execution
}
# Should pass validation and return minimal 8x8 latent
with pytest.raises(comfy.model_management.InterruptProcessingException):
self.advanced_node.sample(**params)
def test_auto_calculation_mode(self):
"""Test auto-calculation modes work correctly."""
params = {
"base_high": self.mock_base_high,
"lightning_high": self.mock_lightning_high,
"lightning_low": self.mock_lightning_low,
"positive": MagicMock(),
"negative": MagicMock(),
"latent_image": self.mock_latent,
"seed": 42,
"sigma_shift": 5.0,
"base_steps": -1, # Auto-calculate
"base_quality_threshold": 25, # Add missing parameter
"base_cfg": 3.5,
"lightning_start": 2,
"lightning_steps": 8,
"lightning_cfg": 1.0,
"base_sampler": "euler",
"base_scheduler": "simple",
"lightning_sampler": "euler",
"lightning_scheduler": "simple",
"switch_strategy": "Manual switch step",
"switch_boundary": 0.875,
"switch_step": -1, # Auto-calculate
"dry_run": True, # Use dry run to avoid sampling execution
}
# Should pass validation and return minimal 8x8 latent
with pytest.raises(comfy.model_management.InterruptProcessingException):
self.advanced_node.sample(**params)
@pytest.mark.skipif(not COMFYUI_AVAILABLE, reason="ComfyUI dependencies not available")
class TestSimpleNodeLightningStart:
"""Test Simple node with various lightning_start values (v0.6.1 feature)."""
def setup_method(self):
"""Set up test fixtures."""
self.simple_node = TripleKSampler()
# Create proper model mocks
self.mock_base_high = MagicMock()
self.mock_base_high.model.model_config.sampling_settings = {
"shift": 1.0,
"multiplier": 1000,
}
self.mock_lightning_high = MagicMock()
self.mock_lightning_high.model.model_config.sampling_settings = {
"shift": 1.0,
"multiplier": 1000,
}
self.mock_lightning_low = MagicMock()
self.mock_lightning_low.model.model_config.sampling_settings = {
"shift": 1.0,
"multiplier": 1000,
}
self.mock_latent = {"samples": torch.randn(1, 4, 64, 64)}
def test_simple_node_lightning_start_0(self):
"""Test Simple node with lightning_start=0 (lightning-only mode)."""
params = {
"base_high": self.mock_base_high,
"lightning_high": self.mock_lightning_high,
"lightning_low": self.mock_lightning_low,
"positive": MagicMock(),
"negative": MagicMock(),
"latent_image": self.mock_latent,
"seed": 42,
"sigma_shift": 5.0,
"base_steps": 3, # Required but ignored
"base_quality_threshold": -1,
"base_cfg": 3.5,
"lightning_start": 0, # Lightning-only mode
"lightning_steps": 8,
"lightning_cfg": 1.0, # Required but ignored
"sampler_name": "euler",
"scheduler": "simple",
"switch_strategy": "50% of steps",
}
# Use dry run mode to test lightning-only mode
params["dry_run"] = True
with pytest.raises(comfy.model_management.InterruptProcessingException):
self.simple_node.sample(**params)
def test_simple_node_lightning_start_1(self):
"""Test Simple node with lightning_start=1 (default)."""
params = {
"base_high": self.mock_base_high,
"lightning_high": self.mock_lightning_high,
"lightning_low": self.mock_lightning_low,
"positive": MagicMock(),
"negative": MagicMock(),
"latent_image": self.mock_latent,
"seed": 42,
"sigma_shift": 5.0,
"base_steps": 3, # Required but ignored
"base_quality_threshold": -1,
"base_cfg": 3.5,
"lightning_start": 1, # Default value
"lightning_steps": 8,
"lightning_cfg": 1.0, # Required but ignored
"sampler_name": "euler",
"scheduler": "simple",
"switch_strategy": "50% of steps",
}
# Use dry run mode to test default lightning_start
params["dry_run"] = True
with pytest.raises(comfy.model_management.InterruptProcessingException):
self.simple_node.sample(**params)
def test_simple_node_lightning_start_higher_values(self):
"""Test Simple node with higher lightning_start values."""
# Test with valid combinations that pass validation
test_cases = [
{"lightning_start": 2, "lightning_steps": 8}, # switch_step=4, 2 < 4 ✓
{"lightning_start": 3, "lightning_steps": 8}, # switch_step=4, 3 < 4 ✓
{"lightning_start": 2, "lightning_steps": 12}, # switch_step=6, 2 < 6 ✓
]
for case in test_cases:
params = {
"base_high": self.mock_base_high,
"lightning_high": self.mock_lightning_high,
"lightning_low": self.mock_lightning_low,
"positive": MagicMock(),
"negative": MagicMock(),
"latent_image": self.mock_latent,
"seed": 42,
"sigma_shift": 5.0,
"base_steps": 3, # Required but ignored
"base_cfg": 3.5,
"lightning_start": case["lightning_start"],
"lightning_steps": case["lightning_steps"],
"lightning_cfg": 1.0, # Required but ignored
"sampler_name": "euler",
"scheduler": "simple",
"switch_strategy": "50% of steps",
}
# Use dry run mode to test higher lightning_start values
params["dry_run"] = True
with pytest.raises(comfy.model_management.InterruptProcessingException):
self.simple_node.sample(**params)
def test_simple_node_lightning_start_validation(self):
"""Test Simple node lightning_start validation."""
params = {
"base_high": self.mock_base_high,
"lightning_high": self.mock_lightning_high,
"lightning_low": self.mock_lightning_low,
"positive": MagicMock(),
"negative": MagicMock(),
"latent_image": self.mock_latent,
"seed": 42,
"sigma_shift": 5.0,
"base_steps": 3, # Required but ignored
"base_quality_threshold": -1,
"base_cfg": 3.5,
"lightning_start": 8, # Invalid: >= lightning_steps
"lightning_steps": 8,
"lightning_cfg": 1.0, # Required but ignored
"sampler_name": "euler",
"scheduler": "simple",
"switch_strategy": "50% of steps",
}
# Should raise validation error
with pytest.raises(ValueError, match="lightning_start must be within"):
self.simple_node.sample(**params)
@pytest.mark.skipif(not COMFYUI_AVAILABLE, reason="ComfyUI dependencies not available")
class TestDryRunMode:
"""Test dry run mode functionality."""
def setup_method(self):
"""Set up test fixtures."""
self.advanced_node = TripleKSamplerAdvanced()
# Create proper model mocks
self.mock_base_high = MagicMock()
self.mock_base_high.model.model_config.sampling_settings = {
"shift": 1.0,
"multiplier": 1000,
}
self.mock_lightning_high = MagicMock()
self.mock_lightning_high.model.model_config.sampling_settings = {
"shift": 1.0,
"multiplier": 1000,
}
self.mock_lightning_low = MagicMock()
self.mock_lightning_low.model.model_config.sampling_settings = {
"shift": 1.0,
"multiplier": 1000,
}
self.mock_latent = {"samples": torch.randn(1, 4, 64, 64)}
self.base_params = {
"base_high": self.mock_base_high,
"lightning_high": self.mock_lightning_high,
"lightning_low": self.mock_lightning_low,
"positive": MagicMock(),
"negative": MagicMock(),
"latent_image": self.mock_latent,
"seed": 42,
"sigma_shift": 5.0,
"base_steps": 3,
"base_quality_threshold": 20,
"base_cfg": 3.5,
"base_sampler": "euler",
"base_scheduler": "simple",
"lightning_start": 1,
"lightning_steps": 8,
"lightning_cfg": 1.0,
"lightning_sampler": "euler",
"lightning_scheduler": "simple",
"switch_strategy": "50% of steps",
"switch_boundary": 0.875,
"switch_step": -1,
}
def test_dry_run_all_strategies(self):
"""Test dry run mode with all switching strategies."""
from unittest.mock import patch
import torch
strategies = [
"50% of steps",
"Manual switch step",
"T2V boundary",
"I2V boundary",
"Manual boundary",
]
# Mock sigma calculation for boundary strategies
mock_sigmas = torch.tensor([10.0, 5.0, 2.5, 1.25, 0.625, 0.312, 0.156, 0.078])
# Mock timestep function for boundary calculations
def mock_timestep(sigma_val):
"""Mock timestep that returns comparable float values for boundary testing."""
sigma = float(sigma_val)
# Return timesteps that work for both T2V (0.875) and I2V (0.900) boundaries
if sigma >= 2.5:
return 950.0 # Above both boundaries (0.95 > 0.900 > 0.875)
else:
return 850.0 # Below both boundaries (0.85 < 0.875 < 0.900)
# Mock the sampling object's timestep method
mock_sampling = MagicMock()
mock_sampling.timestep.side_effect = mock_timestep
with (
patch("comfy.samplers.calculate_sigmas", return_value=mock_sigmas),
patch.object(
self.advanced_node,
"_patch_models_for_sampling",
return_value=(
self.mock_base_high,
self.mock_lightning_high,
self.mock_lightning_low,
),
),
):
# Mock get_model_object to return our mock sampling
self.mock_lightning_high.get_model_object.return_value = mock_sampling
self.mock_lightning_low.get_model_object.return_value = mock_sampling
for strategy in strategies:
params = self.base_params.copy()
params.update(
{
"switch_strategy": strategy,
"switch_step": 4 if strategy == "Manual switch step" else -1,
"dry_run": True,
}
)
# Should complete without error and return minimal 8x8 latent
with pytest.raises(comfy.model_management.InterruptProcessingException):
self.advanced_node.sample(**params)
def test_dry_run_complex_scenarios(self):
"""Test dry run mode with complex parameter scenarios."""
# Lightning-only mode
params = self.base_params.copy()
params.update(
{
"base_steps": 0,
"lightning_start": 0,
"switch_strategy": "50% of steps",
"dry_run": True,
}
)
with pytest.raises(comfy.model_management.InterruptProcessingException):
self.advanced_node.sample(**params)
# Auto-calculation mode
params.update(
{
"base_steps": -1,
"lightning_start": 1,
"switch_strategy": "Manual switch step",
"switch_step": -1,
}
)
with pytest.raises(comfy.model_management.InterruptProcessingException):
self.advanced_node.sample(**params)
def test_dry_run_validation_still_occurs(self):
"""Test that validation still occurs in dry run mode."""
params = self.base_params.copy()
params.update(
{
"lightning_steps": 1, # Invalid: too small
"dry_run": True,
}
)
# Should still raise validation error even in dry run
with pytest.raises(ValueError, match="lightning_steps must be at least 2"):
self.advanced_node.sample(**params)
@pytest.mark.skipif(not COMFYUI_AVAILABLE, reason="ComfyUI dependencies not available")
class TestConfigParameterBoundaries:
"""Test edge cases for configuration parameters."""
def setup_method(self):
"""Set up test fixtures."""
self.advanced_node = TripleKSamplerAdvanced()
# Create proper model mocks
self.mock_base_high = MagicMock()
self.mock_base_high.model.model_config.sampling_settings = {
"shift": 1.0,
"multiplier": 1000,
}
self.mock_lightning_high = MagicMock()
self.mock_lightning_high.model.model_config.sampling_settings = {
"shift": 1.0,
"multiplier": 1000,
}
self.mock_lightning_low = MagicMock()
self.mock_lightning_low.model.model_config.sampling_settings = {
"shift": 1.0,
"multiplier": 1000,
}
self.mock_latent = {"samples": torch.randn(1, 4, 64, 64)}
def test_sigma_shift_edge_cases(self):
"""Test sigma_shift with edge values."""
edge_values = [0.0, 0.1, 1.0, 10.0, 100.0]
for sigma_shift in edge_values:
params = {
"base_high": self.mock_base_high,
"lightning_high": self.mock_lightning_high,
"lightning_low": self.mock_lightning_low,
"positive": MagicMock(),
"negative": MagicMock(),
"latent_image": self.mock_latent,
"seed": 42,
"sigma_shift": sigma_shift,
"base_steps": 3,
"base_quality_threshold": 20, # Add missing parameter
"base_cfg": 3.5,
"lightning_start": 1,
"lightning_steps": 8,
"lightning_cfg": 1.0,
"base_sampler": "euler",
"base_scheduler": "simple",
"lightning_sampler": "euler",
"lightning_scheduler": "simple",
"switch_strategy": "50% of steps",
"switch_boundary": 0.875,
"switch_step": -1,
"dry_run": True,
}
# Should handle all sigma_shift values gracefully
with pytest.raises(comfy.model_management.InterruptProcessingException):
self.advanced_node.sample(**params)
def test_cfg_extreme_values(self):
"""Test CFG with extreme values."""
cfg_values = [0.0, 0.1, 1.0, 10.0, 50.0, 100.0]
for base_cfg in cfg_values:
for lightning_cfg in cfg_values:
params = {
"base_high": self.mock_base_high,
"lightning_high": self.mock_lightning_high,
"lightning_low": self.mock_lightning_low,
"positive": MagicMock(),
"negative": MagicMock(),
"latent_image": self.mock_latent,
"seed": 42,
"sigma_shift": 5.0,
"base_steps": 3,
"base_quality_threshold": 20, # Add missing parameter
"base_cfg": base_cfg,
"lightning_start": 1,
"lightning_steps": 8,
"lightning_cfg": lightning_cfg,
"base_sampler": "euler",
"base_scheduler": "simple",
"lightning_sampler": "euler",
"lightning_scheduler": "simple",
"switch_strategy": "50% of steps",
"switch_boundary": 0.875,
"switch_step": -1,
"dry_run": True,
}
with pytest.raises(comfy.model_management.InterruptProcessingException):
self.advanced_node.sample(**params)
def test_large_step_counts(self):
"""Test with very large step counts."""
large_steps = [50, 100, 200]
for lightning_steps in large_steps:
params = {
"base_high": self.mock_base_high,
"lightning_high": self.mock_lightning_high,
"lightning_low": self.mock_lightning_low,
"positive": MagicMock(),
"negative": MagicMock(),
"latent_image": self.mock_latent,
"seed": 42,
"sigma_shift": 5.0,
"base_steps": 10,
"base_quality_threshold": 30, # Add missing parameter
"base_cfg": 3.5,
"lightning_start": 5,
"lightning_steps": lightning_steps,
"lightning_cfg": 1.0,
"base_sampler": "euler",
"base_scheduler": "simple",
"lightning_sampler": "euler",
"lightning_scheduler": "simple",
"switch_strategy": "50% of steps",
"switch_boundary": 0.875,
"switch_step": -1,
"dry_run": True,
}
with pytest.raises(comfy.model_management.InterruptProcessingException):
self.advanced_node.sample(**params)
def test_switch_boundary_full_range(self):
"""Test switch_boundary across full valid range."""
from unittest.mock import patch
import torch
# Mock sigma calculation for boundary strategies
torch.tensor([10.0, 5.0, 2.5, 1.25, 0.625, 0.312, 0.156, 0.078])
# Test strategy UI visibility
strategies_test_cases = [
("50% of steps", False, False),
("Manual switch step", True, False),
("T2V boundary", False, False),
("I2V boundary", False, False),
("Manual boundary", False, True),
]
for strategy, should_show_step, should_show_boundary in strategies_test_cases:
# Simulate the JavaScript logic
show_switch_step = False
show_switch_boundary = False
if strategy == "50% of steps":
show_switch_step = False
show_switch_boundary = False
elif strategy == "Manual switch step":
show_switch_step = True
show_switch_boundary = False
elif strategy in ["T2V boundary", "I2V boundary"]:
show_switch_step = False
show_switch_boundary = False
elif strategy == "Manual boundary":
show_switch_step = False
show_switch_boundary = True
assert show_switch_step == should_show_step, f"Failed for strategy: {strategy}"
assert show_switch_boundary == should_show_boundary, f"Failed for strategy: {strategy}"
if __name__ == "__main__":
pytest.main([__file__, "-v"])