7a0da7932b
Backwards Compatibility / Verify Encryption Constants (push) Waiting to run
Backwards Compatibility / PyPI Version Compatibility (push) Waiting to run
Backwards Compatibility / Database Migration Tests (push) Waiting to run
CodeQL Advanced / Analyze (javascript-typescript) (push) Waiting to run
CodeQL Advanced / Analyze (python) (push) Waiting to run
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-form] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-metrics] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-workflow] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-core] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-pages] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [history-news] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [library] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [link-analytics] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [mobile] (push) Blocked by required conditions
Docker Tests (Consolidated) / detect-changes (push) Waiting to run
Docker Tests (Consolidated) / Build Test Image (push) Waiting to run
Docker Tests (Consolidated) / All Pytest Tests + Coverage (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [accessibility] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [api-crud] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-login] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-pages] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-register] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-core] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-lifecycle] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [error-benchmark] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) (push) Blocked by required conditions
Docker Tests (Consolidated) / Accessibility Tests (push) Blocked by required conditions
Docker Tests (Consolidated) / LLM Unit Tests (push) Blocked by required conditions
Docker Tests (Consolidated) / LLM Example Tests (push) Blocked by required conditions
Docker Tests (Consolidated) / Production Image Smoke Test (push) Blocked by required conditions
Docker Tests (Consolidated) / Infrastructure Tests (push) Blocked by required conditions
OSSF Scorecard / OSSF Security Scorecard Analysis (push) Waiting to run
OSV-Scanner (Scheduled) / scan-scheduled (push) Failing after 0s
Create Release / test-gate (push) Has been cancelled
Create Release / release-gate (push) Has been cancelled
Create Release / ci-gate (push) Has been cancelled
Create Release / version-check (push) Has been cancelled
Create Release / e2e-test-gate (push) Has been cancelled
Create Release / responsive-test-gate (push) Has been cancelled
Create Release / compat-test-gate (push) Has been cancelled
Create Release / compose-integration-gate (push) Has been cancelled
Create Release / vulture-gate (push) Has been cancelled
Create Release / build (push) Has been cancelled
Create Release / provenance (push) Has been cancelled
Create Release / prerelease-docker (push) Has been cancelled
Create Release / publish-docker (push) Has been cancelled
Create Release / create-release (push) Has been cancelled
Create Release / cleanup-changelog (push) Has been cancelled
Create Release / trigger-pypi (push) Has been cancelled
Create Release / monitor-pypi (push) Has been cancelled
Create Release / Clean up orphan prerelease tags and signatures (push) Has been cancelled
214 lines
6.1 KiB
Python
214 lines
6.1 KiB
Python
# allow: no-sut-import — shared test-helper module (not a test); provides utilities imported by other tests
|
|
"""
|
|
Test utilities and helper functions - incorporating scottvr's patterns.
|
|
|
|
This module provides common utilities for tests.
|
|
"""
|
|
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
from typing import Any, Dict, List, Optional
|
|
from unittest.mock import Mock
|
|
|
|
|
|
def add_src_to_path():
|
|
"""Add the src directory to Python path for imports."""
|
|
src_path = str(Path(__file__).parent.parent / "src")
|
|
if src_path not in sys.path:
|
|
sys.path.insert(0, src_path)
|
|
|
|
|
|
def mock_api_response(
|
|
status_code: int = 200,
|
|
json_data: Optional[Dict] = None,
|
|
text: Optional[str] = None,
|
|
raise_exception: Optional[Exception] = None,
|
|
):
|
|
"""
|
|
Create a mock API response object.
|
|
|
|
Args:
|
|
status_code: HTTP status code
|
|
json_data: JSON response data
|
|
text: Text response data
|
|
raise_exception: Exception to raise when accessed
|
|
|
|
Returns:
|
|
Mock response object
|
|
"""
|
|
mock_response = Mock()
|
|
mock_response.status_code = status_code
|
|
|
|
if raise_exception:
|
|
mock_response.json.side_effect = raise_exception
|
|
mock_response.text = str(raise_exception)
|
|
else:
|
|
if json_data is not None:
|
|
mock_response.json.return_value = json_data
|
|
mock_response.text = json.dumps(json_data)
|
|
elif text is not None:
|
|
mock_response.text = text
|
|
mock_response.json.side_effect = json.JSONDecodeError(
|
|
"Not JSON", text, 0
|
|
)
|
|
else:
|
|
mock_response.json.return_value = {}
|
|
mock_response.text = "{}"
|
|
|
|
return mock_response
|
|
|
|
|
|
def assert_search_result_format(result: Dict[str, Any]):
|
|
"""
|
|
Assert that a search result has the correct format.
|
|
|
|
Args:
|
|
result: Search result dictionary to validate
|
|
"""
|
|
assert isinstance(result, dict), "Result should be a dictionary"
|
|
assert "title" in result, "Result should have a title"
|
|
# Some engines use 'link', others use 'url'
|
|
assert "link" in result or "url" in result, (
|
|
"Result should have a link or url"
|
|
)
|
|
assert "snippet" in result, "Result should have a snippet"
|
|
assert isinstance(result["title"], str), "Title should be a string"
|
|
# Check whichever field is present
|
|
if "link" in result:
|
|
assert isinstance(result["link"], str), "Link should be a string"
|
|
if "url" in result:
|
|
assert isinstance(result["url"], str), "URL should be a string"
|
|
assert isinstance(result["snippet"], str), "Snippet should be a string"
|
|
|
|
|
|
def assert_findings_format(findings: Dict[str, Any]):
|
|
"""
|
|
Assert that findings have the correct format.
|
|
|
|
Args:
|
|
findings: Findings dictionary to validate
|
|
"""
|
|
assert isinstance(findings, dict), "Findings should be a dictionary"
|
|
assert "findings" in findings, "Should have findings list"
|
|
assert "current_knowledge" in findings, "Should have current_knowledge"
|
|
assert "iterations" in findings, "Should have iterations count"
|
|
assert "questions_by_iteration" in findings, (
|
|
"Should have questions_by_iteration"
|
|
)
|
|
|
|
assert isinstance(findings["findings"], list), "Findings should be a list"
|
|
assert isinstance(findings["current_knowledge"], str), (
|
|
"Knowledge should be a string"
|
|
)
|
|
assert isinstance(findings["iterations"], int), (
|
|
"Iterations should be an integer"
|
|
)
|
|
assert isinstance(findings["questions_by_iteration"], dict), (
|
|
"Questions should be a dict"
|
|
)
|
|
|
|
|
|
def create_test_research_context(query: str = "test query") -> Dict[str, Any]:
|
|
"""
|
|
Create a test research context with all required fields.
|
|
|
|
Args:
|
|
query: Research query
|
|
|
|
Returns:
|
|
Complete research context dictionary
|
|
"""
|
|
return {
|
|
"query": query,
|
|
"search_tool": "searxng",
|
|
"llm_provider": "ollama",
|
|
"llm_model": "gemma3:12b",
|
|
"search_iterations": 3,
|
|
"questions_per_iteration": 2,
|
|
"temperature": 0.7,
|
|
"max_tokens": 4096,
|
|
"enable_fact_checking": True,
|
|
"search_strategy": "standard",
|
|
}
|
|
|
|
|
|
def mock_progress_callback():
|
|
"""
|
|
Create a mock progress callback that tracks calls.
|
|
|
|
Returns:
|
|
Tuple of (callback function, calls list)
|
|
"""
|
|
calls = []
|
|
|
|
def callback(message: str, progress: int, metadata: Optional[Dict] = None):
|
|
calls.append(
|
|
{
|
|
"message": message,
|
|
"progress": progress,
|
|
"metadata": metadata or {},
|
|
}
|
|
)
|
|
|
|
return callback, calls
|
|
|
|
|
|
def assert_progress_callback_called(
|
|
calls: List[Dict],
|
|
expected_message: str = None,
|
|
expected_progress: int = None,
|
|
):
|
|
"""
|
|
Assert that a progress callback was called with expected parameters.
|
|
|
|
Args:
|
|
calls: List of callback calls
|
|
expected_message: Expected message (substring match)
|
|
expected_progress: Expected progress value
|
|
"""
|
|
assert len(calls) > 0, "Progress callback should have been called"
|
|
|
|
if expected_message:
|
|
messages = [call["message"] for call in calls]
|
|
assert any(expected_message in msg for msg in messages), (
|
|
f"Expected message '{expected_message}' not found in calls: {messages}"
|
|
)
|
|
|
|
if expected_progress is not None:
|
|
progresses = [call["progress"] for call in calls]
|
|
assert expected_progress in progresses, (
|
|
f"Expected progress {expected_progress} not found in calls: {progresses}"
|
|
)
|
|
|
|
|
|
class MockCache:
|
|
"""Mock cache implementation for testing."""
|
|
|
|
def __init__(self):
|
|
self.cache = {}
|
|
self.hits = 0
|
|
self.misses = 0
|
|
|
|
def get(self, key: str) -> Optional[Any]:
|
|
if key in self.cache:
|
|
self.hits += 1
|
|
return self.cache[key]
|
|
self.misses += 1
|
|
return None
|
|
|
|
def set(self, key: str, value: Any):
|
|
self.cache[key] = value
|
|
|
|
def clear(self):
|
|
self.cache.clear()
|
|
self.hits = 0
|
|
self.misses = 0
|
|
|
|
def get_stats(self) -> Dict[str, int]:
|
|
return {
|
|
"hits": self.hits,
|
|
"misses": self.misses,
|
|
"size": len(self.cache),
|
|
}
|