Fix check_service_client_function mock_args bug
A recent commit Ib066add5ff09bd3b32b293833ed6b7a3d5b43955
added a new `mock_args` argument to check_service_client_function
which, if provided, asserts that the mocked REST client is
called with `mock_args`.
The problem is that the payload passed to the REST client uses
json.dumps -- which outputs a dictionary as a string in a
non-deterministic ordering of key/values. This means that
using json.dumps can produce different strings for the same
dictionary. This is a problem because sometimes the strings
might be in a different order causing test failure [0].
The solution is to mock json.dumps and force it to use
sort_keys=True so that the response body is deterministic.
This should be done for create and update actions --
wherever json.dumps is used. This can be done by mocking
json.dumps in each test function that needs to mock out
the actual json.dumps to use sort_keys=True.
This commit mocks json.dumps to use sort_keys=True in
test cases that use mock_args and rely on json.dumps.
[0] http://logs.openstack.org/56/474356/9/check/gate-tempest-python27-ubuntu-xenial/2205abc/console.html#_2017-06-20_17_02_12_867914
Change-Id: I08bf3ac8c471a8112984dc52a2b5b143634d83b7
diff --git a/tempest/tests/lib/services/network/test_security_group_rules_client.py b/tempest/tests/lib/services/network/test_security_group_rules_client.py
index ebffcbe..b9c17a1 100644
--- a/tempest/tests/lib/services/network/test_security_group_rules_client.py
+++ b/tempest/tests/lib/services/network/test_security_group_rules_client.py
@@ -15,8 +15,10 @@
import copy
+import mock
from oslo_serialization import jsonutils as json
+from tempest.lib.services.network import base as network_base
from tempest.lib.services.network import security_group_rules_client
from tempest.tests.lib import fake_auth_provider
from tempest.tests.lib.services import base
@@ -80,16 +82,22 @@
kwargs = {'direction': 'egress',
'security_group_id': '85cc3048-abc3-43cc-89b3-377341426ac5',
'remote_ip_prefix': None}
+ payload = json.dumps({"security_group_rule": kwargs}, sort_keys=True)
+ json_dumps = json.dumps
- self.check_service_client_function(
- self.client.create_security_group_rule,
- 'tempest.lib.common.rest_client.RestClient.post',
- self.FAKE_SECURITY_GROUP_RULE,
- bytes_body,
- status=201,
- mock_args=['v2.0/security-group-rules',
- json.dumps({"security_group_rule": kwargs})],
- **kwargs)
+ # NOTE: Use sort_keys for json.dumps so that the expected and actual
+ # payloads are guaranteed to be identical for mock_args assert check.
+ with mock.patch.object(network_base.json, 'dumps') as mock_dumps:
+ mock_dumps.side_effect = lambda d: json_dumps(d, sort_keys=True)
+
+ self.check_service_client_function(
+ self.client.create_security_group_rule,
+ 'tempest.lib.common.rest_client.RestClient.post',
+ self.FAKE_SECURITY_GROUP_RULE,
+ bytes_body,
+ status=201,
+ mock_args=['v2.0/security-group-rules', payload],
+ **kwargs)
def _test_show_security_group_rule(self, bytes_body=False):
self.check_service_client_function(
diff --git a/tempest/tests/lib/services/network/test_security_groups_client.py b/tempest/tests/lib/services/network/test_security_groups_client.py
index d066378..f96805f 100644
--- a/tempest/tests/lib/services/network/test_security_groups_client.py
+++ b/tempest/tests/lib/services/network/test_security_groups_client.py
@@ -15,8 +15,10 @@
import copy
+import mock
from oslo_serialization import jsonutils as json
+from tempest.lib.services.network import base as network_base
from tempest.lib.services.network import security_groups_client
from tempest.tests.lib import fake_auth_provider
from tempest.tests.lib.services import base
@@ -89,15 +91,22 @@
def _test_create_security_group(self, bytes_body=False):
kwargs = {'name': 'fake-security-group-name'}
- self.check_service_client_function(
- self.client.create_security_group,
- 'tempest.lib.common.rest_client.RestClient.post',
- self.FAKE_SECURITY_GROUP,
- bytes_body,
- status=201,
- mock_args=['v2.0/security-groups',
- json.dumps({"security_group": kwargs})],
- **kwargs)
+ payload = json.dumps({"security_group": kwargs}, sort_keys=True)
+ json_dumps = json.dumps
+
+ # NOTE: Use sort_keys for json.dumps so that the expected and actual
+ # payloads are guaranteed to be identical for mock_args assert check.
+ with mock.patch.object(network_base.json, 'dumps') as mock_dumps:
+ mock_dumps.side_effect = lambda d: json_dumps(d, sort_keys=True)
+
+ self.check_service_client_function(
+ self.client.create_security_group,
+ 'tempest.lib.common.rest_client.RestClient.post',
+ self.FAKE_SECURITY_GROUP,
+ bytes_body,
+ status=201,
+ mock_args=['v2.0/security-groups', payload],
+ **kwargs)
def _test_show_security_group(self, bytes_body=False):
self.check_service_client_function(
@@ -113,15 +122,23 @@
resp_body = copy.deepcopy(self.FAKE_SECURITY_GROUP)
resp_body["security_group"]["name"] = 'updated-security-group-name'
- self.check_service_client_function(
- self.client.update_security_group,
- 'tempest.lib.common.rest_client.RestClient.put',
- resp_body,
- bytes_body,
- security_group_id=self.FAKE_SEC_GROUP_ID,
- mock_args=['v2.0/security-groups/%s' % self.FAKE_SEC_GROUP_ID,
- json.dumps({'security_group': kwargs})],
- **kwargs)
+ payload = json.dumps({'security_group': kwargs}, sort_keys=True)
+ json_dumps = json.dumps
+
+ # NOTE: Use sort_keys for json.dumps so that the expected and actual
+ # payloads are guaranteed to be identical for mock_args assert check.
+ with mock.patch.object(network_base.json, 'dumps') as mock_dumps:
+ mock_dumps.side_effect = lambda d: json_dumps(d, sort_keys=True)
+
+ self.check_service_client_function(
+ self.client.update_security_group,
+ 'tempest.lib.common.rest_client.RestClient.put',
+ resp_body,
+ bytes_body,
+ security_group_id=self.FAKE_SEC_GROUP_ID,
+ mock_args=['v2.0/security-groups/%s' % self.FAKE_SEC_GROUP_ID,
+ payload],
+ **kwargs)
def test_list_security_groups_with_str_body(self):
self._test_list_security_groups()