11# Test cases for https://identitydivision.visualstudio.com/devex/_git/AuthLibrariesApiReview?version=GBdev&path=%2FService%20protection%2FIntial%20set%20of%20protection%20measures.md&_a=preview&anchor=common-test-cases
2+ import pickle
23from time import sleep
34from random import random
45import logging
5- from msal .throttled_http_client import ThrottledHttpClient
6+
7+ from msal .throttled_http_client import (
8+ ThrottledHttpClientBase , ThrottledHttpClient , NormalizedResponse )
9+
610from tests import unittest
7- from tests .http_client import MinimalResponse
11+ from tests .http_client import MinimalResponse as _MinimalResponse
812
913
1014logger = logging .getLogger (__name__ )
1115logging .basicConfig (level = logging .DEBUG )
1216
1317
18+ class MinimalResponse (_MinimalResponse ):
19+ SIGNATURE = str (random ()).encode ("utf-8" )
20+
21+ def __init__ (self , * args , ** kwargs ):
22+ super ().__init__ (* args , ** kwargs )
23+ self ._ = ( # Only an instance attribute will be stored in pickled instance
24+ self .__class__ .SIGNATURE ) # Useful for testing its presence in pickled instance
25+
26+
1427class DummyHttpClient (object ):
15- def __init__ (self , status_code = None , response_headers = None ):
28+ def __init__ (self , status_code = None , response_headers = None , response_text = None ):
1629 self ._status_code = status_code
1730 self ._response_headers = response_headers
31+ self ._response_text = response_text
1832
1933 def _build_dummy_response (self ):
2034 return MinimalResponse (
2135 status_code = self ._status_code ,
2236 headers = self ._response_headers ,
23- text = random (), # So that we'd know whether a new response is received
24- )
37+ text = self ._response_text if self ._response_text is not None else str (
38+ random () # So that we'd know whether a new response is received
39+ ),
40+ )
2541
2642 def post (self , url , params = None , data = None , headers = None , ** kwargs ):
2743 return self ._build_dummy_response ()
@@ -37,19 +53,54 @@ class CloseMethodCalled(Exception):
3753 pass
3854
3955
40- class TestHttpDecoration (unittest .TestCase ):
56+ class ThrottledHttpClientBaseTestCase (unittest .TestCase ):
4157
42- def test_throttled_http_client_should_not_alter_original_http_client (self ):
58+ def assertCleanPickle (self , obj ):
59+ self .assertTrue (bool (obj ), "The object should not be empty" )
60+ self .assertNotIn (
61+ MinimalResponse .SIGNATURE , pickle .dumps (obj ),
62+ "A pickled object should not contain undesirable data" )
63+
64+ def assertValidResponse (self , response ):
65+ self .assertIsInstance (response , NormalizedResponse )
66+ self .assertCleanPickle (response )
67+
68+ def test_pickled_minimal_response_should_contain_signature (self ):
69+ self .assertIn (MinimalResponse .SIGNATURE , pickle .dumps (MinimalResponse (
70+ status_code = 200 , headers = {}, text = "foo" )))
71+
72+ def test_throttled_http_client_base_response_should_not_contain_signature (self ):
73+ http_client = ThrottledHttpClientBase (DummyHttpClient (status_code = 200 ))
74+ response = http_client .post ("https://example.com" )
75+ self .assertValidResponse (response )
76+
77+ def assertNotAlteringOriginalHttpClient (self , ThrottledHttpClientClass ):
4378 original_http_client = DummyHttpClient ()
4479 original_get = original_http_client .get
4580 original_post = original_http_client .post
46- throttled_http_client = ThrottledHttpClient (original_http_client )
81+ throttled_http_client = ThrottledHttpClientClass (original_http_client )
4782 goal = """The implementation should wrap original http_client
4883 and keep it intact, instead of monkey-patching it"""
4984 self .assertNotEqual (throttled_http_client , original_http_client , goal )
5085 self .assertEqual (original_post , original_http_client .post )
5186 self .assertEqual (original_get , original_http_client .get )
5287
88+ def test_throttled_http_client_base_should_not_alter_original_http_client (self ):
89+ self .assertNotAlteringOriginalHttpClient (ThrottledHttpClientBase )
90+
91+ def test_throttled_http_client_base_should_not_nest_http_client (self ):
92+ original_http_client = DummyHttpClient ()
93+ throttled_http_client = ThrottledHttpClientBase (original_http_client )
94+ self .assertIs (original_http_client , throttled_http_client .http_client )
95+ nested_throttled_http_client = ThrottledHttpClientBase (throttled_http_client )
96+ self .assertIs (original_http_client , nested_throttled_http_client .http_client )
97+
98+
99+ class ThrottledHttpClientTestCase (ThrottledHttpClientBaseTestCase ):
100+
101+ def test_throttled_http_client_should_not_alter_original_http_client (self ):
102+ self .assertNotAlteringOriginalHttpClient (ThrottledHttpClient )
103+
53104 def _test_RetryAfter_N_seconds_should_keep_entry_for_N_seconds (
54105 self , http_client , retry_after ):
55106 http_cache = {}
@@ -112,15 +163,23 @@ def test_one_invalid_grant_should_block_a_similar_request(self):
112163 http_client = DummyHttpClient (
113164 status_code = 400 ) # It covers invalid_grant and interaction_required
114165 http_client = ThrottledHttpClient (http_client , http_cache = http_cache )
166+
115167 resp1 = http_client .post ("https://example.com" , data = {"claims" : "foo" })
116168 logger .debug (http_cache )
169+ self .assertValidResponse (resp1 )
117170 resp1_again = http_client .post ("https://example.com" , data = {"claims" : "foo" })
171+ self .assertValidResponse (resp1_again )
118172 self .assertEqual (resp1 .text , resp1_again .text , "Should return a cached response" )
173+
119174 resp2 = http_client .post ("https://example.com" , data = {"claims" : "bar" })
175+ self .assertValidResponse (resp2 )
120176 self .assertNotEqual (resp1 .text , resp2 .text , "Should return a new response" )
121177 resp2_again = http_client .post ("https://example.com" , data = {"claims" : "bar" })
178+ self .assertValidResponse (resp2_again )
122179 self .assertEqual (resp2 .text , resp2_again .text , "Should return a cached response" )
123180
181+ self .assertCleanPickle (http_cache )
182+
124183 def test_one_foci_app_recovering_from_invalid_grant_should_also_unblock_another (self ):
125184 """
126185 Need not test multiple FOCI app's acquire_token_silent() here. By design,
0 commit comments