893 lines
36 KiB
Python
893 lines
36 KiB
Python
"""
|
|
Tests for conditions.py - Download conditions management.
|
|
|
|
This test suite provides comprehensive coverage for the conditions module:
|
|
- Tests Condition dataclass functionality
|
|
- Tests Conditions singleton class behavior
|
|
- Tests condition loading, saving, and validation
|
|
- Tests condition matching against info dicts
|
|
- Tests error handling and edge cases
|
|
|
|
Total test functions: 15+
|
|
All condition management functionality and edge cases are covered.
|
|
"""
|
|
|
|
import json
|
|
import tempfile
|
|
import uuid
|
|
from pathlib import Path
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
import pytest
|
|
|
|
from app.library.conditions import Condition, Conditions
|
|
|
|
|
|
class TestCondition:
|
|
"""Test the Condition dataclass."""
|
|
|
|
def test_condition_creation_with_defaults(self):
|
|
"""Test creating a condition with default values."""
|
|
condition = Condition(name="test", filter="duration > 60")
|
|
|
|
assert condition.id, "Check that ID is generated"
|
|
assert isinstance(condition.id, str)
|
|
|
|
# Check required fields
|
|
assert condition.name == "test"
|
|
assert condition.filter == "duration > 60"
|
|
|
|
assert condition.cli == "", "Check defaults"
|
|
assert condition.extras == {}
|
|
assert condition.enabled is True
|
|
|
|
def test_condition_creation_with_all_fields(self):
|
|
"""Test creating a condition with all fields specified."""
|
|
test_id = str(uuid.uuid4())
|
|
extras = {"key": "value", "number": 42}
|
|
|
|
condition = Condition(
|
|
id=test_id, name="full_test", filter="uploader = 'test'", cli="--format best", extras=extras, enabled=False
|
|
)
|
|
|
|
assert condition.id == test_id
|
|
assert condition.name == "full_test"
|
|
assert condition.filter == "uploader = 'test'"
|
|
assert condition.cli == "--format best"
|
|
assert condition.extras == extras
|
|
assert condition.enabled is False
|
|
|
|
def test_condition_serialize(self):
|
|
"""Test condition serialization to dict."""
|
|
condition = Condition(
|
|
name="serialize_test", filter="title ~= 'test'", cli="--audio-quality 0", extras={"tag": "music"}
|
|
)
|
|
|
|
serialized = condition.serialize()
|
|
|
|
assert isinstance(serialized, dict)
|
|
assert serialized["name"] == "serialize_test"
|
|
assert serialized["filter"] == "title ~= 'test'"
|
|
assert serialized["cli"] == "--audio-quality 0"
|
|
assert serialized["extras"] == {"tag": "music"}
|
|
assert "id" in serialized
|
|
|
|
def test_condition_json(self):
|
|
"""Test condition JSON serialization."""
|
|
condition = Condition(name="json_test", filter="duration < 300")
|
|
|
|
json_str = condition.json()
|
|
|
|
assert isinstance(json_str, str)
|
|
# Should be valid JSON
|
|
data = json.loads(json_str)
|
|
assert data["name"] == "json_test"
|
|
assert data["filter"] == "duration < 300"
|
|
|
|
def test_condition_get_method(self):
|
|
"""Test condition get method for accessing fields."""
|
|
condition = Condition(name="get_test", filter="view_count > 1000", extras={"category": "popular"})
|
|
|
|
assert condition.get("name") == "get_test"
|
|
assert condition.get("filter") == "view_count > 1000"
|
|
assert condition.get("extras") == {"category": "popular"}
|
|
assert condition.get("nonexistent") is None
|
|
assert condition.get("nonexistent", "default") == "default"
|
|
|
|
|
|
class TestConditions:
|
|
"""Test the Conditions singleton class."""
|
|
|
|
def setup_method(self):
|
|
"""Set up test fixtures by clearing singleton instances."""
|
|
Conditions._reset_singleton()
|
|
|
|
def test_conditions_singleton(self):
|
|
"""Test that Conditions follows singleton pattern."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
file_path = Path(temp_dir) / "test_conditions.json"
|
|
|
|
instance1 = Conditions(file=file_path)
|
|
instance2 = Conditions.get_instance()
|
|
|
|
assert instance1 is instance2
|
|
|
|
def test_conditions_initialization(self):
|
|
"""Test Conditions initialization with file path."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
file_path = Path(temp_dir) / "test_conditions.json"
|
|
|
|
conditions = Conditions(file=file_path)
|
|
|
|
assert conditions._file == file_path
|
|
assert isinstance(conditions._items, list)
|
|
|
|
@patch("app.library.conditions.Config.get_instance")
|
|
def test_conditions_default_file_path(self, mock_config):
|
|
"""Test Conditions uses default config path when no file specified."""
|
|
mock_config_instance = MagicMock()
|
|
mock_config_instance.config_path = "/test/config"
|
|
mock_config.return_value = mock_config_instance
|
|
|
|
conditions = Conditions()
|
|
|
|
expected_path = Path("/test/config") / "conditions.json"
|
|
assert conditions._file == expected_path
|
|
|
|
def test_get_all_empty(self):
|
|
"""Test get_all returns empty list when no conditions loaded."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
file_path = Path(temp_dir) / "empty_conditions.json"
|
|
conditions = Conditions(file=file_path)
|
|
|
|
result = conditions.get_all()
|
|
|
|
assert isinstance(result, list)
|
|
assert len(result) == 0
|
|
|
|
def test_clear(self):
|
|
"""Test clearing all conditions."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
file_path = Path(temp_dir) / "clear_test.json"
|
|
conditions = Conditions(file=file_path)
|
|
|
|
# Add some test conditions
|
|
conditions._items = [
|
|
Condition(name="test1", filter="duration > 60"),
|
|
Condition(name="test2", filter="uploader = 'test'"),
|
|
]
|
|
|
|
result = conditions.clear()
|
|
|
|
assert result is conditions # Should return self
|
|
assert len(conditions._items) == 0
|
|
|
|
def test_clear_when_already_empty(self):
|
|
"""Test clearing when conditions list is already empty."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
file_path = Path(temp_dir) / "already_empty.json"
|
|
conditions = Conditions(file=file_path)
|
|
|
|
result = conditions.clear()
|
|
|
|
assert result is conditions
|
|
assert len(conditions._items) == 0
|
|
|
|
def test_load_nonexistent_file(self):
|
|
"""Test loading from non-existent file."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
file_path = Path(temp_dir) / "nonexistent.json"
|
|
conditions = Conditions(file=file_path)
|
|
|
|
result = conditions.load()
|
|
|
|
assert result is conditions
|
|
assert len(conditions._items) == 0
|
|
|
|
def test_load_empty_file(self):
|
|
"""Test loading from empty file."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
file_path = Path(temp_dir) / "empty.json"
|
|
file_path.touch() # Create empty file
|
|
|
|
conditions = Conditions(file=file_path)
|
|
result = conditions.load()
|
|
|
|
assert result is conditions
|
|
assert len(conditions._items) == 0
|
|
|
|
def test_load_valid_conditions(self):
|
|
"""Test loading valid conditions from file."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
file_path = Path(temp_dir) / "valid_conditions.json"
|
|
|
|
# Create test data
|
|
test_data = [
|
|
{
|
|
"id": str(uuid.uuid4()),
|
|
"name": "short_videos",
|
|
"filter": "duration < 300",
|
|
"cli": "--format worst",
|
|
"extras": {"category": "short"},
|
|
"enabled": True,
|
|
"priority": 0,
|
|
"description": "Download short videos",
|
|
},
|
|
{
|
|
"id": str(uuid.uuid4()),
|
|
"name": "music_videos",
|
|
"filter": "title ~= 'music'",
|
|
"cli": "--audio-quality 0",
|
|
"extras": {"type": "audio"},
|
|
"enabled": False,
|
|
"priority": 5,
|
|
"description": "High priority music videos",
|
|
},
|
|
]
|
|
|
|
file_path.write_text(json.dumps(test_data, indent=4))
|
|
|
|
conditions = Conditions(file=file_path)
|
|
result = conditions.load()
|
|
|
|
assert result is conditions
|
|
assert len(conditions._items) == 2
|
|
|
|
assert conditions._items[0].name == "short_videos", "Check first condition"
|
|
assert conditions._items[0].filter == "duration < 300"
|
|
assert conditions._items[0].cli == "--format worst"
|
|
assert conditions._items[0].extras == {"category": "short"}
|
|
assert conditions._items[0].enabled is True
|
|
assert conditions._items[0].priority == 0
|
|
assert conditions._items[0].description == "Download short videos"
|
|
|
|
assert conditions._items[1].name == "music_videos", "Check second condition"
|
|
assert conditions._items[1].filter == "title ~= 'music'"
|
|
assert conditions._items[1].enabled is False
|
|
assert conditions._items[1].priority == 5
|
|
assert conditions._items[1].description == "High priority music videos"
|
|
|
|
def test_load_conditions_without_id(self):
|
|
"""Test loading conditions that don't have ID (should generate ID)."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
file_path = Path(temp_dir) / "no_id_conditions.json"
|
|
|
|
# Create test data without ID
|
|
test_data = [{"name": "no_id_test", "filter": "duration > 120"}]
|
|
|
|
file_path.write_text(json.dumps(test_data))
|
|
|
|
with patch.object(Conditions, "save") as mock_save:
|
|
conditions = Conditions(file=file_path)
|
|
conditions.load()
|
|
|
|
assert len(conditions._items) == 1, "Should have generated ID"
|
|
assert conditions._items[0].id
|
|
assert conditions._items[0].name == "no_id_test"
|
|
|
|
# Should call save due to changes
|
|
mock_save.assert_called_once()
|
|
|
|
def test_load_conditions_without_extras(self):
|
|
"""Test loading conditions that don't have extras field."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
file_path = Path(temp_dir) / "no_extras_conditions.json"
|
|
|
|
test_data = [{"id": str(uuid.uuid4()), "name": "no_extras_test", "filter": "uploader = 'test'"}]
|
|
|
|
file_path.write_text(json.dumps(test_data))
|
|
|
|
with patch.object(Conditions, "save") as mock_save:
|
|
conditions = Conditions(file=file_path)
|
|
conditions.load()
|
|
|
|
assert len(conditions._items) == 1, "Should have generated empty extras"
|
|
assert conditions._items[0].extras == {}
|
|
|
|
# Should call save due to changes
|
|
mock_save.assert_called_once()
|
|
|
|
def test_load_conditions_without_enabled(self):
|
|
"""Test loading conditions that don't have enabled field (should default to True)."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
file_path = Path(temp_dir) / "no_enabled_conditions.json"
|
|
|
|
test_data = [{"id": str(uuid.uuid4()), "name": "no_enabled_test", "filter": "duration > 60", "extras": {}}]
|
|
|
|
file_path.write_text(json.dumps(test_data))
|
|
|
|
with patch.object(Conditions, "save") as mock_save:
|
|
conditions = Conditions(file=file_path)
|
|
conditions.load()
|
|
|
|
assert len(conditions._items) == 1, "Should have generated enabled=True"
|
|
assert conditions._items[0].enabled is True
|
|
|
|
# Should call save due to changes
|
|
mock_save.assert_called_once()
|
|
|
|
def test_load_conditions_without_priority(self):
|
|
"""Test loading conditions that don't have priority field (should default to 0)."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
file_path = Path(temp_dir) / "no_priority_conditions.json"
|
|
|
|
test_data = [{"id": str(uuid.uuid4()), "name": "no_priority_test", "filter": "duration > 60", "extras": {}}]
|
|
|
|
file_path.write_text(json.dumps(test_data))
|
|
|
|
with patch.object(Conditions, "save") as mock_save:
|
|
conditions = Conditions(file=file_path)
|
|
conditions.load()
|
|
|
|
assert len(conditions._items) == 1, "Should have generated priority=0"
|
|
assert conditions._items[0].priority == 0
|
|
|
|
# Should call save due to changes
|
|
mock_save.assert_called_once()
|
|
|
|
def test_load_conditions_without_description(self):
|
|
"""Test loading conditions that don't have description field (should default to empty string)."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
file_path = Path(temp_dir) / "no_description_conditions.json"
|
|
|
|
test_data = [
|
|
{"id": str(uuid.uuid4()), "name": "no_description_test", "filter": "duration > 60", "extras": {}}
|
|
]
|
|
|
|
file_path.write_text(json.dumps(test_data))
|
|
|
|
with patch.object(Conditions, "save") as mock_save:
|
|
conditions = Conditions(file=file_path)
|
|
conditions.load()
|
|
|
|
assert len(conditions._items) == 1, "Should have generated description=''"
|
|
assert conditions._items[0].description == ""
|
|
|
|
# Should call save due to changes
|
|
mock_save.assert_called_once()
|
|
|
|
def test_load_invalid_json(self):
|
|
"""Test loading file with invalid JSON."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
file_path = Path(temp_dir) / "invalid.json"
|
|
file_path.write_text("invalid json content")
|
|
|
|
conditions = Conditions(file=file_path)
|
|
result = conditions.load()
|
|
|
|
assert result is conditions
|
|
assert len(conditions._items) == 0
|
|
|
|
def test_load_invalid_condition_data(self):
|
|
"""Test loading file with invalid condition data."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
file_path = Path(temp_dir) / "invalid_data.json"
|
|
|
|
# Missing required fields
|
|
test_data = [
|
|
{"id": "valid", "name": "valid", "filter": "duration > 60"},
|
|
{"invalid": "data"}, # Missing required fields
|
|
]
|
|
|
|
file_path.write_text(json.dumps(test_data))
|
|
|
|
conditions = Conditions(file=file_path)
|
|
result = conditions.load()
|
|
|
|
assert result is conditions, "Should load only valid conditions"
|
|
assert len(conditions._items) == 1
|
|
assert conditions._items[0].name == "valid"
|
|
|
|
def test_validate_valid_condition_dict(self):
|
|
"""Test validating a valid condition dictionary."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
file_path = Path(temp_dir) / "validate_test.json"
|
|
conditions = Conditions(file=file_path)
|
|
|
|
valid_condition = {
|
|
"id": str(uuid.uuid4()),
|
|
"name": "valid_test",
|
|
"filter": "duration > 60",
|
|
"cli": "--format best",
|
|
"extras": {"key": "value"},
|
|
}
|
|
|
|
result = conditions.validate(valid_condition)
|
|
assert result is True
|
|
|
|
def test_validate_valid_condition_object(self):
|
|
"""Test validating a valid Condition object."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
file_path = Path(temp_dir) / "validate_obj_test.json"
|
|
conditions = Conditions(file=file_path)
|
|
|
|
valid_condition = Condition(name="valid_obj_test", filter="uploader = 'test'")
|
|
|
|
result = conditions.validate(valid_condition)
|
|
assert result is True
|
|
|
|
def test_validate_missing_id(self):
|
|
"""Test validating condition without ID."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
file_path = Path(temp_dir) / "no_id_validate.json"
|
|
conditions = Conditions(file=file_path)
|
|
|
|
invalid_condition = {"name": "no_id_test", "filter": "duration > 60"}
|
|
|
|
with pytest.raises(ValueError, match="No id found"):
|
|
conditions.validate(invalid_condition)
|
|
|
|
def test_validate_missing_name(self):
|
|
"""Test validating condition without name."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
file_path = Path(temp_dir) / "no_name_validate.json"
|
|
conditions = Conditions(file=file_path)
|
|
|
|
invalid_condition = {"id": str(uuid.uuid4()), "filter": "duration > 60"}
|
|
|
|
with pytest.raises(ValueError, match="No name found"):
|
|
conditions.validate(invalid_condition)
|
|
|
|
def test_validate_missing_filter(self):
|
|
"""Test validating condition without filter."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
file_path = Path(temp_dir) / "no_filter_validate.json"
|
|
conditions = Conditions(file=file_path)
|
|
|
|
invalid_condition = {"id": str(uuid.uuid4()), "name": "no_filter_test"}
|
|
|
|
with pytest.raises(ValueError, match="No filter found"):
|
|
conditions.validate(invalid_condition)
|
|
|
|
def test_validate_invalid_filter(self):
|
|
"""Test validating condition with invalid filter syntax."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
file_path = Path(temp_dir) / "invalid_filter.json"
|
|
conditions = Conditions(file=file_path)
|
|
|
|
# Use a filter that will cause a syntax error in the parser
|
|
invalid_condition = {
|
|
"id": str(uuid.uuid4()),
|
|
"name": "invalid_filter_test",
|
|
"filter": "duration > & < 60", # Invalid syntax with consecutive operators
|
|
"cli": "",
|
|
"extras": {},
|
|
}
|
|
|
|
with pytest.raises(ValueError, match="Invalid filter"):
|
|
conditions.validate(invalid_condition)
|
|
|
|
def test_validate_invalid_cli(self):
|
|
"""Test validating condition with invalid CLI options."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
file_path = Path(temp_dir) / "invalid_cli.json"
|
|
conditions = Conditions(file=file_path)
|
|
|
|
invalid_condition = {
|
|
"id": str(uuid.uuid4()),
|
|
"name": "invalid_cli_test",
|
|
"filter": "duration > 60",
|
|
"cli": "--invalid-option-that-does-not-exist",
|
|
}
|
|
|
|
with pytest.raises(ValueError, match="Invalid command options"):
|
|
conditions.validate(invalid_condition)
|
|
|
|
def test_validate_invalid_extras_type(self):
|
|
"""Test validating condition with non-dict extras."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
file_path = Path(temp_dir) / "invalid_extras.json"
|
|
conditions = Conditions(file=file_path)
|
|
|
|
invalid_condition = {
|
|
"id": str(uuid.uuid4()),
|
|
"name": "invalid_extras_test",
|
|
"filter": "duration > 60",
|
|
"extras": "not a dict",
|
|
}
|
|
|
|
with pytest.raises(ValueError, match="Extras must be a dictionary"):
|
|
conditions.validate(invalid_condition)
|
|
|
|
def test_validate_invalid_enabled_type(self):
|
|
"""Test validating condition with non-boolean enabled field."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
file_path = Path(temp_dir) / "invalid_enabled.json"
|
|
conditions = Conditions(file=file_path)
|
|
|
|
invalid_condition = {
|
|
"id": str(uuid.uuid4()),
|
|
"name": "invalid_enabled_test",
|
|
"filter": "duration > 60",
|
|
"extras": {},
|
|
"enabled": "yes", # Should be boolean, not string
|
|
}
|
|
|
|
with pytest.raises(ValueError, match="Enabled must be a boolean"):
|
|
conditions.validate(invalid_condition)
|
|
|
|
def test_validate_invalid_priority_type(self):
|
|
"""Test validating condition with non-integer priority field."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
file_path = Path(temp_dir) / "invalid_priority_type.json"
|
|
conditions = Conditions(file=file_path)
|
|
|
|
invalid_condition = {
|
|
"id": str(uuid.uuid4()),
|
|
"name": "invalid_priority_type_test",
|
|
"filter": "duration > 60",
|
|
"extras": {},
|
|
"priority": "10", # Should be integer, not string
|
|
}
|
|
|
|
with pytest.raises(ValueError, match="Priority must be an integer"):
|
|
conditions.validate(invalid_condition)
|
|
|
|
def test_validate_invalid_priority_negative(self):
|
|
"""Test validating condition with negative priority."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
file_path = Path(temp_dir) / "invalid_priority_negative.json"
|
|
conditions = Conditions(file=file_path)
|
|
|
|
invalid_condition = {
|
|
"id": str(uuid.uuid4()),
|
|
"name": "invalid_priority_negative_test",
|
|
"filter": "duration > 60",
|
|
"extras": {},
|
|
"priority": -5, # Should be >= 0
|
|
}
|
|
|
|
with pytest.raises(ValueError, match="Priority must be >= 0"):
|
|
conditions.validate(invalid_condition)
|
|
|
|
def test_validate_invalid_description_type(self):
|
|
"""Test validating condition with non-string description field."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
file_path = Path(temp_dir) / "invalid_description.json"
|
|
conditions = Conditions(file=file_path)
|
|
|
|
invalid_condition = {
|
|
"id": str(uuid.uuid4()),
|
|
"name": "invalid_description_test",
|
|
"filter": "duration > 60",
|
|
"extras": {},
|
|
"description": 123, # Should be string, not integer
|
|
}
|
|
|
|
with pytest.raises(ValueError, match="Description must be a string"):
|
|
conditions.validate(invalid_condition)
|
|
|
|
def test_validate_invalid_item_type(self):
|
|
"""Test validating invalid item type."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
file_path = Path(temp_dir) / "invalid_type.json"
|
|
conditions = Conditions(file=file_path)
|
|
|
|
with pytest.raises(ValueError, match=r"Unexpected.*item type"):
|
|
conditions.validate("invalid type")
|
|
|
|
def test_save_conditions(self):
|
|
"""Test saving conditions to file."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
file_path = Path(temp_dir) / "save_test.json"
|
|
conditions = Conditions(file=file_path)
|
|
|
|
test_conditions = [
|
|
Condition(name="save_test1", filter="duration > 60"),
|
|
Condition(name="save_test2", filter="uploader = 'test'"),
|
|
]
|
|
|
|
result = conditions.save(test_conditions)
|
|
|
|
assert result is conditions
|
|
assert file_path.exists()
|
|
|
|
# Verify file content
|
|
saved_data = json.loads(file_path.read_text())
|
|
assert len(saved_data) == 2
|
|
assert saved_data[0]["name"] == "save_test1"
|
|
assert saved_data[1]["name"] == "save_test2"
|
|
|
|
def test_save_conditions_dict_format(self):
|
|
"""Test saving conditions in dictionary format."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
file_path = Path(temp_dir) / "save_dict_test.json"
|
|
conditions = Conditions(file=file_path)
|
|
|
|
test_conditions = [
|
|
{"id": str(uuid.uuid4()), "name": "dict_test", "filter": "duration < 300", "cli": "", "extras": {}}
|
|
]
|
|
|
|
conditions.save(test_conditions)
|
|
|
|
assert file_path.exists()
|
|
saved_data = json.loads(file_path.read_text())
|
|
assert len(saved_data) == 1
|
|
assert saved_data[0]["name"] == "dict_test"
|
|
|
|
def test_has_condition_by_id(self):
|
|
"""Test checking if condition exists by ID."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
file_path = Path(temp_dir) / "has_test.json"
|
|
conditions = Conditions(file=file_path)
|
|
|
|
test_id = str(uuid.uuid4())
|
|
test_condition = Condition(id=test_id, name="has_test", filter="duration > 60")
|
|
conditions._items = [test_condition]
|
|
|
|
assert conditions.has(test_id) is True
|
|
assert conditions.has("nonexistent") is False
|
|
|
|
def test_has_condition_by_name(self):
|
|
"""Test checking if condition exists by name."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
file_path = Path(temp_dir) / "has_name_test.json"
|
|
conditions = Conditions(file=file_path)
|
|
|
|
test_condition = Condition(name="has_name_test", filter="uploader = 'test'")
|
|
conditions._items = [test_condition]
|
|
|
|
assert conditions.has("has_name_test") is True
|
|
assert conditions.has("nonexistent_name") is False
|
|
|
|
def test_get_condition_by_id(self):
|
|
"""Test getting condition by ID."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
file_path = Path(temp_dir) / "get_id_test.json"
|
|
conditions = Conditions(file=file_path)
|
|
|
|
test_id = str(uuid.uuid4())
|
|
test_condition = Condition(id=test_id, name="get_id_test", filter="duration > 120")
|
|
conditions._items = [test_condition]
|
|
|
|
result = conditions.get(test_id)
|
|
assert result is test_condition
|
|
|
|
assert conditions.get("nonexistent") is None
|
|
|
|
def test_get_condition_by_name(self):
|
|
"""Test getting condition by name."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
file_path = Path(temp_dir) / "get_name_test.json"
|
|
conditions = Conditions(file=file_path)
|
|
|
|
test_condition = Condition(name="get_name_test", filter="title ~= 'music'")
|
|
conditions._items = [test_condition]
|
|
|
|
result = conditions.get("get_name_test")
|
|
assert result is test_condition
|
|
|
|
def test_get_condition_empty_id_or_name(self):
|
|
"""Test getting condition with empty ID or name."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
file_path = Path(temp_dir) / "empty_get_test.json"
|
|
conditions = Conditions(file=file_path)
|
|
|
|
assert conditions.get("") is None
|
|
assert conditions.get(None) is None
|
|
|
|
@patch("app.library.conditions.match_str")
|
|
def test_match_condition_found(self, mock_match_str):
|
|
"""Test matching condition against info dict."""
|
|
mock_match_str.return_value = True
|
|
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
file_path = Path(temp_dir) / "match_test.json"
|
|
conditions = Conditions(file=file_path)
|
|
|
|
test_condition = Condition(name="match_test", filter="duration > 60")
|
|
conditions._items = [test_condition]
|
|
|
|
info_dict = {"duration": 120, "title": "Test Video"}
|
|
result = conditions.match(info_dict)
|
|
|
|
assert result is test_condition
|
|
mock_match_str.assert_called_once_with("duration > 60", info_dict)
|
|
|
|
@patch("app.library.conditions.match_str")
|
|
def test_match_condition_not_found(self, mock_match_str):
|
|
"""Test matching when no condition matches."""
|
|
mock_match_str.return_value = False
|
|
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
file_path = Path(temp_dir) / "no_match_test.json"
|
|
conditions = Conditions(file=file_path)
|
|
|
|
test_condition = Condition(name="no_match_test", filter="duration > 300")
|
|
conditions._items = [test_condition]
|
|
|
|
info_dict = {"duration": 60, "title": "Short Video"}
|
|
result = conditions.match(info_dict)
|
|
|
|
assert result is None
|
|
|
|
def test_match_empty_conditions(self):
|
|
"""Test matching with empty conditions list."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
file_path = Path(temp_dir) / "empty_match_test.json"
|
|
conditions = Conditions(file=file_path)
|
|
|
|
info_dict = {"duration": 120}
|
|
result = conditions.match(info_dict)
|
|
|
|
assert result is None
|
|
|
|
def test_match_invalid_info_dict(self):
|
|
"""Test matching with invalid info dict."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
file_path = Path(temp_dir) / "invalid_info_test.json"
|
|
conditions = Conditions(file=file_path)
|
|
|
|
test_condition = Condition(name="test", filter="duration > 60")
|
|
conditions._items = [test_condition]
|
|
|
|
assert conditions.match(None) is None, "Test with None"
|
|
|
|
assert conditions.match({}) is None, "Test with empty dict"
|
|
|
|
assert conditions.match("not a dict") is None, "Test with non-dict"
|
|
|
|
@patch("app.library.conditions.match_str")
|
|
def test_match_filter_evaluation_error(self, mock_match_str):
|
|
"""Test matching when filter evaluation raises exception."""
|
|
mock_match_str.side_effect = Exception("Filter error")
|
|
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
file_path = Path(temp_dir) / "filter_error_test.json"
|
|
conditions = Conditions(file=file_path)
|
|
|
|
test_condition = Condition(name="error_test", filter="invalid filter")
|
|
conditions._items = [test_condition]
|
|
|
|
info_dict = {"duration": 120}
|
|
result = conditions.match(info_dict)
|
|
|
|
assert result is None
|
|
|
|
@patch("app.library.conditions.match_str")
|
|
def test_match_disabled_condition(self, mock_match_str):
|
|
"""Test that disabled conditions are skipped during matching."""
|
|
mock_match_str.return_value = True
|
|
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
file_path = Path(temp_dir) / "disabled_match_test.json"
|
|
conditions = Conditions(file=file_path)
|
|
|
|
# Add disabled condition
|
|
disabled_condition = Condition(name="disabled_test", filter="duration > 60", enabled=False)
|
|
enabled_condition = Condition(name="enabled_test", filter="duration > 120", enabled=True)
|
|
conditions._items = [disabled_condition, enabled_condition]
|
|
|
|
info_dict = {"duration": 150}
|
|
result = conditions.match(info_dict)
|
|
|
|
assert result is enabled_condition, "Should skip disabled condition and match enabled one"
|
|
# Should only call match_str once for enabled condition
|
|
mock_match_str.assert_called_once_with("duration > 120", info_dict)
|
|
|
|
@patch("app.library.conditions.match_str")
|
|
def test_match_priority_sorting(self, mock_match_str):
|
|
"""Test that conditions are evaluated by priority (highest first)."""
|
|
# All filters will match, so we test that highest priority wins
|
|
mock_match_str.return_value = True
|
|
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
file_path = Path(temp_dir) / "priority_test.json"
|
|
conditions = Conditions(file=file_path)
|
|
|
|
# Add conditions with different priorities (not in priority order)
|
|
low_priority = Condition(name="low", filter="duration > 60", priority=1)
|
|
high_priority = Condition(name="high", filter="duration > 60", priority=10)
|
|
default_priority = Condition(name="default", filter="duration > 60", priority=0)
|
|
|
|
# Add in wrong order to verify sorting
|
|
conditions._items = [low_priority, default_priority, high_priority]
|
|
|
|
info_dict = {"duration": 120}
|
|
result = conditions.match(info_dict)
|
|
|
|
assert result is high_priority, "Should match high_priority first (priority=10)"
|
|
# Should only call match_str once for highest priority condition
|
|
mock_match_str.assert_called_once_with("duration > 60", info_dict)
|
|
|
|
def test_match_empty_filter(self):
|
|
"""Test matching with condition that has empty filter."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
file_path = Path(temp_dir) / "empty_filter_test.json"
|
|
conditions = Conditions(file=file_path)
|
|
|
|
test_condition = Condition(name="empty_filter", filter="")
|
|
conditions._items = [test_condition]
|
|
|
|
info_dict = {"duration": 120}
|
|
result = conditions.match(info_dict)
|
|
|
|
assert result is None
|
|
|
|
@patch("app.library.conditions.match_str")
|
|
def test_single_match_found(self, mock_match_str):
|
|
"""Test single condition matching."""
|
|
mock_match_str.return_value = True
|
|
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
file_path = Path(temp_dir) / "single_match_test.json"
|
|
conditions = Conditions(file=file_path)
|
|
|
|
test_condition = Condition(name="single_test", filter="uploader = 'test'")
|
|
conditions._items = [test_condition]
|
|
|
|
info_dict = {"uploader": "test", "title": "Test Video"}
|
|
result = conditions.single_match("single_test", info_dict)
|
|
|
|
assert result is test_condition
|
|
mock_match_str.assert_called_once_with("uploader = 'test'", info_dict)
|
|
|
|
@patch("app.library.conditions.match_str")
|
|
def test_single_match_not_found(self, mock_match_str):
|
|
"""Test single condition matching when condition doesn't match."""
|
|
mock_match_str.return_value = False
|
|
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
file_path = Path(temp_dir) / "single_no_match_test.json"
|
|
conditions = Conditions(file=file_path)
|
|
|
|
test_condition = Condition(name="single_no_match", filter="uploader = 'other'")
|
|
conditions._items = [test_condition]
|
|
|
|
info_dict = {"uploader": "test", "title": "Test Video"}
|
|
result = conditions.single_match("single_no_match", info_dict)
|
|
|
|
assert result is None
|
|
|
|
def test_single_match_nonexistent_condition(self):
|
|
"""Test single matching with non-existent condition name."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
file_path = Path(temp_dir) / "nonexistent_single_test.json"
|
|
conditions = Conditions(file=file_path)
|
|
|
|
info_dict = {"duration": 120}
|
|
result = conditions.single_match("nonexistent", info_dict)
|
|
|
|
assert result is None
|
|
|
|
def test_single_match_condition_no_filter(self):
|
|
"""Test single matching with condition that has no filter."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
file_path = Path(temp_dir) / "no_filter_single_test.json"
|
|
conditions = Conditions(file=file_path)
|
|
|
|
test_condition = Condition(name="no_filter_single", filter="")
|
|
conditions._items = [test_condition]
|
|
|
|
info_dict = {"duration": 120}
|
|
result = conditions.single_match("no_filter_single", info_dict)
|
|
|
|
assert result is None
|
|
|
|
def test_single_match_disabled_condition(self):
|
|
"""Test single matching with disabled condition."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
file_path = Path(temp_dir) / "disabled_single_test.json"
|
|
conditions = Conditions(file=file_path)
|
|
|
|
test_condition = Condition(name="disabled_single", filter="duration > 60", enabled=False)
|
|
conditions._items = [test_condition]
|
|
|
|
info_dict = {"duration": 120}
|
|
result = conditions.single_match("disabled_single", info_dict)
|
|
|
|
assert result is None, "Should return None because condition is disabled"
|
|
|
|
def test_single_match_invalid_inputs(self):
|
|
"""Test single matching with invalid inputs."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
file_path = Path(temp_dir) / "invalid_single_test.json"
|
|
conditions = Conditions(file=file_path)
|
|
|
|
assert conditions.single_match("test", {"duration": 120}) is None, "Test with empty conditions"
|
|
|
|
assert conditions.single_match("test", None) is None, "Test with None info"
|
|
|
|
assert conditions.single_match("test", {}) is None, "Test with empty info dict"
|
|
|
|
assert conditions.single_match("test", "not a dict") is None, "Test with non-dict info"
|