Skip to content

Commit 26c56f5

Browse files
Add QueryableFake gateway
1 parent 268c0d6 commit 26c56f5

File tree

3 files changed

+63
-1
lines changed

3 files changed

+63
-1
lines changed

docs/configuration.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ General Settings
1919
Twilio_.
2020
* ``'two_factor.gateways.fake.Fake'`` for development, recording tokens to the
2121
default logger.
22+
* ``'two_factor.gateways.fake.QueryableFake'`` for testing, recording tokens
23+
to the ``QueryableFake`` singleton.
2224

2325
``TWO_FACTOR_SMS_GATEWAY`` (default: ``None``)
2426
Which gateway to use for sending text messages. Should be set to a module or
@@ -28,6 +30,8 @@ General Settings
2830
Twilio_.
2931
* ``'two_factor.gateways.fake.Fake'`` for development, recording tokens to the
3032
default logger.
33+
* ``'two_factor.gateways.fake.QueryableFake'`` for testing, recording tokens
34+
to the ``QueryableFake`` singleton.
3135

3236
``LOGIN_URL``
3337
Should point to the login view provided by this application as described in
@@ -118,6 +122,10 @@ Fake Gateway
118122
------------
119123
.. autoclass:: two_factor.gateways.fake.Fake
120124

125+
QueryableFake Gateway
126+
------------
127+
.. autoclass:: two_factor.gateways.fake.QueryableFake
128+
121129
.. _LOGIN_URL: https://docs.djangoproject.com/en/dev/ref/settings/#login-url
122130
.. _LOGIN_REDIRECT_URL: https://docs.djangoproject.com/en/dev/ref/settings/#login-redirect-url
123131
.. _LOGOUT_REDIRECT_URL: https://docs.djangoproject.com/en/dev/ref/settings/#logout-redirect-url

tests/test_gateways.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from django.utils.six.moves.urllib.parse import urlencode
88
from phonenumber_field.phonenumber import PhoneNumber
99

10-
from two_factor.gateways.fake import Fake
10+
from two_factor.gateways.fake import Fake, QueryableFake
1111
from two_factor.gateways.twilio.gateway import Twilio
1212

1313
try:
@@ -117,3 +117,15 @@ def test_gateway(self, logger):
117117
fake.send_sms(device=Mock(number=PhoneNumber.from_string('+123')), token=code)
118118
logger.info.assert_called_with(
119119
'Fake SMS to %s: "Your token is: %s"', '+123', code)
120+
121+
122+
class QueryableFakeGatewayTest(TestCase):
123+
def test_gateway(self):
124+
fake = QueryableFake()
125+
126+
for code in ['654321', '87654321']:
127+
fake.make_call(device=Mock(number=PhoneNumber.from_string('+123')), token=code)
128+
self.assertEqual(fake.call_tokens['+123'].pop(), code)
129+
130+
fake.send_sms(device=Mock(number=PhoneNumber.from_string('+123')), token=code)
131+
self.assertEqual(fake.sms_tokens['+123'].pop(), code)

two_factor/gateways/fake.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import logging
2+
from collections import defaultdict
23

34
logger = logging.getLogger(__name__)
45

@@ -33,3 +34,44 @@ def make_call(device, token):
3334
@staticmethod
3435
def send_sms(device, token):
3536
logger.info('Fake SMS to %s: "Your token is: %s"', device.number.as_e164, token)
37+
38+
39+
class QueryableFake(object):
40+
"""A Fake gateway that can be queried.
41+
42+
For example, you may use this in your unit tests::
43+
44+
>>> from django.test import TestCase, override_settings
45+
>>> from django.contrib.auth import get_user_model
46+
>>> from django.conf import settings
47+
>>> from two_factor.gateways.fake import QueryableFake
48+
>>> from two_factor.models import PhoneDevice
49+
>>> from phonenumber_field.phonenumber import PhoneNumber
50+
>>>
51+
>>> class MyTestCase(TestCase):
52+
... @override_settings(
53+
... TWO_FACTOR_SMS_GATEWAY='two_factor.gateways.fake.QueryableFake',
54+
... )
55+
... def test_something(self):
56+
... user = get_user_model().objects.create(...)
57+
... PhoneDevice.objects.create(
58+
... user=user, name='default', method='sms',
59+
... number=PhoneNumber.from_string('+441234567890'),
60+
... )
61+
... self.client.post(settings.LOGIN_URL,
62+
... 'username': user.username,
63+
... 'password': 'password',
64+
... })
65+
... token = QueryableFake.sms_tokens['+441234567890'].pop()
66+
... self.client.post(settings.LOGIN_URL, {'token': token})
67+
"""
68+
sms_tokens = defaultdict(list)
69+
call_tokens = defaultdict(list)
70+
71+
@classmethod
72+
def make_call(cls, device, token):
73+
cls.call_tokens[str(device.number)].append(token)
74+
75+
@classmethod
76+
def send_sms(cls, device, token):
77+
cls.sms_tokens[str(device.number)].append(token)

0 commit comments

Comments
 (0)