def test_request_sfdb_valid_sfdb_filepath(self, mocked_input): test_sfdb = ui.request_sfdb('') test_filepath = get_resource_filepath('test_duplicates.sfdb') expected_output = sfdb.SFDBContainer.from_file(test_filepath) self.assertEqual(expected_output, test_sfdb)
def test_get_resource_filepath_resource_in_project(self): test_file = 'empty.sfdb' test_filepath = utilities.get_resource_filepath(test_file) dir1 = "sfdbtester" dir2 = "resources" self.assertTrue(dir1 in test_filepath) self.assertTrue(dir2 in test_filepath) self.assertTrue(test_file in test_filepath)
def test_read_sfdb_from_file_single_entry(self): test_sfdb_filepath = get_resource_filepath('single_entry.sfdb') read_sfdb_lines = SFDBContainer.read_sfdb_from_file(test_sfdb_filepath) expected_lines = [ 'ENCODING UTF8', 'INIT', 'TABLE SFI_TESTTABLE', 'COLUMNS COLUMN1 COLUMN2 COLUMN3', 'INSERT', 'val1 val2 val3' ] self.assertEqual(expected_lines, read_sfdb_lines)
def test_read_sfdb_from_file_only_header(self): test_sfdb_filepath = get_resource_filepath('only_header.sfdb') read_sfdb_lines = SFDBContainer.read_sfdb_from_file(test_sfdb_filepath) expected_lines = [ 'ENCODING UTF8', 'INIT', 'TABLE SFI_TESTTABLE', 'COLUMNS COLUMN1 COLUMN2 COLUMN3', 'INSERT' ] self.assertEqual(expected_lines, read_sfdb_lines)
def test_check_content_format_wrong_content(self): wrong_content_format_filepath = get_resource_filepath( 'wrong_content_format.sfdb') test_sfdb = sfdb.SFDBContainer.from_file(wrong_content_format_filepath) faulty_lines = sc.check_content_format(test_sfdb) expected_output = [(1, ['val1', 'val2']), (2, ['val1']), (3, ['val1', 'val2', 'val3', 'val4']), (4, ['val1', 'val2 val3'])] self.assertEqual(expected_output, faulty_lines)
def test_check_for_duplicates_with_duplicates(self): no_duplicates_sfdb_filepath = get_resource_filepath( 'test_duplicates.sfdb') test_sfdb = sfdb.SFDBContainer.from_file(no_duplicates_sfdb_filepath) faulty_lines = sc.check_for_duplicates(test_sfdb) expected_output = [(np.array([1, 2], dtype='int32'), np.array(['val4', 'val5', 'val6']))] np.testing.assert_array_equal(expected_output[0][0], faulty_lines[0][0]) np.testing.assert_array_equal(expected_output[0][1], faulty_lines[0][1])
def test_write_to_file_valid_filepath(self): test_output_filepath = get_resource_filepath('tempfile.sfdb') test_sfdb = create_test_sfdbcontainer() test_sfdb.write_to_file(test_output_filepath) with open(test_output_filepath) as f: output = f.readlines() expected_output = [ 'ENCODING UTF8\n', 'INIT\n', 'TABLE SMALL_TEST\n', 'COLUMNS COLUMN1 COLUMN2\n', 'INSERT\n', 'val1 val2\n', 'val3 val4\n' ] self.assertEqual(expected_output, output)
def test_write_with_duplicates(self): test_output_filepath = get_resource_filepath('tempfile.sfdb') test_entries = [['1', '2'], ['1', '2'], ['3', '4'], ['3', '4']] test_sfdb = create_test_sfdbcontainer(entries=test_entries) test_sfdb.write_to_file(test_output_filepath, remove_duplicates=True) with open(test_output_filepath) as f: output = f.readlines() expected_output = [ 'ENCODING UTF8\n', 'INIT\n', 'TABLE SMALL_TEST\n', 'COLUMNS COLUMN1 COLUMN2\n', 'INSERT\n', '1 2\n', '3 4\n' ] self.assertEqual(expected_output, output)
def test_check_excel_autoformatting_excel_formatted_sfdb(self): excel_formatted_sfdb_filepath = get_resource_filepath( 'excel_formatting.sfdb') test_sfdb = sfdb.SFDBContainer.from_file(excel_formatted_sfdb_filepath) faulty_lines = sc.check_excel_autoformatting(test_sfdb) expected_output1 = (0, 0, np.array(['4.3E+04', 'val2'], dtype='<U8')) self.assertEqual(expected_output1[0], faulty_lines[0][0]) self.assertEqual(expected_output1[1], faulty_lines[0][1]) np.testing.assert_array_equal(faulty_lines[0][2], expected_output1[2]) expected_output2 = (1, 1, np.array(['val1', '1.23E+08'], dtype='<U8')) self.assertEqual(expected_output2[0], faulty_lines[1][0]) self.assertEqual(expected_output2[1], faulty_lines[1][1]) np.testing.assert_array_equal(faulty_lines[1][2], expected_output2[2])
def test_request_filepath_valid_filepath(self, mocked_input): filepath = ui.request_filepath('') expected_output = get_resource_filepath('empty.sfdb') self.assertEqual(expected_output, filepath) self.assertTrue(os.path.isfile(filepath))
def test_get_resource_filepath_dir(self): test_file = 'test_dir' test_filepath = utilities.get_resource_filepath(test_file) contained_in_expected_output = '/sfdbtester/resources/test_dir' self.assertTrue(contained_in_expected_output in test_filepath)
def test_get_resource_filepath_nonexistant_resource(self): test_file = 'ThisResourceDoesNotExist' with self.assertRaises(ValueError): utilities.get_resource_filepath(test_file)
class SQLTableSchema: """Part of an SFDB object. Defines the datatypes of the individual columns of an sfdb and the associated conditions entries need to fulfill. Datatypes are SQL datatypes. All already known/defined Tables are written in the sfdb_schemas.json resource. These defined requirements can then be used by checks to see whether all entries in the SFDB fulfill them.""" sfdb_schema_file = get_resource_filepath('sfdb_schemas.json') def __init__(self, sql_table_name): self.table_name = sql_table_name self.column_properties = self._get_column_properties() @property def columns(self): """Returns a list of all columns in this sql_table_scheme""" return list(self.column_properties.keys()) @columns.setter def columns(self, column_object_list): """Sets the list of column properties.""" self.column_properties = column_object_list def __len__(self): """Return number of columns defined by the schema""" return len(self.column_properties) def __getitem__(self, column): """Return a column defined by the schema""" if column not in self.columns: raise ColumnError(f'Column {column} does not exist in table {self.table_name}!') return self.column_properties[column] def _get_column_properties(self): """Retrieves the SQL column definitions for the SFDB file based on user input if available. Returns: list: A list of tuples (ColumnName (string), Datatype (string), Length (int), IsNullAllowed (bool)) None: When users SQL column definitions are not already known to the program (Other)""" known_sfdb_schemas = SQLTableSchema._get_known_sfdb_schemas() if self.table_name in known_sfdb_schemas: this_schema = known_sfdb_schemas[self.table_name] for column_name, column in this_schema.items(): if column.datatype == 'datetime' or column.datatype == 'datetime2': this_schema[column_name] = Column(column.name, column.datatype, 10, column.with_null) return this_schema return None @classmethod def _get_known_sfdb_schemas(cls): """Reads in the SFDB schemas of all known tables provided by the sfdb_schemas.json resource and returns them as dictionary""" with open(cls.sfdb_schema_file, mode='r') as schema_file: sfdb_schemas_json = json.load(schema_file) schemas_dict = {} for table_name, table_columns in sfdb_schemas_json.items(): column_properties = {} for col in table_columns: column = Column(col['column_name'], col['datatype'], col['length'], col['with_null']) column_properties[col['column_name']] = column schemas_dict[table_name] = column_properties return schemas_dict def is_full_schema(self): """Checks whether the schema actually defines any columns""" return self.column_properties is not None def get_datatype_regex_pattern(self, column_name): """Generates a Pattern object of a regular expression that can match any column entry in an SQL table with this column definition of datatype and length. So far only covers nvarchar, int, bool, bit, datetime and datetime2. Datetime has not been tested yet. Parameters: column_name (string): The name of the sfdb column for which the regular expression is generated Returns: Pattern: Pattern object of a regular expression that matches any column entry with the provided datatype and length None: When needed SQL datatype is not hard-coded in this function. """ if column_name not in self.columns: raise ValueError('Column not in SQL table schema.') datatype = self.column_properties[column_name].datatype.lower() length = self.column_properties[column_name].length regex_string = None if datatype == 'nvarchar': regex_string = r'^.{0,' + str(length) + '}$' elif datatype == 'int': regex_string = r'^\d{1,' + str(length) + '}$' elif datatype == 'bool' or datatype.lower() == 'bit': regex_string = r'^[01]$' elif datatype == 'datetime2' or datatype.lower() == 'datetime': regex_string = r'^\d\d\d\d-\d\d-\d\d$' return re.compile(regex_string, re.IGNORECASE) if regex_string else regex_string
def test_read_sfdb_from_file_empty_file(self): empty_sfdb_filepath = get_resource_filepath('empty.sfdb') with self.assertRaises(NotSFDBFileError): SFDBContainer.read_sfdb_from_file(empty_sfdb_filepath)
def test___eq__string(self): test_sfdb = create_test_sfdbcontainer() with self.assertRaises(ValueError): test_sfdb.__eq__(get_resource_filepath('duplicates_test.sfdb'))
def test_read_sfdb_from_file_wrong_header_sfdb(self): wrong_header_sfdb_filepath = get_resource_filepath('wrong_header.sfdb') with self.assertRaises(NotSFDBFileError): SFDBContainer.read_sfdb_from_file(wrong_header_sfdb_filepath)
class TestUserInput(ut.TestCase): def setUp(self) -> None: pass def test_request_regex_pattern_regex_input(self): with mock.patch('builtins.input', return_value=r'\d\d'): test_pattern = ui.request_regex_pattern('') test_string = '01' self.assertIsNotNone(test_pattern.match(test_string)) @mock.patch('builtins.input', return_value=r'\l\d') @mock.patch('builtins.print', side_effect=[None, Exception('To Break the Loop!')]) def test_request_regex_pattern_non_regex_input(self, mocked_input, mocked_print): with self.assertRaises(Exception): ui.request_regex_pattern('') mocked_print.assert_called_with( 'The input was not valid regular expression') @mock.patch('builtins.input', return_value='') @mock.patch('builtins.print') def test_request_regex_pattern_no_input(self, mocked_print, mocked_input): test_pattern = ui.request_regex_pattern('') mocked_print.assert_called_with('No Regex provided.') self.assertIsNone(test_pattern) @mock.patch('builtins.input', return_value='1 2 12') def test_request_list_of_int_list_of_int(self, mocked_input): int_list = ui.request_list_of_int('') expected_output = [1, 2, 12] self.assertEqual(expected_output, int_list) @mock.patch('builtins.input', return_value='1.3 2.4 12.5') @mock.patch('builtins.print', side_effect=Exception('Break The Loop!')) def test_request_list_of_int_list_of_float(self, mocked_print, mocked_input): with self.assertRaises(Exception): ui.request_list_of_int('') mocked_print.assert_called_with( f"!WARNING! The following entries are not integers :\n1.3 2.4 12.5" ) @mock.patch('builtins.input', return_value='a bc def') @mock.patch('builtins.print', side_effect=Exception('Break The Loop!')) def test_request_list_of_int_list_of_string(self, mocked_print, mocked_input): with self.assertRaises(Exception): ui.request_list_of_int('') mocked_print.assert_called_with( f"!WARNING! The following entries are not integers :\na bc def") @mock.patch('builtins.input', return_value='') @mock.patch('builtins.print') def test_request_list_of_int_empty_string(self, mocked_print, mocked_input): ui.request_list_of_int('') mocked_print.assert_called_with(f"No list of numbers provided.") @mock.patch('builtins.input', return_value='1 2 3') @mock.patch('builtins.print', side_effect=Exception('Break The Loop!')) def test_request_list_of_int_list_of_int_below_min(self, mocked_print, mocked_input): with self.assertRaises(Exception): ui.request_list_of_int('', min_value=2) mocked_print.assert_called_with( f"!WARNING! The following entries are below the allowed minimum of 2:\n1" ) @mock.patch('builtins.input', return_value='1 2 3') @mock.patch('builtins.print', side_effect=Exception('Break The Loop!')) def test_request_list_of_int_list_of_int_above_max(self, mocked_print, mocked_input): with self.assertRaises(Exception): ui.request_list_of_int('', max_value=2) mocked_print.assert_called_with( f"!WARNING! The following entries are above the allowed maximum of 2 :\n3" ) @mock.patch('builtins.input', return_value='1 2 3') def test_request_list_of_int_list_of_int_between_min_max( self, mocked_input): int_list = ui.request_list_of_int('', min_value=0, max_value=4) expected_output = [1, 2, 3] self.assertEqual(expected_output, int_list) @mock.patch('builtins.input', return_value='a bc def') def test_request_items_of_list_valid_items(self, mocked_input): test_items = ['a', 'bc', 'gh', 'def'] item_list = ui.request_items_of_list('', test_items) expected_output = ['a', 'bc', 'def'] self.assertEqual(expected_output, item_list) @mock.patch('builtins.input', return_value='') def test_request_items_of_list_no_items(self, mocked_input): test_items = ['a', 'bc', 'gh', 'def'] item_list = ui.request_items_of_list('', test_items) expected_output = [] self.assertEqual(expected_output, item_list) @mock.patch('builtins.input', return_value='ij') @mock.patch('builtins.print', side_effect=Exception('Break The Loop!')) def test_request_items_of_list_invalid_items(self, mocked_print, mocked_input): with self.assertRaises(Exception): test_items = ['a', 'bc', 'gh', 'def'] ui.request_items_of_list('', test_items) mocked_print.assert_called_with( f"!WARNING! The following entries are not in the list of possible items:\nij" ) @mock.patch('builtins.input', return_value='') def test_request_filepath_empty_string(self, mocked_input): filepath = ui.request_filepath('') expected_output = '' self.assertEqual(expected_output, filepath) @mock.patch('builtins.input', return_value='5') @mock.patch('builtins.print', side_effect=Exception('Break The Loop!')) def test_request_filepath_int(self, mocked_print, mocked_input): with self.assertRaises(Exception): ui.request_filepath('') mocked_print.assert_called_with(f"5 is not a valid filepath.") @mock.patch('builtins.input', return_value= f"{get_resource_filepath('empty.sfdb')}_invalid_path_addition") @mock.patch('builtins.print', side_effect=Exception('Break The Loop!')) def test_request_invalid_filepath(self, mocked_print, mocked_input): with self.assertRaises(Exception): ui.request_filepath('') mocked_print.assert_called_with( f"{get_resource_filepath('empty.sfdb')}_invalid_path_addition " f"is not a valid filepath.") @mock.patch('builtins.input', return_value=get_resource_filepath('empty.sfdb')) def test_request_filepath_valid_filepath(self, mocked_input): filepath = ui.request_filepath('') expected_output = get_resource_filepath('empty.sfdb') self.assertEqual(expected_output, filepath) self.assertTrue(os.path.isfile(filepath)) @mock.patch('builtins.input', return_value=f"{get_resource_filepath('test_duplicates.sfdb')}" ) def test_request_sfdb_valid_sfdb_filepath(self, mocked_input): test_sfdb = ui.request_sfdb('') test_filepath = get_resource_filepath('test_duplicates.sfdb') expected_output = sfdb.SFDBContainer.from_file(test_filepath) self.assertEqual(expected_output, test_sfdb) @mock.patch('builtins.input', return_value=f"{get_resource_filepath('empty.sfdb')}") def test_request_sfdb_valid_filepath_to_invalid_sfdb(self, mocked_input): with self.assertRaises(sfdb.NotSFDBFileError): ui.request_sfdb('') @mock.patch('builtins.print', side_effect=Exception('Break The Loop!')) @mock.patch('builtins.input', return_value= f"{get_resource_filepath('empty.sfdb')}_invalid_path_addition") def test_request_sfdb_invalid_filepath(self, mocked_input, mocked_print): with self.assertRaises(Exception): ui.request_filepath('') mocked_print.assert_called_with( f"{get_resource_filepath('empty.sfdb')}_invalid_path_addition " f"is not a valid filepath.") @mock.patch('builtins.input', return_value="") def test_request_sfdb_empty_filepath(self, mocked_input): test_sfdb = ui.request_sfdb('') self.assertIsNone(test_sfdb)
def configurate_logger(log_filepath): logging.addLevelName(LOGFILE_LEVEL, 'LOGFILE') logging.log_filepath = log_filepath config_file = get_resource_filepath('logging.cfg') logging.config.fileConfig(config_file)