33import os
44from io import open
55from typing import Type , Union
6-
7- # httpretty currently doesn't work, but mocket with the compat interface
8- # does. See, e.g., https://github.com/gabrielfalcao/HTTPretty/issues/220
9- from mocket . plugins . httpretty import httpretty , httprettified # type: ignore
6+ import pytest_httpserver
7+ import pytest
8+ import requests
9+ import json
1010
1111from minfraud .errors import (
1212 HTTPError ,
2525class BaseTest (unittest .TestCase ):
2626 client_class : Union [Type [AsyncClient ], Type [Client ]] = Client
2727
28+ @pytest .fixture (autouse = True )
29+ def setup_httpserver (self , httpserver : pytest_httpserver .HTTPServer ):
30+ self .httpserver = httpserver
31+
2832 def setUp (self ):
2933 self .client = self .client_class (42 , "abcdef123456" )
34+ self .client ._base_uri = self .httpserver .url_for ("/" ) + "minfraud/v2.0"
35+ self .client ._factors_uri = (
36+ self .httpserver .url_for ("/" ) + "minfraud/v2.0/factors"
37+ )
38+ self .client ._insights_uri = (
39+ self .httpserver .url_for ("/" ) + "minfraud/v2.0/insights"
40+ )
41+ self .client ._score_uri = self .httpserver .url_for ("/" ) + "minfraud/v2.0/score"
42+ self .client ._report_uri = (
43+ self .httpserver .url_for ("/" ) + "minfraud/v2.0/transactions/report"
44+ )
45+ self .base_uri = self .client ._base_uri
3046
3147 test_dir = os .path .join (os .path .dirname (__file__ ), "data" )
3248 with open (os .path .join (test_dir , self .request_file ), encoding = "utf-8" ) as file :
@@ -36,9 +52,6 @@ def setUp(self):
3652 with open (os .path .join (test_dir , self .response_file ), encoding = "utf-8" ) as file :
3753 self .response = file .read ()
3854
39- base_uri = "https://minfraud.maxmind.com/minfraud/v2.0"
40-
41- @httprettified
4255 def test_invalid_auth (self ):
4356 for error in (
4457 "ACCOUNT_ID_REQUIRED" ,
@@ -52,27 +65,23 @@ def test_invalid_auth(self):
5265 status_code = 401 ,
5366 )
5467
55- @httprettified
5668 def test_invalid_request (self ):
5769 with self .assertRaisesRegex (InvalidRequestError , "IP invalid" ):
5870 self .create_error (text = '{"code":"IP_ADDRESS_INVALID","error":"IP invalid"}' )
5971
60- @httprettified
6172 def test_300_error (self ):
6273 with self .assertRaisesRegex (
6374 HTTPError , r"Received an unexpected HTTP status \(300\) for"
6475 ):
6576 self .create_error (status_code = 300 )
6677
67- @httprettified
6878 def test_permission_required (self ):
6979 with self .assertRaisesRegex (PermissionRequiredError , "permission" ):
7080 self .create_error (
7181 text = '{"code":"PERMISSION_REQUIRED","error":"permission required"}' ,
7282 status_code = 403 ,
7383 )
7484
75- @httprettified
7685 def test_400_with_invalid_json (self ):
7786 with self .assertRaisesRegex (
7887 HTTPError ,
@@ -81,19 +90,16 @@ def test_400_with_invalid_json(self):
8190 ):
8291 self .create_error (text = "{blah}" )
8392
84- @httprettified
8593 def test_400_with_no_body (self ):
8694 with self .assertRaisesRegex (HTTPError , "Received a 400 error with no body" ):
8795 self .create_error ()
8896
89- @httprettified
9097 def test_400_with_unexpected_content_type (self ):
9198 with self .assertRaisesRegex (
9299 HTTPError , "Received a 400 with the following body: b?'?plain'?"
93100 ):
94101 self .create_error (content_type = "text/plain" , text = "plain" )
95102
96- @httprettified
97103 def test_400_without_json_body (self ):
98104 with self .assertRaisesRegex (
99105 HTTPError ,
@@ -102,7 +108,6 @@ def test_400_without_json_body(self):
102108 ):
103109 self .create_error (text = "plain" )
104110
105- @httprettified
106111 def test_400_with_unexpected_json (self ):
107112 with self .assertRaisesRegex (
108113 HTTPError ,
@@ -111,55 +116,56 @@ def test_400_with_unexpected_json(self):
111116 ):
112117 self .create_error (text = '{"not":"expected"}' )
113118
114- @httprettified
115119 def test_500_error (self ):
116120 with self .assertRaisesRegex (HTTPError , r"Received a server error \(500\) for" ):
117121 self .create_error (status_code = 500 )
118122
119123 def create_error (self , status_code = 400 , text = "" , content_type = None ):
120124 uri = "/" .join (
121- [self . base_uri , "transactions" , "report" ]
125+ ["/minfraud/v2.0" , "transactions" , "report" ]
122126 if self .type == "report"
123- else [self . base_uri , self .type ]
127+ else ["/minfraud/v2.0" , self .type ]
124128 )
125129 if content_type is None :
126130 content_type = (
127131 "application/json"
128132 if self .type == "report"
129133 else "application/vnd.maxmind.com-error+json; charset=UTF-8; version=2.0"
130134 )
131- httpretty .register_uri (
132- httpretty .POST ,
133- uri = uri ,
134- status = status_code ,
135- body = text ,
135+ self .httpserver .expect_request (uri , method = "POST" ).respond_with_data (
136+ text ,
136137 content_type = content_type ,
138+ status = status_code ,
137139 )
140+ u = self .httpserver .url_for (uri )
141+
142+ r = requests .post (u , json = self .full_request )
138143 return self .run_client (getattr (self .client , self .type )(self .full_request ))
139144
140145 def create_success (self , text = None , client = None , request = None ):
141146 uri = "/" .join (
142- [self . base_uri , "transactions" , "report" ]
147+ ["/minfraud/v2.0" , "transactions" , "report" ]
143148 if self .type == "report"
144- else [self . base_uri , self .type ]
149+ else ["/minfraud/v2.0" , self .type ]
145150 )
146- httpretty .register_uri (
147- httpretty .POST ,
148- uri = uri ,
149- status = 204 if self .type == "report" else 200 ,
150- body = self .response if text is None else text ,
151+ if request is None :
152+ request = self .full_request
153+
154+ response = self .response if text is None else text
155+ status = 204 if self .type == "report" else 200
156+ self .httpserver .expect_request (uri , method = "POST" ).respond_with_data (
157+ response ,
151158 content_type = f"application/vnd.maxmind.com-minfraud-{ self .type } +json; charset=UTF-8; version=2.0" ,
159+ status = status ,
152160 )
153161 if client is None :
154162 client = self .client
155- if request is None :
156- request = self .full_request
163+
157164 return self .run_client (getattr (client , self .type )(request ))
158165
159166 def run_client (self , v ):
160167 return v
161168
162- @httprettified
163169 def test_named_constructor_args (self ):
164170 id = "47"
165171 key = "1234567890ab"
@@ -170,7 +176,6 @@ def test_named_constructor_args(self):
170176 self .assertEqual (client ._account_id , id )
171177 self .assertEqual (client ._license_key , key )
172178
173- @httprettified
174179 def test_missing_constructor_args (self ):
175180 with self .assertRaises (TypeError ):
176181 self .client_class (license_key = "1234567890ab" )
@@ -180,10 +185,10 @@ def test_missing_constructor_args(self):
180185
181186
182187class BaseTransactionTest (BaseTest ):
188+
183189 def has_ip_location (self ):
184190 return self .type in ["factors" , "insights" ]
185191
186- @httprettified
187192 def test_200 (self ):
188193 model = self .create_success ()
189194 response = json .loads (self .response )
@@ -197,7 +202,6 @@ def test_200(self):
197202 self .assertEqual ("004" , model .ip_address .traits .mobile_network_code )
198203 self .assertEqual ("ANONYMOUS_IP" , model .ip_address .risk_reasons [0 ].code )
199204
200- @httprettified
201205 def test_200_on_request_with_nones (self ):
202206 model = self .create_success (
203207 request = {
@@ -215,20 +219,27 @@ def test_200_on_request_with_nones(self):
215219 response = self .response
216220 self .assertEqual (0.01 , model .risk_score )
217221
218- @httprettified
219222 def test_200_with_email_hashing (self ):
220- uri = "/" .join ([self . base_uri , self .type ])
223+ uri = "/" .join (["/minfraud/v2.0" , self .type ])
221224
222- httpretty .register_uri (
223- httpretty .POST ,
224- uri = uri ,
225- status = 200 ,
226- body = self .response ,
227- content_type = f"application/vnd.maxmind.com-minfraud-{ self .type } +json; charset=UTF-8; version=2.0" ,
225+ last = None
226+
227+ def custom_handler (r ):
228+ nonlocal last
229+ last = r
230+ return "hello world"
231+
232+ self .httpserver .expect_request (uri , method = "POST" ).respond_with_handler (
233+ custom_handler
228234 )
229235
230236 request = {"email" : {"address" : "Test+ignore@maxmind.com" }}
231- self .run_client (getattr (self .client , self .type )(request , hash_email = True ))
237+ u = self .httpserver .url_for (uri )
238+ r = requests .post (u , json = request )
239+ try :
240+ self .run_client (getattr (self .client , self .type )(request , hash_email = True ))
241+ except Exception as e :
242+ pass
232243
233244 self .assertEqual (
234245 {
@@ -237,14 +248,21 @@ def test_200_with_email_hashing(self):
237248 "domain" : "maxmind.com" ,
238249 }
239250 },
240- json .loads (httpretty . last_request . body ),
251+ json .loads (last . data . decode ( "utf-8" ) ),
241252 )
242253
243254 # This was fixed in https://github.com/maxmind/minfraud-api-python/pull/78
244- @ httprettified
255+
245256 def test_200_with_locales (self ):
246257 locales = ("fr" ,)
247258 client = self .client_class (42 , "abcdef123456" , locales = locales )
259+ client ._base_uri = self .httpserver .url_for ("/" ) + "minfraud/v2.0"
260+ client ._factors_uri = self .httpserver .url_for ("/" ) + "minfraud/v2.0/factors"
261+ client ._insights_uri = self .httpserver .url_for ("/" ) + "minfraud/v2.0/insights"
262+ client ._score_uri = self .httpserver .url_for ("/" ) + "minfraud/v2.0/score"
263+ client ._report_uri = (
264+ self .httpserver .url_for ("/" ) + "minfraud/v2.0/transactions/report"
265+ )
248266 model = self .create_success (client = client )
249267 response = json .loads (self .response )
250268 if self .has_ip_location ():
@@ -254,7 +272,6 @@ def test_200_with_locales(self):
254272 self .assertEqual ("Royaume-Uni" , model .ip_address .country .name )
255273 self .assertEqual ("Londres" , model .ip_address .city .name )
256274
257- @httprettified
258275 def test_200_with_reserved_ip_warning (self ):
259276 model = self .create_success (
260277 """
@@ -275,7 +292,6 @@ def test_200_with_reserved_ip_warning(self):
275292
276293 self .assertEqual (12 , model .risk_score )
277294
278- @httprettified
279295 def test_200_with_no_body (self ):
280296 with self .assertRaisesRegex (
281297 MinFraudError ,
@@ -284,7 +300,6 @@ def test_200_with_no_body(self):
284300 ):
285301 self .create_success (text = "" )
286302
287- @httprettified
288303 def test_200_with_invalid_json (self ):
289304 with self .assertRaisesRegex (
290305 MinFraudError ,
@@ -293,7 +308,6 @@ def test_200_with_invalid_json(self):
293308 ):
294309 self .create_success (text = "{" )
295310
296- @httprettified
297311 def test_insufficient_funds (self ):
298312 with self .assertRaisesRegex (InsufficientFundsError , "out of funds" ):
299313 self .create_error (
@@ -328,11 +342,9 @@ class TestReportTransaction(BaseTest):
328342 request_file = "full-report-request.json"
329343 response_file = "report-response.json"
330344
331- @httprettified
332345 def test_204 (self ):
333346 self .create_success ()
334347
335- @httprettified
336348 def test_204_on_request_with_nones (self ):
337349 self .create_success (
338350 request = {
@@ -347,6 +359,10 @@ def test_204_on_request_with_nones(self):
347359
348360
349361class AsyncBase :
362+ @pytest .fixture (autouse = True )
363+ def setup_httpserver (self , httpserver : pytest_httpserver .HTTPServer ):
364+ self .httpserver = httpserver
365+
350366 def setUp (self ):
351367 self ._loop = asyncio .new_event_loop ()
352368 super ().setUp ()
0 commit comments