File size: 3,144 Bytes
e6583bf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
import sys
import os
import importlib
import pytest
from dotenv import load_dotenv

# Ensure project root is importable when tests run from anywhere
PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
if PROJECT_ROOT not in sys.path:
    sys.path.insert(0, PROJECT_ROOT)

load_dotenv()  # Load env vars for tests


class _MockResponse:
    def __init__(self, content: str):
        self.content = content


@pytest.fixture(autouse=True)
def no_real_llm_calls(monkeypatch):
    """

    Autouse fixture that prevents real LLM/endpoint calls during tests

    by monkeypatching chain-like objects to return dummy responses.



    This tries to import modules and patches attributes on module objects

    instead of using dotted-name strings (which cause import attempts

    and can raise ModuleNotFoundError).

    """

    # Helper to safely import a module name, return None on failure
    def try_import(name):
        try:
            return importlib.import_module(name)
        except ModuleNotFoundError:
            return None

    # 1) Patch writer_chain in graph_article.writer if present
    writer_mod = try_import("graph_article.writer")
    if writer_mod is not None:
        monkeypatch.setattr(
            writer_mod,
            "writer_chain",
            lambda *a, **kw: _MockResponse("Mocked writer response"),
            raising=False,
        )

    # 2) Patch critic_chain in graph_article.critic if present
    critic_mod = try_import("graph_article.critic")
    if critic_mod is not None:
        monkeypatch.setattr(
            critic_mod,
            "critic_chain",
            lambda *a, **kw: _MockResponse("ACCEPTED"),
            raising=False,
        )

    # 3) Patch summarizer: try both spellings
    summarizer_mod = try_import("graph_web.summarizer") or try_import("graph_web.summariser")
    if summarizer_mod is not None:
        # patch summarize_chain (callable)
        monkeypatch.setattr(
            summarizer_mod,
            "summarize_chain",
            lambda *a, **kw: _MockResponse("Mocked summary"),
            raising=False,
        )

    # 4) Patch ChatHuggingFace class in langchain_huggingface if available
    lch_mod = try_import("langchain_huggingface")
    if lch_mod is not None:
        # Replace the ChatHuggingFace class with a callable factory that returns an object
        # whose __call__ returns a MockResponse. Simpler: patch the class name itself to a
        # lambda that returns our callable.
        class DummyChat:
            def __init__(self, *a, **kw):
                pass

            def __call__(self, *a, **kw):
                return _MockResponse("Mocked generic LLM response")

        monkeypatch.setattr(lch_mod, "ChatHuggingFace", DummyChat, raising=False)

    # 5) As an extra safe fallback, if modules aren't importable, try to monkeypatch the
    # dotted names but don't let import errors propagate (monkeypatch.setattr with strings
    # can still try to import — we avoid that by only patching module objects above).
    yield