Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 33 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,36 @@ webvoyager7_final.ipynb

rag_store_webpage/

backend_old/
backend_old/

# Testing
.pytest_cache/
.coverage
htmlcov/
coverage.xml
*.py[cod]
*$py.class

# Claude settings
.claude/*

# Virtual environments
*.venv
env/
ENV/
env.bak/
venv.bak/

# IDE
.vscode/
.idea/
*.swp
*.swo
*~

# Build artifacts
dist/
build/
*.egg-info/
.eggs/

983 changes: 205 additions & 778 deletions backend/poetry.lock

Large diffs are not rendered by default.

61 changes: 59 additions & 2 deletions backend/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ packages = [
]

[tool.poetry.dependencies]
python = ">=3.12,<3.13"
python = ">=3.11,<3.13"
langchain = "0.3.14"
langchain-anthropic = "0.3.5"
langchain-core = "^0.3.34"
Expand All @@ -35,7 +35,64 @@ lxml = {extras = ["html-clean"], version = "^5.3.1"}
lxml-html-clean = "^0.4.1"
# Add nltk as alternative to spacy for text splitting
nltk = "^3.8.1"
spacy = "^3.8.4"
# spacy = "^3.8.4" # Temporarily disabled due to blis build issue

[tool.poetry.group.test.dependencies]
pytest = "^8.3.4"
pytest-cov = "^6.0.0"
pytest-mock = "^3.14.0"

# Poetry scripts are defined in a different way
# Use "poetry run pytest" instead

[tool.pytest.ini_options]
minversion = "8.0"
testpaths = ["tests"]
python_files = ["test_*.py", "*_test.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
addopts = [
"-ra",
"--strict-markers",
"--strict-config",
]
markers = [
"unit: Unit tests",
"integration: Integration tests",
"slow: Tests that take a long time to run",
]

[tool.coverage.run]
source = ["tests"]
omit = [
"*/__pycache__/*",
"*/venv/*",
"*/.venv/*",
"*/site-packages/*",
]

[tool.coverage.report]
exclude_lines = [
"pragma: no cover",
"def __repr__",
"if self.debug:",
"if settings.DEBUG",
"raise AssertionError",
"raise NotImplementedError",
"if 0:",
"if __name__ == .__main__.:",
"class .*\\bProtocol\\):",
"@(abc\\.)?abstractmethod",
]
precision = 2
show_missing = true
skip_covered = false

[tool.coverage.html]
directory = "htmlcov"

[tool.coverage.xml]
output = "coverage.xml"

[build-system]
requires = ["poetry-core>=2.0.0,<3.0.0"]
Expand Down
Empty file added backend/tests/__init__.py
Empty file.
159 changes: 159 additions & 0 deletions backend/tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
"""Shared pytest fixtures and configuration for all tests."""

import os
import tempfile
from pathlib import Path
from typing import Generator, Dict, Any
import pytest
from unittest.mock import Mock, MagicMock


@pytest.fixture
def temp_dir() -> Generator[Path, None, None]:
"""Create a temporary directory for test files."""
with tempfile.TemporaryDirectory() as tmpdir:
yield Path(tmpdir)


@pytest.fixture
def mock_env(monkeypatch) -> Dict[str, str]:
"""Mock environment variables for testing."""
test_env = {
"ANTHROPIC_API_KEY": "test-anthropic-key",
"OPENAI_API_KEY": "test-openai-key",
"LANGCHAIN_API_KEY": "test-langchain-key",
"LANGCHAIN_PROJECT": "test-project",
"LANGCHAIN_TRACING_V2": "false",
}

for key, value in test_env.items():
monkeypatch.setenv(key, value)

return test_env


@pytest.fixture
def mock_config() -> Dict[str, Any]:
"""Provide mock configuration for tests."""
return {
"browser": {
"headless": True,
"viewport": {"width": 1280, "height": 720},
"user_agent": "Mozilla/5.0 (Test Browser)",
},
"agents": {
"max_retries": 3,
"timeout": 30,
"temperature": 0.7,
},
"api": {
"rate_limit": 100,
"batch_size": 10,
}
}


@pytest.fixture
def mock_browser():
"""Mock browser instance for testing browser-related functionality."""
browser = MagicMock()
browser.new_page.return_value = MagicMock()
browser.close = MagicMock()
return browser


@pytest.fixture
def mock_page():
"""Mock page instance for testing page interactions."""
page = MagicMock()
page.goto = MagicMock()
page.wait_for_selector = MagicMock()
page.evaluate = MagicMock(return_value={})
page.screenshot = MagicMock(return_value=b"fake_screenshot_data")
page.content = MagicMock(return_value="<html><body>Test content</body></html>")
page.url = "https://example.com"
return page


@pytest.fixture
def mock_llm():
"""Mock LLM instance for testing agent functionality."""
llm = MagicMock()
llm.invoke = MagicMock(return_value="Mock LLM response")
llm.stream = MagicMock(return_value=iter(["Mock", " streaming", " response"]))
return llm


@pytest.fixture
def sample_html_content() -> str:
"""Provide sample HTML content for testing."""
return """
<!DOCTYPE html>
<html>
<head>
<title>Test Page</title>
</head>
<body>
<h1>Test Header</h1>
<div id="content">
<p>This is a test paragraph.</p>
<button id="test-button">Click me</button>
</div>
<form id="test-form">
<input type="text" name="username" id="username" />
<input type="password" name="password" id="password" />
<button type="submit">Submit</button>
</form>
</body>
</html>
"""


@pytest.fixture
def sample_research_data() -> Dict[str, Any]:
"""Provide sample research data for testing."""
return {
"query": "test research query",
"sources": [
{
"url": "https://example.com/article1",
"title": "Example Article 1",
"content": "This is the content of article 1",
"relevance_score": 0.95
},
{
"url": "https://example.com/article2",
"title": "Example Article 2",
"content": "This is the content of article 2",
"relevance_score": 0.87
}
],
"summary": "This is a summary of the research findings",
"metadata": {
"timestamp": "2025-01-01T00:00:00Z",
"duration_seconds": 120,
"sources_count": 2
}
}


@pytest.fixture(autouse=True)
def reset_singleton_instances():
"""Reset any singleton instances between tests."""
yield


@pytest.fixture
def mock_playwright_context():
"""Mock Playwright browser context."""
context = MagicMock()
context.new_page = MagicMock()
context.close = MagicMock()
return context


@pytest.fixture
def capture_logs(caplog):
"""Fixture to capture and assert on log messages."""
with caplog.at_level("DEBUG"):
yield caplog
Empty file.
Loading