summaryrefslogblamecommitdiffstatshomepage
path: root/tests/api/test_character.py
blob: 3737d49cef011be10b9e15accab92aa424a6f777 (plain) (tree)
1
2
3
4
5
6




                                   
 























































































































































































































































































                                                                                  
from datetime import datetime as dt
from datetime import timezone

import pytest
from conftest import DB, Response

from hircine.db.models import Character


@pytest.fixture
def query_character(execute_id):
    query = """
    query character($id: Int!) {
        character(id: $id) {
            __typename
            ... on Character {
                id
                name
            }
            ... on Error {
                message
            }
            ... on IDNotFoundError {
                id
            }
        }
    }
    """

    return execute_id(query)


@pytest.fixture
def query_characters(execute):
    query = """
    query characters {
        characters {
            __typename
            count
            edges {
                id
                name
            }
        }
    }
    """

    return execute(query)


@pytest.fixture
def add_character(execute_add):
    mutation = """
    mutation addCharacter($input: AddCharacterInput!) {
        addCharacter(input: $input) {
            __typename
            ... on AddSuccess {
                id
            }
            ... on Error {
                message
            }
            ... on InvalidParameterError {
                parameter
            }
        }
    }
    """

    return execute_add(mutation)


@pytest.fixture
def update_characters(execute_update):
    mutation = """
    mutation updateCharacters($ids: [Int!]!, $input: UpdateCharacterInput!) {
        updateCharacters(ids: $ids, input: $input) {
            __typename
            ... on Success {
                message
            }
            ... on Error {
                message
            }
            ... on IDNotFoundError {
                id
            }
            ... on InvalidParameterError {
                parameter
            }
        }
    }
    """  # noqa: E501

    return execute_update(mutation)


@pytest.fixture
def delete_characters(execute_delete):
    mutation = """
    mutation deleteCharacters($ids: [Int!]!) {
        deleteCharacters(ids: $ids) {
            __typename
            ... on Success {
                message
            }
            ... on Error {
                message
            }
            ... on IDNotFoundError {
                id
            }
        }
    }
    """

    return execute_delete(mutation)


@pytest.mark.anyio
async def test_query_character(query_character, gen_character):
    character = await DB.add(next(gen_character))

    response = Response(await query_character(character.id))
    response.assert_is("Character")

    assert response.id == character.id
    assert response.name == character.name


@pytest.mark.anyio
async def test_query_character_fails_not_found(query_character):
    response = Response(await query_character(1))
    response.assert_is("IDNotFoundError")
    assert response.id == 1
    assert response.message == "Character ID not found: '1'"


@pytest.mark.anyio
async def test_query_characters(query_characters, gen_character):
    characters = await DB.add_all(*gen_character)

    response = Response(await query_characters())
    response.assert_is("CharacterFilterResult")

    assert response.count == len(characters)
    assert isinstance((response.edges), list)
    assert len(response.edges) == len(characters)

    edges = iter(response.edges)
    for character in sorted(characters, key=lambda a: a.name):
        edge = next(edges)
        assert edge["id"] == character.id
        assert edge["name"] == character.name


@pytest.mark.anyio
async def test_add_character(add_character):
    response = Response(await add_character({"name": "added character"}))
    response.assert_is("AddSuccess")

    character = await DB.get(Character, response.id)
    assert character is not None
    assert character.name == "added character"


@pytest.mark.anyio
async def test_add_character_fails_empty_parameter(add_character):
    response = Response(await add_character({"name": ""}))

    response.assert_is("InvalidParameterError")
    assert response.parameter == "name"
    assert response.message == "Invalid parameter 'name': cannot be empty"


@pytest.mark.anyio
async def test_add_character_fails_exists(add_character, gen_character):
    character = await DB.add(next(gen_character))

    response = Response(await add_character({"name": character.name}))
    response.assert_is("NameExistsError")
    assert response.message == "Another Character with this name exists"


@pytest.mark.anyio
async def test_delete_character(delete_characters, gen_character):
    character = await DB.add(next(gen_character))
    id = character.id

    response = Response(await delete_characters(id))
    response.assert_is("DeleteSuccess")

    character = await DB.get(Character, id)
    assert character is None


@pytest.mark.anyio
async def test_delete_character_not_found(delete_characters):
    response = Response(await delete_characters(1))

    response.assert_is("IDNotFoundError")
    assert response.id == 1
    assert response.message == "Character ID not found: '1'"


@pytest.mark.anyio
async def test_update_character(update_characters, gen_character):
    character = await DB.add(next(gen_character))

    input = {"name": "updated character"}
    response = Response(await update_characters(character.id, input))
    response.assert_is("UpdateSuccess")

    character = await DB.get(Character, character.id)
    assert character is not None
    assert character.name == "updated character"


@pytest.mark.anyio
async def test_update_character_fails_exists(update_characters, gen_character):
    first = await DB.add(next(gen_character))
    second = await DB.add(next(gen_character))

    response = Response(await update_characters(second.id, {"name": first.name}))
    response.assert_is("NameExistsError")
    assert response.message == "Another Character with this name exists"


@pytest.mark.anyio
async def test_update_character_fails_not_found(update_characters):
    response = Response(await update_characters(1, {"name": "updated_character"}))

    response.assert_is("IDNotFoundError")
    assert response.id == 1
    assert response.message == "Character ID not found: '1'"


@pytest.mark.anyio
async def test_update_characters_cannot_bulk_edit_name(
    update_characters, gen_character
):
    first = await DB.add(next(gen_character))
    second = await DB.add(next(gen_character))

    response = Response(
        await update_characters([first.id, second.id], {"name": "unique"})
    )
    response.assert_is("InvalidParameterError")


@pytest.mark.parametrize(
    "empty",
    [
        None,
        "",
    ],
    ids=[
        "none",
        "empty string",
    ],
)
@pytest.mark.anyio
async def test_update_character_fails_empty_parameter(
    update_characters, gen_character, empty
):
    character = await DB.add(next(gen_character))
    response = Response(await update_characters(character.id, {"name": empty}))

    response.assert_is("InvalidParameterError")
    assert response.parameter == "name"
    assert response.message == "Invalid parameter 'name': cannot be empty"


@pytest.mark.anyio
async def test_update_character_changes_updated_at(update_characters):
    original_character = Character(name="character")
    original_character.updated_at = dt(2023, 1, 1, tzinfo=timezone.utc)
    original_character = await DB.add(original_character)

    response = Response(
        await update_characters(original_character.id, {"name": "updated"})
    )
    response.assert_is("UpdateSuccess")

    character = await DB.get(Character, original_character.id)
    assert character.updated_at > original_character.updated_at