2013-09-20 01:00:54 +08:00
|
|
|
# Copyright (c) 2010-2012 OpenStack Foundation
|
2013-03-30 15:55:29 +03:00
|
|
|
#
|
|
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
# you may not use this file except in compliance with the License.
|
|
|
|
# You may obtain a copy of the License at
|
|
|
|
#
|
|
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
#
|
|
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
|
|
# implied.
|
|
|
|
# See the License for the specific language governing permissions and
|
|
|
|
# limitations under the License.
|
|
|
|
|
|
|
|
import mock
|
|
|
|
import unittest
|
|
|
|
|
2014-11-17 20:29:45 -08:00
|
|
|
from eventlet import Timeout
|
|
|
|
|
2013-03-30 15:55:29 +03:00
|
|
|
from swift.common.swob import Request
|
|
|
|
from swift.proxy import server as proxy_server
|
|
|
|
from swift.proxy.controllers.base import headers_to_container_info
|
|
|
|
from test.unit import fake_http_connect, FakeRing, FakeMemcache
|
2014-05-27 16:57:25 -07:00
|
|
|
from swift.common.storage_policy import StoragePolicy
|
2013-12-03 22:02:39 +00:00
|
|
|
from swift.common.request_helpers import get_sys_meta_prefix
|
2013-03-30 15:55:29 +03:00
|
|
|
|
2014-11-17 20:29:45 -08:00
|
|
|
from test.unit import patch_policies, mocked_http_conn, debug_logger
|
2014-05-27 16:57:25 -07:00
|
|
|
from test.unit.common.ring.test_ring import TestRingBase
|
2014-11-17 20:29:45 -08:00
|
|
|
from test.unit.proxy.test_server import node_error_count
|
2013-03-30 15:55:29 +03:00
|
|
|
|
2014-05-27 16:57:25 -07:00
|
|
|
|
|
|
|
@patch_policies([StoragePolicy(0, 'zero', True, object_ring=FakeRing())])
|
|
|
|
class TestContainerController(TestRingBase):
|
2016-02-18 21:06:40 -08:00
|
|
|
|
|
|
|
CONTAINER_REPLICAS = 3
|
|
|
|
|
2013-03-30 15:55:29 +03:00
|
|
|
def setUp(self):
|
2014-05-27 16:57:25 -07:00
|
|
|
TestRingBase.setUp(self)
|
2014-11-17 20:29:45 -08:00
|
|
|
self.logger = debug_logger()
|
2016-02-18 21:06:40 -08:00
|
|
|
self.container_ring = FakeRing(replicas=self.CONTAINER_REPLICAS,
|
|
|
|
max_more_nodes=9)
|
2013-03-30 15:55:29 +03:00
|
|
|
self.app = proxy_server.Application(None, FakeMemcache(),
|
2014-11-17 20:29:45 -08:00
|
|
|
logger=self.logger,
|
2013-03-30 15:55:29 +03:00
|
|
|
account_ring=FakeRing(),
|
2014-11-17 20:29:45 -08:00
|
|
|
container_ring=self.container_ring)
|
|
|
|
|
|
|
|
self.account_info = {
|
|
|
|
'status': 200,
|
|
|
|
'container_count': '10',
|
|
|
|
'total_object_count': '100',
|
|
|
|
'bytes': '1000',
|
|
|
|
'meta': {},
|
|
|
|
'sysmeta': {},
|
|
|
|
}
|
|
|
|
|
|
|
|
class FakeAccountInfoContainerController(
|
|
|
|
proxy_server.ContainerController):
|
|
|
|
|
|
|
|
def account_info(controller, *args, **kwargs):
|
|
|
|
patch_path = 'swift.proxy.controllers.base.get_info'
|
|
|
|
with mock.patch(patch_path) as mock_get_info:
|
|
|
|
mock_get_info.return_value = dict(self.account_info)
|
|
|
|
return super(FakeAccountInfoContainerController,
|
|
|
|
controller).account_info(
|
|
|
|
*args, **kwargs)
|
|
|
|
_orig_get_controller = self.app.get_controller
|
|
|
|
|
|
|
|
def wrapped_get_controller(*args, **kwargs):
|
|
|
|
with mock.patch('swift.proxy.server.ContainerController',
|
|
|
|
new=FakeAccountInfoContainerController):
|
|
|
|
return _orig_get_controller(*args, **kwargs)
|
|
|
|
self.app.get_controller = wrapped_get_controller
|
2013-03-30 15:55:29 +03:00
|
|
|
|
2016-01-22 07:06:45 +00:00
|
|
|
def _make_callback_func(self, context):
|
|
|
|
def callback(ipaddr, port, device, partition, method, path,
|
|
|
|
headers=None, query_string=None, ssl=False):
|
|
|
|
context['method'] = method
|
|
|
|
context['path'] = path
|
|
|
|
context['headers'] = headers or {}
|
|
|
|
return callback
|
|
|
|
|
|
|
|
def _assert_responses(self, method, test_cases):
|
|
|
|
controller = proxy_server.ContainerController(self.app, 'a', 'c')
|
|
|
|
|
|
|
|
for responses, expected in test_cases:
|
|
|
|
with mock.patch(
|
|
|
|
'swift.proxy.controllers.base.http_connect',
|
|
|
|
fake_http_connect(*responses)):
|
|
|
|
req = Request.blank('/v1/a/c')
|
|
|
|
resp = getattr(controller, method)(req)
|
|
|
|
|
|
|
|
self.assertEqual(expected,
|
|
|
|
resp.status_int,
|
|
|
|
'Expected %s but got %s. Failed case: %s' %
|
|
|
|
(expected, resp.status_int, str(responses)))
|
|
|
|
|
2013-03-30 15:55:29 +03:00
|
|
|
def test_container_info_in_response_env(self):
|
|
|
|
controller = proxy_server.ContainerController(self.app, 'a', 'c')
|
|
|
|
with mock.patch('swift.proxy.controllers.base.http_connect',
|
|
|
|
fake_http_connect(200, 200, body='')):
|
Stop mutating PATH_INFO in proxy server
The proxy server was calling swob.Request.path_info_pop() prior to
instantiating a controller so that req.path_info was just /a/c/o (sans
/v1). The version got moved over into SCRIPT_NAME.
This lead to some unfortunate behavior when trying to re-use a request
from middleware. Something like this:
# Imagine we're a WSGIContext object here.
#
# To start, SCRIPT_NAME = '' and PATH_INFO='/v1/a/c/o'
resp_iter = self._app_call(env, start_response)
# Now SCRIPT_NAME='/v1' and PATH_INFO ='/a/c/o'
if something_special in self._response_headers:
env['REQUEST_METHOD'] = 'GET'
env.pop('HTTP_RANGE', None)
# 404 SURPRISE! The proxy calls path_info_pop() again,
# and now SCRIPT_NAME='/v1/a' and PATH_INFO='/c/o', so this
# gets treated as a container request. Yikes.
resp_iter = self._app_call(env, start_response)
Now we just leave SCRIPT_NAME and PATH_INFO alone. To make life easy
for everyone who does want just /a/c/o, I defined
swob.Request.swift_entity_path, which just strips off the /v1.
Note that there's still one call to path_info_pop() in tempauth, but
that's only for requests going to /auth, so it won't affect Swift API
requests. It might be a good idea to remove that one later, but let's
do one thing at a time.
Change-Id: I87557a11c01f3f3889b610578cda6ba7d3933e7a
2013-12-03 22:18:46 -08:00
|
|
|
req = Request.blank('/v1/a/c', {'PATH_INFO': '/v1/a/c'})
|
2013-03-30 15:55:29 +03:00
|
|
|
resp = controller.HEAD(req)
|
|
|
|
self.assertEqual(2, resp.status_int // 100)
|
Make info caching work across subrequests
Previously, if you called get_account_info, get_container_info, or
get_object_info, then the results of that call would be cached in the
WSGI environment as top-level keys. This is okay, except that if you,
in middleware, copy the WSGI environment and then make a subrequest
using the copy, information retrieved in the subrequest is cached
only in the copy and not in the original. This can mean lots of extra
trips to memcache for, say, SLO validation where the segments are in
another container; the object HEAD ends up getting container info for
the segment container, but then the next object HEAD gets it again.
This commit moves the cache for get_*_info into a dictionary at
environ['swift.infocache']; this way, you can shallow-copy the request
environment and still get the benefits from the cache.
Change-Id: I3481b38b41c33cd1e39e19baab56193c5f9bf6ac
2016-01-21 13:19:30 -08:00
|
|
|
self.assertTrue(
|
|
|
|
"swift.container/a/c" in resp.environ['swift.infocache'])
|
|
|
|
self.assertEqual(
|
|
|
|
headers_to_container_info(resp.headers),
|
|
|
|
resp.environ['swift.infocache']['swift.container/a/c'])
|
2013-03-30 15:55:29 +03:00
|
|
|
|
2013-06-27 14:11:25 +00:00
|
|
|
def test_swift_owner(self):
|
|
|
|
owner_headers = {
|
|
|
|
'x-container-read': 'value', 'x-container-write': 'value',
|
|
|
|
'x-container-sync-key': 'value', 'x-container-sync-to': 'value'}
|
|
|
|
controller = proxy_server.ContainerController(self.app, 'a', 'c')
|
|
|
|
|
Stop mutating PATH_INFO in proxy server
The proxy server was calling swob.Request.path_info_pop() prior to
instantiating a controller so that req.path_info was just /a/c/o (sans
/v1). The version got moved over into SCRIPT_NAME.
This lead to some unfortunate behavior when trying to re-use a request
from middleware. Something like this:
# Imagine we're a WSGIContext object here.
#
# To start, SCRIPT_NAME = '' and PATH_INFO='/v1/a/c/o'
resp_iter = self._app_call(env, start_response)
# Now SCRIPT_NAME='/v1' and PATH_INFO ='/a/c/o'
if something_special in self._response_headers:
env['REQUEST_METHOD'] = 'GET'
env.pop('HTTP_RANGE', None)
# 404 SURPRISE! The proxy calls path_info_pop() again,
# and now SCRIPT_NAME='/v1/a' and PATH_INFO='/c/o', so this
# gets treated as a container request. Yikes.
resp_iter = self._app_call(env, start_response)
Now we just leave SCRIPT_NAME and PATH_INFO alone. To make life easy
for everyone who does want just /a/c/o, I defined
swob.Request.swift_entity_path, which just strips off the /v1.
Note that there's still one call to path_info_pop() in tempauth, but
that's only for requests going to /auth, so it won't affect Swift API
requests. It might be a good idea to remove that one later, but let's
do one thing at a time.
Change-Id: I87557a11c01f3f3889b610578cda6ba7d3933e7a
2013-12-03 22:18:46 -08:00
|
|
|
req = Request.blank('/v1/a/c')
|
2013-06-27 14:11:25 +00:00
|
|
|
with mock.patch('swift.proxy.controllers.base.http_connect',
|
|
|
|
fake_http_connect(200, 200, headers=owner_headers)):
|
|
|
|
resp = controller.HEAD(req)
|
2015-08-06 10:01:17 -05:00
|
|
|
self.assertEqual(2, resp.status_int // 100)
|
2013-06-27 14:11:25 +00:00
|
|
|
for key in owner_headers:
|
|
|
|
self.assertTrue(key not in resp.headers)
|
|
|
|
|
Stop mutating PATH_INFO in proxy server
The proxy server was calling swob.Request.path_info_pop() prior to
instantiating a controller so that req.path_info was just /a/c/o (sans
/v1). The version got moved over into SCRIPT_NAME.
This lead to some unfortunate behavior when trying to re-use a request
from middleware. Something like this:
# Imagine we're a WSGIContext object here.
#
# To start, SCRIPT_NAME = '' and PATH_INFO='/v1/a/c/o'
resp_iter = self._app_call(env, start_response)
# Now SCRIPT_NAME='/v1' and PATH_INFO ='/a/c/o'
if something_special in self._response_headers:
env['REQUEST_METHOD'] = 'GET'
env.pop('HTTP_RANGE', None)
# 404 SURPRISE! The proxy calls path_info_pop() again,
# and now SCRIPT_NAME='/v1/a' and PATH_INFO='/c/o', so this
# gets treated as a container request. Yikes.
resp_iter = self._app_call(env, start_response)
Now we just leave SCRIPT_NAME and PATH_INFO alone. To make life easy
for everyone who does want just /a/c/o, I defined
swob.Request.swift_entity_path, which just strips off the /v1.
Note that there's still one call to path_info_pop() in tempauth, but
that's only for requests going to /auth, so it won't affect Swift API
requests. It might be a good idea to remove that one later, but let's
do one thing at a time.
Change-Id: I87557a11c01f3f3889b610578cda6ba7d3933e7a
2013-12-03 22:18:46 -08:00
|
|
|
req = Request.blank('/v1/a/c', environ={'swift_owner': True})
|
2013-06-27 14:11:25 +00:00
|
|
|
with mock.patch('swift.proxy.controllers.base.http_connect',
|
|
|
|
fake_http_connect(200, 200, headers=owner_headers)):
|
|
|
|
resp = controller.HEAD(req)
|
2015-08-06 10:01:17 -05:00
|
|
|
self.assertEqual(2, resp.status_int // 100)
|
2013-06-27 14:11:25 +00:00
|
|
|
for key in owner_headers:
|
|
|
|
self.assertTrue(key in resp.headers)
|
|
|
|
|
2013-12-03 22:02:39 +00:00
|
|
|
def test_sys_meta_headers_PUT(self):
|
|
|
|
# check that headers in sys meta namespace make it through
|
|
|
|
# the container controller
|
|
|
|
sys_meta_key = '%stest' % get_sys_meta_prefix('container')
|
|
|
|
sys_meta_key = sys_meta_key.title()
|
|
|
|
user_meta_key = 'X-Container-Meta-Test'
|
|
|
|
controller = proxy_server.ContainerController(self.app, 'a', 'c')
|
|
|
|
|
|
|
|
context = {}
|
|
|
|
callback = self._make_callback_func(context)
|
|
|
|
hdrs_in = {sys_meta_key: 'foo',
|
|
|
|
user_meta_key: 'bar',
|
|
|
|
'x-timestamp': '1.0'}
|
|
|
|
req = Request.blank('/v1/a/c', headers=hdrs_in)
|
|
|
|
with mock.patch('swift.proxy.controllers.base.http_connect',
|
|
|
|
fake_http_connect(200, 200, give_connect=callback)):
|
|
|
|
controller.PUT(req)
|
|
|
|
self.assertEqual(context['method'], 'PUT')
|
|
|
|
self.assertTrue(sys_meta_key in context['headers'])
|
|
|
|
self.assertEqual(context['headers'][sys_meta_key], 'foo')
|
|
|
|
self.assertTrue(user_meta_key in context['headers'])
|
|
|
|
self.assertEqual(context['headers'][user_meta_key], 'bar')
|
|
|
|
self.assertNotEqual(context['headers']['x-timestamp'], '1.0')
|
|
|
|
|
|
|
|
def test_sys_meta_headers_POST(self):
|
|
|
|
# check that headers in sys meta namespace make it through
|
|
|
|
# the container controller
|
|
|
|
sys_meta_key = '%stest' % get_sys_meta_prefix('container')
|
|
|
|
sys_meta_key = sys_meta_key.title()
|
|
|
|
user_meta_key = 'X-Container-Meta-Test'
|
|
|
|
controller = proxy_server.ContainerController(self.app, 'a', 'c')
|
|
|
|
context = {}
|
|
|
|
callback = self._make_callback_func(context)
|
|
|
|
hdrs_in = {sys_meta_key: 'foo',
|
|
|
|
user_meta_key: 'bar',
|
|
|
|
'x-timestamp': '1.0'}
|
|
|
|
req = Request.blank('/v1/a/c', headers=hdrs_in)
|
|
|
|
with mock.patch('swift.proxy.controllers.base.http_connect',
|
|
|
|
fake_http_connect(200, 200, give_connect=callback)):
|
|
|
|
controller.POST(req)
|
|
|
|
self.assertEqual(context['method'], 'POST')
|
|
|
|
self.assertTrue(sys_meta_key in context['headers'])
|
|
|
|
self.assertEqual(context['headers'][sys_meta_key], 'foo')
|
|
|
|
self.assertTrue(user_meta_key in context['headers'])
|
|
|
|
self.assertEqual(context['headers'][user_meta_key], 'bar')
|
|
|
|
self.assertNotEqual(context['headers']['x-timestamp'], '1.0')
|
|
|
|
|
2014-11-17 20:29:45 -08:00
|
|
|
def test_node_errors(self):
|
|
|
|
self.app.sort_nodes = lambda n: n
|
|
|
|
|
|
|
|
for method in ('PUT', 'DELETE', 'POST'):
|
|
|
|
def test_status_map(statuses, expected):
|
|
|
|
self.app._error_limiting = {}
|
|
|
|
req = Request.blank('/v1/a/c', method=method)
|
|
|
|
with mocked_http_conn(*statuses) as fake_conn:
|
|
|
|
resp = req.get_response(self.app)
|
|
|
|
self.assertEqual(resp.status_int, expected)
|
|
|
|
for req in fake_conn.requests:
|
|
|
|
self.assertEqual(req['method'], method)
|
2015-07-21 19:23:00 +05:30
|
|
|
self.assertTrue(req['path'].endswith('/a/c'))
|
2014-11-17 20:29:45 -08:00
|
|
|
|
|
|
|
base_status = [201] * 3
|
|
|
|
# test happy path
|
|
|
|
test_status_map(list(base_status), 201)
|
|
|
|
for i in range(3):
|
|
|
|
self.assertEqual(node_error_count(
|
|
|
|
self.app, self.container_ring.devs[i]), 0)
|
|
|
|
# single node errors and test isolation
|
|
|
|
for i in range(3):
|
|
|
|
status_list = list(base_status)
|
|
|
|
status_list[i] = 503
|
|
|
|
status_list.append(201)
|
|
|
|
test_status_map(status_list, 201)
|
|
|
|
for j in range(3):
|
|
|
|
expected = 1 if j == i else 0
|
|
|
|
self.assertEqual(node_error_count(
|
|
|
|
self.app, self.container_ring.devs[j]), expected)
|
|
|
|
# timeout
|
|
|
|
test_status_map((201, Timeout(), 201, 201), 201)
|
|
|
|
self.assertEqual(node_error_count(
|
|
|
|
self.app, self.container_ring.devs[1]), 1)
|
|
|
|
|
|
|
|
# exception
|
|
|
|
test_status_map((Exception('kaboom!'), 201, 201, 201), 201)
|
|
|
|
self.assertEqual(node_error_count(
|
|
|
|
self.app, self.container_ring.devs[0]), 1)
|
|
|
|
|
|
|
|
# insufficient storage
|
|
|
|
test_status_map((201, 201, 507, 201), 201)
|
|
|
|
self.assertEqual(node_error_count(
|
|
|
|
self.app, self.container_ring.devs[2]),
|
|
|
|
self.app.error_suppression_limit + 1)
|
|
|
|
|
2016-01-22 07:06:45 +00:00
|
|
|
def test_response_code_for_PUT(self):
|
|
|
|
PUT_TEST_CASES = [
|
2016-02-22 16:10:09 +09:00
|
|
|
((201, 201, 201), 201),
|
|
|
|
((201, 201, 404), 201),
|
|
|
|
((201, 201, 503), 201),
|
|
|
|
((201, 404, 404), 404),
|
|
|
|
((201, 404, 503), 503),
|
|
|
|
((201, 503, 503), 503),
|
2016-01-22 07:06:45 +00:00
|
|
|
((404, 404, 404), 404),
|
|
|
|
((404, 404, 503), 404),
|
|
|
|
((404, 503, 503), 503),
|
|
|
|
((503, 503, 503), 503)
|
|
|
|
]
|
|
|
|
self._assert_responses('PUT', PUT_TEST_CASES)
|
|
|
|
|
|
|
|
def test_response_code_for_DELETE(self):
|
|
|
|
DELETE_TEST_CASES = [
|
|
|
|
((204, 204, 204), 204),
|
|
|
|
((204, 204, 404), 204),
|
|
|
|
((204, 204, 503), 204),
|
|
|
|
((204, 404, 404), 404),
|
|
|
|
((204, 404, 503), 503),
|
|
|
|
((204, 503, 503), 503),
|
|
|
|
((404, 404, 404), 404),
|
|
|
|
((404, 404, 503), 404),
|
|
|
|
((404, 503, 503), 503),
|
|
|
|
((503, 503, 503), 503)
|
|
|
|
]
|
|
|
|
self._assert_responses('DELETE', DELETE_TEST_CASES)
|
|
|
|
|
|
|
|
def test_response_code_for_POST(self):
|
|
|
|
POST_TEST_CASES = [
|
|
|
|
((204, 204, 204), 204),
|
|
|
|
((204, 204, 404), 204),
|
|
|
|
((204, 204, 503), 204),
|
|
|
|
((204, 404, 404), 404),
|
|
|
|
((204, 404, 503), 503),
|
|
|
|
((204, 503, 503), 503),
|
|
|
|
((404, 404, 404), 404),
|
|
|
|
((404, 404, 503), 404),
|
|
|
|
((404, 503, 503), 503),
|
|
|
|
((503, 503, 503), 503)
|
|
|
|
]
|
|
|
|
self._assert_responses('POST', POST_TEST_CASES)
|
|
|
|
|
|
|
|
|
|
|
|
@patch_policies(
|
|
|
|
[StoragePolicy(0, 'zero', True, object_ring=FakeRing(replicas=4))])
|
|
|
|
class TestContainerController4Replicas(TestContainerController):
|
|
|
|
|
2016-02-18 21:06:40 -08:00
|
|
|
CONTAINER_REPLICAS = 4
|
2016-01-22 07:06:45 +00:00
|
|
|
|
|
|
|
def test_response_code_for_PUT(self):
|
|
|
|
PUT_TEST_CASES = [
|
2016-02-22 16:10:09 +09:00
|
|
|
((201, 201, 201, 201), 201),
|
|
|
|
((201, 201, 201, 404), 201),
|
|
|
|
((201, 201, 201, 503), 201),
|
2016-04-27 16:59:00 -05:00
|
|
|
((201, 201, 404, 404), 201),
|
|
|
|
((201, 201, 404, 503), 201),
|
|
|
|
((201, 201, 503, 503), 201),
|
2016-02-22 16:10:09 +09:00
|
|
|
((201, 404, 404, 404), 404),
|
2016-04-27 16:59:00 -05:00
|
|
|
((201, 404, 404, 503), 404),
|
2016-02-22 16:10:09 +09:00
|
|
|
((201, 404, 503, 503), 503),
|
|
|
|
((201, 503, 503, 503), 503),
|
2016-01-22 07:06:45 +00:00
|
|
|
((404, 404, 404, 404), 404),
|
|
|
|
((404, 404, 404, 503), 404),
|
2016-04-27 16:59:00 -05:00
|
|
|
((404, 404, 503, 503), 404),
|
2016-01-22 07:06:45 +00:00
|
|
|
((404, 503, 503, 503), 503),
|
|
|
|
((503, 503, 503, 503), 503)
|
|
|
|
]
|
|
|
|
self._assert_responses('PUT', PUT_TEST_CASES)
|
|
|
|
|
|
|
|
def test_response_code_for_DELETE(self):
|
|
|
|
DELETE_TEST_CASES = [
|
|
|
|
((204, 204, 204, 204), 204),
|
|
|
|
((204, 204, 204, 404), 204),
|
|
|
|
((204, 204, 204, 503), 204),
|
2016-04-27 16:59:00 -05:00
|
|
|
((204, 204, 404, 404), 204),
|
|
|
|
((204, 204, 404, 503), 204),
|
|
|
|
((204, 204, 503, 503), 204),
|
2016-01-22 07:06:45 +00:00
|
|
|
((204, 404, 404, 404), 404),
|
2016-04-27 16:59:00 -05:00
|
|
|
((204, 404, 404, 503), 404),
|
2016-01-22 07:06:45 +00:00
|
|
|
((204, 404, 503, 503), 503),
|
|
|
|
((204, 503, 503, 503), 503),
|
|
|
|
((404, 404, 404, 404), 404),
|
|
|
|
((404, 404, 404, 503), 404),
|
2016-04-27 16:59:00 -05:00
|
|
|
((404, 404, 503, 503), 404),
|
2016-01-22 07:06:45 +00:00
|
|
|
((404, 503, 503, 503), 503),
|
|
|
|
((503, 503, 503, 503), 503)
|
|
|
|
]
|
|
|
|
self._assert_responses('DELETE', DELETE_TEST_CASES)
|
|
|
|
|
|
|
|
def test_response_code_for_POST(self):
|
|
|
|
POST_TEST_CASES = [
|
|
|
|
((204, 204, 204, 204), 204),
|
|
|
|
((204, 204, 204, 404), 204),
|
|
|
|
((204, 204, 204, 503), 204),
|
2016-04-27 16:59:00 -05:00
|
|
|
((204, 204, 404, 404), 204),
|
|
|
|
((204, 204, 404, 503), 204),
|
|
|
|
((204, 204, 503, 503), 204),
|
2016-01-22 07:06:45 +00:00
|
|
|
((204, 404, 404, 404), 404),
|
2016-04-27 16:59:00 -05:00
|
|
|
((204, 404, 404, 503), 404),
|
2016-01-22 07:06:45 +00:00
|
|
|
((204, 404, 503, 503), 503),
|
|
|
|
((204, 503, 503, 503), 503),
|
|
|
|
((404, 404, 404, 404), 404),
|
|
|
|
((404, 404, 404, 503), 404),
|
2016-04-27 16:59:00 -05:00
|
|
|
((404, 404, 503, 503), 404),
|
2016-01-22 07:06:45 +00:00
|
|
|
((404, 503, 503, 503), 503),
|
|
|
|
((503, 503, 503, 503), 503)
|
|
|
|
]
|
|
|
|
self._assert_responses('POST', POST_TEST_CASES)
|
|
|
|
|
2013-03-30 15:55:29 +03:00
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
unittest.main()
|