Merge "Refactor System Tests"
This commit is contained in:
commit
014039f03b
@ -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
|
49
marconi/tests/functional/README.rst
Normal file
49
marconi/tests/functional/README.rst
Normal 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
|
16
marconi/tests/functional/__init__.py
Normal file
16
marconi/tests/functional/__init__.py
Normal 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"""
|
235
marconi/tests/functional/test_claims.py
Normal file
235
marconi/tests/functional/test_claims.py
Normal 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)
|
292
marconi/tests/functional/test_messages.py
Normal file
292
marconi/tests/functional/test_messages.py
Normal 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)
|
288
marconi/tests/functional/test_queue.py
Normal file
288
marconi/tests/functional/test_queue.py
Normal 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)
|
16
marconi/tests/functional/util/__init__.py
Normal file
16
marconi/tests/functional/util/__init__.py
Normal 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"""
|
38
marconi/tests/functional/util/base.py
Normal file
38
marconi/tests/functional/util/base.py
Normal 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')
|
@ -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)
|
||||
|
109
marconi/tests/functional/util/helpers.py
Normal file
109
marconi/tests/functional/util/helpers.py
Normal 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)]
|
@ -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:
|
@ -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
|
@ -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'
|
@ -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']
|
@ -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)))
|
@ -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
|
@ -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']
|
@ -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
|
@ -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']
|
Loading…
x
Reference in New Issue
Block a user