"""Tests for fuel-specific pattern matching.""" import pytest from app.patterns.fuel_patterns import FuelPatternMatcher, fuel_matcher class TestFuelQuantityExtraction: """Test fuel quantity (gallons/liters) extraction.""" def test_gallons_suffix(self) -> None: """Test 'XX.XXX GAL' pattern.""" text = "10.500 GAL" result = fuel_matcher.extract_gallons(text) assert result is not None assert result.value == 10.5 assert result.unit == "GAL" assert result.confidence > 0.9 def test_gallons_full_word(self) -> None: """Test 'XX.XXX GALLONS' pattern.""" text = "10.500 GALLONS" result = fuel_matcher.extract_gallons(text) assert result is not None assert result.value == 10.5 def test_gallons_prefix(self) -> None: """Test 'GALLONS: XX.XXX' pattern.""" text = "GALLONS: 10.500" result = fuel_matcher.extract_gallons(text) assert result is not None assert result.value == 10.5 def test_gal_prefix(self) -> None: """Test 'GAL: XX.XXX' pattern.""" text = "GAL: 10.500" result = fuel_matcher.extract_gallons(text) assert result is not None assert result.value == 10.5 def test_volume_label(self) -> None: """Test 'VOLUME XX.XXX' pattern.""" text = "VOLUME: 10.500" result = fuel_matcher.extract_gallons(text) assert result is not None assert result.value == 10.5 def test_liters_suffix(self) -> None: """Test 'XX.XX L' pattern.""" text = "40.5 L" result = fuel_matcher.extract_liters(text) assert result is not None assert result.value == 40.5 assert result.unit == "L" def test_liters_full_word(self) -> None: """Test 'XX.XX LITERS' pattern.""" text = "40.5 LITERS" result = fuel_matcher.extract_liters(text) assert result is not None assert result.value == 40.5 def test_quantity_prefers_gallons(self) -> None: """Test extract_quantity prefers gallons for US receipts.""" text = "10.500 GAL" result = fuel_matcher.extract_quantity(text) assert result is not None assert result.unit == "GAL" def test_reasonable_quantity_filter(self) -> None: """Test unreasonable quantities are filtered.""" # Too small text = "0.001 GAL" result = fuel_matcher.extract_gallons(text) assert result is None # Too large text = "100.0 GAL" result = fuel_matcher.extract_gallons(text) assert result is None class TestFuelPriceExtraction: """Test price per unit extraction.""" def test_price_per_gal_dollar_sign(self) -> None: """Test '$X.XXX/GAL' pattern.""" text = "$3.679/GAL" result = fuel_matcher.extract_price_per_unit(text) assert result is not None assert result.value == 3.679 assert result.unit == "GAL" assert result.confidence > 0.95 def test_price_per_gal_no_dollar(self) -> None: """Test 'X.XXX/GAL' pattern.""" text = "3.679/GAL" result = fuel_matcher.extract_price_per_unit(text) assert result is not None assert result.value == 3.679 def test_labeled_price_gal(self) -> None: """Test 'PRICE/GAL $X.XXX' pattern.""" text = "PRICE/GAL $3.679" result = fuel_matcher.extract_price_per_unit(text) assert result is not None assert result.value == 3.679 def test_unit_price(self) -> None: """Test 'UNIT PRICE $X.XXX' pattern.""" text = "UNIT PRICE: $3.679" result = fuel_matcher.extract_price_per_unit(text) assert result is not None assert result.value == 3.679 def test_at_price(self) -> None: """Test '@ $X.XXX' pattern.""" text = "10.500 GAL @ $3.679" result = fuel_matcher.extract_price_per_unit(text) assert result is not None assert result.value == 3.679 def test_ppg_pattern(self) -> None: """Test 'PPG $X.XXX' pattern.""" text = "PPG: $3.679" result = fuel_matcher.extract_price_per_unit(text) assert result is not None assert result.value == 3.679 def test_reasonable_price_filter(self) -> None: """Test unreasonable prices are filtered.""" # Too low text = "$0.50/GAL" result = fuel_matcher.extract_price_per_unit(text) assert result is None # Too high text = "$15.00/GAL" result = fuel_matcher.extract_price_per_unit(text) assert result is None class TestFuelGradeExtraction: """Test fuel grade/octane extraction.""" def test_regular_87(self) -> None: """Test 'REGULAR 87' pattern.""" text = "REGULAR 87" result = fuel_matcher.extract_grade(text) assert result is not None assert result.value == "87" assert "Regular" in result.display_name def test_reg_87(self) -> None: """Test 'REG 87' pattern.""" text = "REG 87" result = fuel_matcher.extract_grade(text) assert result is not None assert result.value == "87" def test_unleaded_87(self) -> None: """Test 'UNLEADED 87' pattern.""" text = "UNLEADED 87" result = fuel_matcher.extract_grade(text) assert result is not None assert result.value == "87" def test_plus_89(self) -> None: """Test 'PLUS 89' pattern.""" text = "PLUS 89" result = fuel_matcher.extract_grade(text) assert result is not None assert result.value == "89" assert "Plus" in result.display_name def test_midgrade_89(self) -> None: """Test 'MIDGRADE 89' pattern.""" text = "MIDGRADE 89" result = fuel_matcher.extract_grade(text) assert result is not None assert result.value == "89" def test_premium_93(self) -> None: """Test 'PREMIUM 93' pattern.""" text = "PREMIUM 93" result = fuel_matcher.extract_grade(text) assert result is not None assert result.value == "93" assert "Premium" in result.display_name def test_super_93(self) -> None: """Test 'SUPER 93' pattern.""" text = "SUPER 93" result = fuel_matcher.extract_grade(text) assert result is not None assert result.value == "93" def test_diesel(self) -> None: """Test 'DIESEL' pattern.""" text = "DIESEL #2" result = fuel_matcher.extract_grade(text) assert result is not None assert result.value == "DIESEL" assert "Diesel" in result.display_name def test_e85(self) -> None: """Test 'E85' ethanol pattern.""" text = "E85" result = fuel_matcher.extract_grade(text) assert result is not None assert result.value == "E85" def test_octane_only(self) -> None: """Test standalone octane number.""" text = "87 OCTANE" result = fuel_matcher.extract_grade(text) assert result is not None assert result.value == "87" class TestMerchantExtraction: """Test gas station name extraction.""" def test_shell_station(self) -> None: """Test Shell station detection.""" text = "SHELL\n123 MAIN ST" result = fuel_matcher.extract_merchant_name(text) assert result is not None merchant, confidence = result assert "SHELL" in merchant.upper() assert confidence > 0.8 def test_chevron_station(self) -> None: """Test Chevron station detection.""" text = "CHEVRON #12345\n456 OAK AVE" result = fuel_matcher.extract_merchant_name(text) assert result is not None merchant, confidence = result assert "CHEVRON" in merchant.upper() def test_costco_gas(self) -> None: """Test Costco gas detection.""" text = "COSTCO GASOLINE\n789 WAREHOUSE BLVD" result = fuel_matcher.extract_merchant_name(text) assert result is not None merchant, confidence = result assert "COSTCO" in merchant.upper() def test_unknown_station_fallback(self) -> None: """Test fallback to first line for unknown stations.""" text = "JOE'S GAS\n123 MAIN ST" result = fuel_matcher.extract_merchant_name(text) assert result is not None merchant, confidence = result assert "JOE'S GAS" in merchant assert confidence < 0.7 # Lower confidence for unknown class TestReceiptContextExtraction: """Test extraction from realistic receipt text.""" def test_full_receipt_extraction(self) -> None: """Test all fields from complete receipt text.""" text = """ SHELL 123 MAIN STREET ANYTOWN, USA 12345 DATE: 01/15/2024 TIME: 14:32 PUMP #5 REGULAR 87 10.500 GAL @ $3.679/GAL FUEL TOTAL $38.63 TAX $0.00 TOTAL $38.63 DEBIT CARD ************1234 APPROVED """ # Test all extractors on this text quantity = fuel_matcher.extract_quantity(text) assert quantity is not None assert quantity.value == 10.5 price = fuel_matcher.extract_price_per_unit(text) assert price is not None assert price.value == 3.679 grade = fuel_matcher.extract_grade(text) assert grade is not None assert grade.value == "87" merchant = fuel_matcher.extract_merchant_name(text) assert merchant is not None assert "SHELL" in merchant[0].upper()