Projet

Général

Profil

0001-sms-send-SMS-asynchronously-21465.patch

Nicolas Roche, 25 mai 2020 16:09

Télécharger (9,41 ko)

Voir les différences:

Subject: [PATCH] sms: send SMS asynchronously (#21465)

 passerelle/base/models.py |  9 +++++++--
 tests/test_orange.py      | 22 ++++++++++++++-------
 tests/test_sms.py         | 40 ++++++++++++++++++++++++++++++---------
 tests/utils.py            |  4 +++-
 4 files changed, 56 insertions(+), 19 deletions(-)
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
        self.send_msg(**kwargs)
966 972
        SMSLog.objects.create(appname=self.get_connector_slug(), slug=self.slug)
967
        return result
968 973

  
969 974
    class Meta:
970 975
        abstract = True
971 976

  
972 977

  
973 978
@six.python_2_unicode_compatible
974 979
class SMSLog(models.Model):
975 980
    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
-