app.bettersight.io/backend/tests/services/test_trip_finder_service.py

137 lines
6.0 KiB
Python

from services import trip_finder_service
def test_scores_destination_and_duration_match_highest():
urls = [
'https://intrepidtravel.com/peru-classic-15-days',
'https://intrepidtravel.com/blog/about-us',
'https://intrepidtravel.com/vietnam-explorer-10-days',
]
result = trip_finder_service.score_and_rank_urls(urls, 'Peru', 15)
assert result['url'] == 'https://intrepidtravel.com/peru-classic-15-days'
assert result['found'] is True
assert result['confidence'] > 0
def test_no_keyword_match_returns_not_found():
urls = ['https://intrepidtravel.com/about-us', 'https://intrepidtravel.com/contact']
result = trip_finder_service.score_and_rank_urls(urls, 'Peru', 15)
assert result == {'url': None, 'confidence': 0, 'found': False}
def test_empty_url_list_returns_not_found():
result = trip_finder_service.score_and_rank_urls([], 'Peru', 15)
assert result == {'url': None, 'confidence': 0, 'found': False}
def test_low_confidence_match_is_not_found():
# Only a weak category-keyword match, no destination/duration signal —
# should fall below CONFIDENCE_FOUND_THRESHOLD (0.3).
urls = ['https://intrepidtravel.com/some-other-trek']
result = trip_finder_service.score_and_rank_urls(urls, 'Peru', 15)
assert result['found'] is False
def test_wrong_destination_with_duration_and_category_match_is_not_found():
# Regression test — a Turkey URL previously scored 3/4 = 0.75 confidence
# against a Peru search purely from duration ('9') + category ('classic')
# matches, despite zero actual destination relevance. This is the exact
# bug that served a Turkey trip for a Peru search via the fast path in
# production. Destination match is now a hard requirement, not an
# additive bonus duration/category can compensate for.
urls = ['https://someexplorer.com/turkey-classic-cultural-tour-9-days']
result = trip_finder_service.score_and_rank_urls(urls, 'Peru', 9)
assert result == {'url': None, 'confidence': 0, 'found': False}
def test_bare_root_domain_url_is_never_a_candidate_even_with_destination_in_domain_name():
# Regression test — a bare root domain could otherwise score high
# enough to be a confident "found" match if the destination keyword
# happens to appear in the domain itself (e.g. a competitor literally
# named "peru-travel.com"), silently presenting a homepage as if it
# were a trip-specific page. Root-domain URLs are excluded before
# scoring, same as the destination-keyword gate.
urls = ['https://www.peru-travel.com/', 'https://www.peru-travel.com']
result = trip_finder_service.score_and_rank_urls(urls, 'Peru', 15)
assert result == {'url': None, 'confidence': 0, 'found': False}
def test_root_domain_gate_does_not_exclude_urls_with_a_real_path():
# Sanity check — the gate only excludes bare root domains, not
# legitimate trip pages that also happen to be on a destination-named
# domain.
urls = ['https://www.peru-travel.com/', 'https://www.peru-travel.com/classic-15-days']
result = trip_finder_service.score_and_rank_urls(urls, 'Peru', 15)
assert result['url'] == 'https://www.peru-travel.com/classic-15-days'
assert result['found'] is True
def test_find_competitor_trip_url_handles_firecrawl_failure_gracefully(monkeypatch):
import requests
def _raise(*args, **kwargs):
raise requests.RequestException('connection refused')
monkeypatch.setattr(trip_finder_service.requests, 'post', _raise)
result = trip_finder_service.find_competitor_trip_url('https://intrepidtravel.com', 'Peru', 15, 'Classic')
assert result == {'url': None, 'confidence': 0, 'found': False}
def test_find_competitor_trip_url_uses_generous_map_timeout(monkeypatch):
# Regression test — a large real production site's sitemap.xml fetch
# can take well over 15s (confirmed directly: ~21.6s for a real site),
# which a too-tight timeout silently turns into a false "no match
# found" instead of a real answer. Guards against re-tightening this.
captured = {}
class _FakeResponse:
def raise_for_status(self):
pass
def json(self):
return {'links': []}
def _fake_post(url, json, timeout):
captured['timeout'] = timeout
return _FakeResponse()
monkeypatch.setattr(trip_finder_service.requests, 'post', _fake_post)
trip_finder_service.find_competitor_trip_url('https://gadventures.com', 'Peru', 15, 'Classic')
assert captured['timeout'] == trip_finder_service.FIRECRAWL_MAP_TIMEOUT_SECONDS
assert captured['timeout'] >= 30
def test_find_own_trip_url_delegates_to_find_competitor_trip_url(monkeypatch):
calls = []
def _fake_find(root_url, destination, duration, travel_style):
calls.append((root_url, destination, duration, travel_style))
return {'url': f'{root_url}/peru-classic-15-days', 'confidence': 0.9, 'found': True}
monkeypatch.setattr(trip_finder_service, 'find_competitor_trip_url', _fake_find)
result = trip_finder_service.find_own_trip_url('https://gadventures.com', 'Peru', 15, 'Classic')
assert calls == [('https://gadventures.com', 'Peru', 15, 'Classic')]
assert result == {'url': 'https://gadventures.com/peru-classic-15-days', 'confidence': 0.9, 'found': True}
def test_find_all_competitor_urls_runs_concurrently_and_preserves_competitor_ids(monkeypatch):
def _fake_find(root_url, destination, duration, travel_style):
return {'url': f'{root_url}/match', 'confidence': 0.8, 'found': True}
monkeypatch.setattr(trip_finder_service, 'find_competitor_trip_url', _fake_find)
competitors = [
{'id': 'c1', 'name': 'Intrepid', 'website': 'https://intrepidtravel.com'},
{'id': 'c2', 'name': 'Exodus', 'website': 'https://exodustravels.com'},
]
results = trip_finder_service.find_all_competitor_urls(competitors, 'Peru', 15, 'Classic')
assert len(results) == 2
ids = {r['competitor_id'] for r in results}
assert ids == {'c1', 'c2'}
for r in results:
assert r['found'] is True