1+ import math
2+
13import pytest
24
35from app .models import User
46from tests import utils
7+ from flask_jwt_extended import decode_token
58
69
710class TestAuth :
811912 TEST_PASSWORD = "testpassword"
13+ ACCESS_TOKEN_DELTA = 10800 # 3 hours in seconds
14+ REFRESH_TOKEN_DELTA = 259200 # 3 days in seconds
1015
1116 @pytest .fixture (autouse = True )
1217 def setup (self , client ):
@@ -41,6 +46,34 @@ def _test_invalid_request_data(self, endpoint, expected_status=400):
4146 response = self .client .post (endpoint , data = "not json data" )
4247 assert response .status_code == 415
4348
49+ def _decode_token (self , token ):
50+ # Needs Flask app context for secret/algorithms from current_app.config
51+ with self .client .application .app_context ():
52+ return decode_token (token , allow_expired = False )
53+ def _assert_jwt_structure (self , token , expected_sub , expected_type , fresh = False ):
54+ assert token .count ("." ) == 2 , f"Token does not have three segments: { token } "
55+ payload = self ._decode_token (token )
56+ assert payload ["sub" ] == expected_sub
57+ assert payload ["type" ] == expected_type
58+ assert "iat" in payload
59+ assert "exp" in payload
60+ assert "jti" in payload
61+ assert payload ["fresh" ] is fresh
62+
63+ # Expiry check
64+ expected_delta = None
65+ if expected_type == "access" :
66+ expected_delta = self .ACCESS_TOKEN_DELTA
67+ elif expected_type == "refresh" :
68+ expected_delta = self .REFRESH_TOKEN_DELTA
69+
70+ if expected_delta is not None :
71+ actual_delta = payload ["exp" ] - payload ["iat" ]
72+ # Allow a small margin (e.g., 0-2 seconds) for processing time
73+ assert math .isclose (actual_delta , expected_delta , abs_tol = 2 ), (
74+ f"Token expiry delta { actual_delta } != expected { expected_delta } "
75+ )
76+
4477 def test_register_success (self , register_user ):
4578 response = register_user (self .TEST_EMAIL , self .TEST_PASSWORD )
4679
@@ -81,8 +114,10 @@ def test_login_success(self, register_user, login_user):
81114 data = response .get_json ()
82115 assert "access_token" in data
83116 assert "refresh_token" in data
84- assert len (data ["access_token" ]) > 0
85- assert len (data ["refresh_token" ]) > 0
117+
118+ user = self ._verify_user_in_db (self .TEST_EMAIL )
119+ self ._assert_jwt_structure (data ["access_token" ], expected_sub = str (user .id ), expected_type = "access" , fresh = True )
120+ self ._assert_jwt_structure (data ["refresh_token" ], expected_sub = str (user .id ), expected_type = "refresh" )
86121
87122 def test_login_invalid_password (self , register_user , login_user ):
88123 register_user (self .TEST_EMAIL , self .TEST_PASSWORD )
@@ -109,6 +144,9 @@ def test_refresh_token(self, register_user, login_user):
109144 assert data ["access_token" ] != original_access_token
110145 assert "refresh_token" not in data
111146
147+ user = self ._verify_user_in_db (self .TEST_EMAIL )
148+ self ._assert_jwt_structure (data ["access_token" ], expected_sub = str (user .id ), expected_type = "access" )
149+
112150 def test_refresh_token_invalid (self , register_user , login_user ):
113151 # Access token test
114152 register_user (self .TEST_EMAIL , self .TEST_PASSWORD )
0 commit comments