- Created integration tests for complex scenarios in `tests/integration/test_complex_scenarios.py`. - Added unit tests for core functionality, convenience functions, security features, and error handling in `tests/unit/test_archive_basics.py`, `tests/unit/test_convenience_functions.py`, `tests/unit/test_security_and_errors.py`. - Implemented tests for temporary file exclusion, large file operations, and complex directory structures. - Included tests for atomic operations and compression level validation. - Established a structure for integration and unit tests with appropriate docstrings and assertions.
210 lines
7.7 KiB
Python
210 lines
7.7 KiB
Python
"""Tests for security features and error handling."""
|
|
|
|
from unittest.mock import patch
|
|
|
|
import pytest
|
|
|
|
from tzst import TzstArchive, create_archive, extract_archive
|
|
from tzst import test_archive as tzst_test_archive
|
|
|
|
|
|
class TestErrorHandling:
|
|
"""Test error handling."""
|
|
|
|
def test_invalid_archive_mode(self, sample_archive_path):
|
|
"""Test invalid archive mode."""
|
|
with pytest.raises(ValueError):
|
|
TzstArchive(sample_archive_path, "invalid")
|
|
|
|
def test_append_mode_not_supported(self, sample_archive_path):
|
|
"""Test that append mode raises NotImplementedError."""
|
|
with pytest.raises(NotImplementedError):
|
|
TzstArchive(sample_archive_path, "a")
|
|
|
|
def test_archive_not_open(self, sample_archive_path):
|
|
"""Test operations on non-open archive."""
|
|
archive = TzstArchive(sample_archive_path, "r")
|
|
|
|
with pytest.raises(RuntimeError):
|
|
archive.getnames()
|
|
|
|
def test_file_not_found(self, temp_dir):
|
|
"""Test handling of non-existent files."""
|
|
archive_path = temp_dir / "test.tzst"
|
|
fake_file = temp_dir / "fake.txt"
|
|
|
|
with pytest.raises(FileNotFoundError):
|
|
create_archive(archive_path, [fake_file])
|
|
|
|
|
|
class TestSecurityFiltering:
|
|
"""Test security filtering mechanisms."""
|
|
|
|
def test_tar_filter_extraction(self, sample_files, temp_dir):
|
|
"""Test extraction with TAR security filter."""
|
|
file_paths = [f for f in sample_files if f.is_file()]
|
|
archive_path = temp_dir / "tar_filter_test.tzst"
|
|
|
|
# Create archive
|
|
create_archive(archive_path, file_paths)
|
|
|
|
# Extract with tar filter
|
|
extract_dir = temp_dir / "tar_filtered"
|
|
with patch("tzst.core.TzstArchive.extract") as mock_extract:
|
|
extract_archive(archive_path, extract_dir, filter="tar")
|
|
|
|
# Verify filter was passed
|
|
call_args = mock_extract.call_args
|
|
assert call_args[1]["filter"] == "tar"
|
|
|
|
def test_data_filter_extraction(self, sample_files, temp_dir):
|
|
"""Test extraction with data security filter."""
|
|
file_paths = [f for f in sample_files if f.is_file()]
|
|
archive_path = temp_dir / "data_filter_test.tzst"
|
|
|
|
# Create archive
|
|
create_archive(archive_path, file_paths)
|
|
|
|
# Extract with data filter (default for security)
|
|
extract_dir = temp_dir / "data_filtered"
|
|
with patch("tzst.core.TzstArchive.extract") as mock_extract:
|
|
extract_archive(archive_path, extract_dir, filter="data")
|
|
|
|
# Verify filter was passed
|
|
call_args = mock_extract.call_args
|
|
assert call_args[1]["filter"] == "data"
|
|
|
|
def test_invalid_filter_raises_error(self, sample_files, temp_dir):
|
|
"""Test that invalid filters raise appropriate errors."""
|
|
file_paths = [f for f in sample_files if f.is_file()]
|
|
archive_path = temp_dir / "invalid_filter_test.tzst"
|
|
|
|
# Create archive
|
|
create_archive(archive_path, file_paths)
|
|
|
|
# Try to extract with invalid filter
|
|
extract_dir = temp_dir / "invalid_filtered"
|
|
with pytest.raises(ValueError):
|
|
extract_archive(archive_path, extract_dir, filter="invalid")
|
|
|
|
|
|
class TestCompressionValidation:
|
|
"""Test compression level validation."""
|
|
|
|
def test_compression_level_boundary_values(self, sample_files, temp_dir):
|
|
"""Test boundary compression level values."""
|
|
file_paths = [f for f in sample_files if f.is_file()]
|
|
|
|
# Test level 1 (minimum valid)
|
|
archive_path_min = temp_dir / "min_compression.tzst"
|
|
create_archive(archive_path_min, file_paths, compression_level=1)
|
|
assert archive_path_min.exists()
|
|
assert tzst_test_archive(archive_path_min) is True
|
|
|
|
# Test level 22 (maximum valid)
|
|
archive_path_max = temp_dir / "max_compression.tzst"
|
|
create_archive(archive_path_max, file_paths, compression_level=22)
|
|
assert archive_path_max.exists()
|
|
assert tzst_test_archive(archive_path_max) is True
|
|
|
|
def test_invalid_compression_levels_raise_error(self, sample_files, temp_dir):
|
|
"""Test that invalid compression levels raise appropriate errors."""
|
|
file_paths = [f for f in sample_files if f.is_file()]
|
|
archive_path = temp_dir / "invalid_compression.tzst"
|
|
|
|
# Test invalid levels that should raise ValueError
|
|
for invalid_level in [0, 23, -1, 100]:
|
|
with pytest.raises(ValueError):
|
|
create_archive(
|
|
archive_path, file_paths, compression_level=invalid_level
|
|
)
|
|
|
|
|
|
class TestSpecialFileTypes:
|
|
"""Test handling of special file types and edge cases."""
|
|
|
|
def test_empty_files(self, temp_dir):
|
|
"""Test archiving and extracting empty files."""
|
|
empty_file = temp_dir / "empty.txt"
|
|
empty_file.touch()
|
|
|
|
archive_path = temp_dir / "empty_file.tzst"
|
|
create_archive(archive_path, [empty_file])
|
|
|
|
assert archive_path.exists()
|
|
assert tzst_test_archive(archive_path) is True
|
|
|
|
# Test extraction
|
|
extract_dir = temp_dir / "empty_extracted"
|
|
extract_archive(archive_path, extract_dir)
|
|
|
|
extracted_file = extract_dir / "empty.txt"
|
|
assert extracted_file.exists()
|
|
assert extracted_file.stat().st_size == 0
|
|
|
|
def test_binary_files(self, temp_dir):
|
|
"""Test archiving binary files."""
|
|
binary_file = temp_dir / "binary.bin"
|
|
binary_content = bytes(range(256)) * 100 # 25.6KB of binary data
|
|
binary_file.write_bytes(binary_content)
|
|
|
|
archive_path = temp_dir / "binary_file.tzst"
|
|
create_archive(archive_path, [binary_file])
|
|
|
|
assert archive_path.exists()
|
|
assert tzst_test_archive(archive_path) is True
|
|
|
|
# Test extraction
|
|
extract_dir = temp_dir / "binary_extracted"
|
|
extract_archive(archive_path, extract_dir)
|
|
|
|
extracted_file = extract_dir / "binary.bin"
|
|
assert extracted_file.exists()
|
|
extracted_content = extracted_file.read_bytes()
|
|
assert extracted_content == binary_content
|
|
|
|
def test_files_with_special_characters(self, temp_dir):
|
|
"""Test files with special characters in names."""
|
|
special_chars_file = temp_dir / "file!@#$%^&()_+{}[]-',.txt"
|
|
special_chars_file.write_text("Special characters content")
|
|
|
|
archive_path = temp_dir / "special_chars.tzst"
|
|
create_archive(archive_path, [special_chars_file])
|
|
|
|
assert archive_path.exists()
|
|
assert tzst_test_archive(archive_path) is True
|
|
|
|
|
|
class TestAppendModeDocumentation:
|
|
"""Test that append mode provides helpful error messages."""
|
|
|
|
def test_append_mode_error_message(self, temp_dir):
|
|
"""Test that append mode raises informative error."""
|
|
archive_path = temp_dir / "append_test.tzst"
|
|
|
|
with pytest.raises(NotImplementedError) as exc_info:
|
|
TzstArchive(archive_path, "a")
|
|
|
|
error_msg = str(exc_info.value)
|
|
assert "append mode" in error_msg.lower()
|
|
assert "alternatives" in error_msg.lower() or "alternative" in error_msg.lower()
|
|
assert "decompressing" in error_msg.lower()
|
|
assert "recompressing" in error_msg.lower()
|
|
|
|
def test_append_mode_error_in_open(self, temp_dir):
|
|
"""Test append mode error when opening existing archive."""
|
|
archive_path = temp_dir / "append_open_test.tzst"
|
|
|
|
# Create an archive first
|
|
with TzstArchive(archive_path, "w"):
|
|
pass
|
|
|
|
# Try to open in append mode
|
|
with pytest.raises(NotImplementedError) as exc_info:
|
|
TzstArchive(archive_path, "a")
|
|
|
|
error_msg = str(exc_info.value)
|
|
assert (
|
|
"multiple archives" in error_msg.lower() or "recreate" in error_msg.lower()
|
|
)
|