mirror of
https://github.com/ManiMatter/decluttarr.git
synced 2026-04-25 02:05:52 +02:00
Bug Fixes & Automated Testing - remove_failed_imports.py #patch
This commit is contained in:
33
tests/jobs/remove_failed_imports/mock_data/mock_data_1.json
Normal file
33
tests/jobs/remove_failed_imports/mock_data/mock_data_1.json
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"records": [
|
||||
{
|
||||
"id": 1,
|
||||
"downloadId": "A123",
|
||||
"title": "Sonarr Title 1",
|
||||
"status": "completed",
|
||||
"trackedDownloadStatus": "ok",
|
||||
"trackedDownloadState": "importing",
|
||||
"statusMessages": []
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"downloadId": "B123",
|
||||
"title": "Sonarr Title 2",
|
||||
"status": "completed",
|
||||
"trackedDownloadStatus": "warning",
|
||||
"trackedDownloadState": "importBlocked",
|
||||
"statusMessages": [
|
||||
{
|
||||
"title": "One or more episodes expected in this release were not imported or missing from the release",
|
||||
"messages": []
|
||||
},
|
||||
{
|
||||
"title": "Sonarr Title 2.mkv",
|
||||
"messages": [
|
||||
"Episode XYZ was not found in the grabbed release: Sonarr Title 2.mkv"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
32
tests/jobs/remove_failed_imports/mock_data/mock_data_2.json
Normal file
32
tests/jobs/remove_failed_imports/mock_data/mock_data_2.json
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"records": [
|
||||
{
|
||||
"id": 1,
|
||||
"downloadId": "A123",
|
||||
"title": "Sonarr Title 1",
|
||||
"status": "completed",
|
||||
"trackedDownloadStatus": "warning",
|
||||
"trackedDownloadState": "importBlocked",
|
||||
"statusMessages": [
|
||||
{
|
||||
"title": "First Message",
|
||||
"messages": [
|
||||
"Message 1 - hello world"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Duplicate of First Message",
|
||||
"messages": [
|
||||
"Message 1 - hello world"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Second of Message",
|
||||
"messages": [
|
||||
"Message 2 - goodbye all"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
60
tests/jobs/remove_failed_imports/mock_data/mock_data_3.json
Normal file
60
tests/jobs/remove_failed_imports/mock_data/mock_data_3.json
Normal file
@@ -0,0 +1,60 @@
|
||||
{
|
||||
"records": [
|
||||
{
|
||||
"id": 1,
|
||||
"downloadId": "A123",
|
||||
"title": "Sonarr Title 1",
|
||||
"status": "completed",
|
||||
"trackedDownloadStatus": "warning",
|
||||
"trackedDownloadState": "importPending",
|
||||
"statusMessages": [
|
||||
{
|
||||
"title": "First Message",
|
||||
"messages": [
|
||||
"Message 1 - hello world"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Duplicate of First Message",
|
||||
"messages": [
|
||||
"Message 1 - hello world"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Second of Message",
|
||||
"messages": [
|
||||
"Message 2 - goodbye all"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"downloadId": "B123",
|
||||
"title": "Sonarr Title 2",
|
||||
"status": "completed",
|
||||
"trackedDownloadStatus": "warning",
|
||||
"trackedDownloadState": "importFailed",
|
||||
"statusMessages": [
|
||||
{
|
||||
"title": "First Message",
|
||||
"messages": [
|
||||
"Message 1 - hello world"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Duplicate of First Message",
|
||||
"messages": [
|
||||
"Message 1 - hello world"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Second of Message",
|
||||
"messages": [
|
||||
"Message 2 - goodbye all"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
import os
|
||||
os.environ['IS_IN_PYTEST'] = 'true'
|
||||
import logging
|
||||
import json
|
||||
import pytest
|
||||
from typing import Dict, Set, Any
|
||||
from unittest.mock import AsyncMock
|
||||
from src.jobs.remove_failed_imports import remove_failed_imports
|
||||
|
||||
|
||||
# Utility function to load mock data
|
||||
def load_mock_data(file_name):
|
||||
with open(file_name, 'r') as file:
|
||||
return json.load(file)
|
||||
|
||||
async def mock_get_queue(mock_data):
|
||||
logging.debug("Mock get_queue called")
|
||||
return mock_data
|
||||
|
||||
async def run_test(
|
||||
settingsDict: Dict[str, Any],
|
||||
expected_removal_messages: Dict[int, Set[str]],
|
||||
mock_data_file: str,
|
||||
monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
# Load mock data
|
||||
mock_data = load_mock_data(mock_data_file)
|
||||
|
||||
# Create an AsyncMock for execute_checks with side effect
|
||||
execute_checks_mock = AsyncMock()
|
||||
|
||||
# Define a side effect function
|
||||
def side_effect(*args, **kwargs):
|
||||
logging.debug("Mock execute_checks called with kwargs: %s", kwargs)
|
||||
# Return the affectedItems from kwargs
|
||||
return kwargs.get('affectedItems', [])
|
||||
|
||||
# Attach side effect to the mock
|
||||
execute_checks_mock.side_effect = side_effect
|
||||
|
||||
# Create an async mock for get_queue that returns mock_data
|
||||
mock_get_queue = AsyncMock(return_value=mock_data)
|
||||
|
||||
# Patch the methods
|
||||
monkeypatch.setattr('src.jobs.remove_failed_imports.get_queue', mock_get_queue)
|
||||
monkeypatch.setattr('src.jobs.remove_failed_imports.execute_checks', execute_checks_mock)
|
||||
|
||||
# Call the function
|
||||
await remove_failed_imports(settingsDict=settingsDict, BASE_URL='', API_KEY='', NAME='', deleted_downloads=set(), defective_tracker=set(), protectedDownloadIDs=set(), privateDowloadIDs=set())
|
||||
|
||||
# Assertions
|
||||
assert execute_checks_mock.called # Ensure the mock was called
|
||||
|
||||
# Assert expected items are there
|
||||
args, kwargs = execute_checks_mock.call_args
|
||||
affectedItems = kwargs.get('affectedItems', [])
|
||||
affectedItems_ids = {item['id'] for item in affectedItems}
|
||||
expectedItems_ids = set(expected_removal_messages.keys())
|
||||
assert len(affectedItems) == len(expected_removal_messages)
|
||||
assert affectedItems_ids == expectedItems_ids
|
||||
|
||||
# Assert all expected messages are there
|
||||
for affectedItem in affectedItems:
|
||||
assert 'removal_messages' in affectedItem
|
||||
assert expected_removal_messages[affectedItem['id']] == set(affectedItem.get('removal_messages', []))
|
||||
@@ -0,0 +1,39 @@
|
||||
import pytest
|
||||
from remove_failed_imports_utils import run_test
|
||||
mock_data_file = 'tests/jobs/remove_failed_imports/mock_data/mock_data_1.json'
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_with_pattern_one_message(monkeypatch):
|
||||
settingsDict = {'FAILED_IMPORT_MESSAGE_PATTERNS': ['not found in the grabbed release']}
|
||||
expected_removal_messages = {
|
||||
2: {
|
||||
'>>>>> Tracked Download State: importBlocked',
|
||||
'>>>>> Status Messages (matching specified patterns):',
|
||||
'>>>>> - Episode XYZ was not found in the grabbed release: Sonarr Title 2.mkv',
|
||||
}
|
||||
}
|
||||
await run_test(settingsDict, expected_removal_messages, mock_data_file, monkeypatch)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_with_empty_pattern_one_message(monkeypatch):
|
||||
settingsDict = {'FAILED_IMPORT_MESSAGE_PATTERNS': []}
|
||||
expected_removal_messages = {
|
||||
2: {
|
||||
'>>>>> Tracked Download State: importBlocked',
|
||||
'>>>>> Status Messages (All):',
|
||||
'>>>>> - Episode XYZ was not found in the grabbed release: Sonarr Title 2.mkv',
|
||||
}
|
||||
}
|
||||
await run_test(settingsDict, expected_removal_messages, mock_data_file, monkeypatch)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_without_pattern_one_message(monkeypatch):
|
||||
settingsDict = {}
|
||||
expected_removal_messages = {
|
||||
2: {
|
||||
'>>>>> Tracked Download State: importBlocked',
|
||||
'>>>>> Status Messages (All):',
|
||||
'>>>>> - Episode XYZ was not found in the grabbed release: Sonarr Title 2.mkv',
|
||||
}
|
||||
}
|
||||
await run_test(settingsDict, expected_removal_messages, mock_data_file, monkeypatch)
|
||||
@@ -0,0 +1,41 @@
|
||||
import pytest
|
||||
from remove_failed_imports_utils import run_test
|
||||
mock_data_file = 'tests/jobs/remove_failed_imports/mock_data/mock_data_2.json'
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_multiple_status_messages_multiple_pattern(monkeypatch):
|
||||
settingsDict = {'FAILED_IMPORT_MESSAGE_PATTERNS': ['world', 'all']}
|
||||
expected_removal_messages = {
|
||||
1: {
|
||||
'>>>>> Tracked Download State: importBlocked',
|
||||
'>>>>> Status Messages (matching specified patterns):',
|
||||
'>>>>> - Message 1 - hello world',
|
||||
'>>>>> - Message 2 - goodbye all',
|
||||
}
|
||||
}
|
||||
await run_test(settingsDict, expected_removal_messages, mock_data_file, monkeypatch)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_multiple_status_messages_single_pattern(monkeypatch):
|
||||
settingsDict = {'FAILED_IMPORT_MESSAGE_PATTERNS': ['world']}
|
||||
expected_removal_messages = {
|
||||
1: {
|
||||
'>>>>> Tracked Download State: importBlocked',
|
||||
'>>>>> Status Messages (matching specified patterns):',
|
||||
'>>>>> - Message 1 - hello world'
|
||||
}
|
||||
}
|
||||
await run_test(settingsDict, expected_removal_messages, mock_data_file, monkeypatch)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_multiple_status_messages_no_pattern(monkeypatch):
|
||||
settingsDict = {}
|
||||
expected_removal_messages = {
|
||||
1: {
|
||||
'>>>>> Tracked Download State: importBlocked',
|
||||
'>>>>> Status Messages (All):',
|
||||
'>>>>> - Message 1 - hello world',
|
||||
'>>>>> - Message 2 - goodbye all',
|
||||
}
|
||||
}
|
||||
await run_test(settingsDict, expected_removal_messages, mock_data_file, monkeypatch)
|
||||
@@ -0,0 +1,59 @@
|
||||
import pytest
|
||||
from remove_failed_imports_utils import run_test
|
||||
mock_data_file = 'tests/jobs/remove_failed_imports/mock_data/mock_data_3.json'
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_multiple_statuses_multiple_pattern(monkeypatch):
|
||||
settingsDict = {'FAILED_IMPORT_MESSAGE_PATTERNS': ['world', 'all']}
|
||||
expected_removal_messages = {
|
||||
1: {
|
||||
'>>>>> Tracked Download State: importPending',
|
||||
'>>>>> Status Messages (matching specified patterns):',
|
||||
'>>>>> - Message 1 - hello world',
|
||||
'>>>>> - Message 2 - goodbye all',
|
||||
},
|
||||
2: {
|
||||
'>>>>> Tracked Download State: importFailed',
|
||||
'>>>>> Status Messages (matching specified patterns):',
|
||||
'>>>>> - Message 1 - hello world',
|
||||
'>>>>> - Message 2 - goodbye all',
|
||||
}
|
||||
}
|
||||
await run_test(settingsDict, expected_removal_messages, mock_data_file, monkeypatch)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_multiple_statuses_single_pattern(monkeypatch):
|
||||
settingsDict = {'FAILED_IMPORT_MESSAGE_PATTERNS': ['world']}
|
||||
expected_removal_messages = {
|
||||
1: {
|
||||
'>>>>> Tracked Download State: importPending',
|
||||
'>>>>> Status Messages (matching specified patterns):',
|
||||
'>>>>> - Message 1 - hello world'
|
||||
},
|
||||
2: {
|
||||
'>>>>> Tracked Download State: importFailed',
|
||||
'>>>>> Status Messages (matching specified patterns):',
|
||||
'>>>>> - Message 1 - hello world'
|
||||
}
|
||||
}
|
||||
await run_test(settingsDict, expected_removal_messages, mock_data_file, monkeypatch)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_multiple_statuses_no_pattern(monkeypatch):
|
||||
settingsDict = {}
|
||||
expected_removal_messages = {
|
||||
1: {
|
||||
'>>>>> Tracked Download State: importPending',
|
||||
'>>>>> Status Messages (All):',
|
||||
'>>>>> - Message 1 - hello world',
|
||||
'>>>>> - Message 2 - goodbye all',
|
||||
},
|
||||
2: {
|
||||
'>>>>> Tracked Download State: importFailed',
|
||||
'>>>>> Status Messages (All):',
|
||||
'>>>>> - Message 1 - hello world',
|
||||
'>>>>> - Message 2 - goodbye all',
|
||||
}
|
||||
}
|
||||
await run_test(settingsDict, expected_removal_messages, mock_data_file, monkeypatch)
|
||||
11
tests/utils/remove_download/mock_data/mock_data_1.json
Normal file
11
tests/utils/remove_download/mock_data/mock_data_1.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"id": 1,
|
||||
"downloadId": "A",
|
||||
"title": "Sonarr Title 1",
|
||||
"removal_messages": [
|
||||
">>>>> Tracked Download State: importBlocked",
|
||||
">>>>> Status Messages (matching specified patterns):",
|
||||
">>>>> - Episode XYZ was not found in the grabbed release: Sonarr Title 2.mkv",
|
||||
">>>>> - And yet another message"
|
||||
]
|
||||
}
|
||||
52
tests/utils/remove_download/remove_download_utils.py
Normal file
52
tests/utils/remove_download/remove_download_utils.py
Normal file
@@ -0,0 +1,52 @@
|
||||
import os
|
||||
os.environ['IS_IN_PYTEST'] = 'true'
|
||||
import logging
|
||||
import json
|
||||
import pytest
|
||||
from typing import Dict, Set, Any
|
||||
from src.utils.shared import remove_download
|
||||
from src.utils.trackers import Deleted_Downloads
|
||||
|
||||
|
||||
|
||||
# Utility function to load mock data
|
||||
def load_mock_data(file_name):
|
||||
with open(file_name, 'r') as file:
|
||||
return json.load(file)
|
||||
|
||||
async def mock_rest_delete() -> None:
|
||||
logger.debug(f"Mock rest_delete called with URL")
|
||||
|
||||
|
||||
async def run_test(
|
||||
settingsDict: Dict[str, Any],
|
||||
expected_removal_messages: Set[str],
|
||||
failType: str,
|
||||
removeFromClient: bool,
|
||||
mock_data_file: str,
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
caplog: pytest.LogCaptureFixture
|
||||
) -> None:
|
||||
# Load mock data
|
||||
affectedItem = load_mock_data(mock_data_file)
|
||||
|
||||
# Mock the `rest_delete` function
|
||||
monkeypatch.setattr('src.utils.shared.rest_delete', mock_rest_delete)
|
||||
|
||||
# Call the function
|
||||
with caplog.at_level(logging.INFO):
|
||||
# Call the function and assert no exceptions
|
||||
try:
|
||||
deleted_downloads = Deleted_Downloads([])
|
||||
await remove_download(settingsDict=settingsDict, BASE_URL='', API_KEY='', affectedItem=affectedItem, failType=failType, addToBlocklist=True, deleted_downloads=deleted_downloads, removeFromClient=removeFromClient)
|
||||
except Exception as e:
|
||||
pytest.fail(f"remove_download raised an exception: {e}")
|
||||
|
||||
# Assertions:
|
||||
# Check that expected log messages are in the captured log
|
||||
log_messages = {record.message for record in caplog.records if record.levelname == 'INFO'}
|
||||
|
||||
assert expected_removal_messages == log_messages
|
||||
|
||||
# Check that the affectedItem's downloadId was added to deleted_downloads
|
||||
assert affectedItem['downloadId'] in deleted_downloads.dict
|
||||
33
tests/utils/remove_download/test_remove_download_1.py
Normal file
33
tests/utils/remove_download/test_remove_download_1.py
Normal file
@@ -0,0 +1,33 @@
|
||||
|
||||
import pytest
|
||||
from remove_download_utils import run_test
|
||||
# Parameters identical across all tests
|
||||
mock_data_file = 'tests/utils/remove_download/mock_data/mock_data_1.json'
|
||||
failType = 'failed import'
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_removal_with_removal_messages(monkeypatch, caplog):
|
||||
settingsDict = {'TEST_RUN': True}
|
||||
removeFromClient = True
|
||||
expected_removal_messages = {
|
||||
'>>> Removing failed import download: Sonarr Title 1',
|
||||
'>>>>> Tracked Download State: importBlocked',
|
||||
'>>>>> Status Messages (matching specified patterns):',
|
||||
'>>>>> - Episode XYZ was not found in the grabbed release: Sonarr Title 2.mkv',
|
||||
'>>>>> - And yet another message'
|
||||
}
|
||||
await run_test(settingsDict=settingsDict, expected_removal_messages=expected_removal_messages, failType=failType, removeFromClient=removeFromClient, mock_data_file=mock_data_file, monkeypatch=monkeypatch, caplog=caplog)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_schizophrenic_removal_with_removal_messages(monkeypatch, caplog):
|
||||
settingsDict = {'TEST_RUN': True}
|
||||
removeFromClient = False
|
||||
expected_removal_messages = {
|
||||
'>>> Removing failed import download (without removing from torrent client): Sonarr Title 1',
|
||||
'>>>>> Tracked Download State: importBlocked',
|
||||
'>>>>> Status Messages (matching specified patterns):',
|
||||
'>>>>> - Episode XYZ was not found in the grabbed release: Sonarr Title 2.mkv',
|
||||
'>>>>> - And yet another message'
|
||||
}
|
||||
await run_test(settingsDict=settingsDict, expected_removal_messages=expected_removal_messages, failType=failType, removeFromClient=removeFromClient, mock_data_file=mock_data_file, monkeypatch=monkeypatch, caplog=caplog)
|
||||
Reference in New Issue
Block a user