Add tests for edge cases and coverage improvements
Introduced new test cases in `test_convenience_functions.py` and `test_security_and_errors.py` to cover edge cases, improve code coverage, and validate specific scenarios such as invalid modes, compression levels, and operations on closed archives. These changes address missing lines from the coverage report and enhance robustness.
This commit is contained in:
1 parent
a4ddd106f8
commit
0ecbdb3ea0
2 files changed
+300
No files matched your search
@@ -178,3 +178,80 @@ class TestCompressionLevels:
|
||||
|
||||
assert "compression level" in str(exc_info.value).lower()
|
||||
assert "1" in str(exc_info.value) and "22" in str(exc_info.value)
|
||||
|
||||
|
||||
class TestEdgeCaseCoverage:
|
||||
"""Test edge cases to improve coverage."""
|
||||
|
||||
def test_empty_files_list(self, temp_dir):
|
||||
"""Test create_archive with empty files list."""
|
||||
archive_path = temp_dir / "empty.tzst"
|
||||
|
||||
# Should create an empty archive
|
||||
create_archive(archive_path, [])
|
||||
|
||||
assert archive_path.exists()
|
||||
contents = list_archive(archive_path)
|
||||
assert len(contents) == 0
|
||||
|
||||
def test_create_archive_with_use_temp_file_false(self, temp_dir):
|
||||
"""Test creating archive with use_temp_file=False."""
|
||||
test_file = temp_dir / "test.txt"
|
||||
test_file.write_text("test content")
|
||||
archive_path = temp_dir / "test.tzst"
|
||||
|
||||
# Test non-atomic mode
|
||||
create_archive(archive_path, [str(test_file)], use_temp_file=False)
|
||||
|
||||
assert archive_path.exists()
|
||||
contents = list_archive(archive_path)
|
||||
assert len(contents) == 1
|
||||
|
||||
def test_extract_archive_to_specific_path(self, temp_dir):
|
||||
"""Test extracting archive to specific path."""
|
||||
# Create test archive
|
||||
test_file = temp_dir / "test.txt"
|
||||
test_file.write_text("test content")
|
||||
archive_path = temp_dir / "test.tzst"
|
||||
create_archive(archive_path, [str(test_file)])
|
||||
|
||||
# Extract to specific directory
|
||||
extract_dir = temp_dir / "extracted"
|
||||
extract_archive(archive_path, extract_dir)
|
||||
|
||||
assert extract_dir.exists()
|
||||
assert (extract_dir / "test.txt").exists()
|
||||
assert (extract_dir / "test.txt").read_text() == "test content"
|
||||
|
||||
def test_list_archive_with_streaming(self, temp_dir):
|
||||
"""Test listing archive with streaming mode."""
|
||||
test_file = temp_dir / "test.txt"
|
||||
test_file.write_text("test content")
|
||||
archive_path = temp_dir / "test.tzst"
|
||||
create_archive(archive_path, [str(test_file)])
|
||||
|
||||
# Test with streaming=True
|
||||
contents = list_archive(archive_path, streaming=True)
|
||||
assert len(contents) == 1
|
||||
assert contents[0]["name"] == "test.txt"
|
||||
|
||||
def test_test_archive_success(self, temp_dir):
|
||||
"""Test testing a valid archive."""
|
||||
test_file = temp_dir / "test.txt"
|
||||
test_file.write_text("test content")
|
||||
archive_path = temp_dir / "test.tzst"
|
||||
create_archive(archive_path, [str(test_file)])
|
||||
|
||||
# Test archive - should return True for valid archive
|
||||
result = tzst_test_archive(archive_path)
|
||||
assert result is True
|
||||
|
||||
def test_test_archive_failure(self, temp_dir):
|
||||
"""Test testing an invalid archive."""
|
||||
# Create a file that's not a valid archive
|
||||
invalid_archive = temp_dir / "invalid.tzst"
|
||||
invalid_archive.write_text("This is not a valid archive")
|
||||
|
||||
# Test archive - should return False for invalid archive
|
||||
result = tzst_test_archive(invalid_archive)
|
||||
assert result is False
|
||||
@@ -119,6 +119,132 @@ class TestCompressionValidation:
|
||||
archive_path, file_paths, compression_level=invalid_level
|
||||
)
|
||||
|
||||
def test_specific_compression_level_validation(self, temp_dir):
|
||||
"""Test specific compression level validation to cover missing lines."""
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
with tempfile.NamedTemporaryFile(suffix=".tzst", delete=False) as f:
|
||||
archive_path = Path(f.name)
|
||||
|
||||
try:
|
||||
# Test compression level too low (line 60)
|
||||
with pytest.raises(ValueError, match="Invalid compression level '0'"):
|
||||
TzstArchive(archive_path, mode="w", compression_level=0)
|
||||
|
||||
# Test compression level too high (line 66)
|
||||
with pytest.raises(ValueError, match="Invalid compression level '23'"):
|
||||
TzstArchive(archive_path, mode="w", compression_level=23)
|
||||
|
||||
finally:
|
||||
archive_path.unlink(missing_ok=True)
|
||||
|
||||
def test_invalid_mode_validation(self, temp_dir):
|
||||
"""Test invalid mode validation to cover missing lines."""
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
with tempfile.NamedTemporaryFile(suffix=".tzst", delete=False) as f:
|
||||
archive_path = Path(f.name)
|
||||
|
||||
try:
|
||||
# Test invalid mode (line 54)
|
||||
with pytest.raises(ValueError, match="Invalid mode 'x'"):
|
||||
TzstArchive(archive_path, mode="x")
|
||||
|
||||
# Test invalid mode with additional characters
|
||||
with pytest.raises(ValueError, match="Invalid mode 'rb'"):
|
||||
TzstArchive(archive_path, mode="rb")
|
||||
|
||||
finally:
|
||||
archive_path.unlink(missing_ok=True)
|
||||
|
||||
def test_runtime_errors_for_wrong_mode_operations(self, temp_dir):
|
||||
"""Test RuntimeError for operations on wrong mode archives."""
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
with tempfile.NamedTemporaryFile(suffix=".tzst", delete=False) as f:
|
||||
archive_path = Path(f.name)
|
||||
|
||||
try:
|
||||
# Create empty archive first
|
||||
with TzstArchive(archive_path, mode="w") as archive:
|
||||
pass
|
||||
|
||||
# Test read operations on write mode
|
||||
with TzstArchive(archive_path, mode="w") as archive:
|
||||
with pytest.raises(RuntimeError, match="Archive not open for reading"):
|
||||
archive.getmembers()
|
||||
|
||||
with pytest.raises(RuntimeError, match="Archive not open for reading"):
|
||||
archive.getnames()
|
||||
|
||||
with pytest.raises(RuntimeError, match="Archive not open for reading"):
|
||||
archive.extractfile("test")
|
||||
|
||||
finally:
|
||||
archive_path.unlink(missing_ok=True)
|
||||
|
||||
def test_operations_on_closed_archive(self, temp_dir):
|
||||
"""Test operations on closed archive to cover missing lines."""
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
with tempfile.NamedTemporaryFile(suffix=".tzst", delete=False) as f:
|
||||
archive_path = Path(f.name)
|
||||
|
||||
try:
|
||||
# Create and close archive
|
||||
archive = TzstArchive(archive_path, mode="w")
|
||||
archive.close()
|
||||
|
||||
# Test operations on closed archive
|
||||
with pytest.raises(RuntimeError, match="Archive not open"):
|
||||
archive.getmembers()
|
||||
|
||||
with pytest.raises(RuntimeError, match="Archive not open"):
|
||||
archive.getnames()
|
||||
|
||||
with pytest.raises(RuntimeError, match="Archive not open"):
|
||||
archive.extractfile("test")
|
||||
|
||||
finally:
|
||||
archive_path.unlink(missing_ok=True)
|
||||
|
||||
def test_close_error_handling(self, temp_dir):
|
||||
"""Test error handling in close method."""
|
||||
from pathlib import Path
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
# Create a mock archive that will raise exceptions during close
|
||||
archive = TzstArchive.__new__(TzstArchive)
|
||||
archive.path = Path("test.tzst")
|
||||
archive.mode = "w"
|
||||
archive.compression_level = 3
|
||||
|
||||
# Create mock objects that raise exceptions when closed
|
||||
mock_tarfile = MagicMock()
|
||||
mock_tarfile.close.side_effect = Exception("Mock tarfile close error")
|
||||
|
||||
mock_stream = MagicMock()
|
||||
mock_stream.close.side_effect = Exception("Mock stream close error")
|
||||
|
||||
mock_fileobj = MagicMock()
|
||||
mock_fileobj.close.side_effect = Exception("Mock fileobj close error")
|
||||
|
||||
archive._tarfile = mock_tarfile
|
||||
archive._compressed_stream = mock_stream
|
||||
archive._fileobj = mock_fileobj
|
||||
|
||||
# This should not raise an exception despite the mock exceptions
|
||||
archive.close()
|
||||
|
||||
# Verify all close methods were called
|
||||
mock_tarfile.close.assert_called_once()
|
||||
mock_stream.close.assert_called_once()
|
||||
mock_fileobj.close.assert_called_once()
|
||||
|
||||
|
||||
class TestSpecialFileTypes:
|
||||
"""Test handling of special file types and edge cases."""
|
||||
@@ -207,3 +333,100 @@ class TestAppendModeDocumentation:
|
||||
assert (
|
||||
"multiple archives" in error_msg.lower() or "recreate" in error_msg.lower()
|
||||
)
|
||||
|
||||
|
||||
class TestSpecificMissingLineCoverage:
|
||||
"""Test specific missing lines from coverage report."""
|
||||
|
||||
def test_invalid_mode_validation_specific(self, temp_dir):
|
||||
"""Test specific invalid mode validation to cover missing lines."""
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
with tempfile.NamedTemporaryFile(suffix=".tzst", delete=False) as f:
|
||||
archive_path = Path(f.name)
|
||||
|
||||
try:
|
||||
# Test invalid mode (line 54)
|
||||
with pytest.raises(ValueError, match="Invalid mode 'x'"):
|
||||
TzstArchive(archive_path, mode="x")
|
||||
|
||||
# Test invalid mode with additional characters
|
||||
with pytest.raises(ValueError, match="Invalid mode 'rb'"):
|
||||
TzstArchive(archive_path, mode="rb")
|
||||
|
||||
finally:
|
||||
archive_path.unlink(missing_ok=True)
|
||||
|
||||
def test_compression_level_validation_specific(self, temp_dir):
|
||||
"""Test specific compression level validation to cover missing lines."""
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
with tempfile.NamedTemporaryFile(suffix=".tzst", delete=False) as f:
|
||||
archive_path = Path(f.name)
|
||||
|
||||
try:
|
||||
# Test compression level too low (line 60)
|
||||
with pytest.raises(ValueError, match="Invalid compression level '0'"):
|
||||
TzstArchive(archive_path, mode="w", compression_level=0)
|
||||
|
||||
# Test compression level too high (line 66)
|
||||
with pytest.raises(ValueError, match="Invalid compression level '23'"):
|
||||
TzstArchive(archive_path, mode="w", compression_level=23)
|
||||
|
||||
finally:
|
||||
archive_path.unlink(missing_ok=True)
|
||||
|
||||
def test_runtime_errors_for_wrong_mode_operations(self, temp_dir):
|
||||
"""Test RuntimeError for operations on wrong mode archives."""
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
with tempfile.NamedTemporaryFile(suffix=".tzst", delete=False) as f:
|
||||
archive_path = Path(f.name)
|
||||
|
||||
try:
|
||||
# Create empty archive first
|
||||
with TzstArchive(archive_path, mode="w") as archive:
|
||||
pass
|
||||
|
||||
# Test read operations on write mode
|
||||
with TzstArchive(archive_path, mode="w") as archive:
|
||||
with pytest.raises(RuntimeError, match="Archive not open for reading"):
|
||||
archive.getmembers()
|
||||
|
||||
with pytest.raises(RuntimeError, match="Archive not open for reading"):
|
||||
archive.getnames()
|
||||
|
||||
with pytest.raises(RuntimeError, match="Archive not open for reading"):
|
||||
archive.extractfile("test")
|
||||
|
||||
finally:
|
||||
archive_path.unlink(missing_ok=True)
|
||||
|
||||
def test_operations_on_closed_archive_specific(self, temp_dir):
|
||||
"""Test operations on closed archive to cover missing lines."""
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
with tempfile.NamedTemporaryFile(suffix=".tzst", delete=False) as f:
|
||||
archive_path = Path(f.name)
|
||||
|
||||
try:
|
||||
# Create and close archive
|
||||
archive = TzstArchive(archive_path, mode="w")
|
||||
archive.close()
|
||||
|
||||
# Test operations on closed archive
|
||||
with pytest.raises(RuntimeError, match="Archive not open"):
|
||||
archive.getmembers()
|
||||
|
||||
with pytest.raises(RuntimeError, match="Archive not open"):
|
||||
archive.getnames()
|
||||
|
||||
with pytest.raises(RuntimeError, match="Archive not open"):
|
||||
archive.extractfile("test")
|
||||
|
||||
finally:
|
||||
archive_path.unlink(missing_ok=True)
|
||||
Reference in new issue
Block a user