Files
wehub-resource-sync 7a0da7932b
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
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-form] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-metrics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-workflow] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-core] (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [history-news] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [library] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [link-analytics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-core] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-lifecycle] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [error-benchmark] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) (push) Has been cancelled
Docker Tests (Consolidated) / Accessibility Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Unit Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Example Tests (push) Has been cancelled
Docker Tests (Consolidated) / Production Image Smoke Test (push) Has been cancelled
Docker Tests (Consolidated) / Infrastructure Tests (push) Has been cancelled
OSSF Scorecard / OSSF Security Scorecard Analysis (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [mobile] (push) Has been cancelled
Backwards Compatibility / Verify Encryption Constants (push) Has been cancelled
Backwards Compatibility / PyPI Version Compatibility (push) Has been cancelled
Backwards Compatibility / Database Migration Tests (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
Docker Tests (Consolidated) / detect-changes (push) Has been cancelled
Docker Tests (Consolidated) / Build Test Image (push) Has been cancelled
Docker Tests (Consolidated) / All Pytest Tests + Coverage (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [accessibility] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [api-crud] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-login] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-register] (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:08:55 +08:00

197 lines
7.0 KiB
Python

"""Tests for metrics database module."""
from unittest.mock import MagicMock, Mock, patch
import pytest
from local_deep_research.metrics.database import MetricsDatabase
class TestMetricsDatabaseInit:
"""Tests for MetricsDatabase initialization."""
def test_initializes_with_no_credentials(self):
"""Should initialize without credentials."""
db = MetricsDatabase()
assert db.username is None
assert db.password is None
def test_initializes_with_username(self):
"""Should store username if provided."""
db = MetricsDatabase(username="testuser")
assert db.username == "testuser"
assert db.password is None
def test_initializes_with_credentials(self):
"""Should store both username and password."""
db = MetricsDatabase(username="testuser", password="testpass")
assert db.username == "testuser"
assert db.password == "testpass"
class TestMetricsDatabaseGetSession:
"""Tests for get_session method."""
def test_yields_none_when_no_username_available(self):
"""Should yield None when no username available."""
db = MetricsDatabase()
with patch("flask.session", {}):
with db.get_session() as session:
assert session is None
def test_uses_provided_username(self):
"""Should use username provided to get_session."""
db = MetricsDatabase()
mock_session = MagicMock()
mock_cm = MagicMock()
mock_cm.__enter__ = Mock(return_value=mock_session)
mock_cm.__exit__ = Mock(return_value=None)
with patch(
"local_deep_research.metrics.database.get_user_db_session",
return_value=mock_cm,
):
with db.get_session(username="testuser") as session:
assert session == mock_session
def test_uses_stored_username_as_fallback(self):
"""Should use stored username if not provided."""
db = MetricsDatabase(username="stored_user")
mock_session = MagicMock()
mock_cm = MagicMock()
mock_cm.__enter__ = Mock(return_value=mock_session)
mock_cm.__exit__ = Mock(return_value=None)
with patch(
"local_deep_research.metrics.database.get_user_db_session",
return_value=mock_cm,
):
with db.get_session() as session:
assert session == mock_session
def test_uses_flask_session_as_fallback(self):
"""Should try Flask session if no username provided."""
db = MetricsDatabase()
mock_session = MagicMock()
mock_cm = MagicMock()
mock_cm.__enter__ = Mock(return_value=mock_session)
mock_cm.__exit__ = Mock(return_value=None)
with patch("flask.session", {"username": "flask_user"}):
with patch(
"local_deep_research.metrics.database.get_user_db_session",
return_value=mock_cm,
):
with db.get_session() as session:
assert session == mock_session
def test_uses_thread_safe_access_with_password(self):
"""Should use thread metrics writer when password provided."""
db = MetricsDatabase(username="testuser", password="testpass")
mock_session = MagicMock()
mock_writer = MagicMock()
mock_cm = MagicMock()
mock_cm.__enter__ = Mock(return_value=mock_session)
mock_cm.__exit__ = Mock(return_value=None)
mock_writer.get_session.return_value = mock_cm
with patch(
"local_deep_research.database.thread_metrics.metrics_writer",
mock_writer,
):
with db.get_session() as session:
mock_writer.set_user_password.assert_called_with(
"testuser", "testpass"
)
assert session == mock_session
def test_overrides_stored_username(self):
"""Should use provided username over stored one."""
db = MetricsDatabase(username="stored")
mock_session = MagicMock()
mock_cm = MagicMock()
mock_cm.__enter__ = Mock(return_value=mock_session)
mock_cm.__exit__ = Mock(return_value=None)
with patch(
"local_deep_research.metrics.database.get_user_db_session",
return_value=mock_cm,
) as mock_get_session:
# Provide override username
with db.get_session(username="override"):
mock_get_session.assert_called_with("override")
def test_handles_flask_import_error(self):
"""Should handle case when Flask is not available."""
db = MetricsDatabase()
# Simulate Flask not being available
with patch.dict("sys.modules", {"flask": None}):
with db.get_session() as session:
# Should yield None without raising
assert session is None
class TestMetricsDatabaseContextManager:
"""Tests for context manager behavior."""
def test_context_manager_returns_session(self):
"""Context manager should yield a session."""
db = MetricsDatabase(username="testuser")
mock_session = MagicMock()
mock_cm = MagicMock()
mock_cm.__enter__ = Mock(return_value=mock_session)
mock_cm.__exit__ = Mock(return_value=None)
with patch(
"local_deep_research.metrics.database.get_user_db_session",
return_value=mock_cm,
):
with db.get_session() as session:
assert session is not None
def test_context_manager_handles_exceptions(self):
"""Context manager should handle exceptions properly."""
db = MetricsDatabase(username="testuser")
mock_cm = MagicMock()
mock_cm.__enter__ = Mock(side_effect=Exception("DB Error"))
mock_cm.__exit__ = Mock(return_value=None)
with patch(
"local_deep_research.metrics.database.get_user_db_session",
return_value=mock_cm,
):
with pytest.raises(Exception, match="DB Error"):
with db.get_session():
pass
class TestFlaskSessionRuntimeError:
"""Tests for Flask session RuntimeError handling (PR #2014).
PR #2014 changed bare `except:` to `except RuntimeError:` when
accessing flask_session outside request context.
"""
def test_runtime_error_caught_outside_flask_context(self):
"""RuntimeError is raised when accessing Flask session outside context."""
with pytest.raises(RuntimeError):
from flask import session as flask_session
flask_session.get("username")
def test_runtime_error_is_specific_exception(self):
"""RuntimeError is caught specifically, not by bare except."""
caught_type = None
try:
from flask import session as flask_session
flask_session.get("username")
except RuntimeError:
caught_type = "RuntimeError"
except Exception:
caught_type = "Exception"
assert caught_type == "RuntimeError"