add more unit tests to managers

In this commit we add several unit tests that covers some managers.
We chose to write unit tests for managers that are closest to Synergy
core functionalities in a first stage.
Also, we test only method specific to each manager as the base methods
of Manager class have been tested in Synergy.

Managers with unit tests:
- FairShareManager
- QueueManager
- QuotaManager (and DynamicQuota)
- SchedulerManager

Change-Id: I831649e48b36bbaf94f6790fbf1a8954636fac32
This commit is contained in:
Vincent Llorens 2016-06-10 17:31:49 +02:00
parent d7fd5a85a7
commit 7a73fd7f9f
8 changed files with 695 additions and 26 deletions

View File

@ -1,26 +0,0 @@
# 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 synergy_scheduler_manager.quota_manager import DynamicQuota
from synergy_scheduler_manager.tests import base
class TestDynamicQuota(base.TestCase):
def setUp(self):
super(TestDynamicQuota, self).setUp()
self.dyn_quota = DynamicQuota()
def test_add_project(self):
project_id = 1
self.dyn_quota.addProject(project_id, "test_project")
self.assertIn(project_id, self.dyn_quota.getProjects())

View File

@ -0,0 +1,116 @@
# 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 synergy_scheduler_manager.quota_manager import DynamicQuota
from synergy_scheduler_manager.tests.unit import base
class TestDynamicQuota(base.TestCase):
def setUp(self):
super(TestDynamicQuota, self).setUp()
self.dyn_quota = DynamicQuota()
def test_get_add_project_no_usage(self):
self.dyn_quota.addProject(prj_id=1, prj_name="test_project")
project = self.dyn_quota.getProject(1)
self.assertEqual("test_project", project["name"])
self.assertEqual(0, project["cores"])
self.assertEqual(0, project["ram"])
self.assertEqual({"active": [], "pending": []}, project["instances"])
self.assertEqual(0, project["TTL"])
def test_get_add_project_with_usage(self):
fake_usage = {"cores": 5, "ram": 12, "instances": ["a", "b"]}
self.dyn_quota.addProject(prj_id=1, prj_name="test", usage=fake_usage)
project = self.dyn_quota.getProject(1)
self.assertEqual("test", project["name"])
self.assertEqual(5, project["cores"])
self.assertEqual(12, project["ram"])
self.assertEqual({"active": ["a", "b"], "pending": []},
project["instances"])
self.assertEqual(0, project["TTL"])
self.assertEqual(12, self.dyn_quota.ram["in_use"])
self.assertEqual(5, self.dyn_quota.cores["in_use"])
def test_get_size(self):
size = self.dyn_quota.getSize()
self.assertEqual(0, size["cores"])
self.assertEqual(0, size["ram"])
def test_set_size(self):
self.dyn_quota.setSize(cores=10, ram=20)
self.assertEqual(10, self.dyn_quota.cores["limit"])
self.assertEqual(20, self.dyn_quota.ram["limit"])
def test_get_projects(self):
self.assertEqual(self.dyn_quota.projects, self.dyn_quota.getProjects())
def test_remove_project(self):
self.dyn_quota.addProject(prj_id=1, prj_name="test")
self.assertIn(1, self.dyn_quota.projects)
self.dyn_quota.removeProject(1)
self.assertNotIn(1, self.dyn_quota.projects)
def test_allocate_single_instance(self):
self.dyn_quota.setSize(cores=20, ram=100)
self.dyn_quota.addProject(prj_id=1, prj_name="test")
self.dyn_quota.allocate("a", prj_id=1, cores=5, ram=10)
project = self.dyn_quota.getProject(1)
self.assertIn("a", project["instances"]["active"])
self.assertEqual(5, project["cores"])
self.assertEqual(10, project["ram"])
self.assertEqual(5, self.dyn_quota.cores["in_use"])
self.assertEqual(10, self.dyn_quota.ram["in_use"])
def test_allocate_multiple_instances(self):
self.dyn_quota.setSize(cores=30, ram=100)
self.dyn_quota.addProject(prj_id=1, prj_name="test")
self.dyn_quota.allocate("a", prj_id=1, cores=5, ram=10)
self.dyn_quota.allocate("b", prj_id=1, cores=7, ram=20)
self.dyn_quota.allocate("c", prj_id=1, cores=10, ram=20)
project = self.dyn_quota.getProject(1)
self.assertIn("a", project["instances"]["active"])
self.assertIn("b", project["instances"]["active"])
self.assertIn("c", project["instances"]["active"])
self.assertEqual(22, project["cores"])
self.assertEqual(50, project["ram"])
self.assertEqual(22, self.dyn_quota.cores["in_use"])
self.assertEqual(50, self.dyn_quota.ram["in_use"])
def test_allocate_multiple_projects(self):
self.dyn_quota.setSize(cores=20, ram=100)
self.dyn_quota.addProject(prj_id=1, prj_name="project_A")
self.dyn_quota.addProject(prj_id=2, prj_name="project_B")
# TODO(vincent): can we allocate the same instance to 2 projects?
self.dyn_quota.allocate("a", prj_id=1, cores=3, ram=10)
self.dyn_quota.allocate("a", prj_id=2, cores=5, ram=15)
project_a = self.dyn_quota.getProject(1)
project_b = self.dyn_quota.getProject(2)
self.assertIn("a", project_a["instances"]["active"])
self.assertIn("a", project_b["instances"]["active"])
self.assertEqual(3, project_a["cores"])
self.assertEqual(10, project_a["ram"])
self.assertEqual(5, project_b["cores"])
self.assertEqual(15, project_b["ram"])
self.assertEqual(8, self.dyn_quota.cores["in_use"])
self.assertEqual(25, self.dyn_quota.ram["in_use"])

View File

@ -0,0 +1,137 @@
# 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 datetime import datetime
from mock import MagicMock
from mock import patch
from synergy_scheduler_manager.fairshare_manager import FairShareManager
from synergy_scheduler_manager.keystone_manager import KeystoneManager
from synergy_scheduler_manager.queue_manager import QueueManager
from synergy_scheduler_manager.quota_manager import QuotaManager
from synergy_scheduler_manager.tests.unit import base
class TestFairshareManager(base.TestCase):
def setUp(self):
super(TestFairshareManager, self).setUp()
self.fsmanager = FairShareManager()
# NOTE(vincent): we cannot import NovaManager in our tests.
# NovaManager depends on the "nova" package (not novaclient), but it is
# not available on PyPI so the test runner will fail to install it.
nova_manager_mock = MagicMock()
self.fsmanager.managers = {
'NovaManager': nova_manager_mock(),
'QueueManager': QueueManager(),
'QuotaManager': QuotaManager(),
'KeystoneManager': KeystoneManager()}
# Mock the configuration since it is initiliazed by synergy-service.
with patch('synergy_scheduler_manager.fairshare_manager.CONF'):
self.fsmanager.setup()
def test_add_project(self):
self.fsmanager.addProject(prj_id=1, prj_name="test_project", share=5)
self.assertEqual(1, self.fsmanager.projects[1]["id"])
self.assertEqual("test_project", self.fsmanager.projects[1]["name"])
self.assertEqual("dynamic", self.fsmanager.projects[1]["type"])
self.assertEqual({}, self.fsmanager.projects[1]["users"])
self.assertEqual({}, self.fsmanager.projects[1]["usage"])
self.assertEqual(5, self.fsmanager.projects[1]["share"])
def test_add_project_no_share(self):
self.fsmanager.addProject(prj_id=1, prj_name="test_project")
self.assertEqual(1, self.fsmanager.projects[1]["id"])
self.assertEqual("test_project", self.fsmanager.projects[1]["name"])
self.assertEqual("dynamic", self.fsmanager.projects[1]["type"])
self.assertEqual({}, self.fsmanager.projects[1]["users"])
self.assertEqual({}, self.fsmanager.projects[1]["usage"])
self.assertEqual(self.fsmanager.default_share,
self.fsmanager.projects[1]["share"])
def test_get_project(self):
self.fsmanager.addProject(prj_id=1, prj_name="test_project")
expected_project = {
"id": 1,
"name": "test_project",
"type": "dynamic",
"users": {},
"usage": {},
"share": self.fsmanager.default_share}
self.assertEqual(expected_project, self.fsmanager.getProject(1))
def test_get_projects(self):
self.fsmanager.addProject(prj_id=1, prj_name="test1")
self.fsmanager.addProject(prj_id=2, prj_name="test2")
expected_projects = {
1: {"id": 1,
"name": "test1",
"type": "dynamic",
"users": {},
"usage": {},
"share": self.fsmanager.default_share},
2: {"id": 2,
"name": "test2",
"type": "dynamic",
"users": {},
"usage": {},
"share": self.fsmanager.default_share}}
self.assertEqual(expected_projects, self.fsmanager.getProjects())
def test_remove_project(self):
self.fsmanager.addProject(prj_id=1, prj_name="test")
self.assertIn(1, self.fsmanager.projects)
self.fsmanager.removeProject(1)
self.assertNotIn(1, self.fsmanager.projects)
def test_calculate_priority_one_user(self):
self.fsmanager.addProject(prj_id=1, prj_name="test")
# Define values used for computing the priority
age_weight = self.fsmanager.age_weight = 1.0
vcpus_weight = self.fsmanager.vcpus_weight = 2.0
memory_weight = self.fsmanager.memory_weight = 3.0
datetime_start = datetime(year=2000, month=1, day=1, hour=0, minute=0)
datetime_stop = datetime(year=2000, month=1, day=1, hour=2, minute=0)
minutes = (datetime_stop - datetime_start).seconds / 60
fairshare_cores = 10
fairshare_ram = 50
# Add a user to the project
self.fsmanager.projects[1]["users"] = {
1: {"fairshare_cores": fairshare_cores,
"fairshare_ram": fairshare_ram}}
# Compute the expected priority given the previously defined values
expected_priority = int(age_weight * minutes +
vcpus_weight * fairshare_cores +
memory_weight * fairshare_ram)
with patch("synergy_scheduler_manager.fairshare_manager.datetime") \
as datetime_mock:
datetime_mock.utcnow.side_effect = (datetime_start, datetime_stop)
priority = self.fsmanager.calculatePriority(user_id=1, prj_id=1)
self.assertEqual(expected_priority, priority)
def test_calculate_fairshare(self):
# TODO(vincent)
pass

View File

@ -0,0 +1,335 @@
# 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 heapq
from mock import call
from mock import create_autospec
from mock import patch
from sqlalchemy.engine.base import Engine
from synergy_scheduler_manager.fairshare_manager import FairShareManager
from synergy_scheduler_manager.queue_manager import PriorityQueue
from synergy_scheduler_manager.queue_manager import Queue
from synergy_scheduler_manager.queue_manager import QueueItem
from synergy_scheduler_manager.tests.unit import base
class TestQueueItem(base.TestCase):
def test_get_set_id(self):
qitem = QueueItem(id=1,
user_id=None,
prj_id=None,
priority=None,
retry_count=None,
creation_time=None,
last_update=None,
data=None)
self.assertEqual(1, qitem.getId())
qitem.setId(10)
self.assertEqual(10, qitem.getId())
def test_get_set_userid(self):
qitem = QueueItem(id=None,
user_id=1,
prj_id=None,
priority=None,
retry_count=None,
creation_time=None,
last_update=None,
data=None)
self.assertEqual(1, qitem.getUserId())
qitem.setUserId(10)
self.assertEqual(10, qitem.getUserId())
def test_get_set_projectid(self):
qitem = QueueItem(id=None,
user_id=None,
prj_id=1,
priority=None,
retry_count=None,
creation_time=None,
last_update=None,
data=None)
self.assertEqual(1, qitem.getProjectId())
qitem.setProjectId(10)
self.assertEqual(10, qitem.getProjectId())
def test_get_set_priority(self):
qitem = QueueItem(id=None,
user_id=None,
prj_id=None,
priority=1,
retry_count=None,
creation_time=None,
last_update=None,
data=None)
self.assertEqual(1, qitem.getPriority())
qitem.setPriority(10)
self.assertEqual(10, qitem.getPriority())
def test_retry_count(self):
qitem = QueueItem(id=None,
user_id=None,
prj_id=None,
priority=None,
retry_count=1,
creation_time=None,
last_update=None,
data=None)
self.assertEqual(1, qitem.getRetryCount())
qitem.setRetryCount(10)
self.assertEqual(10, qitem.getRetryCount())
qitem.incRetryCount()
self.assertEqual(11, qitem.getRetryCount())
def test_get_set_creation_time(self):
qitem = QueueItem(id=None,
user_id=None,
prj_id=None,
priority=None,
retry_count=None,
creation_time="now",
last_update=None,
data=None)
self.assertEqual("now", qitem.getCreationTime())
qitem.setCreationTime("later")
self.assertEqual("later", qitem.getCreationTime())
def test_get_set_last_update(self):
qitem = QueueItem(id=None,
user_id=None,
prj_id=None,
priority=None,
retry_count=None,
creation_time=None,
last_update="now",
data=None)
self.assertEqual("now", qitem.getLastUpdate())
qitem.setLastUpdate("later")
self.assertEqual("later", qitem.getLastUpdate())
def test_get_set_data(self):
qitem = QueueItem(id=None,
user_id=None,
prj_id=None,
priority=None,
retry_count=None,
creation_time=None,
last_update=None,
data=1)
self.assertEqual(1, qitem.getData())
qitem.setData(2)
self.assertEqual(2, qitem.getData())
class TestPriorityQueue(base.TestCase):
def test_put(self):
pq = PriorityQueue()
pq.put(0, "a")
pq.put(5, "b")
pq.put(10, "c")
self.assertIn((0, 0, "a"), pq.queue)
self.assertIn((-5, 1, "b"), pq.queue)
self.assertIn((-10, 2, "c"), pq.queue)
self.assertEqual(3, pq._index)
self.assertEqual((-10, 2, "c"), heapq.heappop(pq.queue))
self.assertEqual((-5, 1, "b"), heapq.heappop(pq.queue))
self.assertEqual((0, 0, "a"), heapq.heappop(pq.queue))
def test_get(self):
pq = PriorityQueue()
pq.put(0, "a")
pq.put(5, "b")
self.assertEqual("b", pq.get())
self.assertEqual("a", pq.get())
def test_size(self):
pq = PriorityQueue()
pq.put(0, "a")
pq.put(5, "b")
pq.put(10, "c")
self.assertEqual(3, pq.size())
class TestQueue(base.TestCase):
def setUp(self):
super(TestQueue, self).setUp()
# Create a Queue that mocks database interaction
self.db_engine_mock = create_autospec(Engine)
self.q = Queue(name="test", db_engine=self.db_engine_mock)
def test_insert_item(self):
self.q.insertItem(user_id=1, prj_id=2, priority=10, data="mydata")
# Check the db call of the item insert
insert_call = call.connect().execute(
'insert into `test` (user_id, prj_id, priority, data) '
'values(%s, %s, %s, %s)', [1, 2, 10, '"mydata"'])
self.assertIn(insert_call, self.db_engine_mock.mock_calls)
# Check the item existence and values in the in-memory queue
priority, index, item = heapq.heappop(self.q.pqueue.queue)
self.assertEqual(-10, priority)
self.assertEqual(0, index)
self.assertEqual(1, item.user_id)
self.assertEqual(2, item.prj_id)
self.assertEqual(10, item.priority)
self.assertEqual(0, item.retry_count)
self.assertIsNone(item.data) # TODO(vincent): should it be "mydata"?
def test_get_size(self):
execute_mock = self.db_engine_mock.connect().execute
execute_call = call('select count(*) from `test`')
fetchone_mock = execute_mock().fetchone
fetchone_mock.return_value = [3]
# Check that getSize() uses the correct sqlalchemy method
self.assertEqual(3, self.q.getSize())
# Check that getSize() uses the correct SQL statement
self.assertEqual(execute_call, execute_mock.call_args)
def test_reinsert_item(self):
# TODO(vincent): what is the purpose of this method?
# It will lead to duplicates.
pass
def test_get_item(self):
# Insert the item and mock its DB insertion
execute_mock = self.db_engine_mock.connect().execute
execute_mock().lastrowid = 123
self.q.insertItem(user_id=1, prj_id=2, priority=10, data="mydata")
# Mock the DB select by returning the same things we inserted before
select_mock = self.db_engine_mock.connect().execute
select_call = call("select user_id, prj_id, priority, retry_count, "
"creation_time, last_update, data from `test` "
"where id=%s", [123])
fetchone_mock = select_mock().fetchone
fetchone_mock.return_value = [1, 2, 10, 0, "now", "now", '"mydata"']
item = self.q.getItem()
self.assertEqual(select_call, select_mock.call_args)
self.assertEqual(123, item.id)
self.assertEqual(1, item.user_id)
self.assertEqual(2, item.prj_id)
self.assertEqual(10, item.priority)
self.assertEqual(0, item.retry_count)
self.assertEqual("now", item.creation_time)
self.assertEqual("now", item.last_update)
self.assertEqual("mydata", item.data)
def test_delete_item(self):
# Mock QueueItem to be deleted
qitem = create_autospec(QueueItem)
qitem.getId.return_value = 123
# Mock the DB delete
execute_mock = self.db_engine_mock.connect().execute
execute_call = call("delete from `test` where id=%s", [123])
self.q.deleteItem(qitem)
self.assertEqual(execute_call, execute_mock.call_args)
def test_update_item(self):
# Mock QueueItem to be updated
qitem = create_autospec(QueueItem)
qitem.getPriority.return_value = 10
qitem.getRetryCount.return_value = 20
qitem.getLastUpdate.return_value = "right_now"
qitem.getId.return_value = 123
# Mock the DB update
execute_mock = self.db_engine_mock.connect().execute
execute_call = call("update `test` set priority=%s, retry_count=%s, "
"last_update=%s where id=%s",
[10, 20, "right_now", 123])
# Check the DB call and that the new QueueItem is in the queue
self.q.updateItem(qitem)
self.assertEqual(execute_call, execute_mock.call_args)
self.assertIn((-10, 0, qitem), self.q.pqueue.queue)
def test_update_priority(self):
qitem1 = QueueItem(
id=1,
user_id=None,
prj_id=None,
priority=0,
retry_count=None,
creation_time="0AC",
last_update="before")
qitem2 = QueueItem(
id=2,
user_id=None,
prj_id=None,
priority=10,
retry_count=None,
creation_time="0AC",
last_update="before")
# TODO(vincent): priority on an item & priority in the queue,
# shouldn't it be the same thing?
self.q.pqueue.put(0, qitem1)
self.q.pqueue.put(10, qitem2)
# Mock fairshare_mgr to fake computing the priority
self.q.fairshare_manager = create_autospec(FairShareManager)
self.q.fairshare_manager.execute.side_effect = [200, 100] # new prio.
# Mock DB update call
execute_mock = self.db_engine_mock.connect().execute
execute_call1 = call("update `test` set priority=%s, last_update=%s "
"where id=%s", [200, "now", 2])
execute_call2 = call("update `test` set priority=%s, last_update=%s "
"where id=%s", [100, "now", 1])
# Mock datetime.now() call so it is predictable
with patch("synergy_scheduler_manager.queue_manager.datetime") as mock:
mock.now.return_value = "now"
self.q.updatePriority()
# Check that that fsmanager.execute was correctly called
self.assertIn(execute_call1, execute_mock.call_args_list)
self.assertIn(execute_call2, execute_mock.call_args_list)
# Check new QueueItem with updated priority are in the pqueue
self.assertEqual(200, qitem2.priority)
self.assertEqual(100, qitem1.priority)

View File

@ -0,0 +1,105 @@
# 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 mock import call
from mock import create_autospec
from mock import MagicMock
from sqlalchemy.engine.base import Engine
from synergy_scheduler_manager.queue_manager import Queue
from synergy_scheduler_manager.queue_manager import QueueItem
from synergy_scheduler_manager.quota_manager import DynamicQuota
from synergy_scheduler_manager.scheduler_manager import Notifications
from synergy_scheduler_manager.scheduler_manager import Worker
from synergy_scheduler_manager.tests.unit import base
class TestNotifications(base.TestCase):
def test_info_dynamic_quota(self):
"""Test that info() makes the correct call to DynamicQuota"""
dynquota_mock = create_autospec(DynamicQuota)
ns = Notifications(dynquota_mock)
payload = {
"state": "deleted",
"instance_id": 1,
"tenant_id": 2,
"memory_mb": 3,
"vcpus": 4}
ns.info(ctxt=None,
publisher_id=None,
event_type="compute.instance.delete.end",
payload=payload,
metadata=None)
self.assertEqual(call(1, 2, 4, 3), dynquota_mock.release.call_args)
class TestWorker(base.TestCase):
def setUp(self):
super(TestWorker, self).setUp()
self.nova_manager_mock = MagicMock()
db_engine_mock = create_autospec(Engine)
self.worker = Worker(
name="test",
queue=Queue("testq", db_engine_mock),
quota=DynamicQuota(),
nova_manager=self.nova_manager_mock)
def test_destroy(self):
"""An empty worker can be destroyed without raising an exception."""
self.worker.destroy()
def test_run_build_server(self):
def nova_exec_side_effect(command, *args, **kwargs):
"""Mock nova.execute to do a successful build."""
if command == "GET_SERVER":
res = {"OS-EXT-STS:vm_state": "building",
"OS-EXT-STS:task_state": "scheduling"}
elif command == "BUILD_SERVER":
res = None
else:
raise TypeError("Wrong arguments to nova exec mock")
return res
# Mock queue.isClosed to do a 1-pass run of the worker
is_closed_mock = create_autospec(self.worker.queue.isClosed)
self.worker.queue.isClosed = is_closed_mock
self.worker.queue.isClosed.side_effect = (False, True)
# Mock QueueItem in the queue
qitem_mock = create_autospec(QueueItem)
get_item_mock = create_autospec(self.worker.queue.getItem)
get_item_mock.return_value = qitem_mock
self.worker.queue.getItem = get_item_mock
# Mock nova "GET_SERVER" and "BUILD_SERVER" calls
nova_exec = self.nova_manager_mock.execute
nova_exec.side_effect = nova_exec_side_effect
# Mock quota allocation
quota_allocate_mock = create_autospec(self.worker.quota.allocate)
quota_allocate_mock.return_value = True
self.worker.quota.allocate = quota_allocate_mock
# Delete item from the queue
delete_item_mock = create_autospec(self.worker.queue.deleteItem)
self.worker.queue.deleteItem = delete_item_mock
# Check that we ask nova to BUILD_SERVER and the qitem is deleted
self.worker.run()
build_server_call = nova_exec.call_args_list[1] # second call
self.assertEqual(("BUILD_SERVER",), build_server_call[0]) # check args
self.assertEqual(call(qitem_mock), delete_item_mock.call_args)

View File

@ -12,3 +12,5 @@ oslotest>=1.10.0 # Apache-2.0
testrepository>=0.0.18
testscenarios>=0.4
testtools>=1.4.0
mock==2.0.0
sqlalchemy>=1.0.0,<1.1.0