Closed brunodelta closed 7 months ago
Atualizei o meu código com a implementação do usuário invalido ao deletar e funcionou com a ordem do código alterada, mas quando fiz a parte de testar a expiração do usuário, ocorreu um problema colateral que quando executo:
(fast-todo-py3.11) PS ...fast-todo> task test tests/test_users.py
# ...
tests/test_users.py::test_update_user PASSED
tests/test_users.py::test_update_user_with_wrong_user PASSED
tests/test_users.py::test_create_user PASSED
tests/test_users.py::test_create_user_but_username_exists PASSED
tests/test_users.py::test_get_users PASSED
tests/test_users.py::test_get_users_with_users PASSED
tests/test_users.py::test_delete_user PASSED
tests/test_users.py::test_delete_user_with_wrong_user PASSED
# ...
Funciona, mas quando executo isso:
(fast-todo-py3.11) PS ...fast-todo> task test
# ...
tests/test_app.py::test_rota_raiz_deve_retornar_200_e_ola_mundo PASSED
tests/test_auth.py::test_get_token PASSED
tests/test_auth.py::test_token_expired_after_time PASSED
tests/test_db.py::test_create_user PASSED
tests/test_security.py::test_jwt PASSED
tests/test_users.py::test_update_user FAILED
======================================================================= FAILURES ========================================================================
___________________________________________________________________ test_update_user ____________________________________________________________________
client = <starlette.testclient.TestClient object at 0x0000013A8C3F4890>, user = <fast_todo.models.User object at 0x0000013A8C3F7F50>
token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0MkB0ZXN0LmNvbSIsImV4cCI6MTcwMzg3NjI3N30.u7Jv1w0DhYHccWSQmpWAXtOWBJoSfrgSHGQeMrXFDhw'
def test_update_user(client, user, token):
response = client.put(
f'/users/{user.id}',
headers={'Authorization': f'Bearer {token}'},
json={
'username': 'new_user',
'email': 'new@email.com',
'password': 'new_pass',
},
)
assert response.status_code == 200
> assert response.json() == {
'id': 0,
'username': 'new_user',
'email': 'new@email.com',
}
E AssertionError: assert {'id': 2, 'username': 'new_user', 'email': 'new@email.com'} == {'id': 0, 'username': 'new_user', 'email': 'new@email.com'}
E Common items:
E {'email': 'new@email.com', 'username': 'new_user'}
E Differing items:
E {'id': 2} != {'id': 0}
E Full diff:
E - {'email': 'new@email.com', 'id': 0, 'username': 'new_user'}
E ? ^
E + {'email': 'new@email.com', 'id': 2, 'username': 'new_user'}
E ? ^
tests\test_users.py:16: AssertionError
# ...
Não sei se isso ocorre por alguma diferença na ordenação dos testes do windows para linux
Mas sinto que isso não deveria ocorrer, mas não sei se isso é um problema que só ocorreu comigo, já que não vi nenhuma outra issue com esse problema
Se precisar de mais informações do código ou testar algo o link dele esta aqui https://github.com/brunodelta/fast-todo/tree/feat-brunodavi-autentificacao-robusta
Ele foi atualizado mais até concluir a parte 7
@brunodelta, assim que possível eu vou analisar esse problema.
Muito obrigado pelo feedback :heart:
Cabei de testar no linux e ocorreu o mesmo problema, acho que devo ter errado algo no código mesmo
Como a cada novo usuário o id é atualizado seria um teste de uma qualidade menor deixar dessa forma?
tests/test_users.py
def test_update_user(client, user, token):
response = client.put(
f'/users/{user.id}',
headers={'Authorization': f'Bearer {token}'},
json={
'username': 'new_user',
'email': 'new@email.com',
'password': 'new_pass',
},
)
assert response.status_code == 200
assert response.json() == {
- 'id': 0,
+ 'id': user.id,
'username': 'new_user',
'email': 'new@email.com',
}
Quando implementei as atualizações dos testes usando o factory-boy na rota de atualização das informações do usuário, demorei um tempo para entender porque os testes estavam dando erro por causa do id do usuário, com isso gostaria de fazer uma sugestão para alertar isso para usuários novatos (como eu) com essas implementações mais complexas que a ordem dos testes afetam seu resultado
Nota do ambiente
```python # Resto do código... @router.post('/token', response_model=Token) def login_for_access_token( form_data: OAuth2Form, session: Session, ): invalid_access = HTTPException( status_code=400, detail='Email ou senha inválido' ) user = session.scalar(select(User).where(User.email == form_data.username)) if not user: raise invalid_access if not verify_password(form_data.password, user.password): raise invalid_access access_token = create_access_token(data={'sub': user.email}) return {'access_token': access_token, 'token_type': 'bearer'} ```fast_todo/routes/auth.py
```python # Resto do código... @router.put('/{user_id}', status_code=200, response_model=UserPublic) def update_user( user_id: int, user: UserSchema, session: Session, current_user: CurrentUser, ): if current_user.id != user_id: raise HTTPException(status_code=400, detail='Permissões insuficientes') current_user.username = user.username current_user.password = user.password current_user.email = user.email session.commit() session.refresh(current_user) return current_user # Resto do código... ```fast_todo/routes/users.py
```python import factory import pytest from fastapi.testclient import TestClient from sqlalchemy import StaticPool, create_engine from sqlalchemy.orm import sessionmaker from fast_todo.app import app from fast_todo.database import get_session from fast_todo.models import Base, User from fast_todo.security import get_password_hash class UserFactory(factory.Factory): class Meta: model = User id = factory.Sequence(lambda n: n) username = factory.LazyAttribute(lambda obj: f'test{obj.id}') email = factory.LazyAttribute(lambda obj: f'{obj.username}@test.com') password = factory.LazyAttribute(lambda obj: f'{obj.username}@example.com') @pytest.fixture def session(): engine = create_engine( 'sqlite:///:memory:', connect_args={'check_same_thread': False}, poolclass=StaticPool, ) Session = sessionmaker(bind=engine) Base.metadata.create_all(engine) yield Session() Base.metadata.drop_all(engine) @pytest.fixture def client(session): def get_session_override(): return session with TestClient(app) as client: app.dependency_overrides[get_session] = get_session_override yield client app.dependency_overrides.clear() @pytest.fixture def user(session): password = 'secret' user = UserFactory(password=get_password_hash(password)) session.add(user) session.commit() session.refresh(user) user.clean_password = password return user @pytest.fixture def other_user(session): password = 'secret' user = UserFactory(password=get_password_hash(password)) session.add(user) session.commit() session.refresh(user) user.clean_password = password return user @pytest.fixture def token(client, user): response = client.post( '/auth/token', data={'username': user.email, 'password': user.clean_password}, ) json = response.json() return json['access_token'] ```tests/conftest.py
```python from fast_todo.schemas import UserPublic def test_update_user(client, user, token): response = client.put( f'/users/{user.id}', headers={'Authorization': f'Bearer {token}'}, json={ 'username': 'new_user', 'email': 'new@email.com', 'password': 'new_pass', }, ) assert response.status_code == 200 assert response.json() == { 'id': 1, 'username': 'new_user', 'email': 'new@email.com', } def test_update_user_with_wrong_user(client, other_user, token): response = client.put( f'/users/{other_user.id}', headers={'Authorization': f'Bearer {token}'}, json={ 'username': 'invalid_user', 'email': 'invalid_email@example.com', 'password': 'wrong_password', }, ) assert response.status_code == 400 assert response.json() == {'detail': 'Permissões insuficientes'} # Resto do código... ```test/test_users.py
Na criação dos meus testes deixava na ordem:
Não tenho certeza se fiz algo de errado, mas só passou nos testes quando deixei na ordem: