Implement configuration management, drag-and-drop functionality, and logging utilities for WebDrop Bridge
This commit is contained in:
parent
04ef84cf9a
commit
6bef2f6119
9 changed files with 1154 additions and 0 deletions
181
tests/unit/test_validator.py
Normal file
181
tests/unit/test_validator.py
Normal file
|
|
@ -0,0 +1,181 @@
|
|||
"""Unit tests for path validator."""
|
||||
|
||||
from pathlib import Path
|
||||
from tempfile import TemporaryDirectory
|
||||
|
||||
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 raises ValidationError."""
|
||||
nonexistent = tmp_path / "nonexistent"
|
||||
|
||||
with pytest.raises(ValidationError, match="does not exist"):
|
||||
PathValidator([nonexistent])
|
||||
|
||||
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
|
||||
Loading…
Add table
Add a link
Reference in a new issue