-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
640 additions
and
41 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,224 @@ | ||
import unittest | ||
from unittest.mock import MagicMock, patch | ||
from parameterized import parameterized | ||
from typing import NamedTuple, Optional | ||
|
||
import fanfic_info | ||
import calibre_info | ||
from calibredb_utils import ( | ||
call_calibre_db, | ||
export_story, | ||
remove_story, | ||
add_story, | ||
) | ||
|
||
|
||
class CallCalibreDbTestCase(unittest.TestCase): | ||
class CallCalibreDbParams(NamedTuple): | ||
command: str | ||
calibre_info: calibre_info.CalibreInfo | ||
fanfic_info: Optional[fanfic_info.FanficInfo] | ||
expected_command: str | ||
should_raise_exception: bool | ||
|
||
@parameterized.expand( | ||
[ | ||
CallCalibreDbParams( | ||
command="list", | ||
calibre_info=MagicMock(), | ||
fanfic_info=None, | ||
expected_command="list ", | ||
should_raise_exception=False, | ||
), | ||
CallCalibreDbParams( | ||
command="add", | ||
calibre_info=MagicMock(), | ||
fanfic_info=MagicMock(calibre_id="123"), | ||
expected_command="add 123", | ||
should_raise_exception=False, | ||
), | ||
CallCalibreDbParams( | ||
command="remove", | ||
calibre_info=MagicMock(), | ||
fanfic_info=MagicMock(calibre_id="123"), | ||
expected_command="remove 123", | ||
should_raise_exception=True, | ||
), | ||
] | ||
) | ||
@patch("calibredb_utils.call", return_value=None) | ||
@patch("calibredb_utils.ff_logging.log_failure") | ||
@patch("calibredb_utils.ff_logging.log_debug") | ||
def test_call_calibre_db( | ||
self, | ||
command, | ||
calibre_info, | ||
fanfic_info, | ||
expected_command, | ||
should_raise_exception, | ||
mock_log_debug, | ||
mock_log_failure, | ||
mock_call, | ||
): | ||
calibre_info.lock = MagicMock() | ||
|
||
if should_raise_exception: | ||
mock_call.side_effect = Exception("Test exception") | ||
|
||
call_calibre_db(command, calibre_info, fanfic_info) | ||
|
||
mock_log_debug.assert_called_once_with( | ||
f'\tCalling calibredb with command: \t"{expected_command} {calibre_info}"' | ||
) | ||
|
||
if should_raise_exception: | ||
mock_log_failure.assert_called_once() | ||
else: | ||
mock_log_failure.assert_not_called() | ||
|
||
|
||
class ExportStoryTestCase(unittest.TestCase): | ||
class ExportStoryParams(NamedTuple): | ||
fanfic_info: fanfic_info.FanficInfo | ||
location: str | ||
calibre_info: calibre_info.CalibreInfo | ||
expected_command: str | ||
|
||
@parameterized.expand( | ||
[ | ||
ExportStoryParams( | ||
fanfic_info=MagicMock(), | ||
location="/fake/location", | ||
calibre_info=MagicMock(), | ||
expected_command='export --dont-save-cover --dont-write-opf --single-dir --to-dir "/fake/location"', | ||
), | ||
] | ||
) | ||
@patch("calibredb_utils.call_calibre_db") | ||
def test_export_story( | ||
self, | ||
fanfic_info, | ||
location, | ||
calibre_info, | ||
expected_command, | ||
mock_call_calibre_db, | ||
): | ||
export_story( | ||
fanfic_info=fanfic_info, | ||
location=location, | ||
calibre_info=calibre_info, | ||
) | ||
mock_call_calibre_db.assert_called_once_with( | ||
expected_command, calibre_info, fanfic_info | ||
) | ||
|
||
|
||
class RemoveStoryTestCase(unittest.TestCase): | ||
class RemoveStoryParams(NamedTuple): | ||
fanfic_info: fanfic_info.FanficInfo | ||
calibre_info: calibre_info.CalibreInfo | ||
expected_command: str | ||
|
||
@parameterized.expand( | ||
[ | ||
RemoveStoryParams( | ||
fanfic_info=MagicMock(calibre_id="123"), | ||
calibre_info=MagicMock(), | ||
expected_command="remove", | ||
), | ||
] | ||
) | ||
@patch("calibredb_utils.call_calibre_db") | ||
def test_remove_story( | ||
self, | ||
fanfic_info, | ||
calibre_info, | ||
expected_command, | ||
mock_call_calibre_db, | ||
): | ||
remove_story(fanfic_info, calibre_info) | ||
mock_call_calibre_db.assert_called_once_with( | ||
expected_command, calibre_info, fanfic_info | ||
) | ||
|
||
|
||
class AddStoryTestCase(unittest.TestCase): | ||
class AddStoryParams(NamedTuple): | ||
location: str | ||
fanfic_info: fanfic_info.FanficInfo | ||
calibre_info: calibre_info.CalibreInfo | ||
epub_files: list | ||
expected_command: str | ||
should_fail: bool | ||
|
||
@parameterized.expand( | ||
[ | ||
AddStoryParams( | ||
location="/fake/location", | ||
fanfic_info=MagicMock(), | ||
calibre_info=MagicMock(return_value="mock_calibre_info"), | ||
epub_files=["/fake/location/story.epub"], | ||
expected_command='add -d {mock} "/fake/location/story.epub"', | ||
should_fail=False, | ||
), | ||
AddStoryParams( | ||
location="/fake/location", | ||
fanfic_info=MagicMock(), | ||
calibre_info=MagicMock(return_value="mock_calibre_info"), | ||
epub_files=[], | ||
expected_command="", | ||
should_fail=True, | ||
), | ||
] | ||
) | ||
@patch("calibredb_utils.system_utils.get_files") | ||
@patch( | ||
"calibredb_utils.regex_parsing.extract_filename", | ||
return_value="Story Title", | ||
) | ||
@patch("calibredb_utils.call_calibre_db") | ||
@patch("calibredb_utils.ff_logging.log_failure") | ||
@patch("calibredb_utils.ff_logging.log") | ||
def test_add_story( | ||
self, | ||
location, | ||
fanfic_info, | ||
calibre_info, | ||
epub_files, | ||
expected_command, | ||
should_fail, | ||
mock_log, | ||
mock_log_failure, | ||
mock_call_calibre_db, | ||
mock_extract_filename, | ||
mock_get_files, | ||
): | ||
mock_get_files.return_value = epub_files | ||
calibre_info.return_value = "mock_calibre_info" | ||
|
||
add_story( | ||
location=location, | ||
fanfic_info=fanfic_info, | ||
calibre_info=calibre_info, | ||
) | ||
|
||
if should_fail: | ||
mock_log_failure.assert_called_once_with( | ||
"No EPUB files found in the specified location." | ||
) | ||
mock_call_calibre_db.assert_not_called() | ||
else: | ||
mock_log.assert_called_once_with( | ||
f"\t({fanfic_info.site}) Adding {epub_files[0]} to Calibre", | ||
"OKGREEN", | ||
) | ||
mock_call_calibre_db.assert_called_once_with( | ||
expected_command.format(mock=calibre_info), | ||
calibre_info, | ||
fanfic_info=None, | ||
) | ||
self.assertEqual(fanfic_info.title, "Story Title") | ||
|
||
|
||
if __name__ == "__main__": | ||
unittest.main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import unittest | ||
from unittest.mock import patch, MagicMock | ||
from notification_base import ( | ||
NotificationBase, | ||
retry_decorator, | ||
kSleepTime, | ||
kMaxAttempts, | ||
) | ||
import tomllib | ||
|
||
|
||
class TestNotificationBase(unittest.TestCase): | ||
def setUp(self): | ||
patcher_open = patch("builtins.open", new_callable=MagicMock) | ||
patcher_toml_load = patch("tomllib.load", return_value={"key": "value"}) | ||
|
||
self.mock_open = patcher_open.start() | ||
self.mock_toml_load = patcher_toml_load.start() | ||
|
||
self.addCleanup(patcher_open.stop) | ||
self.addCleanup(patcher_toml_load.stop) | ||
|
||
self.notification = NotificationBase("fake_path") | ||
|
||
def test_initialization(self): | ||
# Test initialization and config loading | ||
self.mock_open.assert_called_once_with("fake_path", "rb") | ||
self.mock_toml_load.assert_called_once() | ||
self.assertFalse(self.notification.enabled) | ||
self.assertEqual(self.notification.config, {"key": "value"}) | ||
|
||
def test_send_notification_not_implemented(self): | ||
# Test that send_notification raises NotImplementedError | ||
with self.assertRaises(NotImplementedError): | ||
self.notification.send_notification("title", "body", "site") | ||
|
||
|
||
class TestRetryDecorator(unittest.TestCase): | ||
@patch("time.sleep", return_value=None) | ||
def test_retry_decorator_success(self, mock_sleep): | ||
# Test that the decorated function is called once if it succeeds | ||
mock_func = MagicMock(return_value=True) | ||
decorated_func = retry_decorator(mock_func) | ||
decorated_func() | ||
mock_func.assert_called_once() | ||
mock_sleep.assert_not_called() | ||
|
||
@patch("time.sleep", return_value=None) | ||
def test_retry_decorator_failure(self, mock_sleep): | ||
# Test that the decorated function is retried up to 3 times if it fails | ||
mock_func = MagicMock(return_value=False) | ||
decorated_func = retry_decorator(mock_func) | ||
decorated_func() | ||
self.assertEqual(mock_func.call_count, kMaxAttempts) | ||
self.assertEqual(mock_sleep.call_count, kMaxAttempts - 1) | ||
for i in range(kMaxAttempts - 1): | ||
mock_sleep.assert_any_call(kSleepTime * (i + 1)) | ||
|
||
|
||
if __name__ == "__main__": | ||
unittest.main() |
Oops, something went wrong.