diff --git a/py_dbcn/connectors/core/display.py b/py_dbcn/connectors/core/display.py index cebc7bb4d615f0e48f07f7f2bc26fdbaae12e7a5..c94da2f19c4078c5c75f2c6ced34fea9f0a4583d 100644 --- a/py_dbcn/connectors/core/display.py +++ b/py_dbcn/connectors/core/display.py @@ -110,7 +110,7 @@ class TableDisplay: # Add results. base_row_str = '| {0:<' + '{0}'.format(inner_row_len) + '} |\n' - for result in results: + for result in sorted(results): msg_str += base_row_str.format(str(result).strip()) msg_str += divider @@ -122,12 +122,31 @@ class TableDisplay: def describe(self, results, logger): """Display logic for tables.describe().""" # Initialize record col sets. + + if self._base._config.db_type == 'MySQL': + field_col_index = 0 + type_col_index = 1 + null_col_index = 2 + key_col_index = 3 + default_col_index = 4 + extra_col_index = 5 + elif self._base._config.db_type == 'PostgreSQL': + field_col_index = 3 + type_col_index = 27 + null_col_index = 6 + key_col_index = None + default_col_index = 5 + extra_col_index = None + else: + raise NotImplementedError('Please define expected index to find describe columns.') + field_col_values = [] type_col_values = [] null_col_values = [] key_col_values = [] default_col_values = [] extra_col_values = [] + field_col_max_len = 5 type_col_max_len = 4 null_col_max_len = 4 @@ -135,46 +154,81 @@ class TableDisplay: default_col_max_len = 7 extra_col_max_len = 5 + print('\n\n\n\n') + if len(results) > 1: + for index in range(len(results[1])): + print(' {0} - {1}'.format(index, results[1][index])) + # Populate record col sets. + undefined_value = '{0}__UNDEFINED'.format(self._base._config.db_type) for record in results: - # Handle col 1. - value = record[0] - if value is None: + # Handle col "name". + value = undefined_value + if field_col_index is not None: + value = record[field_col_index] + if value == undefined_value: + value = '' + elif value is None: value = 'NULL' field_col_values.append(value) field_col_max_len = max(field_col_max_len, len(str(value))) - # Handle col 2. - value = record[1] - if value is None: + # Handle col "type". + value = undefined_value + if type_col_index is not None: + value = record[type_col_index] + if value == undefined_value: + value = 'UNKNOWN' + elif value is None: value = 'NULL' type_col_values.append(value) type_col_max_len = max(type_col_max_len, len(str(value))) - # Handle col 3. - value = record[2] - if value is None: + # Handle col "nullable". + value = undefined_value + if null_col_index is not None: + value = record[null_col_index] + if value == undefined_value: + value = 'UNKNOWN' + elif value is None: value = 'NULL' null_col_values.append(value) null_col_max_len = max(null_col_max_len, len(str(value))) - # Handle col 4. - value = record[3] - if value is None: + # Handle col "key". + value = undefined_value + if key_col_index is not None: + value = record[key_col_index] + if value == undefined_value: + value = 'UNKNOWN' + elif value is None: value = 'NULL' key_col_values.append(value) key_col_max_len = max(key_col_max_len, len(str(value))) - # Handle col 5. - value = record[4] - if value is None: + # Handle col "default". + value = undefined_value + if default_col_index is not None: + value = record[default_col_index] + if value == undefined_value: + value = 'UNKNOWN' + elif value is None: value = 'NULL' + elif value.startswith('nextval('): + # TODO: See https://stackoverflow.com/a/8148177 + pass + # value = self._base.query.execute('SELECT {0}'.format(value), display_query=False)[0] + # value = self._base.query.execute('SELECT {0}'.format(value)) default_col_values.append(value) default_col_max_len = max(default_col_max_len, len(str(value))) - # Handle col 6. - value = record[5] - if value is None: + # Handle col "extra". + value = undefined_value + if extra_col_index is not None: + value = record[extra_col_index] + if value == undefined_value: + value = 'UNKNOWN' + elif value is None: value = 'NULL' extra_col_values.append(value) extra_col_max_len = max(extra_col_max_len, len(str(value))) @@ -250,12 +304,20 @@ class RecordDisplay: select_clause = '*' else: select_clause = str(select_clause).strip() + + if self._base._config.db_type == 'MySQL': + col_name_index = 0 + elif self._base._config.db_type == 'PostgreSQL': + col_name_index = 3 + else: + raise NotImplementedError('Please define expected index to find column name.') + # Handle based on star or specific cols. # TODO: Probably need to tokenize this, to properly compare. if select_clause == '*' or '(*)' in select_clause: # Calculate column header values, using all columns. table_cols = [ - x[0] + x[col_name_index] for x in self._base.tables.describe(table_name, display_query=False, display_results=False) ] else: @@ -268,22 +330,28 @@ class RecordDisplay: # Calculate column header values, filtered by select clause. table_cols = [ - x[0] + x[col_name_index] for x in self._base.tables.describe(table_name, display_query=False, display_results=False) - if x[0] in select_clause + if x[col_name_index] in select_clause ] col_len_array = [] total_col_len = 0 for table_col in table_cols: col_len = len(table_col) record_len = self._base.query.execute( - 'SELECT MAX(LENGTH({2}{0}{2})) FROM {1};'.format( + self._parent.max_col_length_query .format( table_col, table_name, - self._base.validate._quote_column_format, + self._base.validate._quote_identifier_format, ), display_query=False, )[0][0] + print('') + print('table_name: {0}'.format(table_name)) + print('tab_col: {0}'.format(table_col)) + print('col_len: {0}'.format(col_len)) + print('record_len: {0}'.format(record_len)) + print('') length = max(col_len, record_len or 0) col_len_array.append(length) total_col_len += length + 2 diff --git a/py_dbcn/connectors/mysql/display.py b/py_dbcn/connectors/mysql/display.py index 81f83663e2cdf7b0a650d3426a2d3c31531c6b23..b543d1010f9c52fbb7e805c5590683da0a8160cf 100644 --- a/py_dbcn/connectors/mysql/display.py +++ b/py_dbcn/connectors/mysql/display.py @@ -5,6 +5,7 @@ Contains database connection logic specific to MySQL databases. """ # System Imports. +import textwrap # Internal Imports. from py_dbcn.connectors.core.display import BaseDisplay @@ -24,3 +25,7 @@ class MysqlDisplay(BaseDisplay): super().__init__(parent, *args, **kwargs) logger.debug('Generating related (MySQL) Display class.') + + self.max_col_length_query = textwrap.dedent(""" + SELECT MAX(LENGTH({2}{0}{2})) FROM {1}; + """.strip()) diff --git a/py_dbcn/connectors/postgresql/display.py b/py_dbcn/connectors/postgresql/display.py index d1b151482749d53981a96bbeda92df2cbf6cae76..b5e2cfcdef96c3fce244a28241ca836062377ab5 100644 --- a/py_dbcn/connectors/postgresql/display.py +++ b/py_dbcn/connectors/postgresql/display.py @@ -5,6 +5,7 @@ Contains database connection logic specific to PostgreSQL databases. """ # System Imports. +import textwrap # Internal Imports. from py_dbcn.connectors.core.display import BaseDisplay @@ -24,3 +25,7 @@ class PostgresqlDisplay(BaseDisplay): super().__init__(parent, *args, **kwargs) logger.debug('Generating related (PostgreSQL) Display class.') + + self.max_col_length_query = textwrap.dedent(""" + SELECT MAX(LENGTH(CAST({2}{0}{2} as text))) FROM {1}; + """.strip()) diff --git a/tests/connectors/core/test_display.py b/tests/connectors/core/test_display.py index 651dd4c88451edda17cd54e2c23ccea58b059d33..b21bab12b06214a0ff28e7ccd5f6cfec71e5ae42 100644 --- a/tests/connectors/core/test_display.py +++ b/tests/connectors/core/test_display.py @@ -121,7 +121,7 @@ class CoreDisplayBaseTestMixin: # Test with empty value. Should equal length of database name. return_val = self.connector.display._get_longest(test_list) - self.assertEqual(return_val, 42) + self.assertEqual(return_val, 37 + len(self.db_type)) # Test with select function. Aka, verify that it equals length of database name. db_name_len = len(self.connector.database.select()) @@ -131,47 +131,47 @@ class CoreDisplayBaseTestMixin: # Test with list being larger. test_list = ['{0}__plus_ten'.format(self.test_db_name)] return_val = self.connector.display._get_longest(test_list) - self.assertEqual(return_val, 52) + self.assertEqual(return_val, 47 + len(self.db_type)) # Test with multiple list values, all smaller. test_list = ['a', 'bb', 'ccc'] return_val = self.connector.display._get_longest(test_list) - self.assertEqual(return_val, 42) + self.assertEqual(return_val, 37 + len(self.db_type)) # Test with multiple list values, one equal. test_list = [ 'a', 'bb', - 'd' * 42, + 'd' * (37 + len(self.db_type)), 'ccc', ] return_val = self.connector.display._get_longest(test_list) - self.assertEqual(return_val, 42) + self.assertEqual(return_val, 37 + len(self.db_type)) # Test with multiple list values, one larger. test_list = [ 'a', 'bb', - 'd' * 42, - 'e' * 47, + 'd' * (37 + len(self.db_type)), + 'e' * (42 + len(self.db_type)), 'ccc', ] return_val = self.connector.display._get_longest(test_list) - self.assertEqual(return_val, 47) + self.assertEqual(return_val, 42 + len(self.db_type)) # Test with multiple list values, multiple larger. test_list = [ 'a', - 'h' * 49, - 'f' * 45, + 'h' * (44 + len(self.db_type)), + 'f' * (40 + len(self.db_type)), 'bb', - 'd' * 42, - 'e' * 47, + 'd' * (37 + len(self.db_type)), + 'e' * (42 + len(self.db_type)), 'ccc', - 'g' * 46, + 'g' * (41 + len(self.db_type)), ] return_val = self.connector.display._get_longest(test_list) - self.assertEqual(return_val, 49) + self.assertEqual(return_val, 44 + len(self.db_type)) class CoreDisplayTablesTestMixin: @@ -191,14 +191,7 @@ class CoreDisplayTablesTestMixin: def test__display__show_tables(self): """""" - columns = """( - id INT NOT NULL AUTO_INCREMENT, - name VARCHAR(100), - description VARCHAR(100), - PRIMARY KEY ( id ) - )""" - - show_tables_query = '{0}SHOW TABLES;{1}'.format(OUTPUT_QUERY, OUTPUT_RESET) + show_tables_query = '{0}{1}{2}'.format(OUTPUT_QUERY, self.show_tables_query, OUTPUT_RESET) # Since this directly tests display of tables, ensure we use a fresh database. db_name = '{0}d__tables__show'.format(self.test_db_name[0:-15]) @@ -214,7 +207,7 @@ class CoreDisplayTablesTestMixin: with self.subTest('Db name longer - Pt 1'): # Create table. - self.connector.tables.create('category', columns, display_query=False) + self.connector.tables.create('category', self.columns_query__basic, display_query=False) # Capture logging output. with self.assertLogs(None, 'INFO') as ilog: @@ -227,7 +220,7 @@ class CoreDisplayTablesTestMixin: with self.subTest('Db name longer - Pt 2'): # Create table. - self.connector.tables.create('priority', columns, display_query=False) + self.connector.tables.create('priority', self.columns_query__basic, display_query=False) # Capture logging output. with self.assertLogs(None, 'INFO') as ilog: @@ -240,7 +233,7 @@ class CoreDisplayTablesTestMixin: with self.subTest('Db name longer - Pt 3'): # Create table. - self.connector.tables.create('a', columns, display_query=False) + self.connector.tables.create('a', self.columns_query__basic, display_query=False) # Capture logging output. with self.assertLogs(None, 'INFO') as ilog: @@ -254,8 +247,8 @@ class CoreDisplayTablesTestMixin: with self.subTest('Db name and table name equal length'): # Create table. self.connector.tables.create( - 'test__testing__this_is_a_really_long_table_name__test_', - columns, + 'test___{0}___this_is_a_really_long_table_name__test_'.format(self.db_type.lower()), + self.columns_query__basic, display_query=False, ) @@ -271,8 +264,8 @@ class CoreDisplayTablesTestMixin: with self.subTest('Table name longer - Pt 1'): # Create table. self.connector.tables.create( - 'test__testing__this_is_a_really_long_table_name__test__', - columns, + 'test___{0}___this_is_a_really_long_table_name__test__'.format(self.db_type.lower()), + self.columns_query__basic, display_query=False, ) @@ -287,7 +280,7 @@ class CoreDisplayTablesTestMixin: with self.subTest('Table name longer - Pt 2'): # Create table. - self.connector.tables.create('zzz', columns, display_query=False) + self.connector.tables.create('zzz', self.columns_query__basic, display_query=False) # Capture logging output. with self.assertLogs(None, 'INFO') as ilog: @@ -301,8 +294,8 @@ class CoreDisplayTablesTestMixin: with self.subTest('Table name longer - Pt 3'): # Create table. self.connector.tables.create( - 'test__testing__this_is_a_really_long_table_name__testing__', - columns, + 'test___{0}___this_is_a_really_long_table_name__testing__'.format(self.db_type.lower()), + self.columns_query__basic, display_query=False, ) @@ -317,12 +310,7 @@ class CoreDisplayTablesTestMixin: def test__display__describe_tables(self): """""" - columns = """( - id INT NOT NULL AUTO_INCREMENT, - PRIMARY KEY ( id ) - )""" - - describe_table_query = '{0}DESCRIBE category;{1}'.format(OUTPUT_QUERY, OUTPUT_RESET) + describe_table_query = '{0}{1}{2}'.format(OUTPUT_QUERY, self.describe_table_query, OUTPUT_RESET) # Since this directly tests display of tables, ensure we use a fresh database. db_name = '{0}d__tables__desc'.format(self.test_db_name[0:-15]) @@ -330,7 +318,7 @@ class CoreDisplayTablesTestMixin: self.connector.database.use(db_name, display_query=False) # Create initial table to describe. - self.connector.tables.create('category', columns, display_query=False) + self.connector.tables.create('category', self.columns_query__minimal, display_query=False) with self.subTest('With only id'): # Capture logging output. @@ -386,16 +374,9 @@ class CoreDisplayRecordsMixin: def test__display__select_records__basic(self): """""" - columns = """( - id INT NOT NULL AUTO_INCREMENT, - name VARCHAR(100), - description VARCHAR(100), - PRIMARY KEY ( id ) - )""" - select_from_query = '{0}SELECT * FROM category;{1}'.format(OUTPUT_QUERY, OUTPUT_RESET) - self.connector.tables.create('category', columns, display_query=False) + self.connector.tables.create('category', self.columns_query__basic, display_query=False) with self.subTest('With no records present'): # Capture logging output. @@ -409,7 +390,11 @@ class CoreDisplayRecordsMixin: with self.subTest('With 1 record present'): # Create record. - self.connector.records.insert('category', '(1, "tn", "td")', display_query=False) + self.connector.records.insert( + 'category', + '(1, {0}tn{0}, {0}td{0})'.format(self.connector.validate._quote_str_literal_format), + display_query=False, + ) # Capture logging output. with self.assertLogs(None, 'INFO') as ilog: @@ -422,7 +407,11 @@ class CoreDisplayRecordsMixin: with self.subTest('With 2 records present'): # Create record. - self.connector.records.insert('category', '(2, "t n", "t d")', display_query=False) + self.connector.records.insert( + 'category', + '(2, {0}t n{0}, {0}t d{0})'.format(self.connector.validate._quote_str_literal_format), + display_query=False, + ) # Capture logging output. with self.assertLogs(None, 'INFO') as ilog: @@ -435,7 +424,11 @@ class CoreDisplayRecordsMixin: with self.subTest('With 3 records present'): # Create record. - self.connector.records.insert('category', '(3, "te n", "te d")', display_query=False) + self.connector.records.insert( + 'category', + '(3, {0}te n{0}, {0}te d{0})'.format(self.connector.validate._quote_str_literal_format), + display_query=False, + ) # Capture logging output. with self.assertLogs(None, 'INFO') as ilog: @@ -448,7 +441,11 @@ class CoreDisplayRecordsMixin: with self.subTest('With 4 records present'): # Create record. - self.connector.records.insert('category', '(4, "tes n", "tes d")', display_query=False) + self.connector.records.insert( + 'category', + '(4, {0}tes n{0}, {0}tes d{0})'.format(self.connector.validate._quote_str_literal_format), + display_query=False, + ) # Capture logging output. with self.assertLogs(None, 'INFO') as ilog: @@ -461,7 +458,11 @@ class CoreDisplayRecordsMixin: with self.subTest('With 5 records present'): # Create record. - self.connector.records.insert('category', '(5, "test n", "test d")', display_query=False) + self.connector.records.insert( + 'category', + '(5, {0}test n{0}, {0}test d{0})'.format(self.connector.validate._quote_str_literal_format), + display_query=False, + ) # Capture logging output. with self.assertLogs(None, 'INFO') as ilog: @@ -474,7 +475,11 @@ class CoreDisplayRecordsMixin: with self.subTest('With 6 records present'): # Create record. - self.connector.records.insert('category', '(6, "test na", "test de")', display_query=False) + self.connector.records.insert( + 'category', + '(6, {0}test na{0}, {0}test de{0})'.format(self.connector.validate._quote_str_literal_format), + display_query=False, + ) # Capture logging output. with self.assertLogs(None, 'INFO') as ilog: @@ -487,7 +492,11 @@ class CoreDisplayRecordsMixin: with self.subTest('With 7 records present'): # Create record. - self.connector.records.insert('category', '(7, "test nam", "test des")', display_query=False) + self.connector.records.insert( + 'category', + '(7, {0}test nam{0}, {0}test des{0})'.format(self.connector.validate._quote_str_literal_format), + display_query=False, + ) # Capture logging output. with self.assertLogs(None, 'INFO') as ilog: @@ -500,7 +509,11 @@ class CoreDisplayRecordsMixin: with self.subTest('With 8 records present'): # Create record. - self.connector.records.insert('category', '(8, "test name", "test desc")', display_query=False) + self.connector.records.insert( + 'category', + '(8, {0}test name{0}, {0}test desc{0})'.format(self.connector.validate._quote_str_literal_format), + display_query=False, + ) # Capture logging output. with self.assertLogs(None, 'INFO') as ilog: @@ -513,7 +526,11 @@ class CoreDisplayRecordsMixin: with self.subTest('With 9 records present'): # Create record. - self.connector.records.insert('category', '(9, "test name", "test descr")', display_query=False) + self.connector.records.insert( + 'category', + '(9, {0}test name{0}, {0}test descr{0})'.format(self.connector.validate._quote_str_literal_format), + display_query=False, + ) # Capture logging output. with self.assertLogs(None, 'INFO') as ilog: @@ -526,7 +543,11 @@ class CoreDisplayRecordsMixin: with self.subTest('With 10 records present'): # Create record. - self.connector.records.insert('category', '(10, "test name", "test descri")', display_query=False) + self.connector.records.insert( + 'category', + '(10, {0}test name{0}, {0}test descri{0})'.format(self.connector.validate._quote_str_literal_format), + display_query=False, + ) # Capture logging output. with self.assertLogs(None, 'INFO') as ilog: @@ -539,7 +560,11 @@ class CoreDisplayRecordsMixin: with self.subTest('With 11 records present'): # Create record. - self.connector.records.insert('category', '(101, "test name", "test descrip")', display_query=False) + self.connector.records.insert( + 'category', + '(101, {0}test name{0}, {0}test descrip{0})'.format(self.connector.validate._quote_str_literal_format), + display_query=False, + ) # Capture logging output. with self.assertLogs(None, 'INFO') as ilog: @@ -552,7 +577,13 @@ class CoreDisplayRecordsMixin: with self.subTest('With 12 records present'): # Create record. - self.connector.records.insert('category', '(1010, "test name", "test descript")') + self.connector.records.insert( + 'category', + '(1010, {0}test name{0}, {0}test descript{0})'.format( + self.connector.validate._quote_str_literal_format, + ), + display_query=False, + ) # Capture logging output. with self.assertLogs(None, 'INFO') as ilog: @@ -565,7 +596,13 @@ class CoreDisplayRecordsMixin: with self.subTest('With 13 records present'): # Create record. - self.connector.records.insert('category', '(10101, "test name", "test descripti")', display_query=False) + self.connector.records.insert( + 'category', + '(10101, {0}test name{0}, {0}test descripti{0})'.format( + self.connector.validate._quote_str_literal_format, + ), + display_query=False, + ) # Capture logging output. with self.assertLogs(None, 'INFO') as ilog: @@ -578,7 +615,13 @@ class CoreDisplayRecordsMixin: with self.subTest('With 14 records present'): # Create record. - self.connector.records.insert('category', '(101010, "test name", "test descriptio")', display_query=False) + self.connector.records.insert( + 'category', + '(101010, {0}test name{0}, {0}test descriptio{0})'.format( + self.connector.validate._quote_str_literal_format, + ), + display_query=False, + ) # Capture logging output. with self.assertLogs(None, 'INFO') as ilog: @@ -591,7 +634,13 @@ class CoreDisplayRecordsMixin: with self.subTest('With 15 records present'): # Create record. - self.connector.records.insert('category', '(1010101, "test name", "test description")', display_query=False) + self.connector.records.insert( + 'category', + '(1010101, {0}test name{0}, {0}test description{0})'.format( + self.connector.validate._quote_str_literal_format, + ), + display_query=False, + ) # Capture logging output. with self.assertLogs(None, 'INFO') as ilog: diff --git a/tests/connectors/mysql/expected_display_output.py b/tests/connectors/mysql/expected_display_output.py index 315893e199f046db6efc13ed28cc51f2bda50077..792e2ee7fb936f3cdde6955421de4963751626a5 100644 --- a/tests/connectors/mysql/expected_display_output.py +++ b/tests/connectors/mysql/expected_display_output.py @@ -42,7 +42,7 @@ EXPECTED__TABLE__SHOW__EQUAL_LENGTH = """ | a | | category | | priority | -| test__testing__this_is_a_really_long_table_name__test_ | +| test___mysql___this_is_a_really_long_table_name__test_ | +--------------------------------------------------------+ """.strip() @@ -54,8 +54,8 @@ EXPECTED__TABLE__SHOW__TABLE_LONGER__PT_1 = """ | a | | category | | priority | -| test__testing__this_is_a_really_long_table_name__test_ | -| test__testing__this_is_a_really_long_table_name__test__ | +| test___mysql___this_is_a_really_long_table_name__test_ | +| test___mysql___this_is_a_really_long_table_name__test__ | +---------------------------------------------------------+ """.strip() @@ -67,8 +67,8 @@ EXPECTED__TABLE__SHOW__TABLE_LONGER__PT_2 = """ | a | | category | | priority | -| test__testing__this_is_a_really_long_table_name__test_ | -| test__testing__this_is_a_really_long_table_name__test__ | +| test___mysql___this_is_a_really_long_table_name__test_ | +| test___mysql___this_is_a_really_long_table_name__test__ | | zzz | +---------------------------------------------------------+ """.strip() @@ -81,9 +81,9 @@ EXPECTED__TABLE__SHOW__TABLE_LONGER__PT_3 = """ | a | | category | | priority | -| test__testing__this_is_a_really_long_table_name__test_ | -| test__testing__this_is_a_really_long_table_name__test__ | -| test__testing__this_is_a_really_long_table_name__testing__ | +| test___mysql___this_is_a_really_long_table_name__test_ | +| test___mysql___this_is_a_really_long_table_name__test__ | +| test___mysql___this_is_a_really_long_table_name__testing__ | | zzz | +------------------------------------------------------------+ """.strip() diff --git a/tests/connectors/mysql/test_core.py b/tests/connectors/mysql/test_core.py index 99c9c512241561987bf4f8f7db801342c976bbf9..68f4789b6a5cd7aae2bbfbef39d68cfd9d82d31d 100644 --- a/tests/connectors/mysql/test_core.py +++ b/tests/connectors/mysql/test_core.py @@ -45,26 +45,3 @@ class TestMysqlDatabaseParent(CoreTestParent): cls.db_type = cls.connector._config.db_type cls._implemented_db_types = cls.connector._config._implemented_db_types cls.db_error_handler = MySQLdb - -class TestMysqlErrorHandlers(TestMysqlDatabaseParent): - """""" - @classmethod - def setUpClass(cls): - # Run parent setup logic. - super().setUpClass() - - # Also call CoreTestMixin setup logic. - cls.set_up_class() - - # Define database name to use in tests. - cls.test_db_name = '{0}test_error_handlers'.format(cls.test_db_name_start) - - @classmethod - def set_up_class(cls): - """ - Acts as the equivalent of the UnitTesting "setUpClass()" function. - - However, since this is not inheriting from a given TestCase, - calling the literal function here would override instead. - """ - cls.test_db_name_start = cls.test_db_name_start.format(cls.db_type) diff --git a/tests/connectors/mysql/test_display.py b/tests/connectors/mysql/test_display.py index 11e4c4f0395732f4c9acb08b88a5c55eb57e345e..00ff0eca8ee947bea5d0a9489655b8014f9842eb 100644 --- a/tests/connectors/mysql/test_display.py +++ b/tests/connectors/mysql/test_display.py @@ -14,6 +14,27 @@ from tests.connectors.core.test_display import ( ) +SHOW_TABLES_QUERY = """ +SHOW TABLES; +""".strip() +DESCRIBE_TABLE_QUERY = """DESCRIBE category;""" +COLUMNS_QUERY__MINIMAL = """ +( + id INT NOT NULL AUTO_INCREMENT, + PRIMARY KEY ( id ) +) +""".strip() +COLUMNS_QUERY__BASIC = """ +( + id INT NOT NULL AUTO_INCREMENT, + name VARCHAR(100), + description VARCHAR(100), + PRIMARY KEY ( id ) +) +""".strip() + + + class TestMysqlDisplayCore(TestMysqlDatabaseParent, CoreDisplayBaseTestMixin): """ Tests "MySQL" DB Connector class display logic. @@ -43,6 +64,7 @@ class TestMysqlDisplayCore(TestMysqlDatabaseParent, CoreDisplayBaseTestMixin): # Define expected output to compare against. cls.expected_output = ExpectedOutput + cls.show_tables_query = SHOW_TABLES_QUERY class TestMysqlDisplayTables(TestMysqlDatabaseParent, CoreDisplayTablesTestMixin): @@ -74,6 +96,10 @@ class TestMysqlDisplayTables(TestMysqlDatabaseParent, CoreDisplayTablesTestMixin # Define expected output to compare against. cls.expected_output = ExpectedOutput + cls.show_tables_query = SHOW_TABLES_QUERY + cls.describe_table_query = DESCRIBE_TABLE_QUERY + cls.columns_query__minimal = COLUMNS_QUERY__MINIMAL + cls.columns_query__basic = COLUMNS_QUERY__BASIC class TestMysqlDisplayRecords(TestMysqlDatabaseParent, CoreDisplayRecordsMixin): @@ -105,3 +131,6 @@ class TestMysqlDisplayRecords(TestMysqlDatabaseParent, CoreDisplayRecordsMixin): # Define expected output to compare against. cls.expected_output = ExpectedOutput + cls.show_tables_query = SHOW_TABLES_QUERY + cls.columns_query__minimal = COLUMNS_QUERY__MINIMAL + cls.columns_query__basic = COLUMNS_QUERY__BASIC diff --git a/tests/connectors/postgresql/expected_display_output.py b/tests/connectors/postgresql/expected_display_output.py new file mode 100644 index 0000000000000000000000000000000000000000..5ab1c32f5af259a99fa765afed26c1ea11d4c75a --- /dev/null +++ b/tests/connectors/postgresql/expected_display_output.py @@ -0,0 +1,401 @@ +""" +Expected display output for various functions. +""" + + +# region Table Display Output + +EXPECTED__TABLE__SHOW__DB_LONGER__PT_1 = """ ++-------------------------------------------------------------+ +| Tables in pydbcn__postgresql_unittest__test_d__tables__show | ++-------------------------------------------------------------+ +| category | ++-------------------------------------------------------------+ +""".strip() + + +EXPECTED__TABLE__SHOW__DB_LONGER__PT_2 = """ ++-------------------------------------------------------------+ +| Tables in pydbcn__postgresql_unittest__test_d__tables__show | ++-------------------------------------------------------------+ +| category | +| priority | ++-------------------------------------------------------------+ +""".strip() + + +EXPECTED__TABLE__SHOW__DB_LONGER__PT_3 = """ ++-------------------------------------------------------------+ +| Tables in pydbcn__postgresql_unittest__test_d__tables__show | ++-------------------------------------------------------------+ +| a | +| category | +| priority | ++-------------------------------------------------------------+ +""".strip() + + +EXPECTED__TABLE__SHOW__EQUAL_LENGTH = """ ++-------------------------------------------------------------+ +| Tables in pydbcn__postgresql_unittest__test_d__tables__show | ++-------------------------------------------------------------+ +| a | +| category | +| priority | +| test___postgresql___this_is_a_really_long_table_name__test_ | ++-------------------------------------------------------------+ +""".strip() + + +EXPECTED__TABLE__SHOW__TABLE_LONGER__PT_1 = """ ++--------------------------------------------------------------+ +| Tables in pydbcn__postgresql_unittest__test_d__tables__show | ++--------------------------------------------------------------+ +| a | +| category | +| priority | +| test___postgresql___this_is_a_really_long_table_name__test_ | +| test___postgresql___this_is_a_really_long_table_name__test__ | ++--------------------------------------------------------------+ +""".strip() + + +EXPECTED__TABLE__SHOW__TABLE_LONGER__PT_2 = """ ++--------------------------------------------------------------+ +| Tables in pydbcn__postgresql_unittest__test_d__tables__show | ++--------------------------------------------------------------+ +| a | +| category | +| priority | +| test___postgresql___this_is_a_really_long_table_name__test_ | +| test___postgresql___this_is_a_really_long_table_name__test__ | +| zzz | ++--------------------------------------------------------------+ +""".strip() + + +EXPECTED__TABLE__SHOW__TABLE_LONGER__PT_3 = """ ++-----------------------------------------------------------------+ +| Tables in pydbcn__postgresql_unittest__test_d__tables__show | ++-----------------------------------------------------------------+ +| a | +| category | +| priority | +| test___postgresql___this_is_a_really_long_table_name__test_ | +| test___postgresql___this_is_a_really_long_table_name__test__ | +| test___postgresql___this_is_a_really_long_table_name__testing__ | +| zzz | ++-----------------------------------------------------------------+ +""".strip() + + +EXPECTED__TABLE__DESCRIBE__COLS_ID = """ ++-------+------+------+---------+--------------------------------------+---------+ +| Field | Type | Null | Key | Default | Extra | ++-------+------+------+---------+--------------------------------------+---------+ +| id | int4 | NO | UNKNOWN | nextval('category_id_seq'::regclass) | UNKNOWN | ++-------+------+------+---------+--------------------------------------+---------+ +""".strip() + + +EXPECTED__TABLE__DESCRIBE__COLS_ID_NAME = """ ++-------+---------+------+---------+--------------------------------------+---------+ +| Field | Type | Null | Key | Default | Extra | ++-------+---------+------+---------+--------------------------------------+---------+ +| id | int4 | NO | UNKNOWN | nextval('category_id_seq'::regclass) | UNKNOWN | +| name | varchar | YES | UNKNOWN | NULL | UNKNOWN | ++-------+---------+------+---------+--------------------------------------+---------+ +""".strip() + + +EXPECTED__TABLE__DESCRIBE__COLS_ID_NAME_DESC = """ ++-------------+---------+------+---------+--------------------------------------+---------+ +| Field | Type | Null | Key | Default | Extra | ++-------------+---------+------+---------+--------------------------------------+---------+ +| id | int4 | NO | UNKNOWN | nextval('category_id_seq'::regclass) | UNKNOWN | +| name | varchar | YES | UNKNOWN | NULL | UNKNOWN | +| description | varchar | YES | UNKNOWN | NULL | UNKNOWN | ++-------------+---------+------+---------+--------------------------------------+---------+ +""".strip() + +# endregion Table Display Output + + +# region Record Display Output + +EXPECTED__RECORD__SELECT__PT_1 = """ ++----+------+-------------+ +| id | name | description | ++----+------+-------------+ +| 1 | tn | td | ++----+------+-------------+ +""".strip() + + +EXPECTED__RECORD__SELECT__PT_2 = """ ++----+------+-------------+ +| id | name | description | ++----+------+-------------+ +| 1 | tn | td | +| 2 | t n | t d | ++----+------+-------------+ +""".strip() + + +EXPECTED__RECORD__SELECT__PT_3 = """ ++----+------+-------------+ +| id | name | description | ++----+------+-------------+ +| 1 | tn | td | +| 2 | t n | t d | +| 3 | te n | te d | ++----+------+-------------+ +""".strip() + + +EXPECTED__RECORD__SELECT__PT_4 = """ ++----+-------+-------------+ +| id | name | description | ++----+-------+-------------+ +| 1 | tn | td | +| 2 | t n | t d | +| 3 | te n | te d | +| 4 | tes n | tes d | ++----+-------+-------------+ +""".strip() + + +EXPECTED__RECORD__SELECT__PT_5 = """ ++----+--------+-------------+ +| id | name | description | ++----+--------+-------------+ +| 1 | tn | td | +| 2 | t n | t d | +| 3 | te n | te d | +| 4 | tes n | tes d | +| 5 | test n | test d | ++----+--------+-------------+ +""".strip() + + +EXPECTED__RECORD__SELECT__PT_6 = """ ++----+---------+-------------+ +| id | name | description | ++----+---------+-------------+ +| 1 | tn | td | +| 2 | t n | t d | +| 3 | te n | te d | +| 4 | tes n | tes d | +| 5 | test n | test d | +| 6 | test na | test de | ++----+---------+-------------+ +""".strip() + + +EXPECTED__RECORD__SELECT__PT_7 = """ ++----+----------+-------------+ +| id | name | description | ++----+----------+-------------+ +| 1 | tn | td | +| 2 | t n | t d | +| 3 | te n | te d | +| 4 | tes n | tes d | +| 5 | test n | test d | +| 6 | test na | test de | +| 7 | test nam | test des | ++----+----------+-------------+ +""".strip() + + +EXPECTED__RECORD__SELECT__PT_8 = """ ++----+-----------+-------------+ +| id | name | description | ++----+-----------+-------------+ +| 1 | tn | td | +| 2 | t n | t d | +| 3 | te n | te d | +| 4 | tes n | tes d | +| 5 | test n | test d | +| 6 | test na | test de | +| 7 | test nam | test des | +| 8 | test name | test desc | ++----+-----------+-------------+ +""".strip() + + +EXPECTED__RECORD__SELECT__PT_9 = """ ++----+-----------+-------------+ +| id | name | description | ++----+-----------+-------------+ +| 1 | tn | td | +| 2 | t n | t d | +| 3 | te n | te d | +| 4 | tes n | tes d | +| 5 | test n | test d | +| 6 | test na | test de | +| 7 | test nam | test des | +| 8 | test name | test desc | +| 9 | test name | test descr | ++----+-----------+-------------+ +""".strip() + + +EXPECTED__RECORD__SELECT__PT_10 = """ ++----+-----------+-------------+ +| id | name | description | ++----+-----------+-------------+ +| 1 | tn | td | +| 2 | t n | t d | +| 3 | te n | te d | +| 4 | tes n | tes d | +| 5 | test n | test d | +| 6 | test na | test de | +| 7 | test nam | test des | +| 8 | test name | test desc | +| 9 | test name | test descr | +| 10 | test name | test descri | ++----+-----------+-------------+ +""".strip() + + +EXPECTED__RECORD__SELECT__PT_11 = """ ++-----+-----------+--------------+ +| id | name | description | ++-----+-----------+--------------+ +| 1 | tn | td | +| 2 | t n | t d | +| 3 | te n | te d | +| 4 | tes n | tes d | +| 5 | test n | test d | +| 6 | test na | test de | +| 7 | test nam | test des | +| 8 | test name | test desc | +| 9 | test name | test descr | +| 10 | test name | test descri | +| 101 | test name | test descrip | ++-----+-----------+--------------+ +""".strip() + + +EXPECTED__RECORD__SELECT__PT_12 = """ ++------+-----------+---------------+ +| id | name | description | ++------+-----------+---------------+ +| 1 | tn | td | +| 2 | t n | t d | +| 3 | te n | te d | +| 4 | tes n | tes d | +| 5 | test n | test d | +| 6 | test na | test de | +| 7 | test nam | test des | +| 8 | test name | test desc | +| 9 | test name | test descr | +| 10 | test name | test descri | +| 101 | test name | test descrip | +| 1010 | test name | test descript | ++------+-----------+---------------+ +""".strip() + + +EXPECTED__RECORD__SELECT__PT_13 = """ ++-------+-----------+----------------+ +| id | name | description | ++-------+-----------+----------------+ +| 1 | tn | td | +| 2 | t n | t d | +| 3 | te n | te d | +| 4 | tes n | tes d | +| 5 | test n | test d | +| 6 | test na | test de | +| 7 | test nam | test des | +| 8 | test name | test desc | +| 9 | test name | test descr | +| 10 | test name | test descri | +| 101 | test name | test descrip | +| 1010 | test name | test descript | +| 10101 | test name | test descripti | ++-------+-----------+----------------+ +""".strip() + + +EXPECTED__RECORD__SELECT__PT_14 = """ ++--------+-----------+-----------------+ +| id | name | description | ++--------+-----------+-----------------+ +| 1 | tn | td | +| 2 | t n | t d | +| 3 | te n | te d | +| 4 | tes n | tes d | +| 5 | test n | test d | +| 6 | test na | test de | +| 7 | test nam | test des | +| 8 | test name | test desc | +| 9 | test name | test descr | +| 10 | test name | test descri | +| 101 | test name | test descrip | +| 1010 | test name | test descript | +| 10101 | test name | test descripti | +| 101010 | test name | test descriptio | ++--------+-----------+-----------------+ +""".strip() + + +EXPECTED__RECORD__SELECT__PT_15 = """ ++---------+-----------+------------------+ +| id | name | description | ++---------+-----------+------------------+ +| 1 | tn | td | +| 2 | t n | t d | +| 3 | te n | te d | +| 4 | tes n | tes d | +| 5 | test n | test d | +| 6 | test na | test de | +| 7 | test nam | test des | +| 8 | test name | test desc | +| 9 | test name | test descr | +| 10 | test name | test descri | +| 101 | test name | test descrip | +| 1010 | test name | test descript | +| 10101 | test name | test descripti | +| 101010 | test name | test descriptio | +| 1010101 | test name | test description | ++---------+-----------+------------------+ +""".strip() + +# endregion Record Display Output + + +class ExpectedOutput: + """Class of expected output values, for easy importing into test files.""" + + class Tables: + SHOW__DB_LONGER__PT_1 = EXPECTED__TABLE__SHOW__DB_LONGER__PT_1 + SHOW__DB_LONGER__PT_2 = EXPECTED__TABLE__SHOW__DB_LONGER__PT_2 + SHOW__DB_LONGER__PT_3 = EXPECTED__TABLE__SHOW__DB_LONGER__PT_3 + SHOW__EQUAL_LENGTH = EXPECTED__TABLE__SHOW__EQUAL_LENGTH + SHOW__TABLE_LONGER__PT_1 = EXPECTED__TABLE__SHOW__TABLE_LONGER__PT_1 + SHOW__TABLE_LONGER__PT_2 = EXPECTED__TABLE__SHOW__TABLE_LONGER__PT_2 + SHOW__TABLE_LONGER__PT_3 = EXPECTED__TABLE__SHOW__TABLE_LONGER__PT_3 + DESCRIBE__COLS_ID = EXPECTED__TABLE__DESCRIBE__COLS_ID + DESCRIBE__COLS_ID_NAME = EXPECTED__TABLE__DESCRIBE__COLS_ID_NAME + DESCRIBE__COLS_ID_NAME_DESC = EXPECTED__TABLE__DESCRIBE__COLS_ID_NAME_DESC + + class Records: + SELECT__PT_1 = EXPECTED__RECORD__SELECT__PT_1 + SELECT__PT_2 = EXPECTED__RECORD__SELECT__PT_2 + SELECT__PT_3 = EXPECTED__RECORD__SELECT__PT_3 + SELECT__PT_4 = EXPECTED__RECORD__SELECT__PT_4 + SELECT__PT_5 = EXPECTED__RECORD__SELECT__PT_5 + SELECT__PT_6 = EXPECTED__RECORD__SELECT__PT_6 + SELECT__PT_7 = EXPECTED__RECORD__SELECT__PT_7 + SELECT__PT_8 = EXPECTED__RECORD__SELECT__PT_8 + SELECT__PT_9 = EXPECTED__RECORD__SELECT__PT_9 + SELECT__PT_10 = EXPECTED__RECORD__SELECT__PT_10 + SELECT__PT_11 = EXPECTED__RECORD__SELECT__PT_11 + SELECT__PT_12 = EXPECTED__RECORD__SELECT__PT_12 + SELECT__PT_13 = EXPECTED__RECORD__SELECT__PT_13 + SELECT__PT_14 = EXPECTED__RECORD__SELECT__PT_14 + SELECT__PT_15 = EXPECTED__RECORD__SELECT__PT_15 + + tables = Tables() + records = Records() diff --git a/tests/connectors/postgresql/test_display.py b/tests/connectors/postgresql/test_display.py index 2ab304b9380b977bfd5225e4fc039952d497084a..040ca3665717a9b2d89d4fecd64902ffc7b6e517 100644 --- a/tests/connectors/postgresql/test_display.py +++ b/tests/connectors/postgresql/test_display.py @@ -3,18 +3,133 @@ Tests for "display" logic of "PostgreSQL" DB Connector class. """ # System Imports. -import unittest # Internal Imports. -from config import mysql_config, sqlite_config -from py_dbcn.connectors import MysqlDbConnector, PostgresqlDbConnector, SqliteDbConnector +from .expected_display_output import ExpectedOutput +from .test_core import TestPostgresqlDatabaseParent +from tests.connectors.core.test_display import ( + CoreDisplayBaseTestMixin, + CoreDisplayTablesTestMixin, + CoreDisplayRecordsMixin, +) -class TestPostgresqlDisplay(unittest.TestCase): +SHOW_TABLES_QUERY = """ +SELECT table_name FROM information_schema.tables +WHERE table_schema = 'public'; +""".strip() +DESCRIBE_TABLE_QUERY = """ +SELECT * FROM information_schema.columns +WHERE (table_schema = 'public' AND table_name = 'category'); +""".strip() +COLUMNS_QUERY__MINIMAL = """ +( + id serial PRIMARY KEY +) +""".strip() +COLUMNS_QUERY__BASIC = """ +( + id serial PRIMARY KEY, + name VARCHAR(100), + description VARCHAR(100) +) +""".strip() + + +class TestPostgresqlDisplay(TestPostgresqlDatabaseParent, CoreDisplayBaseTestMixin): + """ + Tests "PostgreSQL" DB Connector class display logic. + """ + @classmethod + def setUpClass(cls): + # Run parent setup logic. + super().setUpClass() + + # Also call CoreTestMixin setup logic. + cls.set_up_class() + + # Define database name to use in tests. + cls.test_db_name = '{0}test_display__core'.format(cls.test_db_name_start) + + # Initialize database for tests. + cls.connector.database.create(cls.test_db_name) + cls.connector.database.use(cls.test_db_name) + + # Check that database has no tables. + results = cls.connector.tables.show() + if len(results) > 0: + for result in results: + cls.connector.tables.drop(result) + + # Define expected output to compare against. + cls.expected_output = ExpectedOutput + cls.show_tables_query = SHOW_TABLES_QUERY + + +class TestPostgresqlDisplayTables(TestPostgresqlDatabaseParent, CoreDisplayTablesTestMixin): + """ + Tests "Postgresql" DB Connector class display logic. + + Specifically tests logic defined in "tables" display subclass. + """ + @classmethod + def setUpClass(cls): + # Run parent setup logic. + super().setUpClass() + + # Also call CoreTestMixin setup logic. + cls.set_up_class() + + # Define database name to use in tests. + cls.test_db_name = '{0}test_display__tables'.format(cls.test_db_name_start) + + # Initialize database for tests. + cls.connector.database.create(cls.test_db_name) + cls.connector.database.use(cls.test_db_name) + + # Check that database has no tables. + results = cls.connector.tables.show() + if len(results) > 0: + for result in results: + cls.connector.tables.drop(result) + + # Define expected output to compare against. + cls.expected_output = ExpectedOutput + cls.show_tables_query = SHOW_TABLES_QUERY + cls.describe_table_query = DESCRIBE_TABLE_QUERY + cls.columns_query__minimal = COLUMNS_QUERY__MINIMAL + cls.columns_query__basic = COLUMNS_QUERY__BASIC + + +class TestPostgreSQLDisplayRecords(TestPostgresqlDatabaseParent, CoreDisplayRecordsMixin): """ Tests "PostgreSQL" DB Connector class display logic. + + Specifically tests logic defined in "records" display subclass. """ @classmethod def setUpClass(cls): # Run parent setup logic. super().setUpClass() + + # Also call CoreTestMixin setup logic. + cls.set_up_class() + + # Define database name to use in tests. + cls.test_db_name = '{0}test_display__records'.format(cls.test_db_name_start) + + # Initialize database for tests. + cls.connector.database.create(cls.test_db_name) + cls.connector.database.use(cls.test_db_name) + + # Check that database has no tables. + results = cls.connector.tables.show() + if len(results) > 0: + for result in results: + cls.connector.tables.drop(result) + + # Define expected output to compare against. + cls.expected_output = ExpectedOutput + cls.show_tables_query = SHOW_TABLES_QUERY + cls.columns_query__minimal = COLUMNS_QUERY__MINIMAL + cls.columns_query__basic = COLUMNS_QUERY__BASIC