Add tests for ComicCacher and ComicVineTalker
Move fixtures to conftest.py
Move test data to testing module
This commit is contained in:
Timmy Welch 2022-07-11 16:10:31 -07:00
parent a3e1153283
commit 0bbf417133
11 changed files with 468 additions and 101 deletions

View File

@ -133,7 +133,10 @@ class ComicCacher:
cur = con.cursor()
# remove all previous entries with this search term
cur.execute("DELETE FROM VolumeSearchCache WHERE search_term = ?", [search_term.casefold()])
cur.execute(
"DELETE FROM VolumeSearchCache WHERE search_term = ? AND source_name = ?",
[search_term.casefold(), source_name],
)
# now add in new results
for record in cv_search_results:
@ -212,7 +215,7 @@ class ComicCacher:
# remove all previous entries with this search term
cur.execute("DELETE FROM AltCovers WHERE issue_id=? AND source_name=?", [issue_id, source_name])
url_list_str = ", ".join(url_list)
url_list_str = ",".join(url_list)
# now add in new record
cur.execute(
"INSERT INTO AltCovers (source_name, issue_id, url_list) VALUES(?, ?, ?)",
@ -237,12 +240,9 @@ class ComicCacher:
return []
url_list_str = row[0]
if len(url_list_str) == 0:
if not url_list_str:
return []
raw_list = url_list_str.split(",")
url_list = []
for item in raw_list:
url_list.append(str(item).strip())
url_list = url_list_str.split(",")
return url_list
def add_volume_info(self, source_name: str, cv_volume_record: CVVolumeResults) -> None:

107
testing/comicdata.py Normal file
View File

@ -0,0 +1,107 @@
from __future__ import annotations
import dataclasses
import comicapi.genericmetadata
import comictaggerlib.resulttypes
from comicapi import utils
search_results = [
comictaggerlib.resulttypes.CVVolumeResults(
count_of_issues=1,
description="this is a description",
id=1,
image={"super_url": "https://test.org/image/1"},
name="test",
publisher=comictaggerlib.resulttypes.CVPublisher(name="test"),
start_year="", # This is currently submitted as a string and returned as an int
),
comictaggerlib.resulttypes.CVVolumeResults(
count_of_issues=1,
description="this is a description",
id=1,
image={"super_url": "https://test.org/image/2"},
name="test 2",
publisher=comictaggerlib.resulttypes.CVPublisher(name="test"),
start_year="", # This is currently submitted as a string and returned as an int
),
]
alt_covers = [
{"issue_id": 1, "url_list": ["https://test.org/image/1"]},
{"issue_id": 2, "url_list": ["https://test.org/image/2"]},
]
select_details = [
{
"issue_id": 1,
"image_url": "https://test.org/image/1",
"thumb_image_url": "https://test.org/thumb/1",
"cover_date": "1998",
"site_detail_url": "https://test.org/1",
},
{
"issue_id": 2,
"image_url": "https://test.org/image/2",
"thumb_image_url": "https://test.org/thumb/2",
"cover_date": "1998",
"site_detail_url": "https://test.org/2",
},
]
# Used to test GenericMetadata.overlay
metadata = [
(
comicapi.genericmetadata.GenericMetadata(series="test", issue="2", title="never"),
dataclasses.replace(comicapi.genericmetadata.md_test, series="test", issue="2", title="never"),
),
(
comicapi.genericmetadata.GenericMetadata(series="", issue="2", title="never"),
dataclasses.replace(comicapi.genericmetadata.md_test, series=None, issue="2", title="never"),
),
(
comicapi.genericmetadata.GenericMetadata(),
dataclasses.replace(comicapi.genericmetadata.md_test),
),
]
credits = [
("writer", "Dara Naraghi"),
("writeR", "Dara Naraghi"),
]
imprints = [
("marvel", ("", "Marvel")),
("marvel comics", ("", "Marvel")),
("aircel", ("Aircel Comics", "Marvel")),
]
additional_imprints = [
("test", ("Test", "Marvel")),
("temp", ("Temp", "DC Comics")),
]
all_imprints = imprints + additional_imprints
seed_imprints = {
"Marvel": utils.ImprintDict(
"Marvel",
{
"marvel comics": "",
"aircel": "Aircel Comics",
},
)
}
additional_seed_imprints = {
"Marvel": utils.ImprintDict("Marvel", {"test": "Test"}),
"DC Comics": utils.ImprintDict("DC Comics", {"temp": "Temp"}),
}
all_seed_imprints = {
"Marvel": seed_imprints["Marvel"].copy(),
"DC Comics": additional_seed_imprints["DC Comics"].copy(),
}
all_seed_imprints["Marvel"].update(additional_seed_imprints["Marvel"])
conflicting_seed_imprints = {"Marvel": {"test": "Never"}}

173
testing/comicvine.py Normal file
View File

@ -0,0 +1,173 @@
from __future__ import annotations
from typing import Any
import comicapi.genericmetadata
import comictaggerlib.comicvinetalker
cv_issue_result: dict[str, Any] = {
"error": "OK",
"limit": 1,
"offset": 0,
"number_of_page_results": 1,
"number_of_total_results": 1,
"status_code": 1,
"results": {
"aliases": None,
"api_detail_url": "https://comicvine.gamespot.com/api/issue/4000-311811/",
"associated_images": [],
"character_credits": [],
"character_died_in": [],
"concept_credits": [],
"cover_date": "2007-12-01",
"date_added": "2012-01-18 06:48:56",
"date_last_updated": "2012-01-18 17:27:48",
"deck": None,
"description": '<p><i>IDW Publishing continues to bring the stories of acclaimed science-fiction author Cory Doctorow to comics, this time offering up "Craphound." Despite the exoskeleton and mouth full of poisonous suckers, Jerry got along with the alien better than with most humans. In fact, Jerry had nicknamed him "Craphound", after their shared love of hunting for unique treasures at garage sales and thrift stores. They were buddies. That is, until Craphound found the old cowboy trunk. Adapted by Dara Naraghi (whose Lifelike debuts this month, too. See previous page for info), with a cover by Eisner-winning artist Paul Pope!</i></p>',
"first_appearance_characters": None,
"first_appearance_concepts": None,
"first_appearance_locations": None,
"first_appearance_objects": None,
"first_appearance_storyarcs": None,
"first_appearance_teams": None,
"has_staff_review": False,
"id": 311811,
"image": {
"icon_url": "https://comicvine.gamespot.com/a/uploads/square_avatar/11/115179/2165551-cd3.jpg",
"medium_url": "https://comicvine.gamespot.com/a/uploads/scale_medium/11/115179/2165551-cd3.jpg",
"screen_url": "https://comicvine.gamespot.com/a/uploads/screen_medium/11/115179/2165551-cd3.jpg",
"screen_large_url": "https://comicvine.gamespot.com/a/uploads/screen_kubrick/11/115179/2165551-cd3.jpg",
"small_url": "https://comicvine.gamespot.com/a/uploads/scale_small/11/115179/2165551-cd3.jpg",
"super_url": "https://comicvine.gamespot.com/a/uploads/scale_large/11/115179/2165551-cd3.jpg",
"thumb_url": "https://comicvine.gamespot.com/a/uploads/scale_avatar/11/115179/2165551-cd3.jpg",
"tiny_url": "https://comicvine.gamespot.com/a/uploads/square_mini/11/115179/2165551-cd3.jpg",
"original_url": "https://comicvine.gamespot.com/a/uploads/original/11/115179/2165551-cd3.jpg",
"image_tags": "All Images",
},
"issue_number": "3",
"location_credits": [],
"name": "Craphound",
"object_credits": [],
"person_credits": [
{
"api_detail_url": "https://comicvine.gamespot.com/api/person/4040-56410/",
"id": 56410,
"name": "Dara Naraghi",
"site_detail_url": "https://comicvine.gamespot.com/dara-naraghi/4040-56410/",
"role": "writer",
},
{
"api_detail_url": "https://comicvine.gamespot.com/api/person/4040-4306/",
"id": 4306,
"name": "Paul Pope",
"site_detail_url": "https://comicvine.gamespot.com/paul-pope/4040-4306/",
"role": "cover",
},
],
"site_detail_url": "https://comicvine.gamespot.com/cory-doctorows-futuristic-tales-of-the-here-and-no/4000-311811/",
"store_date": None,
"story_arc_credits": [],
"team_credits": [],
"team_disbanded_in": [],
"volume": {
"api_detail_url": "https://comicvine.gamespot.com/api/volume/4050-23437/",
"id": 23437,
"name": "Cory Doctorow's Futuristic Tales of the Here and Now",
"site_detail_url": "https://comicvine.gamespot.com/cory-doctorows-futuristic-tales-of-the-here-and-no/4050-23437/",
},
},
"version": "1.0",
}
cv_volume_result: dict[str, Any] = {
"error": "OK",
"limit": 1,
"offset": 0,
"number_of_page_results": 1,
"number_of_total_results": 1,
"status_code": 1,
"results": {
"count_of_issues": 6,
"id": 23437,
"name": "Cory Doctorow's Futuristic Tales of the Here and Now",
"publisher": {
"api_detail_url": "https://comicvine.gamespot.com/api/publisher/4010-1190/",
"id": 1190,
"name": "IDW Publishing",
},
"start_year": "2007",
},
"version": "1.0",
}
date = comictaggerlib.comicvinetalker.ComicVineTalker().parse_date_str(cv_issue_result["results"]["cover_date"])
cv_md = comicapi.genericmetadata.GenericMetadata(
is_empty=False,
tag_origin=None,
series=cv_issue_result["results"]["volume"]["name"],
issue=cv_issue_result["results"]["issue_number"],
title=cv_issue_result["results"]["name"],
publisher=cv_volume_result["results"]["publisher"]["name"],
month=date[1],
year=date[2],
day=date[0],
issue_count=None,
volume=None,
genre=None,
language=None,
comments=comictaggerlib.comicvinetalker.ComicVineTalker().cleanup_html(
cv_issue_result["results"]["description"], False
),
volume_count=None,
critical_rating=None,
country=None,
alternate_series=None,
alternate_number=None,
alternate_count=None,
imprint=None,
notes="Tagged with ComicTagger 1.4.4a9.dev20 using info from Comic Vine on 2022-07-11 17:42:41. [Issue ID 311811]",
web_link=cv_issue_result["results"]["site_detail_url"],
format=None,
manga=None,
black_and_white=None,
page_count=None,
maturity_rating=None,
story_arc=None,
series_group=None,
scan_info=None,
characters="",
teams="",
locations="",
credits=[
{
"person": cv_issue_result["results"]["person_credits"][0]["name"],
"role": cv_issue_result["results"]["person_credits"][0]["role"].title(),
"primary": False,
},
{
"person": cv_issue_result["results"]["person_credits"][1]["name"],
"role": cv_issue_result["results"]["person_credits"][1]["role"].title(),
"primary": False,
},
],
tags=[],
pages=[],
price=None,
is_version_of=None,
rights=None,
identifier=None,
last_mark=None,
cover_image=None,
)
class MockResponse:
"""Mocks the response object from requests"""
def __init__(self, result: dict[str, Any]) -> None:
self.status_code = 200
self.result = result
# mock json() method always returns a specific testing dictionary
def json(self) -> dict[str, list]:
return self.result

View File

@ -3,59 +3,7 @@ from __future__ import annotations
import pytest
from comicapi import utils
imprints = [
("marvel", ("", "Marvel")),
("marvel comics", ("", "Marvel")),
("aircel", ("Aircel Comics", "Marvel")),
]
additional_imprints = [
("test", ("Test", "Marvel")),
("temp", ("Temp", "DC Comics")),
]
all_imprints = imprints + additional_imprints
seed = {
"Marvel": utils.ImprintDict(
"Marvel",
{
"marvel comics": "",
"aircel": "Aircel Comics",
},
)
}
additional_seed = {
"Marvel": utils.ImprintDict("Marvel", {"test": "Test"}),
"DC Comics": utils.ImprintDict("DC Comics", {"temp": "Temp"}),
}
all_seed = {
"Marvel": seed["Marvel"].copy(),
"DC Comics": additional_seed["DC Comics"].copy(),
}
all_seed["Marvel"].update(additional_seed["Marvel"])
conflicting_seed = {"Marvel": {"test": "Never"}}
# manually seeds publishers
@pytest.fixture
def seed_publishers(monkeypatch):
publisher_seed = {}
for publisher, imprint in seed.items():
publisher_seed[publisher] = imprint
monkeypatch.setattr(utils, "publishers", publisher_seed)
@pytest.fixture
def seed_all_publishers(monkeypatch):
publisher_seed = {}
for publisher, imprint in all_seed.items():
publisher_seed[publisher] = imprint
monkeypatch.setattr(utils, "publishers", publisher_seed)
from testing.comicdata import additional_seed_imprints, all_imprints, conflicting_seed_imprints, imprints, seed_imprints
# test that that an empty list returns the input unchanged
@ -73,14 +21,14 @@ def test_get_publisher(publisher: str, expected: tuple[str, str], seed_publisher
# tests that update_publishers will initially set values
@pytest.mark.parametrize("publisher, expected", imprints)
def test_set_publisher(publisher: str, expected: tuple[str, str]):
utils.update_publishers(seed)
utils.update_publishers(seed_imprints)
assert expected == utils.get_publisher(publisher)
# tests that update_publishers will add to existing values
@pytest.mark.parametrize("publisher, expected", all_imprints)
def test_update_publisher(publisher: str, expected: tuple[str, str], seed_publishers):
utils.update_publishers(additional_seed)
utils.update_publishers(additional_seed_imprints)
assert expected == utils.get_publisher(publisher)
@ -88,6 +36,6 @@ def test_update_publisher(publisher: str, expected: tuple[str, str], seed_publis
def test_conflict_publisher(seed_all_publishers):
assert ("Test", "Marvel") == utils.get_publisher("test")
utils.update_publishers(conflicting_seed)
utils.update_publishers(conflicting_seed_imprints)
assert ("Never", "Marvel") == utils.get_publisher("test")

View File

@ -7,14 +7,15 @@ import pytest
import comicapi.comicarchive
import comicapi.genericmetadata
import testing
thisdir = pathlib.Path(__file__).parent
cbz_path = thisdir / "data" / "Cory Doctorow's Futuristic Tales of the Here and Now #001 - Anda's Game (2007).cbz"
datadir = pathlib.Path(testing.__file__).parent / "data"
cbz_path = datadir / "Cory Doctorow's Futuristic Tales of the Here and Now #001 - Anda's Game (2007).cbz"
@pytest.mark.xfail(not comicapi.comicarchive.rar_support, reason="rar support")
def test_getPageNameList():
c = comicapi.comicarchive.ComicArchive(thisdir / "data" / "fake_cbr.cbr")
c = comicapi.comicarchive.ComicArchive(datadir / "fake_cbr.cbr")
pageNameList = c.get_page_name_list()
assert pageNameList == [
@ -44,9 +45,7 @@ def test_page_type_read():
def test_metadata_read():
c = comicapi.comicarchive.ComicArchive(
thisdir / "data" / "Cory Doctorow's Futuristic Tales of the Here and Now #001 - Anda's Game (2007).cbz"
)
c = comicapi.comicarchive.ComicArchive(cbz_path)
md = c.read_cix()
assert md == comicapi.genericmetadata.md_test

44
tests/comiccacher_test.py Normal file
View File

@ -0,0 +1,44 @@
from __future__ import annotations
import pytest
import comictaggerlib.comiccacher
import comictaggerlib.resulttypes
from testing.comicdata import alt_covers, search_results, select_details
def test_create_cache(settings):
comictaggerlib.comiccacher.ComicCacher()
assert (settings.get_settings_folder() / "settings").exists()
def test_search_results(comic_cache):
comic_cache.add_search_results(
"test",
"test search",
search_results,
)
assert search_results == comic_cache.get_search_results("test", "test search")
@pytest.mark.parametrize("alt_cover", alt_covers)
def test_alt_covers(comic_cache, alt_cover):
comic_cache.add_alt_covers(**alt_cover, source_name="test")
assert alt_cover["url_list"] == comic_cache.get_alt_covers(issue_id=alt_cover["issue_id"], source_name="test")
@pytest.mark.parametrize("volume_info", search_results)
def test_volume_info(comic_cache, volume_info):
comic_cache.add_volume_info(cv_volume_record=volume_info, source_name="test")
vi = volume_info.copy()
del vi["description"]
del vi["image"]
assert vi == comic_cache.get_volume_info(volume_id=volume_info["id"], source_name="test")
@pytest.mark.parametrize("details", select_details)
def test_issue_select_details(comic_cache, details):
comic_cache.add_issue_select_details(**details)
det = details.copy()
del det["issue_id"]
assert det == comic_cache.get_issue_select_details(details["issue_id"], "test")

View File

@ -0,0 +1,37 @@
from __future__ import annotations
import pytest
import comictaggerlib.comicvinetalker
import testing.comicvine
from testing.comicdata import select_details
def test_fetch_volume_data(comicvine_api, settings, mock_now, comic_cache):
ct = comictaggerlib.comicvinetalker.ComicVineTalker()
volume = ct.fetch_volume_data(23437)
volume["start_year"] = int(volume["start_year"])
del volume["publisher"]["id"]
del volume["publisher"]["api_detail_url"]
assert volume == comic_cache.get_volume_info(23437, ct.source_name)
def test_fetch_issue_data_by_issue_id(comicvine_api, settings, mock_now):
ct = comictaggerlib.comicvinetalker.ComicVineTalker()
md = ct.fetch_issue_data_by_issue_id(311811, settings)
assert md == testing.comicvine.cv_md
@pytest.mark.parametrize("details", select_details)
def test_issue_select_details(comic_cache, details):
ct = comictaggerlib.comicvinetalker.ComicVineTalker()
ct.cache_issue_select_details(
issue_id=details["issue_id"],
image_url=details["image_url"],
thumb_url=details["thumb_image_url"],
cover_date=details["cover_date"],
page_url=details["site_detail_url"],
)
det = details.copy()
del det["issue_id"]
assert det == comic_cache.get_issue_select_details(details["issue_id"], ct.source_name)

88
tests/conftest.py Normal file
View File

@ -0,0 +1,88 @@
from __future__ import annotations
import dataclasses
import datetime
import unittest.mock
from typing import Any, Generator
import pytest
import requests
import comicapi.genericmetadata
import comictaggerlib.comiccacher
import comictaggerlib.comicvinetalker
import comictaggerlib.settings
from comicapi import utils
from testing import comicvine
from testing.comicdata import all_seed_imprints, seed_imprints
@pytest.fixture(autouse=True)
def no_requests(monkeypatch) -> None:
"""Remove requests.sessions.Session.request for all tests."""
monkeypatch.delattr("requests.sessions.Session.request")
@pytest.fixture
def comicvine_api(monkeypatch) -> unittest.mock.Mock:
# Any arguments may be passed and mock_get() will always return our
# mocked object, which only has the .json() method or None for invalid urls.
def mock_get(*args, **kwargs):
if args:
if args[0].startswith("https://comicvine.gamespot.com/api/volume/4050-23437"):
return comicvine.MockResponse(comicvine.cv_volume_result)
if args[0].startswith("https://comicvine.gamespot.com/api/issue/4000-311811"):
return comicvine.MockResponse(comicvine.cv_issue_result)
return comicvine.MockResponse({})
m_get = unittest.mock.Mock(side_effect=mock_get)
# apply the monkeypatch for requests.get to mock_get
monkeypatch.setattr(requests, "get", m_get)
return m_get
@pytest.fixture
def mock_now(monkeypatch):
class mydatetime:
time = datetime.datetime(2022, 7, 11, 17, 42, 41)
@classmethod
def now(cls):
return cls.time
monkeypatch.setattr(comictaggerlib.comicvinetalker, "datetime", mydatetime)
@pytest.fixture
def md():
yield dataclasses.replace(comicapi.genericmetadata.md_test)
# manually seeds publishers
@pytest.fixture
def seed_publishers(monkeypatch):
publisher_seed = {}
for publisher, imprint in seed_imprints.items():
publisher_seed[publisher] = imprint
monkeypatch.setattr(utils, "publishers", publisher_seed)
@pytest.fixture
def seed_all_publishers(monkeypatch):
publisher_seed = {}
for publisher, imprint in all_seed_imprints.items():
publisher_seed[publisher] = imprint
monkeypatch.setattr(utils, "publishers", publisher_seed)
@pytest.fixture
def settings(tmp_path):
yield comictaggerlib.settings.ComicTaggerSettings(tmp_path / "settings")
@pytest.fixture
def comic_cache(settings) -> Generator[comictaggerlib.comiccacher.ComicCacher, Any, None]:
yield comictaggerlib.comiccacher.ComicCacher()

View File

@ -1,37 +1,14 @@
from __future__ import annotations
import dataclasses
import pytest
import comicapi.genericmetadata
from testing.comicdata import credits, metadata
@pytest.fixture
def md():
yield dataclasses.replace(comicapi.genericmetadata.md_test)
stuff = [
(
{"series": "test", "issue": "2", "title": "never"},
dataclasses.replace(comicapi.genericmetadata.md_test, series="test", issue="2", title="never"),
),
(
{"series": "", "issue": "2", "title": "never"},
dataclasses.replace(comicapi.genericmetadata.md_test, series=None, issue="2", title="never"),
),
(
{},
dataclasses.replace(comicapi.genericmetadata.md_test),
),
]
@pytest.mark.parametrize("replaced, expected", stuff)
@pytest.mark.parametrize("replaced, expected", metadata)
def test_metadata_overlay(md: comicapi.genericmetadata.GenericMetadata, replaced, expected):
md_overlay = comicapi.genericmetadata.GenericMetadata(**replaced)
md.overlay(md_overlay)
md.overlay(replaced)
assert md == expected
@ -51,12 +28,6 @@ def test_add_credit_primary():
md.credits == [{"person": "test", "role": "writer", "primary": True}]
credits = [
("writer", "Dara Naraghi"),
("writeR", "Dara Naraghi"),
]
@pytest.mark.parametrize("role, expected", credits)
def test_get_primary_credit(md, role, expected):
assert md.get_primary_credit(role) == expected