<reponame>quanttyo/GastroHelper
# noqa
import pytest

from .test_auth import test_user


def _equal(values, expected_values):
    """Equal two dicts."""
    return set(values.keys()) not in set(
        set(values.keys()) ^ set(expected_values.keys())
    )


@pytest.mark.parametrize(
    "identifier, product_data, expected",
    [
        (
            1,
            {
                "name": "Olive oil",
                "protein": 0,
                "fat": 99.8,
                "carbohydrate": 0,
                "energy": 898,
            },
            201,
        ),
        (
            2,
            {
                "name": "Beef",
                "protein": 18.9,
                "fat": 12.4,
                "carbohydrate": 0,
                "energy": 187,
            },
            201,
        ),
        (
            3,
            {
                "name": "Milk 3.2%",
                "protein": 2.9,
                "fat": 3.2,
                "carbohydrate": 4.7,
                "energy": 59,
            },
            201,
        ),
        (
            4,
            {
                "name": "Apple",
                "protein": 0.4,
                "fat": 0.4,
                "carbohydrate": 9.8,
                "energy": 47,
            },
            201,
        ),
        (
            5,
            {
                "name": "Cornflakes",
                "protein": 6.9,
                "fat": 2.5,
                "carbohydrate": 83.6,
                "energy": 363,
            },
            201,
        ),
    ],
)
@pytest.mark.asyncio()
async def test_create_view_update_product(
    client, token, identifier, product_data, expected
):
    """Test product.

    Testing post, get and put requests to the product route.
    """
    access_token = await token(**test_user)

    response = await client.post(
        "/products", json=product_data, headers={"Authorization": access_token}
    )
    assert response.status_code == expected
    response = response.json()

    assert _equal(response, product_data)
    assert all(item in response.items() for item in product_data.items())

    response = await client.get(
        f"/products/{identifier}", headers={"Authorization": access_token}
    )

    assert response.status_code == 200

    response = response.json()

    assert _equal(response, product_data)
    assert all(item in response.items() for item in product_data.items())

    response = await client.get(
        "/products", headers={"Authorization": access_token}
    )
    list_names = [item["name"] for item in response.json()]
    assert product_data["name"] in list_names

    product_data_modified = {
        k: v if type(v) is str else v + 1.0
        for (k, v) in product_data.items()
        if k != "portion"
    }

    response = await client.put(
        f"/products/{identifier}",
        json=product_data_modified,
        headers={"Authorization": access_token},
    )

    assert response.status_code == expected
    response = response.json()
    assert _equal(response, product_data_modified)
    assert all(
        item in response.items() for item in product_data_modified.items()
    )


@pytest.mark.asyncio
async def test_bad_product(token, client):
    """Bad product.

    Testing correct validation of product route.
    """
    access_token = await token(**test_user)
    product_data = {
        "protein": 1.4,
        "fat": 0,
        "carbohydrate": 10.4,
        "energy": 47,
    }
    response = await client.post(
        "/products", json=product_data, headers={"Authorization": access_token}
    )
    assert response.status_code == 422
    assert b"field required" in response.content


@pytest.mark.parametrize(
    "identifier, recipe_data, expected, products, expected_pfc",
    [
        (
            1,
            {
                "name": "Meat steak",
                "description": "Steak",
                "breakfast": True,
                "lunch": False,
                "dinner": False,
                "five_o_clock": False,
                "supper": False,
            },
            201,
            ((1, 0.1), (2, 2)),
            {"protein": 40, "fat": 37, "carbohydrate": 2, "energy": 466},
        ),
        (
            2,
            {
                "name": "Half an apple",
                "description": "apple",
                "breakfast": True,
                "lunch": False,
                "dinner": False,
                "five_o_clock": False,
                "supper": True,
            },
            201,
            ((4, 0.5),),
            {"protein": 1, "fat": 1, "carbohydrate": 5, "energy": 24},
        ),
        (
            3,
            {
                "name": "<NAME>",
                "description": "good breakfast",
                "breakfast": True,
                "lunch": False,
                "dinner": False,
                "five_o_clock": False,
                "supper": False,
            },
            201,
            ((3, 2), (5, 1.5)),
            {"protein": 20, "fat": 13, "carbohydrate": 138, "energy": 666},
        ),
    ],
)
@pytest.mark.asyncio()
async def test_recipes(
    client, token, identifier, recipe_data, expected, products, expected_pfc
):
    """Test recipes.

    Testing recipes route for post, put and get requests.
    """
    access_token = await token(**test_user)
    response = await client.post(
        "/recipes", json=recipe_data, headers={"Authorization": access_token}
    )
    assert response.status_code == expected
    response = response.json()
    assert _equal(response, recipe_data)

    for item in products:
        response = await client.post(
            f"/recipes/{identifier}/products",
            json={"product_id": item[0], "amount": item[1]},
            headers={"Authorization": access_token},
        )
        assert response.status_code == 200

    response = await client.get(
        f"/recipes/{identifier}", headers={"Authorization": access_token}
    )

    assert response.status_code == 200
    response = response.json()
    final_pfc = response["final_p_f_c"]

    assert all(item in final_pfc.items() for item in expected_pfc.items())

    pfc = {"protein": 0, "fat": 0, "carbohydrate": 0, "energy": 0}
    for item in products:
        response = await client.get(
            f"/products/{item[0]}", headers={"Authorization": access_token}
        )
        response = {
            k: v
            for (k, v) in response.json().items()
            if k in ["protein", "fat", "carbohydrate", "energy"]
        }

        pfc = {
            k: pfc.get(k, 0) + round(response.get(k, 0) * item[1])
            for k in set(pfc)
        }

    assert all(item in final_pfc.items() for item in pfc.items())


@pytest.mark.parametrize(
    "login_data, changed_values, expected_msg, expected_code",
    [
        (
            {"username": "testuser02", "password": "<PASSWORD>"},
            {"name": "change me"},
            b"Permission error or invalid id",
            422,
        ),
        (
            {"username": "testuser03", "password": "<PASSWORD>"},
            {"name": "change me 2"},
            b"Permission error or invalid id",
            422,
        ),
    ],
)
class TestPermission:
    """Test permissions."""

    @pytest.mark.asyncio()
    async def test_products(
        self,
        client,
        token,
        login_data,
        changed_values,
        expected_msg,
        expected_code,
    ):
        """Test persmissions for products route."""
        await client.post("/user/signup", json=login_data)

        access_token = await token(**login_data)
        response = await client.put(
            f"/products/1",
            json=changed_values,
            headers={"Authorization": access_token},
        )

        assert expected_msg in response.content
        assert expected_code == response.status_code

    @pytest.mark.asyncio()
    async def test_recipes(
        self,
        client,
        token,
        login_data,
        changed_values,
        expected_msg,
        expected_code,
    ):
        """Test persmision for recipes route."""
        access_token = await token(**login_data)

        response = await client.put(
            f"/recipes/1",
            json=changed_values,
            headers={"Authorization": access_token},
        )

        assert expected_msg in response.content
        assert expected_code == response.status_code

# noqa
import pytest

from .test_auth import test_user


def _equal(values, expected_values):
    """Equal two dicts."""
    return set(values.keys()) not in set(
        set(values.keys()) ^ set(expected_values.keys())
    )


@pytest.mark.parametrize(
    "identifier, product_data, expected",
    [
        (
            1,
            {
                "name": "Olive oil",
                "protein": 0,
                "fat": 99.8,
                "carbohydrate": 0,
                "energy": 898,
            },
            201,
        ),
        (
            2,
            {
                "name": "Beef",
                "protein": 18.9,
                "fat": 12.4,
                "carbohydrate": 0,
                "energy": 187,
            },
            201,
        ),
        (
            3,
            {
                "name": "Milk 3.2%",
                "protein": 2.9,
                "fat": 3.2,
                "carbohydrate": 4.7,
                "energy": 59,
            },
            201,
        ),
        (
            4,
            {
                "name": "Apple",
                "protein": 0.4,
                "fat": 0.4,
                "carbohydrate": 9.8,
                "energy": 47,
            },
            201,
        ),
        (
            5,
            {
                "name": "Cornflakes",
                "protein": 6.9,
                "fat": 2.5,
                "carbohydrate": 83.6,
                "energy": 363,
            },
            201,
        ),
    ],
)
@pytest.mark.asyncio()
async def test_create_view_update_product(
    client, token, identifier, product_data, expected
):
    """Test product.

    Testing post, get and put requests to the product route.
    """
    access_token = await token(**test_user)

    response = await client.post(
        "/products", json=product_data, headers={"Authorization": access_token}
    )
    assert response.status_code == expected
    response = response.json()

    assert _equal(response, product_data)
    assert all(item in response.items() for item in product_data.items())

    response = await client.get(
        f"/products/{identifier}", headers={"Authorization": access_token}
    )

    assert response.status_code == 200

    response = response.json()

    assert _equal(response, product_data)
    assert all(item in response.items() for item in product_data.items())

    response = await client.get(
        "/products", headers={"Authorization": access_token}
    )
    list_names = [item["name"] for item in response.json()]
    assert product_data["name"] in list_names

    product_data_modified = {
        k: v if type(v) is str else v + 1.0
        for (k, v) in product_data.items()
        if k != "portion"
    }

    response = await client.put(
        f"/products/{identifier}",
        json=product_data_modified,
        headers={"Authorization": access_token},
    )

    assert response.status_code == expected
    response = response.json()
    assert _equal(response, product_data_modified)
    assert all(
        item in response.items() for item in product_data_modified.items()
    )


@pytest.mark.asyncio
async def test_bad_product(token, client):
    """Bad product.

    Testing correct validation of product route.
    """
    access_token = await token(**test_user)
    product_data = {
        "protein": 1.4,
        "fat": 0,
        "carbohydrate": 10.4,
        "energy": 47,
    }
    response = await client.post(
        "/products", json=product_data, headers={"Authorization": access_token}
    )
    assert response.status_code == 422
    assert b"field required" in response.content


@pytest.mark.parametrize(
    "identifier, recipe_data, expected, products, expected_pfc",
    [
        (
            1,
            {
                "name": "Meat steak",
                "description": "Steak",
                "breakfast": True,
                "lunch": False,
                "dinner": False,
                "five_o_clock": False,
                "supper": False,
            },
            201,
            ((1, 0.1), (2, 2)),
            {"protein": 40, "fat": 37, "carbohydrate": 2, "energy": 466},
        ),
        (
            2,
            {
                "name": "Half an apple",
                "description": "apple",
                "breakfast": True,
                "lunch": False,
                "dinner": False,
                "five_o_clock": False,
                "supper": True,
            },
            201,
            ((4, 0.5),),
            {"protein": 1, "fat": 1, "carbohydrate": 5, "energy": 24},
        ),
        (
            3,
            {
                "name": "<NAME>",
                "description": "good breakfast",
                "breakfast": True,
                "lunch": False,
                "dinner": False,
                "five_o_clock": False,
                "supper": False,
            },
            201,
            ((3, 2), (5, 1.5)),
            {"protein": 20, "fat": 13, "carbohydrate": 138, "energy": 666},
        ),
    ],
)
@pytest.mark.asyncio()
async def test_recipes(
    client, token, identifier, recipe_data, expected, products, expected_pfc
):
    """Test recipes.

    Testing recipes route for post, put and get requests.
    """
    access_token = await token(**test_user)
    response = await client.post(
        "/recipes", json=recipe_data, headers={"Authorization": access_token}
    )
    assert response.status_code == expected
    response = response.json()
    assert _equal(response, recipe_data)

    for item in products:
        response = await client.post(
            f"/recipes/{identifier}/products",
            json={"product_id": item[0], "amount": item[1]},
            headers={"Authorization": access_token},
        )
        assert response.status_code == 200

    response = await client.get(
        f"/recipes/{identifier}", headers={"Authorization": access_token}
    )

    assert response.status_code == 200
    response = response.json()
    final_pfc = response["final_p_f_c"]

    assert all(item in final_pfc.items() for item in expected_pfc.items())

    pfc = {"protein": 0, "fat": 0, "carbohydrate": 0, "energy": 0}
    for item in products:
        response = await client.get(
            f"/products/{item[0]}", headers={"Authorization": access_token}
        )
        response = {
            k: v
            for (k, v) in response.json().items()
            if k in ["protein", "fat", "carbohydrate", "energy"]
        }

        pfc = {
            k: pfc.get(k, 0) + round(response.get(k, 0) * item[1])
            for k in set(pfc)
        }

    assert all(item in final_pfc.items() for item in pfc.items())


@pytest.mark.parametrize(
    "login_data, changed_values, expected_msg, expected_code",
    [
        (
            {"username": "testuser02", "password": "<PASSWORD>"},
            {"name": "change me"},
            b"Permission error or invalid id",
            422,
        ),
        (
            {"username": "testuser03", "password": "<PASSWORD>"},
            {"name": "change me 2"},
            b"Permission error or invalid id",
            422,
        ),
    ],
)
class TestPermission:
    """Test permissions."""

    @pytest.mark.asyncio()
    async def test_products(
        self,
        client,
        token,
        login_data,
        changed_values,
        expected_msg,
        expected_code,
    ):
        """Test persmissions for products route."""
        await client.post("/user/signup", json=login_data)

        access_token = await token(**login_data)
        response = await client.put(
            f"/products/1",
            json=changed_values,
            headers={"Authorization": access_token},
        )

        assert expected_msg in response.content
        assert expected_code == response.status_code

    @pytest.mark.asyncio()
    async def test_recipes(
        self,
        client,
        token,
        login_data,
        changed_values,
        expected_msg,
        expected_code,
    ):
        """Test persmision for recipes route."""
        access_token = await token(**login_data)

        response = await client.put(
            f"/recipes/1",
            json=changed_values,
            headers={"Authorization": access_token},
        )

        assert expected_msg in response.content
        assert expected_code == response.status_code
# noqa Equal two dicts. Test product.      Testing post, get and put requests to the product route. Bad product.      Testing correct validation of product route. Test recipes.      Testing recipes route for post, put and get requests. Test permissions. Test persmissions for products route. Test persmision for recipes route.