316 lines
12 KiB
Python
316 lines
12 KiB
Python
# 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 datetime
|
|
import json
|
|
from unittest import mock
|
|
|
|
import ddt
|
|
import falcon
|
|
from oslo_serialization import jsonutils
|
|
from oslo_utils import timeutils
|
|
from oslo_utils import uuidutils
|
|
from testtools import matchers
|
|
|
|
from zaqar import tests as testing
|
|
from zaqar.tests.unit.transport.wsgi import base
|
|
|
|
|
|
@ddt.ddt
|
|
class TestClaimsMongoDB(base.V1_1Base):
|
|
|
|
config_file = 'wsgi_mongodb.conf'
|
|
|
|
@testing.requires_mongodb
|
|
def setUp(self):
|
|
super(TestClaimsMongoDB, self).setUp()
|
|
|
|
self.default_claim_ttl = self.boot.transport._defaults.claim_ttl
|
|
self.project_id = '737_abc8332832'
|
|
self.headers = {
|
|
'Client-ID': uuidutils.generate_uuid(),
|
|
'X-Project-ID': self.project_id
|
|
}
|
|
self.queue_path = self.url_prefix + '/queues/fizbit'
|
|
self.claims_path = self.queue_path + '/claims'
|
|
self.messages_path = self.queue_path + '/messages'
|
|
|
|
doc = json.dumps({"_ttl": 60})
|
|
|
|
self.simulate_put(self.queue_path, body=doc, headers=self.headers)
|
|
self.assertEqual(falcon.HTTP_201, self.srmock.status)
|
|
|
|
doc = json.dumps({'messages': [{'body': 239, 'ttl': 300}] * 10})
|
|
self.simulate_post(self.queue_path + '/messages',
|
|
body=doc, headers=self.headers)
|
|
self.assertEqual(falcon.HTTP_201, self.srmock.status)
|
|
|
|
def tearDown(self):
|
|
storage = self.boot.storage._storage
|
|
control = self.boot.control
|
|
connection = storage.connection
|
|
|
|
connection.drop_database(control.queues_database)
|
|
|
|
for db in storage.message_databases:
|
|
connection.drop_database(db)
|
|
self.simulate_delete(self.queue_path, headers=self.headers)
|
|
|
|
super(TestClaimsMongoDB, self).tearDown()
|
|
|
|
@ddt.data('[', '[]', '.', '"fail"')
|
|
def test_bad_claim(self, doc):
|
|
self.simulate_post(self.claims_path, body=doc, headers=self.headers)
|
|
self.assertEqual(falcon.HTTP_400, self.srmock.status)
|
|
|
|
href = self._get_a_claim()
|
|
|
|
self.simulate_patch(href, body=doc, headers=self.headers)
|
|
self.assertEqual(falcon.HTTP_400, self.srmock.status)
|
|
|
|
def test_exceeded_claim(self):
|
|
self.simulate_post(self.claims_path,
|
|
body='{"ttl": 100, "grace": 60}',
|
|
query_string='limit=21', headers=self.headers)
|
|
|
|
self.assertEqual(falcon.HTTP_400, self.srmock.status)
|
|
|
|
@ddt.data((-1, -1), (59, 60), (60, 59), (60, 43201), (43201, 60))
|
|
def test_unacceptable_ttl_or_grace(self, ttl_grace):
|
|
ttl, grace = ttl_grace
|
|
self.simulate_post(self.claims_path,
|
|
body=json.dumps({'ttl': ttl, 'grace': grace}),
|
|
headers=self.headers)
|
|
|
|
self.assertEqual(falcon.HTTP_400, self.srmock.status)
|
|
|
|
@ddt.data(-1, 59, 43201)
|
|
def test_unacceptable_new_ttl(self, ttl):
|
|
href = self._get_a_claim()
|
|
|
|
self.simulate_patch(href,
|
|
body=json.dumps({'ttl': ttl}),
|
|
headers=self.headers)
|
|
|
|
self.assertEqual(falcon.HTTP_400, self.srmock.status)
|
|
|
|
def test_default_ttl_and_grace(self):
|
|
self.simulate_post(self.claims_path,
|
|
body='{}', headers=self.headers)
|
|
|
|
self.assertEqual(falcon.HTTP_201, self.srmock.status)
|
|
|
|
body = self.simulate_get(self.srmock.headers_dict['location'],
|
|
headers=self.headers)
|
|
|
|
claim = jsonutils.loads(body[0])
|
|
self.assertEqual(self.default_claim_ttl, claim['ttl'])
|
|
|
|
def _get_a_claim(self):
|
|
doc = '{"ttl": 100, "grace": 60}'
|
|
self.simulate_post(self.claims_path, body=doc, headers=self.headers)
|
|
return self.srmock.headers_dict['Location']
|
|
|
|
def test_lifecycle(self):
|
|
doc = '{"ttl": 100, "grace": 60}'
|
|
|
|
# First, claim some messages
|
|
body = self.simulate_post(self.claims_path, body=doc,
|
|
headers=self.headers)
|
|
self.assertEqual(falcon.HTTP_201, self.srmock.status)
|
|
|
|
claimed = jsonutils.loads(body[0])['messages']
|
|
claim_href = self.srmock.headers_dict['Location']
|
|
message_href, params = claimed[0]['href'].split('?')
|
|
|
|
# No more messages to claim
|
|
self.simulate_post(self.claims_path, body=doc,
|
|
query_string='limit=3', headers=self.headers)
|
|
self.assertEqual(falcon.HTTP_204, self.srmock.status)
|
|
|
|
# Listing messages, by default, won't include claimed, will echo
|
|
body = self.simulate_get(self.messages_path,
|
|
headers=self.headers,
|
|
query_string="echo=true")
|
|
self.assertEqual(falcon.HTTP_200, self.srmock.status)
|
|
self._empty_message_list(body)
|
|
|
|
# Listing messages, by default, won't include claimed, won't echo
|
|
body = self.simulate_get(self.messages_path,
|
|
headers=self.headers,
|
|
query_string="echo=false")
|
|
self.assertEqual(falcon.HTTP_200, self.srmock.status)
|
|
self._empty_message_list(body)
|
|
|
|
# List messages, include_claimed, but don't echo
|
|
body = self.simulate_get(self.messages_path,
|
|
query_string='include_claimed=true'
|
|
'&echo=false',
|
|
headers=self.headers)
|
|
|
|
self.assertEqual(falcon.HTTP_200, self.srmock.status)
|
|
self._empty_message_list(body)
|
|
|
|
# List messages with a different client-id and echo=false.
|
|
# Should return some messages
|
|
headers = self.headers.copy()
|
|
headers["Client-ID"] = uuidutils.generate_uuid()
|
|
body = self.simulate_get(self.messages_path,
|
|
query_string='include_claimed=true'
|
|
'&echo=false',
|
|
headers=headers)
|
|
|
|
self.assertEqual(falcon.HTTP_200, self.srmock.status)
|
|
|
|
# Include claimed messages this time, and echo
|
|
body = self.simulate_get(self.messages_path,
|
|
query_string='include_claimed=true'
|
|
'&echo=true',
|
|
headers=self.headers)
|
|
listed = jsonutils.loads(body[0])
|
|
self.assertEqual(falcon.HTTP_200, self.srmock.status)
|
|
self.assertEqual(len(claimed), len(listed['messages']))
|
|
|
|
now = timeutils.utcnow() + datetime.timedelta(seconds=10)
|
|
timeutils_utcnow = 'oslo_utils.timeutils.utcnow'
|
|
with mock.patch(timeutils_utcnow) as mock_utcnow:
|
|
mock_utcnow.return_value = now
|
|
body = self.simulate_get(claim_href, headers=self.headers)
|
|
|
|
claim = jsonutils.loads(body[0])
|
|
|
|
self.assertEqual(falcon.HTTP_200, self.srmock.status)
|
|
self.assertEqual(100, claim['ttl'])
|
|
# NOTE(cpp-cabrera): verify that claim age is non-negative
|
|
self.assertThat(claim['age'], matchers.GreaterThan(-1))
|
|
|
|
# Try to delete the message without submitting a claim_id
|
|
self.simulate_delete(message_href, headers=self.headers)
|
|
self.assertEqual(falcon.HTTP_403, self.srmock.status)
|
|
|
|
# Delete the message and its associated claim
|
|
self.simulate_delete(message_href,
|
|
query_string=params, headers=self.headers)
|
|
self.assertEqual(falcon.HTTP_204, self.srmock.status)
|
|
|
|
# Try to get it from the wrong project
|
|
headers = {
|
|
'Client-ID': uuidutils.generate_uuid(),
|
|
'X-Project-ID': 'bogusproject'
|
|
}
|
|
self.simulate_get(message_href, query_string=params, headers=headers)
|
|
self.assertEqual(falcon.HTTP_404, self.srmock.status)
|
|
|
|
# Get the message
|
|
self.simulate_get(message_href, query_string=params,
|
|
headers=self.headers)
|
|
self.assertEqual(falcon.HTTP_404, self.srmock.status)
|
|
|
|
# Update the claim
|
|
new_claim_ttl = '{"ttl": 60, "grace": 60}'
|
|
creation = timeutils.utcnow()
|
|
self.simulate_patch(claim_href, body=new_claim_ttl,
|
|
headers=self.headers)
|
|
self.assertEqual(falcon.HTTP_204, self.srmock.status)
|
|
|
|
# Get the claimed messages (again)
|
|
body = self.simulate_get(claim_href, headers=self.headers)
|
|
query = timeutils.utcnow()
|
|
claim = jsonutils.loads(body[0])
|
|
message_href, params = claim['messages'][0]['href'].split('?')
|
|
|
|
self.assertEqual(60, claim['ttl'])
|
|
estimated_age = timeutils.delta_seconds(creation, query)
|
|
self.assertGreater(estimated_age, claim['age'])
|
|
|
|
# Delete the claim
|
|
self.simulate_delete(claim['href'], headers=self.headers)
|
|
self.assertEqual(falcon.HTTP_204, self.srmock.status)
|
|
|
|
# Try to delete a message with an invalid claim ID
|
|
self.simulate_delete(message_href,
|
|
query_string=params, headers=self.headers)
|
|
self.assertEqual(falcon.HTTP_400, self.srmock.status)
|
|
|
|
# Make sure it wasn't deleted!
|
|
self.simulate_get(message_href, query_string=params,
|
|
headers=self.headers)
|
|
self.assertEqual(falcon.HTTP_200, self.srmock.status)
|
|
|
|
# Try to get a claim that doesn't exist
|
|
self.simulate_get(claim['href'], headers=self.headers)
|
|
self.assertEqual(falcon.HTTP_404, self.srmock.status)
|
|
|
|
# Try to update a claim that doesn't exist
|
|
self.simulate_patch(claim['href'], body=doc,
|
|
headers=self.headers)
|
|
self.assertEqual(falcon.HTTP_404, self.srmock.status)
|
|
|
|
def test_post_claim_nonexistent_queue(self):
|
|
path = self.url_prefix + '/queues/nonexistent/claims'
|
|
self.simulate_post(path,
|
|
body='{"ttl": 100, "grace": 60}',
|
|
headers=self.headers)
|
|
self.assertEqual(falcon.HTTP_204, self.srmock.status)
|
|
|
|
def test_get_claim_nonexistent_queue(self):
|
|
path = self.url_prefix + '/queues/nonexistent/claims/aaabbbba'
|
|
self.simulate_get(path, headers=self.headers)
|
|
self.assertEqual(falcon.HTTP_404, self.srmock.status)
|
|
|
|
# NOTE(cpp-cabrera): regression test against bug #1203842
|
|
def test_get_nonexistent_claim_404s(self):
|
|
self.simulate_get(self.claims_path + '/a', headers=self.headers)
|
|
self.assertEqual(falcon.HTTP_404, self.srmock.status)
|
|
|
|
def test_delete_nonexistent_claim_204s(self):
|
|
self.simulate_delete(self.claims_path + '/a',
|
|
headers=self.headers)
|
|
self.assertEqual(falcon.HTTP_204, self.srmock.status)
|
|
|
|
def test_patch_nonexistent_claim_404s(self):
|
|
patch_data = json.dumps({'ttl': 100})
|
|
self.simulate_patch(self.claims_path + '/a', body=patch_data,
|
|
headers=self.headers)
|
|
self.assertEqual(falcon.HTTP_404, self.srmock.status)
|
|
|
|
|
|
class TestClaimsFaultyDriver(base.V1_1BaseFaulty):
|
|
|
|
config_file = 'wsgi_faulty.conf'
|
|
|
|
def test_simple(self):
|
|
self.project_id = '480924abc_'
|
|
self.headers = {
|
|
'Client-ID': uuidutils.generate_uuid(),
|
|
'X-Project-ID': self.project_id
|
|
}
|
|
|
|
claims_path = self.url_prefix + '/queues/fizbit/claims'
|
|
doc = '{"ttl": 100, "grace": 60}'
|
|
|
|
self.simulate_post(claims_path, body=doc, headers=self.headers)
|
|
self.assertEqual(falcon.HTTP_503, self.srmock.status)
|
|
|
|
self.simulate_get(claims_path + '/nichts', headers=self.headers)
|
|
self.assertEqual(falcon.HTTP_503, self.srmock.status)
|
|
|
|
self.simulate_patch(claims_path + '/nichts', body=doc,
|
|
headers=self.headers)
|
|
self.assertEqual(falcon.HTTP_503, self.srmock.status)
|
|
|
|
self.simulate_delete(claims_path + '/foo', headers=self.headers)
|
|
self.assertEqual(falcon.HTTP_503, self.srmock.status)
|