File: test_vendor_sap.py

package info (click to toggle)
python-pyodata 1.11.2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,280 kB
  • sloc: python: 7,170; xml: 947; makefile: 67; sh: 15
file content (273 lines) | stat: -rw-r--r-- 8,823 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
"""PyOData Vendor SAP tests"""

import logging
from typing import NamedTuple, ByteString
import pytest
from pyodata.exceptions import PyODataException, HttpError
from pyodata.vendor import SAP
import responses
import requests
import json


class MockResponse(NamedTuple):
    content: ByteString


@pytest.fixture
def response_with_error():
    return MockResponse(
        b'{"error": { "message": { "value": "Gateway Error" } } }')

@pytest.fixture
def response_with_error_and_innererror():
    return MockResponse(
        b'{ "error": {\n\
              "message": { "value": "Gateway Error" },\n\
              "innererror": { "errordetails" : [\n\
                             { "message" : "Inner Error 1" },\n\
                             { "message" : "Inner Error 2" } ] } } }\n')


def test_parse_invalid_json():
    """Make sure an invalid JSON does not cause a disaster"""

    sap_error = SAP.BusinessGatewayError('Programmer message',
                                         MockResponse(b'random data'))

    assert str(sap_error) == 'Programmer message'
    assert not sap_error.errordetails


def test_parse_without_error():
    """Make sure a JSON without error member does not cause a disaster"""

    sap_error = SAP.BusinessGatewayError('Programmer message',
                                         MockResponse(b'{"random": "data"}'))

    assert str(sap_error) == 'Programmer message'
    assert not sap_error.errordetails


def test_parse_without_error_object():
    """Make sure a JSON without error member does not cause a disaster"""

    sap_error = SAP.BusinessGatewayError('Programmer message',
                                         MockResponse(b'{"error": "data"}'))

    assert str(sap_error) == 'Programmer message'
    assert not sap_error.errordetails


def test_parse_without_message():
    """Make sure a JSON without message member does not cause a disaster"""

    sap_error = SAP.BusinessGatewayError('Programmer message',
                                         MockResponse(b'{"error": { "data" : "foo" } }'))

    assert str(sap_error) == 'Programmer message'
    assert not sap_error.errordetails


def test_parse_without_message_object():
    """Make sure a JSON without message member does not cause a disaster"""

    sap_error = SAP.BusinessGatewayError('Programmer message',
                                         MockResponse(b'{"error": { "message" : "foo" } }'))

    assert str(sap_error) == 'Programmer message'
    assert not sap_error.errordetails


def test_parse_without_value():
    """Make sure a JSON without value member does not cause a disaster"""

    sap_error = SAP.BusinessGatewayError(
        'Programmer message',
        MockResponse(b'{"error": { "message" : { "foo" : "value" } } }'))

    assert str(sap_error) == 'Programmer message'
    assert not sap_error.errordetails


def test_parse_with_error(response_with_error):
    """Make sure a JSON without message member does not cause a disaster"""

    sap_error = SAP.BusinessGatewayError(
        'Programmer message',
        response_with_error)

    assert str(sap_error) == 'Gateway Error'
    assert not sap_error.errordetails


def test_parse_without_errordetails():
    """Make sure a JSON without errordetails member
       does not cause a disaster
    """

    sap_error = SAP.BusinessGatewayError(
        'Programmer message',
        MockResponse(b'{"error" : {\n\
            "innererror": { "message" : "value" } } }'))

    assert str(sap_error) == 'Programmer message'
    assert not sap_error.errordetails


def test_parse_without_array_errordetails():
    """Make sure a JSON without array errordetails member
       does not cause a disaster
    """

    sap_error = SAP.BusinessGatewayError(
        'Programmer message',
        MockResponse(b'{"error" : {\n\
            "innererror": { "errordetails" : "value" } } }'))

    assert str(sap_error) == 'Programmer message'
    assert not sap_error.errordetails


def test_parse_errordetails_no_object():
    """Make sure a JSON where error details are not objects
       does not cause a disaster
    """

    sap_error = SAP.BusinessGatewayError(
        'Programmer message',
        MockResponse(b'{"error" : {\n\
            "innererror": { "errordetails" : [ "foo", "bar" ] } } }'))

    assert str(sap_error) == 'Programmer message'
    assert not sap_error.errordetails


def test_parse_errordetails_no_message():
    """Make sure a JSON where error details misses the member message
       does not cause a disaster
    """

    sap_error = SAP.BusinessGatewayError(
        'Programmer message',
        MockResponse(b'{"error" : {\n\
            "innererror": { "errordetails" : [ { "foo" : "bar" } ] } } }'))

    assert str(sap_error) == 'Programmer message'
    assert [''] == sap_error.errordetails


def test_parse_with_error_and_innererror(response_with_error_and_innererror):
    """Make sure we parse out data correctly"""

    sap_error = SAP.BusinessGatewayError(
        'Programmer error',
        response_with_error_and_innererror)

    assert str(sap_error) == 'Gateway Error'
    assert sap_error.errordetails
    assert 2 == len(sap_error.errordetails)
    assert sap_error.errordetails[0] == 'Inner Error 1'
    assert sap_error.errordetails[1] == 'Inner Error 2'


def test_vendor_http_error(response_with_error):
    """Check that HttpError correctly returns
       an instance of BusinessGatewayError
    """

    logging.debug('First run')
    http_error = HttpError('Foo bar', response_with_error)
    assert isinstance(http_error, HttpError)
    assert str(http_error) == 'Foo bar'

    logging.debug('Second run')
    HttpError.VendorType = SAP.BusinessGatewayError
    sap_error = HttpError('Another foo bar', response_with_error)
    assert isinstance(sap_error, SAP.BusinessGatewayError)
    assert str(sap_error) == 'Gateway Error'


MOCK_AUTH_URL = "https://example.authentication.hana.ondemand.com"
MOCK_BTP_USER = "example_btp_user@gmail.com"
MOCK_BTP_PASSWORD = "example_password"
MOCK_KEY = {
    "uaa": {
        "url": MOCK_AUTH_URL,
        "clientid": "example-client-id",
        "clientsecret": "example-client-secret"
    }
}


@responses.activate
def test_add_btp_token_to_session_valid():
    """Valid username, password and key return a session with set token"""

    responses.add(
        responses.POST,
        MOCK_AUTH_URL + f'/oauth/token?grant_type=password&username={MOCK_BTP_USER}&password={MOCK_BTP_PASSWORD}',
        headers={'Content-type': 'application/json'},
        json={
            'access_token': 'valid_access_token',
            'token_type': 'bearer',
            'id_token': 'valid_id_token',
            'refresh_token': 'valid_refresh_token',
            'expires_in': 43199,
            'scope': 'openid uaa.user',
            'jti': 'valid_jti'
        },
        status=200)

    result = SAP.add_btp_token_to_session(requests.Session(), MOCK_KEY, MOCK_BTP_USER, MOCK_BTP_PASSWORD)
    assert result.headers['Authorization'] == 'Bearer valid_id_token'


@responses.activate
def test_add_btp_token_to_session_invalid_user():
    """Invalid username returns an HttpError"""

    invalid_user = "invalid@user.com"

    responses.add(
        responses.POST,
        MOCK_AUTH_URL + f'/oauth/token?grant_type=password&username={invalid_user}&password={MOCK_BTP_PASSWORD}',
        headers={'Content-type': 'application/json'},
        json={
            'error': 'unauthorized',
            'error_description': {
                'error': 'invalid_grant',
                'error_description': 'User authentication failed.'
            }
        },
        status=401)

    with pytest.raises(HttpError) as caught:
        SAP.add_btp_token_to_session(requests.Session(), MOCK_KEY, invalid_user, MOCK_BTP_PASSWORD)

    assert caught.value.response.status_code == 401
    assert json.loads(caught.value.response.text)['error_description']['error'] == 'invalid_grant'


@responses.activate
def test_add_btp_token_to_session_invalid_clientid():
    """Invalid clientid in key returns an HttpError"""

    invalid_key = MOCK_KEY.copy()
    invalid_key['uaa']['clientid'] = 'invalid-client-id'

    responses.add(
        responses.POST,
        MOCK_AUTH_URL + f'/oauth/token?grant_type=password&username={MOCK_BTP_USER}&password={MOCK_BTP_PASSWORD}',
        headers={'Content-type': 'application/json'},
        json={
            'error': 'unauthorized',
            'error_description': 'Bad credentials'
        },
        status=401)

    with pytest.raises(HttpError) as caught:
        SAP.add_btp_token_to_session(requests.Session(), invalid_key, MOCK_BTP_USER, MOCK_BTP_PASSWORD)

    assert caught.value.response.status_code == 401
    assert json.loads(caught.value.response.text)['error_description'] == 'Bad credentials'