from core import ai_fetch class _FakeMessage: def __init__(self, content, annotations=None): self.content = content self.annotations = annotations class _FakeChoice: def __init__(self, message): self.message = message class _FakeResponse: def __init__(self, message): self.choices = [_FakeChoice(message)] def test_fetch_with_ai_returns_result_when_content_sufficient(monkeypatch): class _FakeClient: class chat: class completions: @staticmethod def create(**kwargs): return _FakeResponse(_FakeMessage('x' * 400)) monkeypatch.setattr(ai_fetch, 'OpenAI', lambda api_key, base_url: _FakeClient()) result = ai_fetch.fetch_with_ai('https://intrepidtravel.com/peru') assert result['success'] is True assert result['char_count'] == 400 def test_fetch_with_ai_returns_none_when_content_too_short(monkeypatch): class _FakeClient: class chat: class completions: @staticmethod def create(**kwargs): return _FakeResponse(_FakeMessage('too short')) monkeypatch.setattr(ai_fetch, 'OpenAI', lambda api_key, base_url: _FakeClient()) assert ai_fetch.fetch_with_ai('https://intrepidtravel.com/peru') is None def test_fetch_with_ai_returns_none_on_exception(monkeypatch): class _FakeClient: class chat: class completions: @staticmethod def create(**kwargs): raise Exception('api down') monkeypatch.setattr(ai_fetch, 'OpenAI', lambda api_key, base_url: _FakeClient()) assert ai_fetch.fetch_with_ai('https://intrepidtravel.com/peru') is None def test_fetch_with_ai_appends_citation_content(monkeypatch): citation = type('Citation', (), {'content': 'extra citation text'})() annotation = type('Annotation', (), {'url_citation': citation})() class _FakeClient: class chat: class completions: @staticmethod def create(**kwargs): return _FakeResponse(_FakeMessage('x' * 350, annotations=[annotation])) monkeypatch.setattr(ai_fetch, 'OpenAI', lambda api_key, base_url: _FakeClient()) result = ai_fetch.fetch_with_ai('https://intrepidtravel.com/peru') assert 'extra citation text' in result['text'] def test_fetch_with_ai_prompt_forbids_markdown_and_scopes_to_single_page(monkeypatch): """ Regression test — a real failure showed gpt-4o-mini defaulting to a markdown-formatted, multi-trip summary of the whole domain when the prompt didn't explicitly forbid that. The resulting markdown then confused the downstream extraction model (build_prompt()'s "Return ONLY a valid JSON object, no markdown" instruction) into producing unparseable output. The prompt sent here must explicitly rule both of those out. """ captured = {} class _FakeClient: class chat: class completions: @staticmethod def create(**kwargs): captured['messages'] = kwargs['messages'] return _FakeResponse(_FakeMessage('x' * 400)) monkeypatch.setattr(ai_fetch, 'OpenAI', lambda api_key, base_url: _FakeClient()) ai_fetch.fetch_with_ai('https://intrepidtravel.com/peru') prompt_text = captured['messages'][0]['content'] assert 'no markdown' in prompt_text.lower() assert 'this exact url' in prompt_text.lower() or 'this one tour' in prompt_text.lower()