All checks were successful
CI / changes (pull_request) Successful in 10s
CI Trade-In / changes (pull_request) Successful in 11s
CI / backend-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 1m14s
79 lines
3.2 KiB
Python
79 lines
3.2 KiB
Python
"""Тесты для app/core/password.py — bcrypt hash/verify (#2550)."""
|
||
|
||
from __future__ import annotations
|
||
|
||
import pytest
|
||
|
||
from app.core.password import hash_password, verify_password
|
||
|
||
|
||
def test_roundtrip() -> None:
|
||
"""hash_password → verify_password с тем же паролем возвращает True."""
|
||
hashed = hash_password("correct horse battery staple")
|
||
assert verify_password("correct horse battery staple", hashed) is True
|
||
|
||
|
||
def test_wrong_password_returns_false() -> None:
|
||
"""Неверный пароль против валидного хеша → False."""
|
||
hashed = hash_password("correct horse battery staple")
|
||
assert verify_password("wrong password", hashed) is False
|
||
|
||
|
||
def test_hash_too_long_raises_value_error() -> None:
|
||
"""Пароль >72 байт в UTF-8 → ValueError в hash_password (нет silent truncation)."""
|
||
long_password = "a" * 73
|
||
with pytest.raises(ValueError):
|
||
hash_password(long_password)
|
||
|
||
|
||
def test_hash_exactly_72_bytes_ok() -> None:
|
||
"""Ровно 72 байта — граничное значение, ещё допустимо."""
|
||
password = "a" * 72
|
||
hashed = hash_password(password)
|
||
assert verify_password(password, hashed) is True
|
||
|
||
|
||
def test_hash_too_long_multibyte_raises_value_error() -> None:
|
||
"""40 кириллических символов = 80 байт UTF-8 (2 байта/символ) → ValueError.
|
||
|
||
Проверяет, что лимит считается в байтах, а не в символах — иначе 40-символьный
|
||
кириллический пароль (< 72 символов, но 80 байт) прошёл бы мимо guard'а.
|
||
"""
|
||
long_cyrillic_password = "а" * 40
|
||
assert len(long_cyrillic_password.encode("utf-8")) == 80
|
||
with pytest.raises(ValueError):
|
||
hash_password(long_cyrillic_password)
|
||
|
||
|
||
def test_verify_too_long_returns_false_not_raise() -> None:
|
||
"""verify_password на >72-байтовом пароле возвращает False, НЕ raise."""
|
||
hashed = hash_password("some valid password")
|
||
long_password = "a" * 73
|
||
assert verify_password(long_password, hashed) is False
|
||
|
||
|
||
def test_hash_empty_raises_value_error() -> None:
|
||
"""Пустой пароль → ValueError в hash_password."""
|
||
with pytest.raises(ValueError):
|
||
hash_password("")
|
||
|
||
|
||
def test_verify_empty_returns_false() -> None:
|
||
"""Пустой пароль в verify_password → False (не raise)."""
|
||
hashed = hash_password("some valid password")
|
||
assert verify_password("", hashed) is False
|
||
|
||
|
||
def test_hash_is_unique_due_to_salt() -> None:
|
||
"""Два хеша одного пароля различаются (уникальная соль на каждый вызов)."""
|
||
password = "correct horse battery staple"
|
||
hash1 = hash_password(password)
|
||
hash2 = hash_password(password)
|
||
assert hash1 != hash2
|
||
assert verify_password(password, hash1) is True
|
||
assert verify_password(password, hash2) is True
|
||
|
||
|
||
def test_verify_malformed_hash_returns_false() -> None:
|
||
"""Некорректный (не-bcrypt) хеш в verify_password → False, не raise."""
|
||
assert verify_password("some password", "not-a-bcrypt-hash") is False
|