blob: ace2b8059834b333dc4d3e6ff48e76a329883839 [file] [log] [blame]
Matthew Treinish9e26ca82016-02-23 11:43:20 -05001# Copyright 2013 IBM Corp.
2#
3# Licensed under the Apache License, Version 2.0 (the "License"); you may
4# not use this file except in compliance with the License. You may obtain
5# a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations
13# under the License.
14
15import copy
16import json
17
Ngo Quoc Cuong33710b32017-05-11 14:17:17 +070018import fixtures
Matthew Treinish9e26ca82016-02-23 11:43:20 -050019import jsonschema
Matthew Treinish9e26ca82016-02-23 11:43:20 -050020import six
21
Jordan Pittier00f25962016-03-18 17:10:07 +010022from tempest.lib.common import http
Matthew Treinish9e26ca82016-02-23 11:43:20 -050023from tempest.lib.common import rest_client
24from tempest.lib import exceptions
Matthew Treinishffad78a2016-04-16 14:39:52 -040025from tempest.tests import base
Matthew Treinish9e26ca82016-02-23 11:43:20 -050026from tempest.tests.lib import fake_auth_provider
27from tempest.tests.lib import fake_http
Jordan Pittier0e53b612016-03-03 14:23:17 +010028import tempest.tests.utils as utils
Matthew Treinish9e26ca82016-02-23 11:43:20 -050029
30
31class BaseRestClientTestClass(base.TestCase):
32
33 url = 'fake_endpoint'
34
35 def setUp(self):
36 super(BaseRestClientTestClass, self).setUp()
37 self.fake_auth_provider = fake_auth_provider.FakeAuthProvider()
38 self.rest_client = rest_client.RestClient(
39 self.fake_auth_provider, None, None)
Jordan Pittier0021c292016-03-29 21:33:34 +020040 self.patchobject(http.ClosingHttp, 'request', self.fake_http.request)
Ngo Quoc Cuong33710b32017-05-11 14:17:17 +070041 self.useFixture(fixtures.MockPatchObject(self.rest_client,
42 '_log_request'))
Matthew Treinish9e26ca82016-02-23 11:43:20 -050043
44
45class TestRestClientHTTPMethods(BaseRestClientTestClass):
46 def setUp(self):
47 self.fake_http = fake_http.fake_httplib2()
48 super(TestRestClientHTTPMethods, self).setUp()
Ngo Quoc Cuong33710b32017-05-11 14:17:17 +070049 self.useFixture(fixtures.MockPatchObject(self.rest_client,
50 '_error_checker'))
Matthew Treinish9e26ca82016-02-23 11:43:20 -050051
52 def test_post(self):
53 __, return_dict = self.rest_client.post(self.url, {}, {})
54 self.assertEqual('POST', return_dict['method'])
55
56 def test_get(self):
57 __, return_dict = self.rest_client.get(self.url)
58 self.assertEqual('GET', return_dict['method'])
59
60 def test_delete(self):
61 __, return_dict = self.rest_client.delete(self.url)
62 self.assertEqual('DELETE', return_dict['method'])
63
64 def test_patch(self):
65 __, return_dict = self.rest_client.patch(self.url, {}, {})
66 self.assertEqual('PATCH', return_dict['method'])
67
68 def test_put(self):
69 __, return_dict = self.rest_client.put(self.url, {}, {})
70 self.assertEqual('PUT', return_dict['method'])
71
72 def test_head(self):
Ngo Quoc Cuong33710b32017-05-11 14:17:17 +070073 self.useFixture(fixtures.MockPatchObject(self.rest_client,
74 'response_checker'))
Matthew Treinish9e26ca82016-02-23 11:43:20 -050075 __, return_dict = self.rest_client.head(self.url)
76 self.assertEqual('HEAD', return_dict['method'])
77
78 def test_copy(self):
79 __, return_dict = self.rest_client.copy(self.url)
80 self.assertEqual('COPY', return_dict['method'])
81
82
83class TestRestClientNotFoundHandling(BaseRestClientTestClass):
84 def setUp(self):
85 self.fake_http = fake_http.fake_httplib2(404)
86 super(TestRestClientNotFoundHandling, self).setUp()
87
88 def test_post(self):
89 self.assertRaises(exceptions.NotFound, self.rest_client.post,
90 self.url, {}, {})
91
92
93class TestRestClientHeadersJSON(TestRestClientHTTPMethods):
Matthew Treinish9e26ca82016-02-23 11:43:20 -050094
95 def _verify_headers(self, resp):
Matthew Treinish9e26ca82016-02-23 11:43:20 -050096 resp = dict((k.lower(), v) for k, v in six.iteritems(resp))
97 self.assertEqual(self.header_value, resp['accept'])
98 self.assertEqual(self.header_value, resp['content-type'])
99
100 def setUp(self):
101 super(TestRestClientHeadersJSON, self).setUp()
Masayuki Igawa189b92f2017-04-24 18:57:17 +0900102 self.header_value = 'application/json'
Matthew Treinish9e26ca82016-02-23 11:43:20 -0500103
104 def test_post(self):
105 resp, __ = self.rest_client.post(self.url, {})
106 self._verify_headers(resp)
107
108 def test_get(self):
109 resp, __ = self.rest_client.get(self.url)
110 self._verify_headers(resp)
111
112 def test_delete(self):
113 resp, __ = self.rest_client.delete(self.url)
114 self._verify_headers(resp)
115
116 def test_patch(self):
117 resp, __ = self.rest_client.patch(self.url, {})
118 self._verify_headers(resp)
119
120 def test_put(self):
121 resp, __ = self.rest_client.put(self.url, {})
122 self._verify_headers(resp)
123
124 def test_head(self):
Ngo Quoc Cuong33710b32017-05-11 14:17:17 +0700125 self.useFixture(fixtures.MockPatchObject(self.rest_client,
126 'response_checker'))
Matthew Treinish9e26ca82016-02-23 11:43:20 -0500127 resp, __ = self.rest_client.head(self.url)
128 self._verify_headers(resp)
129
130 def test_copy(self):
131 resp, __ = self.rest_client.copy(self.url)
132 self._verify_headers(resp)
133
134
135class TestRestClientUpdateHeaders(BaseRestClientTestClass):
136 def setUp(self):
137 self.fake_http = fake_http.fake_httplib2()
138 super(TestRestClientUpdateHeaders, self).setUp()
Ngo Quoc Cuong33710b32017-05-11 14:17:17 +0700139 self.useFixture(fixtures.MockPatchObject(self.rest_client,
140 '_error_checker'))
Matthew Treinish9e26ca82016-02-23 11:43:20 -0500141 self.headers = {'X-Configuration-Session': 'session_id'}
142
143 def test_post_update_headers(self):
144 __, return_dict = self.rest_client.post(self.url, {},
145 extra_headers=True,
146 headers=self.headers)
147
148 self.assertDictContainsSubset(
149 {'X-Configuration-Session': 'session_id',
150 'Content-Type': 'application/json',
151 'Accept': 'application/json'},
152 return_dict['headers']
153 )
154
155 def test_get_update_headers(self):
156 __, return_dict = self.rest_client.get(self.url,
157 extra_headers=True,
158 headers=self.headers)
159
160 self.assertDictContainsSubset(
161 {'X-Configuration-Session': 'session_id',
162 'Content-Type': 'application/json',
163 'Accept': 'application/json'},
164 return_dict['headers']
165 )
166
167 def test_delete_update_headers(self):
168 __, return_dict = self.rest_client.delete(self.url,
169 extra_headers=True,
170 headers=self.headers)
171
172 self.assertDictContainsSubset(
173 {'X-Configuration-Session': 'session_id',
174 'Content-Type': 'application/json',
175 'Accept': 'application/json'},
176 return_dict['headers']
177 )
178
179 def test_patch_update_headers(self):
180 __, return_dict = self.rest_client.patch(self.url, {},
181 extra_headers=True,
182 headers=self.headers)
183
184 self.assertDictContainsSubset(
185 {'X-Configuration-Session': 'session_id',
186 'Content-Type': 'application/json',
187 'Accept': 'application/json'},
188 return_dict['headers']
189 )
190
191 def test_put_update_headers(self):
192 __, return_dict = self.rest_client.put(self.url, {},
193 extra_headers=True,
194 headers=self.headers)
195
196 self.assertDictContainsSubset(
197 {'X-Configuration-Session': 'session_id',
198 'Content-Type': 'application/json',
199 'Accept': 'application/json'},
200 return_dict['headers']
201 )
202
203 def test_head_update_headers(self):
Ngo Quoc Cuong33710b32017-05-11 14:17:17 +0700204 self.useFixture(fixtures.MockPatchObject(self.rest_client,
205 'response_checker'))
Matthew Treinish9e26ca82016-02-23 11:43:20 -0500206
207 __, return_dict = self.rest_client.head(self.url,
208 extra_headers=True,
209 headers=self.headers)
210
211 self.assertDictContainsSubset(
212 {'X-Configuration-Session': 'session_id',
213 'Content-Type': 'application/json',
214 'Accept': 'application/json'},
215 return_dict['headers']
216 )
217
218 def test_copy_update_headers(self):
219 __, return_dict = self.rest_client.copy(self.url,
220 extra_headers=True,
221 headers=self.headers)
222
223 self.assertDictContainsSubset(
224 {'X-Configuration-Session': 'session_id',
225 'Content-Type': 'application/json',
226 'Accept': 'application/json'},
227 return_dict['headers']
228 )
229
230
231class TestRestClientParseRespJSON(BaseRestClientTestClass):
232 TYPE = "json"
233
234 keys = ["fake_key1", "fake_key2"]
235 values = ["fake_value1", "fake_value2"]
236 item_expected = dict((key, value) for (key, value) in zip(keys, values))
237 list_expected = {"body_list": [
238 {keys[0]: values[0]},
239 {keys[1]: values[1]},
240 ]}
241 dict_expected = {"body_dict": {
242 keys[0]: values[0],
243 keys[1]: values[1],
244 }}
245 null_dict = {}
246
247 def setUp(self):
248 self.fake_http = fake_http.fake_httplib2()
249 super(TestRestClientParseRespJSON, self).setUp()
250 self.rest_client.TYPE = self.TYPE
251
252 def test_parse_resp_body_item(self):
253 body = self.rest_client._parse_resp(json.dumps(self.item_expected))
254 self.assertEqual(self.item_expected, body)
255
256 def test_parse_resp_body_list(self):
257 body = self.rest_client._parse_resp(json.dumps(self.list_expected))
258 self.assertEqual(self.list_expected["body_list"], body)
259
260 def test_parse_resp_body_dict(self):
261 body = self.rest_client._parse_resp(json.dumps(self.dict_expected))
262 self.assertEqual(self.dict_expected["body_dict"], body)
263
264 def test_parse_resp_two_top_keys(self):
265 dict_two_keys = self.dict_expected.copy()
266 dict_two_keys.update({"second_key": ""})
267 body = self.rest_client._parse_resp(json.dumps(dict_two_keys))
268 self.assertEqual(dict_two_keys, body)
269
270 def test_parse_resp_one_top_key_without_list_or_dict(self):
271 data = {"one_top_key": "not_list_or_dict_value"}
272 body = self.rest_client._parse_resp(json.dumps(data))
273 self.assertEqual(data, body)
274
275 def test_parse_nullable_dict(self):
276 body = self.rest_client._parse_resp(json.dumps(self.null_dict))
277 self.assertEqual(self.null_dict, body)
278
279
280class TestRestClientErrorCheckerJSON(base.TestCase):
281 c_type = "application/json"
282
283 def set_data(self, r_code, enc=None, r_body=None, absolute_limit=True):
284 if enc is None:
285 enc = self.c_type
286 resp_dict = {'status': r_code, 'content-type': enc}
287 resp_body = {'resp_body': 'fake_resp_body'}
288
289 if absolute_limit is False:
290 resp_dict.update({'retry-after': 120})
291 resp_body.update({'overLimit': {'message': 'fake_message'}})
Jordan Pittier00f25962016-03-18 17:10:07 +0100292 resp = fake_http.fake_http_response(headers=resp_dict,
293 status=int(r_code),
294 body=json.dumps(resp_body))
Matthew Treinish9e26ca82016-02-23 11:43:20 -0500295 data = {
Matthew Treinish9e26ca82016-02-23 11:43:20 -0500296 "resp": resp,
297 "resp_body": json.dumps(resp_body)
298 }
299 if r_body is not None:
300 data.update({"resp_body": r_body})
301 return data
302
303 def setUp(self):
304 super(TestRestClientErrorCheckerJSON, self).setUp()
305 self.rest_client = rest_client.RestClient(
306 fake_auth_provider.FakeAuthProvider(), None, None)
307
308 def test_response_less_than_400(self):
309 self.rest_client._error_checker(**self.set_data("399"))
310
311 def _test_error_checker(self, exception_type, data):
312 e = self.assertRaises(exception_type,
313 self.rest_client._error_checker,
314 **data)
315 self.assertEqual(e.resp, data['resp'])
316 self.assertTrue(hasattr(e, 'resp_body'))
317 return e
318
319 def test_response_400(self):
320 self._test_error_checker(exceptions.BadRequest, self.set_data("400"))
321
322 def test_response_401(self):
323 self._test_error_checker(exceptions.Unauthorized, self.set_data("401"))
324
325 def test_response_403(self):
326 self._test_error_checker(exceptions.Forbidden, self.set_data("403"))
327
328 def test_response_404(self):
329 self._test_error_checker(exceptions.NotFound, self.set_data("404"))
330
331 def test_response_409(self):
332 self._test_error_checker(exceptions.Conflict, self.set_data("409"))
333
334 def test_response_410(self):
335 self._test_error_checker(exceptions.Gone, self.set_data("410"))
336
Kevin Bentona82bc862017-02-13 01:16:13 -0800337 def test_response_412(self):
338 self._test_error_checker(exceptions.PreconditionFailed,
339 self.set_data("412"))
340
Matthew Treinish9e26ca82016-02-23 11:43:20 -0500341 def test_response_413(self):
342 self._test_error_checker(exceptions.OverLimit, self.set_data("413"))
343
344 def test_response_413_without_absolute_limit(self):
345 self._test_error_checker(exceptions.RateLimitExceeded,
346 self.set_data("413", absolute_limit=False))
347
348 def test_response_415(self):
349 self._test_error_checker(exceptions.InvalidContentType,
350 self.set_data("415"))
351
352 def test_response_422(self):
353 self._test_error_checker(exceptions.UnprocessableEntity,
354 self.set_data("422"))
355
356 def test_response_500_with_text(self):
357 # _parse_resp is expected to return 'str'
358 self._test_error_checker(exceptions.ServerFault, self.set_data("500"))
359
360 def test_response_501_with_text(self):
361 self._test_error_checker(exceptions.NotImplemented,
362 self.set_data("501"))
363
364 def test_response_400_with_dict(self):
365 r_body = '{"resp_body": {"err": "fake_resp_body"}}'
366 e = self._test_error_checker(exceptions.BadRequest,
367 self.set_data("400", r_body=r_body))
368
369 if self.c_type == 'application/json':
370 expected = {"err": "fake_resp_body"}
371 else:
372 expected = r_body
373 self.assertEqual(expected, e.resp_body)
374
375 def test_response_401_with_dict(self):
376 r_body = '{"resp_body": {"err": "fake_resp_body"}}'
377 e = self._test_error_checker(exceptions.Unauthorized,
378 self.set_data("401", r_body=r_body))
379
380 if self.c_type == 'application/json':
381 expected = {"err": "fake_resp_body"}
382 else:
383 expected = r_body
384 self.assertEqual(expected, e.resp_body)
385
386 def test_response_403_with_dict(self):
387 r_body = '{"resp_body": {"err": "fake_resp_body"}}'
388 e = self._test_error_checker(exceptions.Forbidden,
389 self.set_data("403", r_body=r_body))
390
391 if self.c_type == 'application/json':
392 expected = {"err": "fake_resp_body"}
393 else:
394 expected = r_body
395 self.assertEqual(expected, e.resp_body)
396
397 def test_response_404_with_dict(self):
398 r_body = '{"resp_body": {"err": "fake_resp_body"}}'
399 e = self._test_error_checker(exceptions.NotFound,
400 self.set_data("404", r_body=r_body))
401
402 if self.c_type == 'application/json':
403 expected = {"err": "fake_resp_body"}
404 else:
405 expected = r_body
406 self.assertEqual(expected, e.resp_body)
407
408 def test_response_404_with_invalid_dict(self):
409 r_body = '{"foo": "bar"]'
410 e = self._test_error_checker(exceptions.NotFound,
411 self.set_data("404", r_body=r_body))
412
413 expected = r_body
414 self.assertEqual(expected, e.resp_body)
415
416 def test_response_410_with_dict(self):
417 r_body = '{"resp_body": {"err": "fake_resp_body"}}'
418 e = self._test_error_checker(exceptions.Gone,
419 self.set_data("410", r_body=r_body))
420
421 if self.c_type == 'application/json':
422 expected = {"err": "fake_resp_body"}
423 else:
424 expected = r_body
425 self.assertEqual(expected, e.resp_body)
426
427 def test_response_410_with_invalid_dict(self):
428 r_body = '{"foo": "bar"]'
429 e = self._test_error_checker(exceptions.Gone,
430 self.set_data("410", r_body=r_body))
431
432 expected = r_body
433 self.assertEqual(expected, e.resp_body)
434
435 def test_response_409_with_dict(self):
436 r_body = '{"resp_body": {"err": "fake_resp_body"}}'
437 e = self._test_error_checker(exceptions.Conflict,
438 self.set_data("409", r_body=r_body))
439
440 if self.c_type == 'application/json':
441 expected = {"err": "fake_resp_body"}
442 else:
443 expected = r_body
444 self.assertEqual(expected, e.resp_body)
445
446 def test_response_500_with_dict(self):
447 r_body = '{"resp_body": {"err": "fake_resp_body"}}'
448 e = self._test_error_checker(exceptions.ServerFault,
449 self.set_data("500", r_body=r_body))
450
451 if self.c_type == 'application/json':
452 expected = {"err": "fake_resp_body"}
453 else:
454 expected = r_body
455 self.assertEqual(expected, e.resp_body)
456
457 def test_response_501_with_dict(self):
458 r_body = '{"resp_body": {"err": "fake_resp_body"}}'
459 self._test_error_checker(exceptions.NotImplemented,
460 self.set_data("501", r_body=r_body))
461
462 def test_response_bigger_than_400(self):
463 # Any response code, that bigger than 400, and not in
Kevin Bentona82bc862017-02-13 01:16:13 -0800464 # (401, 403, 404, 409, 412, 413, 422, 500, 501)
Matthew Treinish9e26ca82016-02-23 11:43:20 -0500465 self._test_error_checker(exceptions.UnexpectedResponseCode,
466 self.set_data("402"))
467
468
469class TestRestClientErrorCheckerTEXT(TestRestClientErrorCheckerJSON):
470 c_type = "text/plain"
471
472 def test_fake_content_type(self):
473 # This test is required only in one exemplar
474 # Any response code, that bigger than 400, and not in
475 # (401, 403, 404, 409, 413, 422, 500, 501)
476 self._test_error_checker(exceptions.UnexpectedContentType,
477 self.set_data("405", enc="fake_enc"))
478
479 def test_response_413_without_absolute_limit(self):
480 # Skip this test because rest_client cannot get overLimit message
481 # from text body.
482 pass
483
484
485class TestRestClientUtils(BaseRestClientTestClass):
486
487 def _is_resource_deleted(self, resource_id):
488 if not isinstance(self.retry_pass, int):
489 return False
490 if self.retry_count >= self.retry_pass:
491 return True
492 self.retry_count = self.retry_count + 1
493 return False
494
495 def setUp(self):
496 self.fake_http = fake_http.fake_httplib2()
497 super(TestRestClientUtils, self).setUp()
498 self.retry_count = 0
499 self.retry_pass = None
500 self.original_deleted_method = self.rest_client.is_resource_deleted
501 self.rest_client.is_resource_deleted = self._is_resource_deleted
502
503 def test_wait_for_resource_deletion(self):
504 self.retry_pass = 2
505 # Ensure timeout long enough for loop execution to hit retry count
506 self.rest_client.build_timeout = 500
507 sleep_mock = self.patch('time.sleep')
508 self.rest_client.wait_for_resource_deletion('1234')
509 self.assertEqual(len(sleep_mock.mock_calls), 2)
510
511 def test_wait_for_resource_deletion_not_deleted(self):
512 self.patch('time.sleep')
513 # Set timeout to be very quick to force exception faster
Jordan Pittier0e53b612016-03-03 14:23:17 +0100514 timeout = 1
515 self.rest_client.build_timeout = timeout
516
517 time_mock = self.patch('time.time')
518 time_mock.side_effect = utils.generate_timeout_series(timeout)
519
Matthew Treinish9e26ca82016-02-23 11:43:20 -0500520 self.assertRaises(exceptions.TimeoutException,
521 self.rest_client.wait_for_resource_deletion,
522 '1234')
523
Jordan Pittier0e53b612016-03-03 14:23:17 +0100524 # time.time() should be called twice, first to start the timer
525 # and then to compute the timedelta
526 self.assertEqual(2, time_mock.call_count)
527
Matthew Treinish9e26ca82016-02-23 11:43:20 -0500528 def test_wait_for_deletion_with_unimplemented_deleted_method(self):
529 self.rest_client.is_resource_deleted = self.original_deleted_method
530 self.assertRaises(NotImplementedError,
531 self.rest_client.wait_for_resource_deletion,
532 '1234')
533
534 def test_get_versions(self):
535 self.rest_client._parse_resp = lambda x: [{'id': 'v1'}, {'id': 'v2'}]
536 actual_resp, actual_versions = self.rest_client.get_versions()
537 self.assertEqual(['v1', 'v2'], list(actual_versions))
538
539 def test__str__(self):
540 def get_token():
541 return "deadbeef"
542
543 self.fake_auth_provider.get_token = get_token
544 self.assertIsNotNone(str(self.rest_client))
545
546
Paul Glass119565a2016-04-06 11:41:42 -0500547class TestRateLimiting(BaseRestClientTestClass):
548
549 def setUp(self):
550 self.fake_http = fake_http.fake_httplib2()
551 super(TestRateLimiting, self).setUp()
552
553 def test__get_retry_after_delay_with_integer(self):
554 resp = {'retry-after': '123'}
555 self.assertEqual(123, self.rest_client._get_retry_after_delay(resp))
556
557 def test__get_retry_after_delay_with_http_date(self):
558 resp = {
559 'date': 'Mon, 4 Apr 2016 21:56:23 GMT',
560 'retry-after': 'Mon, 4 Apr 2016 21:58:26 GMT',
561 }
562 self.assertEqual(123, self.rest_client._get_retry_after_delay(resp))
563
564 def test__get_retry_after_delay_of_zero_with_integer(self):
565 resp = {'retry-after': '0'}
566 self.assertEqual(1, self.rest_client._get_retry_after_delay(resp))
567
568 def test__get_retry_after_delay_of_zero_with_http_date(self):
569 resp = {
570 'date': 'Mon, 4 Apr 2016 21:56:23 GMT',
571 'retry-after': 'Mon, 4 Apr 2016 21:56:23 GMT',
572 }
573 self.assertEqual(1, self.rest_client._get_retry_after_delay(resp))
574
575 def test__get_retry_after_delay_with_missing_date_header(self):
576 resp = {
577 'retry-after': 'Mon, 4 Apr 2016 21:58:26 GMT',
578 }
579 self.assertRaises(ValueError, self.rest_client._get_retry_after_delay,
580 resp)
581
582 def test__get_retry_after_delay_with_invalid_http_date(self):
583 resp = {
584 'retry-after': 'Mon, 4 AAA 2016 21:58:26 GMT',
585 'date': 'Mon, 4 Apr 2016 21:56:23 GMT',
586 }
587 self.assertRaises(ValueError, self.rest_client._get_retry_after_delay,
588 resp)
589
590 def test__get_retry_after_delay_with_missing_retry_after_header(self):
591 self.assertRaises(ValueError, self.rest_client._get_retry_after_delay,
592 {})
593
594 def test_is_absolute_limit_gives_false_with_retry_after(self):
595 resp = {'retry-after': 123}
596
597 # is_absolute_limit() requires the overLimit body to be unwrapped
598 resp_body = self.rest_client._parse_resp("""{
599 "overLimit": {
600 "message": ""
601 }
602 }""")
603 self.assertFalse(self.rest_client.is_absolute_limit(resp, resp_body))
604
605
Matthew Treinish9e26ca82016-02-23 11:43:20 -0500606class TestProperties(BaseRestClientTestClass):
607
608 def setUp(self):
609 self.fake_http = fake_http.fake_httplib2()
610 super(TestProperties, self).setUp()
611 creds_dict = {
612 'username': 'test-user',
613 'user_id': 'test-user_id',
614 'tenant_name': 'test-tenant_name',
615 'tenant_id': 'test-tenant_id',
616 'password': 'test-password'
617 }
618 self.rest_client = rest_client.RestClient(
619 fake_auth_provider.FakeAuthProvider(creds_dict=creds_dict),
620 None, None)
621
622 def test_properties(self):
623 self.assertEqual('test-user', self.rest_client.user)
624 self.assertEqual('test-user_id', self.rest_client.user_id)
625 self.assertEqual('test-tenant_name', self.rest_client.tenant_name)
626 self.assertEqual('test-tenant_id', self.rest_client.tenant_id)
627 self.assertEqual('test-password', self.rest_client.password)
628
629 self.rest_client.api_version = 'v1'
630 expected = {'api_version': 'v1',
631 'endpoint_type': 'publicURL',
632 'region': None,
Eric Wehrmeister54c7bd42016-02-24 11:11:07 -0600633 'name': None,
Matthew Treinish9e26ca82016-02-23 11:43:20 -0500634 'service': None,
635 'skip_path': True}
636 self.rest_client.skip_path()
637 self.assertEqual(expected, self.rest_client.filters)
638
639 self.rest_client.reset_path()
640 self.rest_client.api_version = 'v1'
641 expected = {'api_version': 'v1',
642 'endpoint_type': 'publicURL',
643 'region': None,
Eric Wehrmeister54c7bd42016-02-24 11:11:07 -0600644 'name': None,
Matthew Treinish9e26ca82016-02-23 11:43:20 -0500645 'service': None}
646 self.assertEqual(expected, self.rest_client.filters)
647
648
649class TestExpectedSuccess(BaseRestClientTestClass):
650
651 def setUp(self):
652 self.fake_http = fake_http.fake_httplib2()
653 super(TestExpectedSuccess, self).setUp()
654
655 def test_expected_succes_int_match(self):
656 expected_code = 202
657 read_code = 202
658 resp = self.rest_client.expected_success(expected_code, read_code)
659 # Assert None resp on success
660 self.assertFalse(resp)
661
662 def test_expected_succes_int_no_match(self):
663 expected_code = 204
664 read_code = 202
665 self.assertRaises(exceptions.InvalidHttpSuccessCode,
666 self.rest_client.expected_success,
667 expected_code, read_code)
668
669 def test_expected_succes_list_match(self):
670 expected_code = [202, 204]
671 read_code = 202
672 resp = self.rest_client.expected_success(expected_code, read_code)
673 # Assert None resp on success
674 self.assertFalse(resp)
675
676 def test_expected_succes_list_no_match(self):
677 expected_code = [202, 204]
678 read_code = 200
679 self.assertRaises(exceptions.InvalidHttpSuccessCode,
680 self.rest_client.expected_success,
681 expected_code, read_code)
682
683 def test_non_success_expected_int(self):
684 expected_code = 404
685 read_code = 202
686 self.assertRaises(AssertionError, self.rest_client.expected_success,
687 expected_code, read_code)
688
689 def test_non_success_expected_list(self):
690 expected_code = [404, 202]
691 read_code = 202
692 self.assertRaises(AssertionError, self.rest_client.expected_success,
693 expected_code, read_code)
694
ghanshyamc3074202016-04-18 15:20:45 +0900695 def test_non_success_read_code_as_string(self):
696 expected_code = 202
697 read_code = '202'
698 self.assertRaises(TypeError, self.rest_client.expected_success,
699 expected_code, read_code)
700
701 def test_non_success_read_code_as_list(self):
702 expected_code = 202
703 read_code = [202]
704 self.assertRaises(TypeError, self.rest_client.expected_success,
705 expected_code, read_code)
706
707 def test_non_success_expected_code_as_non_int(self):
708 expected_code = ['201', 202]
709 read_code = 202
710 self.assertRaises(AssertionError, self.rest_client.expected_success,
711 expected_code, read_code)
712
Matthew Treinish9e26ca82016-02-23 11:43:20 -0500713
714class TestResponseBody(base.TestCase):
715
716 def test_str(self):
717 response = {'status': 200}
718 body = {'key1': 'value1'}
719 actual = rest_client.ResponseBody(response, body)
720 self.assertEqual("response: %s\nBody: %s" % (response, body),
721 str(actual))
722
723
724class TestResponseBodyData(base.TestCase):
725
726 def test_str(self):
727 response = {'status': 200}
728 data = 'data1'
729 actual = rest_client.ResponseBodyData(response, data)
730 self.assertEqual("response: %s\nBody: %s" % (response, data),
731 str(actual))
732
733
734class TestResponseBodyList(base.TestCase):
735
736 def test_str(self):
737 response = {'status': 200}
738 body = ['value1', 'value2', 'value3']
739 actual = rest_client.ResponseBodyList(response, body)
740 self.assertEqual("response: %s\nBody: %s" % (response, body),
741 str(actual))
742
743
744class TestJSONSchemaValidationBase(base.TestCase):
745
746 class Response(dict):
747
748 def __getattr__(self, attr):
749 return self[attr]
750
751 def __setattr__(self, attr, value):
752 self[attr] = value
753
754 def setUp(self):
755 super(TestJSONSchemaValidationBase, self).setUp()
756 self.fake_auth_provider = fake_auth_provider.FakeAuthProvider()
757 self.rest_client = rest_client.RestClient(
758 self.fake_auth_provider, None, None)
759
760 def _test_validate_pass(self, schema, resp_body, status=200):
761 resp = self.Response()
762 resp.status = status
763 self.rest_client.validate_response(schema, resp, resp_body)
764
765 def _test_validate_fail(self, schema, resp_body, status=200,
766 error_msg="HTTP response body is invalid"):
767 resp = self.Response()
768 resp.status = status
769 ex = self.assertRaises(exceptions.InvalidHTTPResponseBody,
770 self.rest_client.validate_response,
771 schema, resp, resp_body)
772 self.assertIn(error_msg, ex._error_string)
773
774
775class TestRestClientJSONSchemaValidation(TestJSONSchemaValidationBase):
776
777 schema = {
778 'status_code': [200],
779 'response_body': {
780 'type': 'object',
781 'properties': {
782 'foo': {
783 'type': 'integer',
784 },
785 },
786 'required': ['foo']
787 }
788 }
789
790 def test_validate_pass_with_http_success_code(self):
791 body = {'foo': 12}
792 self._test_validate_pass(self.schema, body, status=200)
793
794 def test_validate_pass_with_http_redirect_code(self):
795 body = {'foo': 12}
796 schema = copy.deepcopy(self.schema)
797 schema['status_code'] = 300
798 self._test_validate_pass(schema, body, status=300)
799
800 def test_validate_not_http_success_code(self):
801 schema = {
802 'status_code': [200]
803 }
804 body = {}
805 self._test_validate_pass(schema, body, status=400)
806
807 def test_validate_multiple_allowed_type(self):
808 schema = {
809 'status_code': [200],
810 'response_body': {
811 'type': 'object',
812 'properties': {
813 'foo': {
814 'type': ['integer', 'string'],
815 },
816 },
817 'required': ['foo']
818 }
819 }
820 body = {'foo': 12}
821 self._test_validate_pass(schema, body)
822 body = {'foo': '12'}
823 self._test_validate_pass(schema, body)
824
825 def test_validate_enable_additional_property_pass(self):
826 schema = {
827 'status_code': [200],
828 'response_body': {
829 'type': 'object',
830 'properties': {
831 'foo': {'type': 'integer'}
832 },
833 'additionalProperties': True,
834 'required': ['foo']
835 }
836 }
837 body = {'foo': 12, 'foo2': 'foo2value'}
838 self._test_validate_pass(schema, body)
839
840 def test_validate_disable_additional_property_pass(self):
841 schema = {
842 'status_code': [200],
843 'response_body': {
844 'type': 'object',
845 'properties': {
846 'foo': {'type': 'integer'}
847 },
848 'additionalProperties': False,
849 'required': ['foo']
850 }
851 }
852 body = {'foo': 12}
853 self._test_validate_pass(schema, body)
854
855 def test_validate_disable_additional_property_fail(self):
856 schema = {
857 'status_code': [200],
858 'response_body': {
859 'type': 'object',
860 'properties': {
861 'foo': {'type': 'integer'}
862 },
863 'additionalProperties': False,
864 'required': ['foo']
865 }
866 }
867 body = {'foo': 12, 'foo2': 'foo2value'}
868 self._test_validate_fail(schema, body)
869
870 def test_validate_wrong_status_code(self):
871 schema = {
872 'status_code': [202]
873 }
874 body = {}
875 resp = self.Response()
876 resp.status = 200
877 ex = self.assertRaises(exceptions.InvalidHttpSuccessCode,
878 self.rest_client.validate_response,
879 schema, resp, body)
880 self.assertIn("Unexpected http success status code", ex._error_string)
881
882 def test_validate_wrong_attribute_type(self):
883 body = {'foo': 1.2}
884 self._test_validate_fail(self.schema, body)
885
886 def test_validate_unexpected_response_body(self):
887 schema = {
888 'status_code': [200],
889 }
890 body = {'foo': 12}
891 self._test_validate_fail(
892 schema, body,
893 error_msg="HTTP response body should not exist")
894
895 def test_validate_missing_response_body(self):
896 body = {}
897 self._test_validate_fail(self.schema, body)
898
899 def test_validate_missing_required_attribute(self):
900 body = {'notfoo': 12}
901 self._test_validate_fail(self.schema, body)
902
903 def test_validate_response_body_not_list(self):
904 schema = {
905 'status_code': [200],
906 'response_body': {
907 'type': 'object',
908 'properties': {
909 'list_items': {
910 'type': 'array',
911 'items': {'foo': {'type': 'integer'}}
912 }
913 },
914 'required': ['list_items'],
915 }
916 }
917 body = {'foo': 12}
918 self._test_validate_fail(schema, body)
919
920 def test_validate_response_body_list_pass(self):
921 schema = {
922 'status_code': [200],
923 'response_body': {
924 'type': 'object',
925 'properties': {
926 'list_items': {
927 'type': 'array',
928 'items': {'foo': {'type': 'integer'}}
929 }
930 },
931 'required': ['list_items'],
932 }
933 }
934 body = {'list_items': [{'foo': 12}, {'foo': 10}]}
935 self._test_validate_pass(schema, body)
936
937
938class TestRestClientJSONHeaderSchemaValidation(TestJSONSchemaValidationBase):
939
940 schema = {
941 'status_code': [200],
942 'response_header': {
943 'type': 'object',
944 'properties': {
945 'foo': {'type': 'integer'}
946 },
947 'required': ['foo']
948 }
949 }
950
951 def test_validate_header_schema_pass(self):
952 resp_body = {}
953 resp = self.Response()
954 resp.status = 200
955 resp.foo = 12
956 self.rest_client.validate_response(self.schema, resp, resp_body)
957
958 def test_validate_header_schema_fail(self):
959 resp_body = {}
960 resp = self.Response()
961 resp.status = 200
962 resp.foo = 1.2
963 ex = self.assertRaises(exceptions.InvalidHTTPResponseHeader,
964 self.rest_client.validate_response,
965 self.schema, resp, resp_body)
966 self.assertIn("HTTP response header is invalid", ex._error_string)
967
968
969class TestRestClientJSONSchemaFormatValidation(TestJSONSchemaValidationBase):
970
971 schema = {
972 'status_code': [200],
973 'response_body': {
974 'type': 'object',
975 'properties': {
976 'foo': {
977 'type': 'string',
978 'format': 'email'
979 }
980 },
981 'required': ['foo']
982 }
983 }
984
985 def test_validate_format_pass(self):
986 body = {'foo': 'example@example.com'}
987 self._test_validate_pass(self.schema, body)
988
989 def test_validate_format_fail(self):
990 body = {'foo': 'wrong_email'}
991 self._test_validate_fail(self.schema, body)
992
993 def test_validate_formats_in_oneOf_pass(self):
994 schema = {
995 'status_code': [200],
996 'response_body': {
997 'type': 'object',
998 'properties': {
999 'foo': {
1000 'type': 'string',
1001 'oneOf': [
1002 {'format': 'ipv4'},
1003 {'format': 'ipv6'}
1004 ]
1005 }
1006 },
1007 'required': ['foo']
1008 }
1009 }
1010 body = {'foo': '10.0.0.0'}
1011 self._test_validate_pass(schema, body)
1012 body = {'foo': 'FE80:0000:0000:0000:0202:B3FF:FE1E:8329'}
1013 self._test_validate_pass(schema, body)
1014
1015 def test_validate_formats_in_oneOf_fail_both_match(self):
1016 schema = {
1017 'status_code': [200],
1018 'response_body': {
1019 'type': 'object',
1020 'properties': {
1021 'foo': {
1022 'type': 'string',
1023 'oneOf': [
1024 {'format': 'ipv4'},
1025 {'format': 'ipv4'}
1026 ]
1027 }
1028 },
1029 'required': ['foo']
1030 }
1031 }
1032 body = {'foo': '10.0.0.0'}
1033 self._test_validate_fail(schema, body)
1034
1035 def test_validate_formats_in_oneOf_fail_no_match(self):
1036 schema = {
1037 'status_code': [200],
1038 'response_body': {
1039 'type': 'object',
1040 'properties': {
1041 'foo': {
1042 'type': 'string',
1043 'oneOf': [
1044 {'format': 'ipv4'},
1045 {'format': 'ipv6'}
1046 ]
1047 }
1048 },
1049 'required': ['foo']
1050 }
1051 }
1052 body = {'foo': 'wrong_ip_format'}
1053 self._test_validate_fail(schema, body)
1054
1055 def test_validate_formats_in_anyOf_pass(self):
1056 schema = {
1057 'status_code': [200],
1058 'response_body': {
1059 'type': 'object',
1060 'properties': {
1061 'foo': {
1062 'type': 'string',
1063 'anyOf': [
1064 {'format': 'ipv4'},
1065 {'format': 'ipv6'}
1066 ]
1067 }
1068 },
1069 'required': ['foo']
1070 }
1071 }
1072 body = {'foo': '10.0.0.0'}
1073 self._test_validate_pass(schema, body)
1074 body = {'foo': 'FE80:0000:0000:0000:0202:B3FF:FE1E:8329'}
1075 self._test_validate_pass(schema, body)
1076
1077 def test_validate_formats_in_anyOf_pass_both_match(self):
1078 schema = {
1079 'status_code': [200],
1080 'response_body': {
1081 'type': 'object',
1082 'properties': {
1083 'foo': {
1084 'type': 'string',
1085 'anyOf': [
1086 {'format': 'ipv4'},
1087 {'format': 'ipv4'}
1088 ]
1089 }
1090 },
1091 'required': ['foo']
1092 }
1093 }
1094 body = {'foo': '10.0.0.0'}
1095 self._test_validate_pass(schema, body)
1096
1097 def test_validate_formats_in_anyOf_fail_no_match(self):
1098 schema = {
1099 'status_code': [200],
1100 'response_body': {
1101 'type': 'object',
1102 'properties': {
1103 'foo': {
1104 'type': 'string',
1105 'anyOf': [
1106 {'format': 'ipv4'},
1107 {'format': 'ipv6'}
1108 ]
1109 }
1110 },
1111 'required': ['foo']
1112 }
1113 }
1114 body = {'foo': 'wrong_ip_format'}
1115 self._test_validate_fail(schema, body)
1116
1117 def test_validate_formats_pass_for_unknow_format(self):
1118 schema = {
1119 'status_code': [200],
1120 'response_body': {
1121 'type': 'object',
1122 'properties': {
1123 'foo': {
1124 'type': 'string',
1125 'format': 'UNKNOWN'
1126 }
1127 },
1128 'required': ['foo']
1129 }
1130 }
1131 body = {'foo': 'example@example.com'}
1132 self._test_validate_pass(schema, body)
1133
1134
1135class TestRestClientJSONSchemaValidatorVersion(TestJSONSchemaValidationBase):
1136
1137 schema = {
1138 'status_code': [200],
1139 'response_body': {
1140 'type': 'object',
1141 'properties': {
1142 'foo': {'type': 'string'}
1143 }
1144 }
1145 }
1146
1147 def test_current_json_schema_validator_version(self):
Ngo Quoc Cuong33710b32017-05-11 14:17:17 +07001148 with fixtures.MockPatchObject(jsonschema.Draft4Validator,
1149 "check_schema") as chk_schema:
Matthew Treinish9e26ca82016-02-23 11:43:20 -05001150 body = {'foo': 'test'}
1151 self._test_validate_pass(self.schema, body)
1152 chk_schema.mock.assert_called_once_with(
1153 self.schema['response_body'])