# Copyright (c) 2017 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 json import unittest from swift.common.swob import Request, HTTPOk from swift.common.middleware import listing_formats from test.unit.common.middleware.helpers import FakeSwift class TestListingFormats(unittest.TestCase): def setUp(self): self.fake_swift = FakeSwift() self.app = listing_formats.ListingFilter(self.fake_swift) self.fake_account_listing = json.dumps([ {'name': 'bar', 'bytes': 0, 'count': 0, 'last_modified': '1970-01-01T00:00:00.000000'}, {'subdir': 'foo_'}, ]) self.fake_container_listing = json.dumps([ {'name': 'bar', 'hash': 'etag', 'bytes': 0, 'content_type': 'text/plain', 'last_modified': '1970-01-01T00:00:00.000000'}, {'subdir': 'foo/'}, ]) def test_valid_account(self): self.fake_swift.register('GET', '/v1/a', HTTPOk, { 'Content-Length': str(len(self.fake_account_listing)), 'Content-Type': 'application/json'}, self.fake_account_listing) req = Request.blank('/v1/a') resp = req.get_response(self.app) self.assertEqual(resp.body, 'bar\nfoo_\n') self.assertEqual(resp.headers['Content-Type'], 'text/plain; charset=utf-8') self.assertEqual(self.fake_swift.calls[-1], ( 'GET', '/v1/a?format=json')) req = Request.blank('/v1/a?format=txt') resp = req.get_response(self.app) self.assertEqual(resp.body, 'bar\nfoo_\n') self.assertEqual(resp.headers['Content-Type'], 'text/plain; charset=utf-8') self.assertEqual(self.fake_swift.calls[-1], ( 'GET', '/v1/a?format=json')) req = Request.blank('/v1/a?format=json') resp = req.get_response(self.app) self.assertEqual(resp.body, self.fake_account_listing) self.assertEqual(resp.headers['Content-Type'], 'application/json; charset=utf-8') self.assertEqual(self.fake_swift.calls[-1], ( 'GET', '/v1/a?format=json')) req = Request.blank('/v1/a?format=xml') resp = req.get_response(self.app) self.assertEqual(resp.body.split('\n'), [ '', '', 'bar00' '1970-01-01T00:00:00.000000' '', '', '', ]) self.assertEqual(resp.headers['Content-Type'], 'application/xml; charset=utf-8') self.assertEqual(self.fake_swift.calls[-1], ( 'GET', '/v1/a?format=json')) def test_valid_container(self): self.fake_swift.register('GET', '/v1/a/c', HTTPOk, { 'Content-Length': str(len(self.fake_container_listing)), 'Content-Type': 'application/json'}, self.fake_container_listing) req = Request.blank('/v1/a/c') resp = req.get_response(self.app) self.assertEqual(resp.body, 'bar\nfoo/\n') self.assertEqual(resp.headers['Content-Type'], 'text/plain; charset=utf-8') self.assertEqual(self.fake_swift.calls[-1], ( 'GET', '/v1/a/c?format=json')) req = Request.blank('/v1/a/c?format=txt') resp = req.get_response(self.app) self.assertEqual(resp.body, 'bar\nfoo/\n') self.assertEqual(resp.headers['Content-Type'], 'text/plain; charset=utf-8') self.assertEqual(self.fake_swift.calls[-1], ( 'GET', '/v1/a/c?format=json')) req = Request.blank('/v1/a/c?format=json') resp = req.get_response(self.app) self.assertEqual(resp.body, self.fake_container_listing) self.assertEqual(resp.headers['Content-Type'], 'application/json; charset=utf-8') self.assertEqual(self.fake_swift.calls[-1], ( 'GET', '/v1/a/c?format=json')) req = Request.blank('/v1/a/c?format=xml') resp = req.get_response(self.app) self.assertEqual( resp.body, '\n' '' 'baretag0' 'text/plain' '1970-01-01T00:00:00.000000' '' 'foo/' '' ) self.assertEqual(resp.headers['Content-Type'], 'application/xml; charset=utf-8') self.assertEqual(self.fake_swift.calls[-1], ( 'GET', '/v1/a/c?format=json')) def test_blank_account(self): self.fake_swift.register('GET', '/v1/a', HTTPOk, { 'Content-Length': '2', 'Content-Type': 'application/json'}, '[]') req = Request.blank('/v1/a') resp = req.get_response(self.app) self.assertEqual(resp.status, '204 No Content') self.assertEqual(resp.body, '') self.assertEqual(resp.headers['Content-Type'], 'text/plain; charset=utf-8') self.assertEqual(self.fake_swift.calls[-1], ( 'GET', '/v1/a?format=json')) req = Request.blank('/v1/a?format=txt') resp = req.get_response(self.app) self.assertEqual(resp.status, '204 No Content') self.assertEqual(resp.body, '') self.assertEqual(resp.headers['Content-Type'], 'text/plain; charset=utf-8') self.assertEqual(self.fake_swift.calls[-1], ( 'GET', '/v1/a?format=json')) req = Request.blank('/v1/a?format=json') resp = req.get_response(self.app) self.assertEqual(resp.status, '200 OK') self.assertEqual(resp.body, '[]') self.assertEqual(resp.headers['Content-Type'], 'application/json; charset=utf-8') self.assertEqual(self.fake_swift.calls[-1], ( 'GET', '/v1/a?format=json')) req = Request.blank('/v1/a?format=xml') resp = req.get_response(self.app) self.assertEqual(resp.status, '200 OK') self.assertEqual(resp.body.split('\n'), [ '', '', '', ]) self.assertEqual(resp.headers['Content-Type'], 'application/xml; charset=utf-8') self.assertEqual(self.fake_swift.calls[-1], ( 'GET', '/v1/a?format=json')) def test_blank_container(self): self.fake_swift.register('GET', '/v1/a/c', HTTPOk, { 'Content-Length': '2', 'Content-Type': 'application/json'}, '[]') req = Request.blank('/v1/a/c') resp = req.get_response(self.app) self.assertEqual(resp.status, '204 No Content') self.assertEqual(resp.body, '') self.assertEqual(resp.headers['Content-Type'], 'text/plain; charset=utf-8') self.assertEqual(self.fake_swift.calls[-1], ( 'GET', '/v1/a/c?format=json')) req = Request.blank('/v1/a/c?format=txt') resp = req.get_response(self.app) self.assertEqual(resp.status, '204 No Content') self.assertEqual(resp.body, '') self.assertEqual(resp.headers['Content-Type'], 'text/plain; charset=utf-8') self.assertEqual(self.fake_swift.calls[-1], ( 'GET', '/v1/a/c?format=json')) req = Request.blank('/v1/a/c?format=json') resp = req.get_response(self.app) self.assertEqual(resp.status, '200 OK') self.assertEqual(resp.body, '[]') self.assertEqual(resp.headers['Content-Type'], 'application/json; charset=utf-8') self.assertEqual(self.fake_swift.calls[-1], ( 'GET', '/v1/a/c?format=json')) req = Request.blank('/v1/a/c?format=xml') resp = req.get_response(self.app) self.assertEqual(resp.status, '200 OK') self.assertEqual(resp.body.split('\n'), [ '', '', ]) self.assertEqual(resp.headers['Content-Type'], 'application/xml; charset=utf-8') self.assertEqual(self.fake_swift.calls[-1], ( 'GET', '/v1/a/c?format=json')) def test_pass_through(self): def do_test(path): self.fake_swift.register( 'GET', path, HTTPOk, { 'Content-Length': str(len(self.fake_container_listing)), 'Content-Type': 'application/json'}, self.fake_container_listing) req = Request.blank(path + '?format=xml') resp = req.get_response(self.app) self.assertEqual(resp.body, self.fake_container_listing) self.assertEqual(resp.headers['Content-Type'], 'application/json') self.assertEqual(self.fake_swift.calls[-1], ( 'GET', path + '?format=xml')) # query param is unchanged do_test('/') do_test('/v1') do_test('/auth/v1.0') do_test('/v1/a/c/o') def test_static_web_not_json(self): body = 'doesnt matter' self.fake_swift.register( 'GET', '/v1/staticweb/not-json', HTTPOk, {'Content-Length': str(len(body)), 'Content-Type': 'text/plain'}, body) resp = Request.blank('/v1/staticweb/not-json').get_response(self.app) self.assertEqual(resp.body, body) self.assertEqual(resp.headers['Content-Type'], 'text/plain') # We *did* try, though self.assertEqual(self.fake_swift.calls[-1], ( 'GET', '/v1/staticweb/not-json?format=json')) # TODO: add a similar test that has *no* content-type # FakeSwift seems to make this hard to do def test_static_web_not_really_json(self): body = 'raises ValueError' self.fake_swift.register( 'GET', '/v1/staticweb/not-json', HTTPOk, {'Content-Length': str(len(body)), 'Content-Type': 'application/json'}, body) resp = Request.blank('/v1/staticweb/not-json').get_response(self.app) self.assertEqual(resp.body, body) self.assertEqual(resp.headers['Content-Type'], 'application/json') self.assertEqual(self.fake_swift.calls[-1], ( 'GET', '/v1/staticweb/not-json?format=json')) def test_static_web_pretend_to_be_giant_json(self): body = json.dumps(self.fake_container_listing * 1000000) self.assertGreater( # sanity len(body), listing_formats.MAX_CONTAINER_LISTING_CONTENT_LENGTH) self.fake_swift.register( 'GET', '/v1/staticweb/not-json', HTTPOk, {'Content-Type': 'application/json'}, body) resp = Request.blank('/v1/staticweb/not-json').get_response(self.app) self.assertEqual(resp.body, body) self.assertEqual(resp.headers['Content-Type'], 'application/json') self.assertEqual(self.fake_swift.calls[-1], ( 'GET', '/v1/staticweb/not-json?format=json')) # TODO: add a similar test for chunked transfers # (staticweb referencing a DLO that doesn't fit in a single listing?) def test_static_web_bad_json(self): def do_test(body_obj): body = json.dumps(body_obj) self.fake_swift.register( 'GET', '/v1/staticweb/bad-json', HTTPOk, {'Content-Length': str(len(body)), 'Content-Type': 'application/json'}, body) def do_sub_test(path): resp = Request.blank(path).get_response(self.app) self.assertEqual(resp.body, body) # NB: no charset is added; we pass through whatever we got self.assertEqual(resp.headers['Content-Type'], 'application/json') self.assertEqual(self.fake_swift.calls[-1], ( 'GET', '/v1/staticweb/bad-json?format=json')) do_sub_test('/v1/staticweb/bad-json') do_sub_test('/v1/staticweb/bad-json?format=txt') do_sub_test('/v1/staticweb/bad-json?format=xml') do_sub_test('/v1/staticweb/bad-json?format=json') do_test({}) do_test({'non-empty': 'hash'}) do_test(None) do_test(0) do_test('some string') do_test([None]) do_test([0]) do_test(['some string']) def test_static_web_bad_but_not_terrible_json(self): body = json.dumps([{'no name': 'nor subdir'}]) self.fake_swift.register( 'GET', '/v1/staticweb/bad-json', HTTPOk, {'Content-Length': str(len(body)), 'Content-Type': 'application/json'}, body) def do_test(path, expect_charset=False): resp = Request.blank(path).get_response(self.app) self.assertEqual(resp.body, body) if expect_charset: self.assertEqual(resp.headers['Content-Type'], 'application/json; charset=utf-8') else: self.assertEqual(resp.headers['Content-Type'], 'application/json') self.assertEqual(self.fake_swift.calls[-1], ( 'GET', '/v1/staticweb/bad-json?format=json')) do_test('/v1/staticweb/bad-json') do_test('/v1/staticweb/bad-json?format=txt') do_test('/v1/staticweb/bad-json?format=xml') # The response we get is *just close enough* to being valid that we # assume it is and slap on the missing charset. If you set up staticweb # to serve back such responses, your clients are already hosed. do_test('/v1/staticweb/bad-json?format=json', expect_charset=True)