Files
learningcircuit--local-deep…/tests/database/test_session_context_deep_coverage.py
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

267 lines
9.3 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Deep coverage tests for database/session_context.py.
Focuses on uncovered branches:
- get_user_db_session: g.db_session reuse path
- get_user_db_session: g.user_password path
- get_user_db_session: session_password_store path
- get_user_db_session: thread context password path
- get_user_db_session: unencrypted DB placeholder
- get_user_db_session: failed session raises DatabaseSessionError
- ensure_db_session: user not in session (unauthenticated)
- ensure_db_session: db not connected + encrypted -> redirect
- ensure_db_session: db not connected + unencrypted -> reopen
- UNENCRYPTED_DB_PLACEHOLDER constant
- DatabaseAccessMixin.execute_with_db
"""
from unittest.mock import MagicMock, patch
import pytest
MODULE = "local_deep_research.database.session_context"
# ---------------------------------------------------------------------------
# Constants
# ---------------------------------------------------------------------------
class TestConstants:
def test_unencrypted_placeholder_is_string(self):
from local_deep_research.database.session_context import (
UNENCRYPTED_DB_PLACEHOLDER,
)
assert isinstance(UNENCRYPTED_DB_PLACEHOLDER, str)
assert len(UNENCRYPTED_DB_PLACEHOLDER) > 0
def test_database_session_error_is_exception(self):
from local_deep_research.database.session_context import (
DatabaseSessionError,
)
assert issubclass(DatabaseSessionError, Exception)
# ---------------------------------------------------------------------------
# get_user_db_session happy paths
# ---------------------------------------------------------------------------
class TestGetUserDbSessionHappyPaths:
def test_uses_g_db_session_when_available(self):
"""When g.db_session exists, it is yielded without opening a new session."""
from local_deep_research.database.session_context import (
get_user_db_session,
)
mock_sess = MagicMock()
mock_g = MagicMock()
mock_g.db_session = mock_sess
with (
patch(f"{MODULE}.has_request_context", return_value=False),
patch(f"{MODULE}.has_app_context", return_value=True),
patch(f"{MODULE}.g", mock_g),
):
with get_user_db_session(username="alice") as sess:
assert sess is mock_sess
def test_unencrypted_db_uses_placeholder_password( # DevSkim: ignore DS101155
self,
):
"""When no password and db is unencrypted, placeholder password is used.""" # DevSkim: ignore DS101155
from local_deep_research.database.session_context import (
get_user_db_session,
)
mock_sess = MagicMock()
mock_g = MagicMock(spec=[]) # No db_session attribute
with (
patch(f"{MODULE}.has_request_context", return_value=False),
patch(f"{MODULE}.has_app_context", return_value=False),
patch(f"{MODULE}.get_search_context", return_value=None),
patch(f"{MODULE}.db_manager") as mock_db,
patch(
"local_deep_research.database.thread_local_session.thread_session_manager.get_session",
return_value=mock_sess,
),
patch(f"{MODULE}.g", mock_g),
):
mock_db.has_encryption = False
with get_user_db_session(username="alice") as sess:
assert sess is mock_sess
def test_password_from_thread_context(self):
"""Password is retrieved from thread search context."""
from local_deep_research.database.session_context import (
get_user_db_session,
)
mock_sess = MagicMock()
mock_g = MagicMock(spec=[])
with (
patch(f"{MODULE}.has_request_context", return_value=False),
patch(f"{MODULE}.has_app_context", return_value=False),
patch(
f"{MODULE}.get_search_context",
return_value={"user_password": "thread_pw"},
),
patch(f"{MODULE}.db_manager") as mock_db,
patch(
"local_deep_research.database.thread_local_session.thread_session_manager.get_session",
return_value=mock_sess,
),
patch(f"{MODULE}.g", mock_g),
):
mock_db.has_encryption = True
with get_user_db_session(username="alice") as sess:
assert sess is mock_sess
# ---------------------------------------------------------------------------
# get_user_db_session error paths
# ---------------------------------------------------------------------------
class TestGetUserDbSessionErrors:
def test_no_username_raises(self):
from local_deep_research.database.session_context import (
get_user_db_session,
DatabaseSessionError,
)
with (
patch(f"{MODULE}.has_request_context", return_value=False),
patch(f"{MODULE}.has_app_context", return_value=False),
):
with pytest.raises(
DatabaseSessionError, match="No authenticated user"
):
with get_user_db_session():
pass
def test_encrypted_db_no_password_raises(self):
from local_deep_research.database.session_context import (
get_user_db_session,
DatabaseSessionError,
)
mock_g = MagicMock(spec=[])
with (
patch(f"{MODULE}.has_request_context", return_value=False),
patch(f"{MODULE}.has_app_context", return_value=False),
patch(f"{MODULE}.get_search_context", return_value=None),
patch(f"{MODULE}.db_manager") as mock_db,
patch(f"{MODULE}.g", mock_g),
):
mock_db.has_encryption = True
with pytest.raises(DatabaseSessionError, match="requires password"):
with get_user_db_session(username="alice"):
pass
def test_failed_session_raises(self):
"""When get_metrics_session returns None, DatabaseSessionError is raised."""
from local_deep_research.database.session_context import (
get_user_db_session,
DatabaseSessionError,
)
mock_g = MagicMock(spec=[])
with (
patch(f"{MODULE}.has_request_context", return_value=False),
patch(f"{MODULE}.has_app_context", return_value=False),
patch(f"{MODULE}.get_search_context", return_value=None),
patch(f"{MODULE}.db_manager") as mock_db,
patch(
"local_deep_research.database.thread_local_session.thread_session_manager.get_session",
return_value=None,
),
patch(f"{MODULE}.g", mock_g),
):
mock_db.has_encryption = False
with pytest.raises(
DatabaseSessionError, match="Could not establish session"
):
with get_user_db_session(username="alice"):
pass
# ---------------------------------------------------------------------------
# with_user_database decorator
# ---------------------------------------------------------------------------
class TestWithUserDatabase:
def test_injects_session_as_first_arg(self):
from local_deep_research.database.session_context import (
with_user_database,
)
mock_sess = MagicMock()
@with_user_database
def my_func(db_session, x, y=0):
return (db_session, x + y)
with patch(f"{MODULE}.get_user_db_session") as mock_ctx:
mock_ctx.return_value.__enter__ = MagicMock(return_value=mock_sess)
mock_ctx.return_value.__exit__ = MagicMock(return_value=False)
result = my_func(10, y=5)
assert result == (mock_sess, 15)
def test_passes_username_and_password_from_kwargs(self):
from local_deep_research.database.session_context import (
with_user_database,
)
@with_user_database
def my_func(db_session):
return db_session
mock_sess = MagicMock()
with patch(f"{MODULE}.get_user_db_session") as mock_ctx:
mock_ctx.return_value.__enter__ = MagicMock(return_value=mock_sess)
mock_ctx.return_value.__exit__ = MagicMock(return_value=False)
result = my_func(_username="alice", _password="pw")
mock_ctx.assert_called_once_with("alice", "pw")
assert result is mock_sess
# ---------------------------------------------------------------------------
# DatabaseAccessMixin
# ---------------------------------------------------------------------------
class TestDatabaseAccessMixin:
def test_get_db_session_raises_deprecation_warning(self):
from local_deep_research.database.session_context import (
DatabaseAccessMixin,
)
class MyService(DatabaseAccessMixin):
pass
svc = MyService()
with pytest.raises(DeprecationWarning):
svc.get_db_session()
def test_get_db_session_message_content(self):
from local_deep_research.database.session_context import (
DatabaseAccessMixin,
)
svc = DatabaseAccessMixin()
try:
svc.get_db_session()
except DeprecationWarning as e:
assert "deprecated" in str(
e
).lower() or "get_user_db_session" in str(e)