import pytest
from conftest import DB, Response
from hircine.db.models import Namespace, Tag
@pytest.fixture
def query_comic_filter(execute_filter):
query = """
query comics($filter: ComicFilterInput) {
comics(filter: $filter) {
__typename
count
edges {
id
title
}
}
}
"""
return execute_filter(query)
@pytest.fixture
def query_string_filter(execute_filter):
query = """
query artists($filter: ArtistFilterInput) {
artists(filter: $filter) {
__typename
count
edges {
id
name
}
}
}
"""
return execute_filter(query)
@pytest.fixture
def query_tag_filter(execute_filter):
query = """
query tags($filter: TagFilterInput) {
tags(filter: $filter) {
__typename
count
edges {
id
name
}
}
}
"""
return execute_filter(query)
def id_list(edges):
return sorted([int(edge["id"]) for edge in edges])
@pytest.mark.parametrize(
"filter,ids",
[
(
{"include": {"name": {"contains": "robin"}}},
[3, 4],
),
({"exclude": {"name": {"contains": "smith"}}}, [2, 3]),
(
{
"exclude": {"name": {"contains": "robin"}},
"include": {"name": {"contains": "smith"}},
},
[1],
),
],
ids=[
"includes",
"excludes",
"includes and excludes",
],
)
@pytest.mark.anyio
async def test_string_filter(query_string_filter, gen_artist, filter, ids):
await DB.add_all(*gen_artist)
response = Response(await query_string_filter(filter))
response.assert_is("ArtistFilterResult")
assert id_list(response.edges) == ids
@pytest.mark.parametrize(
"filter,empty_response",
[
({"include": {"name": {"contains": ""}}}, False),
({"include": {"name": {}}}, False),
({"exclude": {"name": {"contains": ""}}}, True),
({"exclude": {"name": {}}}, False),
],
ids=[
"string (include)",
"field (include)",
"string (exclude)",
"field (exclude)",
],
)
@pytest.mark.anyio
async def test_string_filter_handles_empty(
query_string_filter, gen_artist, filter, empty_response
):
artists = await DB.add_all(*gen_artist)
response = Response(await query_string_filter(filter))
response.assert_is("ArtistFilterResult")
if empty_response:
assert response.edges == []
assert response.count == 0
else:
assert len(response.edges) == len(artists)
assert response.count == len(artists)
@pytest.mark.parametrize(
"filter",
[
{"include": {}},
{"exclude": {}},
],
ids=[
"include",
"exclude",
],
)
@pytest.mark.anyio
async def test_filter_handles_empty_field(query_string_filter, gen_artist, filter):
artists = await DB.add_all(*gen_artist)
response = Response(await query_string_filter(filter))
response.assert_is("ArtistFilterResult")
assert len(response.edges) == len(artists)
assert response.count == len(artists)
@pytest.mark.parametrize(
"filter,ids",
[
(
{"include": {"artists": {"any": 1}}},
[1, 3],
),
(
{"include": {"artists": {"all": 1}}},
[1, 3],
),
(
{"include": {"artists": {"any": [1, 4]}}},
[1, 3, 4],
),
(
{"include": {"artists": {"all": [1, 4]}}},
[3],
),
(
{"exclude": {"artists": {"any": 1}}},
[2, 4],
),
(
{"exclude": {"artists": {"all": 1}}},
[2, 4],
),
(
{"exclude": {"artists": {"any": [1, 4]}}},
[2],
),
(
{"exclude": {"artists": {"all": [1, 4]}}},
[1, 2, 4],
),
(
{
"include": {"artists": {"any": [1]}},
"exclude": {"artists": {"all": [4]}},
},
[1],
),
(
{
"include": {"artists": {"any": [1, 4]}},
"exclude": {"artists": {"all": [1, 4]}},
},
[1, 4],
),
(
{
"include": {"artists": {"any": [1, 4], "all": [1, 2]}},
},
[1],
),
],
ids=[
"includes any (single)",
"includes all (single)",
"includes any (list)",
"includes all (list)",
"excludes any (single)",
"excludes all (single)",
"excludes any (list)",
"excludes all (list)",
"includes and excludes (single)",
"includes and excludes (list)",
"includes any and all",
],
)
@pytest.mark.anyio
async def test_assoc_filter(query_comic_filter, gen_comic, filter, ids):
await DB.add_all(*gen_comic)
response = Response(await query_comic_filter(filter))
response.assert_is("ComicFilterResult")
assert id_list(response.edges) == ids
@pytest.mark.parametrize(
"filter,empty_response",
[
({"include": {"artists": {"any": []}}}, True),
({"include": {"artists": {"all": []}}}, True),
({"include": {"artists": {}}}, False),
({"exclude": {"artists": {"any": []}}}, False),
({"exclude": {"artists": {"all": []}}}, False),
({"exclude": {"artists": {}}}, False),
({"include": {"tags": {"any": ""}}}, True),
({"include": {"tags": {"any": ":"}}}, True),
],
ids=[
"list (include any)",
"list (include all)",
"field (include)",
"list (exclude any)",
"list (exclude all)",
"field (exclude)",
"string (tags)",
"specifier (tags)",
],
)
@pytest.mark.anyio
async def test_assoc_filter_handles_empty(
query_comic_filter, gen_comic, filter, empty_response
):
comics = await DB.add_all(*gen_comic)
response = Response(await query_comic_filter(filter))
response.assert_is("ComicFilterResult")
if empty_response:
assert response.edges == []
assert response.count == 0
else:
assert len(response.edges) == len(comics)
assert response.count == len(comics)
@pytest.mark.parametrize(
"filter,ids",
[
(
{"include": {"tags": {"any": "1:"}}},
[1, 2, 3],
),
(
{"include": {"tags": {"any": ":2"}}},
[1, 4],
),
(
{"include": {"tags": {"exact": ["1:3", "2:1"]}}},
[2],
),
(
{"include": {"tags": {"exact": ["1:"]}}},
[3],
),
(
{"include": {"tags": {"exact": [":4"]}}},
[3],
),
(
{"exclude": {"tags": {"all": ["1:4", "1:1"]}}},
[2, 3, 4],
),
(
{"exclude": {"tags": {"exact": ["2:1", "2:2", "2:3"]}}},
[1, 2, 3],
),
(
{"exclude": {"tags": {"exact": ["1:"]}}},
[1, 2, 4],
),
(
{"exclude": {"tags": {"exact": [":4"]}}},
[1, 2, 4],
),
],
ids=[
"includes any namespace",
"includes any tag",
"includes exact tags",
"includes exact namespace",
"includes exact tag",
"excludes all tags",
"includes exact tags",
"includes exact namespace",
"includes exact tag",
],
)
@pytest.mark.anyio
async def test_assoc_tag_filter(query_comic_filter, gen_comic, filter, ids):
await DB.add_all(*gen_comic)
response = Response(await query_comic_filter(filter))
response.assert_is("ComicFilterResult")
assert id_list(response.edges) == ids
@pytest.mark.parametrize(
"filter,ids",
[
(
{"include": {"favourite": True}},
[1],
),
(
{"include": {"rating": {"any": "EXPLICIT"}}},
[3],
),
(
{"include": {"category": {"any": "MANGA"}}},
[1, 2],
),
(
{"include": {"censorship": {"any": "MOSAIC"}}},
[3],
),
(
{"exclude": {"favourite": True}},
[2, 3, 4],
),
(
{"exclude": {"rating": {"any": ["EXPLICIT", "QUESTIONABLE"]}}},
[1, 4],
),
],
ids=[
"includes favourite",
"includes rating",
"includes category",
"includes censorship",
"excludes favourite",
"excludes ratings",
],
)
@pytest.mark.anyio
async def test_field_filter(query_comic_filter, gen_comic, filter, ids):
await DB.add_all(*gen_comic)
response = Response(await query_comic_filter(filter))
response.assert_is("ComicFilterResult")
assert id_list(response.edges) == ids
@pytest.mark.parametrize(
"filter,ids",
[
(
{"include": {"rating": {"empty": True}}},
[100],
),
(
{"include": {"rating": {"empty": False}}},
[1, 2],
),
(
{"exclude": {"rating": {"empty": True}}},
[1, 2],
),
(
{"exclude": {"rating": {"empty": False}}},
[100],
),
],
ids=[
"includes rating empty",
"includes rating not empty",
"excludes rating empty",
"excludes rating not empty",
],
)
@pytest.mark.anyio
async def test_field_presence(query_comic_filter, gen_comic, empty_comic, filter, ids):
await DB.add(next(gen_comic))
await DB.add(next(gen_comic))
await DB.add(empty_comic)
response = Response(await query_comic_filter(filter))
response.assert_is("ComicFilterResult")
assert id_list(response.edges) == ids
@pytest.mark.parametrize(
"filter,ids",
[
(
{"include": {"artists": {"empty": True}}},
[100],
),
(
{"include": {"artists": {"empty": False}}},
[1, 2],
),
(
{"exclude": {"artists": {"empty": True}}},
[1, 2],
),
(
{"exclude": {"artists": {"empty": False}}},
[100],
),
(
{"include": {"tags": {"empty": True}}},
[100],
),
(
{"include": {"tags": {"empty": False}}},
[1, 2],
),
(
{"exclude": {"tags": {"empty": True}}},
[1, 2],
),
(
{"exclude": {"tags": {"empty": False}}},
[100],
),
],
ids=[
"includes artist empty",
"includes artist not empty",
"excludes artist empty",
"excludes artist not empty",
"includes tags empty",
"includes tags not empty",
"excludes tags empty",
"excludes tags not empty",
],
)
@pytest.mark.anyio
async def test_assoc_presence(query_comic_filter, gen_comic, empty_comic, filter, ids):
await DB.add(next(gen_comic))
await DB.add(next(gen_comic))
await DB.add(empty_comic)
response = Response(await query_comic_filter(filter))
response.assert_is("ComicFilterResult")
assert id_list(response.edges) == ids
@pytest.mark.parametrize(
"filter,ids",
[
(
{"include": {"namespaces": {"any": 1}}},
[1, 2],
),
(
{"include": {"namespaces": {"all": [1, 2]}}},
[2],
),
(
{"include": {"namespaces": {"exact": [1]}}},
[1],
),
(
{"exclude": {"namespaces": {"any": 2}}},
[1],
),
(
{"exclude": {"namespaces": {"exact": [1]}}},
[2],
),
],
ids=[
"includes any namespace",
"includes all namespace",
"includes exact namespaces",
"excludes any namespace",
"excludes exact namespaces",
],
)
@pytest.mark.anyio
async def test_tag_assoc_filter(query_tag_filter, gen_namespace, gen_tag, filter, ids):
foo = await DB.add(Namespace(id=1, name="foo"))
bar = await DB.add(Namespace(id=2, name="bar"))
await DB.add(Tag(id=1, name="small", namespaces=[foo]))
await DB.add(Tag(id=2, name="large", namespaces=[foo, bar]))
response = Response(await query_tag_filter(filter))
response.assert_is("TagFilterResult")
assert id_list(response.edges) == ids