Add parametric tests of Swift account API
Add and reorganize positive tests of Swift account API from the viewpoint of
completeness of HTTP methods and query parameters.
Change-Id: I648011acd142171d88c33644cb26078146120ce4
diff --git a/tempest/api/object_storage/test_account_services.py b/tempest/api/object_storage/test_account_services.py
index 5456768..245408d 100644
--- a/tempest/api/object_storage/test_account_services.py
+++ b/tempest/api/object_storage/test_account_services.py
@@ -16,10 +16,15 @@
import random
from tempest.api.object_storage import base
+from tempest import clients
from tempest.common import custom_matchers
from tempest.common.utils import data_utils
+from tempest import config
+from tempest import exceptions
from tempest import test
+CONF = config.CONF
+
class AccountTest(base.BaseObjectTest):
@classmethod
@@ -35,20 +40,109 @@
@classmethod
def tearDownClass(cls):
cls.delete_containers(cls.containers)
+ cls.data.teardown_all()
super(AccountTest, cls).tearDownClass()
@test.attr(type='smoke')
def test_list_containers(self):
# list of all containers should not be empty
- params = {'format': 'json'}
- resp, container_list = \
- self.account_client.list_account_containers(params=params)
+ resp, container_list = self.account_client.list_account_containers()
self.assertHeaders(resp, 'Account', 'GET')
self.assertIsNotNone(container_list)
- container_names = [c['name'] for c in container_list]
for container_name in self.containers:
- self.assertIn(container_name, container_names)
+ self.assertIn(container_name, container_list)
+
+ @test.attr(type='smoke')
+ def test_list_no_containers(self):
+ # List request to empty account
+
+ # To test listing no containers, create new user other than
+ # the base user of this instance.
+ self.data.setup_test_user()
+
+ os_test_user = clients.Manager(
+ self.data.test_user,
+ self.data.test_password,
+ self.data.test_tenant)
+
+ # Retrieve the id of an operator role of object storage
+ test_role_id = None
+ swift_role = CONF.object_storage.operator_role
+ try:
+ _, roles = self.os_admin.identity_client.list_roles()
+ test_role_id = next(r['id'] for r in roles if r['name']
+ == swift_role)
+ except StopIteration:
+ msg = "%s role found" % swift_role
+ raise exceptions.NotFound(msg)
+
+ # Retrieve the test_user id
+ _, users = self.os_admin.identity_client.get_users()
+ test_user_id = next(usr['id'] for usr in users if usr['name']
+ == self.data.test_user)
+
+ # Retrieve the test_tenant id
+ _, tenants = self.os_admin.identity_client.list_tenants()
+ test_tenant_id = next(tnt['id'] for tnt in tenants if tnt['name']
+ == self.data.test_tenant)
+
+ # Assign the newly created user the appropriate operator role
+ self.os_admin.identity_client.assign_user_role(
+ test_tenant_id,
+ test_user_id,
+ test_role_id)
+
+ resp, container_list = \
+ os_test_user.account_client.list_account_containers()
+ self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
+
+ # When sending a request to an account which has not received a PUT
+ # container request, the response does not contain 'accept-ranges'
+ # header. This is a special case, therefore the existence of response
+ # headers is checked without custom matcher.
+ self.assertIn('content-length', resp)
+ self.assertIn('x-timestamp', resp)
+ self.assertIn('x-account-bytes-used', resp)
+ self.assertIn('x-account-container-count', resp)
+ self.assertIn('x-account-object-count', resp)
+ self.assertIn('content-type', resp)
+ self.assertIn('x-trans-id', resp)
+ self.assertIn('date', resp)
+
+ # Check only the format of common headers with custom matcher
+ self.assertThat(resp, custom_matchers.AreAllWellFormatted())
+
+ self.assertEqual(len(container_list), 0)
+
+ @test.attr(type='smoke')
+ def test_list_containers_with_format_json(self):
+ # list containers setting format parameter to 'json'
+ params = {'format': 'json'}
+ resp, container_list = self.account_client.list_account_containers(
+ params=params)
+ self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
+ self.assertHeaders(resp, 'Account', 'GET')
+ self.assertIsNotNone(container_list)
+ self.assertTrue([c['name'] for c in container_list])
+ self.assertTrue([c['count'] for c in container_list])
+ self.assertTrue([c['bytes'] for c in container_list])
+
+ @test.attr(type='smoke')
+ def test_list_containers_with_format_xml(self):
+ # list containers setting format parameter to 'xml'
+ params = {'format': 'xml'}
+ resp, container_list = self.account_client.list_account_containers(
+ params=params)
+ self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
+ self.assertHeaders(resp, 'Account', 'GET')
+ self.assertIsNotNone(container_list)
+ self.assertEqual(container_list.tag, 'account')
+ self.assertTrue('name' in container_list.keys())
+ self.assertEqual(container_list.find(".//container").tag, 'container')
+ self.assertEqual(container_list.find(".//name").tag, 'name')
+ self.assertEqual(container_list.find(".//count").tag, 'count')
+ self.assertEqual(container_list.find(".//bytes").tag, 'bytes')
@test.attr(type='smoke')
def test_list_extensions(self):
@@ -107,6 +201,17 @@
self.assertEqual(len(container_list), self.containers_count / 2)
@test.attr(type='smoke')
+ def test_list_containers_with_marker_and_end_marker(self):
+ # list containers combining marker and end_marker param
+ params = {'marker': self.containers[0],
+ 'end_marker': self.containers[self.containers_count - 1]}
+ resp, container_list = self.account_client.list_account_containers(
+ params=params)
+ self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
+ self.assertHeaders(resp, 'Account', 'GET')
+ self.assertEqual(len(container_list), self.containers_count - 2)
+
+ @test.attr(type='smoke')
def test_list_containers_with_limit_and_marker(self):
# list containers combining marker and limit param
# result are always limitated by the limit whatever the marker
@@ -121,34 +226,125 @@
self.assertTrue(len(container_list) <= limit, str(container_list))
@test.attr(type='smoke')
- def test_list_account_metadata(self):
- # list all account metadata
- resp, metadata = self.account_client.list_account_metadata()
+ def test_list_containers_with_limit_and_end_marker(self):
+ # list containers combining limit and end_marker param
+ limit = random.randint(1, self.containers_count)
+ params = {'limit': limit,
+ 'end_marker': self.containers[self.containers_count / 2]}
+ resp, container_list = self.account_client.list_account_containers(
+ params=params)
self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
- self.assertHeaders(resp, 'Account', 'HEAD')
+ self.assertHeaders(resp, 'Account', 'GET')
+ self.assertEqual(len(container_list),
+ min(limit, self.containers_count / 2))
@test.attr(type='smoke')
- def test_create_and_delete_account_metadata(self):
- header = 'test-account-meta'
- data = 'Meta!'
+ def test_list_containers_with_limit_and_marker_and_end_marker(self):
+ # list containers combining limit, marker and end_marker param
+ limit = random.randint(1, self.containers_count)
+ params = {'limit': limit,
+ 'marker': self.containers[0],
+ 'end_marker': self.containers[self.containers_count - 1]}
+ resp, container_list = self.account_client.list_account_containers(
+ params=params)
+ self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
+ self.assertHeaders(resp, 'Account', 'GET')
+ self.assertEqual(len(container_list),
+ min(limit, self.containers_count - 2))
+
+ @test.attr(type='smoke')
+ def test_list_account_metadata(self):
+ # list all account metadata
+
+ # set metadata to account
+ metadata = {'test-account-meta1': 'Meta1',
+ 'test-account-meta2': 'Meta2'}
+ resp, _ = self.account_client.create_account_metadata(metadata)
+
+ resp, _ = self.account_client.list_account_metadata()
+ self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
+ self.assertHeaders(resp, 'Account', 'HEAD')
+ self.assertIn('x-account-meta-test-account-meta1', resp)
+ self.assertIn('x-account-meta-test-account-meta2', resp)
+ self.account_client.delete_account_metadata(metadata)
+
+ @test.attr(type='smoke')
+ def test_list_no_account_metadata(self):
+ # list no account metadata
+ resp, _ = self.account_client.list_account_metadata()
+ self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
+ self.assertHeaders(resp, 'Account', 'HEAD')
+ self.assertNotIn('x-account-meta-', str(resp))
+
+ @test.attr(type='smoke')
+ def test_update_account_metadata_with_create_metadata(self):
# add metadata to account
- resp, _ = self.account_client.create_account_metadata(
- metadata={header: data})
+ metadata = {'test-account-meta1': 'Meta1'}
+ resp, _ = self.account_client.create_account_metadata(metadata)
self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
self.assertHeaders(resp, 'Account', 'POST')
- resp, _ = self.account_client.list_account_metadata()
- self.assertHeaders(resp, 'Account', 'HEAD')
+ resp, body = self.account_client.list_account_metadata()
+ self.assertIn('x-account-meta-test-account-meta1', resp)
+ self.assertEqual(resp['x-account-meta-test-account-meta1'],
+ metadata['test-account-meta1'])
- self.assertIn('x-account-meta-' + header, resp)
- self.assertEqual(resp['x-account-meta-' + header], data)
+ self.account_client.delete_account_metadata(metadata)
+ @test.attr(type='smoke')
+ def test_update_account_metadata_with_delete_matadata(self):
# delete metadata from account
- resp, _ = \
- self.account_client.delete_account_metadata(metadata=[header])
+ metadata = {'test-account-meta1': 'Meta1'}
+ self.account_client.create_account_metadata(metadata)
+ resp, _ = self.account_client.delete_account_metadata(metadata)
self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
self.assertHeaders(resp, 'Account', 'POST')
resp, _ = self.account_client.list_account_metadata()
- self.assertHeaders(resp, 'Account', 'HEAD')
- self.assertNotIn('x-account-meta-' + header, resp)
+ self.assertNotIn('x-account-meta-test-account-meta1', resp)
+
+ @test.attr(type='smoke')
+ def test_update_account_metadata_with_create_matadata_key(self):
+ # if the value of metadata is not set, the metadata is not
+ # registered at a server
+ metadata = {'test-account-meta1': ''}
+ resp, _ = self.account_client.create_account_metadata(metadata)
+ self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
+ self.assertHeaders(resp, 'Account', 'POST')
+
+ resp, _ = self.account_client.list_account_metadata()
+ self.assertNotIn('x-account-meta-test-account-meta1', resp)
+
+ @test.attr(type='smoke')
+ def test_update_account_metadata_with_delete_matadata_key(self):
+ # Although the value of metadata is not set, the feature of
+ # deleting metadata is valid
+ metadata_1 = {'test-account-meta1': 'Meta1'}
+ self.account_client.create_account_metadata(metadata_1)
+ metadata_2 = {'test-account-meta1': ''}
+ resp, _ = self.account_client.delete_account_metadata(metadata_2)
+ self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
+ self.assertHeaders(resp, 'Account', 'POST')
+
+ resp, _ = self.account_client.list_account_metadata()
+ self.assertNotIn('x-account-meta-test-account-meta1', resp)
+
+ @test.attr(type='smoke')
+ def test_update_account_metadata_with_create_and_delete_metadata(self):
+ # Send a request adding and deleting metadata requests simultaneously
+ metadata_1 = {'test-account-meta1': 'Meta1'}
+ self.account_client.create_account_metadata(metadata_1)
+ metadata_2 = {'test-account-meta2': 'Meta2'}
+ resp, body = self.account_client.create_and_delete_account_metadata(
+ metadata_2,
+ metadata_1)
+ self.assertIn(int(resp['status']), test.HTTP_SUCCESS)
+ self.assertHeaders(resp, 'Account', 'POST')
+
+ resp, _ = self.account_client.list_account_metadata()
+ self.assertNotIn('x-account-meta-test-account-meta1', resp)
+ self.assertIn('x-account-meta-test-account-meta2', resp)
+ self.assertEqual(resp['x-account-meta-test-account-meta2'],
+ metadata_2['test-account-meta2'])
+
+ self.account_client.delete_account_metadata(metadata_2)
diff --git a/tempest/api/object_storage/test_object_temp_url.py b/tempest/api/object_storage/test_object_temp_url.py
index 47c270e..c597255 100644
--- a/tempest/api/object_storage/test_object_temp_url.py
+++ b/tempest/api/object_storage/test_object_temp_url.py
@@ -40,9 +40,9 @@
# update account metadata
cls.key = 'Meta'
cls.metadatas = []
- cls.metadata = {'Temp-URL-Key': cls.key}
- cls.metadatas.append(cls.metadata)
- cls.account_client.create_account_metadata(metadata=cls.metadata)
+ metadata = {'Temp-URL-Key': cls.key}
+ cls.metadatas.append(metadata)
+ cls.account_client.create_account_metadata(metadata=metadata)
# create an object
cls.object_name = data_utils.rand_name(name='ObjectTemp')
@@ -53,7 +53,7 @@
@classmethod
def tearDownClass(cls):
- for metadata in cls.metadata:
+ for metadata in cls.metadatas:
cls.account_client.delete_account_metadata(
metadata=metadata)
diff --git a/tempest/services/object_storage/account_client.py b/tempest/services/object_storage/account_client.py
index efac5f5..be314cc 100644
--- a/tempest/services/object_storage/account_client.py
+++ b/tempest/services/object_storage/account_client.py
@@ -20,6 +20,7 @@
from tempest.common.rest_client import RestClient
from tempest import config
from tempest import exceptions
+from xml.etree import ElementTree as etree
CONF = config.CONF
@@ -28,7 +29,6 @@
def __init__(self, auth_provider):
super(AccountClient, self).__init__(auth_provider)
self.service = CONF.object_storage.catalog_type
- self.format = 'json'
def create_account(self, data=None,
params=None,
@@ -87,7 +87,25 @@
headers = {}
for item in metadata:
- headers[metadata_prefix + item] = 'x'
+ headers[metadata_prefix + item] = metadata[item]
+ resp, body = self.post('', headers=headers, body=None)
+ return resp, body
+
+ def create_and_delete_account_metadata(
+ self,
+ create_metadata=None,
+ delete_metadata=None,
+ create_metadata_prefix='X-Account-Meta-',
+ delete_metadata_prefix='X-Remove-Account-Meta-'):
+ """
+ Creates and deletes an account metadata entry.
+ """
+ headers = {}
+ for key in create_metadata:
+ headers[create_metadata_prefix + key] = create_metadata[key]
+ for key in delete_metadata:
+ headers[delete_metadata_prefix + key] = delete_metadata[key]
+
resp, body = self.post('', headers=headers, body=None)
return resp, body
@@ -112,24 +130,23 @@
response.
DEFAULT: Python-List returned in response body
"""
+ url = '?%s' % urllib.urlencode(params) if params else ''
- if params:
- if 'format' not in params:
- params['format'] = self.format
- else:
- params = {'format': self.format}
-
- url = '?' + urllib.urlencode(params)
- resp, body = self.get(url)
-
+ resp, body = self.get(url, headers={})
if params and params.get('format') == 'json':
body = json.loads(body)
+ elif params and params.get('format') == 'xml':
+ body = etree.fromstring(body)
+ else:
+ body = body.strip().splitlines()
return resp, body
def list_extensions(self):
self.skip_path()
- resp, body = self.get('info')
- self.reset_path()
+ try:
+ resp, body = self.get('info')
+ finally:
+ self.reset_path()
body = json.loads(body)
return resp, body