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