285 lines
11 KiB
Python
285 lines
11 KiB
Python
"""
|
|
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() |