Merge "Refactor System Tests"

This commit is contained in:
Jenkins 2013-08-29 18:29:40 +00:00 committed by Gerrit Code Review
commit 014039f03b
26 changed files with 1047 additions and 1765 deletions

View File

@ -1,6 +1,5 @@
[auth]
auth_on = true
#auth endpoint - url to get the auth token
auth_on = false
url = https://identity.xxx.xxxx.com/v2.0/tokens
username = user
password = secret

View File

@ -0,0 +1,49 @@
Marconi Functional Tests
====================
Marconi's functional tests treat Marconi as a black box. In other
words, the API calls attempt to simulate an actual user. Unlike unit tests,
the functional tests do not use mockendpoints.
Running the Functional Tests
------------------------
#. Setup a Marconi server. Refer to the Marconi `README`_ on
how to run Marconi locally, or simply use an existing server.
#. Install functional tests dependencies. ::
pip install -r functional-test-requirements.txt
#. cd to the marconi/tests/functional directory
#. Copy marconi/tests/etc/functional-tests.conf-sample to one of the following locations::
~/.marconi/functional-tests.conf
/etc/marconi/functional-tests.conf
#. Update the config file to point to the Marconi server you want to run
the tests against
#. If leaving keystone auth enabled, update system-tests.conf with a
valid set of credentials.
#. Now, to run the sytem tests, simply use the nosetests commands,
from the marconi/tests/functional directory. e.g.:
Run all test suites: ::
nosetests -v
Adding New Tests
----------------
#. Add test case to an appropriate test case file: ::
queue/test_queue.py
messages/test_messages.py
claim/test_claims.py
.. _README : https://github.com/stackforge/marconi/blob/master/README.rst
.. _requests : https://pypi.python.org/pypi/requests

View File

@ -0,0 +1,16 @@
# Copyright (c) 2013 Rackspace Hosting, Inc.
#
# 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.
"""Marconi Functional Tests"""

View File

@ -0,0 +1,235 @@
# Copyright (c) 2013 Rackspace, Inc.
#
# 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.
from marconi.tests.functional.util import base
from marconi.tests.functional.util import config
from marconi.tests.functional.util import helpers
from marconi.tests.functional.util import http
import ddt
import json
import uuid
@ddt.ddt
class TestClaims(base.FunctionalTestBase):
"""Tests for Claims."""
@classmethod
def setUpClass(cls):
"""Create Queue, Post Messages for Claim Tests."""
cls.cfg = config.Config()
cls.header = helpers.create_marconi_headers()
cls.headers_response_with_body = set(['location',
'content-type'])
def setUp(self):
super(TestClaims, self).setUp()
self.queue_url = self.cfg.base_url + '/queues/{}'.format(uuid.uuid1())
http.put(self.queue_url, self.header)
self.claim_url = self.queue_url + '/claims'
#Post Messages
url = self.queue_url + '/messages'
doc = helpers.get_message_body(messagecount=
self.cfg.message_paging_uplimit)
for i in range(25):
http.post(url, self.header, doc)
@ddt.data('', '?limit=2')
def test_claim_messages(self, url_appender):
"""Claim messages."""
if url_appender:
message_count = int(url_appender.split('?limit=')[1])
else:
message_count = 10
url = self.claim_url + url_appender
doc = '{"ttl": 300, "grace": 100}'
result = http.post(url, self.header, doc)
self.assertEqual(result.status_code, 201)
actual_message_count = len(result.json())
self.assertMessageCount(message_count, actual_message_count)
response_headers = set(result.headers.keys())
self.assertIsSubset(self.headers_response_with_body, response_headers)
test_claim_messages.tags = ['smoke', 'positive']
def test_query_claim(self):
"""Query Claim."""
url = self.claim_url + '?limit=1'
doc = '{"ttl": 300, "grace": 100}'
result = http.post(url, self.header, doc)
location = result.headers['Location']
url = self.cfg.base_server + location
result = http.get(url, self.header)
self.assertEqual(result.status_code, 200)
test_query_claim.tags = ['smoke', 'positive']
def test_claim_more_than_allowed(self):
"""Claim more than max allowed per request.
Marconi allows a maximum of 20 messages per claim.
"""
url = self.claim_url + '?limit=' + \
str(self.cfg.message_paging_uplimit + 1)
doc = '{"ttl": 300, "grace": 100}'
result = http.post(url, self.header, doc)
self.assertEqual(result.status_code, 400)
test_claim_more_than_allowed.tags = ['negative']
def test_claim_patch(self):
"""Update Claim."""
#Test Setup - Post Claim
doc = '{"ttl": 300, "grace": 400}'
result = http.post(self.claim_url, self.header, doc)
self.assertEqual(result.status_code, 201)
#Patch Claim
claim_location = result.headers['Location']
url = self.cfg.base_server + claim_location
doc_updated = '{"ttl": 300}'
result = http.patch(url, self.header, doc_updated)
self.assertEqual(result.status_code, 204)
#verify that the claim TTL is updated
result = http.get(url, self.header)
new_ttl = result.json()['ttl']
self.assertEqual(new_ttl, 300)
test_claim_patch.tags = ['smoke', 'positive']
def test_delete_claimed_message(self):
"""Delete message belonging to a Claim."""
#Test Setup - Post claim
doc = '{"ttl": 60, "grace": 60}'
result = http.post(self.claim_url, self.header, doc)
self.assertEqual(result.status_code, 201)
href_list = [result.json()[i]['href']
for i in range(len(result.json()))]
url_list = [self.cfg.base_server + href
for href in href_list]
#Delete Claimed Messages
for url in url_list:
result = http.delete(url, self.header)
self.assertEqual(result.status_code, 204)
test_delete_claimed_message.tags = ['smoke', 'positive']
def test_claim_release(self):
"""Release Claim."""
doc = '{"ttl": 300, "grace": 100}'
result = http.post(self.claim_url, self.header, doc)
self.assertEqual(result.status_code, 201)
#Extract claim location and construct the claim URL.
location = result.headers['Location']
url = self.cfg.base_server + location
#Release Claim.
result = http.delete(url, self.header)
self.assertEqual(result.status_code, 204)
test_claim_release.tags = ['smoke', 'positive']
@ddt.data(10000000000000000000, -100, 1, 59, 43201, -10000000000000000000)
def test_claim_invalid_ttl(self, ttl):
"""Post Claim with invalid TTL.
The request JSON body will have a TTL value
outside the allowed range.Allowed ttl values is
60 <= ttl <= 43200.
"""
doc = {"ttl": ttl, "grace": 100}
result = http.post(self.claim_url, self.header, json.dumps(doc))
self.assertEqual(result.status_code, 400)
test_claim_invalid_ttl.tags = ['negative']
@ddt.data(10000000000000000000, -100, 1, 59, 43201, -10000000000000000000)
def test_claim_invalid_grace(self, grace):
"""Post Claim with invalid grace.
The request JSON body will have a grace value
outside the allowed range.Allowed grace values is
60 <= grace <= 43200.
"""
doc = {"ttl": 100, "grace": grace}
result = http.post(self.claim_url, self.header, json.dumps(doc))
self.assertEqual(result.status_code, 400)
test_claim_invalid_grace.tags = ['negative']
@ddt.data(0, -100, 30, 10000000000000000000)
def test_claim_invalid_limit(self, grace):
"""Post Claim with invalid limit.
The request url will have a limit outside the allowed range.
Allowed limit values are 0 < limit <= 20(default max).
"""
doc = {"ttl": 100, "grace": grace}
result = http.post(self.claim_url, self.header, json.dumps(doc))
self.assertEqual(result.status_code, 400)
test_claim_invalid_limit.tags = ['negative']
@ddt.data(10000000000000000000, -100, 1, 59, 43201, -10000000000000000000)
def test_patch_claim_invalid_ttl(self, ttl):
"""Patch Claim with invalid TTL.
The request JSON body will have a TTL value
outside the allowed range.Allowed ttl values is
60 <= ttl <= 43200.
"""
doc = '{"ttl": 100, "grace": 100}'
result = http.post(self.claim_url, self.header, doc)
self.assertEqual(result.status_code, 201)
#Extract claim location and construct the claim URL.
location = result.headers['Location']
url = self.cfg.base_server + location
#Patch Claim.
doc = {"ttl": ttl}
result = http.patch(url, self.header, json.dumps(doc))
self.assertEqual(result.status_code, 400)
test_patch_claim_invalid_ttl.tags = ['negative']
def tearDown(self):
"""Delete Queue after Claim Test."""
super(TestClaims, self).tearDown()
http.delete(self.queue_url, self.header)

View File

@ -0,0 +1,292 @@
# Copyright (c) 2013 Rackspace, Inc.
#
# 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.
from marconi.tests.functional.util import base
from marconi.tests.functional.util import config
from marconi.tests.functional.util import helpers
from marconi.tests.functional.util import http
import ddt
import uuid
@ddt.ddt
class TestMessages(base.FunctionalTestBase):
"""Tests for Messages."""
@classmethod
def setUpClass(cls):
cls.cfg = config.Config()
cls.header = helpers.create_marconi_headers()
cls.headers_response_with_body = set(['location',
'content-type'])
def setUp(self):
super(TestMessages, self).setUp()
self.queue_url = self.cfg.base_url + '/queues/{}'.format(uuid.uuid1())
http.put(self.queue_url, self.header)
self.message_url = self.queue_url + '/messages'
def test_message_single_insert(self):
"""Insert Single Message into the Queue.
This test also verifies that claimed messages are
retuned (or not) depending on the include_claimed flag.
"""
doc = helpers.get_message_body(messagecount=1)
result = http.post(self.message_url, self.header, doc)
self.assertEqual(result.status_code, 201)
response_headers = set(result.headers.keys())
self.assertIsSubset(self.headers_response_with_body, response_headers)
# GET on posted message
href = result.json()['resources'][0]
url = self.cfg.base_server + href
result = http.get(url, self.header)
self.assertEqual(result.status_code, 200)
# Compare message metadata
result_body = result.json()['body']
posted_metadata = doc[0]['body']
self.assertEqual(result_body, posted_metadata)
# Post a claim & verify the include_claimed flag.
url = self.queue_url + '/claims'
doc = '{"ttl": 300, "grace": 100}'
result = http.post(url, self.header, doc)
self.assertEqual(result.status_code, 201)
url = self.message_url + '?include_claimed=true&echo=true'
result = http.get(url, self.header)
self.assertEqual(result.status_code, 200)
response_message_body = result.json()["messages"][0]["body"]
self.assertEqual(response_message_body, posted_metadata)
# By default, include_claimed = false
result = http.get(self.message_url, self.header)
self.assertEqual(result.status_code, 204)
test_message_single_insert.tags = ['smoke', 'positive']
def test_message_bulk_insert(self):
"""Bulk Insert Messages into the Queue."""
message_count = 10
doc = helpers.get_message_body(messagecount=message_count)
result = http.post(self.message_url, self.header, doc)
self.assertEqual(result.status_code, 201)
# GET on posted messages
location = result.headers['location']
url = self.cfg.base_server + location
result = http.get(url, self.header)
self.assertEqual(result.status_code, 200)
# Compare message metadata
result_body = [result.json()[i]['body']
for i in range(len(result.json()))]
result_body.sort()
posted_metadata = [doc[i]['body']
for i in range(message_count)]
posted_metadata.sort()
self.assertEqual(result_body, posted_metadata)
test_message_bulk_insert.tags = ['smoke', 'positive']
@ddt.data('', '&limit=5')
def test_get_message(self, url_param):
"""Get Messages."""
if url_param:
expected_msg_count = int(url_param.split('&limit=')[1])
else:
expected_msg_count = 10
# Test Setup
doc = helpers.get_message_body(messagecount=20)
result = http.post(self.message_url, self.header, doc)
self.assertEqual(result.status_code, 201)
url = self.message_url + '?echo=True' + url_param
#Follow the hrefs & perform GET, till the end of messages i.e. http 204
while result.status_code in [201, 200]:
result = http.get(url, self.header)
self.assertIn(result.status_code, [200, 204])
if result.status_code == 200:
actual_msg_count = len(result.json()['messages'])
self.assertMessageCount(actual_msg_count, expected_msg_count)
href = result.json()['links'][0]['href']
url = self.cfg.base_server + href
self.assertEqual(result.status_code, 204)
test_get_message.tags = ['smoke', 'positive']
def test_message_delete(self):
"""Delete Message."""
# Test Setup
doc = helpers.get_message_body(messagecount=1)
result = http.post(self.message_url, self.header, doc)
self.assertEqual(result.status_code, 201)
# Delete posted message
href = result.json()['resources'][0]
url = self.cfg.base_server + href
result = http.delete(url, self.header)
self.assertEqual(result.status_code, 204)
result = http.get(url, self.header)
self.assertEqual(result.status_code, 404)
test_message_delete.tags = ['smoke', 'positive']
def test_message_bulk_delete(self):
"""Bulk Delete Messages."""
doc = helpers.get_message_body(messagecount=10)
result = http.post(self.message_url, self.header, doc)
self.assertEqual(result.status_code, 201)
# Delete posted messages
location = result.headers['Location']
url = self.cfg.base_server + location
result = http.delete(url, self.header)
self.assertEqual(result.status_code, 204)
result = http.get(url, self.header)
self.assertEqual(result.status_code, 204)
test_message_bulk_delete.tags = ['smoke', 'positive']
def test_message_delete_nonexisting(self):
"""Delete non-existing Messages."""
url = self.message_url + '/non-existing'
result = http.delete(url, self.header)
self.assertEqual(result.status_code, 204)
test_message_delete_nonexisting.tags = ['negative']
def test_message_partial_delete(self):
"""Delete Messages will be partially successful."""
doc = helpers.get_message_body(messagecount=3)
result = http.post(self.message_url, self.header, doc)
self.assertEqual(result.status_code, 201)
# Delete posted message
location = result.headers['Location']
url = self.cfg.base_server + location
url += ',nonexisting'
result = http.delete(url, self.header)
self.assertEqual(result.status_code, 204)
test_message_partial_delete.tags = ['negative']
def test_message_partial_get(self):
"""Get Messages will be partially successful."""
doc = helpers.get_message_body(messagecount=3)
result = http.post(self.message_url, self.header, doc)
self.assertEqual(result.status_code, 201)
# Get posted message and a nonexisting message
location = result.headers['Location']
url = self.cfg.base_server + location
url += ',nonexisting'
result = http.get(url, self.header)
self.assertEqual(result.status_code, 200)
test_message_partial_get.tags = ['negative']
def test_message_bulk_insert_60(self):
"""Insert more than max allowed messages.
Marconi allows a maximum of 50 message per POST.
"""
doc = helpers.get_message_body(messagecount=60)
result = http.post(self.message_url, self.header, doc)
self.assertEqual(result.status_code, 400)
test_message_bulk_insert_60.tags = ['negative']
@ddt.data(10000000000000000000, -100, 0, 30, -10000000000000000000)
def test_message_get_invalid_limit(self, limit):
"""Get Messages with invalid value for limit.
Allowed values for limit are 0 < limit <= 20(configurable).
"""
url = self.message_url + '?limit=' + str(limit)
result = http.get(url, self.header)
self.assertEqual(result.status_code, 400)
test_message_get_invalid_limit.tags = ['negative']
def test_message_bulk_delete_negative(self):
"""Delete more messages than allowed in a single request.
By default, max messages that can be deleted in a single
request is 20.
"""
url = self.message_url + '?ids=' \
+ ','.join(str(i) for i in
range(self.cfg.message_paging_uplimit + 1))
result = http.delete(url, self.header)
self.assertEqual(result.status_code, 400)
test_message_bulk_delete_negative.tags = ['negative']
def test_message_bulk_get_negative(self):
"""GET more messages by id than allowed in a single request.
By default, max messages that can be fetched in a single
request is 20.
"""
url = self.message_url + '?ids=' \
+ ','.join(str(i) for i in
range(self.cfg.message_paging_uplimit + 1))
result = http.get(url, self.header)
self.assertEqual(result.status_code, 400)
test_message_bulk_get_negative.tags = ['negative']
def test_get_messages_malformed_marker(self):
"""Get messages with non-existing marker."""
url = self.message_url + '?marker=invalid'
result = http.get(url, self.header)
self.assertEqual(result.status_code, 204)
test_get_messages_malformed_marker.tags = ['negative']
def tearDown(self):
super(TestMessages, self).tearDown()
http.delete(self.queue_url, self.header)

View File

@ -0,0 +1,288 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2013 Rackspace, Inc.
#
# 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.
from marconi.tests.functional.util import base
from marconi.tests.functional.util import config
from marconi.tests.functional.util import helpers
from marconi.tests.functional.util import http
import copy
import ddt
import json
import uuid
@ddt.ddt
class TestInsertQueue(base.FunctionalTestBase):
"""Tests for Insert queue."""
@classmethod
def setUpClass(cls):
cls.cfg = config.Config()
cls.header = helpers.create_marconi_headers()
cls.headers_response_empty = set(['location'])
cls.headers_response_with_body = set(['content-location',
'content-type'])
@ddt.data('qtestqueue', 'TESTqueue', 'hyphen-name', '_undersore',
'i' * 64)
def test_insert_queue(self, queue_name):
"""Create Queue."""
self.url = self.cfg.base_url + '/queues/' + queue_name
result = http.put(self.url, self.header)
self.assertEqual(result.status_code, 201)
response_headers = set(result.headers.keys())
self.assertIsSubset(self.headers_response_empty, response_headers)
self.url = self.url + '/metadata'
result = http.get(self.url, self.header)
self.assertEqual(result.status_code, 200)
self.assertEqual(result.json(), {})
test_insert_queue.tags = ['positive', 'smoke']
@ddt.data('汉字漢字', '@$@^qw', 'i' * 65)
def test_insert_queue_invalid_name(self, queue_name):
"""Create Queue."""
self.url = self.cfg.base_url + '/queues/' + queue_name
result = http.put(self.url, self.header)
self.assertEqual(result.status_code, 400)
self.url = self.url + '/metadata'
result = http.get(self.url, self.header)
self.assertEqual(result.status_code, 404)
test_insert_queue_invalid_name.tags = ['negative']
def test_insert_queue_invalid_authtoken(self):
"""Insert Queue with invalid authtoken."""
self.url = self.cfg.base_url + '/queues/invalidauthtoken'
header = copy.copy(self.header)
header["X-Auth-Token"] = 'invalid'
result = http.put(self.url, header)
self.assertEqual(result.status_code, 401)
test_insert_queue_invalid_authtoken.tags = ['negative']
def test_insert_queue_header_plaintext(self):
"""Insert Queue with 'Accept': 'plain/text'."""
self.url = self.cfg.base_url + '/queues/plaintextheader'
header = copy.copy(self.header)
header["Accept"] = 'plain/text'
result = http.put(self.url, header)
self.assertEqual(result.status_code, 406)
test_insert_queue_header_plaintext.tags = ['negative']
def test_insert_queue_header_asterisk(self):
"""Insert Queue with 'Accept': '*/*'."""
self.url = self.cfg.base_url + '/queues/asteriskinheader'
header = copy.copy(self.header)
header["Accept"] = '*/*'
result = http.put(self.url, header)
self.assertEqual(result.status_code, 201)
test_insert_queue_header_asterisk.tags = ['positive']
def test_insert_queue_with_metadata(self):
"""Insert queue with a non-empty request body."""
self.url = self.cfg.base_url + '/queues/hasmetadata'
doc = '{"queue": "Has Metadata"}'
result = http.put(self.url, self.header, doc)
self.assertEqual(result.status_code, 201)
self.url = self.cfg.base_url + '/queues/hasmetadata/metadata'
result = http.get(self.url, self.header)
self.assertEqual(result.status_code, 200)
self.assertEqual(result.json(), {})
test_insert_queue_with_metadata.tags = ['negative']
def tearDown(self):
super(TestInsertQueue, self).tearDown()
http.delete(self.url, self.header)
@ddt.ddt
class TestQueueMetaData(base.FunctionalTestBase):
"""Tests for queue metadata."""
@classmethod
def setUpClass(cls):
cls.cfg = config.Config()
cls.header = helpers.create_marconi_headers()
cls.headers_response_with_body = set(['location',
'content-type'])
def setUp(self):
super(TestQueueMetaData, self).setUp()
self.queue_url = self.cfg.base_url + '/queues/{}'.format(uuid.uuid1())
http.put(self.queue_url, self.header)
self.queue_metadata_url = self.queue_url + '/metadata'
@ddt.data({},
{"_queue": "Top Level field with _"},
{"汉字": "non ASCII metadata"},
{"queue": "#$%^&Apple"},
{"queue": "i" * 65000}
)
def test_insert_queue_metadata(self, doc):
"""Insert Queue with empty json."""
result = http.put(self.queue_metadata_url, self.header,
json.dumps(doc))
self.assertEqual(result.status_code, 204)
result = http.get(self.queue_metadata_url, self.header)
self.assertEqual(result.status_code, 200)
self.assertEqual(result.json(), doc)
test_insert_queue_metadata.tags = ['smoke', 'positive']
@ddt.data('not_a_dict',
{"queue": "i" * 65537}
)
def test_insert_queue_invalid_metadata(self, doc):
"""Insert invalid metadata."""
result = http.put(self.queue_metadata_url, self.header, str(doc))
self.assertEqual(result.status_code, 400)
test_insert_queue_invalid_metadata.tags = ['negative']
def tearDown(self):
super(TestQueueMetaData, self).tearDown()
http.delete(self.queue_url, self.header)
@ddt.ddt
class TestQueueMisc(base.FunctionalTestBase):
@classmethod
def setUpClass(cls):
cls.cfg = config.Config()
cls.header = helpers.create_marconi_headers()
cls.headers_response_empty = set(['location'])
cls.headers_response_with_body = set(['content-location',
'content-type'])
cls.queue_url = cls.cfg.base_url + '/queues/{}'.format(uuid.uuid1())
http.put(cls.queue_url, cls.header)
url = cls.queue_url + '/metadata'
metadata = {"queue_metadata": "TEST METADATA"}
http.put(url, cls.header, json.dumps(metadata))
def test_list_queues(self):
"""List Queues."""
url = self.cfg.base_url + '/queues'
result = http.get(url, self.header)
self.assertEqual(result.status_code, 200)
response_keys_actual = result.json().keys()
response_keys_actual.sort()
response_keys_expected = ['links', 'queues']
self.assertEqual(response_keys_actual, response_keys_expected)
test_list_queues.tags = ['smoke', 'positive']
def test_list_queues_detailed(self):
"""List Queues with detailed = True."""
url = self.cfg.base_url + '/queues?detailed=True'
result = http.get(url, self.header)
self.assertEqual(result.status_code, 200)
response_keys_actual = result.json()['queues'][0].keys()
response_keys_actual.sort()
response_keys_expected = ['href', 'metadata', 'name']
self.assertEqual(response_keys_actual, response_keys_expected)
test_list_queues_detailed.tags = ['smoke', 'positive']
@ddt.data(0, -1, 30)
def test_list_queue_invalid_limit(self, limit):
"""List Queues with a limit value that is not allowed."""
url = self.cfg.base_url + '/queues?limit=' + str(limit)
result = http.get(url, self.header)
self.assertEqual(result.status_code, 400)
test_list_queue_invalid_limit.tags = ['negative']
def test_check_health(self):
"""Test health endpoint."""
url = self.cfg.base_url + '/health'
result = http.get(url, self.header)
self.assertEqual(result.status_code, 204)
test_check_health.tags = ['positive']
def test_check_queue_exists(self):
"""Checks if queue exists."""
url = self.cfg.base_url + '/queues/testqueue'
http.put(url, self.header)
result = http.get(url, self.header)
self.assertEqual(result.status_code, 204)
result = http.head(url, self.header)
self.assertEqual(result.status_code, 204)
test_check_queue_exists.tags = ['positive']
def test_check_queue_exists_negative(self):
"""Checks non-existing queue."""
url = self.cfg.base_url + '/queues/nonexistingqueue'
result = http.get(url, self.header)
self.assertEqual(result.status_code, 404)
result = http.head(url, self.header)
self.assertEqual(result.status_code, 404)
test_check_queue_exists_negative.tags = ['negative']
def test_get_queue_malformed_marker(self):
"""List queues with invalid marker."""
url = self.cfg.base_url + '/queues?marker=invalid'
result = http.get(url, self.header)
self.assertEqual(result.status_code, 204)
test_get_queue_malformed_marker.tags = ['negative']
@classmethod
def tearDownClass(cls):
"""Delete Queue."""
url = cls.cfg.base_url + '/queues/testqueue'
http.delete(url, cls.header)

View File

@ -0,0 +1,16 @@
# Copyright (c) 2013 Rackspace Hosting, Inc.
#
# 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.
"""Functional Test Utilities"""

View File

@ -0,0 +1,38 @@
# Copyright (c) 2013 Rackspace, Inc.
#
# 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 testtools
class FunctionalTestBase(testtools.TestCase):
def assertIsSubset(self, required_values, actual_values):
"""Checks if a list is subset of another.
:param required_values: superset list.
:param required_values: subset list.
"""
form = 'Missing Header(s) - {}'
self.assertTrue(required_values.issubset(actual_values),
msg=form.format((required_values - actual_values)))
def assertMessageCount(self, expectedCount, actualCount):
"""Checks if number of messages returned <= limit
:param expectedCount: limit value passed in the url (OR) default(10).
:param actualCount: number of messages returned in the API response.
"""
self.assertTrue(actualCount <= expectedCount,
msg='More Messages returned than allowed')

View File

@ -21,11 +21,11 @@ import uuid
class Config(object):
def __init__(self, config_path=None):
if config_path is None:
if os.path.exists('/etc/marconi/system-tests.conf'):
config_path = '/etc/marconi/system-tests.conf'
if os.path.exists('/etc/marconi/functional-tests.conf'):
config_path = '/etc/marconi/functional-tests.conf'
else:
config_path = os.path.expanduser('~/.marconi'
'/system-tests.conf')
'/functional-tests.conf')
self.parser = ConfigParser.SafeConfigParser()
self.parser.read(config_path)

View File

@ -0,0 +1,109 @@
# Copyright (c) 2013 Rackspace, Inc.
#
# 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.
from marconi.tests.functional.util import config
from marconi.tests.functional.util import http
import json
import random
import string
CFG = config.Config()
def get_keystone_token():
"""Gets Keystone Auth token."""
req_json = {
'auth': {
'passwordCredentials': {
'username': CFG.username,
'password': CFG.password
},
},
}
header = {"Content-Type": "application/json",
"Accept": "application/json"}
url = CFG.auth_url
response = http.post(url=url, header=header, body=req_json)
response_body = json.loads(response.text)
auth_token = response_body['access']['token']['id']
return auth_token
def create_marconi_headers():
"""Returns headers to be used for all Marconi requests."""
auth_token = get_keystone_token()
headers = {"Host": CFG.host,
"User-Agent": CFG.user_agent,
"Accept": "application/json",
"X-Project-ID": CFG.project_id,
"X-Auth-Token": auth_token,
"Client-ID": CFG.uuid
}
return headers
def generate_dict(dict_length):
"""Returns dictionary of specified length. Key:Value is random data.
:param dict_length: length of the dictionary
"""
return dict([(generate_random_string(), generate_random_string())
for _ in range(dict_length)])
def generate_random_string(length=10):
"""Returns an ASCII string of specified length."""
chars = string.ascii_letters + string.digits
return ''.join(random.choice(chars) for i in range(length))
def single_message_body(**kwargs):
"""Returns message body for one message .
The ttl will be a random value (60 <= TTL <= 1209600).
The message body will be random dict.
:param **kwargs: can be {messagesize: x} , where x is message size
:param **kwargs: can be {ttl: x} , where x is ttl in seconds
"""
valid_ttl = random.randint(60, 1209600)
if 'messagesize' in kwargs.keys():
body = generate_dict(kwargs['messagesize'])
else:
body = generate_dict(2)
if 'ttl' in kwargs:
ttl = kwargs['ttl']
else:
ttl = valid_ttl
message_body = {'ttl': ttl, 'body': body}
return message_body
def get_message_body(**kwargs):
"""Returns request body for post message tests.
:param **kwargs: can be {messagecount: x} , x is the # of messages.
"""
message_count = kwargs['messagecount']
return [single_message_body(**kwargs) for i in range(message_count)]

View File

@ -12,15 +12,11 @@
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import json
import requests
def get(url, header=''):
"""Does http GET."""
if header:
header = json.loads(header)
try:
response = requests.get(url, headers=header)
except requests.ConnectionError as detail:
@ -36,8 +32,6 @@ def get(url, header=''):
def head(url, header=''):
"""Does http HEAD."""
if header:
header = json.loads(header)
try:
response = requests.head(url, headers=header)
except requests.ConnectionError as detail:
@ -53,8 +47,6 @@ def head(url, header=''):
def post(url, header='', body=''):
"""Does http POST."""
if header:
header = json.loads(header)
body = str(body)
body = body.replace("'", '"')
try:
@ -73,9 +65,6 @@ def post(url, header='', body=''):
def put(url, header='', body=''):
"""Does http PUT."""
response = None
if header:
header = json.loads(header)
try:
response = requests.put(url, headers=header, data=body)
except requests.ConnectionError as detail:
@ -92,9 +81,6 @@ def put(url, header='', body=''):
def delete(url, header=''):
"""Does http DELETE."""
response = None
if header:
header = json.loads(header)
try:
response = requests.delete(url, headers=header)
except requests.ConnectionError as detail:
@ -111,9 +97,6 @@ def delete(url, header=''):
def patch(url, header='', body=''):
"""Does http PATCH."""
response = None
if header:
header = json.loads(header)
try:
response = requests.patch(url, headers=header, data=body)
except requests.ConnectionError as detail:

View File

@ -1,59 +0,0 @@
Marconi System Tests
====================
Marconi's system tests treat Marconi as a black box. In other
words, the API calls attempt to simulate an actual user. For
example, unlike unit tests, the system tests do not use mock
endpoints.
Running the System Tests
------------------------
#. Setup a Marconi server. Refer to the Marconi `README`_ on
how to run Marconi locally, or simply use an existing server.
#. System tests require the `requests` package. Run
the following to install it: ::
pip install -r system-test-requirements.txt
#. cd to the marconi/tests/system directory
#. Copy etc/system-tests.conf-sample to one of the following locations::
~/.marconi/system-tests.conf
/etc/marconi/system-tests.conf
#. Update the config file to point to the Marconi server you want to run
the tests against
#. If leaving keystone auth enabled, update system-tests.conf with a
valid set of credentials.
#. Now, to run the sytem tests, simply use the nosetests commands,
from the marconi/tests/system directory. e.g.:
Run all test suites: ::
nosetests -v
Adding New Tests
----------------
#. Add test case to an appropriate test case file: ::
queue/test_queue.py
messages/test_messages.py
claim/test_claims.py
#. Add any validation logic you might need, to the following utility modules:
* corresponing \*fnlib.py (e.g. queue/queuefnlib.py)
* common/functionlib.py (i.e., if the code can be used
across multiple test suites)
.. _README : https://github.com/stackforge/marconi/blob/master/README.rst
.. _requests : https://pypi.python.org/pypi/requests

View File

@ -1,223 +0,0 @@
# Copyright (c) 2013 Rackspace, Inc.
#
# 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.
from __future__ import print_function
import json
from marconi.tests.system.common import functionlib
from marconi.tests.system.common import http
def verify_claim_msg(count, *claim_response):
"""Verifies claim messages.
Validation steps include - verifying the
1. number of messages returned is <= limit specified.
2. query claim & verifying the response.
:param count: limit specified in the claim request.
:param claim_response : [header, body] returned for post claim request.
"""
test_result_flag = False
headers = claim_response[0]
body = claim_response[1]
test_result_flag = verify_claim_msglength(count, body)
if test_result_flag:
test_result_flag = query_claim(headers, body)
else:
print('More msgs returned than specified in limit')
return test_result_flag
def verify_claim_msglength(count, *body):
"""Validates that number of messages returned is <= limit specified.
:param count: value of limit specified in the post claim.
:param *body: response body returned for the post claim.
"""
msg_list = body
msg_list = json.loads(msg_list[0])
return (len(msg_list) <= count)
def query_claim(headers, *body):
"""Verifies the response of post claim.
Does a Query Claim using the href in post claim.
Compares the messages returned in Query claim with the messages
returned on Post Claim.
:param headers: headers returned in the post claim response.
:param *body: message list returned in the post claim response.
"""
test_result_flag = False
msg_list = body[0]
msg_list = json.loads(msg_list)
location = headers['Location']
url = functionlib.create_url_from_appender(location)
header = functionlib.create_marconi_headers()
get_msg = http.get(url, header)
if get_msg.status_code == 200:
query_body = json.loads(get_msg.text)
query_msgs = query_body['messages']
test_result_flag = verify_query_msgs(query_msgs, msg_list)
if not test_result_flag:
print('URL')
print(url)
print('HEADER')
print(header)
print('Messages returned by Query Claim')
print(query_msgs)
print('# of Messages returned by Query Claim', len(query_msgs))
print('Messages returned by Claim Messages')
print(msg_list)
print('# of Messages returned by Claim messages', len(msg_list))
print('Query Claim Failed')
return test_result_flag
def verify_query_msgs(querymsgs, msg_list):
"""Verifies response from Query claim.
Compares the messages returned in Query Claim with the messages
returned when the claim was posted.
:param querymsgs: response body returned for Query Claim.
:param msg_list: message list returned for the original claim.
"""
test_result_flag = True
idx = 0
for msg in querymsgs:
if ((msg['body'] != msg_list[idx]['body']) or
(msg['href'] != msg_list[idx]['href']) or
(msg['ttl'] != msg_list[idx]['ttl'])):
test_result_flag = False
idx = idx + 1
return test_result_flag
def verify_patch_claim(url, header, ttl_extended):
"""Verifies if patch claim was successful.
The following steps are performed for the verification.
1. GETs the claim
2. Checks tht the actual claim TTL value is > TTL in the patch request
:param ttl_extended : TTL posted in the patch request.
"""
test_result_flag = True
get_claim = http.get(url, header)
response_body = json.loads(get_claim.text)
ttl = response_body['ttl']
if ttl < ttl_extended:
print(get_claim.status_code)
print(get_claim.headers)
print(get_claim.text)
test_result_flag = False
return test_result_flag
def create_urllist_fromhref(*response):
"""EXtracts href & creates a url list.
:param *response : http response containing the list of messages.
"""
rspbody = json.loads(response[1])
urllist = [functionlib.create_url_from_appender(item['href'])
for item in rspbody]
return urllist
def delete_claimed_msgs(*claim_response):
"""Deletes claimed messages.
Verifies that the deletes were successful with a GET on the deleted msg.
:param *claim_response: [header, body] returned for post claim request.
"""
test_result_flag = False
urllist = create_urllist_fromhref(*claim_response)
header = functionlib.create_marconi_headers()
for url in urllist:
delete_response = http.delete(url, header)
if delete_response.status_code == 204:
test_result_flag = functionlib.verify_delete(url, header)
else:
print('DELETE message with claim ID: {}'.format(url))
print(delete_response.status_code)
print(delete_response.headers)
print(delete_response.text)
print('Delete Claimed Message Failed')
return test_result_flag
def get_claimed_msgs(*claim_response):
"""Does get on all messages returned in the claim.
:param *claim_response: [header, body] returned for post claim request.
"""
test_result_flag = True
urllist = create_urllist_fromhref(*claim_response)
header = functionlib.create_marconi_headers()
for url in urllist:
get_response = http.get(url, header)
if get_response.status_code != 200:
print(url)
print(header)
print('Get Response Code: {}'.format(get_response.status_code))
test_result_flag = False
if not test_result_flag:
assert test_result_flag, 'Get Claimed message Failed'
def release_claim(*claim_response):
"""Deletes claim & verifies the delete was successful.
Extracts claim id from the POST response input & deletes the claim.
If DELETE claim succeeds, verifies that a GET claim returns 404.
:param *claim_response: [header, body] returned for post claim request.
"""
test_result_flag = False
headers = claim_response[0]
location = headers['Location']
url = functionlib.create_url_from_appender(location)
header = functionlib.create_marconi_headers()
release_response = http.delete(url, header)
if release_response.status_code == 204:
test_result_flag = functionlib.verify_delete(url, header)
else:
print('Release Claim HTTP code:{}'.format(
release_response.status_code))
print(release_response.headers)
print(release_response.text)
assert test_result_flag, 'Release Claim Failed'
if not test_result_flag:
assert test_result_flag, 'Get claim after the release failed'

View File

@ -1,240 +0,0 @@
# Copyright (c) 2013 Rackspace, Inc.
#
# 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.
from marconi.tests.system.claim import claimfnlib
from marconi.tests.system.common import config
from marconi.tests.system.common import functionlib
from marconi.tests.system.common import http
from marconi.tests.system.messages import msgfnlib
import ddt
import json
@ddt.ddt
class TestClaims(functionlib.TestUtils):
"""Tests for Claims."""
def setUp(self):
super(TestClaims, self).setUp()
self.cfg = config.Config()
self.header = functionlib.create_marconi_headers()
self.headers_response_with_body = set(['location',
'content-type'])
def test_000_claim_setup(self):
"""Create Queue, Post Messages for Claim Tests."""
url = self.cfg.base_url + '/queues/claimtestqueue'
result = http.put(url, self.header)
self.assertEqual(result.status_code, 201)
#Post Messages
url = self.cfg.base_url + '/queues/claimtestqueue/messages'
doc = msgfnlib.get_message_body(messagecount=
self.cfg.message_paging_uplimit)
for i in range(25):
result = http.post(url, self.header, doc)
self.assertEqual(result.status_code, 201)
test_000_claim_setup.tags = ['smoke', 'positive']
def test_001_claim_2messages(self):
"""Claim 2 messages."""
message_count = 2
url = self.cfg.base_url + '/queues/claimtestqueue/claims?limit=2'
doc = '{"ttl": 300, "grace": 100}'
result = http.post(url, self.header, doc)
self.assertEqual(result.status_code, 201)
test_result_flag = claimfnlib.verify_claim_msg(
message_count, result.headers, result.text)
self.assertEqual(test_result_flag, True)
response_headers = set(result.headers.keys())
self.assertIsSubset(self.headers_response_with_body, response_headers)
test_001_claim_2messages.tags = ['smoke', 'positive']
def test_002_claim_default_messages(self):
"""Claim messages with no URL parameters.
By default, Marconi will return 10 messages.
"""
default_message_count = 10
url = self.cfg.base_url + '/queues/claimtestqueue/claims'
doc = '{"ttl": 300, "grace": 100}'
result = http.post(url, self.header, doc)
self.assertEqual(result.status_code, 201)
test_result_flag = claimfnlib.verify_claim_msg(
default_message_count, result.headers, result.text)
self.assertEqual(test_result_flag, True)
test_002_claim_default_messages.tags = ['smoke', 'positive']
def test_003_claim_more_than_allowed(self):
"""Claim more than max allowed per request.
Marconi allows a maximum of 20 messages per claim.
"""
url = self.cfg.base_url + '/queues/claimtestqueue/claims?limit=' + \
str(self.cfg.message_paging_uplimit + 1)
doc = '{"ttl": 300, "grace": 100}'
result = http.post(url, self.header, doc)
self.assertEqual(result.status_code, 400)
test_003_claim_more_than_allowed.tags = ['negative']
def test_004_claim_patch(self):
"""Update Claim."""
#Test Setup - Post Claim
url = self.cfg.base_url + '/queues/claimtestqueue/claims'
doc = '{"ttl": 300, "grace": 400}'
result = http.post(url, self.header, doc)
self.assertEqual(result.status_code, 201)
#Patch Claim
claim_location = result.headers['Location']
url = self.cfg.base_server + claim_location
doc_updated = '{"ttl": 300}'
result = http.patch(url, self.header, doc_updated)
self.assertEqual(result.status_code, 204)
test_result_flag = claimfnlib.verify_patch_claim(url,
self.header, 300)
self.assertEqual(test_result_flag, True)
test_004_claim_patch.tags = ['smoke', 'positive']
def test_005_delete_claimed_message(self):
"""Delete message belonging to a Claim."""
#Test Setup - Post claim
url = self.cfg.base_url + '/queues/claimtestqueue/claims'
doc = '{"ttl": 60, "grace": 60}'
result = http.post(url, self.header, doc)
self.assertEqual(result.status_code, 201)
#Delete Claimed Message & Verify the delete
test_result_flag = claimfnlib.delete_claimed_msgs(
result.headers, result.text)
self.assertEqual(test_result_flag, True)
test_005_delete_claimed_message.tags = ['smoke', 'positive']
def test_006_claim_release(self):
"""Release Claim."""
url = self.cfg.base_url + '/queues/claimtestqueue/claims'
doc = '{"ttl": 300, "grace": 100}'
result = http.post(url, self.header, doc)
self.assertEqual(result.status_code, 201)
#Extract claim location and construct the claim URL.
location = result.headers['Location']
url = self.cfg.base_server + location
#Release Claim.
result = http.delete(url, self.header)
self.assertEqual(result.status_code, 204)
test_006_claim_release.tags = ['smoke', 'positive']
@ddt.data(10000000000000000000, -100, 1, 59, 43201, -10000000000000000000)
def test_007_claim_invalid_ttl(self, ttl):
"""Post Claim with invalid TTL.
The request JSON body will have a TTL value
outside the allowed range.Allowed ttl values is
60 <= ttl <= 43200.
"""
url = self.cfg.base_url + '/queues/claimtestqueue/claims'
doc = {"ttl": ttl, "grace": 100}
result = http.post(url, self.header, json.dumps(doc))
self.assertEqual(result.status_code, 400)
test_007_claim_invalid_ttl.tags = ['negative']
@ddt.data(10000000000000000000, -100, 1, 59, 43201, -10000000000000000000)
def test_008_claim_invalid_grace(self, grace):
"""Post Claim with invalid grace.
The request JSON body will have a grace value
outside the allowed range.Allowed grace values is
60 <= grace <= 43200.
"""
url = self.cfg.base_url + '/queues/claimtestqueue/claims'
doc = {"ttl": 100, "grace": grace}
result = http.post(url, self.header, json.dumps(doc))
self.assertEqual(result.status_code, 400)
test_008_claim_invalid_grace.tags = ['negative']
@ddt.data(0, -100, 30, 10000000000000000000)
def test_009_claim_invalid_limit(self, grace):
"""Post Claim with invalid limit.
The request url will have a limit outside the allowed range.
Allowed limit values are 0 < limit <= 20(default max).
"""
url = self.cfg.base_url + '/queues/claimtestqueue/claims'
doc = {"ttl": 100, "grace": grace}
result = http.post(url, self.header, json.dumps(doc))
self.assertEqual(result.status_code, 400)
test_009_claim_invalid_limit.tags = ['negative']
@ddt.data(10000000000000000000, -100, 1, 59, 43201, -10000000000000000000)
def test_010_patch_claim_invalid_ttl(self, ttl):
"""Patch Claim with invalid TTL.
The request JSON body will have a TTL value
outside the allowed range.Allowed ttl values is
60 <= ttl <= 43200.
"""
url = self.cfg.base_url + '/queues/claimtestqueue/claims'
doc = '{"ttl": 100, "grace": 100}'
result = http.post(url, self.header, doc)
self.assertEqual(result.status_code, 201)
#Extract claim location and construct the claim URL.
location = result.headers['Location']
url = self.cfg.base_server + location
#Patch Claim.
doc = {"ttl": ttl}
result = http.patch(url, self.header, json.dumps(doc))
self.assertEqual(result.status_code, 400)
test_010_patch_claim_invalid_ttl.tags = ['negative']
def test_999_claim_teardown(self):
"""Delete Queue after Claim Tests."""
url = self.cfg.base_url + '/queues/claimtestqueue'
result = http.delete(url, self.header)
self.assertEqual(result.status_code, 204)
test_999_claim_teardown.tags = ['smoke', 'positive']

View File

@ -1,185 +0,0 @@
# Copyright (c) 2013 Rackspace, Inc.
#
# 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.
from __future__ import print_function
import binascii
import json
import os
import string
import testtools
from marconi.tests.system.common import config
from marconi.tests.system.common import http
CFG = config.Config()
def get_keystone_token():
"""Gets Keystone Auth token."""
req_json = {
'auth': {
'passwordCredentials': {
'username': CFG.username,
'password': CFG.password
},
},
}
header = '{"Host": "identity.api.rackspacecloud.com",'
header += '"Content-Type": "application/json","Accept":"application/json"}'
url = CFG.auth_url
response = http.post(url=url, header=header, body=req_json)
response_body = json.loads(response.text)
auth_token = response_body['access']['token']['id']
return auth_token
def get_auth_token():
"""Returns a valid auth token if auth is turned on."""
if CFG.auth_enabled:
auth_token = get_keystone_token()
else:
auth_token = 'notrealtoken'
return auth_token
def create_marconi_headers():
"""Returns headers to be used for all Marconi requests."""
auth_token = get_auth_token()
headers = ('{"Host": "$host","User-Agent": "$user_agent","Date":"DATE",'
'"Accept": "application/json","Accept-Encoding": "gzip",'
'"X-Project-ID": "$project_id",'
'"X-Auth-Token": "$token","Client-ID": "$uuid"}')
headers = string.Template(headers)
return headers.substitute(host=CFG.host, user_agent=CFG.user_agent,
project_id=CFG.project_id,
token=auth_token, uuid=CFG.uuid)
def invalid_auth_token_header():
"""Returns a header with invalid auth token."""
headers = '{"Host":"$host","User-Agent":"$user_agent","Date":"DATE",'
headers += '"Accept": "application/json","Accept-Encoding": "gzip",'
headers += '"X-Project-ID": "$project_id",'
headers += '"X-Auth-Token": "InvalidToken"}'
headers = string.Template(headers)
return headers.substitute(host=CFG.host,
project_id=CFG.project_id,
user_agent=CFG.user_agent)
def plain_text_in_header():
"""Returns headers to be used for all Marconi requests."""
auth_token = get_auth_token()
headers = '{"Host":"$host","User-Agent":"$user_agent","Date":"DATE",'
headers += '"Accept": "text/plain","Accept-Encoding": "gzip",'
headers += '"X-Project-ID": "$project_id",'
headers += '"X-Auth-Token": "$token","Client-ID": "$uuid"}'
headers = string.Template(headers)
return headers.substitute(host=CFG.host, user_agent=CFG.user_agent,
project_id=CFG.project_id,
token=auth_token, uuid=CFG.uuid)
def asterisk_in_header():
"""Returns headers to be used for all Marconi requests."""
auth_token = get_auth_token()
headers = '{"Host":"$host","User-Agent":"$user_agent","Date":"DATE",'
headers += '"Accept": "*/*","Accept-Encoding": "gzip",'
headers += '"X-Project-ID": "$project_id",'
headers += '"X-Auth-Token": "$token"}'
headers = string.Template(headers)
return headers.substitute(host=CFG.host, user_agent=CFG.user_agent,
project_id=CFG.project_id, token=auth_token)
def get_headers(input_header):
"""Creates http request headers.
1. If header value is specified in the test_data.csv, that will be used.
2. Headers can also be substituted in the Robot test case definition
file (*_tests.txt)
3. If 1. & 2. is not true -->
Replaces the header data with generic Marconi headers.
"""
if input_header:
header = input_header
else:
header = create_marconi_headers()
return header
def get_custom_body(kwargs):
"""Returns a custom request body."""
req_body = {'data': '[DATA]'}
if 'metadatasize' in kwargs.keys():
random_data = binascii.b2a_hex(os.urandom(kwargs['metadatasize']))
req_body['data'] = random_data
return json.dumps(req_body)
def create_url_from_appender(appender):
"""Returns complete url using the appender (with a a preceding '/')."""
next_url = str(CFG.base_server + appender)
return(next_url)
def get_url_from_location(header):
"""returns : the complete url referring to the location."""
location = header['location']
url = create_url_from_appender(location)
return url
def verify_delete(url, header):
"""Verifies the DELETE was successful, with a GET on the deleted item."""
test_result_flag = False
getmsg = http.get(url, header)
if getmsg.status_code == 404:
test_result_flag = True
else:
print('GET after DELETE failed')
print('URL')
print(url)
print('headers')
print(header)
print('Response Body')
print(getmsg.text)
print('GET Code {}'.format(getmsg.status_code))
return test_result_flag
class TestUtils(testtools.TestCase):
def assertIsSubset(self, required_values, actual_values):
form = 'Missing Header(s) - {}'
self.assertTrue(required_values.issubset(actual_values),
msg=form.format((required_values - actual_values)))

View File

@ -1,210 +0,0 @@
# Copyright (c) 2013 Rackspace, Inc.
#
# 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.
from __future__ import print_function
import json
import random
import string
from marconi.tests.system.common import config
from marconi.tests.system.common import functionlib
from marconi.tests.system.common import http
def generate_dict(dict_length):
"""Returns dictionary of specified length. Key:Value is random data.
:param dict_length: length of the dictionary
"""
dict = {}
while len(dict) < dict_length:
key = generate_random_string()
value = generate_random_string()
dict[key] = value
return dict
def generate_random_string(length=10):
"""Returns an ASCII string of specified length."""
chars = string.ascii_letters + string.digits
return ''.join(random.choice(chars) for i in range(length))
def single_message_body(**kwargs):
"""Returns message body for one message .
The ttl will be a random value (60 <= TTL <= 1209600).
The message body will be random dict.
:param **kwargs: can be {messagesize: x} , where x is message size
:param **kwargs: can be {ttl: x} , where x is ttl in seconds
"""
valid_ttl = random.randint(60, 1209600)
if 'messagesize' in kwargs.keys():
body = generate_dict(kwargs['messagesize'])
else:
body = generate_dict(2)
if 'ttl' in kwargs.keys():
ttl = kwargs['ttl']
else:
ttl = valid_ttl
message_body = {'ttl': ttl, 'body': body}
return message_body
def get_message_body(**kwargs):
"""Returns request body for post message tests.
:param **kwargs: can be {messagecount: x} , where x is the # of messages.
"""
message_count = kwargs['messagecount']
multiple_message_body = []
for i in range(message_count):
message_body = single_message_body(**kwargs)
multiple_message_body.append(message_body)
return multiple_message_body
def create_url(*msg_id_list):
"""Creates url list for retrieving messages with message id."""
cfg = config.Config()
url = [(cfg.base_url + msg_id) for msg_id in msg_id_list]
return url
def verify_msg_length(count=10, *msg_list):
"""Verifies the number of messages returned.
:param count: limit specified in the GET url.
:param *msg_list : list of message returned in the GET.
"""
test_result_flag = False
msg_body = json.loads(msg_list[0])
msg_list = msg_body['messages']
msg_count = len(msg_list)
if (msg_count <= count):
test_result_flag = True
else:
return test_result_flag
return test_result_flag
def get_href(*msg_list):
"""Extracts href.
:param *msg_list: list of messages returned by the server.
"""
msg_body = json.loads(msg_list[0])
link = msg_body['links']
href = link[0]['href']
return href
def verify_post_msg(msg_headers, posted_body):
"""Verifies the response of POST Message(s).
Retrieves the posted Message(s) & validates the message metadata.
:param msg_headers: headers returned for post message request.
:param posted_body: message metadata(s) in the post message request.
"""
test_result_flag = False
location = msg_headers['location']
url = functionlib.create_url_from_appender(location)
header = functionlib.create_marconi_headers()
getmsg = http.get(url, header)
if getmsg.status_code == 200:
test_result_flag = True
else:
print('Failed to GET {}'.format(url))
print('Request Header')
print(header)
print('Response Headers')
print(getmsg.headers)
print('Response Body')
print(getmsg.text)
return test_result_flag
def get_next_msgset(responsetext):
"""Follows the href path & GETs the next batch of messages recursively."""
test_result_flag = False
href = get_href(responsetext)
url = functionlib.create_url_from_appender(href)
header = functionlib.create_marconi_headers()
getmsg = http.get(url, header)
if getmsg.status_code == 200:
return get_next_msgset(getmsg.text)
elif getmsg.status_code == 204:
test_result_flag = True
return test_result_flag
else:
test_result_flag = False
print('Failed to GET {}'.format(url))
print(getmsg.text)
assert test_result_flag, 'HTTP code {}'.format(getmsg.status_code)
def verify_get_msgs(count, *getresponse):
"""Verifies GET message & does a recursive GET if needed.
:param count: limit value specified in the get message request.
:param *getresponse: [headers, body] returned for get message request.
"""
test_result_flag = False
body = getresponse[1]
msglengthflag = verify_msg_length(count, body)
if msglengthflag:
test_result_flag = get_next_msgset(body)
else:
print('Messages returned exceed requested number of messages')
test_result_flag = False
return test_result_flag
def delete_msg(*postresponse):
"""Post DELETE message & verifies that a subsequent GET returns 404.
:param *postresponse: [headers, body] returned for post message request.
"""
test_result_flag = False
headers = str(postresponse[0])
headers = headers.replace("'", '"')
headers = json.loads(headers)
location = headers['location']
url = functionlib.create_url_from_appender(location)
header = functionlib.create_marconi_headers()
deletemsg = http.delete(url, header)
if deletemsg.status_code == 204:
test_result_flag = functionlib.verify_delete(url, header)
else:
print('DELETE message failed')
print('URL')
print(url)
print('headers')
print(header)
print('Response Body')
print(deletemsg.text)
print('DELETE Code {}'.format(deletemsg.status_code))
return test_result_flag

View File

@ -1,304 +0,0 @@
# Copyright (c) 2013 Rackspace, Inc.
#
# 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.
from marconi.tests.system.common import config
from marconi.tests.system.common import functionlib
from marconi.tests.system.common import http
from marconi.tests.system.messages import msgfnlib
import ddt
@ddt.ddt
class TestMessages(functionlib.TestUtils):
"""Tests for Messages."""
def setUp(self):
super(TestMessages, self).setUp()
self.cfg = config.Config()
self.header = functionlib.create_marconi_headers()
self.headers_response_with_body = set(['location',
'content-type'])
def test_000_message_setup(self):
"""Create Queue for Message Tests."""
url = self.cfg.base_url + '/queues/messagetestqueue'
result = http.put(url, self.header)
self.assertEqual(result.status_code, 201)
test_000_message_setup.tags = ['smoke', 'positive']
def test_001_message_single_insert(self):
"""Insert Single Message into the Queue.
This test also verifies that claimed messages are
retuned (or not) depending on the include_claimed flag.
"""
doc = msgfnlib.get_message_body(messagecount=1)
url = self.cfg.base_url + '/queues/messagetestqueue/messages'
result = http.post(url, self.header, doc)
self.assertEqual(result.status_code, 201)
response_headers = set(result.headers.keys())
self.assertIsSubset(self.headers_response_with_body, response_headers)
#GET on posted message
href = result.json()['resources'][0]
url = self.cfg.base_server + href
result = http.get(url, self.header)
self.assertEqual(result.status_code, 200)
#Compare message metadata
result_body = result.json()['body']
posted_metadata = doc[0]['body']
self.assertEqual(result_body, posted_metadata)
#Post a claim & verify the include_claimed flag.
url = self.cfg.base_url + '/queues/messagetestqueue/claims'
doc = '{"ttl": 300, "grace": 100}'
result = http.post(url, self.header, doc)
self.assertEqual(result.status_code, 201)
url = self.cfg.base_url + '/queues/messagetestqueue/messages' \
'?include_claimed=true&echo=true'
result = http.get(url, self.header)
self.assertEqual(result.status_code, 200)
response_message_body = result.json()["messages"][0]["body"]
self.assertEqual(response_message_body, posted_metadata)
#By default, include_claimed = false
url = self.cfg.base_url + '/queues/messagetestqueue/messages'
result = http.get(url, self.header)
self.assertEqual(result.status_code, 204)
test_001_message_single_insert.tags = ['smoke', 'positive']
def test_002_message_bulk_insert(self):
"""Bulk Insert Messages into the Queue."""
message_count = 10
doc = msgfnlib.get_message_body(messagecount=message_count)
url = self.cfg.base_url + '/queues/messagetestqueue/messages'
result = http.post(url, self.header, doc)
self.assertEqual(result.status_code, 201)
#GET on posted messages
location = result.headers['location']
url = self.cfg.base_server + location
result = http.get(url, self.header)
self.assertEqual(result.status_code, 200)
#Compare message metadata
result_body = [result.json()[i]['body']
for i in range(len(result.json()))]
result_body.sort()
posted_metadata = [doc[i]['body']
for i in range(message_count)]
posted_metadata.sort()
self.assertEqual(result_body, posted_metadata)
test_002_message_bulk_insert.tags = ['smoke', 'positive']
def test_003_message_get_no_params(self):
"""Get Messages with no params."""
default_msg_count = 10
url = self.cfg.base_url + '/queues/messagetestqueue/messages'
result = http.get(url, self.header)
self.assertEqual(result.status_code, 200)
test_result_flag = msgfnlib.verify_get_msgs(default_msg_count,
result.headers,
result.text)
self.assertEqual(test_result_flag, True)
test_003_message_get_no_params.tags = ['smoke', 'positive']
def test_004_message_get_limit_5(self):
"""Get Messages with no params."""
msg_count = 5
url = self.cfg.base_url + '/queues/messagetestqueue/messages?limit=5'
result = http.get(url, self.header)
self.assertEqual(result.status_code, 200)
test_result_flag = msgfnlib.verify_get_msgs(msg_count,
result.headers,
result.text)
self.assertEqual(test_result_flag, True)
test_004_message_get_limit_5.tags = ['smoke', 'positive']
def test_005_message_delete(self):
"""Delete Message."""
doc = msgfnlib.get_message_body(messagecount=1)
url = self.cfg.base_url + '/queues/messagetestqueue/messages'
result = http.post(url, self.header, doc)
self.assertEqual(result.status_code, 201)
#Delete posted message
href = result.json()['resources'][0]
url = self.cfg.base_server + href
result = http.delete(url, self.header)
self.assertEqual(result.status_code, 204)
result = http.get(url, self.header)
self.assertEqual(result.status_code, 404)
test_005_message_delete.tags = ['smoke', 'positive']
def test_006_message_bulk_delete(self):
"""Bulk Delete Messages."""
doc = msgfnlib.get_message_body(messagecount=10)
url = self.cfg.base_url + '/queues/messagetestqueue/messages'
result = http.post(url, self.header, doc)
self.assertEqual(result.status_code, 201)
#Delete posted messages
location = result.headers['Location']
url = self.cfg.base_server + location
result = http.delete(url, self.header)
self.assertEqual(result.status_code, 204)
result = http.get(url, self.header)
self.assertEqual(result.status_code, 204)
test_006_message_bulk_delete.tags = ['smoke', 'positive']
def test_007_message_delete_nonexisting(self):
"""Delete non-existing Messages."""
url = self.cfg.base_url + '/queues/messagetestqueue/messages' \
'/non-existing'
result = http.delete(url, self.header)
self.assertEqual(result.status_code, 204)
test_007_message_delete_nonexisting.tags = ['negative']
def test_008_message_partial_delete(self):
"""Delete Messages will be partially successful."""
doc = msgfnlib.get_message_body(messagecount=3)
url = self.cfg.base_url + '/queues/messagetestqueue/messages'
result = http.post(url, self.header, doc)
self.assertEqual(result.status_code, 201)
#Delete posted message
location = result.headers['Location']
url = self.cfg.base_server + location
url += ',nonexisting'
result = http.delete(url, self.header)
self.assertEqual(result.status_code, 204)
test_008_message_partial_delete.tags = ['negative']
def test_009_message_partial_get(self):
"""Get Messages will be partially successful."""
doc = msgfnlib.get_message_body(messagecount=3)
url = self.cfg.base_url + '/queues/messagetestqueue/messages'
result = http.post(url, self.header, doc)
self.assertEqual(result.status_code, 201)
#Get posted message and a nonexisting message
location = result.headers['Location']
url = self.cfg.base_server + location
url += ',nonexisting'
result = http.get(url, self.header)
self.assertEqual(result.status_code, 200)
test_009_message_partial_get.tags = ['negative']
def test_010_message_bulk_insert_60(self):
"""Insert more than max allowed messages.
Marconi allows a maximum of 50 message per POST.
"""
doc = msgfnlib.get_message_body(messagecount=60)
url = self.cfg.base_url + '/queues/messagetestqueue/messages'
result = http.post(url, self.header, doc)
self.assertEqual(result.status_code, 400)
test_010_message_bulk_insert_60.tags = ['negative']
@ddt.data(10000000000000000000, -100, 0, 30, -10000000000000000000)
def test_011_message_get_invalid_limit(self, limit):
"""Get Messages with invalid value for limit.
Allowed values for limit are 0 < limit <= 20(configurable).
"""
url = self.cfg.base_url + '/queues/messagetestqueue/messages?limit=' \
+ str(limit)
result = http.get(url, self.header)
self.assertEqual(result.status_code, 400)
test_011_message_get_invalid_limit.tags = ['negative']
def test_012_message_bulk_delete(self):
"""Delete more messages than allowed in a single request.
By default, max messages that can be deleted in a single
request is 20.
"""
url = self.cfg.base_url + '/queues/messagetestqueue/messages?ids=' \
+ ','.join(str(i) for i in
range(self.cfg.message_paging_uplimit + 1))
result = http.delete(url, self.header)
self.assertEqual(result.status_code, 400)
test_012_message_bulk_delete.tags = ['negative']
def test_013_message_bulk_get(self):
"""GET more messages by id than allowed in a single request.
By default, max messages that can be fetched in a single
request is 20.
"""
url = self.cfg.base_url + '/queues/messagetestqueue/messages?ids=' \
+ ','.join(str(i) for i in
range(self.cfg.message_paging_uplimit + 1))
result = http.get(url, self.header)
self.assertEqual(result.status_code, 400)
test_013_message_bulk_get.tags = ['negative']
def test_014_get_messages_malformed_marker(self):
"""Get messages with non-existing marker."""
url = self.cfg.base_url + '/queues/messagetestqueue/messages' \
'?marker=invalid'
result = http.get(url, self.header)
self.assertEqual(result.status_code, 204)
test_014_get_messages_malformed_marker.tags = ['negative']
def test_999_message_teardown(self):
url = self.cfg.base_url + '/queues/messagetestqueue'
http.delete(url, self.header)
test_999_message_teardown.tags = ['smoke', 'positive']

View File

@ -1,146 +0,0 @@
# Copyright (c) 2013 Rackspace, Inc.
#
# 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.
from __future__ import print_function
import binascii
import json
import os
from marconi.tests.system.common import functionlib
from marconi.tests.system.common import http
def verify_queue_stats(*get_response):
"""Verifies GET queue/stats response.
Verification Steps:
1. stats json body has the keys - action & messages.
2. messages json has the keys - claimed & free.
3. claimed & free key values are int.
:param *getresponse: [headers, body] returned for get queue stats.
"""
test_result_flag = True
headers = get_response[0]
body = json.loads(get_response[1])
keys_in_body = body.keys()
keys_in_body.sort()
if (keys_in_body == ['messages']):
stats = body['messages']
keys_in_stats = stats.keys()
keys_in_stats.sort()
if (keys_in_stats == ['claimed', 'free', 'total']):
try:
int(stats['claimed'])
int(stats['free'])
int(stats['total'])
except ValueError:
test_result_flag = False
else:
test_result_flag = False
else:
test_result_flag = False
if test_result_flag:
return test_result_flag
else:
print(headers)
print(body)
assert test_result_flag, 'Get Request stats failed'
def get_queue_name(namelength=65):
"""Returns a queuename of specified length.
By default, a name longer than Marconi allows - currently 64 char.
:param namelength: length of the queue name.
"""
appender = '/queues/' + binascii.b2a_hex(os.urandom(namelength))
return appender
def verify_list_queues(*list_queue_response):
"""Verifies the response of list queues.
:param *list_queue_response: [header, body] returned for list queue.
"""
test_result_flag = True
response_body = json.loads(list_queue_response[1])
links = response_body['links']
href = links[0]['href']
detail_enabled = 'detailed=true' in href
queue_list = response_body['queues']
test_result_flags = [verify_listed(queue, detail_enabled)
for queue in queue_list]
if False in test_result_flags:
test_result_flag = False
print('List Queue API response: {}'.format(response_body))
return test_result_flag
if links[0]['rel'] == 'next':
test_result_flag = list_queues(href)
return test_result_flag
def verify_listed(queue, detail_enabled):
"""Verifies the listed queues.
:param queue: queue returned in the list queues response.
:param detail_enabled: indicates if queue contains metadata
"""
test_result_flag = True
keys = queue.keys()
keys.sort()
if detail_enabled:
expected_keys = ['href', 'metadata', 'name']
else:
expected_keys = ['href', 'name']
if keys == expected_keys:
return test_result_flag
else:
print('list_queue response does not match expected response')
print(queue)
test_result_flag = False
return test_result_flag
def list_queues(href):
"""Lists queue using the href value.
:param href: href returned by a previous list queue request.
"""
test_result_flag = False
url = functionlib.create_url_from_appender(href)
header = functionlib.create_marconi_headers()
list_queue_response = http.get(url, header)
if list_queue_response.status_code == 200:
headers = list_queue_response.headers
text = list_queue_response.text
test_result_flag = verify_list_queues(headers, text)
elif list_queue_response.status_code == 204:
test_result_flag = True
return test_result_flag

View File

@ -1,376 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2013 Rackspace, Inc.
#
# 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.
from marconi.tests.system.common import config
from marconi.tests.system.common import functionlib
from marconi.tests.system.common import http
from marconi.tests.system.queue import queuefnlib
import ddt
import json
@ddt.ddt
class TestQueue(functionlib.TestUtils):
"""Tests for queue."""
def setUp(self):
super(TestQueue, self).setUp()
self.cfg = config.Config()
self.header = functionlib.create_marconi_headers()
self.headers_response_empty = set(['location'])
self.headers_response_with_body = set(['content-location',
'content-type'])
def test_001_insert_queue(self):
"""Create Queue."""
url = self.cfg.base_url + '/queues/qtestqueue'
result = http.put(url, self.header)
self.assertEqual(result.status_code, 201)
response_headers = set(result.headers.keys())
self.assertIsSubset(self.headers_response_empty, response_headers)
url = self.cfg.base_url + '/queues/qtestqueue/metadata'
result = http.get(url, self.header)
self.assertEqual(result.status_code, 200)
self.assertEqual(result.json(), {})
http.delete(url, self.header)
test_001_insert_queue.tags = ['smoke', 'positive', 'create_queue']
def test_002_insert_queue_case_insensitive(self):
"""Insert Queue with same name, different case."""
url = self.cfg.base_url + '/queues/QteStquEue'
result = http.put(url, self.header)
self.assertEqual(result.status_code, 201)
http.delete(url, self.header)
test_002_insert_queue_case_insensitive.tags = ['positive']
def test_003_insert_queue_empty_json(self):
"""Update Queue with empty json."""
url = self.cfg.base_url + '/queues/emptyjson'
doc = '{}'
result = http.put(url, self.header, doc)
self.assertEqual(result.status_code, 201)
http.delete(url, self.header)
test_003_insert_queue_empty_json.tags = ['smoke', 'positive']
def test_004_insert_queue_invalid_authtoken(self):
"""Insert Queue with invalid authtoken."""
url = self.cfg.base_url + '/queues/invalidauthtoken'
header = functionlib.invalid_auth_token_header()
result = http.put(url, header)
self.assertEqual(result.status_code, 401)
test_004_insert_queue_invalid_authtoken.tags = ['negative']
def test_006_insert_queue_header_plaintext(self):
"""Insert Queue with 'Accept': 'plain/text'."""
url = self.cfg.base_url + '/queues/plaintextheader'
header = functionlib.plain_text_in_header()
result = http.put(url, header)
self.assertEqual(result.status_code, 406)
http.delete(url, self.header)
test_006_insert_queue_header_plaintext.tags = ['negative']
def test_007_insert_queue_header_asterisk(self):
"""Insert Queue with 'Accept': '*/*'."""
url = self.cfg.base_url + '/queues/asteriskinheader'
header = functionlib.asterisk_in_header()
result = http.put(url, header)
self.assertEqual(result.status_code, 201)
http.delete(url, self.header)
test_007_insert_queue_header_asterisk.tags = ['positive']
def test_008_insert_queue_nonASCII_name(self):
"""Insert Queue with non ASCII name."""
url = self.cfg.base_url + '/queues/汉字漢字'
doc = '{"queue": "non ASCII name"}'
result = http.put(url, self.header, doc)
self.assertEqual(result.status_code, 400)
test_008_insert_queue_nonASCII_name.tags = ['negative']
def test_009_insert_queue_long_queuename(self):
"""Insert queue with name > 64 bytes."""
url = self.cfg.base_url + queuefnlib.get_queue_name()
result = http.put(url, self.header)
self.assertEqual(result.status_code, 400)
test_009_insert_queue_long_queuename.tags = ['negative']
def test_010_insert_queue_hyphenated_queuename(self):
"""Insert queue with hyphen in name."""
url = self.cfg.base_url + '/queues/hyphen-name'
result = http.put(url, self.header)
self.assertEqual(result.status_code, 201)
http.delete(url, self.header)
test_010_insert_queue_hyphenated_queuename.tags = ['positive']
def test_011_insert_queue_invalid_char(self):
"""Insert queue with invalid characters in name."""
url = self.cfg.base_url + '/queues/@$@^qw)'
result = http.put(url, self.header)
self.assertEqual(result.status_code, 400)
test_011_insert_queue_invalid_char.tags = ['negative']
def test_012_insert_queue_with_metadata(self):
"""Insert queue with a non-empty request body."""
url = self.cfg.base_url + '/queues/hasmetadata'
doc = '{"queue": "Has Metadata"}'
result = http.put(url, self.header, doc)
self.assertEqual(result.status_code, 201)
url = self.cfg.base_url + '/queues/hasmetadata/metadata'
result = http.get(url, self.header)
self.assertEqual(result.status_code, 200)
self.assertEqual(result.json(), {})
url = self.cfg.base_url + '/queues/hasmetadata'
http.delete(url, self.header)
test_012_insert_queue_with_metadata.tags = ['negative']
def test_013_queue_metadata_invalid_authtoken(self):
"""Update Queue with invalid authtoken."""
url = self.cfg.base_url + '/queues/qtestqueue/metadata'
header = functionlib.invalid_auth_token_header()
doc = '{"queue": "invalid auth token"}'
result = http.put(url, header, doc)
self.assertEqual(result.status_code, 401)
test_013_queue_metadata_invalid_authtoken.tags = ['negative']
def test_014_queue_metadata_toplevel_underscore(self):
"""Insert Queue with underscore in toplevel field."""
url = self.cfg.base_url + '/queues/toplevel'
result = http.put(url, self.header)
url = self.cfg.base_url + '/queues/toplevel/metadata'
doc = '{"_queue": "Top Level field with _"}'
result = http.put(url, self.header, doc)
self.assertEqual(result.status_code, 204)
result = http.get(url, self.header)
self.assertEqual(result.status_code, 200)
self.assertEqual(result.json(), json.loads(doc))
url = self.cfg.base_url + '/queues/toplevel'
http.delete(url, self.header)
test_014_queue_metadata_toplevel_underscore.tags = ['negative']
def test_015_queue_insert_nonASCII_metadata(self):
"""Insert Queue with non ASCII name."""
url = self.cfg.base_url + '/queues/nonASCIImetadata'
result = http.put(url, self.header)
url = self.cfg.base_url + '/queues/nonASCIImetadata/metadata'
doc = '{"汉字": "non ASCII metadata"}'
result = http.put(url, self.header, doc)
self.assertEqual(result.status_code, 204)
result = http.get(url, self.header)
self.assertEqual(result.status_code, 200)
url = self.cfg.base_url + '/queues/nonASCIImetadata'
result = http.delete(url, self.header)
test_015_queue_insert_nonASCII_metadata.tags = ['negative']
def test_016_queue_insert_metadata_size65535(self):
"""Updates Queue with metadata_size = 65535."""
url = self.cfg.base_url + '/queues/qtestqueue'
result = http.put(url, self.header)
url = self.cfg.base_url + '/queues/qtestqueue/metadata'
doc = functionlib.get_custom_body({"metadatasize": 65535})
result = http.put(url, self.header, doc)
self.assertEqual(result.status_code, 204)
result = http.get(url, self.header)
self.assertEqual(result.status_code, 200)
self.assertEqual(result.json(), json.loads(doc))
test_016_queue_insert_metadata_size65535.tags = ['positive']
def test_017_queue_insert_metadata_size65536(self):
"""Updates Queue with metadata_size = 65536."""
url = self.cfg.base_url + '/queues/qtestqueue/metadata'
doc = functionlib.get_custom_body({"metadatasize": 65536})
result = http.put(url, self.header, doc)
self.assertEqual(result.status_code, 204)
result = http.get(url, self.header)
self.assertEqual(result.status_code, 200)
self.assertEqual(result.json(), json.loads(doc))
test_017_queue_insert_metadata_size65536.tags = ['positive']
def test_018_queue_insert_metadata_size65537(self):
"""Updates Queue with metadata_size = 65537."""
url = self.cfg.base_url + '/queues/qtestqueue/metadata'
doc = functionlib.get_custom_body({"metadatasize": 65537})
result = http.put(url, self.header, doc)
self.assertEqual(result.status_code, 400)
test_018_queue_insert_metadata_size65537.tags = ['negative']
def test_019_queue_insert_metadata_invalidchar(self):
"""Update Queues with invalid char in metadata."""
url = self.cfg.base_url + '/queues/qtestqueue/metadata'
doc = '{"queue": "#$%^&Apple"}'
result = http.put(url, self.header, doc)
self.assertEqual(result.status_code, 204)
url = self.cfg.base_url + '/queues/qtestqueue'
test_019_queue_insert_metadata_invalidchar.tags = ['negative']
def test_020_queue_stats(self):
"""Insert queue with name > 64 bytes."""
url = self.cfg.base_url + '/queues/qtestqueue/stats'
result = http.get(url, self.header)
self.assertEqual(result.status_code, 200)
test_result_flag = queuefnlib.verify_queue_stats(result.headers,
result.text)
self.assertEqual(test_result_flag, True)
test_020_queue_stats.tags = ['smoke', 'positive']
def test_021_queue_list(self):
"""List Queues."""
url = self.cfg.base_url + '/queues'
result = http.get(url, self.header)
self.assertEqual(result.status_code, 200)
test_result_flag = queuefnlib.verify_list_queues(result.headers,
result.text)
self.assertEqual(test_result_flag, True)
test_021_queue_list.tags = ['smoke', 'positive']
def test_022_queue_list_detailed(self):
"""List Queues with detailed = True."""
url = self.cfg.base_url + '/queues?detailed=True'
result = http.get(url, self.header)
self.assertEqual(result.status_code, 200)
test_result_flag = queuefnlib.verify_list_queues(result.headers,
result.text)
self.assertEqual(test_result_flag, True)
test_022_queue_list_detailed.tags = ['smoke', 'positive']
@ddt.data(0, -1, 30)
def test_023_queue_list_invalid_limit(self, limit):
"""List Queues with a limit value that is not allowed."""
url = self.cfg.base_url + '/queues?limit=' + str(limit)
result = http.get(url, self.header)
self.assertEqual(result.status_code, 400)
test_023_queue_list_invalid_limit.tags = ['negative']
def test_024_check_health(self):
"""Test health endpoint."""
url = self.cfg.base_url + '/health'
result = http.get(url, self.header)
self.assertEqual(result.status_code, 204)
test_024_check_health.tags = ['positive']
def test_025_check_queue_exists(self):
"""Checks if queue exists."""
url = self.cfg.base_url + '/queues/qtestqueue'
http.put(url, self.header)
result = http.get(url, self.header)
self.assertEqual(result.status_code, 204)
result = http.head(url, self.header)
self.assertEqual(result.status_code, 204)
test_025_check_queue_exists.tags = ['positive']
def test_026_check_queue_exists(self):
"""Checks non-existing queue."""
url = self.cfg.base_url + '/queues/nonexistingqueue'
result = http.get(url, self.header)
self.assertEqual(result.status_code, 404)
result = http.head(url, self.header)
self.assertEqual(result.status_code, 404)
test_026_check_queue_exists.tags = ['negative']
def test_026_get_queue_malformed_marker(self):
"""Checks non-existing queue."""
url = self.cfg.base_url + '/queues/qtestqueue?marker=invalid'
result = http.get(url, self.header)
self.assertEqual(result.status_code, 204)
test_026_get_queue_malformed_marker.tags = ['negative']
def test_999_delete_queue(self):
"""Delete Queue.
Deletes Queue & performs GET to confirm 404.
"""
url = self.cfg.base_url + '/queues/qtestqueue'
result = http.delete(url, self.header)
self.assertEqual(result.status_code, 204)
test_999_delete_queue.tags = ['smoke']