swift/test/unit/common/middleware/test_helpers.py
Alistair Coles 365db20275 FakeSwift: use HTTPMethodNotAllowed not HTTPNotImplemented
If a method is not allowed, real swift proxy server app will return an
HTTPMethodNotAllowed response, whereas FakeSwift would previously
*raise* HTTPNotImplemented. S3Api deliberately sends requests with
method 'TEST' which is not allowed/implemented. To workaround the
difference in real and fake swift behaviour, FakeSwift was configured
to allow the 'TEST' method, and then in some tests an
HTTPMethodNotAllowed response was registered for 'TEST' requests!

This patch modifies FakeSwift to return an HTTPMethodNotAllowed
response to the incoming request when the request method is not
allowed.

It is no longer necessary for FakeSwift to support extending the
default list of allowed methods.

Change-Id: I550d0174e14a5d5a05d26e5cbe9d3353f5da4e8a
2023-12-14 17:00:11 +00:00

824 lines
35 KiB
Python

# -*- coding: utf-8 -*-
# Copyright (c) 2023 OpenStack Foundation
#
# 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 unittest
from swift.common.storage_policy import POLICIES
from swift.common.swob import Request, HTTPOk, HTTPNotFound, \
HTTPCreated, HeaderKeyDict
from swift.common import request_helpers as rh
from swift.common.middleware.s3api.utils import sysmeta_header
from test.unit.common.middleware.helpers import FakeSwift
class TestFakeSwift(unittest.TestCase):
def test_allowed_methods(self):
def do_test(swift, method, exp_status):
path = '/v1/a/c/o'
swift.register(method, path, HTTPOk, {}, None)
req = Request.blank(path)
req.method = method
self.assertEqual(exp_status, req.get_response(swift).status_int)
for method in ('PUT', 'POST', 'DELETE', 'GET', 'HEAD', 'OPTIONS',
'REPLICATE', 'SSYNC', 'UPDATE'):
do_test(FakeSwift(), method, 200)
do_test(FakeSwift(), 'TEST', 405)
do_test(FakeSwift(), 'get', 405)
def test_not_registered(self):
swift = FakeSwift()
def do_test(method):
req = Request.blank('/v1/a/c/o')
req.method = method
with self.assertRaises(KeyError):
req.get_response(swift)
do_test('GET')
do_test('HEAD')
do_test('POST')
do_test('PUT')
do_test('DELETE')
def test_GET_registered(self):
# verify that a single registered GET response is sufficient to handle
# GETs and HEADS, with and without query strings
swift = FakeSwift()
swift.register('GET', '/v1/a/c/o', HTTPOk, {'X-Foo': 'Bar'}, b'stuff')
req = Request.blank('/v1/a/c/o')
req.method = 'GET'
resp = req.get_response(swift)
self.assertEqual(200, resp.status_int)
self.assertEqual({'Content-Length': '5',
'Content-Type': 'text/html; charset=UTF-8',
'X-Foo': 'Bar'},
resp.headers)
self.assertEqual(b'stuff', resp.body)
self.assertEqual(1, swift.call_count)
self.assertEqual(('GET', '/v1/a/c/o'), swift.calls[-1])
req = Request.blank('/v1/a/c/o')
req.method = 'GET'
req.query_string = 'p=q'
resp = req.get_response(swift)
self.assertEqual(200, resp.status_int)
self.assertEqual({'Content-Length': '5',
'Content-Type': 'text/html; charset=UTF-8',
'X-Foo': 'Bar'},
resp.headers)
self.assertEqual(b'stuff', resp.body)
self.assertEqual(2, swift.call_count)
self.assertEqual(('GET', '/v1/a/c/o?p=q'), swift.calls[-1])
req = Request.blank('/v1/a/c/o')
req.method = 'HEAD'
resp = req.get_response(swift)
self.assertEqual(200, resp.status_int)
self.assertEqual({'Content-Type': 'text/html; charset=UTF-8',
'X-Foo': 'Bar'},
resp.headers)
self.assertEqual(b'', resp.body)
self.assertEqual(3, swift.call_count)
self.assertEqual(('HEAD', '/v1/a/c/o'), swift.calls[-1])
req = Request.blank('/v1/a/c/o')
req.method = 'HEAD'
req.query_string = 'p=q'
resp = req.get_response(swift)
self.assertEqual(200, resp.status_int)
self.assertEqual({'Content-Type': 'text/html; charset=UTF-8',
'X-Foo': 'Bar'},
resp.headers)
self.assertEqual(b'', resp.body)
self.assertEqual(4, swift.call_count)
self.assertEqual(('HEAD', '/v1/a/c/o?p=q'), swift.calls[-1])
def test_GET_registered_with_query_string(self):
# verify that a single registered GET response is sufficient to handle
# GETs and HEADS, with and without query strings
swift = FakeSwift()
swift.register('GET', '/v1/a/c/o?p=q', HTTPOk,
{'X-Foo': 'Bar'}, b'stuff')
req = Request.blank('/v1/a/c/o')
req.method = 'GET'
with self.assertRaises(KeyError):
resp = req.get_response(swift)
req.query_string = 'p=q'
resp = req.get_response(swift)
self.assertEqual(200, resp.status_int)
self.assertEqual({'Content-Length': '5',
'Content-Type': 'text/html; charset=UTF-8',
'X-Foo': 'Bar'},
resp.headers)
self.assertEqual(b'stuff', resp.body)
self.assertEqual(1, swift.call_count)
self.assertEqual(('GET', '/v1/a/c/o?p=q'), swift.calls[-1])
def test_GET_and_HEAD_registered(self):
# verify that a registered HEAD response will be preferred over GET for
# HEAD request
swift = FakeSwift()
swift.register('GET', '/v1/a/c/o', HTTPOk, {'X-Foo': 'Bar'}, b'stuff')
swift.register('HEAD', '/v1/a/c/o', HTTPNotFound, {}, b'')
req = Request.blank('/v1/a/c/o')
req.method = 'GET'
resp = req.get_response(swift)
self.assertEqual(200, resp.status_int)
self.assertEqual({'Content-Length': '5',
'Content-Type': 'text/html; charset=UTF-8',
'X-Foo': 'Bar'},
resp.headers)
self.assertEqual(b'stuff', resp.body)
self.assertEqual(1, swift.call_count)
self.assertEqual(('GET', '/v1/a/c/o'), swift.calls[-1])
req = Request.blank('/v1/a/c/o')
req.method = 'HEAD'
resp = req.get_response(swift)
self.assertEqual(404, resp.status_int)
self.assertEqual({'Content-Type': 'text/html; charset=UTF-8'},
resp.headers)
self.assertEqual(b'', resp.body)
self.assertEqual(2, swift.call_count)
self.assertEqual(('HEAD', '/v1/a/c/o'), swift.calls[-1])
req = Request.blank('/v1/a/c/o')
req.method = 'HEAD'
req.query_string = 'p=q'
resp = req.get_response(swift)
self.assertEqual(404, resp.status_int)
self.assertEqual({'Content-Type': 'text/html; charset=UTF-8'},
resp.headers)
self.assertEqual(b'', resp.body)
self.assertEqual(3, swift.call_count)
self.assertEqual(('HEAD', '/v1/a/c/o?p=q'), swift.calls[-1])
def test_PUT_uploaded(self):
# verify an uploaded object is sufficient to handle GETs and HEADS,
# with and without query strings
swift = FakeSwift()
swift.register('PUT', '/v1/a/c/o', HTTPCreated, {}, None)
req = Request.blank('/v1/a/c/o', body=b'stuff')
req.method = 'PUT'
resp = req.get_response(swift)
self.assertEqual(201, resp.status_int)
self.assertEqual({'Content-Length': '5',
'Etag': 'c13d88cb4cb02003daedb8a84e5d272a',
'Content-Type': 'text/html; charset=UTF-8'},
resp.headers)
self.assertEqual(b'', resp.body)
self.assertEqual(1, swift.call_count)
self.assertEqual(('PUT', '/v1/a/c/o'), swift.calls[-1])
req = Request.blank('/v1/a/c/o')
req.method = 'GET'
resp = req.get_response(swift)
self.assertEqual(200, resp.status_int)
self.assertEqual({'Content-Length': '5',
'Content-Type': 'text/html; charset=UTF-8',
'Host': 'localhost:80'},
resp.headers)
self.assertEqual(b'stuff', resp.body)
self.assertEqual(2, swift.call_count)
self.assertEqual(('GET', '/v1/a/c/o'), swift.calls[-1])
req = Request.blank('/v1/a/c/o')
req.method = 'GET'
req.query_string = 'p=q'
resp = req.get_response(swift)
self.assertEqual(200, resp.status_int)
self.assertEqual({'Content-Length': '5',
'Content-Type': 'text/html; charset=UTF-8',
'Host': 'localhost:80'},
resp.headers)
self.assertEqual(b'stuff', resp.body)
self.assertEqual(3, swift.call_count)
self.assertEqual(('GET', '/v1/a/c/o?p=q'), swift.calls[-1])
req = Request.blank('/v1/a/c/o')
req.method = 'HEAD'
resp = req.get_response(swift)
self.assertEqual(200, resp.status_int)
self.assertEqual({'Content-Length': '5',
'Content-Type': 'text/html; charset=UTF-8',
'Host': 'localhost:80'},
resp.headers)
self.assertEqual(b'', resp.body)
self.assertEqual(4, swift.call_count)
self.assertEqual(('HEAD', '/v1/a/c/o'), swift.calls[-1])
req = Request.blank('/v1/a/c/o')
req.method = 'HEAD'
req.query_string = 'p=q'
resp = req.get_response(swift)
self.assertEqual(200, resp.status_int)
self.assertEqual({'Content-Length': '5',
'Content-Type': 'text/html; charset=UTF-8',
'Host': 'localhost:80'},
resp.headers)
self.assertEqual(b'', resp.body)
self.assertEqual(5, swift.call_count)
self.assertEqual(('HEAD', '/v1/a/c/o?p=q'), swift.calls[-1])
def test_PUT_uploaded_with_query_string(self):
# verify an uploaded object with query string is sufficient to handle
# GETs and HEADS, with and without query strings
swift = FakeSwift()
swift.register('PUT', '/v1/a/c/o', HTTPCreated, {}, None)
req = Request.blank('/v1/a/c/o', body=b'stuff')
req.method = 'PUT'
req.query_string = 'multipart-manifest=put'
resp = req.get_response(swift)
self.assertEqual(201, resp.status_int)
self.assertEqual({'Content-Length': '5',
'Etag': 'c13d88cb4cb02003daedb8a84e5d272a',
'Content-Type': 'text/html; charset=UTF-8'},
resp.headers)
self.assertEqual(b'', resp.body)
self.assertEqual(1, swift.call_count)
self.assertEqual(('PUT', '/v1/a/c/o?multipart-manifest=put'),
swift.calls[-1])
# note: query string is not included in uploaded key
self.assertEqual(
{'/v1/a/c/o': ({'Host': 'localhost:80',
'Content-Length': '5'},
b'stuff')},
swift.uploaded)
req = Request.blank('/v1/a/c/o')
req.method = 'GET'
resp = req.get_response(swift)
self.assertEqual(200, resp.status_int)
self.assertEqual({'Content-Length': '5',
'Content-Type': 'text/html; charset=UTF-8',
'Host': 'localhost:80'},
resp.headers)
self.assertEqual(b'stuff', resp.body)
self.assertEqual(2, swift.call_count)
self.assertEqual(('GET', '/v1/a/c/o'), swift.calls[-1])
req = Request.blank('/v1/a/c/o')
req.method = 'GET'
req.query_string = 'p=q' # note: differs from PUT query string
resp = req.get_response(swift)
self.assertEqual(200, resp.status_int)
self.assertEqual({'Content-Length': '5',
'Content-Type': 'text/html; charset=UTF-8',
'Host': 'localhost:80'},
resp.headers)
self.assertEqual(b'stuff', resp.body)
self.assertEqual(3, swift.call_count)
self.assertEqual(('GET', '/v1/a/c/o?p=q'), swift.calls[-1])
req = Request.blank('/v1/a/c/o')
req.method = 'HEAD'
resp = req.get_response(swift)
self.assertEqual(200, resp.status_int)
self.assertEqual({'Content-Length': '5',
'Content-Type': 'text/html; charset=UTF-8',
'Host': 'localhost:80'},
resp.headers)
self.assertEqual(b'', resp.body)
self.assertEqual(4, swift.call_count)
self.assertEqual(('HEAD', '/v1/a/c/o'), swift.calls[-1])
req = Request.blank('/v1/a/c/o')
req.method = 'HEAD'
req.query_string = 'p=q'
resp = req.get_response(swift)
self.assertEqual(200, resp.status_int)
self.assertEqual({'Content-Length': '5',
'Content-Type': 'text/html; charset=UTF-8',
'Host': 'localhost:80'},
resp.headers)
self.assertEqual(b'', resp.body)
self.assertEqual(5, swift.call_count)
self.assertEqual(('HEAD', '/v1/a/c/o?p=q'), swift.calls[-1])
def test_PUT_POST(self):
# verify an uploaded object is updated by a POST
swift = FakeSwift()
swift.register('PUT', '/v1/a/c/o', HTTPCreated, {}, None)
# Note: the POST must be registered
swift.register('POST', '/v1/a/c/o', HTTPCreated, {}, None)
req = Request.blank('/v1/a/c/o', body=b'stuff',
headers={'X-Object-Meta-Foo': 'Bar'})
req.method = 'PUT'
resp = req.get_response(swift)
self.assertEqual(201, resp.status_int)
self.assertEqual({'Content-Length': '5',
'Etag': 'c13d88cb4cb02003daedb8a84e5d272a',
'Content-Type': 'text/html; charset=UTF-8'},
resp.headers)
self.assertEqual(b'', resp.body)
self.assertEqual(1, swift.call_count)
self.assertEqual(('PUT', '/v1/a/c/o'), swift.calls[-1])
self.assertEqual(
{'/v1/a/c/o': ({'Host': 'localhost:80',
'Content-Length': '5',
'X-Object-Meta-Foo': 'Bar'},
b'stuff')},
swift.uploaded)
# POST should update the uploaded object
req = Request.blank('/v1/a/c/o', body=b'stuff',
headers={'X-Object-Meta-Foo': 'Baz'})
req.method = 'POST'
resp = req.get_response(swift)
self.assertEqual(201, resp.status_int)
self.assertEqual({'Content-Type': 'text/html; charset=UTF-8'},
resp.headers)
self.assertEqual(b'', resp.body)
self.assertEqual(2, swift.call_count)
self.assertEqual(('POST', '/v1/a/c/o'), swift.calls[-1])
self.assertEqual(
{'/v1/a/c/o': ({'Host': 'localhost:80',
'Content-Length': '5',
'X-Object-Meta-Foo': 'Baz'},
b'stuff')},
swift.uploaded)
def test_PUT_with_query_string_POST(self):
# verify an uploaded object with query string is updated by a POST
swift = FakeSwift()
swift.register('PUT', '/v1/a/c/o', HTTPCreated, {}, None)
# Note: the POST must be registered
swift.register('POST', '/v1/a/c/o', HTTPCreated, {}, None)
req = Request.blank('/v1/a/c/o', body=b'stuff',
headers={'X-Object-Meta-Foo': 'Bar'})
req.method = 'PUT'
req.query_string = 'p=q'
resp = req.get_response(swift)
self.assertEqual(201, resp.status_int)
self.assertEqual({'Content-Length': '5',
'Etag': 'c13d88cb4cb02003daedb8a84e5d272a',
'Content-Type': 'text/html; charset=UTF-8'},
resp.headers)
self.assertEqual(b'', resp.body)
self.assertEqual(1, swift.call_count)
self.assertEqual(('PUT', '/v1/a/c/o?p=q'), swift.calls[-1])
# note: query string is not included in uploaded key
self.assertEqual(
{'/v1/a/c/o': ({'Host': 'localhost:80',
'Content-Length': '5',
'X-Object-Meta-Foo': 'Bar'},
b'stuff')},
swift.uploaded)
# POST without query string should update the uploaded object
req = Request.blank('/v1/a/c/o', body=b'stuff',
headers={'X-Object-Meta-Foo': 'Baz'})
req.method = 'POST'
resp = req.get_response(swift)
self.assertEqual(201, resp.status_int)
self.assertEqual({'Content-Type': 'text/html; charset=UTF-8'},
resp.headers)
self.assertEqual(b'', resp.body)
self.assertEqual(2, swift.call_count)
self.assertEqual(('POST', '/v1/a/c/o'), swift.calls[-1])
self.assertEqual(
{'/v1/a/c/o': ({'Host': 'localhost:80',
'Content-Length': '5',
'X-Object-Meta-Foo': 'Baz'},
b'stuff')},
swift.uploaded)
# POST with different query string should update the uploaded object
req = Request.blank('/v1/a/c/o', body=b'stuff',
headers={'X-Object-Meta-Foo': 'Bof'})
req.method = 'POST'
req.query_string = 'x=y'
resp = req.get_response(swift)
self.assertEqual(201, resp.status_int)
self.assertEqual({'Content-Type': 'text/html; charset=UTF-8'},
resp.headers)
self.assertEqual(b'', resp.body)
self.assertEqual(3, swift.call_count)
self.assertEqual(('POST', '/v1/a/c/o?x=y'), swift.calls[-1])
self.assertEqual(
{'/v1/a/c/o': ({'Host': 'localhost:80',
'Content-Length': '5',
'X-Object-Meta-Foo': 'Bof'},
b'stuff')},
swift.uploaded)
req = Request.blank('/v1/a/c/o')
req.method = 'GET'
resp = req.get_response(swift)
self.assertEqual(200, resp.status_int)
self.assertEqual({'Content-Length': '5',
'Content-Type': 'text/html; charset=UTF-8',
'Host': 'localhost:80',
'X-Object-Meta-Foo': 'Bof'},
resp.headers)
self.assertEqual(b'stuff', resp.body)
self.assertEqual(4, swift.call_count)
self.assertEqual(('GET', '/v1/a/c/o'), swift.calls[-1])
def test_GET_registered_overrides_uploaded(self):
swift = FakeSwift()
swift.register('PUT', '/v1/a/c/o', HTTPCreated, {}, None)
swift.register('GET', '/v1/a/c/o', HTTPOk, {}, b'not stuff')
req = Request.blank('/v1/a/c/o', body=b'stuff',
headers={'X-Object-Meta-Foo': 'Bar'})
req.method = 'PUT'
resp = req.get_response(swift)
self.assertEqual(201, resp.status_int)
self.assertEqual({'Content-Length': '5',
'Etag': 'c13d88cb4cb02003daedb8a84e5d272a',
'Content-Type': 'text/html; charset=UTF-8'},
resp.headers)
self.assertEqual(b'', resp.body)
self.assertEqual(1, swift.call_count)
self.assertEqual(('PUT', '/v1/a/c/o'), swift.calls[-1])
self.assertEqual(
{'/v1/a/c/o': ({'Host': 'localhost:80',
'Content-Length': '5',
'X-Object-Meta-Foo': 'Bar'},
b'stuff')},
swift.uploaded)
req = Request.blank('/v1/a/c/o')
req.method = 'GET'
resp = req.get_response(swift)
self.assertEqual(200, resp.status_int)
self.assertEqual({'Content-Length': '9',
'Content-Type': 'text/html; charset=UTF-8'},
resp.headers)
self.assertEqual(b'not stuff', resp.body)
self.assertEqual(2, swift.call_count)
self.assertEqual(('GET', '/v1/a/c/o'), swift.calls[-1])
def test_range(self):
swift = FakeSwift()
swift.register('GET', '/v1/a/c/o', HTTPOk, {}, b'stuff')
req = Request.blank('/v1/a/c/o', headers={'Range': 'bytes=0-2'})
resp = req.get_response(swift)
self.assertEqual(206, resp.status_int)
self.assertEqual(b'stu', resp.body)
self.assertEqual('bytes 0-2/5', resp.headers['Content-Range'])
self.assertEqual('bytes=0-2', req.headers.get('Range'))
self.assertEqual('bytes=0-2',
swift.calls_with_headers[-1].headers.get('Range'))
def test_range_ignore_range_header(self):
swift = FakeSwift()
swift.register('GET', '/v1/a/c/o', HTTPOk, {
# the value of the matching header doesn't matter
'X-Object-Sysmeta-Magic': 'False'
}, b'stuff')
req = Request.blank('/v1/a/c/o', headers={'Range': 'bytes=0-2'})
rh.update_ignore_range_header(req, 'X-Object-Sysmeta-Magic')
resp = req.get_response(swift)
self.assertEqual(200, resp.status_int)
self.assertEqual(b'stuff', resp.body)
self.assertNotIn('Content-Range', resp.headers)
self.assertEqual('bytes=0-2', req.headers.get('Range'))
self.assertEqual('bytes=0-2',
swift.calls_with_headers[-1].headers.get('Range'))
def test_range_ignore_range_header_old_swift(self):
swift = FakeSwift()
swift.can_ignore_range = False
swift.register('GET', '/v1/a/c/o', HTTPOk, {
# the value of the matching header doesn't matter
'X-Object-Sysmeta-Magic': 'False'
}, b'stuff')
req = Request.blank('/v1/a/c/o', headers={'Range': 'bytes=0-2'})
rh.update_ignore_range_header(req, 'X-Object-Sysmeta-Magic')
resp = req.get_response(swift)
self.assertEqual(206, resp.status_int)
self.assertEqual(b'stu', resp.body)
self.assertEqual('bytes 0-2/5', resp.headers['Content-Range'])
self.assertEqual('bytes=0-2', req.headers.get('Range'))
self.assertEqual('bytes=0-2',
swift.calls_with_headers[-1].headers.get('Range'))
def test_range_ignore_range_header_ignored(self):
swift = FakeSwift()
# range is only ignored if registered response has matching metadata
swift.register('GET', '/v1/a/c/o', HTTPOk, {}, b'stuff')
req = Request.blank('/v1/a/c/o', headers={'Range': 'bytes=0-2'})
rh.update_ignore_range_header(req, 'X-Object-Sysmeta-Magic')
resp = req.get_response(swift)
self.assertEqual(206, resp.status_int)
self.assertEqual(b'stu', resp.body)
self.assertEqual('bytes 0-2/5', resp.headers['Content-Range'])
self.assertEqual('bytes=0-2', req.headers.get('Range'))
self.assertEqual('bytes=0-2',
swift.calls_with_headers[-1].headers.get('Range'))
def test_object_GET_updated_with_storage_policy(self):
swift = FakeSwift()
swift.register('GET', '/v1/a/c/o', HTTPOk, {}, body=b'stuff')
req = Request.blank('/v1/a/c/o')
req.method = 'GET'
self.assertNotIn('X-Backend-Storage-Policy-Index', req.headers)
resp = req.get_response(swift)
self.assertEqual(200, resp.status_int)
self.assertEqual({'Content-Length': '5',
'Content-Type': 'text/html; charset=UTF-8'},
resp.headers)
self.assertEqual(b'stuff', resp.body)
self.assertEqual(1, swift.call_count)
self.assertEqual(('GET', '/v1/a/c/o'), swift.calls[0])
self.assertEqual(('GET', '/v1/a/c/o',
{'Host': 'localhost:80'}), # from swob
swift.calls_with_headers[0])
# default storage policy is applied...
self.assertEqual(str(int(POLICIES.default)),
req.headers.get('X-Backend-Storage-Policy-Index'))
# register a container with storage policy 99...
swift.register('HEAD', '/v1/a/c', HTTPOk,
{'X-Backend-Storage-Policy-Index': '99'}, None)
req = Request.blank('/v1/a/c/o')
req.method = 'GET'
self.assertNotIn('X-Backend-Storage-Policy-Index', req.headers)
resp = req.get_response(swift)
self.assertEqual(200, resp.status_int)
self.assertEqual({'Content-Length': '5',
'Content-Type': 'text/html; charset=UTF-8'},
resp.headers)
self.assertEqual(b'stuff', resp.body)
self.assertEqual(2, swift.call_count)
self.assertEqual(('GET', '/v1/a/c/o'), swift.calls[1])
self.assertEqual(('GET', '/v1/a/c/o',
{'Host': 'localhost:80'}), # from swob
swift.calls_with_headers[1])
self.assertEqual(
'99', req.headers.get('X-Backend-Storage-Policy-Index'))
class TestFakeSwiftMultipleResponses(unittest.TestCase):
def test_register_response_is_forever(self):
swift = FakeSwift()
swift.register('GET', '/v1/a/c/o',
HTTPOk, {'X-Foo': 'Bar'}, b'stuff')
req = Request.blank('/v1/a/c/o')
resp = req.get_response(swift)
self.assertEqual(200, resp.status_int)
self.assertEqual('Bar', resp.headers['X-Foo'])
# you can get this response as much as you want
for i in range(10):
resp = req.get_response(swift)
self.assertEqual(200, resp.status_int)
self.assertEqual('Bar', resp.headers['X-Foo'])
def test_register_response_is_last_response_wins(self):
swift = FakeSwift()
swift.register('GET', '/v1/a/c/o',
HTTPOk, {'X-Foo': 'Bar'}, b'stuff')
req = Request.blank('/v1/a/c/o')
resp = req.get_response(swift)
self.assertEqual(200, resp.status_int)
self.assertEqual('Bar', resp.headers['X-Foo'])
swift.register('GET', '/v1/a/c/o',
HTTPOk, {'X-Foo': 'Baz'}, b'other')
resp = req.get_response(swift)
self.assertEqual(200, resp.status_int)
self.assertEqual('Baz', resp.headers['X-Foo'])
# you can get this new response as much as you want
for i in range(10):
resp = req.get_response(swift)
self.assertEqual(200, resp.status_int)
self.assertEqual('Baz', resp.headers['X-Foo'])
def test_register_next_response_is_last_response_wins(self):
swift = FakeSwift()
swift.register(
'GET', '/v1/a/c/o',
HTTPOk, {'X-Foo': 'Bar'}, b'stuff')
swift.register_next_response(
'GET', '/v1/a/c/o',
HTTPOk, {'X-Foo': 'Baz'}, b'other')
req = Request.blank('/v1/a/c/o')
resp = req.get_response(swift)
self.assertEqual(200, resp.status_int)
self.assertEqual('Bar', resp.headers['X-Foo'])
resp = req.get_response(swift)
self.assertEqual(200, resp.status_int)
self.assertEqual('Baz', resp.headers['X-Foo'])
# you can get this new response as much as you want
for i in range(10):
resp = req.get_response(swift)
self.assertEqual(200, resp.status_int)
self.assertEqual('Baz', resp.headers['X-Foo'])
def test_register_next_response_keeps_current_registered_response(self):
# we expect test authors will typically 'd register ALL their responses
# before you start calling FakeSwift
swift = FakeSwift()
swift.register(
'GET', '/v1/a/c/o',
HTTPOk, {'X-Foo': 'Bar'}, b'stuff')
req = Request.blank('/v1/a/c/o')
# we get the registered response, obviously
resp = req.get_response(swift)
self.assertEqual(200, resp.status_int)
self.assertEqual('Bar', resp.headers['X-Foo'])
# because before calling register_next_response, no resp are consumed
swift.register_next_response(
'GET', '/v1/a/c/o',
HTTPOk, {'X-Foo': 'Baz'}, b'other')
# so, this is the "current" response, not the *next* response
resp = req.get_response(swift)
self.assertEqual(200, resp.status_int)
self.assertEqual('Bar', resp.headers['X-Foo'])
# the *next* response is the next response
resp = req.get_response(swift)
self.assertEqual(200, resp.status_int)
self.assertEqual('Baz', resp.headers['X-Foo'])
def test_register_next_response_first(self):
# you can just use register_next_response
swift = FakeSwift()
swift.register_next_response(
'GET', '/v1/a/c/o',
HTTPOk, {'X-Foo': 'Bar'}, b'stuff')
swift.register_next_response(
'GET', '/v1/a/c/o',
HTTPOk, {'X-Foo': 'Baz'}, b'other')
req = Request.blank('/v1/a/c/o')
# it works just like you'd called register
resp = req.get_response(swift)
self.assertEqual(200, resp.status_int)
self.assertEqual('Bar', resp.headers['X-Foo'])
resp = req.get_response(swift)
self.assertEqual(200, resp.status_int)
self.assertEqual('Baz', resp.headers['X-Foo'])
# you can get this new response as much as you want
for i in range(10):
resp = req.get_response(swift)
self.assertEqual(200, resp.status_int)
self.assertEqual('Baz', resp.headers['X-Foo'])
def test_register_resets(self):
swift = FakeSwift()
swift.register_next_response(
'GET', '/v1/a/c/o',
HTTPOk, {'X-Foo': 'Bar'}, b'stuff')
req = Request.blank('/v1/a/c/o')
resp = req.get_response(swift)
self.assertEqual(200, resp.status_int)
self.assertEqual('Bar', resp.headers['X-Foo'])
# you can get this response as much as you want
for i in range(10):
resp = req.get_response(swift)
self.assertEqual(200, resp.status_int)
self.assertEqual('Bar', resp.headers['X-Foo'])
# if you call register mid test you immediately reset the resp
swift.register(
'GET', '/v1/a/c/o',
HTTPOk, {'X-Foo': 'Baz'}, b'other')
resp = req.get_response(swift)
self.assertEqual(200, resp.status_int)
self.assertEqual('Baz', resp.headers['X-Foo'])
# you can get this new response as much as you want
for i in range(10):
resp = req.get_response(swift)
self.assertEqual(200, resp.status_int)
self.assertEqual('Baz', resp.headers['X-Foo'])
class TestFakeSwiftStickyHeaders(unittest.TestCase):
def setUp(self):
self.swift = FakeSwift()
self.path = '/v1/AUTH_test/bucket'
def _check_headers(self, method, path, exp_headers):
captured_headers = {}
def start_response(status, resp_headers):
self.assertEqual(status, '200 OK')
captured_headers.update(resp_headers)
env = {'REQUEST_METHOD': method, 'PATH_INFO': path}
body_iter = self.swift(env, start_response)
b''.join(body_iter)
captured_headers.pop('Content-Type')
self.assertEqual(exp_headers, captured_headers)
def test_sticky_headers(self):
sticky_headers = HeaderKeyDict({
sysmeta_header('container', 'acl'): 'test',
'x-container-meta-foo': 'bar',
})
self.swift.update_sticky_response_headers(self.path, sticky_headers)
# register a response for this path with no headers
self.swift.register('GET', self.path, HTTPOk, {}, None)
self._check_headers('HEAD', self.path, sticky_headers)
self._check_headers('GET', self.path, sticky_headers)
# sticky headers are not applied to PUT, POST, DELETE
self.swift.register('PUT', self.path, HTTPOk, {}, None)
self._check_headers('PUT', self.path, {})
self.swift.register('POST', self.path, HTTPOk, {}, None)
self._check_headers('POST', self.path, {})
self.swift.register('DELETE', self.path, HTTPOk, {}, None)
self._check_headers('DELETE', self.path, {})
def test_sticky_headers_match_path(self):
other_path = self.path + '-other'
sticky_headers = HeaderKeyDict({
sysmeta_header('container', 'acl'): 'test',
'x-container-meta-foo': 'bar',
})
sticky_headers_other = HeaderKeyDict({
'x-container-meta-foo': 'other',
})
self.swift.update_sticky_response_headers(self.path, sticky_headers)
self.swift.update_sticky_response_headers(other_path,
sticky_headers_other)
self.swift.register('GET', self.path, HTTPOk, {}, None)
self.swift.register('GET', other_path, HTTPOk, {}, None)
self._check_headers('HEAD', self.path, sticky_headers)
self._check_headers('GET', other_path, sticky_headers_other)
def test_sticky_headers_update(self):
sticky_headers = HeaderKeyDict({
sysmeta_header('container', 'acl'): 'test',
'x-container-meta-foo': 'bar'
})
exp_headers = sticky_headers.copy()
self.swift.update_sticky_response_headers(self.path, sticky_headers)
self.swift.register('HEAD', self.path, HTTPOk, {}, None)
self._check_headers('HEAD', self.path, exp_headers)
# check that FakeSwift made a *copy*
sticky_headers['x-container-meta-foo'] = 'changed'
self._check_headers('HEAD', self.path, exp_headers)
# check existing are updated not replaced
sticky_headers = HeaderKeyDict({
sysmeta_header('container', 'acl'): 'test-modified',
'x-container-meta-bar': 'foo'
})
exp_headers.update(sticky_headers)
self.swift.update_sticky_response_headers(self.path, sticky_headers)
self._check_headers('HEAD', self.path, exp_headers)
def test_sticky_headers_add_to_response_headers(self):
sticky_headers = HeaderKeyDict({
'x-container-meta-foo': 'bar',
})
self.swift.update_sticky_response_headers(self.path, sticky_headers)
# register a response with another header
self.swift.register('HEAD', self.path, HTTPOk, {
'x-backend-storage-policy-index': '1',
}, None)
self._check_headers('HEAD', self.path, HeaderKeyDict({
'x-container-meta-foo': 'bar',
'x-backend-storage-policy-index': '1',
}))
def test_sticky_headers_overwritten_by_response_header(self):
sticky_headers = HeaderKeyDict({
'x-container-meta-foo': 'bar',
'x-backend-storage-policy-index': '0',
})
self.swift.update_sticky_response_headers(self.path, sticky_headers)
# register a response with a different value for a sticky header
self.swift.register('HEAD', self.path, HTTPOk, {
'x-container-meta-foo': 'different',
}, None)
self._check_headers('HEAD', self.path, HeaderKeyDict({
'x-container-meta-foo': 'different',
'x-backend-storage-policy-index': '0',
}))
if __name__ == '__main__':
unittest.main()