test_validation.py•8.85 kB
"""Tests for validation module."""
import pytest
from pathlib import Path
from aseprite_mcp.core.validation import (
validate_file_path,
validate_color,
validate_dimensions,
validate_coordinates,
validate_export_format,
sanitize_lua_string,
validate_layer_name
)
from aseprite_mcp.core.exceptions import ValidationError, SecurityError
class TestValidateFilePath:
"""Tests for validate_file_path function."""
def test_valid_path(self, tmp_path):
"""Test validation of valid paths."""
# Create a test file
test_file = tmp_path / "test.aseprite"
test_file.touch()
# Should validate existing file
result = validate_file_path(str(test_file), must_exist=True)
assert result == test_file
# Should validate new file path
new_file = tmp_path / "new.aseprite"
result = validate_file_path(str(new_file), must_exist=False)
assert result == new_file
def test_empty_path(self):
"""Test that empty path raises ValidationError."""
with pytest.raises(ValidationError) as excinfo:
validate_file_path("")
assert "Path cannot be empty" in str(excinfo.value)
def test_path_traversal(self):
"""Test that path traversal attempts raise SecurityError."""
with pytest.raises(SecurityError) as excinfo:
validate_file_path("../../../etc/passwd")
assert "Path traversal detected" in str(excinfo.value)
def test_must_exist(self, tmp_path):
"""Test must_exist validation."""
non_existent = tmp_path / "does_not_exist.aseprite"
with pytest.raises(ValidationError) as excinfo:
validate_file_path(str(non_existent), must_exist=True)
assert "File does not exist" in str(excinfo.value)
def test_parent_directory_must_exist(self, tmp_path):
"""Test that parent directory must exist for new files."""
new_file = tmp_path / "nonexistent_dir" / "file.aseprite"
with pytest.raises(ValidationError) as excinfo:
validate_file_path(str(new_file), must_exist=False)
assert "Parent directory does not exist" in str(excinfo.value)
class TestValidateColor:
"""Tests for validate_color function."""
def test_valid_colors(self):
"""Test validation of valid color formats."""
assert validate_color("#FF0000") == "FF0000"
assert validate_color("FF0000") == "FF0000"
assert validate_color("#ff0000") == "FF0000"
assert validate_color("00ff00") == "00FF00"
def test_empty_color(self):
"""Test that empty color raises ValidationError."""
with pytest.raises(ValidationError) as excinfo:
validate_color("")
assert "Color cannot be empty" in str(excinfo.value)
def test_invalid_format(self):
"""Test that invalid formats raise ValidationError."""
invalid_colors = [
"red",
"#FF00",
"GGGGGG",
"#FF00GG",
"12345",
"#1234567"
]
for color in invalid_colors:
with pytest.raises(ValidationError) as excinfo:
validate_color(color)
assert "6-character hex value" in str(excinfo.value)
class TestValidateDimensions:
"""Tests for validate_dimensions function."""
def test_valid_dimensions(self):
"""Test validation of valid dimensions."""
assert validate_dimensions(100, 200) == (100, 200)
assert validate_dimensions(1, 1) == (1, 1)
assert validate_dimensions(1000, 2000) == (1000, 2000)
def test_zero_dimensions(self):
"""Test that zero dimensions raise ValidationError."""
with pytest.raises(ValidationError) as excinfo:
validate_dimensions(0, 100)
assert "Width must be positive" in str(excinfo.value)
with pytest.raises(ValidationError) as excinfo:
validate_dimensions(100, 0)
assert "Height must be positive" in str(excinfo.value)
def test_negative_dimensions(self):
"""Test that negative dimensions raise ValidationError."""
with pytest.raises(ValidationError) as excinfo:
validate_dimensions(-10, 100)
assert "Width must be positive" in str(excinfo.value)
def test_max_size(self):
"""Test maximum size validation."""
# Should work with default max
validate_dimensions(10000, 10000)
# Should fail when exceeding max
with pytest.raises(ValidationError) as excinfo:
validate_dimensions(10001, 100)
assert "Width exceeds maximum" in str(excinfo.value)
# Custom max size
with pytest.raises(ValidationError) as excinfo:
validate_dimensions(101, 100, max_size=100)
assert "Width exceeds maximum of 100" in str(excinfo.value)
class TestValidateCoordinates:
"""Tests for validate_coordinates function."""
def test_valid_coordinates(self):
"""Test validation of valid coordinates."""
assert validate_coordinates(0, 0, 100, 100) == (0, 0)
assert validate_coordinates(50, 50, 100, 100) == (50, 50)
assert validate_coordinates(99, 99, 100, 100) == (99, 99)
def test_out_of_bounds(self):
"""Test out of bounds coordinates."""
with pytest.raises(ValidationError) as excinfo:
validate_coordinates(-1, 0, 100, 100)
assert "X coordinate must be between" in str(excinfo.value)
with pytest.raises(ValidationError) as excinfo:
validate_coordinates(100, 0, 100, 100)
assert "X coordinate must be between 0 and 99" in str(excinfo.value)
with pytest.raises(ValidationError) as excinfo:
validate_coordinates(0, -1, 100, 100)
assert "Y coordinate must be between" in str(excinfo.value)
class TestValidateExportFormat:
"""Tests for validate_export_format function."""
def test_valid_formats(self):
"""Test validation of valid export formats."""
valid_formats = ['png', 'gif', 'jpg', 'jpeg', 'bmp', 'tiff', 'webp']
for fmt in valid_formats:
assert validate_export_format(fmt) == fmt
assert validate_export_format(fmt.upper()) == fmt
def test_invalid_format(self):
"""Test invalid export formats."""
with pytest.raises(ValidationError) as excinfo:
validate_export_format("pdf")
assert "Format must be one of" in str(excinfo.value)
class TestSanitizeLuaString:
"""Tests for sanitize_lua_string function."""
def test_basic_strings(self):
"""Test sanitization of basic strings."""
assert sanitize_lua_string("hello") == "hello"
assert sanitize_lua_string("hello world") == "hello world"
def test_quotes_escaping(self):
"""Test escaping of quotes."""
assert sanitize_lua_string('hello "world"') == 'hello \\"world\\"'
assert sanitize_lua_string("test's string") == "test's string"
def test_backslash_escaping(self):
"""Test escaping of backslashes."""
assert sanitize_lua_string("C:\\path\\to\\file") == "C:\\\\path\\\\to\\\\file"
assert sanitize_lua_string("line1\\nline2") == "line1\\\\nline2"
class TestValidateLayerName:
"""Tests for validate_layer_name function."""
def test_valid_names(self):
"""Test validation of valid layer names."""
assert validate_layer_name("Layer 1") == "Layer 1"
assert validate_layer_name("Background") == "Background"
assert validate_layer_name("my-layer_123") == "my-layer_123"
def test_empty_name(self):
"""Test that empty name raises ValidationError."""
with pytest.raises(ValidationError) as excinfo:
validate_layer_name("")
assert "Layer name cannot be empty" in str(excinfo.value)
def test_long_name(self):
"""Test that overly long names raise ValidationError."""
with pytest.raises(ValidationError) as excinfo:
validate_layer_name("a" * 256)
assert "Layer name too long" in str(excinfo.value)
def test_invalid_characters(self):
"""Test that invalid characters raise ValidationError."""
invalid_names = [
"layer/name",
"layer\\name",
"layer:name",
"layer*name",
"layer?name",
'layer"name',
"layer<name>",
"layer|name"
]
for name in invalid_names:
with pytest.raises(ValidationError) as excinfo:
validate_layer_name(name)
assert "Layer name cannot contain" in str(excinfo.value)