278 lines
7.9 KiB
Python
278 lines
7.9 KiB
Python
"""
|
|
Unit tests for interaction command factory.
|
|
"""
|
|
|
|
import pytest
|
|
from unittest.mock import Mock
|
|
from pyPhotoAlbum.mixins.interaction_command_factory import (
|
|
InteractionCommandFactory,
|
|
InteractionState
|
|
)
|
|
from pyPhotoAlbum.mixins.interaction_command_builders import CommandBuilder
|
|
|
|
|
|
class TestInteractionState:
|
|
"""Tests for InteractionState value object."""
|
|
|
|
def test_initialization(self):
|
|
"""Test that InteractionState initializes correctly."""
|
|
element = Mock()
|
|
state = InteractionState(
|
|
element=element,
|
|
interaction_type='move',
|
|
position=(0.0, 0.0),
|
|
size=(100.0, 100.0),
|
|
rotation=0.0
|
|
)
|
|
|
|
assert state.element == element
|
|
assert state.interaction_type == 'move'
|
|
assert state.position == (0.0, 0.0)
|
|
assert state.size == (100.0, 100.0)
|
|
assert state.rotation == 0.0
|
|
|
|
def test_to_dict(self):
|
|
"""Test that to_dict returns correct dictionary."""
|
|
state = InteractionState(
|
|
position=(0.0, 0.0),
|
|
size=(100.0, 100.0)
|
|
)
|
|
|
|
result = state.to_dict()
|
|
|
|
assert result == {
|
|
'position': (0.0, 0.0),
|
|
'size': (100.0, 100.0)
|
|
}
|
|
|
|
def test_to_dict_excludes_none(self):
|
|
"""Test that to_dict excludes None values."""
|
|
state = InteractionState(
|
|
position=(0.0, 0.0),
|
|
size=None
|
|
)
|
|
|
|
result = state.to_dict()
|
|
|
|
assert 'position' in result
|
|
assert 'size' not in result
|
|
|
|
def test_is_valid_with_required_fields(self):
|
|
"""Test that is_valid returns True when required fields are present."""
|
|
element = Mock()
|
|
state = InteractionState(
|
|
element=element,
|
|
interaction_type='move'
|
|
)
|
|
|
|
assert state.is_valid()
|
|
|
|
def test_is_valid_without_element(self):
|
|
"""Test that is_valid returns False without element."""
|
|
state = InteractionState(
|
|
element=None,
|
|
interaction_type='move'
|
|
)
|
|
|
|
assert not state.is_valid()
|
|
|
|
def test_is_valid_without_interaction_type(self):
|
|
"""Test that is_valid returns False without interaction_type."""
|
|
element = Mock()
|
|
state = InteractionState(
|
|
element=element,
|
|
interaction_type=None
|
|
)
|
|
|
|
assert not state.is_valid()
|
|
|
|
def test_clear(self):
|
|
"""Test that clear resets all fields."""
|
|
element = Mock()
|
|
state = InteractionState(
|
|
element=element,
|
|
interaction_type='move',
|
|
position=(0.0, 0.0),
|
|
size=(100.0, 100.0),
|
|
rotation=0.0
|
|
)
|
|
|
|
state.clear()
|
|
|
|
assert state.element is None
|
|
assert state.interaction_type is None
|
|
assert state.position is None
|
|
assert state.size is None
|
|
assert state.rotation is None
|
|
|
|
|
|
class TestInteractionCommandFactory:
|
|
"""Tests for InteractionCommandFactory."""
|
|
|
|
def test_initialization_registers_default_builders(self):
|
|
"""Test that factory initializes with default builders."""
|
|
factory = InteractionCommandFactory()
|
|
|
|
assert factory.has_builder('move')
|
|
assert factory.has_builder('resize')
|
|
assert factory.has_builder('rotate')
|
|
assert factory.has_builder('image_pan')
|
|
|
|
def test_register_builder(self):
|
|
"""Test registering a custom builder."""
|
|
factory = InteractionCommandFactory()
|
|
custom_builder = Mock(spec=CommandBuilder)
|
|
|
|
factory.register_builder('custom', custom_builder)
|
|
|
|
assert factory.has_builder('custom')
|
|
|
|
def test_get_supported_types(self):
|
|
"""Test getting list of supported types."""
|
|
factory = InteractionCommandFactory()
|
|
|
|
types = factory.get_supported_types()
|
|
|
|
assert 'move' in types
|
|
assert 'resize' in types
|
|
assert 'rotate' in types
|
|
assert 'image_pan' in types
|
|
|
|
def test_create_command_move(self):
|
|
"""Test creating a move command."""
|
|
factory = InteractionCommandFactory()
|
|
element = Mock()
|
|
element.position = (10.0, 10.0)
|
|
|
|
start_state = {'position': (0.0, 0.0)}
|
|
|
|
command = factory.create_command('move', element, start_state)
|
|
|
|
assert command is not None
|
|
|
|
def test_create_command_resize(self):
|
|
"""Test creating a resize command."""
|
|
factory = InteractionCommandFactory()
|
|
element = Mock()
|
|
element.position = (10.0, 10.0)
|
|
element.size = (200.0, 200.0)
|
|
|
|
start_state = {
|
|
'position': (0.0, 0.0),
|
|
'size': (100.0, 100.0)
|
|
}
|
|
|
|
command = factory.create_command('resize', element, start_state)
|
|
|
|
assert command is not None
|
|
|
|
def test_create_command_rotate(self):
|
|
"""Test creating a rotate command."""
|
|
factory = InteractionCommandFactory()
|
|
element = Mock()
|
|
element.rotation = 45.0
|
|
|
|
start_state = {'rotation': 0.0}
|
|
|
|
command = factory.create_command('rotate', element, start_state)
|
|
|
|
assert command is not None
|
|
|
|
def test_create_command_unknown_type(self, capsys):
|
|
"""Test creating command with unknown type."""
|
|
factory = InteractionCommandFactory()
|
|
element = Mock()
|
|
|
|
command = factory.create_command('unknown', element, {})
|
|
|
|
assert command is None
|
|
captured = capsys.readouterr()
|
|
assert "No builder registered for interaction type 'unknown'" in captured.out
|
|
|
|
def test_create_command_no_significant_change(self):
|
|
"""Test that no command is created for insignificant changes."""
|
|
factory = InteractionCommandFactory()
|
|
element = Mock()
|
|
element.position = (0.05, 0.05)
|
|
|
|
start_state = {'position': (0.0, 0.0)}
|
|
|
|
command = factory.create_command('move', element, start_state)
|
|
|
|
assert command is None
|
|
|
|
def test_create_command_with_custom_builder(self):
|
|
"""Test using a custom builder."""
|
|
factory = InteractionCommandFactory()
|
|
|
|
# Create a mock builder that always returns a mock command
|
|
custom_builder = Mock(spec=CommandBuilder)
|
|
mock_command = Mock()
|
|
custom_builder.can_build.return_value = True
|
|
custom_builder.build.return_value = mock_command
|
|
|
|
factory.register_builder('custom', custom_builder)
|
|
|
|
element = Mock()
|
|
start_state = {'position': (0.0, 0.0)}
|
|
|
|
command = factory.create_command('custom', element, start_state)
|
|
|
|
assert command == mock_command
|
|
custom_builder.can_build.assert_called_once()
|
|
custom_builder.build.assert_called_once()
|
|
|
|
|
|
class TestInteractionStateIntegration:
|
|
"""Integration tests for InteractionState with factory."""
|
|
|
|
def test_state_to_dict_with_factory(self):
|
|
"""Test that state.to_dict() works with factory."""
|
|
factory = InteractionCommandFactory()
|
|
element = Mock()
|
|
element.position = (10.0, 10.0)
|
|
|
|
state = InteractionState(
|
|
element=element,
|
|
interaction_type='move',
|
|
position=(0.0, 0.0)
|
|
)
|
|
|
|
command = factory.create_command(
|
|
state.interaction_type,
|
|
state.element,
|
|
state.to_dict()
|
|
)
|
|
|
|
assert command is not None
|
|
|
|
def test_state_lifecycle(self):
|
|
"""Test complete lifecycle of interaction state."""
|
|
element = Mock()
|
|
element.position = (0.0, 0.0)
|
|
|
|
# Begin interaction
|
|
state = InteractionState()
|
|
state.element = element
|
|
state.interaction_type = 'move'
|
|
state.position = element.position
|
|
|
|
assert state.is_valid()
|
|
|
|
# Simulate movement
|
|
element.position = (10.0, 10.0)
|
|
|
|
# Create command
|
|
factory = InteractionCommandFactory()
|
|
command = factory.create_command(
|
|
state.interaction_type,
|
|
state.element,
|
|
state.to_dict()
|
|
)
|
|
|
|
assert command is not None
|
|
|
|
# Clear state
|
|
state.clear()
|
|
assert not state.is_valid()
|