Research-Stack/5-Applications/nodupe/tests/scanner_engine/test_processor.py
2026-05-05 21:15:26 -05:00

441 lines
14 KiB
Python

"""Tests for file processor module."""
from pathlib import Path
from unittest.mock import MagicMock, mock_open, patch
import pytest
class TestFileProcessor:
"""Test FileProcessor class."""
def test_processor_initialization(self):
"""Test processor initialization."""
from nodupe.tools.scanner_engine.processor import FileProcessor
processor = FileProcessor()
assert processor._hash_algorithm == 'sha256'
assert processor._hash_buffer_size == 65536
def test_processor_with_custom_walker(self):
"""Test processor with custom file walker."""
from nodupe.tools.scanner_engine.processor import FileProcessor
from nodupe.tools.scanner_engine.walker import FileWalker
mock_walker = MagicMock(spec=FileWalker)
processor = FileProcessor(file_walker=mock_walker)
assert processor.file_walker is mock_walker
def test_processor_with_custom_hasher(self):
"""Test processor with custom hasher."""
from nodupe.tools.scanner_engine.processor import FileProcessor
mock_hasher = MagicMock()
mock_hasher.hash_file.return_value = "abc123"
mock_hasher.get_algorithm.return_value = "sha256"
mock_hasher.set_algorithm = MagicMock()
processor = FileProcessor(hasher=mock_hasher)
assert processor._hasher is mock_hasher
def test_processor_default_hasher(self):
"""Test processor uses default hasher when none provided."""
from nodupe.tools.scanner_engine.processor import FileProcessor
with patch('nodupe.tools.scanner_engine.processor.global_container') as mock_container:
mock_container.get_service.return_value = None
processor = FileProcessor()
# Should create a default hasher
assert processor._hasher is not None
def test_process_single_file(self, temp_dir):
"""Test processing a single file."""
from nodupe.tools.scanner_engine.processor import FileProcessor
test_file = temp_dir / "test.txt"
test_file.write_text("test content")
processor = FileProcessor()
file_info = {
'path': str(test_file),
'name': 'test.txt',
'extension': '.txt',
'size': 12
}
result = processor._process_single_file(file_info)
assert result is not None
assert 'hash' in result
assert 'hash_algorithm' in result
assert result['is_duplicate'] is False
assert result['duplicate_of'] is None
def test_calculate_file_hash(self, temp_dir):
"""Test file hash calculation."""
from nodupe.tools.scanner_engine.processor import FileProcessor
test_file = temp_dir / "test.txt"
test_file.write_text("test content")
processor = FileProcessor()
hash_value = processor._calculate_file_hash(str(test_file))
assert hash_value is not None
assert len(hash_value) > 0
def test_calculate_file_hash_error(self):
"""Test file hash calculation with error."""
from nodupe.tools.scanner_engine.processor import FileProcessor
processor = FileProcessor()
with pytest.raises(Exception):
processor._calculate_file_hash("/nonexistent/file.txt")
def test_detect_duplicates_empty_list(self):
"""Test detect duplicates with empty list."""
from nodupe.tools.scanner_engine.processor import FileProcessor
processor = FileProcessor()
result = processor.detect_duplicates([])
assert result == []
def test_detect_duplicates_no_duplicates(self):
"""Test detect duplicates with unique files."""
from nodupe.tools.scanner_engine.processor import FileProcessor
processor = FileProcessor()
files = [
{'path': '/file1.txt', 'hash': 'hash1', 'size': 100},
{'path': '/file2.txt', 'hash': 'hash2', 'size': 200},
]
result = processor.detect_duplicates(files)
assert all(not f.get('is_duplicate', False) for f in result)
def test_detect_duplicates_with_duplicates(self, temp_dir):
"""Test detect duplicates with duplicate files."""
from nodupe.tools.scanner_engine.processor import FileProcessor
# Create duplicate files
file1 = temp_dir / "file1.txt"
file2 = temp_dir / "file2.txt"
file1.write_text("same content")
file2.write_text("same content")
processor = FileProcessor()
files = [
{'path': str(file1), 'hash': 'same_hash', 'size': 12},
{'path': str(file2), 'hash': 'same_hash', 'size': 12},
]
result = processor.detect_duplicates(files)
# One should be marked as duplicate
duplicates = [f for f in result if f.get('is_duplicate')]
originals = [f for f in result if not f.get('is_duplicate')]
assert len(duplicates) == 1
assert len(originals) == 1
def test_get_basic_file_info(self, temp_dir):
"""Test getting basic file info."""
from nodupe.tools.scanner_engine.processor import FileProcessor
test_file = temp_dir / "test.txt"
test_file.write_text("test content")
processor = FileProcessor()
info = processor._get_basic_file_info(str(test_file))
assert 'path' in info
assert 'name' in info
assert 'extension' in info
assert 'size' in info
assert 'modified_time' in info
assert 'created_time' in info
assert info['is_file'] is True
assert info['is_directory'] is False
def test_get_basic_file_info_error(self):
"""Test getting basic file info for nonexistent file."""
from nodupe.tools.scanner_engine.processor import FileProcessor
processor = FileProcessor()
with pytest.raises(Exception):
processor._get_basic_file_info("/nonexistent/file.txt")
def test_set_hash_algorithm(self):
"""Test setting hash algorithm."""
from nodupe.tools.scanner_engine.processor import FileProcessor
processor = FileProcessor()
processor.set_hash_algorithm('md5')
assert processor._hash_algorithm == 'md5'
def test_set_hash_algorithm_invalid(self):
"""Test setting invalid hash algorithm."""
from nodupe.tools.scanner_engine.processor import FileProcessor
processor = FileProcessor()
with pytest.raises(ValueError):
processor.set_hash_algorithm('invalid_algorithm')
def test_get_hash_algorithm(self):
"""Test getting hash algorithm."""
from nodupe.tools.scanner_engine.processor import FileProcessor
processor = FileProcessor()
assert processor.get_hash_algorithm() == 'sha256'
processor.set_hash_algorithm('md5')
assert processor.get_hash_algorithm() == 'md5'
def test_set_hash_buffer_size(self):
"""Test setting hash buffer size."""
from nodupe.tools.scanner_engine.processor import FileProcessor
processor = FileProcessor()
processor.set_hash_buffer_size(32768)
assert processor._hash_buffer_size == 32768
def test_set_hash_buffer_size_invalid(self):
"""Test setting invalid buffer size."""
from nodupe.tools.scanner_engine.processor import FileProcessor
processor = FileProcessor()
with pytest.raises(ValueError):
processor.set_hash_buffer_size(0)
with pytest.raises(ValueError):
processor.set_hash_buffer_size(-1)
def test_get_hash_buffer_size(self):
"""Test getting hash buffer size."""
from nodupe.tools.scanner_engine.processor import FileProcessor
processor = FileProcessor()
assert processor.get_hash_buffer_size() == 65536
processor.set_hash_buffer_size(32768)
assert processor.get_hash_buffer_size() == 32768
def test_process_files_empty_directory(self, temp_dir):
"""Test processing files in empty directory."""
from nodupe.tools.scanner_engine.processor import FileProcessor
empty_dir = temp_dir / "empty"
empty_dir.mkdir()
processor = FileProcessor()
result = processor.process_files(str(empty_dir))
assert result == []
def test_batch_process_files(self, temp_dir):
"""Test batch processing files."""
from nodupe.tools.scanner_engine.processor import FileProcessor
# Create test files
file1 = temp_dir / "file1.txt"
file2 = temp_dir / "file2.txt"
file1.write_text("content 1")
file2.write_text("content 2")
processor = FileProcessor()
result = processor.batch_process_files([str(file1), str(file2)])
assert len(result) == 2
assert all('hash' in f for f in result)
def test_batch_process_files_nonexistent(self):
"""Test batch processing with nonexistent files."""
from nodupe.tools.scanner_engine.processor import FileProcessor
processor = FileProcessor()
result = processor.batch_process_files(['/nonexistent/file1.txt', '/nonexistent/file2.txt'])
assert result == []
def test_default_hasher_hash_string(self):
"""Test default hasher hash_string method."""
from nodupe.tools.scanner_engine.processor import FileProcessor
processor = FileProcessor()
hash_value = processor._hasher.hash_string("test data")
assert hash_value is not None
assert len(hash_value) > 0
def test_default_hasher_hash_bytes(self):
"""Test default hasher hash_bytes method."""
from nodupe.tools.scanner_engine.processor import FileProcessor
processor = FileProcessor()
hash_value = processor._hasher.hash_bytes(b"test data")
assert hash_value is not None
assert len(hash_value) > 0
def test_default_hasher_verify_hash(self, temp_dir):
"""Test default hasher verify_hash method."""
from nodupe.tools.scanner_engine.processor import FileProcessor
test_file = temp_dir / "test.txt"
test_file.write_text("test content")
processor = FileProcessor()
# Calculate hash
expected_hash = processor._hasher.hash_file(str(test_file))
# Verify correct hash
assert processor._hasher.verify_hash(str(test_file), expected_hash) is True
# Verify incorrect hash
assert processor._hasher.verify_hash(str(test_file), "wrong_hash") is False
def test_default_hasher_set_algorithm(self):
"""Test default hasher set_algorithm method."""
from nodupe.tools.scanner_engine.processor import FileProcessor
processor = FileProcessor()
processor._hasher.set_algorithm('md5')
assert processor._hasher.get_algorithm() == 'md5'
def test_default_hasher_get_algorithm(self):
"""Test default hasher get_algorithm method."""
from nodupe.tools.scanner_engine.processor import FileProcessor
processor = FileProcessor()
assert processor._hasher.get_algorithm() == 'sha256'
def test_default_hasher_get_available_algorithms(self):
"""Test default hasher get_available_algorithms method."""
from nodupe.tools.scanner_engine.processor import FileProcessor
processor = FileProcessor()
algorithms = processor._hasher.get_available_algorithms()
assert 'sha256' in algorithms
assert 'md5' in algorithms
def test_detect_duplicates(self, temp_dir):
"""Test detect_duplicates method."""
from nodupe.tools.scanner_engine.processor import FileProcessor
processor = FileProcessor()
# Create files with same content (duplicates)
file1 = temp_dir / "file1.txt"
file2 = temp_dir / "file2.txt"
file1.write_text("same content")
file2.write_text("same content")
files = [
{'path': str(file1), 'hash': 'abc123', 'size': 12},
{'path': str(file2), 'hash': 'abc123', 'size': 12},
{'path': str(temp_dir / "unique.txt"), 'hash': 'xyz789', 'size': 14},
]
duplicates = processor.detect_duplicates(files)
assert len(duplicates) > 0
def test_batch_process_files_with_error(self, temp_dir):
"""Test batch_process_files handles errors gracefully."""
from nodupe.tools.scanner_engine.processor import FileProcessor
processor = FileProcessor()
# Create a valid file
valid_file = temp_dir / "valid.txt"
valid_file.write_text("valid content")
# Process with a non-existent file (should handle error)
file_paths = [str(valid_file), str(temp_dir / "nonexistent.txt")]
result = processor.batch_process_files(file_paths)
# Should process valid file, skip invalid
assert len(result) >= 1
def test_process_files_with_on_progress_callback(self, temp_dir):
"""Test process_files calls on_progress callback."""
from nodupe.tools.scanner_engine.processor import FileProcessor
processor = FileProcessor()
# Create multiple files
for i in range(15):
(temp_dir / f"file{i}.txt").write_text(f"content {i}")
progress_calls = []
def on_progress(progress):
progress_calls.append(progress)
result = processor.process_files(str(temp_dir), on_progress=on_progress)
# Should have called progress callback
assert len(progress_calls) > 0
assert 'files_processed' in progress_calls[0]
assert 'total_files' in progress_calls[0]
class TestCreateFileProcessor:
"""Test create_file_processor factory function."""
def test_create_file_processor_default(self):
"""Test creating file processor with defaults."""
from nodupe.tools.scanner_engine.processor import create_file_processor
processor = create_file_processor()
assert processor is not None
# Default hasher should be an instance, not a class
assert not isinstance(processor._hasher, type)
def test_create_file_processor_with_walker(self):
"""Test creating file processor with custom walker."""
from nodupe.tools.scanner_engine.processor import create_file_processor
from nodupe.tools.scanner_engine.walker import FileWalker
mock_walker = MagicMock(spec=FileWalker)
processor = create_file_processor(mock_walker)
assert processor.file_walker is mock_walker