Research-Stack/4-Infrastructure/NoDupeLabs/tests/test_100_coverage_final.py

1337 lines
51 KiB
Python

# SPDX-License-Identifier: Apache-2.0
# Copyright (c) 2025 Allaun
"""Final Coverage Completion Tests.
Tests to achieve 100% coverage for all 11 target files.
Note: Now uses pickle-safe test helpers for ProcessPoolExecutor testing.
See: docs/PARALLEL_TESTING_SUSTAINABILITY.md
"""
import mmap
import os
import shutil
import tarfile
import tempfile
import time
import zipfile
from pathlib import Path
from unittest.mock import MagicMock, Mock, mock_open, patch
import pytest
# Loader Tests
from nodupe.core.loader import (
CoreLoader,
bootstrap,
)
from nodupe.tools.hashing.autotune_logic import (
autotune_hash_algorithm,
create_autotuned_hasher,
)
from nodupe.tools.databases.connection import get_connection
# Discovery Tests
from nodupe.core.tool_system.discovery import (
ToolDiscovery,
ToolDiscoveryError,
ToolInfo,
create_tool_discovery,
)
# Archive Logic Tests
from nodupe.tools.archive.archive_logic import (
ArchiveHandler,
ArchiveHandlerError,
create_archive_handler,
)
# Leap Year Tests
from nodupe.tools.leap_year.leap_year import LeapYearTool
# MIME Logic Tests
from nodupe.tools.mime.mime_logic import MIMEDetection, MIMEDetectionError
# Import pickle-safe test helpers for process testing
from tests.parallel.test_helpers import (
double_number,
square_number,
add_one,
identity,
maybe_raise,
)
# MIME Tool Tests
from nodupe.tools.mime.mime_tool import StandardMIMETool, register_tool
# Filesystem Tests
from nodupe.tools.os_filesystem.filesystem import Filesystem, FilesystemError
# MMAP Handler Tests
from nodupe.tools.os_filesystem.mmap_handler import MMAPHandler
# Parallel Logic Tests
from nodupe.tools.parallel.parallel_logic import Parallel, ParallelError, ParallelProgress
# Security Logic Tests
from nodupe.tools.security_audit.security_logic import Security, SecurityError
# =============================================================================
# Archive Logic - Missing Lines: 97, 99, 101, 103, 105, 161-163, 216->214, 242->241, 243-268
# =============================================================================
class TestArchiveLogicMissingCoverage:
"""Test missing coverage in archive_logic.py."""
def test_detect_archive_format_nonexistent_file(self):
"""Test detect_archive_format with nonexistent file - line 97."""
handler = ArchiveHandler()
result = handler.detect_archive_format('/nonexistent/file.zip')
assert result is None
def test_detect_archive_format_extension_fallback_zip(self):
"""Test extension fallback for zip - line 99."""
handler = ArchiveHandler()
with tempfile.NamedTemporaryFile(suffix='.zip', delete=False) as f:
f.write(b'not a real zip')
f.flush()
try:
# Mock mime detection to return None
with patch.object(handler._mime_detector, 'detect_mime_type', return_value=None):
result = handler.detect_archive_format(f.name)
assert result == 'zip'
finally:
os.unlink(f.name)
def test_detect_archive_format_extension_fallback_tar(self):
"""Test extension fallback for tar - line 101."""
handler = ArchiveHandler()
with tempfile.NamedTemporaryFile(suffix='.tar', delete=False) as f:
f.write(b'not a real tar')
f.flush()
try:
with patch.object(handler._mime_detector, 'detect_mime_type', return_value=None):
result = handler.detect_archive_format(f.name)
assert result == 'tar'
finally:
os.unlink(f.name)
def test_detect_archive_format_extension_fallback_tgz(self):
"""Test extension fallback for tgz - line 103."""
handler = ArchiveHandler()
with tempfile.NamedTemporaryFile(suffix='.tgz', delete=False) as f:
f.write(b'not a real tgz')
f.flush()
try:
with patch.object(handler._mime_detector, 'detect_mime_type', return_value=None):
result = handler.detect_archive_format(f.name)
assert result == 'tar.gz'
finally:
os.unlink(f.name)
def test_detect_archive_format_extension_fallback_tbz2(self):
"""Test extension fallback for tbz2 - line 105."""
handler = ArchiveHandler()
with tempfile.NamedTemporaryFile(suffix='.tbz2', delete=False) as f:
f.write(b'not a real tbz2')
f.flush()
try:
with patch.object(handler._mime_detector, 'detect_mime_type', return_value=None):
result = handler.detect_archive_format(f.name)
assert result == 'tar.bz2'
finally:
os.unlink(f.name)
def test_extract_archive_unsupported_format_mime(self):
"""Test extract_archive with unsupported format - lines 161-163."""
handler = ArchiveHandler()
with tempfile.NamedTemporaryFile(suffix='.xyz', delete=False) as f:
f.write(b'unknown format')
f.flush()
try:
with patch.object(handler._mime_detector, 'detect_mime_type', return_value='application/unknown'):
with pytest.raises(ArchiveHandlerError) as exc_info:
handler.extract_archive(f.name)
assert 'Unsupported archive format' in str(exc_info.value)
finally:
os.unlink(f.name)
def test_create_archive_tar_lzma_format(self):
"""Test create_archive with tar.lzma format - line 216->214."""
handler = ArchiveHandler()
with tempfile.NamedTemporaryFile(suffix='.tar.lzma', delete=False) as f:
archive_path = f.name
with tempfile.NamedTemporaryFile(delete=False) as data_file:
data_file.write(b'test data')
data_file.flush()
data_path = data_file.name
try:
# tar.lzma is not supported for creation - falls back to tar mode
result = handler.create_archive(archive_path, [data_path], format='tar.lzma')
# Should create a tar file (tar.lzma falls through to tar mode)
assert result == archive_path
assert os.path.exists(archive_path)
finally:
os.unlink(archive_path)
os.unlink(data_path)
def test_get_archive_contents_info_exception_path(self):
"""Test get_archive_contents_info exception path - lines 242->241, 243-268."""
handler = ArchiveHandler()
# Create a valid zip file
with tempfile.NamedTemporaryFile(suffix='.zip', delete=False) as f:
archive_path = f.name
with zipfile.ZipFile(archive_path, 'w') as zf:
zf.writestr('test.txt', 'test content')
try:
# Mock extract_archive to raise an exception
with patch.object(handler, 'extract_archive', side_effect=Exception('Extraction failed')):
result = handler.get_archive_contents_info(archive_path, '/base')
assert result == []
finally:
os.unlink(archive_path)
def test_get_archive_contents_info_stat_error(self):
"""Test get_archive_contents_info with stat error."""
handler = ArchiveHandler()
with tempfile.NamedTemporaryFile(suffix='.zip', delete=False) as f:
archive_path = f.name
with zipfile.ZipFile(archive_path, 'w') as zf:
zf.writestr('test.txt', 'test content')
try:
# Extract first
extracted = handler.extract_archive(archive_path)
# Now mock stat to raise exception
with patch.object(Path, 'stat', side_effect=Exception('Stat failed')):
result = handler.get_archive_contents_info(archive_path, '/base')
# Should return empty list due to exception handling
finally:
handler.cleanup()
os.unlink(archive_path)
# =============================================================================
# MIME Tool - Missing Line: 72->78
# =============================================================================
class TestMIMEToolMissingCoverage:
"""Test missing coverage in mime_tool.py."""
def test_run_standalone_no_file_arg(self):
"""Test run_standalone with no file argument - line 72->78."""
tool = StandardMIMETool()
# When no args, should print help and raise SystemExit(0)
with pytest.raises(SystemExit) as exc_info:
tool.run_standalone([])
assert exc_info.value.code == 0
# =============================================================================
# MIME Logic - Missing Lines: 179, 210-216, 247
# =============================================================================
class TestMIMELogicMissingCoverage:
"""Test missing coverage in mime_logic.py."""
def test_detect_by_magic_no_match(self):
"""Test _detect_by_magic with no matching magic number - line 179."""
from nodupe.tools.mime.mime_logic import MIMEDetection
detector = MIMEDetection()
with tempfile.NamedTemporaryFile(delete=False) as f:
f.write(b'unknown file content')
f.flush()
try:
result = detector._detect_by_magic(Path(f.name))
assert result is None
finally:
os.unlink(f.name)
def test_detect_by_magic_ogg(self):
"""Test _detect_by_magic for OGG format - line 210-216."""
from nodupe.tools.mime.mime_logic import MIMEDetection
detector = MIMEDetection()
with tempfile.NamedTemporaryFile(delete=False) as f:
f.write(b'OggS\x00\x00\x00\x00')
f.flush()
try:
result = detector._detect_by_magic(Path(f.name))
assert result == 'audio/ogg'
finally:
os.unlink(f.name)
def test_detect_by_magic_read_error(self):
"""Test _detect_by_magic with read error - line 247."""
from nodupe.tools.mime.mime_logic import MIMEDetection
detector = MIMEDetection()
mock_path = MagicMock()
mock_path.open.side_effect = IOError('Cannot read')
result = detector._detect_by_magic(mock_path)
assert result is None
# =============================================================================
# Loader - Missing Lines: 66->65, 162->165, 192->196, 253-257, 295->307, 296->295, 301, 303-304, 343-346, 374, 377, 414
# =============================================================================
class TestLoaderMissingCoverage:
"""Test missing coverage in loader.py."""
def test_initialize_double_init(self):
"""Test initialize when already initialized - line 66->65."""
loader = CoreLoader()
loader.initialized = True
# Should return immediately
loader.initialize()
assert loader.initialized is True
def test_initialize_config_merge_nested_dict(self):
"""Test config merge with nested dicts - line 162->165."""
loader = CoreLoader()
mock_config = MagicMock()
mock_config.config = {'tools': {'existing': 'value'}}
with patch('nodupe.core.loader.load_config', return_value=mock_config), \
patch.object(loader, '_apply_platform_autoconfig', return_value={'tools': {'new': 'value'}}), \
patch('nodupe.core.loader.ToolRegistry'), \
patch('nodupe.core.loader.create_tool_loader'), \
patch('nodupe.core.loader.create_tool_discovery'), \
patch('nodupe.core.loader.create_lifecycle_manager'), \
patch('nodupe.core.loader.ToolHotReload'), \
patch('nodupe.core.loader.ToolIPCServer'), \
patch('nodupe.core.loader.logging'):
try:
loader.initialize()
except Exception:
pass # Expected due to mocking
# Both keys should exist
assert 'existing' in mock_config.config['tools']
assert 'new' in mock_config.config['tools']
def test_discover_and_load_tools_disabled(self):
"""Test tool discovery when auto_load is disabled - line 192->196."""
loader = CoreLoader()
loader.config = MagicMock()
loader.config.config = {'tools': {'auto_load': False}}
loader.tool_discovery = MagicMock()
loader._discover_and_load_tools()
# Should not call discover_tools_in_directory
loader.tool_discovery.discover_tools_in_directory.assert_not_called()
def test_perform_hash_autotuning_no_hasher_with_error(self):
"""Test hash autotuning with error - lines 253-257."""
loader = CoreLoader()
loader.container = MagicMock()
loader.container.get_service.return_value = None
loader.logger = MagicMock()
with patch('nodupe.core.loader.autotune_hash_algorithm', side_effect=Exception('Autotune failed')), \
patch('nodupe.core.loader.create_autotuned_hasher') as mock_create:
mock_create.return_value = (MagicMock(), {})
loader._perform_hash_autotuning()
# Should create fallback hasher
assert loader.container.register_service.called
def test_shutdown_not_initialized(self):
"""Test shutdown when not initialized - line 295->307."""
loader = CoreLoader()
loader.initialized = False
# Should return immediately
loader.shutdown()
def test_shutdown_exception_handling(self, caplog):
"""Test shutdown with exception - lines 296->295, 301, 303-304."""
loader = CoreLoader()
loader.initialized = True
loader.tool_lifecycle = MagicMock()
loader.tool_lifecycle.shutdown_all_tools.side_effect = Exception('Shutdown failed')
loader.logger = MagicMock()
loader.shutdown()
# Should log error but continue
assert loader.logger.error.called
def test_apply_platform_autoconfig_psutil_exception(self):
"""Test _apply_platform_autoconfig with psutil exception - lines 343-346."""
loader = CoreLoader()
with patch('nodupe.core.loader.psutil') as mock_psutil:
mock_psutil.virtual_memory.side_effect = Exception('psutil failed')
mock_psutil.disk_partitions.return_value = []
result = loader._detect_system_resources()
assert 'ram_gb' in result
assert result['ram_gb'] == 8 # Default value
def test_detect_thread_restrictions_kubernetes(self):
"""Test thread restriction detection for Kubernetes - line 374."""
loader = CoreLoader()
system_info = {}
with patch.dict(os.environ, {'KUBERNETES_SERVICE_HOST': 'localhost'}):
loader._detect_thread_restrictions(system_info)
assert system_info['thread_restrictions_detected'] is True
assert 'kubernetes' in system_info['thread_restriction_reasons']
def test_detect_thread_restrictions_docker(self):
"""Test thread restriction detection for Docker - line 377."""
loader = CoreLoader()
system_info = {}
with patch.dict(os.environ, {'DOCKER_CONTAINER': 'true'}):
loader._detect_thread_restrictions(system_info)
assert system_info['thread_restrictions_detected'] is True
assert 'container' in system_info['thread_restriction_reasons']
def test_detect_thread_restrictions_cgroup(self):
"""Test thread restriction detection for cgroups - line 414."""
loader = CoreLoader()
system_info = {}
# Create a mock cgroup file
with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.cgroup') as f:
f.write('50000')
f.flush()
cgroup_path = f.name
try:
with patch('nodupe.core.loader.os.path.exists', return_value=True), \
patch('nodupe.core.loader.open', mock_open(read_data='50000')):
loader._detect_thread_restrictions(system_info)
assert system_info['thread_restrictions_detected'] is True
assert 'cgroup_cpu_limit' in system_info['thread_restriction_reasons']
finally:
os.unlink(cgroup_path)
def test_get_connection_existing(self):
"""Test get_connection with existing database."""
mock_db = MagicMock()
with patch('nodupe.tools.databases.connection.global_container') as mock_container:
mock_container.get_service.return_value = mock_db
result = get_connection()
# Should return the mock database
assert result is not None
def test_autotune_hash_algorithm(self):
"""Test autotune_hash_algorithm function."""
result = autotune_hash_algorithm()
assert 'optimal_algorithm' in result
assert result['optimal_algorithm'] == 'blake3'
def test_create_autotuned_hasher(self):
"""Test create_autotuned_hasher function."""
hasher, config = create_autotuned_hasher()
assert hasher is not None
assert config['algorithm'] == 'blake3'
# =============================================================================
# Discovery - Missing Lines: 148->129, 155-161, 165-167, 193->192, 235->234, 239-244, 273->272, 275, 312, 318, 335, 371->365, 383, 385, 400->365, 405-406, 411->365, 469-478
# =============================================================================
class TestDiscoveryMissingCoverage:
"""Test missing coverage in discovery.py."""
def test_discover_tools_in_directory_iterdir_exception(self):
"""Test discover_tools_in_directory with iterdir exception - line 148->129."""
discovery = ToolDiscovery()
mock_dir = MagicMock()
mock_dir.iterdir.side_effect = TypeError('Not a directory')
result = discovery.discover_tools_in_directory(mock_dir)
assert result == []
def test_discover_tools_in_directory_os_error(self):
"""Test discover_tools_in_directory with OSError - lines 155-161."""
discovery = ToolDiscovery()
mock_dir = MagicMock()
mock_dir.iterdir.side_effect = OSError('Directory not found')
result = discovery.discover_tools_in_directory(mock_dir)
assert result == []
def test_discover_tools_in_directory_item_exception(self):
"""Test discover_tools_in_directory with item exception - lines 165-167."""
discovery = ToolDiscovery()
mock_item = MagicMock()
mock_item.is_file.side_effect = Exception('Item check failed')
mock_dir = MagicMock()
mock_dir.iterdir.return_value = [mock_item]
result = discovery.discover_tools_in_directory(mock_dir)
assert result == []
def test_discover_tools_in_directories_exception(self):
"""Test discover_tools_in_directories with exception - line 193->192."""
discovery = ToolDiscovery()
mock_dir = MagicMock()
mock_dir.iterdir.side_effect = ToolDiscoveryError('Discovery failed')
result = discovery.discover_tools_in_directories([mock_dir])
assert result == []
def test_find_tool_by_name_in_discovered(self):
"""Test find_tool_by_name in discovered tools - line 235->234."""
discovery = ToolDiscovery()
tool_info = ToolInfo(name='test_tool', file_path=Path('/test.py'))
discovery._discovered_tools.append(tool_info)
result = discovery.find_tool_by_name('test_tool')
assert result == tool_info
def test_find_tool_by_name_not_found(self):
"""Test find_tool_by_name not found - lines 239-244."""
discovery = ToolDiscovery()
result = discovery.find_tool_by_name('nonexistent_tool')
assert result is None
def test_find_tool_by_name_subdir_check(self):
"""Test find_tool_by_name with subdir check - line 273->272."""
discovery = ToolDiscovery()
mock_dir = MagicMock()
mock_dir.exists.return_value = False
with patch('nodupe.core.tool_system.discovery.Path', return_value=mock_dir):
result = discovery.find_tool_by_name('test_tool', search_directories=[MagicMock()])
assert result is None
def test_find_tool_by_name_exception(self):
"""Test find_tool_by_name with exception - line 275."""
discovery = ToolDiscovery()
mock_dir = MagicMock()
mock_dir.iterdir.side_effect = ToolDiscoveryError('Failed')
result = discovery.find_tool_by_name('test_tool', search_directories=[mock_dir])
assert result is None
def test_refresh_discovery(self):
"""Test refresh_discovery - line 312."""
discovery = ToolDiscovery()
discovery._discovered_tools.append(ToolInfo(name='test', file_path=Path('/test.py')))
discovery.refresh_discovery()
assert len(discovery._discovered_tools) == 0
def test_get_discovered_tool_found(self):
"""Test get_discovered_tool found - line 318."""
discovery = ToolDiscovery()
tool_info = ToolInfo(name='test_tool', file_path=Path('/test.py'))
discovery._discovered_tools.append(tool_info)
result = discovery.get_discovered_tool('test_tool')
assert result == tool_info
def test_get_discovered_tool_not_found(self):
"""Test get_discovered_tool not found - line 335."""
discovery = ToolDiscovery()
result = discovery.get_discovered_tool('nonexistent')
assert result is None
def test_is_tool_discovered_true(self):
"""Test is_tool_discovered true - line 371->365."""
discovery = ToolDiscovery()
discovery._discovered_tools.append(ToolInfo(name='test_tool', file_path=Path('/test.py')))
result = discovery.is_tool_discovered('test_tool')
assert result is True
def test_is_tool_discovered_false(self):
"""Test is_tool_discovered false - line 383."""
discovery = ToolDiscovery()
result = discovery.is_tool_discovered('nonexistent')
assert result is False
def test_extract_tool_info_exception(self):
"""Test _extract_tool_info with exception - line 385."""
discovery = ToolDiscovery()
mock_path = MagicMock()
mock_path.open.side_effect = Exception('Read failed')
result = discovery._extract_tool_info(mock_path)
assert result is None
def test_parse_metadata_capabilities_exception(self):
"""Test _parse_metadata with capabilities exception - line 400->365."""
discovery = ToolDiscovery()
content = """
CAPABILITIES = "invalid syntax {{{"
"""
metadata = discovery._parse_metadata(content)
# Should handle gracefully - may not have capabilities key
assert metadata is not None
def test_parse_metadata_dependencies_exception(self):
"""Test _parse_metadata with dependencies exception - lines 405-406."""
discovery = ToolDiscovery()
content = """
DEPENDENCIES = "invalid syntax {{{"
"""
metadata = discovery._parse_metadata(content)
# Should handle gracefully
assert metadata.get('dependencies') is None or metadata.get('dependencies') == []
def test_parse_metadata_version_exception(self):
"""Test _parse_metadata with version exception - line 411->365."""
discovery = ToolDiscovery()
content = """
__version__ = "invalid syntax {{{"
"""
metadata = discovery._parse_metadata(content)
# Should handle gracefully
assert 'version' in metadata
def test_validate_tool_file_empty(self):
"""Test validate_tool_file with empty file - lines 469-478."""
discovery = ToolDiscovery()
with tempfile.NamedTemporaryFile(suffix='.py', delete=False) as f:
# Empty file
temp_path = Path(f.name)
try:
result = discovery.validate_tool_file(temp_path)
assert result is False
finally:
os.unlink(temp_path)
# =============================================================================
# Parallel Logic - Missing Lines: 128-130, 132, 165, 195, 202-204, 264, 268-273, 280-282, 284, 291-322, 326-330, 340-341, 355-356, 367
# =============================================================================
class TestParallelLogicMissingCoverage:
"""Test missing coverage in parallel_logic.py.
Note: Uses pickle-safe helpers from test_helpers for process testing.
See: docs/PARALLEL_TESTING_SUSTAINABILITY.md
"""
def test_process_in_parallel_task_exception(self):
"""Test process_in_parallel with task exception - lines 128-130, 132."""
# Use pickle-safe maybe_raise for process testing
with pytest.raises(ParallelError) as exc_info:
Parallel.process_in_parallel(
maybe_raise, # ✅ Pickle-safe
[1, -1, 3], # -1 triggers error
workers=2,
use_processes=False # Threads for error testing
)
assert 'Error for value -1' in str(exc_info.value)
def test_map_parallel_unordered_batch_size_one(self):
"""Test map_parallel_unordered with batch_size=1 - line 165."""
items = [1, 2, 3]
results = list(Parallel.map_parallel_unordered(
double_number, items, workers=2, use_processes=True, prefer_batches=True
))
assert sorted(results) == [2, 4, 6]
def test_map_parallel_unordered_interpreter_exception(self):
"""Test map_parallel_unordered interpreter exception path - line 195."""
items = [1, 2, 3]
with patch('nodupe.tools.parallel.parallel_logic.Parallel.supports_interpreter_pool', return_value=True):
results = list(Parallel.map_parallel_unordered(
double_number, items, workers=2, use_interpreters=True
))
assert sorted(results) == [2, 4, 6]
def test_map_parallel_unordered_batch_exception(self):
"""Test map_parallel_unordered batch exception - lines 202-204."""
items = [1, 2, 3]
with patch.object(os, 'getenv', return_value='invalid'):
results = list(Parallel.map_parallel_unordered(
double_number, items, workers=2, use_processes=True, prefer_batches=True
))
assert sorted(results) == [2, 4, 6]
def test_map_parallel_unordered_cpu_count_exception(self):
"""Test map_parallel_unordered cpu count exception - line 264."""
items = [1, 2, 3]
with patch.object(Parallel, 'get_cpu_count', side_effect=Exception('CPU count failed')):
results = list(Parallel.map_parallel_unordered(
double_number, items, workers=2, use_processes=True
))
assert sorted(results) == [2, 4, 6]
def test_map_parallel_unordered_batch_logging(self):
"""Test map_parallel_unordered with batch logging - lines 268-273."""
items = [1, 2, 3]
with patch.dict(os.environ, {'NODUPE_BATCH_LOG': '1'}):
results = list(Parallel.map_parallel_unordered(
double_number, items, workers=2, use_processes=True, prefer_batches=True
))
assert sorted(results) == [2, 4, 6]
def test_map_parallel_unordered_chunksize_exception(self):
"""Test map_parallel_unordered chunksize exception - lines 280-282, 284."""
items = [1, 2, 3]
# Test with threads (not processes) to avoid multiprocessing issues with mocks
# Use side_effect as a callable that returns default values for specific calls
def getenv_side_effect(key, default=None):
"""Mock getenv that raises exception for specific environment variables."""
if key in ("NODUPE_BATCH_DIVISOR", "NODUPE_CHUNK_FACTOR"):
raise Exception('Env failed')
return default
with patch.object(os, 'getenv', side_effect=getenv_side_effect):
results = list(Parallel.map_parallel_unordered(
double_number, items, workers=2, use_processes=False, prefer_batches=False, prefer_map=True
))
assert sorted(results) == [2, 4, 6]
def test_map_parallel_unordered_bounded_submission(self):
"""Test map_parallel_unordered bounded submission - lines 291-322."""
items = [1, 2, 3, 4, 5]
results = list(Parallel.map_parallel_unordered(
double_number, items, workers=2, use_processes=False
))
assert sorted(results) == [2, 4, 6, 8, 10]
def test_map_parallel_unordered_stopiteration(self):
"""Test map_parallel_unordered StopIteration handling - lines 326-330."""
items = [1] # Single item to trigger StopIteration early
results = list(Parallel.map_parallel_unordered(
double_number, items, workers=2
))
assert results == [2]
def test_map_parallel_unordered_future_exception(self):
"""Test map_parallel_unordered future exception - line 340-341."""
# Use pickle-safe maybe_raise for process testing
with pytest.raises(ParallelError) as exc_info:
list(Parallel.map_parallel_unordered(
maybe_raise, # ✅ Pickle-safe
[-1], # -1 triggers error
workers=1
))
assert 'Error for value -1' in str(exc_info.value)
def test_map_parallel_unordered_timeout(self):
"""Test map_parallel_unordered with timeout - line 355-356."""
from tests.parallel.test_helpers import slow_square
# Use pickle-safe slow_square for timeout testing
with pytest.raises(ParallelError):
list(Parallel.map_parallel_unordered(
slow_square, # ✅ Pickle-safe with delay
[1],
timeout=0.01,
use_processes=False # Threads for timeout testing
))
def test_map_parallel_unordered_keyerror(self):
"""Test map_parallel_unordered KeyError handling - line 367."""
items = [1, 2, 3]
# This should handle KeyError gracefully
results = list(Parallel.map_parallel_unordered(
double_number, items, workers=2
))
assert sorted(results) == [2, 4, 6]
def test_map_parallel_unordered_with_processes(self):
"""Test map_parallel_unordered with ProcessPoolExecutor."""
items = [1, 2, 3, 4, 5]
# Test with actual processes
results = list(Parallel.map_parallel_unordered(
double_number, # ✅ Pickle-safe
items,
workers=2,
use_processes=True # ✅ Test processes
))
assert sorted(results) == [2, 4, 6, 8, 10]
def test_process_in_parallel_with_processes(self):
"""Test process_in_parallel with ProcessPoolExecutor."""
items = [1, 2, 3, 4, 5]
# Test with actual processes
results = Parallel.process_in_parallel(
square_number, # ✅ Pickle-safe
items,
workers=2,
use_processes=True # ✅ Test processes
)
assert results == [1, 4, 9, 16, 25]
def test_thread_vs_process_consistency(self):
"""Verify thread and process results are consistent."""
items = [1, 2, 3, 4, 5]
# Thread result
thread_result = Parallel.process_in_parallel(
double_number,
items,
workers=2,
use_processes=False
)
# Process result
process_result = Parallel.process_in_parallel(
double_number,
items,
workers=2,
use_processes=True
)
# Both should produce same results
assert thread_result == process_result == [2, 4, 6, 8, 10]
# =============================================================================
# Security Logic - Missing Lines: 114, 154->157, 167, 176, 177->181, 183, 184->187, 342->344, 413-414, 442-444
# =============================================================================
class TestSecurityLogicMissingCoverage:
"""Test missing coverage in security_logic.py."""
def test_sanitize_path_null_bytes(self):
"""Test sanitize_path with null bytes - line 114."""
with pytest.raises(SecurityError) as exc_info:
Security.sanitize_path('test\x00path')
assert 'null bytes' in str(exc_info.value)
def test_validate_path_allowed_parent_exception(self):
"""Test validate_path with allowed_parent exception - line 154->157."""
with pytest.raises(SecurityError) as exc_info:
Security.validate_path('/test/path', allowed_parent='/nonexistent')
assert 'Cannot resolve allowed parent' in str(exc_info.value) or 'outside allowed' in str(exc_info.value)
def test_validate_path_must_exist(self):
"""Test validate_path with must_exist - line 167."""
with pytest.raises(SecurityError) as exc_info:
Security.validate_path('/nonexistent/path', must_exist=True)
assert 'does not exist' in str(exc_info.value)
def test_validate_path_must_be_file(self):
"""Test validate_path with must_be_file - line 176."""
with tempfile.TemporaryDirectory() as tmpdir:
with pytest.raises(SecurityError) as exc_info:
Security.validate_path(tmpdir, must_be_file=True)
assert 'not a file' in str(exc_info.value)
def test_validate_path_must_be_dir(self):
"""Test validate_path with must_be_dir - line 177->181."""
with tempfile.NamedTemporaryFile(delete=False) as f:
temp_file = f.name
try:
with pytest.raises(SecurityError) as exc_info:
Security.validate_path(temp_file, must_be_dir=True)
assert 'not a directory' in str(exc_info.value)
finally:
os.unlink(temp_file)
def test_validate_path_resolve_exception(self):
"""Test validate_path with resolve exception - line 183."""
mock_path = MagicMock()
mock_path.resolve.side_effect = RuntimeError('Resolve failed')
with patch('nodupe.tools.security_audit.security_logic.Path', return_value=mock_path):
with pytest.raises(SecurityError) as exc_info:
Security.validate_path(mock_path)
assert 'Cannot resolve' in str(exc_info.value)
def test_validate_path_general_exception(self):
"""Test validate_path with general exception - line 184->187."""
with patch('nodupe.tools.security_audit.security_logic.Path') as mock_path_class:
mock_path_class.side_effect = Exception('Path creation failed')
with pytest.raises(SecurityError) as exc_info:
Security.validate_path('/test')
assert 'validation failed' in str(exc_info.value)
def test_check_permissions_not_exists(self):
"""Test check_permissions with nonexistent path - line 342->344."""
with pytest.raises(SecurityError) as exc_info:
Security.check_permissions('/nonexistent/path', readable=True)
assert 'does not exist' in str(exc_info.value)
def test_sanitize_filename_exception(self):
"""Test sanitize_filename with exception - lines 413-414."""
with patch('nodupe.tools.security_audit.security_logic.re.sub', side_effect=Exception('Regex failed')):
with pytest.raises(SecurityError) as exc_info:
Security.sanitize_filename('test.txt')
assert 'sanitization failed' in str(exc_info.value)
def test_generate_safe_filename_exception(self):
"""Test generate_safe_filename with exception - lines 442-444."""
with patch('nodupe.tools.security_audit.security_logic.Security.sanitize_filename', side_effect=Exception('Failed')):
with pytest.raises(SecurityError) as exc_info:
Security.generate_safe_filename('test')
assert 'generation failed' in str(exc_info.value)
# =============================================================================
# Filesystem - Missing Lines: 52, 74, 91, 110-116, 121-122, 141, 153, 170, 193, 201, 215, 219-220, 235, 256, 258, 273, 289, 291, 306
# =============================================================================
class TestFilesystemMissingCoverage:
"""Test missing coverage in filesystem.py."""
def test_safe_read_not_exists(self):
"""Test safe_read with nonexistent file - line 52."""
with pytest.raises(FilesystemError) as exc_info:
Filesystem.safe_read('/nonexistent/file.txt')
assert 'does not exist' in str(exc_info.value)
def test_safe_read_not_file(self):
"""Test safe_read with directory - line 74."""
with tempfile.TemporaryDirectory() as tmpdir:
with pytest.raises(FilesystemError) as exc_info:
Filesystem.safe_read(tmpdir)
assert 'not a file' in str(exc_info.value)
def test_safe_read_exceeds_max_size(self):
"""Test safe_read with max_size exceeded - line 91."""
with tempfile.NamedTemporaryFile(delete=False) as f:
f.write(b'x' * 100)
temp_file = f.name
try:
with pytest.raises(FilesystemError) as exc_info:
Filesystem.safe_read(temp_file, max_size=50)
assert 'exceeds limit' in str(exc_info.value)
finally:
os.unlink(temp_file)
def test_safe_write_atomic_exception(self):
"""Test safe_write with atomic exception - lines 110-116."""
with tempfile.TemporaryDirectory() as tmpdir:
target = Path(tmpdir) / 'test.txt'
with patch('nodupe.tools.os_filesystem.filesystem.os.write', side_effect=OSError('Write failed')):
with pytest.raises(FilesystemError) as exc_info:
Filesystem.safe_write(target, b'test data', atomic=True)
assert 'Failed to write' in str(exc_info.value)
def test_safe_write_direct_exception(self):
"""Test safe_write with direct write exception - lines 121-122."""
with tempfile.TemporaryDirectory() as tmpdir:
target = Path(tmpdir) / 'test.txt'
with patch('nodupe.tools.os_filesystem.filesystem.Path.write_bytes', side_effect=OSError('Write failed')):
with pytest.raises(FilesystemError) as exc_info:
Filesystem.safe_write(target, b'test data', atomic=False)
assert 'Failed to write' in str(exc_info.value)
def test_validate_path_must_exist_fail(self):
"""Test validate_path with must_exist failure - line 141."""
with pytest.raises(FilesystemError) as exc_info:
Filesystem.validate_path('/nonexistent/path', must_exist=True)
assert 'does not exist' in str(exc_info.value)
def test_validate_path_resolve_exception(self):
"""Test validate_path with resolve exception - line 153."""
mock_path = MagicMock()
mock_path.resolve.side_effect = RuntimeError('Resolve failed')
with patch('nodupe.tools.os_filesystem.filesystem.Path', return_value=mock_path):
with pytest.raises(FilesystemError) as exc_info:
Filesystem.validate_path(mock_path)
assert 'Invalid path' in str(exc_info.value)
def test_get_size_exception(self):
"""Test get_size with exception - line 170."""
mock_path = MagicMock()
mock_path.stat.side_effect = OSError('Stat failed')
with patch('nodupe.tools.os_filesystem.filesystem.Path', return_value=mock_path):
with pytest.raises(FilesystemError) as exc_info:
Filesystem.get_size(mock_path)
assert 'Failed to get file size' in str(exc_info.value)
def test_list_directory_not_dir(self):
"""Test list_directory with non-directory - line 193."""
with tempfile.NamedTemporaryFile(delete=False) as f:
temp_file = f.name
try:
with pytest.raises(FilesystemError) as exc_info:
Filesystem.list_directory(temp_file)
assert 'not a directory' in str(exc_info.value)
finally:
os.unlink(temp_file)
def test_list_directory_exception(self):
"""Test list_directory with exception - line 201."""
mock_path = MagicMock()
mock_path.is_dir.return_value = True
mock_path.glob.side_effect = OSError('Glob failed')
with patch('nodupe.tools.os_filesystem.filesystem.Path', return_value=mock_path):
with pytest.raises(FilesystemError) as exc_info:
Filesystem.list_directory(mock_path)
assert 'Failed to list directory' in str(exc_info.value)
def test_ensure_directory_exception(self):
"""Test ensure_directory with exception - line 215."""
mock_path = MagicMock()
mock_path.mkdir.side_effect = OSError('Mkdir failed')
with patch('nodupe.tools.os_filesystem.filesystem.Path', return_value=mock_path):
with pytest.raises(FilesystemError) as exc_info:
Filesystem.ensure_directory(mock_path)
assert 'Failed to create directory' in str(exc_info.value)
def test_remove_file_exception(self):
"""Test remove_file with exception - lines 219-220."""
mock_path = MagicMock()
mock_path.unlink.side_effect = OSError('Unlink failed')
with patch('nodupe.tools.os_filesystem.filesystem.Path', return_value=mock_path):
with pytest.raises(FilesystemError) as exc_info:
Filesystem.remove_file(mock_path)
assert 'Failed to remove' in str(exc_info.value)
def test_copy_file_not_exists(self):
"""Test copy_file with nonexistent source - line 235."""
with pytest.raises(FilesystemError) as exc_info:
Filesystem.copy_file('/nonexistent/src.txt', '/tmp/dst.txt')
assert 'does not exist' in str(exc_info.value)
def test_copy_file_exists_no_overwrite(self):
"""Test copy_file with existing destination - line 256."""
with tempfile.NamedTemporaryFile(delete=False) as src:
src.write(b'source')
src_path = src.name
with tempfile.NamedTemporaryFile(delete=False) as dst:
dst.write(b'dest')
dst_path = dst.name
try:
with pytest.raises(FilesystemError) as exc_info:
Filesystem.copy_file(src_path, dst_path, overwrite=False)
assert 'Destination file exists' in str(exc_info.value)
finally:
os.unlink(src_path)
os.unlink(dst_path)
def test_copy_file_exception(self):
"""Test copy_file with exception - line 258."""
with patch('nodupe.tools.os_filesystem.filesystem.shutil.copy2', side_effect=OSError('Copy failed')):
with tempfile.NamedTemporaryFile(delete=False) as src:
src.write(b'source')
src_path = src.name
try:
with pytest.raises(FilesystemError) as exc_info:
Filesystem.copy_file(src_path, '/tmp/new_dst.txt', overwrite=True)
assert 'Failed to copy' in str(exc_info.value)
finally:
os.unlink(src_path)
def test_move_file_not_exists(self):
"""Test move_file with nonexistent source - line 273."""
with pytest.raises(FilesystemError) as exc_info:
Filesystem.move_file('/nonexistent/src.txt', '/tmp/dst.txt')
assert 'does not exist' in str(exc_info.value)
def test_move_file_exists_no_overwrite(self):
"""Test move_file with existing destination - line 289."""
with tempfile.NamedTemporaryFile(delete=False) as src:
src.write(b'source')
src_path = src.name
with tempfile.NamedTemporaryFile(delete=False) as dst:
dst.write(b'dest')
dst_path = dst.name
try:
with pytest.raises(FilesystemError) as exc_info:
Filesystem.move_file(src_path, dst_path, overwrite=False)
assert 'Destination file exists' in str(exc_info.value)
finally:
os.unlink(src_path)
os.unlink(dst_path)
def test_move_file_exception(self):
"""Test move_file with exception - line 291."""
with patch('nodupe.tools.os_filesystem.filesystem.shutil.move', side_effect=OSError('Move failed')):
with tempfile.NamedTemporaryFile(delete=False) as src:
src.write(b'source')
src_path = src.name
try:
with pytest.raises(FilesystemError) as exc_info:
Filesystem.move_file(src_path, '/tmp/new_dst.txt', overwrite=True)
assert 'Failed to move' in str(exc_info.value)
finally:
os.unlink(src_path)
def test_safe_read_string_path(self):
"""Test safe_read with string path - line 306."""
with tempfile.NamedTemporaryFile(delete=False) as f:
f.write(b'test data')
temp_file = f.name
try:
result = Filesystem.safe_read(temp_file)
assert result == b'test data'
finally:
os.unlink(temp_file)
# =============================================================================
# MMAP Handler - Missing Line: 50->exit
# =============================================================================
class TestMMAPHandlerMissingCoverage:
"""Test missing coverage in mmap_handler.py."""
def test_mmap_context_exception(self):
"""Test mmap_context with exception - line 50->exit."""
with tempfile.NamedTemporaryFile(delete=False) as f:
f.write(b'test data')
temp_file = f.name
try:
# Test that context manager properly closes on exception
with pytest.raises(ValueError):
with MMAPHandler.mmap_context(temp_file) as mapped:
raise ValueError('Test exception')
# File should be properly closed
finally:
os.unlink(temp_file)
# =============================================================================
# Leap Year - Missing Lines: 128-130, 168->exit, 174->exit, 272, 560->562
# =============================================================================
class TestLeapYearMissingCoverage:
"""Test missing coverage in leap_year.py."""
def test_run_standalone_exception_handling(self):
"""Test run_standalone with exception - lines 128-130."""
tool = LeapYearTool()
# Test with invalid argument that causes argparse to fail
with patch('sys.argv', ['leap_year', 'invalid']):
try:
result = tool.run_standalone(['invalid'])
except SystemExit:
pass # Expected from argparse
def test_shutdown_with_cache_stats(self):
"""Test shutdown with cache stats - line 168->exit."""
tool = LeapYearTool(enable_cache=True)
tool.is_leap_year(2000) # Populate cache
mock_container = MagicMock()
tool.shutdown(mock_container)
# Should log cache stats
def test_shutdown_without_cache(self):
"""Test shutdown without cache - line 174->exit."""
tool = LeapYearTool(enable_cache=False)
mock_container = MagicMock()
tool.shutdown(mock_container)
# Should not log cache stats
def test_get_calendar_info(self):
"""Test get_calendar_info - line 272."""
tool = LeapYearTool()
info = tool.get_calendar_info(2000)
assert info['year'] == 2000
assert info['is_leap_year'] is True
assert info['days_in_year'] == 366
def test_is_gregorian_leap_year(self):
"""Test is_gregorian_leap_year - line 560->562."""
tool = LeapYearTool()
assert tool.is_gregorian_leap_year(2000) is True
assert tool.is_gregorian_leap_year(1900) is False
assert tool.is_gregorian_leap_year(2004) is True
# =============================================================================
# Integration Tests
# =============================================================================
class TestIntegration:
"""Integration tests for multiple modules."""
def test_archive_with_mime_detection(self):
"""Test archive handler with MIME detection."""
handler = ArchiveHandler()
# Create a valid zip file
with tempfile.NamedTemporaryFile(suffix='.zip', delete=False) as f:
archive_path = f.name
with zipfile.ZipFile(archive_path, 'w') as zf:
zf.writestr('test.txt', 'test content')
try:
# Test detection
assert handler.is_archive_file(archive_path) is True
assert handler.detect_archive_format(archive_path) == 'zip'
# Test extraction
result = handler.extract_archive(archive_path)
assert len(result) > 0
finally:
handler.cleanup()
os.unlink(archive_path)
def test_parallel_with_filesystem(self):
"""Test parallel processing with filesystem operations."""
with tempfile.TemporaryDirectory() as tmpdir:
# Create test files
test_files = []
for i in range(5):
test_file = Path(tmpdir) / f'test_{i}.txt'
test_file.write_bytes(f'data_{i}'.encode())
test_files.append(test_file)
# Process in parallel
def read_file(path):
"""Helper function to read file content."""
return Filesystem.safe_read(path)
results = Parallel.process_in_parallel(read_file, test_files, workers=2)
assert len(results) == 5
for i, result in enumerate(results):
assert result == f'data_{i}'.encode()
def test_security_with_filesystem(self):
"""Test security validation with filesystem operations."""
with tempfile.TemporaryDirectory() as tmpdir:
test_file = Path(tmpdir) / 'test.txt'
test_file.write_bytes(b'test data')
# Validate path
assert Security.validate_path(test_file, must_exist=True, must_be_file=True)
# Check permissions
assert Security.check_permissions(test_file, readable=True)
if __name__ == '__main__':
pytest.main([__file__, '-v'])