def test_ChangeEmail_sends_proper_emails_has_password(db, fast_passwords): password = random_hex() new_email = f"{random_hex()}@couchers.org.invalid" user, token = generate_user(hashed_password=hash_password(password)) with account_session(token) as account: account.ChangeEmail( account_pb2.ChangeEmailReq( password=wrappers_pb2.StringValue(value=password), new_email=new_email, )) with session_scope() as session: jobs = (session.execute( select(BackgroundJob).where( BackgroundJob.job_type == BackgroundJobType.send_email)).scalars().all()) assert len(jobs) == 2 payload_for_notification_email = jobs[0].payload payload_for_confirmation_email_new_address = jobs[1].payload unique_string_notification_email_as_bytes = b"You requested that your email on Couchers.org be changed to" unique_string_for_confirmation_email_new_email_address_as_bytes = ( b"You requested that your email be changed to this email address on Couchers.org" ) assert unique_string_notification_email_as_bytes in payload_for_notification_email assert (unique_string_for_confirmation_email_new_email_address_as_bytes in payload_for_confirmation_email_new_address)
def test_GetAccountInfo(db, fast_passwords): # without password user1, token1 = generate_user(hashed_password=None) with account_session(token1) as account: res = account.GetAccountInfo(empty_pb2.Empty()) assert res.login_method == account_pb2.GetAccountInfoRes.LoginMethod.MAGIC_LINK assert not res.has_password # with password user1, token1 = generate_user(hashed_password=hash_password(random_hex())) with account_session(token1) as account: res = account.GetAccountInfo(empty_pb2.Empty()) assert res.login_method == account_pb2.GetAccountInfoRes.LoginMethod.PASSWORD assert res.has_password
def test_multiple_delete_tokens(db): """ Make sure deletion tokens are deleted on delete """ user, token = generate_user() user_id = user.id with account_session(token) as account: account.DeleteAccount(account_pb2.DeleteAccountReq(confirm=True)) account.DeleteAccount(account_pb2.DeleteAccountReq(confirm=True)) account.DeleteAccount(account_pb2.DeleteAccountReq(confirm=True)) with session_scope() as session: assert session.execute( select(func.count()).select_from( AccountDeletionToken)).scalar_one() == 3 token = session.execute( select(AccountDeletionToken)).scalars().first().token with auth_api_session() as (auth_api, metadata_interceptor): auth_api.ConfirmDeleteAccount( auth_pb2.ConfirmDeleteAccountReq(token=token, )) with session_scope() as session: assert not session.execute( select(AccountDeletionToken)).scalar_one_or_none()
def test_ChangeEmail_wrong_token(db, fast_passwords): password = random_hex() new_email = f"{random_hex()}@couchers.org.invalid" user, token = generate_user(db, hashed_password=hash_password(password)) with account_session(db, token) as account: account.ChangeEmail( account_pb2.ChangeEmailReq( password=wrappers_pb2.StringValue(value=password), new_email=new_email, )) with session_scope(db) as session: user_updated = (session.query(User).filter(User.id == user.id).filter( User.new_email == new_email).filter( User.new_email_token_created <= func.now()).filter( User.new_email_token_expiry >= func.now())).one() token = user_updated.new_email_token with auth_api_session(db) as auth_api: with pytest.raises(grpc.RpcError) as e: res = auth_api.CompleteChangeEmail( auth_pb2.CompleteChangeEmailReq( change_email_token="wrongtoken", )) assert e.value.code() == grpc.StatusCode.UNAUTHENTICATED assert e.value.details() == errors.INVALID_TOKEN with session_scope(db) as session: user_updated2 = session.query(User).filter(User.id == user.id).one() assert user_updated2.email == user.email
def test_ChangeEmail(db, fast_passwords): password = random_hex() new_email = f"{random_hex()}@couchers.org.invalid" user, token = generate_user(db, hashed_password=hash_password(password)) with account_session(db, token) as account: account.ChangeEmail( account_pb2.ChangeEmailReq( password=wrappers_pb2.StringValue(value=password), new_email=new_email, )) with session_scope(db) as session: user_updated = (session.query(User).filter(User.id == user.id).filter( User.new_email == new_email).filter( User.new_email_token_created <= func.now()).filter( User.new_email_token_expiry >= func.now())).one() token = user_updated.new_email_token with auth_api_session(db) as auth_api: res = auth_api.CompleteChangeEmail( auth_pb2.CompleteChangeEmailReq(change_email_token=token, )) with session_scope(db) as session: user_updated2 = session.query(User).filter(User.id == user.id).one() assert user_updated2.email == new_email assert user_updated2.new_email is None assert user_updated2.new_email_token is None # check there's no valid tokens left with session_scope(db) as session: assert (session.query(User).filter( User.new_email_token_created <= func.now()).filter( User.new_email_token_expiry >= func.now())).count() == 0
def test_VerifyPhone(): user, token = generate_user() user_id = user.id with account_session(token) as account, api_session(token) as api: with pytest.raises(grpc.RpcError) as e: account.VerifyPhone(account_pb2.VerifyPhoneReq(token="123455")) assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION res = api.GetUser(api_pb2.GetUserReq(user=str(user_id))) assert res.verification == 0.0 with session_scope() as session: user = session.execute( select(User).where(User.id == user_id)).scalar_one() user.phone = "+46701740605" user.phone_verification_token = "111112" user.phone_verification_sent = now() account.VerifyPhone(account_pb2.VerifyPhoneReq(token="111112")) res = api.GetUser(api_pb2.GetUserReq(user=str(user_id))) assert res.verification == 1.0 # Phone number should finally show up on in your profile settings res = account.GetAccountInfo(empty_pb2.Empty()) assert res.phone == "+46701740605"
def test_DeleteAccount_message_storage(db): user, token = generate_user() with account_session(token) as account: account.DeleteAccount( account_pb2.DeleteAccountReq(confirm=True, reason=None)) # not stored account.DeleteAccount( account_pb2.DeleteAccountReq(confirm=True, reason="")) # not stored account.DeleteAccount( account_pb2.DeleteAccountReq(confirm=True, reason="Reason")) account.DeleteAccount( account_pb2.DeleteAccountReq(confirm=True, reason="0192#(&!&#)*@//)(8")) account.DeleteAccount( account_pb2.DeleteAccountReq(confirm=True, reason="\n\n\t")) # not stored account.DeleteAccount( account_pb2.DeleteAccountReq(confirm=True, reason="1337")) with session_scope() as session: assert session.execute( select(func.count()).select_from( AccountDeletionReason)).scalar_one() == 3
def test_contributor_form(db): user, token = generate_user() with account_session(token) as account: res = account.GetContributorFormInfo(empty_pb2.Empty()) assert not res.filled_contributor_form assert res.username == user.username assert res.name == user.name assert res.email == user.email assert res.age == user.age assert res.gender == user.gender assert res.location == user.city account.MarkContributorFormFilled(account_pb2.MarkContributorFormFilledReq(filled_contributor_form=True)) res = account.GetContributorFormInfo(empty_pb2.Empty()) assert res.filled_contributor_form assert res.username == user.username assert res.name == user.name assert res.email == user.email assert res.age == user.age assert res.gender == user.gender assert res.location == user.city account.MarkContributorFormFilled(account_pb2.MarkContributorFormFilledReq(filled_contributor_form=False)) res = account.GetContributorFormInfo(empty_pb2.Empty()) assert not res.filled_contributor_form assert res.username == user.username assert res.name == user.name assert res.email == user.email assert res.age == user.age assert res.gender == user.gender assert res.location == user.city
def test_phone_uniqueness(monkeypatch): user1, token1 = generate_user() user2, token2 = generate_user() with account_session(token1) as account1, account_session( token2) as account2: def succeed(phone, message): return "success" monkeypatch.setattr(couchers.phone.sms, "send_sms", succeed) account1.ChangePhone(account_pb2.ChangePhoneReq(phone="+46701740605")) with session_scope() as session: user = session.execute( select(User).where(User.id == user1.id)).scalar_one() token = user.phone_verification_token account1.VerifyPhone(account_pb2.VerifyPhoneReq(token=token)) res = account1.GetAccountInfo(empty_pb2.Empty()) assert res.phone == "+46701740605" assert res.phone_verified # Let user2 steal user1:s phone number account2.ChangePhone(account_pb2.ChangePhoneReq(phone="+46701740605")) res = account1.GetAccountInfo(empty_pb2.Empty()) assert res.phone == "+46701740605" assert res.phone_verified res = account2.GetAccountInfo(empty_pb2.Empty()) assert res.phone == "+46701740605" assert not res.phone_verified with session_scope() as session: user = session.execute( select(User).where(User.id == user2.id)).scalar_one() token = user.phone_verification_token account2.VerifyPhone(account_pb2.VerifyPhoneReq(token=token)) # number gets wiped when it's stolen res = account1.GetAccountInfo(empty_pb2.Empty()) assert not res.phone assert not res.phone_verified res = account2.GetAccountInfo(empty_pb2.Empty()) assert res.phone == "+46701740605" assert res.phone_verified
def test_full_delete_account_with_recovery(db): user, token = generate_user() user_id = user.id with account_session(token) as account: with pytest.raises(grpc.RpcError) as e: account.DeleteAccount(account_pb2.DeleteAccountReq()) assert e.value.code() == grpc.StatusCode.FAILED_PRECONDITION assert e.value.details() == errors.MUST_CONFIRM_ACCOUNT_DELETE # Check the right email is sent with patch("couchers.email.queue_email") as mock: account.DeleteAccount(account_pb2.DeleteAccountReq(confirm=True)) mock.assert_called_once() (_, _, _, subject, _, _), _ = mock.call_args assert subject == "[TEST] Confirm your Couchers.org account deletion" with session_scope() as session: token_o = session.execute(select(AccountDeletionToken)).scalar_one() token = token_o.token user = session.execute( select(User).where(User.id == user_id)).scalar_one() assert token_o.user == user assert not user.is_deleted assert not user.undelete_token assert not user.undelete_until with auth_api_session() as (auth_api, metadata_interceptor): auth_api.ConfirmDeleteAccount( auth_pb2.ConfirmDeleteAccountReq(token=token, )) with session_scope() as session: assert not session.execute( select(AccountDeletionToken)).scalar_one_or_none() with session_scope() as session: user = session.execute( select(User).where(User.id == user_id)).scalar_one() assert user.is_deleted assert user.undelete_token assert user.undelete_until > now() undelete_token = user.undelete_token with auth_api_session() as (auth_api, metadata_interceptor): auth_api.RecoverAccount( auth_pb2.RecoverAccountReq(token=undelete_token, )) with session_scope() as session: assert not session.execute( select(AccountDeletionToken)).scalar_one_or_none() user = session.execute( select(User).where(User.id == user_id)).scalar_one() assert not user.is_deleted assert not user.undelete_token assert not user.undelete_until
def test_GetAccountInfo(db, fast_passwords): # without password user1, token1 = generate_user(hashed_password=None, email="*****@*****.**") with account_session(token1) as account: res = account.GetAccountInfo(empty_pb2.Empty()) assert res.login_method == account_pb2.GetAccountInfoRes.LoginMethod.MAGIC_LINK assert not res.has_password assert res.email == "*****@*****.**" # with password user1, token1 = generate_user(hashed_password=hash_password(random_hex()), email="*****@*****.**") with account_session(token1) as account: res = account.GetAccountInfo(empty_pb2.Empty()) assert res.login_method == account_pb2.GetAccountInfoRes.LoginMethod.PASSWORD assert res.has_password assert res.email == "*****@*****.**"
def test_contributor_form(db): user, token = generate_user() with account_session(token) as account: res = account.GetContributorFormInfo(empty_pb2.Empty()) assert not res.filled_contributor_form account.FillContributorForm( account_pb2.FillContributorFormReq( contributor_form=auth_pb2.ContributorForm())) res = account.GetContributorFormInfo(empty_pb2.Empty()) assert res.filled_contributor_form
def test_ChangePassword_add_no_passwords(db, fast_passwords): # user does not have an old password and called with empty body user, token = generate_user(db, hashed_password=None) with account_session(db, token) as account: with pytest.raises(grpc.RpcError) as e: account.ChangePassword(account_pb2.ChangePasswordReq()) assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT assert e.value.details() == errors.MISSING_BOTH_PASSWORDS with session_scope(db) as session: updated_user = session.query(User).filter(User.id == user.id).one() assert updated_user.hashed_password == None
def test_ChangeEmail_tokens_two_hour_window(db): def two_hours_one_minute_in_future(): return now() + timedelta(hours=2, minutes=1) def one_minute_ago(): return now() - timedelta(minutes=1) new_email = f"{random_hex()}@couchers.org.invalid" user, token = generate_user(hashed_password=None) with account_session(token) as account: account.ChangeEmail(account_pb2.ChangeEmailReq(new_email=new_email, )) with session_scope() as session: user = session.execute( select(User).where(User.id == user.id)).scalar_one() old_email_token = user.old_email_token new_email_token = user.new_email_token with patch("couchers.servicers.auth.now", one_minute_ago): with auth_api_session() as (auth_api, metadata_interceptor): with pytest.raises(grpc.RpcError) as e: auth_api.ConfirmChangeEmail( auth_pb2.ConfirmChangeEmailReq( change_email_token=old_email_token, )) assert e.value.code() == grpc.StatusCode.NOT_FOUND assert e.value.details() == errors.INVALID_TOKEN with pytest.raises(grpc.RpcError) as e: auth_api.ConfirmChangeEmail( auth_pb2.ConfirmChangeEmailReq( change_email_token=new_email_token, )) assert e.value.code() == grpc.StatusCode.NOT_FOUND assert e.value.details() == errors.INVALID_TOKEN with patch("couchers.servicers.auth.now", two_hours_one_minute_in_future): with auth_api_session() as (auth_api, metadata_interceptor): with pytest.raises(grpc.RpcError) as e: auth_api.ConfirmChangeEmail( auth_pb2.ConfirmChangeEmailReq( change_email_token=old_email_token, )) assert e.value.code() == grpc.StatusCode.NOT_FOUND assert e.value.details() == errors.INVALID_TOKEN with pytest.raises(grpc.RpcError) as e: auth_api.ConfirmChangeEmail( auth_pb2.ConfirmChangeEmailReq( change_email_token=new_email_token, )) assert e.value.code() == grpc.StatusCode.NOT_FOUND assert e.value.details() == errors.INVALID_TOKEN
def test_ChangePassword_normal_no_passwords(db, fast_passwords): # user has old password and called with empty body old_password = random_hex() user, token = generate_user(hashed_password=hash_password(old_password)) with account_session(token) as account: with pytest.raises(grpc.RpcError) as e: account.ChangePassword(account_pb2.ChangePasswordReq()) assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT assert e.value.details() == errors.MISSING_BOTH_PASSWORDS with session_scope() as session: updated_user = session.execute( select(User).where(User.id == user.id)).scalar_one() assert updated_user.hashed_password == hash_password(old_password)
def test_ChangePassword_remove(db, fast_passwords): old_password = random_hex() user, token = generate_user(hashed_password=hash_password(old_password)) with account_session(token) as account: with patch("couchers.servicers.account.send_password_changed_email") as mock: account.ChangePassword( account_pb2.ChangePasswordReq( old_password=wrappers_pb2.StringValue(value=old_password), ) ) mock.assert_called_once() with session_scope() as session: updated_user = session.query(User).filter(User.id == user.id).one() assert updated_user.hashed_password is None
def test_ChangeEmail_has_password(db, fast_passwords): password = random_hex() new_email = f"{random_hex()}@couchers.org.invalid" user, token = generate_user(hashed_password=hash_password(password)) with account_session(token) as account: account.ChangeEmail( account_pb2.ChangeEmailReq( password=wrappers_pb2.StringValue(value=password), new_email=new_email, )) with session_scope() as session: user_updated = session.execute( select(User).where(User.id == user.id)).scalar_one() assert user_updated.email == user.email assert user_updated.new_email == new_email assert user_updated.old_email_token is None assert not user_updated.old_email_token_created assert not user_updated.old_email_token_expiry assert not user_updated.need_to_confirm_via_old_email assert user_updated.new_email_token is not None assert user_updated.new_email_token_created <= now() assert user_updated.new_email_token_expiry >= now() assert user_updated.need_to_confirm_via_new_email token = user_updated.new_email_token with auth_api_session() as (auth_api, metadata_interceptor): res = auth_api.ConfirmChangeEmail( auth_pb2.ConfirmChangeEmailReq(change_email_token=token, )) assert res.state == auth_pb2.EMAIL_CONFIRMATION_STATE_SUCCESS with session_scope() as session: user = session.execute( select(User).where(User.id == user.id)).scalar_one() assert user.email == new_email assert user.new_email is None assert user.old_email_token is None assert user.old_email_token_created is None assert user.old_email_token_expiry is None assert not user.need_to_confirm_via_old_email assert user.new_email_token is None assert user.new_email_token_created is None assert user.new_email_token_expiry is None assert not user.need_to_confirm_via_new_email
def test_ChangePassword_add(db, fast_passwords): # user does not have an old password and is adding a new password new_password = random_hex() user, token = generate_user(db, hashed_password=None) with account_session(db, token) as account: with patch("couchers.servicers.account.send_password_changed_email" ) as mock: account.ChangePassword( account_pb2.ChangePasswordReq( new_password=wrappers_pb2.StringValue( value=new_password), )) mock.assert_called_once() with session_scope(db) as session: updated_user = session.query(User).filter(User.id == user.id).one() assert updated_user.hashed_password == hash_password(new_password)
def test_ChangePassword_remove_wrong_password(db, fast_passwords): old_password = random_hex() user, token = generate_user(db, hashed_password=hash_password(old_password)) with account_session(db, token) as account: with pytest.raises(grpc.RpcError) as e: account.ChangePassword( account_pb2.ChangePasswordReq( old_password=wrappers_pb2.StringValue( value="wrong password"), )) assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT assert e.value.details() == errors.INVALID_USERNAME_OR_PASSWORD with session_scope(db) as session: updated_user = session.query(User).filter(User.id == user.id).one() assert updated_user.hashed_password == hash_password(old_password)
def test_VerifyPhone_antibrute(): user, token = generate_user() user_id = user.id with account_session(token) as account, api_session(token) as api: with session_scope() as session: user = session.execute( select(User).where(User.id == user_id)).scalar_one() user.phone_verification_token = "111112" user.phone_verification_sent = now() user.phone = "+46701740605" for i in range(10): with pytest.raises(grpc.RpcError) as e: account.VerifyPhone(account_pb2.VerifyPhoneReq(token="123455")) if e.value.code() != grpc.StatusCode.NOT_FOUND: break assert e.value.code() == grpc.StatusCode.RESOURCE_EXHAUSTED
def test_GetAccountInfo_regression(db): # there was a bug in evaluating `has_completed_profile` on the backend (in python) # when about_me is None but the user has a key, it was failing because len(about_me) doesn't work on None uploader_user, _ = generate_user() with session_scope() as session: key = random_hex(32) filename = random_hex(32) + ".jpg" session.add( Upload( key=key, filename=filename, creator_user_id=uploader_user.id, )) session.commit() user, token = generate_user(about_me=None, avatar_key=key) with account_session(token) as account: res = account.GetAccountInfo(empty_pb2.Empty())
def test_ChangePassword_regression(db, fast_passwords): # send_password_changed_email wasn't working # user has old password and is changing to new password old_password = random_hex() new_password = random_hex() user, token = generate_user(hashed_password=hash_password(old_password)) with account_session(token) as account: account.ChangePassword( account_pb2.ChangePasswordReq( old_password=wrappers_pb2.StringValue(value=old_password), new_password=wrappers_pb2.StringValue(value=new_password), )) with session_scope() as session: updated_user = session.execute( select(User).where(User.id == user.id)).scalar_one() assert updated_user.hashed_password == hash_password(new_password)
def test_DeleteAccount_start(db): user, token = generate_user() with account_session(token) as account: with patch("couchers.email.queue_email") as mock: account.DeleteAccount( account_pb2.DeleteAccountReq(confirm=True, reason=None)) mock.assert_called_once() (_, _, _, subject, _, _), _ = mock.call_args assert subject == "[TEST] Confirm your Couchers.org account deletion" with session_scope() as session: deletion_token = session.execute( select(AccountDeletionToken).where( AccountDeletionToken.user_id == user.id)).scalar_one() assert deletion_token.is_valid assert not session.execute( select(User).where(User.id == user.id)).scalar_one().is_deleted
def test_ChangePassword_normal_no_password(db, fast_passwords): # user has old password and is changing to new password, but didn't supply old password old_password = random_hex() new_password = random_hex() user, token = generate_user(db, hashed_password=hash_password(old_password)) with account_session(db, token) as account: with pytest.raises(grpc.RpcError) as e: account.ChangePassword( account_pb2.ChangePasswordReq( new_password=wrappers_pb2.StringValue( value=new_password), )) assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT assert e.value.details() == errors.MISSING_PASSWORD with session_scope(db) as session: updated_user = session.query(User).filter(User.id == user.id).one() assert updated_user.hashed_password == hash_password(old_password)
def test_ChangeEmail_no_change(db, fast_passwords): password = random_hex() new_email = f"{random_hex()}@couchers.org.invalid" user, token = generate_user(db, hashed_password=hash_password(password)) with account_session(db, token) as account: with pytest.raises(grpc.RpcError) as e: account.ChangeEmail( account_pb2.ChangeEmailReq( password=wrappers_pb2.StringValue(value=password), new_email=user.email, )) assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT assert e.value.details() == errors.INVALID_EMAIL with session_scope(db) as session: assert (session.query(User).filter( User.new_email_token_created <= func.now()).filter( User.new_email_token_expiry >= func.now())).count() == 0
def test_ChangePassword_add_with_password(db, fast_passwords): # user does not have an old password and is adding a new password, but supplied a password new_password = random_hex() user, token = generate_user(db, hashed_password=None) with account_session(db, token) as account: with pytest.raises(grpc.RpcError) as e: account.ChangePassword( account_pb2.ChangePasswordReq( old_password=wrappers_pb2.StringValue( value="wrong password"), new_password=wrappers_pb2.StringValue(value=new_password), )) assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT assert e.value.details() == errors.NO_PASSWORD with session_scope(db) as session: updated_user = session.query(User).filter(User.id == user.id).one() assert updated_user.hashed_password == None
def test_ChangeEmail_wrong_token(db, fast_passwords): password = random_hex() new_email = f"{random_hex()}@couchers.org.invalid" user, token = generate_user(hashed_password=None) with account_session(token) as account: account.ChangeEmail(account_pb2.ChangeEmailReq(new_email=new_email, )) with auth_api_session() as (auth_api, metadata_interceptor): with pytest.raises(grpc.RpcError) as e: res = auth_api.ConfirmChangeEmail( auth_pb2.ConfirmChangeEmailReq( change_email_token="wrongtoken", )) assert e.value.code() == grpc.StatusCode.NOT_FOUND assert e.value.details() == errors.INVALID_TOKEN with session_scope() as session: user_updated = session.execute( select(User).where(User.id == user.id)).scalar_one() assert user_updated.email == user.email
def test_ChangePassword_normal(db, fast_passwords): # user has old password and is changing to new password old_password = random_hex() new_password = random_hex() user, token = generate_user(hashed_password=hash_password(old_password)) with account_session(token) as account: with patch("couchers.servicers.account.send_password_changed_email" ) as mock: account.ChangePassword( account_pb2.ChangePasswordReq( old_password=wrappers_pb2.StringValue(value=old_password), new_password=wrappers_pb2.StringValue(value=new_password), )) mock.assert_called_once() with session_scope() as session: updated_user = session.execute( select(User).where(User.id == user.id)).scalar_one() assert updated_user.hashed_password == hash_password(new_password)
def test_ChangeEmail_no_change(db, fast_passwords): password = random_hex() user, token = generate_user(hashed_password=hash_password(password)) with account_session(token) as account: with pytest.raises(grpc.RpcError) as e: account.ChangeEmail( account_pb2.ChangeEmailReq( password=wrappers_pb2.StringValue(value=password), new_email=user.email, )) assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT assert e.value.details() == errors.INVALID_EMAIL with session_scope() as session: assert (session.execute( select(func.count()).select_from(User).where( User.new_email_token_created <= func.now()).where( User.new_email_token_expiry >= func.now())) ).scalar_one() == 0
def test_ChangePassword_normal_long_password(db, fast_passwords): # user has old password and is changing to new password, but used short password old_password = random_hex() new_password = random_hex(length=1000) user, token = generate_user(hashed_password=hash_password(old_password)) with account_session(token) as account: with pytest.raises(grpc.RpcError) as e: account.ChangePassword( account_pb2.ChangePasswordReq( old_password=wrappers_pb2.StringValue(value=old_password), new_password=wrappers_pb2.StringValue(value=new_password), ) ) assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT assert e.value.details() == errors.PASSWORD_TOO_LONG with session_scope() as session: updated_user = session.query(User).filter(User.id == user.id).one() assert updated_user.hashed_password == hash_password(old_password)