From d1d654ebac2d51e3841675faeb56480e440f622f Mon Sep 17 00:00:00 2001 From: Wolfgang Müller Date: Tue, 5 Mar 2024 18:08:09 +0100 Subject: Initial commit --- tests/conftest.py | 594 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 594 insertions(+) create mode 100644 tests/conftest.py (limited to 'tests/conftest.py') diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..a36be2d --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,594 @@ +import os +import shutil +from datetime import date, timedelta +from datetime import datetime as dt +from datetime import timezone as tz + +import hircine +import hircine.db as database +import hircine.db.models as models +import hircine.plugins +import pytest +from hircine.app import schema +from hircine.enums import Category, Censorship, Direction, Language, Layout, Rating +from sqlalchemy.ext.asyncio import AsyncSession + + +@pytest.fixture(scope="session") +def anyio_backend(): + return "asyncio" + + +def pytest_addoption(parser): + parser.addoption( + "--sql-echo", + action="store_true", + help="Enable logging of SQL statements", + ) + + +@pytest.fixture +def empty_plugins(monkeypatch): + monkeypatch.setattr(hircine.plugins, "scraper_registry", {}) + monkeypatch.setattr(hircine.plugins, "transformers", []) + + +@pytest.fixture +def data(tmpdir, request): + file = request.module.__file__ + data = os.path.join(os.path.dirname(file), "data") + + if os.path.isdir(data): + shutil.copytree(data, tmpdir, dirs_exist_ok=True) + + return lambda dir: os.path.join(tmpdir, dir) + + +@pytest.fixture(scope="session") +def engine(pytestconfig): + yield database.create_engine(":memory:", echo=pytestconfig.option.sql_echo) + + +@pytest.fixture +async def session(anyio_backend, engine): + async with engine.begin() as conn: + await conn.begin_nested() + yield AsyncSession(conn, expire_on_commit=False, autoflush=False) + await conn.rollback() + + +@pytest.fixture(autouse=True) +async def patch_session(anyio_backend, session, engine, monkeypatch): + monkeypatch.setattr(hircine.db, "session", lambda: session) + + +@pytest.fixture(scope="session", autouse=True) +async def metadata(engine, anyio_backend): + await database.initialize(engine) + + +@pytest.fixture +def schema_execute(): + async def _execute(endpoint, variables=None): + return await schema.execute(endpoint, variable_values=variables) + + return _execute + + +@pytest.fixture +def execute(schema_execute): + def wrapper(q): + async def _execute(): + return await schema_execute(q) + + return _execute + + return wrapper + + +@pytest.fixture +def execute_add(schema_execute): + def wrapper(q): + async def _execute(input): + return await schema_execute(q, {"input": input}) + + return _execute + + return wrapper + + +@pytest.fixture +def execute_update(schema_execute): + def wrapper(q): + async def _execute(ids, input): + return await schema_execute(q, {"ids": ids, "input": input}) + + return _execute + + return wrapper + + +@pytest.fixture +def execute_update_single(schema_execute): + def wrapper(q): + async def _execute(id, input): + return await schema_execute(q, {"id": id, "input": input}) + + return _execute + + return wrapper + + +@pytest.fixture +def execute_delete(schema_execute): + def wrapper(q): + async def _execute(ids): + return await schema_execute(q, {"ids": ids}) + + return _execute + + return wrapper + + +@pytest.fixture +def execute_id(schema_execute): + def wrapper(q): + async def _execute(id): + return await schema_execute(q, {"id": id}) + + return _execute + + return wrapper + + +@pytest.fixture +def execute_filter(schema_execute): + def wrapper(q): + async def _execute(filter=None): + return await schema_execute(q, {"filter": filter} if filter else None) + + return _execute + + return wrapper + + +@pytest.fixture +def execute_sort(schema_execute): + def wrapper(q): + async def _execute(sort=None): + return await schema_execute(q, {"sort": sort} if sort else None) + + return _execute + + return wrapper + + +class DB: + @staticmethod + async def add(model): + async with database.session() as s: + s.add(model) + await s.commit() + return model + + @staticmethod + async def add_all(*models): + async with database.session() as s: + s.add_all(models) + await s.commit() + return models + + @staticmethod + async def get(modelcls, id, full=False): + async with database.session() as s: + options = modelcls.load_full() if full else [] + model = await s.get(modelcls, id, options=options) + return model + + @staticmethod + async def delete(modelcls, id): + async with database.session() as s: + model = await s.get(modelcls, id) + await s.delete(model) + await s.commit() + return + + +class Response: + def __init__(self, response, key=None): + assert response.errors is None + + if key is None: + assert response.data is not None + assert len(response.data) == 1 + key = next(iter(response.data.keys())) + + assert key in response.data + self.data = response.data.get(key) + self.errors = response.errors + + def __getattr__(self, name): + assert name in self.data + return self.data.get(name) + + def assert_is(self, typename): + assert self.data["__typename"] == typename + + +@pytest.fixture +def gen_artist(): + def _gen(): + yield models.Artist(id=1, name="alan smithee") + yield models.Artist(id=2, name="david agnew") + yield models.Artist(id=3, name="robin bland") + yield models.Artist(id=4, name="robin smith") + + return _gen() + + +@pytest.fixture +def gen_character(): + def _gen(): + yield models.Character(id=1, name="greta giraffe") + yield models.Character(id=2, name="bob bear") + yield models.Character(id=3, name="rico rhinoceros") + yield models.Character(id=4, name="ziggy zebra") + + return _gen() + + +@pytest.fixture +def gen_circle(): + def _gen(): + yield models.Circle(id=1, name="archimedes") + yield models.Circle(id=2, name="bankoff") + yield models.Circle(id=3, name="carlyle") + yield models.Circle(id=4, name="ford") + + return _gen() + + +@pytest.fixture +def gen_namespace(): + def _gen(): + yield models.Namespace(id=1, name="animal", sort_name="animal") + yield models.Namespace(id=2, name="human", sort_name="human") + + return _gen() + + +@pytest.fixture +def gen_tag(): + def _gen(): + yield models.Tag( + id=1, name="small", description="barely visible", namespaces=[] + ) + yield models.Tag( + id=2, + name="medium", + description="mostly average", + namespaces=[], + ) + yield models.Tag(id=3, name="big", description="impressive", namespaces=[]) + yield models.Tag( + id=4, name="massive", description="what is THAT", namespaces=[] + ) + + return _gen() + + +@pytest.fixture +def gen_world(): + def _gen(): + yield models.World(id=1, name="animal friends") + yield models.World(id=2, name="criminanimals") + yield models.World(id=3, name="in the nude") + yield models.World(id=4, name="wall street") + + return _gen() + + +@pytest.fixture +def gen_image(): + def _gen(): + yield models.Image( + id=1, hash="1bb05614b44bf177589632a51ce216a2", width=3024, height=2106 + ) + yield models.Image( + id=2, hash="77dfd96aee1bc8c36ab7095fcf18f7ff", width=3024, height=2094 + ) + yield models.Image( + id=3, hash="109aac22f29bd361fbfb19f975a1b7f0", width=3019, height=2089 + ) + yield models.Image( + id=4, hash="e18fc95f00a087ff001ecd8675eddd14", width=3024, height=2097 + ) + yield models.Image( + id=5, hash="0e2cd2f176e792a3777710978768bc90", width=1607, height=2259 + ) + yield models.Image( + id=6, hash="64e50730eb842750ebe5417a524b83e6", width=1556, height=2264 + ) + yield models.Image( + id=7, hash="d906ef54788cae72e1a511c9775e6d68", width=1525, height=2259 + ) + yield models.Image( + id=8, hash="0f8ead4a60df09a1dd071617b5d5583b", width=1545, height=2264 + ) + yield models.Image( + id=9, hash="912ccb4350fb17ea1248e26ecfb5d983", width=1607, height=2259 + ) + yield models.Image( + id=10, hash="108edee1b417f022a6d1f999bd32d16d", width=1546, height=2224 + ) + yield models.Image( + id=11, hash="97c0903cb0962741174f264aaa7015d4", width=1528, height=2257 + ) + yield models.Image( + id=12, hash="b5490ad31d2a8910087ba932073b4e52", width=1543, height=2271 + ) + yield models.Image( + id=13, hash="c9ab7febcb81974a992ed1de60c728ba", width=1611, height=2257 + ) + yield models.Image( + id=14, hash="bcfdf22ec17a09cd4f6a0af86e966e8f", width=1553, height=2265 + ) + yield models.Image( + id=15, hash="1f58f4b08bf6f4ca92bd29cbce26241e", width=1526, height=2258 + ) + yield models.Image( + id=16, hash="f87d7e55203b5e7cf9c801db48624ef0", width=1645, height=2262 + ) + + return _gen() + + +@pytest.fixture +def gen_page(gen_image): + def _gen(): + yield models.Page(id=1, index=1, path="001.png", image=next(gen_image)) + yield models.Page(id=2, index=2, path="002.png", image=next(gen_image)) + yield models.Page(id=3, index=3, path="003.png", image=next(gen_image)) + yield models.Page(id=4, index=4, path="004.png", image=next(gen_image)) + yield models.Page(id=5, index=1, path="00.jpg", image=next(gen_image)) + yield models.Page(id=6, index=2, path="01.jpg", image=next(gen_image)) + yield models.Page(id=7, index=3, path="02.jpg", image=next(gen_image)) + yield models.Page(id=8, index=4, path="03.jpg", image=next(gen_image)) + yield models.Page(id=9, index=1, path="1.jpg", image=next(gen_image)) + yield models.Page(id=10, index=2, path="2.jpg", image=next(gen_image)) + yield models.Page(id=11, index=3, path="10.jpg", image=next(gen_image)) + yield models.Page(id=12, index=4, path="11.jpg", image=next(gen_image)) + yield models.Page(id=13, index=1, path="010.png", image=next(gen_image)) + yield models.Page(id=14, index=2, path="011.png", image=next(gen_image)) + yield models.Page(id=15, index=3, path="012.png", image=next(gen_image)) + yield models.Page(id=16, index=4, path="013.png", image=next(gen_image)) + + return _gen() + + +@pytest.fixture +def gen_jumbled_pages(gen_image): + def _gen(): + yield models.Page(id=101, index=3, path="3.png", image=next(gen_image)) + yield models.Page(id=52, index=9, path="9.png", image=next(gen_image)) + yield models.Page(id=13, index=2, path="2.png", image=next(gen_image)) + yield models.Page(id=258, index=10, path="10.png", image=next(gen_image)) + yield models.Page(id=7, index=7, path="7.jpg", image=next(gen_image)) + yield models.Page(id=25, index=5, path="5.jpg", image=next(gen_image)) + yield models.Page(id=150, index=1, path="1.jpg", image=next(gen_image)) + yield models.Page(id=69, index=4, path="4.jpg", image=next(gen_image)) + yield models.Page(id=219, index=6, path="6.jpg", image=next(gen_image)) + yield models.Page(id=34, index=8, path="8.jpg", image=next(gen_image)) + + return _gen() + + +@pytest.fixture +def gen_jumbled_archive(gen_jumbled_pages): + def _gen(): + pages = [next(gen_jumbled_pages) for _ in range(10)] + yield models.Archive( + id=100, + hash="4e1243bd22c66e76c2ba9eddc1f91394", + path="comics/jumbled.zip", + size=32559235, + mtime=dt(2002, 1, 23).astimezone(), + cover=pages[0].image, + pages=pages, + page_count=len(pages), + ) + + return _gen() + + +@pytest.fixture +def gen_archive(gen_page): + def _gen(): + pages = [next(gen_page) for _ in range(4)] + yield models.Archive( + id=1, + hash="1d394f66c49ccb1d3c30870904d31bd4", + path="comics/archive-01.zip", + size=7340032, + mtime=dt(2016, 5, 10).astimezone(), + cover=pages[0].image, + pages=pages, + page_count=len(pages), + ) + + pages = [next(gen_page) for _ in range(4)] + yield models.Archive( + id=2, + hash="d7d8929b2e606200e863d390f71b53bb", + path="comics/archive-02.zip", + size=11335106, + mtime=dt(2008, 10, 2, tzinfo=tz(timedelta(hours=+6))), + cover=pages[0].image, + pages=pages, + page_count=len(pages), + ) + + pages = [next(gen_page) for _ in range(4)] + yield models.Archive( + id=3, + hash="02669dbe08c4a5f4820c10b3ff2178fa", + path="comics/sub/archive-new.zip", + size=51841969, + mtime=dt(2005, 11, 17, tzinfo=tz(timedelta(hours=+2))), + cover=pages[0].image, + pages=pages, + page_count=len(pages), + ) + + pages = [next(gen_page) for _ in range(4)] + yield models.Archive( + id=4, + hash="6b2ecf5ceb8befd6d0c1cd353a3df709", + path="comics/archive-03.zip", + size=13568769, + mtime=dt(1999, 5, 8, tzinfo=tz(timedelta(hours=-2))), + cover=pages[0].image, + pages=pages, + page_count=len(pages), + ) + + return _gen() + + +@pytest.fixture +def gen_comic( + gen_archive, + gen_artist, + gen_character, + gen_circle, + gen_world, + gen_tag, + gen_namespace, +): + def _gen(): + artists = {a.id: a for a in gen_artist} + characters = {c.id: c for c in gen_character} + + namespaces = {ns.id: ns for ns in gen_namespace} + tags = {t.id: t for t in gen_tag} + + def tag(nid, tid): + return models.ComicTag(namespace=namespaces[nid], tag=tags[tid]) + + archive = next(gen_archive) + yield models.Comic( + id=1, + title="Arid Savannah Adventures", + url="file:///home/savannah/adventures", + category=Category.MANGA, + censorship=Censorship.NONE, + date=date(2010, 7, 5), + direction=Direction.LEFT_TO_RIGHT, + favourite=True, + language=Language.EN, + layout=Layout.SINGLE, + rating=Rating.SAFE, + archive=archive, + artists=[artists[1], artists[2]], + characters=list(characters.values()), + circles=[next(gen_circle)], + worlds=[next(gen_world)], + cover=archive.cover, + pages=archive.pages, + tags=[ + tag(1, 1), + tag(1, 2), + tag(1, 3), + tag(1, 4), + ], + ) + + archive = next(gen_archive) + yield models.Comic( + id=2, + title="This Giraffe Stole My Wallet", + original_title="Diese Giraffe hat mein Geldbeutel geklaut", + url="ftp://crimes.local/giraffes.zip", + category=Category.MANGA, + censorship=Censorship.BAR, + date=date(2002, 2, 17), + direction=Direction.LEFT_TO_RIGHT, + favourite=False, + language=Language.EN, + layout=Layout.SINGLE, + rating=Rating.QUESTIONABLE, + archive=archive, + artists=[artists[3]], + characters=[characters[1]], + circles=[next(gen_circle)], + worlds=[next(gen_world)], + cover=archive.cover, + pages=archive.pages, + tags=[ + tag(1, 3), + tag(2, 1), + ], + ) + + archive = next(gen_archive) + yield models.Comic( + id=3, + title="サイのスパ", + category=Category.ARTBOOK, + censorship=Censorship.MOSAIC, + date=date(2017, 5, 3), + direction=Direction.RIGHT_TO_LEFT, + favourite=False, + language=Language.JA, + layout=Layout.DOUBLE_OFFSET, + rating=Rating.EXPLICIT, + archive=archive, + artists=[artists[1], artists[4]], + characters=[characters[3]], + circles=[next(gen_circle)], + worlds=[next(gen_world)], + cover=archive.cover, + pages=archive.pages, + tags=[ + tag(1, 4), + ], + ) + + archive = next(gen_archive) + yield models.Comic( + id=4, + title="In the Company of Vultures", + category=Category.DOUJINSHI, + date=date(2023, 3, 10), + direction=Direction.LEFT_TO_RIGHT, + favourite=False, + language=Language.EN, + layout=Layout.SINGLE, + rating=Rating.SAFE, + archive=archive, + artists=[artists[4]], + characters=[characters[4]], + circles=[next(gen_circle)], + worlds=[next(gen_world)], + cover=archive.cover, + pages=archive.pages, + tags=[ + tag(2, 1), + tag(2, 2), + tag(2, 3), + ], + ) + + return _gen() + + +@pytest.fixture +def empty_comic(gen_archive): + archive = next(gen_archive) + yield models.Comic( + id=100, + title="Hic Sunt Dracones", + archive=archive, + cover=archive.cover, + pages=archive.pages, + ) -- cgit v1.2.3-2-gb3c3