Files
shelfmark/tests/core/test_request_policy.py
Alex 7d992c3918 User DB cleanup and refactor (#686)
- Refactored user and request code to avoid any database conflicts
- Fix threading behavior with custom script execution
- Harden the no_auth activity user filtering
- Add a hint to add local admin if none is created
- Added secret key to persist login states across updates / restarts
2026-03-02 15:41:15 +00:00

413 lines
14 KiB
Python

"""Tests for request-policy resolution."""
from shelfmark.core.request_policy import (
PolicyMode,
filter_request_policy_settings,
get_source_content_type_capabilities,
merge_request_policy_settings,
normalize_content_type,
resolve_policy_mode,
validate_policy_rules,
)
def test_filter_request_policy_settings_uses_uppercase_allowlist_only():
settings = {
"REQUEST_POLICY_DEFAULT_EBOOK": "download",
"REQUEST_POLICY_DEFAULT_AUDIOBOOK": "request_book",
"REQUEST_POLICY_RULES": [{"source": "*", "content_type": "*", "mode": "blocked"}],
"REQUESTS_ENABLED": True,
"REQUESTS_ALLOW_NOTES": False,
"MAX_PENDING_REQUESTS_PER_USER": 7,
"request_policy_default_ebook": "blocked",
"DESTINATION": "/books/alice",
}
filtered = filter_request_policy_settings(settings)
assert "REQUEST_POLICY_DEFAULT_EBOOK" in filtered
assert "REQUEST_POLICY_DEFAULT_AUDIOBOOK" in filtered
assert "REQUEST_POLICY_RULES" in filtered
assert "REQUESTS_ENABLED" in filtered
assert "REQUESTS_ALLOW_NOTES" in filtered
assert "MAX_PENDING_REQUESTS_PER_USER" in filtered
assert "request_policy_default_ebook" not in filtered
assert "DESTINATION" not in filtered
def test_merge_request_policy_settings_applies_user_overrides():
global_settings = {
"REQUEST_POLICY_DEFAULT_EBOOK": "download",
"REQUEST_POLICY_DEFAULT_AUDIOBOOK": "download",
"REQUEST_POLICY_RULES": [{"source": "*", "content_type": "*", "mode": "request_book"}],
}
user_settings = {
"REQUEST_POLICY_DEFAULT_EBOOK": "blocked",
"DESTINATION": "/books/alice",
}
merged = merge_request_policy_settings(global_settings, user_settings)
assert merged["REQUEST_POLICY_DEFAULT_EBOOK"] == "blocked"
assert merged["REQUEST_POLICY_DEFAULT_AUDIOBOOK"] == "download"
assert "DESTINATION" not in merged
def test_merge_request_policy_settings_overlays_user_rules_on_global_rules():
global_settings = {
"REQUEST_POLICY_DEFAULT_EBOOK": "download",
"REQUEST_POLICY_DEFAULT_AUDIOBOOK": "download",
"REQUEST_POLICY_RULES": [
{"source": "direct_download", "content_type": "ebook", "mode": "download"},
{"source": "prowlarr", "content_type": "ebook", "mode": "request_release"},
],
}
user_settings = {
"REQUEST_POLICY_RULES": [
{"source": "direct_download", "content_type": "ebook", "mode": "blocked"},
],
}
merged = merge_request_policy_settings(global_settings, user_settings)
assert sorted(merged["REQUEST_POLICY_RULES"], key=lambda row: (row["source"], row["content_type"])) == [
{"source": "direct_download", "content_type": "ebook", "mode": "blocked"},
{"source": "prowlarr", "content_type": "ebook", "mode": "request_release"},
]
def test_merge_request_policy_settings_empty_user_rules_preserve_global_rules():
global_settings = {
"REQUEST_POLICY_DEFAULT_EBOOK": "download",
"REQUEST_POLICY_RULES": [
{"source": "prowlarr", "content_type": "ebook", "mode": "request_release"},
],
}
user_settings = {
"REQUEST_POLICY_RULES": [],
}
merged = merge_request_policy_settings(global_settings, user_settings)
assert merged["REQUEST_POLICY_RULES"] == [
{"source": "prowlarr", "content_type": "ebook", "mode": "request_release"},
]
def test_normalize_content_type_defaults_to_ebook_for_unknown_values():
assert normalize_content_type(None) == "ebook"
assert normalize_content_type("") == "ebook"
assert normalize_content_type("book (fiction)") == "ebook"
assert normalize_content_type("mystery-value") == "ebook"
def test_normalize_content_type_detects_audiobook_aliases():
assert normalize_content_type("audiobook") == "audiobook"
assert normalize_content_type("AUDIOBOOKS") == "audiobook"
assert normalize_content_type("book (audiobook)") == "audiobook"
def test_resolve_policy_mode_uses_wildcard_precedence():
settings = {
"REQUEST_POLICY_DEFAULT_EBOOK": "download",
"REQUEST_POLICY_DEFAULT_AUDIOBOOK": "download",
"REQUEST_POLICY_RULES": [
{"source": "*", "content_type": "*", "mode": "blocked"},
{"source": "*", "content_type": "ebook", "mode": "request_release"},
{"source": "prowlarr", "content_type": "*", "mode": "request_release"},
{"source": "prowlarr", "content_type": "ebook", "mode": "download"},
],
}
# (prowlarr, ebook) exact match → download
assert resolve_policy_mode(
source="prowlarr",
content_type="ebook",
global_settings=settings,
) == PolicyMode.DOWNLOAD
# (prowlarr, audiobook) → matches (prowlarr, *) → request_release
assert resolve_policy_mode(
source="prowlarr",
content_type="audiobook",
global_settings=settings,
) == PolicyMode.REQUEST_RELEASE
# (irc, ebook) → matches (*, ebook) → request_release
assert resolve_policy_mode(
source="irc",
content_type="ebook",
global_settings=settings,
) == PolicyMode.REQUEST_RELEASE
# (irc, audiobook) → matches (*, *) → blocked
assert resolve_policy_mode(
source="irc",
content_type="audiobook",
global_settings=settings,
) == PolicyMode.BLOCKED
def test_resolve_policy_mode_uses_content_default_when_no_rule_matches():
settings = {
"REQUEST_POLICY_DEFAULT_EBOOK": "download",
"REQUEST_POLICY_DEFAULT_AUDIOBOOK": "blocked",
"REQUEST_POLICY_RULES": [],
}
assert resolve_policy_mode(
source="direct_download",
content_type="ebook",
global_settings=settings,
) == PolicyMode.DOWNLOAD
assert resolve_policy_mode(
source="direct_download",
content_type="audiobook",
global_settings=settings,
) == PolicyMode.BLOCKED
def test_resolve_policy_mode_caps_at_content_type_default_ceiling():
"""Matrix rules cannot grant more permissive access than the content-type default."""
settings = {
"REQUEST_POLICY_DEFAULT_EBOOK": "request_release",
"REQUEST_POLICY_DEFAULT_AUDIOBOOK": "request_release",
"REQUEST_POLICY_RULES": [
{"source": "prowlarr", "content_type": "ebook", "mode": "download"},
{"source": "irc", "content_type": "ebook", "mode": "blocked"},
],
}
# prowlarr/ebook rule says download, but ceiling is request_release → capped
assert resolve_policy_mode(
source="prowlarr",
content_type="ebook",
global_settings=settings,
) == PolicyMode.REQUEST_RELEASE
# irc/ebook rule says blocked, which is more restrictive than ceiling → stays blocked
assert resolve_policy_mode(
source="irc",
content_type="ebook",
global_settings=settings,
) == PolicyMode.BLOCKED
# no rule for direct_download → falls to ceiling
assert resolve_policy_mode(
source="direct_download",
content_type="ebook",
global_settings=settings,
) == PolicyMode.REQUEST_RELEASE
def test_resolve_policy_mode_request_book_ceiling_overrides_all_rules():
"""request_book defaults stay capped for non-direct sources."""
settings = {
"REQUEST_POLICY_DEFAULT_EBOOK": "request_book",
"REQUEST_POLICY_DEFAULT_AUDIOBOOK": "blocked",
"REQUEST_POLICY_RULES": [
{"source": "prowlarr", "content_type": "ebook", "mode": "download"},
{"source": "direct_download", "content_type": "ebook", "mode": "request_release"},
],
}
# Prowlarr rule tries to upgrade beyond request_book → capped
assert resolve_policy_mode(
source="prowlarr",
content_type="ebook",
global_settings=settings,
) == PolicyMode.REQUEST_BOOK
# Direct-download requests are concrete releases, so request_book normalizes to request_release.
assert resolve_policy_mode(
source="direct_download",
content_type="ebook",
global_settings=settings,
) == PolicyMode.REQUEST_RELEASE
# audiobook default is blocked → even more restrictive ceiling
assert resolve_policy_mode(
source="prowlarr",
content_type="audiobook",
global_settings=settings,
) == PolicyMode.BLOCKED
def test_resolve_policy_mode_falls_back_to_request_book_when_unset():
settings = {
"REQUEST_POLICY_DEFAULT_EBOOK": "not-a-mode",
"REQUEST_POLICY_DEFAULT_AUDIOBOOK": None,
"REQUEST_POLICY_RULES": [],
}
assert resolve_policy_mode(
source="direct_download",
content_type="ebook",
global_settings=settings,
) == PolicyMode.REQUEST_RELEASE
assert resolve_policy_mode(
source="prowlarr",
content_type="audiobook",
global_settings=settings,
) == PolicyMode.REQUEST_BOOK
def test_resolve_policy_mode_ignores_invalid_rule_rows():
settings = {
"REQUEST_POLICY_DEFAULT_EBOOK": "download",
"REQUEST_POLICY_RULES": [
{"source": "direct_download", "content_type": "ebook", "mode": "request_book"},
{"source": "direct_download", "content_type": "ebook", "mode": "not-valid"},
{"source": "direct_download", "content_type": "invalid-type", "mode": "blocked"},
],
}
resolved = resolve_policy_mode(
source="direct_download",
content_type="ebook",
global_settings=settings,
)
assert resolved == PolicyMode.DOWNLOAD
def test_resolve_policy_mode_uses_user_rules_when_present():
global_settings = {
"REQUEST_POLICY_DEFAULT_EBOOK": "download",
"REQUEST_POLICY_RULES": [
{"source": "direct_download", "content_type": "ebook", "mode": "download"},
],
}
user_settings = {
"REQUEST_POLICY_RULES": [
{"source": "direct_download", "content_type": "ebook", "mode": "blocked"},
],
}
resolved = resolve_policy_mode(
source="direct_download",
content_type="ebook",
global_settings=global_settings,
user_settings=user_settings,
)
assert resolved == PolicyMode.BLOCKED
def test_resolve_policy_mode_treats_unknown_source_as_wildcard_context():
settings = {
"REQUEST_POLICY_DEFAULT_EBOOK": "download",
"REQUEST_POLICY_RULES": [
{"source": "*", "content_type": "ebook", "mode": "request_release"},
],
}
resolved = resolve_policy_mode(
source=None,
content_type="ebook",
global_settings=settings,
)
assert resolved == PolicyMode.REQUEST_RELEASE
def test_validate_policy_rules_rejects_unknown_source():
rules = [
{"source": "not-a-source", "content_type": "ebook", "mode": "download"},
]
normalized, errors = validate_policy_rules(
rules,
source_capabilities={
"direct_download": {"ebook"},
"prowlarr": {"ebook", "audiobook"},
},
)
assert normalized == []
assert "unknown source" in errors[0]
def test_validate_policy_rules_rejects_blank_source():
rules = [
{"source": "", "content_type": "ebook", "mode": "download"},
]
normalized, errors = validate_policy_rules(
rules,
source_capabilities={
"direct_download": {"ebook"},
},
)
assert normalized == []
assert "source is required" in errors[0]
def test_validate_policy_rules_rejects_unsupported_source_content_type_pair():
rules = [
{"source": "direct_download", "content_type": "audiobook", "mode": "download"},
]
normalized, errors = validate_policy_rules(
rules,
source_capabilities={
"direct_download": {"ebook"},
"prowlarr": {"ebook", "audiobook"},
},
)
assert normalized == []
assert "does not support content_type 'audiobook'" in errors[0]
def test_validate_policy_rules_rejects_request_book_in_matrix():
rules = [
{"source": "direct_download", "content_type": "ebook", "mode": "request_book"},
]
normalized, errors = validate_policy_rules(
rules,
source_capabilities={"direct_download": {"ebook"}},
)
assert normalized == []
assert "not allowed in matrix rules" in errors[0]
def test_validate_policy_rules_accepts_supported_pairs_and_wildcards():
rules = [
{"source": "prowlarr", "content_type": "audiobook", "mode": "request_release"},
{"source": "direct_download", "content_type": "*", "mode": "blocked"},
{"source": "*", "content_type": "ebook", "mode": "download"},
]
normalized, errors = validate_policy_rules(
rules,
source_capabilities={
"direct_download": {"ebook"},
"prowlarr": {"ebook", "audiobook"},
"irc": {"ebook"},
},
)
assert errors == []
assert normalized == [
{"source": "prowlarr", "content_type": "audiobook", "mode": "request_release"},
{"source": "direct_download", "content_type": "*", "mode": "blocked"},
{"source": "*", "content_type": "ebook", "mode": "download"},
]
def test_get_source_content_type_capabilities_reads_registry(monkeypatch):
monkeypatch.setattr(
"shelfmark.release_sources.list_available_sources",
lambda: [
{
"name": "direct_download",
"display_name": "Direct Download",
"enabled": True,
"supported_content_types": ["ebook"],
},
{
"name": "prowlarr",
"display_name": "Prowlarr",
"enabled": True,
"supported_content_types": ["ebook", "audiobook"],
},
],
)
capabilities = get_source_content_type_capabilities()
assert capabilities["direct_download"] == {"ebook"}
assert capabilities["prowlarr"] == {"ebook", "audiobook"}