Files
The_DisExcel_project/tests/unit/test_jwt.py
T
2026-09-28 09:56:59 +03:00

176 lines
8.1 KiB
Python

from datetime import UTC, datetime, timedelta
import allure
import pytest
from fastapi import HTTPException
from jose import jwt
from src.models.configs_read.env import env_settings
from src.models.pydantic_models.model import validate_password
from src.service.auth.jwt import HashService, JwtService
@pytest.mark.unit
class TestJwt:
@pytest.mark.parametrize("data", [
pytest.param({"sub": "123"}, id="full_sub")
])
async def test_access_create_positive(self, jwt_service:JwtService, data:dict)->None:
with allure.step("create correct access token"):
token = await jwt_service.create_access_token(data)
parts=token.split(".")
assert isinstance(token, str)
assert len(parts)==3
@pytest.mark.parametrize("data, expected_exception, expected_status",[
pytest.param("", AttributeError,None,id="not_dict_value"),
pytest.param({"sub":""},HTTPException,401,id="empty_value"),
pytest.param({"":""},HTTPException,401,id="empty_key_value")
])
async def test_access_create_negative(self, jwt_service:JwtService, data:dict, expected_exception, expected_status:int)->None:
with allure.step("create invalid access token"),pytest.raises(expected_exception) as exc_info:
await jwt_service.create_access_token(data)
if expected_exception is HTTPException:
assert expected_status == exc_info.value.status_code
@pytest.mark.parametrize("data", [
pytest.param({"sub": "123"}, id="full_sub")
])
async def test_refresh_create_positive(self, jwt_service:JwtService, data:dict)->None:
with allure.step("create correct access token"):
token = await jwt_service.create_refresh_token(data)
parts=token[0].split(".")
assert isinstance(token[0], str)
assert isinstance(token[1], str)
assert len(parts)==3
@pytest.mark.parametrize("data, expected_exception",[
pytest.param("", AttributeError,id="not_dict_value"),
pytest.param({"sub":""},HTTPException, id="empty_value"),
pytest.param({"":""},HTTPException, id="empty_key_value")
])
async def test_refresh_create_negative(self, jwt_service:JwtService, data, expected_exception)->None:
with allure.step("create invalid access token"), pytest.raises(expected_exception):
await jwt_service.create_refresh_token(data)
@pytest.mark.parametrize("data", [
pytest.param({"sub": "123", "exp":datetime.now(UTC)+timedelta(minutes=15), "token_type":"access"}, id="correct_data"),
])
async def test_jwt_decode_positive(self, data:dict, monkeypatch, jwt_service:JwtService)->None:
with allure.step("patch a create token function"):
async def fake_create_access_token(data:dict)->str:
return jwt.encode(data, env_settings.SECRET_KEY, env_settings.ALGORITHM)
monkeypatch.setattr(jwt_service, "create_access_token", fake_create_access_token)
with allure.step("create and decode correct token"):
fake_token = await jwt_service.create_access_token(data)
payload=await jwt_service.jwt_decode(fake_token)
assert payload.get("sub")
assert payload.get("exp")
assert payload.get("token_type")
@pytest.mark.parametrize("data, expected_exception, expected_status", [
pytest.param({"sub": "123", "exp":datetime.now(UTC)-timedelta(minutes=15), "token_type":"access"}, HTTPException,401, id="wrong_exp"),
pytest.param({"sub": "123", "exp":datetime.now(UTC)+timedelta(minutes=15)}, HTTPException, 401,id="no_token_type"),
pytest.param({"sub": "123", "token_type":"access"}, HTTPException,401,id="no_exp"),
pytest.param({}, HTTPException, 401,id="empty_data"),
pytest.param("", AttributeError, None,id="not_dict_data")
])
async def test_jwt_decode_invalid(self,jwt_service:JwtService, expected_exception, data, monkeypatch, expected_status)->None:
with allure.step("patch a create token function"):
async def fake_create_access_token(data:dict)->str:
return jwt.encode(data, env_settings.SECRET_KEY, env_settings.ALGORITHM)
monkeypatch.setattr(jwt_service, "create_access_token", fake_create_access_token)
with allure.step("create and decode invalid token"), pytest.raises(expected_exception) as exc_info:
fake_token=await jwt_service.create_access_token(data)
await jwt_service.jwt_decode(fake_token)
if expected_exception is HTTPException:
assert expected_status == exc_info.value.status_code
@pytest.mark.parametrize("time, key, algorithm", [
pytest.param(15, "wrong_key", "HS256",id="wrong_key"),
pytest.param(-15, "correct_key", "HS256", id="wrong_time"),
pytest.param(15, "correct_key", "HS512", id="wrong_algorithm"),
])
async def test_jwt_decode_wrong_env(self, monkeypatch, time:int, key:str, algorithm:str, jwt_service)->None:
with allure.step("patch a create token function"):
async def fake_create_access_token(data:dict, key:str, algorithm:str)->str:
data.update({"exp":datetime.now(UTC)+timedelta(minutes=time)})
return jwt.encode(data, key, algorithm)
monkeypatch.setattr(jwt_service, "create_access_token", fake_create_access_token)
with allure.step("create and decode token with wrong data inside"), pytest.raises(HTTPException):
fake_token=await jwt_service.create_access_token({"sub": "123", "token_type":"access"}, key, algorithm)
await jwt_service.jwt_decode(fake_token)
@pytest.mark.parametrize("password",[
pytest.param("plain_password", id="correct_plain_password")
])
async def test_hash_and_veryfy_positive(self, password:str, hash_service:HashService)->None:
with allure.step("encode password"):
encoded_password=hash_service.plain_to_hash(password)
assert isinstance(encoded_password, str)
assert encoded_password!=password
with allure.step("decode password"):
assert hash_service.verify_password(password, encoded_password) is True
async def test_verify_wrong_password(self, hash_service:HashService)->None:
with allure.step("encode password"):
encoded_password=hash_service.plain_to_hash("plain_password")
with allure.step("decode password"):
assert hash_service.verify_password("wrong_password", encoded_password) is False
async def test_token_to_hash_determistic(self, hash_service:HashService)->None:
assert hash_service.token_to_hash("abc")==hash_service.token_to_hash("abc")
async def test_token_to_hash_different_input(self, hash_service:HashService)->None:
assert hash_service.token_to_hash("abc")!=hash_service.token_to_hash("xyz")
@pytest.mark.parametrize("complexity",[
pytest.param(8,id="low_edge"),
pytest.param(9,id="low_edge+1"),
pytest.param(16,id="default"),
pytest.param(72,id="high_edge"),
pytest.param(71,id="high_edge-1")
])
async def test_generate_temp_password_positive(self, hash_service:HashService, complexity:int)->None:
for _ in range(10000):
password=await hash_service.generate_temp_password(complexity)
try:
validate_password(password)
except ValueError as e:
pytest.fail(f"{password!r}: {e}")
@pytest.mark.parametrize("complexity", [
pytest.param(73,id="too_long"),
pytest.param(7,id="too_short")
])
async def test_generate_temp_password_negative(self, hash_service:HashService, complexity:int)->None:
password = await hash_service.generate_temp_password(complexity)
with pytest.raises(ValueError, match="8-72"):
validate_password(password)