Skip to content

Commit 5a1aad2

Browse files
tests(*): Use parametrize fixtures and refactor tests (#40)
Co-authored-by: Prashant Sharma <[email protected]> Co-authored-by: Sanyam Khurana <[email protected]>
1 parent 24052cf commit 5a1aad2

File tree

7 files changed

+280
-289
lines changed

7 files changed

+280
-289
lines changed

conftest.py

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,20 @@
66
"""
77

88
# Standard Library
9+
import copy
910
import functools
1011

11-
import django
12-
1312
# Third Party Stuff
1413
import pytest
14+
import django
1515
from django.conf import settings
1616

17+
from tests import test_settings
18+
19+
backends = {"twilio.TwilioBackend", "nexmo.NexmoBackend"}
20+
sandbox_backends = {"twilio.TwilioSandboxBackend", "nexmo.NexmoSandboxBackend"}
21+
all_backends = list(backends) + list(sandbox_backends)
22+
1723

1824
class PartialMethodCaller:
1925
def __init__(self, obj, **partial_params):
@@ -48,6 +54,20 @@ def json(self):
4854
return _Client()
4955

5056

57+
@pytest.fixture(params=all_backends)
58+
def backend(request):
59+
phone_verification_settings = copy.deepcopy(
60+
test_settings.DJANGO_SETTINGS.get("PHONE_VERIFICATION")
61+
)
62+
phone_verification_settings["BACKEND"] = f"phone_verify.backends.{request.param}"
63+
if (
64+
request.param == "nexmo.NexmoSandboxBackend"
65+
or request.param == "nexmo.NexmoBackend"
66+
):
67+
phone_verification_settings["OPTIONS"]["KEY"] = "fake"
68+
return phone_verification_settings
69+
70+
5171
def pytest_configure():
5272
from tests import test_settings
5373

phone_verify/backends/nexmo.py

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010

1111

1212
class NexmoBackend(BaseBackend):
13-
1413
def __init__(self, **options):
1514
super().__init__(**options)
1615

@@ -23,11 +22,7 @@ def __init__(self, **options):
2322
self.client = nexmo.Client(key=self._key, secret=self._secret)
2423

2524
def send_sms(self, number, message):
26-
self.client.send_message({
27-
'from': self._from,
28-
'to': number,
29-
'text': message,
30-
})
25+
self.client.send_message({"from": self._from, "to": number, "text": message})
3126

3227
def send_bulk_sms(self, numbers, message):
3328
for number in numbers:
@@ -47,11 +42,7 @@ def __init__(self, **options):
4742
self.client = nexmo.Client(key=self._key, secret=self._secret)
4843

4944
def send_sms(self, number, message):
50-
self.client.send_message({
51-
'from': self._from,
52-
'to': number,
53-
'text': message,
54-
})
45+
self.client.send_message({"from": self._from, "to": number, "text": message})
5546

5647
def send_bulk_sms(self, numbers, message):
5748
for number in numbers:

tests/test_api.py

Lines changed: 165 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@
77

88
import pytest
99
from django.apps import apps
10+
from django.test import override_settings
1011
from django.urls import reverse
11-
12-
from . import test_settings as settings
12+
from conftest import backends, sandbox_backends
13+
from .test_backends import _get_backend_cls
1314
from . import factories as f
1415

1516
pytestmark = pytest.mark.django_db
@@ -19,7 +20,7 @@
1920
SESSION_TOKEN = "phone-auth-session-token"
2021

2122

22-
def test_phone_registration_sends_message(client, mocker):
23+
def test_phone_registration_sends_message(client, mocker, backend):
2324
url = reverse("phone-register")
2425
phone_number = PHONE_NUMBER
2526
data = {"phone_number": phone_number}
@@ -38,132 +39,164 @@ def test_phone_registration_sends_message(client, mocker):
3839
)
3940

4041

41-
def test_security_code_session_token_verification_api(client):
42-
f.create_verification(
43-
security_code=SECURITY_CODE,
44-
phone_number=PHONE_NUMBER,
45-
session_token=SESSION_TOKEN,
46-
)
47-
url = reverse("phone-verify")
48-
data = {
49-
"phone_number": PHONE_NUMBER,
50-
"security_code": SECURITY_CODE,
51-
"session_token": SESSION_TOKEN,
52-
}
53-
response = client.json.post(url, data=data)
54-
assert response.status_code == 200
55-
assert response.data["message"] == "Security code is valid."
56-
57-
58-
def test_phone_verification_with_incomplete_payload(client):
59-
f.create_verification(
60-
security_code=SECURITY_CODE,
61-
phone_number=PHONE_NUMBER,
62-
session_token=SESSION_TOKEN,
63-
)
64-
url = reverse("phone-verify")
65-
data = {"phone_number": PHONE_NUMBER}
66-
response = client.json.post(url, data=data)
67-
assert response.status_code == 400
68-
response_data = json.loads(json.dumps(response.data))
69-
assert response_data["session_token"][0] == "This field is required."
70-
assert response_data["security_code"][0] == "This field is required."
71-
72-
data = {"security_code": SECURITY_CODE}
73-
response = client.json.post(url, data=data)
74-
assert response.status_code == 400
75-
response_data = json.loads(json.dumps(response.data))
76-
assert response_data["phone_number"][0] == "This field is required."
77-
78-
79-
def test_phone_verification_with_incorrect_payload(client):
80-
f.create_verification(
81-
security_code=SECURITY_CODE,
82-
phone_number=PHONE_NUMBER,
83-
session_token=SESSION_TOKEN,
84-
)
85-
url = reverse("phone-verify")
86-
# Payload with wrong session token
87-
data = {
88-
"phone_number": PHONE_NUMBER,
89-
"security_code": SECURITY_CODE,
90-
"session_token": "wrong-session-token",
91-
}
92-
response = client.json.post(url, data=data)
93-
response_data = json.loads(json.dumps(response.data))
94-
assert response.status_code == 400
95-
assert response_data["non_field_errors"][0] == "Session Token mis-match"
96-
97-
# Payload with wrong security code
98-
data = {
99-
"phone_number": PHONE_NUMBER,
100-
"security_code": "999999",
101-
"session_token": SESSION_TOKEN,
102-
}
103-
response = client.json.post(url, data=data)
104-
assert response.status_code == 400
105-
response_data = json.loads(json.dumps(response.data))
106-
assert response_data["non_field_errors"][0] == "Security code is not valid"
107-
108-
# Payload with incorrect phone_number
109-
data = {
110-
"phone_number": "+13478379632",
111-
"security_code": SECURITY_CODE,
112-
"session_token": SESSION_TOKEN,
113-
}
114-
response = client.json.post(url, data=data)
115-
assert response.status_code == 400
116-
response_data = json.loads(json.dumps(response.data))
117-
assert response_data["non_field_errors"][0] == "Security code is not valid"
118-
119-
120-
def test_check_security_code_expiry(client):
121-
f.create_verification(
122-
security_code=SECURITY_CODE,
123-
phone_number=PHONE_NUMBER,
124-
session_token=SESSION_TOKEN,
125-
)
126-
time.sleep(2)
127-
url = reverse("phone-verify")
128-
data = {
129-
"phone_number": PHONE_NUMBER,
130-
"security_code": SECURITY_CODE,
131-
"session_token": SESSION_TOKEN,
132-
}
133-
response = client.json.post(url, data=data)
134-
assert response.status_code == 400
135-
response_data = json.loads(json.dumps(response.data))
136-
assert response_data["non_field_errors"][0] == "Security code has expired"
137-
138-
139-
def test_verified_security_code(client):
140-
f.create_verification(
141-
security_code=SECURITY_CODE,
142-
phone_number=PHONE_NUMBER,
143-
session_token=SESSION_TOKEN,
144-
is_verified=True,
145-
)
146-
url = reverse("phone-verify")
147-
data = {
148-
"phone_number": PHONE_NUMBER,
149-
"security_code": SECURITY_CODE,
150-
"session_token": SESSION_TOKEN,
151-
}
152-
153-
# Security code verification is restricted to one time
154-
settings.DJANGO_SETTINGS["PHONE_VERIFICATION"][
155-
"VERIFY_SECURITY_CODE_ONLY_ONCE"
156-
] = True
157-
response = client.json.post(url, data=data)
158-
response_data = json.loads(json.dumps(response.data))
159-
assert response.status_code == 400
160-
assert response_data["non_field_errors"][0] == "Security code is already verified"
161-
162-
# Security code verification is not restricted to one time
163-
settings.DJANGO_SETTINGS["PHONE_VERIFICATION"][
164-
"VERIFY_SECURITY_CODE_ONLY_ONCE"
165-
] = False
166-
response = client.json.post(url, data=data)
167-
response_data = json.loads(json.dumps(response.data))
168-
assert response.status_code == 200
169-
assert response.data["message"] == "Security code is valid."
42+
def test_security_code_session_token_verification_api(client, backend):
43+
with override_settings(PHONE_VERIFICATION=backend):
44+
f.create_verification(
45+
security_code=SECURITY_CODE,
46+
phone_number=PHONE_NUMBER,
47+
session_token=SESSION_TOKEN,
48+
)
49+
url = reverse("phone-verify")
50+
data = {
51+
"phone_number": PHONE_NUMBER,
52+
"security_code": SECURITY_CODE,
53+
"session_token": SESSION_TOKEN,
54+
}
55+
response = client.json.post(url, data=data)
56+
assert response.status_code == 200
57+
assert response.data["message"] == "Security code is valid."
58+
59+
60+
def test_phone_verification_with_incomplete_payload(client, backend):
61+
with override_settings(PHONE_VERIFICATION=backend):
62+
f.create_verification(
63+
security_code=SECURITY_CODE,
64+
phone_number=PHONE_NUMBER,
65+
session_token=SESSION_TOKEN,
66+
)
67+
url = reverse("phone-verify")
68+
data = {"phone_number": PHONE_NUMBER}
69+
response = client.json.post(url, data=data)
70+
assert response.status_code == 400
71+
response_data = json.loads(json.dumps(response.data))
72+
assert response_data["session_token"][0] == "This field is required."
73+
assert response_data["security_code"][0] == "This field is required."
74+
75+
data = {"security_code": SECURITY_CODE}
76+
response = client.json.post(url, data=data)
77+
assert response.status_code == 400
78+
response_data = json.loads(json.dumps(response.data))
79+
assert response_data["phone_number"][0] == "This field is required."
80+
81+
82+
def test_phone_verification_with_incorrect_payload(client, backend):
83+
with override_settings(PHONE_VERIFICATION=backend):
84+
f.create_verification(
85+
security_code=SECURITY_CODE,
86+
phone_number=PHONE_NUMBER,
87+
session_token=SESSION_TOKEN,
88+
)
89+
url = reverse("phone-verify")
90+
# Payload with wrong session token
91+
data = {
92+
"phone_number": PHONE_NUMBER,
93+
"security_code": SECURITY_CODE,
94+
"session_token": "wrong-session-token",
95+
}
96+
response = client.json.post(url, data=data)
97+
response_data = json.loads(json.dumps(response.data))
98+
99+
backend_cls = _get_backend_cls(backend)
100+
101+
if backend_cls in backends:
102+
assert response.status_code == 400
103+
assert response_data["non_field_errors"][0] == "Session Token mis-match"
104+
elif backend_cls in sandbox_backends:
105+
# Sandbox Backend returns a 200 status code as it does not check the payload information
106+
assert response.status_code == 200
107+
108+
# Payload with wrong security code
109+
data = {
110+
"phone_number": PHONE_NUMBER,
111+
"security_code": "999999",
112+
"session_token": SESSION_TOKEN,
113+
}
114+
response = client.json.post(url, data=data)
115+
response_data = json.loads(json.dumps(response.data))
116+
if backend_cls in backends:
117+
assert response.status_code == 400
118+
assert response_data["non_field_errors"][0] == "Security code is not valid"
119+
elif backend_cls in sandbox_backends:
120+
# Sandbox Backend returns a 200 status code as it does not check the payload information
121+
assert response.status_code == 200
122+
123+
# Payload with incorrect phone_number
124+
data = {
125+
"phone_number": "+13478379632",
126+
"security_code": SECURITY_CODE,
127+
"session_token": SESSION_TOKEN,
128+
}
129+
response = client.json.post(url, data=data)
130+
response_data = json.loads(json.dumps(response.data))
131+
if backend_cls in backends:
132+
assert response.status_code == 400
133+
assert response_data["non_field_errors"][0] == "Security code is not valid"
134+
elif backend_cls in sandbox_backends:
135+
# Sandbox Backend returns a 200 status code as it does not check the payload information
136+
assert response.status_code == 200
137+
138+
139+
def test_check_security_code_expiry(client, backend):
140+
with override_settings(PHONE_VERIFICATION=backend):
141+
f.create_verification(
142+
security_code=SECURITY_CODE,
143+
phone_number=PHONE_NUMBER,
144+
session_token=SESSION_TOKEN,
145+
)
146+
time.sleep(2)
147+
url = reverse("phone-verify")
148+
data = {
149+
"phone_number": PHONE_NUMBER,
150+
"security_code": SECURITY_CODE,
151+
"session_token": SESSION_TOKEN,
152+
}
153+
response = client.json.post(url, data=data)
154+
response_data = json.loads(json.dumps(response.data))
155+
156+
backend_cls = _get_backend_cls(backend)
157+
158+
if backend_cls in backends:
159+
assert response.status_code == 400
160+
assert response_data["non_field_errors"][0] == "Security code has expired"
161+
elif backend_cls in sandbox_backends:
162+
# Sandbox Backend returns a 200 status code when verifying security code expiry
163+
assert response.status_code == 200
164+
165+
166+
def test_verified_security_code(client, backend):
167+
with override_settings(PHONE_VERIFICATION=backend):
168+
f.create_verification(
169+
security_code=SECURITY_CODE,
170+
phone_number=PHONE_NUMBER,
171+
session_token=SESSION_TOKEN,
172+
is_verified=True,
173+
)
174+
url = reverse("phone-verify")
175+
data = {
176+
"phone_number": PHONE_NUMBER,
177+
"security_code": SECURITY_CODE,
178+
"session_token": SESSION_TOKEN,
179+
}
180+
181+
backend_cls = _get_backend_cls(backend)
182+
183+
# Security code verification is restricted to one time
184+
backend["VERIFY_SECURITY_CODE_ONLY_ONCE"] = True
185+
response = client.json.post(url, data=data)
186+
response_data = json.loads(json.dumps(response.data))
187+
if backend_cls in backends:
188+
assert response.status_code == 400
189+
assert (
190+
response_data["non_field_errors"][0]
191+
== "Security code is already verified"
192+
)
193+
elif backend_cls in sandbox_backends:
194+
# Sandbox Backend returns a 200 status code when verifying security code
195+
assert response.status_code == 200
196+
197+
# Security code verification is not restricted to one time
198+
backend["VERIFY_SECURITY_CODE_ONLY_ONCE"] = False
199+
response = client.json.post(url, data=data)
200+
response_data = json.loads(json.dumps(response.data))
201+
assert response.status_code == 200
202+
assert response.data["message"] == "Security code is valid."

0 commit comments

Comments
 (0)