mirror of
https://github.com/semantica-agi/semantica.git
synced 2026-08-29 04:26:20 +00:00
Convert_units() was validating categories on raw input like "kg" or "ft" instead of the normalized unit name, so aliases got checked against a category list that only has canonical names in it. Any alias-based conversion that should've worked just raised ValidationError instead. Fixed by normalizing both units before the category check runs. Also added foot/yard/mile/gallon to the alias map - they already had conversion factors but weren't mapped to their canonical names, so they'd still have failed even after the above fix. Turned out there was a second bug hiding behind the first one: the category check defaults both sides to None, and None == None is True, so two aliases from different categories that neither resolved to a real category would silently pass instead of raising. kg -> ft would just return a number instead of erroring. Normalizing first fixes this too, since aliases now resolve to their actual categories and the mismatch gets caught. Added a regression test locking that second one down - kg->ft and gal->lb now raise ValidationError instead of silently converting. Fixes #931.
95 lines
3.8 KiB
Python
95 lines
3.8 KiB
Python
import unittest
|
|
|
|
from semantica.utils.exceptions import ValidationError
|
|
from semantica.normalize.number_normalizer import (
|
|
NumberNormalizer,
|
|
UnitConverter,
|
|
CurrencyNormalizer,
|
|
ScientificNotationHandler
|
|
)
|
|
|
|
class TestNumberNormalizer(unittest.TestCase):
|
|
def setUp(self):
|
|
self.normalizer = NumberNormalizer()
|
|
|
|
def test_normalize_number_string(self):
|
|
self.assertEqual(self.normalizer.normalize_number("1,234.56"), 1234.56)
|
|
|
|
def test_normalize_quantity(self):
|
|
result = self.normalizer.normalize_quantity("5 kg")
|
|
self.assertEqual(result["value"], 5.0)
|
|
self.assertEqual(result["unit"], "kilogram")
|
|
|
|
result = self.normalizer.normalize_quantity("100 meters")
|
|
self.assertEqual(result["value"], 100.0)
|
|
self.assertEqual(result["unit"], "meter")
|
|
|
|
class TestUnitConverter(unittest.TestCase):
|
|
def setUp(self):
|
|
self.converter = UnitConverter()
|
|
|
|
def test_convert(self):
|
|
# 1 km = 1000 m
|
|
self.assertEqual(self.converter.convert_units(1, "km", "m"), 1000.0)
|
|
# 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")
|
|
|
|
class TestCurrencyNormalizer(unittest.TestCase):
|
|
def setUp(self):
|
|
self.normalizer = CurrencyNormalizer()
|
|
|
|
def test_parse_currency(self):
|
|
result = self.normalizer.normalize_currency("$1,234.56")
|
|
self.assertEqual(result["amount"], 1234.56)
|
|
self.assertEqual(result["currency"], "USD")
|
|
|
|
result = self.normalizer.normalize_currency("100 EUR")
|
|
self.assertEqual(result["amount"], 100.0)
|
|
self.assertEqual(result["currency"], "EUR")
|
|
|
|
def test_symbol_currencies_are_validated_as_supported_codes(self):
|
|
for symbol, expected_code in self.normalizer.currency_symbols.items():
|
|
result = self.normalizer.normalize_currency(f"{symbol}100")
|
|
self.assertEqual(result["currency"], expected_code)
|
|
self.assertTrue(self.normalizer.validate_currency_code(result["currency"]))
|
|
|
|
def test_currency_codes_match_boundaries_without_matching_words(self):
|
|
for value in ("RUB100", "100 RUB", "rub 100"):
|
|
result = self.normalizer.normalize_currency(value)
|
|
self.assertEqual(result["amount"], 100.0)
|
|
self.assertEqual(result["currency"], "RUB")
|
|
|
|
for value in ("ruby 100", "wilson 100"):
|
|
result = self.normalizer.normalize_currency(value)
|
|
self.assertEqual(result["amount"], 100.0)
|
|
self.assertEqual(result["currency"], "USD")
|
|
|
|
|
|
class TestScientificNotationHandler(unittest.TestCase):
|
|
def setUp(self):
|
|
self.handler = ScientificNotationHandler()
|
|
|
|
def test_parse_scientific(self):
|
|
self.assertEqual(self.handler.parse_scientific_notation("1.23e4"), 12300.0)
|
|
self.assertEqual(self.handler.parse_scientific_notation("1.23E-2"), 0.0123)
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|