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
420 lines
13 KiB
Python
420 lines
13 KiB
Python
"""Tests for research-related database models."""
|
|
|
|
import uuid
|
|
from datetime import datetime, timezone
|
|
|
|
import pytest
|
|
from sqlalchemy import create_engine
|
|
from sqlalchemy.orm import sessionmaker
|
|
|
|
from local_deep_research.database.models import (
|
|
Base,
|
|
Research,
|
|
ResearchHistory,
|
|
ResearchMode,
|
|
ResearchResource,
|
|
ResearchStatus,
|
|
ResearchStrategy,
|
|
ResearchTask,
|
|
SearchQuery,
|
|
SearchResult,
|
|
)
|
|
|
|
|
|
class TestResearchModels:
|
|
"""Test suite for research-related models."""
|
|
|
|
@pytest.fixture
|
|
def engine(self):
|
|
"""Create an in-memory SQLite database for testing."""
|
|
engine = create_engine("sqlite:///:memory:")
|
|
Base.metadata.create_all(engine)
|
|
yield engine
|
|
engine.dispose()
|
|
|
|
@pytest.fixture
|
|
def session(self, engine):
|
|
"""Create a database session for testing."""
|
|
Session = sessionmaker(bind=engine)
|
|
session = Session()
|
|
yield session
|
|
session.close()
|
|
|
|
def test_research_history_creation(self, session):
|
|
"""Test creating a ResearchHistory record."""
|
|
research = ResearchHistory(
|
|
id=str(uuid.uuid4()),
|
|
query="What is quantum computing?",
|
|
mode="comprehensive",
|
|
status="completed",
|
|
created_at="2024-01-01T12:00:00",
|
|
completed_at="2024-01-01T12:05:00",
|
|
duration_seconds=300,
|
|
progress=100,
|
|
report_path="/reports/quantum_computing.md",
|
|
title="Quantum Computing Research",
|
|
research_meta={"sources": 10, "quality": "high"},
|
|
)
|
|
|
|
session.add(research)
|
|
session.commit()
|
|
|
|
# Verify the record
|
|
saved = session.query(ResearchHistory).first()
|
|
assert saved is not None
|
|
assert saved.query == "What is quantum computing?"
|
|
assert saved.mode == "comprehensive"
|
|
assert saved.status == "completed"
|
|
assert saved.duration_seconds == 300
|
|
assert saved.progress == 100
|
|
assert saved.research_meta["sources"] == 10
|
|
|
|
def test_research_status_enum(self, session):
|
|
"""Test Research model with ResearchStatus enum."""
|
|
# Create Research with enum status
|
|
research = Research(
|
|
query="Test query",
|
|
status=ResearchStatus.PENDING,
|
|
mode=ResearchMode.QUICK,
|
|
)
|
|
|
|
session.add(research)
|
|
session.commit()
|
|
|
|
# Test status transitions
|
|
research.status = ResearchStatus.IN_PROGRESS
|
|
session.commit()
|
|
|
|
assert research.status == ResearchStatus.IN_PROGRESS
|
|
|
|
# Test all status values
|
|
for status in ResearchStatus:
|
|
r = Research(
|
|
query=f"Test {status.value}",
|
|
status=status,
|
|
mode=ResearchMode.QUICK,
|
|
)
|
|
session.add(r)
|
|
|
|
session.commit()
|
|
|
|
# Verify all statuses
|
|
all_research = session.query(Research).all()
|
|
assert len(all_research) >= len(ResearchStatus)
|
|
|
|
def test_research_progress_log(self, session):
|
|
"""Test progress log JSON field in ResearchHistory."""
|
|
progress_log = {
|
|
"steps": [
|
|
{"time": "2024-01-01T12:00:00", "message": "Starting research"},
|
|
{"time": "2024-01-01T12:01:00", "message": "Searching sources"},
|
|
{"time": "2024-01-01T12:03:00", "message": "Analyzing results"},
|
|
{"time": "2024-01-01T12:05:00", "message": "Generating report"},
|
|
],
|
|
"current_step": 4,
|
|
"total_steps": 4,
|
|
}
|
|
|
|
research = ResearchHistory(
|
|
id=str(uuid.uuid4()),
|
|
query="AI research",
|
|
mode="normal",
|
|
status="completed",
|
|
created_at="2024-01-01T12:00:00",
|
|
progress_log=progress_log,
|
|
progress=100,
|
|
)
|
|
|
|
session.add(research)
|
|
session.commit()
|
|
|
|
# Verify progress log
|
|
saved = session.query(ResearchHistory).first()
|
|
assert saved.progress_log is not None
|
|
assert len(saved.progress_log["steps"]) == 4
|
|
assert saved.progress_log["current_step"] == 4
|
|
|
|
def test_research_task_creation(self, session):
|
|
"""Test ResearchTask model."""
|
|
task = ResearchTask(
|
|
title="Machine Learning Research",
|
|
description="Research current ML trends and applications",
|
|
status="in_progress",
|
|
priority=5,
|
|
tags=["ml", "ai", "trends"],
|
|
research_metadata={
|
|
"estimated_time": "2 hours",
|
|
"complexity": "medium",
|
|
},
|
|
)
|
|
|
|
session.add(task)
|
|
session.commit()
|
|
|
|
# Verify task
|
|
saved = session.query(ResearchTask).first()
|
|
assert saved.title == "Machine Learning Research"
|
|
assert saved.priority == 5
|
|
assert "ml" in saved.tags
|
|
assert saved.research_metadata["complexity"] == "medium"
|
|
|
|
def test_search_query_and_results(self, session):
|
|
"""Test SearchQuery and SearchResult models."""
|
|
# Create research task first
|
|
task = ResearchTask(
|
|
title="Test Research", description="Test", status="pending"
|
|
)
|
|
session.add(task)
|
|
session.commit()
|
|
|
|
# Create search query
|
|
query = SearchQuery(
|
|
research_task_id=task.id,
|
|
query="quantum computing applications",
|
|
search_engine="google",
|
|
search_type="web",
|
|
parameters={"num_results": 10, "time_range": "past_year"},
|
|
status="completed",
|
|
)
|
|
|
|
session.add(query)
|
|
session.commit()
|
|
|
|
# Add search results
|
|
results = [
|
|
SearchResult(
|
|
research_task_id=task.id,
|
|
search_query_id=query.id,
|
|
title="Quantum Computing in 2024",
|
|
url="https://example.com/quantum-2024",
|
|
snippet="Latest developments in quantum computing...",
|
|
relevance_score=0.95,
|
|
content="Full article content here...",
|
|
content_type="article",
|
|
position=1,
|
|
domain="example.com",
|
|
language="en",
|
|
author="Dr. Smith",
|
|
fetch_status="fetched",
|
|
),
|
|
SearchResult(
|
|
research_task_id=task.id,
|
|
search_query_id=query.id,
|
|
title="Practical Quantum Applications",
|
|
url="https://example.com/quantum-apps",
|
|
snippet="Real-world applications of quantum tech...",
|
|
relevance_score=0.87,
|
|
position=2,
|
|
domain="example.com",
|
|
fetch_status="pending",
|
|
),
|
|
]
|
|
|
|
session.add_all(results)
|
|
session.commit()
|
|
|
|
# Verify relationships
|
|
assert len(query.results) == 2
|
|
assert query.results[0].relevance_score == 0.95
|
|
assert task.searches[0].query == "quantum computing applications"
|
|
|
|
def test_research_strategy(self, session):
|
|
"""Test ResearchStrategy model.
|
|
|
|
ResearchStrategy.research_id is a String(36) FK to research_history.id —
|
|
the live UUID-keyed table. The legacy Integer FK to the dormant
|
|
``research`` table caused FOREIGN KEY constraint failed once
|
|
PRAGMA foreign_keys was enabled in v1.6.0; migration 0008 retargets it.
|
|
"""
|
|
history_id = str(uuid.uuid4())
|
|
history = ResearchHistory(
|
|
id=history_id,
|
|
query="Climate change solutions",
|
|
mode="detailed",
|
|
status="completed",
|
|
created_at="2026-01-01T10:00:00",
|
|
)
|
|
session.add(history)
|
|
session.commit()
|
|
|
|
strategy = ResearchStrategy(
|
|
research_id=history_id, strategy_name="comprehensive_search"
|
|
)
|
|
session.add(strategy)
|
|
session.commit()
|
|
|
|
saved = session.query(ResearchStrategy).first()
|
|
assert saved.strategy_name == "comprehensive_search"
|
|
assert saved.research_id == history_id
|
|
|
|
def test_research_relationships(self, session):
|
|
"""Test relationships between research models."""
|
|
# Create research history
|
|
history = ResearchHistory(
|
|
id=str(uuid.uuid4()),
|
|
query="AI Ethics",
|
|
mode="comprehensive",
|
|
status="completed",
|
|
created_at="2024-01-01T10:00:00",
|
|
)
|
|
session.add(history)
|
|
session.commit()
|
|
|
|
# Add resources
|
|
resources = [
|
|
ResearchResource(
|
|
research_id=history.id,
|
|
title="AI Ethics Guidelines",
|
|
url="https://example.com/ai-ethics",
|
|
content_preview="Guidelines for ethical AI development...",
|
|
source_type="article",
|
|
resource_metadata={"credibility": "high"},
|
|
created_at="2024-01-01T10:30:00",
|
|
),
|
|
ResearchResource(
|
|
research_id=history.id,
|
|
title="Ethics in Machine Learning",
|
|
url="https://example.com/ml-ethics",
|
|
content_preview="Exploring ethical considerations in ML...",
|
|
source_type="research_paper",
|
|
resource_metadata={"peer_reviewed": True},
|
|
created_at="2024-01-01T10:45:00",
|
|
),
|
|
]
|
|
|
|
session.add_all(resources)
|
|
session.commit()
|
|
|
|
# Verify relationships
|
|
assert len(history.resources) == 2
|
|
assert history.resources[0].title == "AI Ethics Guidelines"
|
|
|
|
# Test cascade delete
|
|
session.delete(history)
|
|
session.commit()
|
|
|
|
# Resources should be deleted too
|
|
remaining_resources = session.query(ResearchResource).count()
|
|
assert remaining_resources == 0
|
|
|
|
def test_research_metadata_handling(self, session):
|
|
"""Test JSON metadata fields across models."""
|
|
# ResearchHistory with complex metadata
|
|
history = ResearchHistory(
|
|
id=str(uuid.uuid4()),
|
|
query="Complex research",
|
|
mode="normal",
|
|
status="completed",
|
|
created_at="2024-01-01T00:00:00",
|
|
research_meta={
|
|
"sources": {"academic": 5, "news": 10, "blogs": 3},
|
|
"quality_metrics": {
|
|
"relevance": 0.85,
|
|
"credibility": 0.9,
|
|
"recency": 0.95,
|
|
},
|
|
"search_iterations": 3,
|
|
"total_sources_examined": 50,
|
|
},
|
|
)
|
|
|
|
session.add(history)
|
|
session.commit()
|
|
|
|
# Verify complex metadata
|
|
saved = session.query(ResearchHistory).first()
|
|
assert saved.research_meta["sources"]["academic"] == 5
|
|
assert saved.research_meta["quality_metrics"]["relevance"] == 0.85
|
|
|
|
def test_search_result_content(self, session):
|
|
"""Test SearchResult content storage and retrieval."""
|
|
# Create parent objects
|
|
task = ResearchTask(
|
|
title="Content Test",
|
|
description="Testing content storage",
|
|
status="pending",
|
|
)
|
|
session.add(task)
|
|
session.commit()
|
|
|
|
query = SearchQuery(
|
|
research_task_id=task.id,
|
|
query="test query",
|
|
search_engine="google",
|
|
status="completed",
|
|
)
|
|
session.add(query)
|
|
session.commit()
|
|
|
|
# Large content
|
|
large_content = "x" * 10000 # 10KB of content
|
|
|
|
result = SearchResult(
|
|
research_task_id=task.id,
|
|
search_query_id=query.id,
|
|
title="Large Article",
|
|
url="https://example.com/large",
|
|
snippet="Beginning of large article...",
|
|
content=large_content,
|
|
content_type="article",
|
|
relevance_score=0.8,
|
|
position=1,
|
|
domain="example.com",
|
|
language="en",
|
|
fetch_status="fetched",
|
|
fetched_at=datetime.now(timezone.utc),
|
|
)
|
|
|
|
session.add(result)
|
|
session.commit()
|
|
|
|
# Verify content
|
|
saved = session.query(SearchResult).first()
|
|
assert len(saved.content) == 10000
|
|
assert saved.fetch_status == "fetched"
|
|
assert saved.domain == "example.com"
|
|
|
|
def test_research_error_handling(self, session):
|
|
"""Test error tracking in research models."""
|
|
# Failed research
|
|
failed_research = ResearchHistory(
|
|
id=str(uuid.uuid4()),
|
|
query="This will fail",
|
|
mode="quick",
|
|
status="failed",
|
|
created_at="2024-01-01T00:00:00",
|
|
research_meta={
|
|
"error": "NetworkError",
|
|
"error_message": "Connection timeout",
|
|
"retry_count": 3,
|
|
"last_error_timestamp": "2024-01-01T00:05:00",
|
|
},
|
|
)
|
|
|
|
session.add(failed_research)
|
|
session.commit()
|
|
|
|
# Failed search query
|
|
task = ResearchTask(
|
|
title="Error Test", description="Test", status="failed"
|
|
)
|
|
session.add(task)
|
|
session.commit()
|
|
|
|
failed_query = SearchQuery(
|
|
research_task_id=task.id,
|
|
query="problematic query",
|
|
search_engine="bing",
|
|
status="failed",
|
|
error_message="Rate limit exceeded",
|
|
retry_count=5,
|
|
)
|
|
|
|
session.add(failed_query)
|
|
session.commit()
|
|
|
|
# Verify error tracking
|
|
assert failed_research.research_meta["error"] == "NetworkError"
|
|
assert failed_query.error_message == "Rate limit exceeded"
|
|
assert failed_query.retry_count == 5
|