from django.test import TestCase from common.models import Municipality, AdrStreet, AdrPoint from django.contrib.gis.geos import Point from .internal_geocoding import ( normalize_address, search_address_points, search_streets, geocode_internal, DEFAULT_SCORE_THRESHOLD ) class NormalizeAddressTestCase(TestCase): """Test address normalization""" def test_normalize_empty_address(self): self.assertEqual(normalize_address(""), "") self.assertEqual(normalize_address(None), "") def test_normalize_removes_extra_spaces(self): self.assertEqual(normalize_address(" rue de la loi "), "rue de la loi") def test_normalize_lowercase(self): self.assertEqual(normalize_address("Rue DE La LOI"), "rue de la loi") class InternalGeocodingTestCase(TestCase): """Test internal geocoding with fuzzy matching""" def setUp(self): """Create test data""" # Create a municipality self.municipality = Municipality.objects.create( fid="1000", name_fr="Bruxelles", name_nl="Brussel", niscode="21004" ) # Create a street self.street = AdrStreet.objects.create( fid="street_001", name_fr="Rue de la Loi", name_nl="Wetstraat", full_street_fr="Rue de la Loi", full_street_nl="Wetstraat", municipality=self.municipality, lon=4.3676, lat=50.8467, centroid=Point(4.3676, 50.8467, srid=3812) ) # Create address points self.point1 = AdrPoint.objects.create( fid="point_001", number="16", street_name=self.street, municipality=self.municipality, zipcode="1000", full_address_fr="16 Rue de la Loi Bruxelles", full_address_nl="16 Wetstraat Brussel", lon=4.3676, lat=50.8467, geom=Point(4.3676, 50.8467, srid=3812) ) self.point2 = AdrPoint.objects.create( fid="point_002", number="100", street_name=self.street, municipality=self.municipality, zipcode="1000", full_address_fr="100 Rue de la Loi Bruxelles", full_address_nl="100 Wetstraat Brussel", lon=4.3680, lat=50.8470, geom=Point(4.3680, 50.8470, srid=3812) ) def test_search_address_points_exact_match(self): """Test searching for an exact address match""" results = search_address_points("16 Rue de la Loi Bruxelles", limit=5) self.assertGreater(len(results), 0) point, score = results[0] self.assertEqual(point.fid, "point_001") self.assertGreater(score, 90) # Should be a very high score def test_search_address_points_french(self): """Test searching in French""" results = search_address_points("16 rue de la loi", limit=5) self.assertGreater(len(results), 0) point, score = results[0] self.assertEqual(point.number, "16") self.assertGreater(score, 60) def test_search_address_points_dutch(self): """Test searching in Dutch""" results = search_address_points("16 wetstraat", limit=5) self.assertGreater(len(results), 0) point, score = results[0] self.assertEqual(point.number, "16") self.assertGreater(score, 60) def test_search_streets_french(self): """Test searching for streets in French""" results = search_streets("Rue de la Loi", limit=5) self.assertGreater(len(results), 0) street, score = results[0] self.assertEqual(street.fid, "street_001") self.assertGreater(score, 90) def test_search_streets_dutch(self): """Test searching for streets in Dutch""" results = search_streets("Wetstraat", limit=5) self.assertGreater(len(results), 0) street, score = results[0] self.assertEqual(street.fid, "street_001") self.assertGreater(score, 90) def test_geocode_internal_success(self): """Test successful internal geocoding""" lon, lat, found_addr, score = geocode_internal("16 Rue de la Loi", threshold=60) self.assertIsNotNone(lon) self.assertIsNotNone(lat) self.assertIn("16", found_addr) self.assertIn("Rue de la Loi", found_addr) self.assertGreater(score, 60) def test_geocode_internal_street_only(self): """Test geocoding with street name only (no house number)""" lon, lat, found_addr, score = geocode_internal("Rue de la Loi Bruxelles", threshold=60) self.assertIsNotNone(lon) self.assertIsNotNone(lat) self.assertIn("Rue de la Loi", found_addr) self.assertGreater(score, 60) def test_geocode_internal_below_threshold(self): """Test that low-scoring matches are rejected""" # Use a very high threshold that won't be met lon, lat, found_addr, score = geocode_internal("16 Rue de la Loi", threshold=99) # Should return None if score is below threshold # (assuming the score is less than 99) if score < 99: self.assertIsNone(lon) self.assertIsNone(lat) def test_geocode_internal_no_match(self): """Test geocoding with no matching address""" lon, lat, found_addr, score = geocode_internal("Nonexistent Street 999", threshold=80) self.assertIsNone(lon) self.assertIsNone(lat) self.assertEqual(score, 0) def test_geocode_internal_empty_address(self): """Test geocoding with empty address""" lon, lat, found_addr, score = geocode_internal("", threshold=80) self.assertIsNone(lon) self.assertIsNone(lat) self.assertEqual(score, 0) def test_geocode_internal_fuzzy_match(self): """Test fuzzy matching with typos""" # Test with a typo: "Rua" instead of "Rue" lon, lat, found_addr, score = geocode_internal("16 Rua de la Loi", threshold=70) # Should still find a match with a reasonable score if score >= 70: self.assertIsNotNone(lon) self.assertIsNotNone(lat) def test_geocode_internal_partial_match(self): """Test partial address matching""" lon, lat, found_addr, score = geocode_internal("loi 16", threshold=70) # Should find a match even with different word order if score >= 70: self.assertIsNotNone(lon) self.assertIsNotNone(lat) class SearchLimitTestCase(TestCase): """Test search limit functionality""" def setUp(self): """Create multiple test addresses""" self.municipality = Municipality.objects.create( fid="1000", name_fr="Bruxelles", name_nl="Brussel", niscode="21004" ) self.street = AdrStreet.objects.create( fid="street_001", name_fr="Rue de la Loi", name_nl="Wetstraat", full_street_fr="Rue de la Loi", full_street_nl="Wetstraat", municipality=self.municipality, lon=4.3676, lat=50.8467, centroid=Point(4.3676, 50.8467, srid=3812) ) # Create 5 address points for i in range(1, 6): number = str(i * 10) AdrPoint.objects.create( fid=f"point_00{i}", number=number, street_name=self.street, municipality=self.municipality, zipcode="1000", full_address_fr=f"{number} Rue de la Loi Bruxelles", full_address_nl=f"{number} Wetstraat Brussel", lon=4.3676 + i * 0.001, lat=50.8467 + i * 0.001, geom=Point(4.3676 + i * 0.001, 50.8467 + i * 0.001, srid=3812) ) def test_search_respects_limit(self): """Test that search limit is respected""" results = search_address_points("Rue de la Loi", limit=3) self.assertLessEqual(len(results), 3) def test_search_returns_best_matches(self): """Test that search returns best matches first""" results = search_address_points("Rue de la Loi 10", limit=5) if len(results) > 1: # Scores should be in descending order scores = [score for _, score in results] self.assertEqual(scores, sorted(scores, reverse=True))