def test_register_property_collection(self): # Setup: Create a node object server = Server(utils.MockConnection(None)) node_obj = utils.MockNodeObject(server, None, 'obj_name') # If: I register a property collection generator = mock.MagicMock() collection1 = node_obj._register_property_collection(generator) # Then: # ... The returned collection should be a collection with the provided generator self.assertIsInstance(collection1, node.NodeLazyPropertyCollection) self.assertIs(collection1._generator, generator) # ... The collection should be added to the list of registered collections self.assertEqual(len(node_obj._property_collections), 2) self.assertIn(collection1, node_obj._property_collections) # If: I add another one collection2 = node_obj._register_property_collection(generator) # Then: The collection should be appended to the list of registered collections self.assertEqual(len(node_obj._property_collections), 3) self.assertIn(collection1, node_obj._property_collections) self.assertIn(collection2, node_obj._property_collections)
def test_register_child_collection(self): # Setup: Create a node object server = Server(utils.MockConnection(None)) node_obj = utils.MockNodeObject(server, None, 'obj_name') # If: I register a child collection mock_class1 = mock.MagicMock() mock_class1.__name__ = 'mock_class1' mock_class1.get_nodes_for_parent = mock.MagicMock() collection1 = node_obj._register_child_collection(mock_class1) # Then # ... The returned collection should be a collection with the given generator self.assertIsInstance(collection1, node.NodeCollection) # ... The collection should be added to the list of registered collections self.assertEqual(len(node_obj._child_collections), 1) # If: I add another one mock_class2 = mock.MagicMock() mock_class2.__name__ = 'mock_class2' mock_class2.get_nodes_for_parent = mock.MagicMock() collection2 = node_obj._register_child_collection(mock_class2) # Then: The collection should be appended to the list of registered collections self.assertEqual(len(node_obj._child_collections), 2) self.assertTrue( mock_class1.__name__ in node_obj._child_collections.keys()) self.assertTrue( mock_class2.__name__ in node_obj._child_collections.keys()) self.assertIs(node_obj._child_collections[mock_class1.__name__], collection1) self.assertIs(node_obj._child_collections[mock_class2.__name__], collection2)
def test_init(self): # If: I construct a new server object host = 'host' port = '1234' dbname = 'dbname' mock_conn = utils.MockConnection(None, name=dbname, host=host, port=port) server = Server(mock_conn) # Then: # ... The assigned properties should be assigned self.assertIsInstance(server._conn, ServerConnection) self.assertIsInstance(server.connection, ServerConnection) self.assertIs(server.connection.connection, mock_conn) self.assertEqual(server._host, host) self.assertEqual(server.host, host) self.assertEqual(server._port, int(port)) self.assertEqual(server.port, int(port)) self.assertEqual(server._maintenance_db_name, dbname) self.assertEqual(server.maintenance_db_name, dbname) self.assertTupleEqual(server.version, server._conn.version) # ... Recovery options should be a lazily loaded thing self.assertIsInstance(server._recovery_props, NodeLazyPropertyCollection) for key, collection in server._child_objects.items(): # ... The child object collection a NodeCollection self.assertIsInstance(collection, NodeCollection) # ... There should be a property mapped to the node collection prop = getattr(server, inflection.pluralize(key.lower())) self.assertIs(prop, collection)
def test_urn_recursive(self): # Setup: # ... Create a node object with a parent server = Server(utils.MockConnection(None)) node_obj1 = utils.MockNodeObject(server, None, 'parent_name') node_obj1._oid = 123 node_obj2 = utils.MockNodeObject(server, node_obj1, 'obj_name') node_obj2._oid = 456 # If: I get the URN for the child node object urn = node_obj2.urn # Then: # ... I expect it to be formatted as a URL parsed_url = parse.urlparse(urn) # ... The netlocation should be equal to the urn base (with added slashes) # NOTE: Server URN Base is tested in the server class unit tests self.assertEqual(f'//{parsed_url.netloc}/', server.urn_base) # ... The path should have multiple folders under it (list comprehension removes empty strings) split_path = [x for x in parsed_url.path.split('/') if x] self.assertEqual(len(split_path), 2) # ... The parent path should be first self.assertEqual(split_path[0], f'{node_obj1.__class__.__name__}.{node_obj1.oid}') # ... The child path should be second self.assertEqual(split_path[1], f'{node_obj2.__class__.__name__}.{node_obj2.oid}')
def test_from_node_query(self): # If: I create a new object from a node row with the expected parent type mock_server = Server(utils.MockConnection(None)) mock_parent = utils.MockNodeObject( mock_server, None, 'parent') if not self.parent_expected_to_be_none else None obj = self.class_for_test._from_node_query(mock_server, mock_parent, **self.node_query) # Then: # ... The returned object must be an instance of the class NodeObjectTestBase.unittest.assertIsInstance(obj, NodeObject) NodeObjectTestBase.unittest.assertIsInstance(obj, self.class_for_test) # ... Validate the node object properties utils.assert_threeway_equals(mock_server, obj._server, obj.server) utils.assert_threeway_equals(self.node_query['oid'], obj._oid, obj.oid) utils.assert_threeway_equals(self.node_query['name'], obj._name, obj.name) # ... Validate the basic properties for attr, value in self.basic_properties.items(): NodeObjectTestBase.unittest.assertEqual(getattr(obj, attr), value) # ... Validate the collections for attr in self.collections: NodeObjectTestBase.unittest.assertIsInstance( getattr(obj, attr), NodeCollection) # ... Call the validation function self._custom_validate_from_node(obj, mock_server)
def test_execute_dict_success(self): # Setup: Create a mock server connection that will return a result set mock_cursor = utils.MockCursor(utils.get_mock_results()) mock_conn = utils.MockConnection(mock_cursor) # noinspection PyTypeChecker server_conn = pgsmo_utils.querying.ServerConnection(mock_conn) # If: I execute a query as a dictionary results = server_conn.execute_dict('SELECT * FROM pg_class') # Then: # ... Both the columns and the results should be returned self.assertIsInstance(results, tuple) self.assertEqual(len(results), 2) # ... I should get a list of columns returned to me cols = results[0] self.assertIsInstance(cols, list) self.assertListEqual(cols, mock_cursor.description) # ... I should get the results formatted as a list of dictionaries rows = results[1] self.assertIsInstance(rows, list) for idx, row in enumerate(rows): self.assertDictEqual(row, mock_cursor._results[1][idx]) # ... The cursor should be closed mock_cursor.close.assert_called_once()
def test_get_obj_by_urn_wrong_collection(self): # Setup: Create a server object server = Server(utils.MockConnection(None)) with self.assertRaises(ValueError): # If: I get an object by its URN with a URN that points to an invalid path off the server # Then: I should get an exception invalid_urn = parse.urljoin(server.urn_base, 'Datatype.123/') server.get_object_by_urn(invalid_urn)
def test_get_obj_by_urn_wrong_server(self): # Setup: Create a server object server = Server(utils.MockConnection(None)) with self.assertRaises(ValueError): # If: I get an object by its URN with a URN that is invalid for the server # Then: I should get an exception invalid_urn = '//[email protected]:456/Database.123/' server.get_object_by_urn(invalid_urn)
def test_get_obj_by_urn_empty(self): # Setup: Create a server object server = Server(utils.MockConnection(None)) test_cases = [None, '', '\t \n\r'] for test_case in test_cases: with self.assertRaises(ValueError): # If: I get an object by its URN without providing a URN # Then: I should get an exception server.get_object_by_urn(test_case)
def test_get_obj_by_urn_invalid_collection(self): # Setup: Create a node object (without any collections under it) server = Server(utils.MockConnection(None)) node_obj = utils.MockNodeObject(server, None, 'obj_name') with self.assertRaises(ValueError): # If: I have a URN fragment that goes into a collection that doesn't exist # Then: I should get an exception fragment = '/Database.123/' node_obj.get_object_by_urn(fragment)
def _get_node_for_parents_mock_connection(): # ... Create a mockup of a server connection with a mock executor mock_action = mock.Mock() mock_objs = [{'name': 'abc', 'oid': 123}, {'name': 'def', 'oid': 456}] mock_executor = mock.MagicMock(return_value=([{}, {}], mock_objs)) mock_server = Server(utils.MockConnection(None, version="10101"), mock_action) mock_server.connection.execute_dict = mock_executor return mock_server, mock_executor, mock_objs
def test_recovery_properties(self): # Setup: # NOTE: We're *not* mocking out the template rendering b/c this will verify that there's a template # ... Create a mock query execution that will return the properties mock_exec_dict = mock.MagicMock( return_value=([], [TestServer.CHECK_RECOVERY_ROW])) # ... Create an instance of the class and override the connection mock_conn = ServerConnection(utils.MockConnection(None)) mock_conn.execute_dict = mock_exec_dict obj = Server(utils.MockConnection(None)) obj._conn = mock_conn # If: I retrieve all the values in the recovery properties # Then: # ... The properties based on the properties should be availble self.assertEqual(obj.in_recovery, TestServer.CHECK_RECOVERY_ROW['inrecovery']) self.assertEqual(obj.wal_paused, TestServer.CHECK_RECOVERY_ROW['isreplaypaused'])
def _get_mock_node_generator(): server = Server(utils.MockConnection(None)) mock_object1 = utils.MockNodeObject(server, None, 'a') mock_object1._oid = 123 mock_object2 = utils.MockNodeObject(server, None, 'b') mock_object2._oid = 456 mock_objects = [mock_object1, mock_object2] return mock.MagicMock(return_value=mock_objects), mock_objects
def setUp(self): mock_server = Server(utils.MockConnection(None)) db = Database(mock_server, mock_server.maintenance_db_name) mock_server._child_objects[ Database.__name__] = self._as_node_collection([db]) mock_server._search_path = self._as_node_collection([MYSCHEMA]) self.schema1 = Schema(mock_server, db, MYSCHEMA) self.schema2 = Schema(mock_server, db, MYSCHEMA2) db._schemas = self._as_node_collection([self.schema1, self.schema2]) self.mock_server = mock_server self.executor: MetadataExecutor = MetadataExecutor(mock_server)
def test_role_get_database_node(self): # If: I create a DB that is connected name = 'dbname' mock_server = Server(utils.MockConnection(None, name='not_connected')) db = Role(mock_server, name) # Then: node = db.get_database_node() # assert: self.assertIsNone(node)
def test_get_obj_by_urn_base_case(self): # Setup: Create a node object server = Server(utils.MockConnection(None)) node_obj = utils.MockNodeObject(server, None, 'obj_name') # If: I have a URN fragment that returns the object fragment = '/' obj = node_obj.get_object_by_urn(fragment) # Then: I should get that object back self.assertIs(obj, node_obj)
def test_template_path_pg(self): # Setup: Create a mock connection that has PG as the server type mock_server = Server(utils.MockConnection(None)) mock_server._ServerConnection__server_type = mock.MagicMock( return_value='pg') # If: I ask for the template path of the class path = self.class_for_test._template_root(mock_server) # Then: The path should be a string that exists NodeObjectTestBase.unittest.assertIsInstance(path, str) NodeObjectTestBase.unittest.assertTrue(os.path.exists(path))
def test_database_get_database_node(self): # If: I create a DB that is connected name = 'dbname' mock_server = Server(utils.MockConnection(None, name='not_connected')) db = Database(mock_server, name) # Then: node = db.get_database_node() # assert: self.assertIsNotNone(node) self.assertEqual(node.__class__.__name__, 'Database')
def test_get_nodes_for_parent_with_parent(self): # Setup: # ... Create a server connection that will return some mock node rows mock_server, mock_executor, mock_objs = _get_node_for_parents_mock_connection( ) # ... Create a mock _from_node generator (so we can validate calls) mock_obj = {} mock_from_node = mock.MagicMock(return_value=mock_obj) # ... Create a mock template rendered mock_render = mock.MagicMock(return_value="SQL") mock_template_path = mock.MagicMock(return_value="path") # ... Create an object that will be the parent of these nodes name = 'postgres' parent = Database(mock_server, name) parent._oid = 123 parent._connection = utils.MockConnection(None, version="10101") parent._connection.execute_dict = mock_executor # ... Patch the template rendering, and the _from_node_query patch_render_template = 'pgsmo.objects.node_object.templating.render_template' patch_template_path = 'pgsmo.objects.node_object.templating.get_template_path' patch_from_node_query = 'tests.pgsmo_tests.utils.MockNodeObject._from_node_query' with mock.patch(patch_render_template, mock_render, create=True), \ mock.patch(patch_template_path, mock_template_path, create=True), \ mock.patch(patch_from_node_query, mock_from_node, create=True): # If: I ask for a collection of nodes *with a parent object* nodes = utils.MockNodeObject.get_nodes_for_parent( mock_server, parent) # Then: # ... The template path and template renderer should have been called once mock_template_path.assert_called_once_with('template_root', 'nodes.sql', mock_server.version) mock_render.assert_called_once_with('path', macro_roots=None, **{'parent_id': 123}) # ... A query should have been executed mock_executor.assert_called_once_with('SQL') # ... The _from_node should have been called twice with the results of the query mock_from_node.assert_any_call(mock_server, parent, **mock_objs[0]) mock_from_node.assert_any_call(mock_server, parent, **mock_objs[1]) # ... The output should be a list of objects the _from_node returned self.assertIsInstance(nodes, list) self.assertListEqual(nodes, [mock_obj, mock_obj])
def test_init_connected(self): # If: I create a DB that is connected name = 'dbname' mock_server = Server(utils.MockConnection(None, name=name)) db = Database(mock_server, name) # Then: # ... Default validation should pass self._init_validation(db, mock_server, None, name) # ... The schema node collection should be defined self.assertIsInstance(db._schemas, NodeCollection) self.assertIs(db.schemas, db._schemas)
def test_get_obj_by_urn_recurses(self): # Setup: Create a node object with a collection under it server = Server(utils.MockConnection(None)) db_obj = utils.MockNodeObject(server, None, 'db_name') sc_obj = utils.MockNodeObject(server, db_obj, 'schema_name') db_obj._child_collections = {'Schema': {123: sc_obj}} # If: I ask for an object that recurses fragment = '/Schema.123/' obj = db_obj.get_object_by_urn(fragment) # Then: The object I get back should be the same as the one I created self.assertIs(obj, sc_obj)
def test_get_obj_by_urn_success(self): # Setup: Create a server with a database under it server = Server(utils.MockConnection(None)) mock_db = Database(server, 'test_db') mock_db._oid = 123 server._child_objects[Database.__name__] = {123: mock_db} # If: I get an object by its URN urn = parse.urljoin(server.urn_base, '/Database.123/') obj = server.get_object_by_urn(urn) # Then: The object I get back should be the same as the object I provided self.assertIs(obj, mock_db)
def test_init_not_connected(self): # If: I create a DB that is connected name = 'dbname' mock_conn = Server(utils.MockConnection(None, name='not_connected')) db = Database(mock_conn, name) # Then: # ... Default validation should pass self._init_validation(db, mock_conn, None, name) # ... The schema node collection should not be defined self.assertIsNotNone(db._schemas) self.assertIsNotNone(db.schemas)
def test_execute_dict_fail(self): # Setup: Create a mock server connection that will raise an exception mock_cursor = utils.MockCursor(None, throw_on_execute=True) mock_conn = utils.MockConnection(mock_cursor) # noinspection PyTypeChecker server_conn = pgsmo_utils.querying.ServerConnection(mock_conn) # If: I execute a query as a dictionary # Then: # ... I should get an exception with self.assertRaises(Exception): server_conn.execute_dict('SELECT * FROM pg_class') # ... The cursor should be closed mock_cursor.close.assert_called_once()
def test_urn_base(self): # Setup: # ... Create a server object that has a connection server = Server(utils.MockConnection(None)) # If: I get the URN base for the server urn_base = server.urn_base # Then: The urn base should match the expected outcome urn_base_regex = re.compile( r'//(?P<user>.+)@(?P<host>.+):(?P<port>\d+)') urn_base_match = urn_base_regex.match(urn_base) self.assertIsNotNone(urn_base_match) self.assertEqual(urn_base_match.groupdict()['user'], server.connection.dsn_parameters['user']) self.assertEqual(urn_base_match.groupdict()['host'], server.host) self.assertEqual(int(urn_base_match.groupdict()['port']), server.port)
def test_init(self): # If: I create an instance of the provided class mock_server = Server(utils.MockConnection(None)) mock_parent = utils.MockNodeObject( mock_server, None, 'parent') if not self.parent_expected_to_be_none else None name = 'test' obj = self.init_lambda(mock_server, mock_parent, name) # Then: # ... Perform the init validation # noinspection PyTypeChecker self._init_validation(obj, mock_server, mock_parent, name) # ... Call the custom validation function self._custom_validate_init(obj, mock_server)
def test_maintenance_db(self): # Setup: # ... Create a server object that has a connection obj = Server(utils.MockConnection(None, name='dbname')) # ... Mock out the database lazy loader's indexer mock_db = {} mock_db_collection = mock.Mock() mock_db_collection.__getitem__ = mock.MagicMock(return_value=mock_db) obj._child_objects[Database.__name__] = mock_db_collection # If: I retrieve the maintenance db for the server maintenance_db = obj.maintenance_db # Then: # ... It must have come from the mock handler self.assertIs(maintenance_db, mock_db) obj._child_objects[ Database.__name__].__getitem__.assert_called_once_with('dbname')
def test_init(self): # If: I create a node object server = Server(utils.MockConnection(None)) parent = utils.MockNodeObject(server, None, 'parent') node_obj = utils.MockNodeObject(server, parent, 'abc') # Then: The properties should be assigned as defined utils.assert_threeway_equals(None, node_obj._oid, node_obj.oid) utils.assert_threeway_equals('abc', node_obj._name, node_obj.name) utils.assert_threeway_equals(server, node_obj._server, node_obj.server) utils.assert_threeway_equals(parent, node_obj._parent, node_obj.parent) self.assertDictEqual(node_obj._child_collections, {}) self.assertEqual(len(node_obj._property_collections), 1) self.assertIsInstance(node_obj._full_properties, node.NodeLazyPropertyCollection) self.assertEqual(node_obj._full_properties._generator, node_obj._property_generator)
def test_refresh(self): # Setup: # ... Create a server object that has a connection obj = Server(utils.MockConnection(None)) # ... Mock out the reset methods on the various collections obj.databases.reset = mock.MagicMock() obj.roles.reset = mock.MagicMock() obj.tablespaces.reset = mock.MagicMock() obj._recovery_props.reset = mock.MagicMock() # If: I refresh the server obj.refresh() # Then: The collections should have been reset obj.databases.reset.assert_called_once() obj.roles.reset.assert_called_once() obj.tablespaces.reset.assert_called_once() obj._recovery_props.reset.assert_called_once()
def _test_scripting_mixins(self): # Setup: Create an instance of the object mock_server = Server(utils.MockConnection(None)) mock_grand_parent = utils.MockNodeObject( mock_server, None, 'grandparent') if not self.parent_expected_to_be_none else None mock_parent = utils.MockNodeObject( mock_server, mock_grand_parent, 'parent') if not self.parent_expected_to_be_none else None name = 'test' obj = self.init_lambda(mock_server, mock_parent, name) obj._full_properties = self.property_query if isinstance(obj, ScriptableCreate): # If: I script for create script = obj.create_script() # Then: The script should successfully return utils.assert_is_not_none_or_whitespace(script) if isinstance(obj, ScriptableDelete): # If: I script for delete script = obj.delete_script() # Then: The script should successfully return utils.assert_is_not_none_or_whitespace(script) if isinstance(obj, ScriptableUpdate): # If: I script for update script = obj.update_script() # Then: The script should successfully return utils.assert_is_not_none_or_whitespace(script) if isinstance(obj, ScriptableSelect): # If: I script for select script = obj.select_script() # Then: The script should successfully return utils.assert_is_not_none_or_whitespace(script)