Add integration tests

This commit is contained in:
Timmy Welch 2023-12-17 15:27:02 -08:00
parent ab6b970063
commit 45643cc594
12 changed files with 159 additions and 37 deletions

View File

@ -230,9 +230,7 @@ class GenericMetadata:
assign("scan_info", new_md.scan_info)
assign("tags", new_md.tags)
print("before", self.pages, new_md.pages)
assign("pages", new_md.pages)
print("after", self.pages, new_md.pages)
assign("page_count", new_md.page_count)
assign("characters", new_md.characters)

View File

@ -172,7 +172,7 @@ class CLI:
else:
notes = (
f"Tagged with ComicTagger {ctversion.version} using info from {self.current_talker().name} on"
f" {datetime.now():%Y-%m-%d %H:%M:%S}. [Issue ID {ct_md.issue_id}]"
f" {datetime.now():%Y-%m-%d %H:%M:%S}. [Issue ID {ct_md.issue_id}]"
)
md.overlay(ct_md.replace(notes=utils.combine_notes(md.notes, notes, "Tagged with ComicTagger")))
@ -584,7 +584,7 @@ class CLI:
notes = (
f"Tagged with ComicTagger {ctversion.version} using info from {self.current_talker().name} on"
+ f" {datetime.now():%Y-%m-%d %H:%M:%S}. [Issue ID {ct_md.issue_id}]"
+ f" {datetime.now():%Y-%m-%d %H:%M:%S}. [Issue ID {ct_md.issue_id}]"
)
md.overlay(
ct_md.replace(

View File

@ -36,8 +36,8 @@ def archiver(manager: settngs.Manager) -> None:
)
def register_talker_settings(manager: settngs.Manager) -> None:
for talker in comictaggerlib.ctsettings.talkers.values():
def register_talker_settings(manager: settngs.Manager, talkers: dict[str, ComicTalker]) -> None:
for talker in talkers.values():
def api_options(manager: settngs.Manager) -> None:
# The default needs to be unset or None.
@ -76,10 +76,10 @@ def validate_archive_settings(config: settngs.Config[ct_ns]) -> settngs.Config[c
return config
def validate_talker_settings(config: settngs.Config[ct_ns]) -> settngs.Config[ct_ns]:
def validate_talker_settings(config: settngs.Config[ct_ns], talkers: dict[str, ComicTalker]) -> settngs.Config[ct_ns]:
# Apply talker settings from config file
cfg = settngs.normalize_config(config, True, True)
for talker in list(comictaggerlib.ctsettings.talkers.values()):
for talker in list(talkers.values()):
try:
cfg[0][group_for_plugin(talker)] = talker.parse_settings(cfg[0][group_for_plugin(talker)])
except Exception as e:
@ -90,12 +90,12 @@ def validate_talker_settings(config: settngs.Config[ct_ns]) -> settngs.Config[ct
return cast(settngs.Config[ct_ns], settngs.get_namespace(cfg, file=True, cmdline=True))
def validate_plugin_settings(config: settngs.Config[ct_ns]) -> settngs.Config[ct_ns]:
def validate_plugin_settings(config: settngs.Config[ct_ns], talkers: dict[str, ComicTalker]) -> settngs.Config[ct_ns]:
config = validate_archive_settings(config)
config = validate_talker_settings(config)
config = validate_talker_settings(config, talkers)
return config
def register_plugin_settings(manager: settngs.Manager) -> None:
def register_plugin_settings(manager: settngs.Manager, talkers: dict[str, ComicTalker]) -> None:
manager.add_persistent_group("Archive", archiver, False)
register_talker_settings(manager)
register_talker_settings(manager, talkers)

View File

@ -102,7 +102,7 @@ class IssueIdentifier:
self.publisher_filter = [s.strip().casefold() for s in config.Issue_Identifier__publisher_filter]
self.additional_metadata = GenericMetadata()
self.output_function: Callable[[str], None] = lambda x: print(x)
self.output_function: Callable[[str], None] = print
self.progress_callback: Callable[[int, int], None] | None = None
self.cover_url_callback: Callable[[bytes], None] | None = None
self.search_result = self.result_no_matches
@ -271,7 +271,7 @@ class IssueIdentifier:
self.output(msg)
def output(self, *args: Any, file: Any = None, **kwargs: Any) -> None:
# We intercept the file argument otherwise everything is passed to self.output_function
# We intercept and discard the file argument otherwise everything is passed to self.output_function
# Ensure args[0] is defined and is a string for logger.info
if not args:
@ -289,6 +289,7 @@ class IssueIdentifier:
if self.config.Runtime_Options__verbose > 0 or self.config.Runtime_Options__quiet:
return
# default output is stdout
self.output_function(*args, **kwargs)
def get_issue_cover_match_score(
@ -347,7 +348,6 @@ class IssueIdentifier:
if self.cancel:
raise IssueIdentifierCancelled
if use_remote_alternates:
self.log_msg(f"[{len(remote_cover_list) - 1} alt. covers]")
score_list = []

View File

@ -36,6 +36,7 @@ from comictaggerlib.ctsettings import ct_ns
from comictaggerlib.ctversion import version
from comictaggerlib.log import setup_logging
from comictaggerlib.resulttypes import Action
from comictalker.comictalker import ComicTalker
if sys.version_info < (3, 10):
import importlib_metadata
@ -107,6 +108,7 @@ class App:
self.config: settngs.Config[ct_ns]
self.initial_arg_parser = ctsettings.initial_commandline_parser()
self.config_load_success = False
self.talkers: dict[str, ComicTalker]
def run(self) -> None:
configure_locale()
@ -120,7 +122,7 @@ class App:
def load_plugins(self, opts: argparse.Namespace) -> None:
comicapi.comicarchive.load_archive_plugins()
ctsettings.talkers = comictalker.get_talkers(version, opts.config.user_cache_dir)
self.talkers = comictalker.get_talkers(version, opts.config.user_cache_dir)
def list_plugins(
self, talkers: list[comictalker.ComicTalker], archivers: list[type[comicapi.comicarchive.Archiver]]
@ -189,7 +191,7 @@ class App:
)
ctsettings.register_commandline_settings(self.manager)
ctsettings.register_file_settings(self.manager)
ctsettings.register_plugin_settings(self.manager)
ctsettings.register_plugin_settings(self.manager, getattr(self, "talkers", {}))
def parse_settings(self, config_paths: ctsettings.ComicTaggerPaths, *args: str) -> settngs.Config[ct_ns]:
cfg, self.config_load_success = ctsettings.parse_config(
@ -200,7 +202,7 @@ class App:
config = ctsettings.validate_commandline_settings(config, self.manager)
config = ctsettings.validate_file_settings(config)
config = ctsettings.validate_plugin_settings(config)
config = ctsettings.validate_plugin_settings(config, getattr(self, "talkers", {}))
return config
def initialize_dirs(self, paths: ctsettings.ComicTaggerPaths) -> None:
@ -220,10 +222,7 @@ class App:
# config already loaded
error = None
talkers = ctsettings.talkers
del ctsettings.talkers
if len(talkers) < 1:
if len(self.talkers) < 1:
error = error = (
"Failed to load any talkers, please re-install and check the log located in '"
+ str(self.config[0].Runtime_Options__config.user_log_dir)
@ -241,7 +240,7 @@ class App:
update_publishers(self.config)
if self.config[0].Commands__command == Action.list_plugins:
self.list_plugins(list(talkers.values()), comicapi.comicarchive.archivers)
self.list_plugins(list(self.talkers.values()), comicapi.comicarchive.archivers)
return
if self.config[0].Commands__command == Action.save_config:
@ -266,7 +265,7 @@ class App:
if not gui.qt_available:
raise gui.import_error
return gui.open_tagger_window(talkers, self.config, error)
return gui.open_tagger_window(self.talkers, self.config, error)
except ImportError:
self.config[0].Runtime_Options__no_gui = True
logger.warning("PyQt5 is not available. ComicTagger is limited to command-line mode.")
@ -277,7 +276,7 @@ class App:
raise SystemExit(1)
try:
cli.CLI(self.config[0], talkers).run()
cli.CLI(self.config[0], self.talkers).run()
except Exception:
logger.exception("CLI mode failed")

View File

@ -265,9 +265,11 @@ class SeriesSelectionWindow(QtWidgets.QDialog):
def log_id_output(self, text: str) -> None:
if self.iddialog is not None:
print(text, end=" ") # noqa: T201
self.iddialog.textEdit.append(text.rstrip())
self.iddialog.textEdit.ensureCursorVisible()
self.iddialog.textEdit.insertPlainText(text)
QtCore.QCoreApplication.processEvents()
QtCore.QCoreApplication.processEvents()
QtCore.QCoreApplication.processEvents()
def identify_progress(self, cur: int, total: int) -> None:
if self.iddialog is not None:

View File

@ -542,9 +542,7 @@ class SettingsWindow(QtWidgets.QDialog):
QtWidgets.QDialog.accept(self)
def update_talkers_config(self) -> None:
ctsettings.talkers = self.talkers
self.config = ctsettings.plugin.validate_talker_settings(self.config)
del ctsettings.talkers
self.config = ctsettings.plugin.validate_talker_settings(self.config, self.talkers)
def select_rar(self) -> None:
self.select_file(self.leRarExePath, "RAR")

View File

@ -1089,7 +1089,7 @@ class TaggerWindow(QtWidgets.QMainWindow):
notes = (
f"Tagged with ComicTagger {ctversion.version} using info from {self.current_talker().name} on"
f" {datetime.now():%Y-%m-%d %H:%M:%S}. [Issue ID {new_metadata.issue_id}]"
f" {datetime.now():%Y-%m-%d %H:%M:%S}. [Issue ID {new_metadata.issue_id}]"
)
self.metadata.overlay(
new_metadata.replace(
@ -1698,7 +1698,6 @@ class TaggerWindow(QtWidgets.QMainWindow):
return ct_md
def auto_tag_log(self, text: str) -> None:
print(text)
if self.atprogdialog is not None:
self.atprogdialog.textEdit.append(text.rstrip())
self.atprogdialog.textEdit.ensureCursorVisible()
@ -1845,7 +1844,7 @@ class TaggerWindow(QtWidgets.QMainWindow):
else:
notes = (
f"Tagged with ComicTagger {ctversion.version} using info from {self.current_talker().name} on"
f" {datetime.now():%Y-%m-%d %H:%M:%S}. [Issue ID {ct_md.issue_id}]"
f" {datetime.now():%Y-%m-%d %H:%M:%S}. [Issue ID {ct_md.issue_id}]"
)
md.overlay(ct_md.replace(notes=utils.combine_notes(md.notes, notes, "Tagged with ComicTagger")))

View File

@ -289,7 +289,7 @@ deps =
[flake8]
max-line-length = 120
extend-ignore = E203, E501, A003
extend-ignore = E203, E501, A003, T202
extend-exclude = venv, scripts, build, dist, comictaggerlib/ctversion.py
per-file-ignores =
comictaggerlib/cli.py: T20

View File

@ -1,9 +1,11 @@
from __future__ import annotations
import copy
import datetime
import io
import shutil
import unittest.mock
from argparse import Namespace
from collections.abc import Generator
from typing import Any
@ -15,6 +17,7 @@ from pyrate_limiter import Limiter, RequestRate
import comicapi.comicarchive
import comicapi.genericmetadata
import comictaggerlib.cli
import comictaggerlib.ctsettings
import comictalker
import comictalker.comiccacher
@ -127,10 +130,22 @@ def comicvine_api(monkeypatch, cbz, comic_cache, mock_version, config) -> comict
return cv
@pytest.fixture
def mock_now(monkeypatch):
class mydatetime:
time = datetime.datetime(2022, 4, 16, 15, 52, 26)
@classmethod
def now(cls):
return cls.time
monkeypatch.setattr(comictaggerlib.cli, "datetime", mydatetime)
@pytest.fixture
def mock_version(monkeypatch):
version = "1.4.4a9.dev20"
version_tuple = (1, 4, 4, "dev20")
version = "1.3.2a5"
version_tuple = (1, 3, 2)
monkeypatch.setattr(comictaggerlib.ctversion, "version", version)
monkeypatch.setattr(comictaggerlib.ctversion, "__version__", version)
@ -182,6 +197,24 @@ def config(tmp_path):
yield defaults
@pytest.fixture
def plugin_config(tmp_path):
from comictaggerlib.main import App
ns = Namespace(config=comictaggerlib.ctsettings.ComicTaggerPaths(tmp_path / "config"))
app = App()
app.load_plugins(ns)
app.register_settings()
defaults = app.parse_settings(ns.config, "")
defaults[0].Runtime_Options__config.user_data_dir.mkdir(parents=True, exist_ok=True)
defaults[0].Runtime_Options__config.user_config_dir.mkdir(parents=True, exist_ok=True)
defaults[0].Runtime_Options__config.user_cache_dir.mkdir(parents=True, exist_ok=True)
defaults[0].Runtime_Options__config.user_state_dir.mkdir(parents=True, exist_ok=True)
defaults[0].Runtime_Options__config.user_log_dir.mkdir(parents=True, exist_ok=True)
yield (defaults, app.talkers)
@pytest.fixture
def comic_cache(config, mock_version) -> Generator[comictalker.comiccacher.ComicCacher, Any, None]:
yield comictalker.comiccacher.ComicCacher(config[0].Runtime_Options__config.user_cache_dir, mock_version[0])

94
tests/integration_test.py Normal file
View File

@ -0,0 +1,94 @@
from __future__ import annotations
import settngs
import comicapi.comicarchive
import comicapi.comicinfoxml
import comicapi.genericmetadata
import comictaggerlib.resulttypes
from comictaggerlib import ctsettings
from comictaggerlib.cli import CLI
from comictalker.comictalker import ComicTalker
def test_save(
plugin_config: tuple[settngs.Config[ctsettings.ct_ns], dict[str, ComicTalker]],
tmp_comic,
comicvine_api,
md_saved,
mock_now,
) -> None:
# Overwrite the series so it has definitely changed
tmp_comic.write_cix(md_saved.replace(series="nothing"))
md = tmp_comic.read_cix()
# Check that it changed
assert md != md_saved
# Clear the cached metadata
tmp_comic.reset_cache()
# Setup the app
config = plugin_config[0]
talkers = plugin_config[1]
# Save
config[0].Commands__command = comictaggerlib.resulttypes.Action.save
# Check online, should be intercepted by comicvine_api
config[0].Runtime_Options__online = True
# Use the temporary comic we created
config[0].Runtime_Options__files = [tmp_comic.path]
# Save ComicRack tags
config[0].Runtime_Options__type = [comicapi.comicarchive.MetaDataStyle.CIX]
# Search using the correct series since we just put the wrong series name in the CBZ
config[0].Runtime_Options__metadata = comicapi.genericmetadata.GenericMetadata(series=md_saved.series)
# Run ComicTagger
CLI(config[0], talkers).run()
# Read the CBZ
md = tmp_comic.read_cix()
# Validate that we got the correct metadata back
assert md == md_saved
def test_delete(
plugin_config: tuple[settngs.Config[ctsettings.ct_ns], dict[str, ComicTalker]],
tmp_comic,
comicvine_api,
md_saved,
mock_now,
) -> None:
md = tmp_comic.read_cix()
# Check that the metadata starts correct
assert md == md_saved
# Clear the cached metadata
tmp_comic.reset_cache()
# Setup the app
config = plugin_config[0]
talkers = plugin_config[1]
# Delete
config[0].Commands__command = comictaggerlib.resulttypes.Action.delete
# Use the temporary comic we created
config[0].Runtime_Options__files = [tmp_comic.path]
# Delete ComicRack tags
config[0].Runtime_Options__type = [comicapi.comicarchive.MetaDataStyle.CIX]
# Run ComicTagger
CLI(config[0], talkers).run()
# Read the CBZ
md = tmp_comic.read_cix()
# Currently we set the default page list on load
empty_md = comicapi.genericmetadata.GenericMetadata()
empty_md.set_default_page_list(tmp_comic.get_number_of_pages())
# Validate that we got an empty metadata back
assert md == empty_md

View File

@ -69,7 +69,6 @@ def test_search(cbz, config, comicvine_api):
url_image_hash=1747255366011518976,
)
for r, e in zip(results, [cv_expected]):
# del r["url_image_hash"]
assert r == e