import utils.validation as validation import pandas as pd from pandas import testing as tm import math from constants import knr_consts class TestValidation: def test_handle_validation_errors(self): mock_col_name = 'Sales Start Date' mock_invalid_rows = pd.DataFrame(data={mock_col_name: ['01-02-abc', 'notadate']}) mock_err_df = pd.DataFrame(columns=[mock_col_name]) expected_errs_result = [f'Invalid {mock_col_name}'] * 2 err_df_result, errs_result = validation.handle_validation_errors(mock_invalid_rows, mock_col_name, mock_err_df) assert errs_result == expected_errs_result, 'Correct array of errors returned' tm.assert_frame_equal(err_df_result, mock_invalid_rows), 'error df contains the invalid rows' def test_valid_value(self): mock_valid_items_arr = knr_consts.VALID_PRODUCT_FORMATS mock_column_name = "Product Format" assert validation.valid_value("Album", mock_valid_items_arr, "", True, mock_column_name) == True, 'Returns True - valid single item' assert validation.valid_value("Album | EP", mock_valid_items_arr, "", True, mock_column_name) == False, 'Returns False - invalid item' assert validation.valid_value("ALBUM | EP", mock_valid_items_arr, "|", True, mock_column_name) == True, 'Returns true - valid items with separator' assert validation.valid_value("Album | EP | ", mock_valid_items_arr, "|", True, mock_column_name) == True, 'Returns True - items with extra pipe after last item' assert validation.valid_value("", mock_valid_items_arr, "", True, mock_column_name) == False, 'Returns False - empty mandatory item' assert validation.valid_value("", mock_valid_items_arr, "", False, mock_column_name) == True, 'Returns True - empty non-mandatory item' assert validation.valid_value("|", mock_valid_items_arr, "|", True, mock_column_name) == False, 'Returns False - mandatory item with no values' assert validation.valid_value("Short Form", mock_valid_items_arr, "", True, mock_column_name) == True, 'Returns True - valid single item' def test_is_valid_length(self): assert validation.is_valid_length(100, [3]) == True, 'Returns True' assert validation.is_valid_length("100", [3]) == True, 'Returns True' assert validation.is_valid_length("abc123", [6]) == True, 'Returns True' assert validation.is_valid_length(1000, [5]) == False, 'Returns False' assert validation.is_valid_length("10A", [2]) == False, 'Returns False' def test_validate_mandatory_columns(self): mock_valid_ingest_df = pd.DataFrame(data={ # 'Sales Start Date': ["01-Feb-2023"], 'Duration': [300], # 'Product Format': ["Album"], 'Collection Ownership Country': ['DE'] }) mock_valid_ingest_df_str_duration = pd.DataFrame(data={ # 'Sales Start Date': ["01-Feb-2023"], 'Duration': ["00:05:10"], # 'Product Format': ["Album"], 'Collection Ownership Country': ['DE'] }) mock_empty_duration_df = pd.DataFrame(data={ # 'Sales Start Date': ["01-Feb-2023"], 'Duration': [math.nan], # 'Product Format':["Album"], 'Collection Ownership Country': ['DE'] }) # mock_invalid_product_format_df = pd.DataFrame(data={ # 'Sales Start Date': ["01-Feb-2023"], # 'Duration': [300], # 'Product Format': ["DVD"], # 'Collection Ownership Country': ['DE|US'] # }) mock_invalid_country_format_df = pd.DataFrame(data={ # 'Sales Start Date': ["01-Feb-2023"], 'Duration': [300], # 'Product Format': ["Album"], 'Collection Ownership Country': ['Germany'] }) # df_with_col_headers = pd.DataFrame(columns=['Sales Start Date','Duration','Product Format', 'Collection Ownership Country']) df_with_col_headers = pd.DataFrame(columns=['Duration', 'Collection Ownership Country']) valid_input_ingest_df, valid_input_errs_df, valid_input_errs_arr, error_row_indexes = validation.validate_mandatory_columns(mock_valid_ingest_df, df_with_col_headers) tm.assert_frame_equal(valid_input_ingest_df, mock_valid_ingest_df), 'Returns valid rows in ingest df' assert len(valid_input_errs_df.index) == 0, 'errs_df is empty' assert len(valid_input_errs_arr) == 0, 'errors array is empty' assert len(error_row_indexes) == 0 str_duration_ingest_df, str_duration_errs_df, str_duration_errs_arr, error_row_indexes = validation.validate_mandatory_columns(mock_valid_ingest_df_str_duration, df_with_col_headers) tm.assert_frame_equal(str_duration_ingest_df, mock_valid_ingest_df_str_duration), 'Returns valid rows in ingest df' assert len(str_duration_errs_df.index) == 0, 'errs_df is empty' assert len(str_duration_errs_arr) == 0, 'errors array is empty' assert len(error_row_indexes) == 0 empty_input_ingest_df, empty_input_errs_df, empty_input_errs_arr, error_row_indexes = validation.validate_mandatory_columns(mock_empty_duration_df, df_with_col_headers) tm.assert_frame_equal(empty_input_ingest_df, mock_empty_duration_df, check_dtype=False), 'errs_df contains one row' assert empty_input_errs_arr == ['Invalid Duration'], 'errors array contains correct error' assert len(error_row_indexes) == 1 # invalid_input_ingest_df, invalid_input_errs_df, invalid_input_errs_arr, error_row_indexes = validation.validate_mandatory_columns(mock_invalid_product_format_df, df_with_col_headers) # tm.assert_frame_equal(invalid_input_ingest_df, mock_invalid_product_format_df, check_dtype=False), 'errs_df contains one row' # assert invalid_input_errs_arr == ['Invalid Product Format'], 'errors array contains correct error' # assert len(error_row_indexes) == 1 invalid_input_ingest_df, invalid_input_errs_df, invalid_input_errs_arr, error_row_indexes = validation.validate_mandatory_columns(mock_invalid_country_format_df, df_with_col_headers) tm.assert_frame_equal(invalid_input_ingest_df, mock_invalid_country_format_df, check_dtype=False), 'errs_df contains one row' assert invalid_input_errs_arr == ['Invalid Collection Ownership Country'], 'errors array contains correct error' assert len(error_row_indexes) == 1 def test_validate_non_mandatory_columns(self): mock_valid_ingest_df = pd.DataFrame(data={ 'Collection Ownership End Date': ["01-Mar-2023"], 'Collection Ownership Percentage of Rights': [50], 'Collection Ownership Country - Exclusion': ['US'] }) mock_empty_values_df = pd.DataFrame(data={ 'Collection Ownership End Date': [], 'Collection Ownership Percentage of Rights': [], 'Collection Ownership Country - Exclusion': [] }) mock_invalid_country_df = pd.DataFrame(data={ 'Collection Ownership End Date': ["01-Mar-2023"], 'Collection Ownership Percentage of Rights': [100], 'Collection Ownership Country - Exclusion': ['World'] }) mock_invalid_country_df_2 = pd.DataFrame(data={ 'Collection Ownership End Date': ["01-Mar-2023"], 'Collection Ownership Percentage of Rights': [100], 'Collection Ownership Country - Exclusion': ['Germany'] }) mock_invalid_percentage_df = pd.DataFrame(data={ 'Collection Ownership End Date': ["01-Mar-2023"], 'Collection Ownership Percentage of Rights': [500], 'Collection Ownership Country - Exclusion': ['US|DE'] }) df_with_col_headers = pd.DataFrame(columns=['Collection Ownership End Date', 'Collection Ownership Percentage of Rights', 'Collection Ownership Country - Exclusion']) valid_input_ingest_df, valid_input_err_df, valid_input_errs_arr, error_row_indexes = validation.validate_non_mandatory_columns(mock_valid_ingest_df, df_with_col_headers ) tm.assert_frame_equal(valid_input_ingest_df, mock_valid_ingest_df), 'Returns valid rows in ingest df' assert len(valid_input_err_df.index) == 0, 'errs_df is empty' assert len(valid_input_errs_arr) == 0, 'errors array is empty' assert len(error_row_indexes) == 0 empty_input_df, empty_input_errs_df, empty_input_errs_arr, error_row_indexes = validation.validate_non_mandatory_columns(mock_empty_values_df, df_with_col_headers ) tm.assert_frame_equal(empty_input_df, mock_empty_values_df, check_dtype=False), 'Returns ingest df with empty values' assert len(empty_input_errs_df.index) == 0, 'errs_df is empty' assert len(empty_input_errs_arr) == 0, 'errors array is empty' assert len(error_row_indexes) == 0 invalid_input_ingest_df, invalid_input_errs_df, invalid_input_errs_arr, error_row_indexes = validation.validate_non_mandatory_columns(mock_invalid_country_df, df_with_col_headers ) tm.assert_frame_equal(invalid_input_errs_df, mock_invalid_country_df, check_dtype=False), 'errs_df contains one row' assert invalid_input_errs_arr == ['Invalid Collection Ownership Country - Exclusion'], 'errs array contains correct err' assert len(error_row_indexes) == 1 invalid_input_ingest_df, invalid_input_errs_df, invalid_input_errs_arr, error_row_indexes = validation.validate_non_mandatory_columns(mock_invalid_country_df_2, df_with_col_headers ) tm.assert_frame_equal(invalid_input_errs_df, mock_invalid_country_df_2, check_dtype=False), 'errs_df contains one row' assert invalid_input_errs_arr == ['Invalid Collection Ownership Country - Exclusion'], 'errs array contains correct err' assert len(error_row_indexes) == 1 invalid_input_ingest_df, invalid_input_errs_df, invalid_input_errs_arr, error_row_indexes = validation.validate_non_mandatory_columns(mock_invalid_percentage_df, df_with_col_headers ) tm.assert_frame_equal(invalid_input_errs_df, mock_invalid_percentage_df, check_dtype=False), 'errs_df contains one row' assert invalid_input_errs_arr == ['Invalid Collection Ownership Percentage of Rights'], 'errs array contains correct err' assert len(error_row_indexes) == 1 def test_valid_str_duration(self): assert validation.valid_str_duration("01:02:03") == True, 'Validates HH:MM:SS string correctly' assert validation.valid_str_duration("01:02:99") == False, 'Returns False for invalid string of correct pattern' assert validation.valid_str_duration("abc") == False, 'Returns False for invalid string' assert validation.valid_str_duration(1000) == False, 'Returns False for number' def test_valid_duration(self): assert validation.valid_duration("00:03:01") == True assert validation.valid_duration(300) == True assert validation.valid_duration("500") == True assert validation.valid_duration(300.9999) == True assert validation.valid_duration(0) == True assert validation.valid_duration(float("nan")) == False assert validation.valid_duration("abc") == False def test_remove_non_numeric_chars(self): assert validation.remove_non_numeric_chars(782047232304) == "782047232304", "Formats upc of type number" assert validation.remove_non_numeric_chars("8232325323232") == "8232325323232", "Formats upc of type string" assert validation.remove_non_numeric_chars(" 3967 4937 5938 ") == "396749375938", "Removes whitespace" assert validation.remove_non_numeric_chars("0799600587768 ") == "0799600587768", "Removes punctuation" assert validation.remove_non_numeric_chars("ABC-0799600587768") == "0799600587768", "Removes letters" def test_validate_columns_of_set_length(self): MOCK_COLS = ['(P) LINE Year', [4], True], ['UPC', [12, 13], True] mock_valid_ingest_df = pd.DataFrame(data={ '(P) LINE Year': ['2023'], 'UPC': ["0799600587768  "], }) mock_valid_ingest_df_trimmed = pd.DataFrame(data={ '(P) LINE Year': ['2023'], 'UPC': ["0799600587768"], }) mock_valid_ingest_df_num = pd.DataFrame(data={ '(P) LINE Year': ['2023a'], 'UPC': [843930050840], }) mock_valid_ingest_df_num_trimmed = pd.DataFrame(data={ '(P) LINE Year': ['2023'], 'UPC': ["843930050840"], }) mock_invalid_ingest_df = pd.DataFrame(data={ '(P) LINE Year': ['202 '], 'UPC': ["0799600587768"], }) mock_invalid_ingest_df_trimmed = pd.DataFrame(data={ '(P) LINE Year': ['202'], 'UPC': ["0799600587768"], }) mock_empty_df = pd.DataFrame(data={ '(P) LINE Year': [], 'UPC': [], }) valid_input_ingest_df, valid_input_errs_df, valid_input_errs_arr, error_row_indexes = validation.validate_columns_with_set_length(mock_valid_ingest_df, mock_empty_df, MOCK_COLS) tm.assert_frame_equal(valid_input_ingest_df, mock_valid_ingest_df_trimmed), 'Returns valid trimmed rows in ingest df' assert len(valid_input_errs_df.index) == 0, 'errs_df is empty' assert len(valid_input_errs_arr) == 0, 'errors array is empty' assert len(error_row_indexes) == 0 valid_input_ingest_df_num, valid_input_errs_df_num, valid_input_num_errs_arr, error_row_indexes = validation.validate_columns_with_set_length(mock_valid_ingest_df_num, mock_empty_df, MOCK_COLS) tm.assert_frame_equal(valid_input_ingest_df_num, mock_valid_ingest_df_num_trimmed), 'Returns valid trimmed rows in ingest df' assert len(valid_input_errs_df_num.index) == 0, 'errs_df is empty' assert len(valid_input_num_errs_arr) == 0, 'errors array is empty' assert len(error_row_indexes) == 0 invalid_input_ingest_df, invalid_input_errs_df, invalid_input_errs_arr, error_row_indexes = validation.validate_columns_with_set_length(mock_invalid_ingest_df, mock_empty_df, MOCK_COLS) tm.assert_frame_equal(invalid_input_errs_df, mock_invalid_ingest_df_trimmed, check_dtype=False), 'errs_df contains one row' assert invalid_input_errs_arr == ['Invalid (P) LINE Year'], 'errors array contains correct error' assert len(error_row_indexes) == 1 def test_validate_contributor_count(self): mock_empty_ingest_df = pd.DataFrame(data={ 'Contributor Legal Name': [], 'Contributor Type': [], 'Contributor Role(s)': [] }) mock_valid_ingest_df = mock_empty_ingest_df.copy() mock_valid_ingest_df.loc[0] = ['Gerber, Andy|Gerber, Andy|Sabin, Geoff', 'Session Musician|Session Musician|Session Musician', 'Whistle | Drums | Bass Guitar'] mock_valid_ingest_df_with_extra_pipes = mock_empty_ingest_df.copy() mock_valid_ingest_df_with_extra_pipes.loc[0] = ['Gerber, Andy|Gerber, Andy| | Sabin, Geoff | |', 'Session Musician|Session Musician|Session Musician', 'Whistle | Drums | Bass Guitar'] mock_invalid_ingest_df = mock_empty_ingest_df.copy() mock_invalid_ingest_df.loc[0] = ['Gerber, Andy|Gerber, Andy|Sabin, Geoff', 'Session Musician|Session Musician', 'Whistle | Drums | Bass Guitar'] tm.assert_frame_equal(validation.validate_contributor_count(mock_valid_ingest_df), mock_empty_ingest_df, check_dtype=False), 'Returns no invalid rows' tm.assert_frame_equal(validation.validate_contributor_count(mock_valid_ingest_df_with_extra_pipes), mock_empty_ingest_df, check_dtype=False), 'Returns no invalid rows when additional pipes' tm.assert_frame_equal(validation.validate_contributor_count(mock_invalid_ingest_df), mock_invalid_ingest_df, check_dtype=False), 'Returns invalid row' def test_contains_invalid_value(self): INVALID_VALUES = ["MAIN", "MAINPERFORMER", "MAINPERFORMERS"] valid_value = "Guitar | Piano" invalid_value = "Guitar | Main Performer | Piano" invalid_value_with_extra_punctuation = " Guitar | Main |" assert validation.contains_invalid_value(valid_value, INVALID_VALUES, "|", False) == False, 'Returns false for valid string value' assert validation.contains_invalid_value('', INVALID_VALUES, "|", False) == False, 'Returns false for empty string value' assert validation.contains_invalid_value(invalid_value, INVALID_VALUES, "|", False), 'Returns true for invalid value' assert validation.contains_invalid_value(invalid_value_with_extra_punctuation, INVALID_VALUES, "|", False), 'Returns true for invalid value' def test_validate_contributor_columns(self): valid_data = { 'Contributor Type': ["Featured Artist | Featured Artists"], 'Contributor Legal Name': ['Gerber, Andy|Gerber, Andy'], 'Contributor Role(s)': ['Whistle | Drums'], } mock_valid_ingest_df = pd.DataFrame(data=valid_data) mock_invalid_contributor_type_df = pd.DataFrame(data={**valid_data, 'Contributor Type': ['Featrd Performr | Main']}) mock_invalid_contributor_counts_df = pd.DataFrame(data={**valid_data, 'Contributor Legal Name': ['Gerber, Andy|Gerber, Andy|Andy']}) mock_empty_strings_df = pd.DataFrame(data={'Contributor Type': [''], 'Contributor Legal Name': [''], 'Contributor Role(s)': ['']}) df_with_col_headers = pd.DataFrame(columns=['Contributor Type', 'Contributor Legal Name', 'Contributor Role(s)']) mock_name_containing_type_df = pd.DataFrame(data={**valid_data, 'Contributor Legal Name': ['Gerber, Andy|Featured Artist']}) mock_role_containing_type_df = pd.DataFrame(data={**valid_data, 'Contributor Role(s)': ['Featured Artist|Piano']}) valid_input_ingest_df, valid_input_err_df, valid_input_errs_arr = validation.validate_contributor_columns(mock_valid_ingest_df, df_with_col_headers) tm.assert_frame_equal(valid_input_ingest_df, mock_valid_ingest_df), 'Returns valid rows in ingest df' assert len(valid_input_err_df.index) == 0, 'errs_df is empty' assert len(valid_input_errs_arr) == 0, 'errors array is empty' empty_input_df, empty_input_errs_df, empty_input_errs_arr = validation.validate_contributor_columns(mock_empty_strings_df, df_with_col_headers) tm.assert_frame_equal(empty_input_df, mock_empty_strings_df, check_dtype=False), 'Returns ingest df with empty values' assert len(empty_input_errs_df.index) == 0, 'errs_df is empty' assert len(empty_input_errs_arr) == 0, 'errors array is empty' invalid_type_input_ingest_df, invalid_type_input_errs_df, invalid_type_input_errs_arr = validation.validate_contributor_columns(mock_invalid_contributor_type_df, df_with_col_headers) tm.assert_frame_equal(invalid_type_input_ingest_df, mock_empty_strings_df, check_dtype=False), 'ingest df contains row, with contributor cols set to empty strings' tm.assert_frame_equal(invalid_type_input_errs_df, pd.DataFrame(data={**valid_data, 'Contributor Type': ['Featrd Performr | Main']}), check_dtype=False), 'errs_df contains one row with input contributor values' assert invalid_type_input_errs_arr == ['Invalid Contributor Type'], 'errs array contains correct err' invalid_count_ingest_df, invalid_input_errs_df, invalid_input_errs_arr = validation.validate_contributor_columns(mock_invalid_contributor_counts_df, df_with_col_headers) tm.assert_frame_equal(invalid_count_ingest_df, mock_empty_strings_df), 'ingest df contains row, with contributor cols set to empty strings' tm.assert_frame_equal(invalid_input_errs_df, pd.DataFrame(data={**valid_data, 'Contributor Legal Name': ['Gerber, Andy|Gerber, Andy|Andy']}), check_dtype=False), 'errs_df contains one row' assert invalid_input_errs_arr == ['Invalid Contributor Columns - number of Contributor Legal Names, Types, and Roles must be the same.'], 'errs array contains correct err' invalid_name_ingest_df, invalid_name_errs_df, invalid_name_errs_arr = validation.validate_contributor_columns(mock_name_containing_type_df, df_with_col_headers) tm.assert_frame_equal(invalid_name_ingest_df, mock_empty_strings_df), 'ingest df contains row, with contributor cols set to empty strings' tm.assert_frame_equal(invalid_name_errs_df, pd.DataFrame(data={**valid_data, 'Contributor Legal Name': ['Gerber, Andy|Featured Artist']}), check_dtype=False), 'errs_df contains one row' assert invalid_name_errs_arr == ['Invalid Contributor Legal Name (includes a Contributor Type)'], 'errs array contains correct err' invalid_role_ingest_df, invalid_role_errs_df, invalid_role_errs_arr = validation.validate_contributor_columns(mock_role_containing_type_df, df_with_col_headers) tm.assert_frame_equal(invalid_role_ingest_df, mock_empty_strings_df), 'ingest df contains row, with contributor cols set to empty strings' tm.assert_frame_equal(invalid_role_errs_df, pd.DataFrame(data={**valid_data, 'Contributor Role(s)': ['Featured Artist|Piano']}), check_dtype=False), 'errs_df contains one row' assert invalid_role_errs_arr == ['Invalid Contributor Role(s) (includes a Contributor Type)'], 'errs array contains correct err' def test_validate_isrc_column(self): mock_valid_ingest_df = pd.DataFrame(data={ 'ISRC': ["valid-isrc"], }) mock_isrc_to_exclude_df = pd.DataFrame(data={ 'ISRC': ["GBKPL1665961"], }) df_with_col_headers = pd.DataFrame(columns=['ISRC']) valid_input_ingest_df, valid_input_err_df, valid_input_errs_arr, error_row_indexes = validation.validate_isrc_column(mock_valid_ingest_df, df_with_col_headers ) tm.assert_frame_equal(valid_input_ingest_df, mock_valid_ingest_df), 'Returns valid rows in ingest df' assert len(valid_input_err_df.index) == 0, 'errs_df is empty' assert len(valid_input_errs_arr) == 0, 'errors array is empty' assert len(error_row_indexes) == 0 invalid_input_ingest_df, invalid_input_errs_df, invalid_input_errs_arr, error_row_indexes = validation.validate_isrc_column(mock_isrc_to_exclude_df, df_with_col_headers ) tm.assert_frame_equal(invalid_input_errs_df, mock_isrc_to_exclude_df, check_dtype=False), 'errs_df contains one row' assert invalid_input_errs_arr == ['Invalid ISRC - excluded bc of ownership clash'], 'errs array contains correct err' assert len(error_row_indexes) == 1 def test_validate_video_product_metadata(self): valid_data = { 'UPC': ['123456789123'], 'Product Format': ['Clip'], 'Sales Start Date': ['01-Feb-2023'], '(P) LINE Year': ['2022'], '(P) LINE Label': ['some label'], 'Product Primary Artists': ['Some primary artist'], 'Product Name': ['some product name'], 'Product Code': ['some product code'] } mock_valid_ingest_df = pd.DataFrame(data=valid_data) mock_invalid_upc_df = pd.DataFrame(data={**valid_data, 'UPC': ['12345']}) mock_invalid_format_df = pd.DataFrame(data={**valid_data, 'Product Format': ['made up format']}) mock_no_upc_format_df = pd.DataFrame(data={**valid_data, 'UPC': ['']}) mock_pline_df = pd.DataFrame(data={**valid_data, '(P) LINE Year': ['202']}) df_with_col_headers = pd.DataFrame(columns=['UPC', 'Product Format', 'Sales Start Date', '(P) LINE Year', '(P) LINE Label', 'Product Primary Artists', 'Product Name', 'Product Code']) valid_input_ingest_df, valid_input_err_df, valid_input_errs_arr, row_index = validation.validate_video_product_metadata(mock_valid_ingest_df, df_with_col_headers) tm.assert_frame_equal(valid_input_ingest_df, mock_valid_ingest_df), 'Returns valid rows in ingest df' assert len(valid_input_err_df.index) == 0, 'errs_df is empty' assert len(valid_input_errs_arr) == 0, 'errors array is empty' invalid_upc_ingest_df, invalid_upc_err_df, invalid_upc_errs_arr, row_index = validation.validate_video_product_metadata(mock_invalid_upc_df, df_with_col_headers) tm.assert_frame_equal(invalid_upc_ingest_df, mock_invalid_upc_df), 'ingest df contains row' assert len(invalid_upc_err_df.index) == 1, 'errs_df is contains one row' assert invalid_upc_errs_arr == ['Invalid UPC'], 'errs array contains correct err' invalid_format_ingest_df, invalid_format_err_df, invalid_format_errs_arr, row_index = validation.validate_video_product_metadata(mock_invalid_format_df, df_with_col_headers) tm.assert_frame_equal(invalid_format_ingest_df, mock_invalid_format_df), 'ingest df contains row' assert len(invalid_format_err_df.index) == 1, 'errs_df is contains one row' assert invalid_format_errs_arr == ['Invalid Product Format'], 'errs array contains correct err' valid_input_ingest_df, valid_input_err_df, valid_input_errs_arr, row_index = validation.validate_video_product_metadata(mock_no_upc_format_df, df_with_col_headers) tm.assert_frame_equal(valid_input_ingest_df, mock_no_upc_format_df), 'Returns valid rows in ingest df' assert len(valid_input_err_df.index) == 0, 'errs_df is empty' assert len(valid_input_errs_arr) == 0, 'errors array is empty' invalid_pline_ingest_df, invalid_pline_err_df, invalid_pline_errs_arr, row_index = validation.validate_video_product_metadata(mock_pline_df, df_with_col_headers) tm.assert_frame_equal(invalid_pline_ingest_df, mock_pline_df), 'ingest df contains row' assert len(invalid_pline_err_df.index) == 1, 'errs_df is contains one row' assert invalid_pline_errs_arr == ['Invalid (P) LINE Year'], 'errs array contains correct err'