<gh_stars>1-10
from typing import Any, Dict, List
from unittest.mock import AsyncMock, patch

from loguru import logger

import app.resources as res  # isort:skip
from app.models import diff_models  # isort:skip
from app.models.user import get_user, get_user_by_login, User, UserInfo, UserRecordPatch, update, delete  # isort:skip

ListDictStrAny = List[Dict[str, Any]]


async def test_get_user_by_login(users: ListDictStrAny) -> None:
    for user in users:
        result = await get_user_by_login(user['email'], user['password'])
        assert result and UserInfo(**result.dict()) == UserInfo(**user)
    # non existent user
    email = '<EMAIL>'
    password = '<PASSWORD>'
    assert await get_user_by_login(email, password) is None


async def test_get_user(users: ListDictStrAny) -> None:
    user_id: int = users[0]['id']  # type:ignore
    user_key = f'user:{user_id}'

    # get from the database
    assert await res.redis.get(user_key) is None  # not in Redis cache
    user = await get_user(user_id)
    assert user == UserInfo(**users[0])  # got the right user
    assert await res.redis.get(user_key)  # user is in Redis cache

    # get user from Redis
    with patch('app.resources.db', new_callable=AsyncMock) as db:
        user = await get_user(user_id)
    assert user == UserInfo(**users[0])  # got the right user
    assert db.called == 0  # did not reach the database

    # inexistent user
    id_ = user_id + 1
    assert await get_user(id_) is None


async def test_update_user(users: ListDictStrAny) -> None:
    logger.info('Update password')
    user_data = users[0]
    query = User.select(User.c.id == user_data['id'])
    orig_user = await res.db.fetch_one(query)
    new_data = user_data.copy()
    new_data['password'] = '<PASSWORD>'
    fields = diff_models(UserInfo(**user_data), UserRecordPatch(**new_data))
    await update(fields, user_data['id'])
    query = User.select(User.c.id == user_data['id'])
    new_user = await res.db.fetch_one(query)
    assert new_user['password_hash'] != orig_user['password_hash']

    logger.info('Update name, email and admin')
    user_data = users[1]
    query = User.select(User.c.id == user_data['id'])
    orig_user = await res.db.fetch_one(query)
    patch = UserRecordPatch(name='Sicrano', email='<EMAIL>', admin=True)
    fields = diff_models(UserInfo(**user_data), patch)
    await update(fields, user_data['id'])
    query = User.select(User.c.id == user_data['id'])
    new_user = await res.db.fetch_one(query)
    assert new_user['password_hash'] == orig_user['password_hash']
    for field in ('name', 'email', 'admin'):
        assert new_user[field] != orig_user[field]


async def test_delete(users: ListDictStrAny) -> None:
    await delete(users[0]['id'])
    assert await get_user(id=users[0]['id']) is None

from typing import Any, Dict, List
from unittest.mock import AsyncMock, patch

from loguru import logger

import app.resources as res  # isort:skip
from app.models import diff_models  # isort:skip
from app.models.user import get_user, get_user_by_login, User, UserInfo, UserRecordPatch, update, delete  # isort:skip

ListDictStrAny = List[Dict[str, Any]]


async def test_get_user_by_login(users: ListDictStrAny) -> None:
    for user in users:
        result = await get_user_by_login(user['email'], user['password'])
        assert result and UserInfo(**result.dict()) == UserInfo(**user)
    # non existent user
    email = '<EMAIL>'
    password = '<PASSWORD>'
    assert await get_user_by_login(email, password) is None


async def test_get_user(users: ListDictStrAny) -> None:
    user_id: int = users[0]['id']  # type:ignore
    user_key = f'user:{user_id}'

    # get from the database
    assert await res.redis.get(user_key) is None  # not in Redis cache
    user = await get_user(user_id)
    assert user == UserInfo(**users[0])  # got the right user
    assert await res.redis.get(user_key)  # user is in Redis cache

    # get user from Redis
    with patch('app.resources.db', new_callable=AsyncMock) as db:
        user = await get_user(user_id)
    assert user == UserInfo(**users[0])  # got the right user
    assert db.called == 0  # did not reach the database

    # inexistent user
    id_ = user_id + 1
    assert await get_user(id_) is None


async def test_update_user(users: ListDictStrAny) -> None:
    logger.info('Update password')
    user_data = users[0]
    query = User.select(User.c.id == user_data['id'])
    orig_user = await res.db.fetch_one(query)
    new_data = user_data.copy()
    new_data['password'] = '<PASSWORD>'
    fields = diff_models(UserInfo(**user_data), UserRecordPatch(**new_data))
    await update(fields, user_data['id'])
    query = User.select(User.c.id == user_data['id'])
    new_user = await res.db.fetch_one(query)
    assert new_user['password_hash'] != orig_user['password_hash']

    logger.info('Update name, email and admin')
    user_data = users[1]
    query = User.select(User.c.id == user_data['id'])
    orig_user = await res.db.fetch_one(query)
    patch = UserRecordPatch(name='Sicrano', email='<EMAIL>', admin=True)
    fields = diff_models(UserInfo(**user_data), patch)
    await update(fields, user_data['id'])
    query = User.select(User.c.id == user_data['id'])
    new_user = await res.db.fetch_one(query)
    assert new_user['password_hash'] == orig_user['password_hash']
    for field in ('name', 'email', 'admin'):
        assert new_user[field] != orig_user[field]


async def test_delete(users: ListDictStrAny) -> None:
    await delete(users[0]['id'])
    assert await get_user(id=users[0]['id']) is None
# isort:skip # isort:skip # isort:skip # non existent user # type:ignore # get from the database # not in Redis cache # got the right user # user is in Redis cache # get user from Redis # got the right user # did not reach the database # inexistent user