""" Unit Tests for MakeNameMapper Tests the make name normalization functionality including: - Standard filename to display name conversion - Special capitalization cases (BMW, GMC, etc.) - Multi-word names (underscore to space conversion) - Validation against authoritative makes list - Error handling for edge cases """ import unittest import tempfile import json import os from unittest.mock import patch, mock_open # Import the class we're testing from ..utils.make_name_mapper import MakeNameMapper, ValidationReport class TestMakeNameMapper(unittest.TestCase): """Test cases for MakeNameMapper utility""" def setUp(self): """Set up test environment before each test""" self.mapper = MakeNameMapper() def test_normalize_single_word_makes(self): """Test normalization of single-word make names""" test_cases = [ ('toyota.json', 'Toyota'), ('honda.json', 'Honda'), ('ford.json', 'Ford'), ('audi.json', 'Audi'), ('tesla.json', 'Tesla'), ] for filename, expected in test_cases: with self.subTest(filename=filename): result = self.mapper.normalize_make_name(filename) self.assertEqual(result, expected, f"Expected '{expected}' for '{filename}', got '{result}'") def test_normalize_multi_word_makes(self): """Test normalization of multi-word make names (underscore to space)""" test_cases = [ ('alfa_romeo.json', 'Alfa Romeo'), ('land_rover.json', 'Land Rover'), ('rolls_royce.json', 'Rolls Royce'), ('aston_martin.json', 'Aston Martin'), ] for filename, expected in test_cases: with self.subTest(filename=filename): result = self.mapper.normalize_make_name(filename) self.assertEqual(result, expected, f"Expected '{expected}' for '{filename}', got '{result}'") def test_normalize_special_cases(self): """Test special capitalization cases""" test_cases = [ ('bmw.json', 'BMW'), ('gmc.json', 'GMC'), ('mini.json', 'MINI'), ('mclaren.json', 'McLaren'), ] for filename, expected in test_cases: with self.subTest(filename=filename): result = self.mapper.normalize_make_name(filename) self.assertEqual(result, expected, f"Expected '{expected}' for '{filename}', got '{result}'") def test_normalize_edge_cases(self): """Test edge cases and error handling""" test_cases = [ # Edge cases that should still work ('test.json', 'Test'), ('test_brand.json', 'Test Brand'), # Error cases that should return "Unknown" ('.json', 'Unknown'), ('', 'Unknown'), ] for filename, expected in test_cases: with self.subTest(filename=filename): result = self.mapper.normalize_make_name(filename) self.assertEqual(result, expected, f"Expected '{expected}' for '{filename}', got '{result}'") def test_validate_mapping_valid_makes(self): """Test validation of valid make names""" # These should be in the authoritative list valid_cases = [ ('toyota.json', 'Toyota'), ('alfa_romeo.json', 'Alfa Romeo'), ('bmw.json', 'BMW'), ('land_rover.json', 'Land Rover'), ] for filename, display_name in valid_cases: with self.subTest(filename=filename): is_valid = self.mapper.validate_mapping(filename, display_name) self.assertTrue(is_valid, f"'{display_name}' should be valid") def test_validate_mapping_invalid_makes(self): """Test validation of invalid make names""" invalid_cases = [ ('unknown.json', 'Unknown Brand'), ('fake.json', 'Fake'), ('test.json', 'Test Make'), ] for filename, display_name in invalid_cases: with self.subTest(filename=filename): is_valid = self.mapper.validate_mapping(filename, display_name) self.assertFalse(is_valid, f"'{display_name}' should be invalid") def test_reverse_lookup(self): """Test reverse lookup functionality""" test_cases = [ ('Toyota', 'toyota.json'), ('Alfa Romeo', 'alfa_romeo.json'), ('BMW', 'bmw.json'), ('Land Rover', 'land_rover.json'), ('McLaren', 'mclaren.json'), ] for display_name, expected_filename in test_cases: with self.subTest(display_name=display_name): result = self.mapper.get_filename_for_display_name(display_name) self.assertEqual(result, expected_filename, f"Expected '{expected_filename}' for '{display_name}', got '{result}'") def test_get_all_mappings(self): """Test getting all mappings from a directory""" # Create temporary directory with test files with tempfile.TemporaryDirectory() as temp_dir: # Create test JSON files test_files = ['toyota.json', 'alfa_romeo.json', 'bmw.json'] for filename in test_files: file_path = os.path.join(temp_dir, filename) with open(file_path, 'w') as f: json.dump({"test": "data"}, f) mappings = self.mapper.get_all_mappings(temp_dir) # Check that all files were processed self.assertEqual(len(mappings), len(test_files)) # Check specific mappings expected_mappings = { 'toyota.json': 'Toyota', 'alfa_romeo.json': 'Alfa Romeo', 'bmw.json': 'BMW' } for filename, expected_display in expected_mappings.items(): self.assertIn(filename, mappings) self.assertEqual(mappings[filename], expected_display) def test_validation_report(self): """Test validation report generation""" # Create temporary directory with test files with tempfile.TemporaryDirectory() as temp_dir: # Mix of valid and invalid files test_files = { 'toyota.json': {"test": "data"}, # Valid 'alfa_romeo.json': {"test": "data"}, # Valid 'unknown_brand.json': {"test": "data"} # Invalid } for filename, content in test_files.items(): file_path = os.path.join(temp_dir, filename) with open(file_path, 'w') as f: json.dump(content, f) report = self.mapper.validate_all_mappings(temp_dir) # Check report structure self.assertIsInstance(report, ValidationReport) self.assertEqual(report.total_files, 3) self.assertEqual(report.valid_mappings, 2) # toyota and alfa_romeo should be valid self.assertEqual(len(report.mismatches), 1) # unknown_brand should be invalid # Check success rate expected_rate = 2/3 # 2 valid out of 3 total self.assertAlmostEqual(report.success_rate, expected_rate, places=2) # Check mismatch details mismatch = report.mismatches[0] self.assertEqual(mismatch['filename'], 'unknown_brand.json') self.assertEqual(mismatch['mapped_name'], 'Unknown Brand') self.assertEqual(mismatch['status'], 'NOT_FOUND_IN_AUTHORITATIVE') @patch('builtins.open', mock_open(read_data='{"manufacturers": ["Toyota", "BMW", "Custom Brand"]}')) def test_load_custom_authoritative_makes(self): """Test loading custom authoritative makes from file""" with patch('os.path.exists', return_value=True): mapper = MakeNameMapper(sources_dir='test_sources') # Should have loaded the custom list self.assertIn('Toyota', mapper.authoritative_makes) self.assertIn('BMW', mapper.authoritative_makes) self.assertIn('Custom Brand', mapper.authoritative_makes) # Test validation with custom list self.assertTrue(mapper.validate_mapping('custom.json', 'Custom Brand')) def test_make_statistics(self): """Test make statistics calculation""" with tempfile.TemporaryDirectory() as temp_dir: # Create test files representing different categories test_files = [ 'toyota.json', # Single word 'honda.json', # Single word 'alfa_romeo.json', # Multi word 'land_rover.json', # Multi word 'bmw.json', # Special case 'gmc.json', # Special case ] for filename in test_files: file_path = os.path.join(temp_dir, filename) with open(file_path, 'w') as f: json.dump({"test": "data"}, f) stats = self.mapper.get_make_statistics(temp_dir) expected_stats = { 'total': 6, 'single_words': 2, # toyota, honda 'multi_words': 2, # alfa_romeo, land_rover 'special_cases': 2 # bmw, gmc } self.assertEqual(stats, expected_stats) def test_error_handling(self): """Test error handling for various failure scenarios""" # Test with non-existent directory mappings = self.mapper.get_all_mappings('/non/existent/directory') self.assertEqual(mappings, {}) # Test validation report with non-existent directory report = self.mapper.validate_all_mappings('/non/existent/directory') self.assertEqual(report.total_files, 0) self.assertEqual(report.valid_mappings, 0) self.assertEqual(len(report.mismatches), 0) class TestValidationReport(unittest.TestCase): """Test cases for ValidationReport dataclass""" def test_success_rate_calculation(self): """Test success rate calculation""" # Test normal case report = ValidationReport( total_files=10, valid_mappings=8, mismatches=[] ) self.assertEqual(report.success_rate, 0.8) # Test zero division case report_empty = ValidationReport( total_files=0, valid_mappings=0, mismatches=[] ) self.assertEqual(report_empty.success_rate, 0.0) # Test perfect score report_perfect = ValidationReport( total_files=5, valid_mappings=5, mismatches=[] ) self.assertEqual(report_perfect.success_rate, 1.0) if __name__ == '__main__': unittest.main()