""" Unit tests for interaction command builders. """ import pytest from unittest.mock import Mock, MagicMock from pyPhotoAlbum.mixins.interaction_command_builders import ( MoveCommandBuilder, ResizeCommandBuilder, RotateCommandBuilder, ImagePanCommandBuilder, ) from pyPhotoAlbum.mixins.interaction_validators import InteractionChangeDetector class TestMoveCommandBuilder: """Tests for MoveCommandBuilder.""" def test_can_build_with_significant_change(self): """Test that can_build returns True for significant position changes.""" builder = MoveCommandBuilder() element = Mock() element.position = (10.0, 10.0) start_state = {"position": (0.0, 0.0)} assert builder.can_build(element, start_state) def test_can_build_with_insignificant_change(self): """Test that can_build returns False for insignificant changes.""" builder = MoveCommandBuilder() element = Mock() element.position = (0.05, 0.05) start_state = {"position": (0.0, 0.0)} assert not builder.can_build(element, start_state) def test_can_build_with_no_position(self): """Test that can_build returns False when no position in start_state.""" builder = MoveCommandBuilder() element = Mock() element.position = (10.0, 10.0) start_state = {} assert not builder.can_build(element, start_state) def test_build_creates_command(self): """Test that build creates a MoveElementCommand.""" builder = MoveCommandBuilder() element = Mock() element.position = (10.0, 10.0) start_state = {"position": (0.0, 0.0)} command = builder.build(element, start_state) assert command is not None assert command.element == element def test_build_returns_none_for_insignificant_change(self): """Test that build returns None for insignificant changes.""" builder = MoveCommandBuilder() element = Mock() element.position = (0.05, 0.05) start_state = {"position": (0.0, 0.0)} command = builder.build(element, start_state) assert command is None class TestResizeCommandBuilder: """Tests for ResizeCommandBuilder.""" def test_can_build_with_size_change(self): """Test that can_build returns True when size changes.""" builder = ResizeCommandBuilder() element = Mock() element.position = (0.0, 0.0) element.size = (200.0, 200.0) start_state = {"position": (0.0, 0.0), "size": (100.0, 100.0)} assert builder.can_build(element, start_state) def test_can_build_with_position_change(self): """Test that can_build returns True when position changes.""" builder = ResizeCommandBuilder() element = Mock() element.position = (10.0, 10.0) element.size = (100.0, 100.0) start_state = {"position": (0.0, 0.0), "size": (100.0, 100.0)} assert builder.can_build(element, start_state) def test_can_build_with_both_changes(self): """Test that can_build returns True when both position and size change.""" builder = ResizeCommandBuilder() 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)} assert builder.can_build(element, start_state) def test_can_build_with_no_change(self): """Test that can_build returns False when nothing changes.""" builder = ResizeCommandBuilder() element = Mock() element.position = (0.0, 0.0) element.size = (100.0, 100.0) start_state = {"position": (0.0, 0.0), "size": (100.0, 100.0)} assert not builder.can_build(element, start_state) def test_build_creates_command(self): """Test that build creates a ResizeElementCommand.""" builder = ResizeCommandBuilder() 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 = builder.build(element, start_state) assert command is not None assert command.element == element class TestRotateCommandBuilder: """Tests for RotateCommandBuilder.""" def test_can_build_with_significant_change(self): """Test that can_build returns True for significant rotation changes.""" builder = RotateCommandBuilder() element = Mock() element.rotation = 45.0 start_state = {"rotation": 0.0} assert builder.can_build(element, start_state) def test_can_build_with_insignificant_change(self): """Test that can_build returns False for insignificant changes.""" builder = RotateCommandBuilder() element = Mock() element.rotation = 0.05 start_state = {"rotation": 0.0} assert not builder.can_build(element, start_state) def test_build_creates_command(self): """Test that build creates a RotateElementCommand.""" builder = RotateCommandBuilder() element = Mock() element.rotation = 45.0 start_state = {"rotation": 0.0} command = builder.build(element, start_state) assert command is not None assert command.element == element class TestImagePanCommandBuilder: """Tests for ImagePanCommandBuilder.""" def test_can_build_with_image_data(self): """Test that can_build works with ImageData elements.""" from pyPhotoAlbum.models import ImageData builder = ImagePanCommandBuilder() element = Mock(spec=ImageData) element.crop_info = (0.1, 0.1, 0.9, 0.9) start_state = {"crop_info": (0.0, 0.0, 1.0, 1.0)} assert builder.can_build(element, start_state) def test_can_build_with_non_image_data(self): """Test that can_build returns False for non-ImageData elements.""" builder = ImagePanCommandBuilder() element = Mock() element.crop_info = (0.1, 0.1, 0.9, 0.9) start_state = {"crop_info": (0.0, 0.0, 1.0, 1.0)} assert not builder.can_build(element, start_state) def test_can_build_with_insignificant_change(self): """Test that can_build returns False for insignificant crop changes.""" from pyPhotoAlbum.models import ImageData builder = ImagePanCommandBuilder() element = Mock(spec=ImageData) element.crop_info = (0.0001, 0.0001, 1.0, 1.0) start_state = {"crop_info": (0.0, 0.0, 1.0, 1.0)} assert not builder.can_build(element, start_state) def test_build_creates_command(self): """Test that build creates an AdjustImageCropCommand.""" from pyPhotoAlbum.models import ImageData builder = ImagePanCommandBuilder() element = Mock(spec=ImageData) element.crop_info = (0.1, 0.1, 0.9, 0.9) start_state = {"crop_info": (0.0, 0.0, 1.0, 1.0)} command = builder.build(element, start_state) assert command is not None assert command.element == element class TestCommandBuilderIntegration: """Integration tests for command builders.""" def test_builders_use_custom_detector(self): """Test that builders can use custom change detectors.""" detector = InteractionChangeDetector(threshold=10.0) builder = MoveCommandBuilder(change_detector=detector) element = Mock() element.position = (5.0, 5.0) start_state = {"position": (0.0, 0.0)} # With high threshold, this should not build assert not builder.can_build(element, start_state) def test_builder_logging(self, capsys): """Test that builders log command creation.""" builder = MoveCommandBuilder() element = Mock() element.position = (10.0, 10.0) start_state = {"position": (0.0, 0.0)} builder.build(element, start_state) captured = capsys.readouterr() assert "Move command created" in captured.out