diff --git a/semantica/normalize/number_normalizer.py b/semantica/normalize/number_normalizer.py index 29911cb9..0846465d 100644 --- a/semantica/normalize/number_normalizer.py +++ b/semantica/normalize/number_normalizer.py @@ -370,8 +370,12 @@ class UnitConverter: Raises: ValidationError: If units are incompatible or not in same category """ - from_unit = from_unit.lower() - to_unit = to_unit.lower() + # Normalize aliases before validating categories and looking up factors. + # The public API documents abbreviations such as ``kg`` and ``km``; + # validating those raw aliases against the canonical category lists + # incorrectly rejected otherwise supported conversions. + from_unit = self.normalize_unit(from_unit) + to_unit = self.normalize_unit(to_unit) # Validate units if not self.validate_units(from_unit, to_unit): @@ -401,8 +405,8 @@ class UnitConverter: Returns: bool: True if units are compatible (same category), False otherwise """ - from_unit = from_unit.lower() - to_unit = to_unit.lower() + from_unit = self.normalize_unit(from_unit) + to_unit = self.normalize_unit(to_unit) # Check if both units exist if ( @@ -498,6 +502,18 @@ class UnitConverter: "ml": "milliliter", "milliliter": "milliliter", "milliliters": "milliliter", + "ft": "foot", + "foot": "foot", + "feet": "foot", + "yd": "yard", + "yard": "yard", + "yards": "yard", + "mi": "mile", + "mile": "mile", + "miles": "mile", + "gal": "gallon", + "gallon": "gallon", + "gallons": "gallon", } return unit_map.get(unit_lower, unit_lower) diff --git a/tests/normalize/test_number_normalizer.py b/tests/normalize/test_number_normalizer.py index a7cf359c..2e06260c 100644 --- a/tests/normalize/test_number_normalizer.py +++ b/tests/normalize/test_number_normalizer.py @@ -1,4 +1,6 @@ import unittest + +from semantica.utils.exceptions import ValidationError from semantica.normalize.number_normalizer import ( NumberNormalizer, UnitConverter, @@ -32,6 +34,19 @@ class TestUnitConverter(unittest.TestCase): # 1 kg = 1000 g self.assertEqual(self.converter.convert_units(1, "kg", "g"), 1000.0) + def test_convert_accepts_aliases_for_category_validation(self): + # Aliases are part of the documented API, not just parsing syntax. + self.assertEqual(self.converter.convert_units(1, "feet", "m"), 0.3048) + self.assertEqual(self.converter.convert_units(1, "gal", "liter"), 3.78541) + + def test_convert_rejects_mismatched_categories_even_for_aliases(self): + # Both units normalize to canonical names first, so the category + # check sees real categories and rejects cross-category conversions. + with self.assertRaises(ValidationError): + self.converter.convert_units(1, "kg", "ft") + with self.assertRaises(ValidationError): + self.converter.convert_units(1, "gal", "lb") + def test_normalize_unit(self): self.assertEqual(self.converter.normalize_unit("km"), "kilometer") self.assertEqual(self.converter.normalize_unit("kgs"), "kilogram")