Coverage for tests/test_utils.py: 100%

48 statements  

« prev     ^ index     » next       coverage.py v7.16.2, created at 2026-11-13 19:28 +0000

1"""Tests for utils module.""" 

2 

3from copy import deepcopy 

4 

5import pytest 

6 

7from data_api.utils.schema import url_corrector 

8from data_api.utils.search import fuzzy_matches 

9 

10 

11class TestUrlCorrector: 

12 """Tests for url_corrector function.""" 

13 

14 async def test_url_corrector_none_input(self): 

15 """Test that None input returns None.""" 

16 result = url_corrector(None) 

17 assert result is None 

18 

19 async def test_url_corrector_double_slash_prefix(self): 

20 """Test URL starting with //.""" 

21 result = url_corrector("//example.com/path") 

22 assert result == "https://example.com/path" 

23 

24 async def test_url_corrector_valid_http(self): 

25 """Test valid http URL is unchanged.""" 

26 url = "http://example.com/path" 

27 result = url_corrector(url) 

28 assert result == url 

29 

30 async def test_url_corrector_valid_https(self): 

31 """Test valid https URL is unchanged.""" 

32 url = "https://example.com/path" 

33 result = url_corrector(url) 

34 assert result == url 

35 

36 async def test_url_corrector_invalid_protocol(self): 

37 """Test URL with invalid protocol gets corrected.""" 

38 result = url_corrector("ftp://example.com/path") 

39 assert result == "https://example.com/path" 

40 

41 async def test_url_corrector_strips_whitespace(self): 

42 """Test that whitespace is stripped.""" 

43 result = url_corrector(" https://example.com/path ") 

44 assert result == "https://example.com/path" 

45 

46 async def test_url_corrector_no_protocol(self): 

47 """Test URL without protocol is unchanged.""" 

48 url = "example.com/path" 

49 result = url_corrector(url) 

50 assert result == url 

51 

52 

53@pytest.mark.parametrize("threshold", [60, 80]) 

54def test_fuzzy_matches_rank_stably_without_mutating_input(monkeypatch, threshold): 

55 items = [ 

56 {"title": "below"}, 

57 {"title": "threshold"}, 

58 {"title": "best", "id": 1}, 

59 {"title": "best", "id": 2}, 

60 ] 

61 original = deepcopy(items) 

62 scores = {"below": threshold - 1, "threshold": threshold, "best": 100} 

63 monkeypatch.setattr( 

64 "data_api.utils.search.fuzz.partial_ratio", lambda query, value: scores[value] 

65 ) 

66 assert fuzzy_matches(items, "query", "title", threshold) == [items[2], items[3], items[1]] 

67 assert items == original 

68 

69 

70@pytest.mark.parametrize("metadata", [{}, {"title": None}, {"title": ""}]) 

71def test_fuzzy_matches_normalizes_absent_titles(monkeypatch, metadata): 

72 calls = [] 

73 

74 def score(query, value): 

75 assert isinstance(value, str) 

76 calls.append(value) 

77 return 100 if value == query else 0 

78 

79 monkeypatch.setattr("data_api.utils.search.fuzz.partial_ratio", score) 

80 match = {"title": "Notice"} 

81 assert fuzzy_matches([metadata, match], "Notice", "title", 80) == [match] 

82 assert calls == ["", "Notice"]