diff options
| author | Billy Olsen <billy.olsen@canonical.com> | 2014-12-16 23:39:57 -0700 |
|---|---|---|
| committer | Billy Olsen <billy.olsen@canonical.com> | 2014-12-16 23:39:57 -0700 |
| commit | 37c13bbb3939e0c66e8c4c624fd2b20ce0e36ee8 (patch) | |
| tree | dada81308f17ffa4cebc539d9ceb16cc88d402f5 /unit_tests | |
| parent | 0396bd9803c0aa376cfb4dc0cf012d66755d79f3 (diff) | |
Add more unit test coverage.
Diffstat (limited to 'unit_tests')
| -rw-r--r-- | unit_tests/test_hooks.py | 165 |
1 files changed, 151 insertions, 14 deletions
diff --git a/unit_tests/test_hooks.py b/unit_tests/test_hooks.py index 8836087..4e40bdf 100644 --- a/unit_tests/test_hooks.py +++ b/unit_tests/test_hooks.py @@ -3,6 +3,7 @@ from mock import patch import hooks from test_utils import CharmTestCase +from pymongo.errors import OperationFailure # Defines a set of functions to patch on the hooks object. Any of these # methods will be patched by default on the default invocations of the @@ -29,31 +30,20 @@ class MongoHooksTest(CharmTestCase): # test_config dictionary self.config.side_effect = self.test_config.get - # Note: if we need to mock a specific class of an object, we need to - # invoke the patch.object rather than simply mocking the module itself. - # This is typically recommended for patching any object which belongs - # to the object under test (lookup partial-mocks for more information). @patch.object(hooks, 'restart_mongod') @patch.object(hooks, 'enable_replset') # Note: patching the os.environ dictionary in-line here so there's no # additional parameter sent into the function @patch.dict('os.environ', JUJU_UNIT_NAME='fake-unit/0') - def test_replica_set_relation_joined(self, mock_enable, mock_restart): - # only have 1 invocation of the unit_get, so we'll just tell it what - # to return. Can change to be more sophisticated when necessary. + def test_replica_set_relation_joined(self, mock_enable_replset, + mock_restart): self.unit_get.return_value = 'private.address' self.test_config.set('port', '1234') self.test_config.set('replicaset', 'fake-replicaset') self.relation_id.return_value = 'fake-relation-id' - # Partial mock to control the flow around the if check for the enable - # restart. - mock_enable.return_value = False + mock_enable_replset.return_value = False - # This tests the trigger of firing the relation-joined as the python - # invocation works. However, it doesn't seem to accept all the mocking - # in this case, so I'll just invoke the function directly. - # hooks.hooks.execute(['hooks/replicaset-relation-joined']) hooks.replica_set_relation_joined() # Verify that mongodb was NOT restarted since the replicaset we claimed @@ -67,3 +57,150 @@ class MongoHooksTest(CharmTestCase): 'type': 'replset'} # Check that the relation data was set as we expect it to be set. self.relation_set.assert_called_with('fake-relation-id', exp_rel_vals) + + mock_enable_replset.reset_mock() + self.relation_set.reset_mock() + mock_enable_replset.return_value = True + + hooks.replica_set_relation_joined() + + self.assertTrue(mock_restart.called) + self.relation_set.assert_called_with('fake-relation-id', exp_rel_vals) + + def test_init_replset_no_master_node(self): + retval = hooks.init_replset(master_node=None) + self.assertFalse(retval) + + @patch.object(hooks, 'admin_command') + @patch.object(hooks, 'MongoClient') + @patch.object(hooks, 'config') + @patch.object(hooks, 'mongo_client') + @patch('time.sleep') + def test_init_repl_set(self, mock_sleep, mock_mongo_client_fn, + mock_config, mock_mongo_client, mock_admin_command): + master_node = 'mongo.unit.private.address' + mock_mongo_client_fn.return_value = False + + mock_config.return_value = {'replicaset': 'foo', + 'private-address': 'mongo.local', + 'port': '12345'} + + # Put the OK state (1) at the end and check the loop. + ret_values = [{'myState': x} for x in [0, 2, 3, 4, 1]] + mock_admin_command.side_effect = ret_values + + hooks.init_replset(master_node) + + mock_admin_command.assert_called() + self.assertEqual(len(ret_values), mock_admin_command.call_count) + self.assertEqual(len(ret_values) + 1, mock_sleep.call_count) + + mock_admin_command.reset_mock() + exc = [OperationFailure('Received replSetInitiate'), + OperationFailure('unhandled')] + mock_admin_command.side_effect = exc + + try: + hooks.init_replset(master_node) + self.assertTrue(False, msg="Expected error") + except OperationFailure: + pass + + mock_admin_command.assert_called() + self.assertEqual(2, mock_admin_command.call_count) + + @patch.object(hooks, 'mongo_client_smart') + def test_join_replset(self, mock_mongo_client): + hooks.join_replset() + self.assertFalse(mock_mongo_client.called) + + mock_mongo_client.reset_mock() + hooks.join_replset(master_node='mongo.local') + self.assertFalse(mock_mongo_client.called) + + mock_mongo_client.reset_mock() + hooks.join_replset(host='fake-host') + self.assertFalse(mock_mongo_client.called) + + mock_mongo_client.reset_mock() + hooks.join_replset(master_node='mongo.local', host='fake-host') + mock_mongo_client.assert_called_with('localhost', + 'rs.add("fake-host")') + + @patch.object(hooks, 'mongo_client') + def test_leave_replset(self, mock_mongo_client): + hooks.leave_replset() + self.assertFalse(mock_mongo_client.called) + + mock_mongo_client.reset_mock() + hooks.leave_replset(master_node='mongo.local') + self.assertFalse(mock_mongo_client.called) + + mock_mongo_client.reset_mock() + hooks.leave_replset(host='fake-host') + self.assertFalse(mock_mongo_client.called) + + mock_mongo_client.reset_mock() + hooks.leave_replset('mongo.local', 'fake-host') + mock_mongo_client.assert_called_with('mongo.local', + 'rs.remove("fake-host")') + + @patch.object(hooks, 'apt_install') + @patch.object(hooks, 'apt_update') + @patch.object(hooks, 'add_source') + def test_install_hook(self, mock_add_source, mock_apt_update, + mock_apt_install): + self.test_config.set('source', 'fake-source') + self.test_config.set('key', 'fake-key') + + hooks.install_hook() + mock_add_source.assert_called_with('fake-source', 'fake-key') + mock_apt_update.assert_called_with(fatal=True) + mock_apt_install.assert_called_with(packages=hooks.INSTALL_PACKAGES, + fatal=True) + + @patch.object(hooks, 'admin_command') + @patch.object(hooks, 'MongoClient') + @patch('time.sleep') + def test_am_i_primary(self, mock_sleep, mock_mongo_client, mock_admin_cmd): + mock_admin_cmd.side_effect = [{'myState': x} for x in xrange(5)] + expected_results = [True if x == 1 else False for x in xrange(5)] + + # Check expected return values each time... + for exp in expected_results: + rv = hooks.am_i_primary() + self.assertEqual(exp, rv) + + @patch.object(hooks, 'admin_command') + @patch.object(hooks, 'MongoClient') + @patch('time.sleep') + def test_am_i_primary_too_many_attempts(self, mock_sleep, mock_mongo_client, + mock_admin_cmd): + msg = 'replSetInitiate - should come online shortly' + mock_admin_cmd.side_effect = [OperationFailure(msg) for x in xrange(10)] + + try: + rv = hooks.am_i_primary() + self.assertTrue(False, 'Expected failure.') + except hooks.TimeoutException: + self.assertEqual(mock_admin_cmd.call_count, 9) + pass + + @patch.object(hooks, 'admin_command') + @patch.object(hooks, 'MongoClient') + @patch('time.sleep') + def test_am_i_primary_operation_failures(self, mock_sleep, + mock_mongo_client, mock_admin_cmd): + mock_admin_cmd.side_effect = OperationFailure('EMPTYCONFIG') + + rv = hooks.am_i_primary() + mock_admin_cmd.assert_called() + self.assertFalse(rv) + + mock_admin_cmd.reset_mock() + mock_admin_cmd.side_effect = OperationFailure('unexpected failure') + try: + hooks.am_i_primary() + except OperationFailure: + mock_admin_cmd.assert_called() + |
