0001-sms-send-SMS-asynchronously-21465.patch
passerelle/base/models.py | ||
---|---|---|
957 | 957 |
assert isinstance(data['from'], six.text_type), 'from is not a string' |
958 | 958 |
assert all(map(lambda x: isinstance(x, six.text_type), data['to'])), \ |
959 | 959 |
'to is not a list of strings' |
960 | 960 |
except (ValueError, AssertionError) as e: |
961 | 961 |
raise APIError('Payload error: %s' % e) |
962 | 962 |
data['message'] = data['message'][:self.max_message_length] |
963 | 963 |
logging.info('sending SMS to %r from %r', data['to'], data['from']) |
964 | 964 |
stop = not bool('nostop' in request.GET) |
965 |
result = {'data': self.send_msg(data['message'], data['from'], data['to'], stop=stop)} |
|
965 |
self.add_job('send_job', |
|
966 |
text=data['message'], sender=data['from'], destinations=data['to'], |
|
967 |
stop=stop) |
|
968 |
return {'err': 0} |
|
969 | ||
970 |
def send_job(self, *args, **kwargs): |
|
971 |
try: |
|
972 |
self.send_msg(**kwargs) |
|
973 |
except APIError: |
|
974 |
raise # not recoverable |
|
966 | 975 |
SMSLog.objects.create(appname=self.get_connector_slug(), slug=self.slug) |
967 |
return result |
|
968 | 976 | |
969 | 977 |
class Meta: |
970 | 978 |
abstract = True |
971 | 979 | |
972 | 980 | |
973 | 981 |
@six.python_2_unicode_compatible |
974 | 982 |
class SMSLog(models.Model): |
975 | 983 |
timestamp = models.DateTimeField(auto_now_add=True) |
tests/test_orange.py | ||
---|---|---|
19 | 19 | |
20 | 20 |
import httmock |
21 | 21 |
import pytest |
22 | 22 | |
23 | 23 |
from django.contrib.contenttypes.models import ContentType |
24 | 24 |
from django.utils.encoding import force_text |
25 | 25 | |
26 | 26 |
from passerelle.apps.orange.models import OrangeSMSGateway, OrangeError |
27 |
from passerelle.base.models import ApiUser, AccessRight |
|
27 | ||
28 |
from passerelle.base.models import ApiUser, AccessRight, Job |
|
28 | 29 |
from passerelle.utils.jsonresponse import APIError |
29 | 30 | |
30 | 31 | |
31 | 32 |
NETLOC = 'contact-everyone.orange-business.com' |
32 | 33 |
JSON_HEADERS = {'content-type': 'application/json'} |
33 | 34 |
PAYLOAD = { |
34 | 35 |
'message': 'hello', |
35 | 36 |
'from': '+33699999999', |
... | ... | |
158 | 159 | |
159 | 160 |
with pytest.raises(OrangeError, match='Orange returned Invalid JSON content'): |
160 | 161 |
with httmock.HTTMock(mocked_response): |
161 | 162 |
orange.diffusion('my_token', 'gid2', PAYLOAD['to'], PAYLOAD['message']) |
162 | 163 | |
163 | 164 | |
164 | 165 |
def test_send_msg(app, connector): |
165 | 166 |
url = '/%s/%s/send/' % (connector.get_connector_slug(), connector.slug) |
166 |
with httmock.HTTMock(response_token_ok, response_group_ok, response_diffusion_ok):
|
|
167 |
resp = app.post_json(url, params=PAYLOAD, status=200)
|
|
167 |
assert Job.objects.count() == 0
|
|
168 |
resp = app.post_json(url, params=PAYLOAD, status=200) |
|
168 | 169 |
assert not resp.json['err'] |
169 |
assert resp.json['data']['status'] == "I'm ok" |
|
170 |
assert Job.objects.count() == 1 |
|
171 |
with httmock.HTTMock(response_token_ok, response_group_ok, response_diffusion_ok): |
|
172 |
connector.jobs() |
|
173 |
assert Job.objects.all()[0].status == 'completed' |
|
170 | 174 | |
171 | 175 |
# not 201 |
176 |
resp = app.post_json(url, params=PAYLOAD, status=200) |
|
177 |
assert not resp.json['err'] |
|
178 |
assert Job.objects.count() == 2 |
|
172 | 179 |
with httmock.HTTMock(response_token_ok, response_group_ok, response_500): |
173 |
resp = app.post_json(url, params=PAYLOAD, status=200) |
|
174 |
assert resp.json['err'] |
|
175 |
assert resp.json['err_desc'] == 'Orange fails to send SMS: 500, my_error' |
|
180 |
connector.jobs() |
|
181 |
job = Job.objects.all()[1] |
|
182 |
assert job.status == 'failed' |
|
183 |
assert 'Orange fails to send SMS: 500, my_error' in job.status_details['error_summary'] |
tests/test_sms.py | ||
---|---|---|
1 |
import isodate |
|
1 | 2 |
import mock |
2 | 3 |
import pytest |
4 |
from requests import RequestException |
|
3 | 5 | |
4 | 6 |
from django.contrib.contenttypes.models import ContentType |
5 | 7 | |
6 | 8 |
from passerelle.apps.ovh.models import OVHSMSGateway |
7 |
from passerelle.base.models import ApiUser, AccessRight, SMSResource, SMSLog |
|
9 |
from passerelle.base.models import ApiUser, AccessRight, Job, SMSResource, SMSLog
|
|
8 | 10 | |
9 | 11 |
from test_manager import login, admin_user |
10 | 12 | |
11 | 13 |
import utils |
12 | 14 | |
13 | 15 |
pytestmark = pytest.mark.django_db |
14 | 16 | |
15 | 17 |
klasses = SMSResource.__subclasses__() |
... | ... | |
39 | 41 |
# no access check |
40 | 42 |
AccessRight.objects.create(codename='can_send_messages', |
41 | 43 |
apiuser=api, |
42 | 44 |
resource_type=obj_type, |
43 | 45 |
resource_pk=c.pk) |
44 | 46 |
return c |
45 | 47 | |
46 | 48 | |
47 |
def test_connectors(app, connector): |
|
49 |
def test_connectors(app, connector, freezer):
|
|
48 | 50 |
path = '/%s/%s/send/' % (connector.get_connector_slug(), connector.slug) |
49 | 51 |
result = app.post_json(path, params={}) |
50 | 52 |
assert result.json['err'] == 1 |
51 | 53 |
assert result.json['err_desc'].startswith('Payload error: ') |
52 | 54 | |
53 | 55 |
payload = { |
54 | 56 |
'message': 'hello', |
55 | 57 |
'from': '+33699999999', |
56 | 58 |
'to': ['+33688888888', '+33677777777'], |
57 | 59 |
} |
58 |
for test_vector in getattr(connector, 'TEST_DEFAULTS', {}).get('test_vectors', []): |
|
60 |
test_vectors = getattr(connector, 'TEST_DEFAULTS', {}).get('test_vectors', []) |
|
61 |
total = len(test_vectors) |
|
62 |
nb_failed = 0 |
|
63 |
assert Job.objects.count() == 0 |
|
64 |
for test_vector in test_vectors: |
|
65 | ||
66 |
# register job |
|
67 |
freezer.move_to('2019-01-01 00:00:00') |
|
68 |
result = app.post_json(path, params=payload) |
|
69 |
assert result.json['err'] == 0 |
|
70 |
job_id = Job.objects.get(status='registered').id |
|
71 | ||
72 |
# perform job |
|
73 |
freezer.move_to('2019-01-01 01:00:03') |
|
59 | 74 |
with utils.mock_url( |
60 | 75 |
connector.URL, |
61 | 76 |
test_vector.get('response', ''), |
62 | 77 |
test_vector.get('status_code', 200)): |
63 | ||
64 |
result = app.post_json(path, params=payload) |
|
65 |
for key, value in test_vector['result'].items(): |
|
66 |
assert key in result.json |
|
67 |
assert result.json[key] == value |
|
78 |
connector.jobs() |
|
79 |
job = Job.objects.get(id=job_id) |
|
80 |
if job.status == 'failed': |
|
81 |
assert len(job.status_details['error_summary']) > 0 |
|
82 |
assert test_vector['result']['err_desc'] in job.status_details['error_summary'] |
|
83 |
nb_failed += 1 |
|
84 |
else: |
|
85 |
assert job.status == 'completed' |
|
86 |
assert Job.objects.count() == total |
|
87 |
assert SMSLog.objects.count() == total - nb_failed |
|
68 | 88 | |
69 | 89 | |
70 | 90 |
def test_manage_views(admin_user, app, connector): |
71 | 91 |
url = '/%s/%s/' % (connector.get_connector_slug(), connector.slug) |
72 | 92 |
resp = app.get(url) |
73 | 93 |
assert 'Endpoints' in resp.text |
74 | 94 |
assert not 'accessright/add' in resp.text |
75 | 95 |
app = login(app) |
... | ... | |
86 | 106 |
payload = { |
87 | 107 |
'message': message_above_limit, |
88 | 108 |
'from': '+33699999999', |
89 | 109 |
'to': ['+33688888888'], |
90 | 110 |
} |
91 | 111 |
with mock.patch.object(OVHSMSGateway, 'send_msg') as send_function: |
92 | 112 |
send_function.return_value = {} |
93 | 113 |
result = app.post_json(path, params=payload) |
94 |
assert send_function.call_args[0][0] == 'a' * connector.max_message_length |
|
114 |
connector.jobs() |
|
115 |
assert send_function.call_args[1]['text'] == 'a' * connector.max_message_length |
|
95 | 116 | |
96 | 117 | |
97 | 118 |
@pytest.mark.parametrize('connector', [OVHSMSGateway], indirect=True) |
98 | 119 |
def test_sms_log(app, connector): |
99 | 120 |
path = '/%s/%s/send/' % (connector.get_connector_slug(), connector.slug) |
100 | 121 |
assert not SMSLog.objects.filter(appname=connector.get_connector_slug(), slug=connector.slug).exists() |
101 | 122 | |
102 | 123 |
payload = { |
103 | 124 |
'message': 'plop', |
104 | 125 |
'from': '+33699999999', |
105 | 126 |
'to': ['+33688888888'], |
106 | 127 |
} |
107 | 128 |
with mock.patch.object(OVHSMSGateway, 'send_msg') as send_function: |
108 | 129 |
send_function.return_value = {} |
109 | 130 |
result = app.post_json(path, params=payload) |
131 |
connector.jobs() |
|
110 | 132 |
assert SMSLog.objects.filter(appname=connector.get_connector_slug(), slug=connector.slug).exists() |
tests/utils.py | ||
---|---|---|
27 | 27 | |
28 | 28 |
class FakedResponse(mock.Mock): |
29 | 29 |
headers = {} |
30 | 30 | |
31 | 31 |
def json(self): |
32 | 32 |
return json_loads(self.content) |
33 | 33 | |
34 | 34 | |
35 |
def mock_url(url=None, response='', status_code=200, headers=None): |
|
35 |
def mock_url(url=None, response='', status_code=200, headers=None, exception=None):
|
|
36 | 36 |
urlmatch_kwargs = {} |
37 | 37 |
if url: |
38 | 38 |
parsed = urlparse.urlparse(url) |
39 | 39 |
if parsed.netloc: |
40 | 40 |
urlmatch_kwargs['netloc'] = parsed.netloc |
41 | 41 |
if parsed.path: |
42 | 42 |
urlmatch_kwargs['path'] = parsed.path |
43 | 43 | |
44 | 44 |
if not isinstance(response, str): |
45 | 45 |
response = json.dumps(response) |
46 | 46 | |
47 | 47 |
@httmock.urlmatch(**urlmatch_kwargs) |
48 | 48 |
def mocked(url, request): |
49 |
if exception: |
|
50 |
raise exception |
|
49 | 51 |
return httmock.response(status_code, response, headers, request=request) |
50 | 52 |
return httmock.HTTMock(mocked) |
51 | 53 | |
52 | 54 | |
53 | 55 |
def make_resource(model_class, **kwargs): |
54 | 56 |
resource = model_class.objects.create(**kwargs) |
55 | 57 |
setup_access_rights(resource) |
56 | 58 |
return resource |
57 |
- |