Skip to content

Commit 8c0d126

Browse files
committed
Remove mocket
1 parent d9cbc28 commit 8c0d126

File tree

5 files changed

+79
-62
lines changed

5 files changed

+79
-62
lines changed

.github/workflows/test.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@ jobs:
1111

1212
strategy:
1313
matrix:
14-
# We don't test on Windows currently as it appears mocket may not
15-
# work there.
14+
# TODO: add windows-latest
1615
platform: [ubuntu-latest, macos-latest]
1716
python-version: [3.8, 3.9, "3.10", 3.11, 3.12]
1817

minfraud/webservice.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,11 @@ def __init__(
5959
self._timeout = timeout
6060

6161
base_uri = f"https://{host}/minfraud/v2.0"
62-
self._score_uri = "/".join([base_uri, "score"])
63-
self._insights_uri = "/".join([base_uri, "insights"])
64-
self._factors_uri = "/".join([base_uri, "factors"])
65-
self._report_uri = "/".join([base_uri, "transactions", "report"])
62+
self._base_uri = base_uri
63+
self._score_uri = "/".join([self._base_uri, "score"])
64+
self._insights_uri = "/".join([self._base_uri, "insights"])
65+
self._factors_uri = "/".join([self._base_uri, "factors"])
66+
self._report_uri = "/".join([self._base_uri, "transactions", "report"])
6667

6768
def _handle_success(
6869
self,
@@ -397,7 +398,6 @@ async def _response_for(
397398
status = response.status
398399
content_type = response.content_type
399400
raw_body = await response.text()
400-
401401
if status != 200:
402402
raise self._exception_for_error(status, content_type, raw_body, uri)
403403
return self._handle_success(raw_body, uri, model_class)

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ classifiers = [
3535

3636
[project.optional-dependencies]
3737
test = [
38-
"mocket>=3.12.8",
38+
"pytest-httpserver>=1.0.10",
3939
]
4040

4141
[tool.setuptools.package-data]

setup.cfg

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ python =
1515

1616
[testenv:{py38,py39,py310,py311,py312}-test]
1717
deps =
18-
mocket
18+
pytest-httpserver
1919
pytest
2020

2121
commands = pytest tests
@@ -35,6 +35,8 @@ commands = flake8 minfraud
3535
[testenv:py312-mypy]
3636
deps =
3737
mypy
38+
pytest_httpserver
39+
pytest
3840
types-requests
3941
voluptuous-stubs
4042
commands = mypy minfraud tests

tests/test_webservice.py

Lines changed: 69 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
import os
44
from io import open
55
from 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

1111
from minfraud.errors import (
1212
HTTPError,
@@ -25,8 +25,24 @@
2525
class 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

182187
class 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

349361
class 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

Comments
 (0)