137 lines
6.0 KiB
Python
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
|