1+ """
2+ Integration tests for the authentication module using real API calls.
3+ """
4+ import pytest
5+ import os
6+
7+ from basalam_sdk .auth import (
8+ TokenInfo , ClientCredentials , AuthorizationCode
9+ )
10+ from basalam_sdk .config import BasalamConfig , Environment
11+ from basalam_sdk .errors import BasalamAuthError
12+
13+
14+ # Real test client credentials
15+ TEST_CLIENT_ID = ""
16+ TEST_CLIENT_SECRET = ""
17+ TEST_REDIRECT_URI = ""
18+
19+
20+ @pytest .fixture
21+ def integration_config ():
22+ """Create a real config for integration testing."""
23+ return BasalamConfig (
24+ environment = Environment .PRODUCTION ,
25+ timeout = 30.0 ,
26+ user_agent = "Integration Test Agent"
27+ )
28+
29+
30+ @pytest .fixture
31+ def client_credentials_auth (integration_config ):
32+ """Create a real ClientCredentials auth instance for integration testing."""
33+ return ClientCredentials (
34+ client_id = TEST_CLIENT_ID ,
35+ client_secret = TEST_CLIENT_SECRET ,
36+ scopes = ["vendor.profile.read" , "vendor.profile.write" ],
37+ config = integration_config
38+ )
39+
40+
41+ @pytest .fixture
42+ def authorization_code_auth (integration_config ):
43+ """Create a real AuthorizationCode auth instance for integration testing."""
44+ return AuthorizationCode (
45+ client_id = TEST_CLIENT_ID ,
46+ client_secret = TEST_CLIENT_SECRET ,
47+ redirect_uri = TEST_REDIRECT_URI ,
48+ scopes = ["vendor.profile.read" , "vendor.profile.write" ],
49+ config = integration_config
50+ )
51+
52+
53+ @pytest .mark .integration
54+ class TestClientCredentialsIntegration :
55+ """Integration tests for ClientCredentials authentication with real API calls."""
56+
57+ def test_get_token_sync_success (self , client_credentials_auth ):
58+ """Test successful synchronous token acquisition with real API call."""
59+ token = client_credentials_auth .get_token_sync ()
60+
61+ # Verify token structure
62+ assert isinstance (token , TokenInfo )
63+ assert token .access_token is not None
64+ assert len (token .access_token ) > 10 # Should be a substantial token
65+ assert token .token_type == "Bearer"
66+ assert token .expires_in > 0
67+ assert token .created_at is not None
68+
69+ # Verify token timing
70+ assert not token .is_expired
71+ assert not token .should_refresh # Fresh token shouldn't need refresh
72+
73+ # Verify scopes (if returned by the API)
74+ if token .scope :
75+ granted_scopes = token .granted_scopes
76+ assert len (granted_scopes ) > 0
77+ # Check if at least one expected scope is granted
78+ expected_scopes = {"vendor.profile.read" , "vendor.profile.write" }
79+ assert len (granted_scopes .intersection (expected_scopes )) > 0
80+
81+ # Verify the auth instance stores the token
82+ assert client_credentials_auth ._token_info == token
83+
84+ @pytest .mark .asyncio
85+ async def test_get_token_async_success (self , client_credentials_auth ):
86+ """Test successful asynchronous token acquisition with real API call."""
87+ # Clear any existing token
88+ client_credentials_auth ._token_info = None
89+
90+ token = await client_credentials_auth .get_token ()
91+
92+ # Verify token structure
93+ assert isinstance (token , TokenInfo )
94+ assert token .access_token is not None
95+ assert len (token .access_token ) > 10
96+ assert token .token_type == "Bearer"
97+ assert token .expires_in > 0
98+
99+ # Verify token is valid and fresh
100+ assert not token .is_expired
101+ assert not token .should_refresh
102+
103+ def test_get_auth_headers_sync (self , client_credentials_auth ):
104+ """Test getting auth headers with a real token."""
105+ # Get a real token first
106+ token = client_credentials_auth .get_token_sync ()
107+
108+ # Get auth headers
109+ headers = client_credentials_auth .get_auth_headers_sync ()
110+
111+ assert "Authorization" in headers
112+ expected_header = f"{ token .token_type } { token .access_token } "
113+ assert headers ["Authorization" ] == expected_header
114+
115+ @pytest .mark .asyncio
116+ async def test_get_auth_headers_async (self , client_credentials_auth ):
117+ """Test getting auth headers asynchronously with a real token."""
118+ # Clear any existing token
119+ client_credentials_auth ._token_info = None
120+
121+ # Get auth headers (should fetch token automatically)
122+ headers = await client_credentials_auth .get_auth_headers ()
123+
124+ assert "Authorization" in headers
125+ assert headers ["Authorization" ].startswith ("Bearer " )
126+
127+ # Verify token was stored
128+ assert client_credentials_auth ._token_info is not None
129+
130+ def test_refresh_token_sync_success (self , client_credentials_auth ):
131+ """Test successful synchronous token refresh."""
132+ # Get initial token
133+ initial_token = client_credentials_auth .get_token_sync ()
134+ initial_access_token = initial_token .access_token
135+
136+ # Refresh token (for client credentials, this gets a new token)
137+ refreshed_token = client_credentials_auth .refresh_token_sync ()
138+
139+ # Verify we got a token back
140+ assert isinstance (refreshed_token , TokenInfo )
141+ assert refreshed_token .access_token is not None
142+
143+ # For client credentials flow, refresh might return the same token
144+ # if it's still valid, or a new one
145+ assert refreshed_token .token_type == "Bearer"
146+ assert refreshed_token .expires_in > 0
147+
148+ @pytest .mark .asyncio
149+ async def test_refresh_token_async_success (self , client_credentials_auth ):
150+ """Test successful asynchronous token refresh."""
151+ # Get initial token
152+ initial_token = await client_credentials_auth .get_token ()
153+
154+ # Refresh token
155+ refreshed_token = await client_credentials_auth .refresh_token ()
156+
157+ # Verify we got a token back
158+ assert isinstance (refreshed_token , TokenInfo )
159+ assert refreshed_token .access_token is not None
160+ assert refreshed_token .token_type == "Bearer"
161+
162+ def test_scope_validation (self , client_credentials_auth ):
163+ """Test scope validation with a real token."""
164+ # Get a real token
165+ token = client_credentials_auth .get_token_sync ()
166+
167+ # Test scope checking
168+ if token .scope :
169+ granted_scopes = client_credentials_auth .get_granted_scopes ()
170+ assert isinstance (granted_scopes , set )
171+ assert len (granted_scopes ) > 0
172+
173+ # Test has_scope method
174+ for scope in granted_scopes :
175+ assert client_credentials_auth .has_scope (scope )
176+
177+ # Test with a scope we definitely don't have
178+ assert not client_credentials_auth .has_scope ("non.existent.scope" )
179+
180+ def test_multiple_scopes_request (self , integration_config ):
181+ """Test requesting multiple scopes."""
182+ auth = ClientCredentials (
183+ client_id = TEST_CLIENT_ID ,
184+ client_secret = TEST_CLIENT_SECRET ,
185+ scopes = ["vendor.profile.read" , "vendor.profile.write" , "vendor.product.read" ],
186+ config = integration_config
187+ )
188+
189+ token = auth .get_token_sync ()
190+
191+ assert isinstance (token , TokenInfo )
192+ assert token .access_token is not None
193+
194+ # Check if multiple scopes were granted (if the API supports it)
195+ if token .scope :
196+ granted_scopes = token .granted_scopes
197+ # We should have at least one of our requested scopes
198+ requested_scopes = {"vendor.profile.read" , "vendor.profile.write" , "vendor.product.read" }
199+ assert len (granted_scopes .intersection (requested_scopes )) > 0
200+
201+
202+ @pytest .mark .integration
203+ class TestErrorHandlingIntegration :
204+ """Integration tests for error handling with real API calls."""
205+
206+ def test_invalid_client_credentials (self , integration_config ):
207+ """Test behavior with invalid client credentials."""
208+ auth = ClientCredentials (
209+ client_id = "invalid_client_id" ,
210+ client_secret = "invalid_client_secret" ,
211+ scopes = ["vendor.profile.read" ],
212+ config = integration_config
213+ )
214+
215+ # Should raise an authentication error
216+ with pytest .raises (BasalamAuthError ) as exc_info :
217+ auth .get_token_sync ()
218+
219+ assert "Failed to get access token" in str (exc_info .value )
220+
221+ def test_invalid_scope_request (self , integration_config ):
222+ """Test behavior when requesting invalid scopes."""
223+ auth = ClientCredentials (
224+ client_id = TEST_CLIENT_ID ,
225+ client_secret = TEST_CLIENT_SECRET ,
226+ scopes = ["completely.invalid.scope.that.does.not.exist" ],
227+ config = integration_config
228+ )
229+
230+ try :
231+ token = auth .get_token_sync ()
232+ if token .scope :
233+ assert "completely.invalid.scope.that.does.not.exist" not in token .granted_scopes
234+ except BasalamAuthError :
235+ pass
236+
237+ @pytest .mark .asyncio
238+ async def test_network_timeout_handling (self , integration_config ):
239+ """Test behavior with very short timeout."""
240+ short_timeout_config = BasalamConfig (
241+ environment = Environment .PRODUCTION ,
242+ timeout = 0.001 # 1ms - should timeout
243+ )
244+
245+ auth = ClientCredentials (
246+ client_id = TEST_CLIENT_ID ,
247+ client_secret = TEST_CLIENT_SECRET ,
248+ config = short_timeout_config
249+ )
250+
251+ with pytest .raises (BasalamAuthError ):
252+ await auth .get_token ()
0 commit comments