File size: 9,749 Bytes
b610d23
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
"""Tests for validation service."""

import pytest
import tempfile
import shutil
from pathlib import Path
from PIL import Image
import io

from src.storage.validation_service import ValidationService


class TestValidationService:
    """Test cases for ValidationService class."""
    
    def setup_method(self):
        """Set up test fixtures."""
        self.temp_dir = Path(tempfile.mkdtemp())
        self.validator = ValidationService()
    
    def teardown_method(self):
        """Clean up temporary directory."""
        shutil.rmtree(self.temp_dir)
    
    def create_test_image(self, filename: str, width: int = 512, height: int = 512, format: str = 'JPEG') -> Path:
        """Create a test image file."""
        file_path = self.temp_dir / filename
        
        # Create a simple test image
        img = Image.new('RGB', (width, height), color='red')
        img.save(file_path, format)
        
        return file_path
    
    def create_corrupted_file(self, filename: str, content: bytes = b"not an image") -> Path:
        """Create a corrupted file."""
        file_path = self.temp_dir / filename
        file_path.write_bytes(content)
        return file_path
    
    def test_validate_image_format_valid_jpeg(self):
        """Test validation of valid JPEG image."""
        image_path = self.create_test_image("test.jpg", format='JPEG')
        
        is_valid, error = self.validator.validate_image_format(image_path)
        
        assert is_valid is True
        assert error is None
    
    def test_validate_image_format_invalid_format(self):
        """Test validation of non-JPEG image."""
        image_path = self.create_test_image("test.png", format='PNG')
        
        is_valid, error = self.validator.validate_image_format(image_path)
        
        assert is_valid is False
        assert "Expected JPEG format, got PNG" in error
    
    def test_validate_image_format_corrupted_file(self):
        """Test validation of corrupted file."""
        corrupted_path = self.create_corrupted_file("corrupted.jpg")
        
        is_valid, error = self.validator.validate_image_format(corrupted_path)
        
        assert is_valid is False
        assert error is not None
    
    def test_calculate_file_hash(self):
        """Test file hash calculation."""
        image_path = self.create_test_image("test.jpg")
        
        hash1 = self.validator.calculate_file_hash(image_path, 'md5')
        hash2 = self.validator.calculate_file_hash(image_path, 'md5')
        
        assert hash1 is not None
        assert hash1 == hash2  # Same file should have same hash
        assert len(hash1) == 32  # MD5 hash length
    
    def test_calculate_file_hash_different_algorithms(self):
        """Test different hash algorithms."""
        image_path = self.create_test_image("test.jpg")
        
        md5_hash = self.validator.calculate_file_hash(image_path, 'md5')
        sha256_hash = self.validator.calculate_file_hash(image_path, 'sha256')
        
        assert md5_hash is not None
        assert sha256_hash is not None
        assert len(md5_hash) == 32  # MD5
        assert len(sha256_hash) == 64  # SHA256
        assert md5_hash != sha256_hash
    
    def test_validate_file_size_exact_match(self):
        """Test file size validation with exact match."""
        image_path = self.create_test_image("test.jpg")
        actual_size = image_path.stat().st_size
        
        is_valid, error = self.validator.validate_file_size(image_path, actual_size)
        
        assert is_valid is True
        assert error is None
    
    def test_validate_file_size_within_tolerance(self):
        """Test file size validation within tolerance."""
        image_path = self.create_test_image("test.jpg")
        actual_size = image_path.stat().st_size
        
        is_valid, error = self.validator.validate_file_size(image_path, actual_size + 5, tolerance=10)
        
        assert is_valid is True
        assert error is None
    
    def test_validate_file_size_outside_tolerance(self):
        """Test file size validation outside tolerance."""
        image_path = self.create_test_image("test.jpg")
        actual_size = image_path.stat().st_size
        
        is_valid, error = self.validator.validate_file_size(image_path, actual_size + 100, tolerance=10)
        
        assert is_valid is False
        assert "Size mismatch" in error
    
    def test_validate_image_content_valid(self):
        """Test validation of valid image content."""
        image_path = self.create_test_image("test.jpg", width=1024, height=768)
        
        is_valid, error = self.validator.validate_image_content(image_path)
        
        assert is_valid is True
        assert error is None
    
    def test_validate_image_content_too_small(self):
        """Test validation of image that's too small."""
        image_path = self.create_test_image("small.jpg", width=50, height=50)
        
        is_valid, error = self.validator.validate_image_content(image_path)
        
        assert is_valid is False
        assert "Image too small" in error
    
    def test_validate_image_content_too_large(self):
        """Test validation of image that's too large."""
        # Note: We won't actually create a 15000x15000 image for performance reasons
        # Instead, we'll mock the Image.open to return large dimensions
        from unittest.mock import patch, MagicMock
        
        image_path = self.create_test_image("test.jpg")
        
        with patch('PIL.Image.open') as mock_open:
            mock_img = MagicMock()
            mock_img.size = (15000, 15000)
            mock_img.mode = 'RGB'
            mock_open.return_value.__enter__.return_value = mock_img
            
            is_valid, error = self.validator.validate_image_content(image_path)
            
            assert is_valid is False
            assert "Image too large" in error
    
    def test_comprehensive_validation_success(self):
        """Test comprehensive validation of valid image."""
        image_path = self.create_test_image("test.jpg")
        expected_size = image_path.stat().st_size
        
        all_valid, errors = self.validator.comprehensive_validation(image_path, expected_size)
        
        assert all_valid is True
        assert len(errors) == 0
    
    def test_comprehensive_validation_missing_file(self):
        """Test comprehensive validation of missing file."""
        missing_path = self.temp_dir / "missing.jpg"
        
        all_valid, errors = self.validator.comprehensive_validation(missing_path)
        
        assert all_valid is False
        assert len(errors) == 1
        assert "File does not exist" in errors[0]
    
    def test_comprehensive_validation_empty_file(self):
        """Test comprehensive validation of empty file."""
        empty_path = self.temp_dir / "empty.jpg"
        empty_path.write_bytes(b"")
        
        all_valid, errors = self.validator.comprehensive_validation(empty_path)
        
        assert all_valid is False
        assert any("File is empty" in error for error in errors)
    
    def test_comprehensive_validation_multiple_errors(self):
        """Test comprehensive validation with multiple errors."""
        corrupted_path = self.create_corrupted_file("corrupted.jpg")
        
        all_valid, errors = self.validator.comprehensive_validation(corrupted_path, expected_size=1000000)
        
        assert all_valid is False
        assert len(errors) >= 2  # Size mismatch + format error
    
    def test_get_image_info_success(self):
        """Test getting image information."""
        image_path = self.create_test_image("test.jpg", width=800, height=600)
        
        info = self.validator.get_image_info(image_path)
        
        assert info is not None
        assert info['filename'] == 'test.jpg'
        assert info['format'] == 'JPEG'
        assert info['width'] == 800
        assert info['height'] == 600
        assert info['size'] == (800, 600)
        assert info['file_size'] > 0
        assert 'has_transparency' in info
        assert 'has_exif' in info
    
    def test_get_image_info_corrupted_file(self):
        """Test getting image information from corrupted file."""
        corrupted_path = self.create_corrupted_file("corrupted.jpg")
        
        info = self.validator.get_image_info(corrupted_path)
        
        assert info is None
    
    def test_repair_corrupted_image_success(self):
        """Test successful image repair."""
        # Skip this test on Windows due to file locking issues
        import platform
        if platform.system() == "Windows":
            pytest.skip("Skipping repair test on Windows due to file locking")
        
        # Create a valid image first
        image_path = self.create_test_image("test.jpg")
        
        # For this test, we'll assume the image can be "repaired"
        # In reality, this would work on images with minor corruption
        success = self.validator.repair_corrupted_image(image_path)
        
        # The repair should succeed for a valid image
        assert success is True
        assert image_path.exists()
    
    def test_repair_corrupted_image_failure(self):
        """Test failed image repair."""
        corrupted_path = self.create_corrupted_file("corrupted.jpg")
        
        success = self.validator.repair_corrupted_image(corrupted_path)
        
        assert success is False