Files
learningcircuit--local-deep…/tests/database/test_encrypted_database_orm.py
T
wehub-resource-sync 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
chore: import upstream snapshot with attribution
2026-07-13 13:08:55 +08:00

424 lines
13 KiB
Python

#!/usr/bin/env python3
"""Test ORM operations with encrypted user databases."""
import sys
import tempfile
import uuid
from datetime import datetime, timezone
from pathlib import Path
import pytest
sys.path.insert(
0,
str(Path(__file__).parent.parent.parent.resolve()),
)
from local_deep_research.database.encrypted_db import DatabaseManager
from local_deep_research.database.models import (
APIKey,
Report,
ResearchHistory,
ResearchLog,
ResearchResource,
ResearchTask,
SearchQuery,
SearchResult,
UserSettings,
)
class TestEncryptedDatabaseORM:
"""Test ORM operations in encrypted user databases."""
@pytest.fixture
def temp_data_dir(self):
"""Create a temporary directory for test databases."""
with tempfile.TemporaryDirectory() as tmpdir:
yield Path(tmpdir)
@pytest.fixture
def db_manager(self, temp_data_dir, monkeypatch):
"""Create a database manager with temporary directory."""
monkeypatch.setattr(
"local_deep_research.database.encrypted_db.get_data_directory",
lambda: temp_data_dir,
)
manager = DatabaseManager()
yield manager
# Cleanup
for username in list(manager.connections.keys()):
manager.close_user_database(username)
@pytest.fixture
def test_user_session(self, db_manager):
"""Create a test user with encrypted database and return session."""
username = "testuser"
password = "TestPassword123!"
# Create user database
db_manager.create_user_database(username, password)
# Get session
session = db_manager.get_session(username)
yield session
# Cleanup
session.close()
db_manager.close_user_database(username)
def test_user_settings_crud(self, test_user_session):
"""Test CRUD operations on UserSettings."""
session = test_user_session
# Create settings
setting1 = UserSettings(
key="llm.provider",
value={"provider": "openai", "model": "gpt-4"},
category="llm",
)
setting2 = UserSettings(
key="search.engine",
value={"engine": "duckduckgo", "safe_search": True},
category="search",
)
session.add_all([setting1, setting2])
session.commit()
# Read settings
llm_setting = (
session.query(UserSettings).filter_by(key="llm.provider").first()
)
assert llm_setting is not None
assert llm_setting.value["provider"] == "openai"
assert llm_setting.category == "llm"
# Update setting
llm_setting.value = {"provider": "anthropic", "model": "claude-3"}
session.commit()
# Verify update
updated = (
session.query(UserSettings).filter_by(key="llm.provider").first()
)
assert updated.value["provider"] == "anthropic"
# Delete setting
session.delete(setting2)
session.commit()
# Verify deletion
remaining = session.query(UserSettings).count()
assert remaining == 1
def test_api_keys_encryption(self, test_user_session):
"""Test API key storage and retrieval."""
session = test_user_session
# Store API keys
openai_key = APIKey(
provider="openai",
key="sk-test123456789", # This will be encrypted in the database
is_active=True,
)
anthropic_key = APIKey(
provider="anthropic", key="sk-ant-test987654321", is_active=False
)
session.add_all([openai_key, anthropic_key])
session.commit()
# Retrieve and verify
stored_key = session.query(APIKey).filter_by(provider="openai").first()
assert stored_key is not None
assert stored_key.key == "sk-test123456789" # Should decrypt properly
assert stored_key.is_active is True
# Query by active status
active_keys = session.query(APIKey).filter_by(is_active=True).all()
assert len(active_keys) == 1
assert active_keys[0].provider == "openai"
def test_research_history_with_resources(self, test_user_session):
"""Test ResearchHistory with related ResearchResource objects."""
session = test_user_session
# Create research history
research_id = str(uuid.uuid4())
research = ResearchHistory(
id=research_id,
query="quantum computing applications",
mode="detailed",
status="completed",
created_at=datetime.now(timezone.utc).isoformat(),
completed_at=datetime.now(timezone.utc).isoformat(),
progress=100,
research_meta={
"model": "gpt-4",
"temperature": 0.7,
"iterations": 3,
},
)
session.add(research)
session.commit()
# Add resources
resources = [
ResearchResource(
research_id=research_id,
title="Quantum Computing in Drug Discovery",
url="https://example.com/quantum-drug",
content_preview="Recent advances in quantum computing...",
source_type="article",
resource_metadata={"author": "Dr. Smith", "year": 2024},
created_at=datetime.now(timezone.utc).isoformat(),
),
ResearchResource(
research_id=research_id,
title="IBM Quantum Network",
url="https://example.com/ibm-quantum",
content_preview="IBM's quantum computing initiative...",
source_type="web",
resource_metadata={"company": "IBM", "relevance": 0.95},
created_at=datetime.now(timezone.utc).isoformat(),
),
]
session.add_all(resources)
session.commit()
# Query with relationship
stored_research = (
session.query(ResearchHistory).filter_by(id=research_id).first()
)
assert stored_research is not None
assert len(stored_research.resources) == 2
# Query resources directly
quantum_resources = (
session.query(ResearchResource)
.filter(ResearchResource.title.contains("Quantum"))
.all()
)
assert len(quantum_resources) == 2
def test_research_task_workflow(self, test_user_session):
"""Test complete research task workflow with queries and results."""
session = test_user_session
# Create research task
task = ResearchTask(
title="AI Safety Research",
description="Comprehensive research on AI alignment and safety",
status="in_progress",
)
session.add(task)
session.commit()
# Add search queries
query1 = SearchQuery(
research_task_id=task.id,
query="AI alignment problem solutions",
search_engine="google",
search_type="academic",
status="completed",
)
query2 = SearchQuery(
research_task_id=task.id,
query="AI safety research organizations",
search_engine="duckduckgo",
search_type="web",
status="completed",
)
session.add_all([query1, query2])
session.commit()
# Add search results
results = [
SearchResult(
research_task_id=task.id,
search_query_id=query1.id,
title="Concrete Problems in AI Safety",
url="https://example.com/ai-safety-paper",
snippet="A comprehensive survey of AI safety challenges...",
relevance_score=0.98,
position=1,
),
SearchResult(
research_task_id=task.id,
search_query_id=query2.id,
title="Center for AI Safety",
url="https://example.com/cais",
snippet="Leading research organization focused on AI safety...",
relevance_score=0.95,
position=1,
),
]
session.add_all(results)
session.commit()
# Add report
report = Report(
research_task_id=task.id,
title="AI Safety Research Report",
content="# AI Safety Research\n\n## Executive Summary\n...",
format="markdown",
is_draft=False,
word_count=1500,
section_count=5,
)
session.add(report)
session.commit()
# Update task status
task.status = "completed"
task.completed_at = datetime.now(timezone.utc)
session.commit()
# Verify relationships
completed_task = (
session.query(ResearchTask).filter_by(id=task.id).first()
)
assert completed_task.status == "completed"
assert len(completed_task.searches) == 2
assert len(completed_task.results) == 2
assert len(completed_task.reports) == 1
# Query high relevance results
high_relevance = (
session.query(SearchResult)
.filter(SearchResult.relevance_score > 0.9)
.all()
)
assert len(high_relevance) == 2
def test_research_logs(self, test_user_session):
"""Test research logging functionality."""
session = test_user_session
# ResearchLog.research_id is a String(36) FK to
# research_history.id (UUID), not research.id (Integer).
# Production writes UUIDs via log_utils, so the test must too.
research = ResearchHistory(
id=str(uuid.uuid4()),
query="Test research for logging",
mode="detailed",
status="in_progress",
created_at=datetime.now(timezone.utc).isoformat(),
)
session.add(research)
session.commit()
# Add various log entries
logs = [
ResearchLog(
research_id=research.id,
timestamp=datetime.now(timezone.utc),
level="INFO",
message="Starting research process",
module="research_service",
function="start_research",
line_no=100,
),
ResearchLog(
research_id=research.id,
timestamp=datetime.now(timezone.utc),
level="DEBUG",
message="Executing search query",
module="search_engine",
function="search",
line_no=250,
),
ResearchLog(
research_id=research.id,
timestamp=datetime.now(timezone.utc),
level="ERROR",
message="API rate limit exceeded",
module="api_client",
function="make_request",
line_no=75,
),
]
session.add_all(logs)
session.commit()
# Query logs by level
error_logs = (
session.query(ResearchLog)
.filter_by(research_id=research.id, level="ERROR")
.all()
)
assert len(error_logs) == 1
assert "rate limit" in error_logs[0].message
# Query all logs for research
all_logs = (
session.query(ResearchLog)
.filter_by(research_id=research.id)
.order_by(ResearchLog.timestamp)
.all()
)
assert len(all_logs) == 3
def test_database_integrity(self, db_manager):
"""Test database integrity checks."""
username = "integrity_test_user"
password = "IntegrityTest123!"
# Create and open database
db_manager.create_user_database(username, password)
# Check integrity
integrity_ok = db_manager.check_database_integrity(username)
assert integrity_ok is True
# Cleanup
db_manager.close_user_database(username)
def test_concurrent_users(self, db_manager):
"""Test multiple users with separate encrypted databases."""
users = [
("alice", "AlicePass123!"),
("bob", "BobPass456!"),
("charlie", "CharliePass789!"),
]
# Create databases for each user
for username, password in users:
engine = db_manager.create_user_database(username, password)
assert engine is not None
# Add a setting specific to each user
session = db_manager.get_session(username)
setting = UserSettings(
key="user.theme",
value={"theme": f"{username}_theme"},
category="ui",
)
session.add(setting)
session.commit()
session.close()
# Verify each user has their own data
for username, password in users:
session = db_manager.get_session(username)
setting = (
session.query(UserSettings).filter_by(key="user.theme").first()
)
assert setting is not None
assert setting.value["theme"] == f"{username}_theme"
session.close()
# Cleanup
for username, _ in users:
db_manager.close_user_database(username)
if __name__ == "__main__":
pytest.main([__file__, "-v"])