- Renamed `initiate_drag` to `handle_drag` in MainWindow and updated related tests. - Improved drag handling logic to utilize a bridge for starting file drags. - Updated `_on_drag_started` and `_on_drag_failed` methods to match new signatures. - Modified test cases to reflect changes in drag handling and assertions. Enhance path validation and logging - Updated `PathValidator` to log warnings for nonexistent roots instead of raising errors. - Adjusted tests to verify the new behavior of skipping nonexistent roots. Update web application UI and functionality - Changed displayed text for drag items to reflect local paths and Azure Blob Storage URLs. - Added debug logging for drag operations in the web application. - Improved instructions for testing drag and drop functionality. Add configuration documentation and example files - Created `CONFIG_README.md` to provide detailed configuration instructions for WebDrop Bridge. - Added `config.example.json` and `config_test.json` for reference and testing purposes. Implement URL conversion logic - Introduced `URLConverter` class to handle conversion of Azure Blob Storage URLs to local paths. - Added unit tests for URL conversion to ensure correct functionality. Develop download interceptor script - Created `download_interceptor.js` to intercept download-related actions in the web application. - Implemented logging for fetch calls, XMLHttpRequests, and Blob URL creations. Add download test page and related tests - Created `test_download.html` for testing various download scenarios. - Implemented `test_download.py` to verify download path resolution and file construction. - Added `test_url_mappings.py` to ensure URL mappings are loaded correctly. Add unit tests for URL converter - Created `test_url_converter.py` to validate URL conversion logic and mapping behavior.
181 lines
6 KiB
Python
181 lines
6 KiB
Python
"""Unit tests for path validator."""
|
|
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from webdrop_bridge.core.validator import PathValidator, ValidationError
|
|
|
|
|
|
class TestPathValidator:
|
|
"""Test path validation."""
|
|
|
|
def test_validator_initialization(self, tmp_path):
|
|
"""Test creating a validator with valid roots."""
|
|
dir1 = tmp_path / "dir1"
|
|
dir2 = tmp_path / "dir2"
|
|
dir1.mkdir()
|
|
dir2.mkdir()
|
|
|
|
validator = PathValidator([dir1, dir2])
|
|
|
|
assert len(validator.allowed_roots) == 2
|
|
|
|
def test_validator_nonexistent_root(self, tmp_path):
|
|
"""Test that nonexistent root is logged as warning but doesn't raise error."""
|
|
nonexistent = tmp_path / "nonexistent"
|
|
|
|
# Should not raise - just logs warning and skips the root
|
|
validator = PathValidator([nonexistent])
|
|
assert len(validator.allowed_roots) == 0 # Non-existent roots are skipped
|
|
|
|
def test_validator_non_directory_root(self, tmp_path):
|
|
"""Test that non-directory root raises ValidationError."""
|
|
file_path = tmp_path / "file.txt"
|
|
file_path.write_text("test")
|
|
|
|
with pytest.raises(ValidationError, match="not a directory"):
|
|
PathValidator([file_path])
|
|
|
|
def test_validate_valid_file(self, tmp_path):
|
|
"""Test validating a file within allowed root."""
|
|
file_path = tmp_path / "test.txt"
|
|
file_path.write_text("test content")
|
|
|
|
validator = PathValidator([tmp_path])
|
|
|
|
assert validator.validate(file_path) is True
|
|
assert validator.is_valid(file_path) is True
|
|
|
|
def test_validate_nonexistent_file(self, tmp_path):
|
|
"""Test that nonexistent file raises ValidationError."""
|
|
validator = PathValidator([tmp_path])
|
|
nonexistent = tmp_path / "nonexistent.txt"
|
|
|
|
with pytest.raises(ValidationError, match="does not exist"):
|
|
validator.validate(nonexistent)
|
|
|
|
def test_validate_directory_path(self, tmp_path):
|
|
"""Test that directory path raises ValidationError."""
|
|
subdir = tmp_path / "subdir"
|
|
subdir.mkdir()
|
|
|
|
validator = PathValidator([tmp_path])
|
|
|
|
with pytest.raises(ValidationError, match="not a regular file"):
|
|
validator.validate(subdir)
|
|
|
|
def test_validate_file_outside_roots(self, tmp_path):
|
|
"""Test that file outside allowed roots raises ValidationError."""
|
|
allowed_dir = tmp_path / "allowed"
|
|
other_dir = tmp_path / "other"
|
|
allowed_dir.mkdir()
|
|
other_dir.mkdir()
|
|
|
|
file_in_other = other_dir / "test.txt"
|
|
file_in_other.write_text("test")
|
|
|
|
validator = PathValidator([allowed_dir])
|
|
|
|
with pytest.raises(ValidationError, match="not within allowed roots"):
|
|
validator.validate(file_in_other)
|
|
|
|
def test_validate_multiple_roots(self, tmp_path):
|
|
"""Test validating files in multiple allowed roots."""
|
|
dir1 = tmp_path / "dir1"
|
|
dir2 = tmp_path / "dir2"
|
|
dir1.mkdir()
|
|
dir2.mkdir()
|
|
|
|
file1 = dir1 / "file1.txt"
|
|
file2 = dir2 / "file2.txt"
|
|
file1.write_text("content1")
|
|
file2.write_text("content2")
|
|
|
|
validator = PathValidator([dir1, dir2])
|
|
|
|
assert validator.validate(file1) is True
|
|
assert validator.validate(file2) is True
|
|
|
|
def test_validate_with_relative_path(self, tmp_path):
|
|
"""Test validating with relative path (gets resolved)."""
|
|
import os
|
|
|
|
file_path = tmp_path / "test.txt"
|
|
file_path.write_text("test")
|
|
|
|
validator = PathValidator([tmp_path])
|
|
|
|
# Change to tmp_path directory
|
|
original_cwd = os.getcwd()
|
|
try:
|
|
os.chdir(tmp_path)
|
|
# Use relative path
|
|
assert validator.validate(Path("test.txt")) is True
|
|
finally:
|
|
os.chdir(original_cwd)
|
|
|
|
def test_validate_with_path_traversal(self, tmp_path):
|
|
"""Test that path traversal attacks are blocked."""
|
|
allowed_dir = tmp_path / "allowed"
|
|
other_dir = tmp_path / "other"
|
|
allowed_dir.mkdir()
|
|
other_dir.mkdir()
|
|
|
|
file_in_other = other_dir / "secret.txt"
|
|
file_in_other.write_text("secret")
|
|
|
|
validator = PathValidator([allowed_dir])
|
|
|
|
# Try to access file outside root using ..
|
|
traversal_path = allowed_dir / ".." / "other" / "secret.txt"
|
|
|
|
with pytest.raises(ValidationError, match="not within allowed roots"):
|
|
validator.validate(traversal_path)
|
|
|
|
def test_is_valid_doesnt_raise(self, tmp_path):
|
|
"""Test that is_valid() never raises exceptions."""
|
|
validator = PathValidator([tmp_path])
|
|
|
|
# These should all return False, not raise
|
|
assert validator.is_valid(Path("/nonexistent")) is False
|
|
assert validator.is_valid(tmp_path) is False # Directory, not file
|
|
|
|
# Valid file should return True
|
|
file_path = tmp_path / "test.txt"
|
|
file_path.write_text("test")
|
|
assert validator.is_valid(file_path) is True
|
|
|
|
|
|
class TestPathValidatorEdgeCases:
|
|
"""Test edge cases in path validation."""
|
|
|
|
def test_symlink_to_valid_file(self, tmp_path):
|
|
"""Test validating a symlink to a valid file."""
|
|
# Skip on Windows if symlink creation fails
|
|
actual_file = tmp_path / "actual.txt"
|
|
actual_file.write_text("content")
|
|
|
|
try:
|
|
symlink = tmp_path / "link.txt"
|
|
symlink.symlink_to(actual_file)
|
|
|
|
validator = PathValidator([tmp_path])
|
|
# Symlinks resolve to their target, should validate
|
|
assert validator.validate(symlink) is True
|
|
|
|
except (OSError, NotImplementedError):
|
|
# Skip if symlinks not supported
|
|
pytest.skip("Symlinks not supported on this platform")
|
|
|
|
def test_nested_files_in_allowed_root(self, tmp_path):
|
|
"""Test validating files in nested subdirectories."""
|
|
nested_dir = tmp_path / "a" / "b" / "c"
|
|
nested_dir.mkdir(parents=True)
|
|
|
|
nested_file = nested_dir / "file.txt"
|
|
nested_file.write_text("content")
|
|
|
|
validator = PathValidator([tmp_path])
|
|
|
|
assert validator.validate(nested_file) is True
|