Files
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

302 lines
10 KiB
Python

"""Tests for database/session_context.py."""
import pytest
from unittest.mock import Mock, patch
from flask import Flask
@pytest.fixture
def app():
"""Create test Flask application."""
app = Flask(__name__)
app.config["TESTING"] = True
app.secret_key = "test-secret-key"
return app
class TestDatabaseSessionError:
"""Tests for DatabaseSessionError exception."""
def test_exception_can_be_raised(self):
"""Test that DatabaseSessionError can be raised."""
from local_deep_research.database.session_context import (
DatabaseSessionError,
)
with pytest.raises(DatabaseSessionError):
raise DatabaseSessionError("Test error")
def test_exception_message(self):
"""Test that exception preserves message."""
from local_deep_research.database.session_context import (
DatabaseSessionError,
)
try:
raise DatabaseSessionError("Custom error message")
except DatabaseSessionError as e:
assert str(e) == "Custom error message"
class TestGetUserDbSession:
"""Tests for get_user_db_session context manager."""
def test_raises_when_no_username_provided(self, app):
"""Test that error is raised when no username available."""
from local_deep_research.database.session_context import (
get_user_db_session,
DatabaseSessionError,
)
with app.test_request_context():
# No username in session
with pytest.raises(
DatabaseSessionError, match="No authenticated user"
):
with get_user_db_session():
pass
def test_uses_provided_username(self, app):
"""Test that explicitly provided username is used."""
from local_deep_research.database.session_context import (
get_user_db_session,
)
with app.test_request_context():
with patch(
"local_deep_research.database.session_context.db_manager"
) as mock_db:
mock_db.has_encryption = False
with patch(
"local_deep_research.database.thread_local_session.get_metrics_session"
) as mock_get_session:
mock_session = Mock()
mock_get_session.return_value = mock_session
with get_user_db_session(
username="testuser", password="testpass"
) as session:
assert session is mock_session
def test_uses_flask_session_username_when_not_provided(self, app):
"""Test that Flask session username is used when not explicitly provided."""
from local_deep_research.database.session_context import (
get_user_db_session,
UNENCRYPTED_DB_PLACEHOLDER,
)
with app.test_request_context():
from flask import session as flask_session
flask_session["username"] = "flask_user"
with patch(
"local_deep_research.database.session_context.db_manager"
) as mock_db:
mock_db.has_encryption = False
with patch(
"local_deep_research.database.thread_local_session.get_metrics_session"
) as mock_get_session:
mock_session = Mock()
mock_get_session.return_value = mock_session
with get_user_db_session() as _session:
mock_get_session.assert_called_once_with(
"flask_user", UNENCRYPTED_DB_PLACEHOLDER
)
def test_raises_when_encrypted_db_requires_password(self, app):
"""Test error when encrypted DB accessed without password."""
from local_deep_research.database.session_context import (
get_user_db_session,
DatabaseSessionError,
)
with app.test_request_context():
from flask import session as flask_session
flask_session["username"] = "testuser"
with patch(
"local_deep_research.database.session_context.db_manager"
) as mock_db:
mock_db.has_encryption = True
mock_db.connections = {}
with patch(
"local_deep_research.database.session_context.get_search_context"
) as mock_ctx:
mock_ctx.return_value = None
with patch(
"local_deep_research.database.session_passwords.session_password_store"
) as mock_store:
mock_store.get_session_password.return_value = None
with pytest.raises(
DatabaseSessionError, match="requires password"
):
with get_user_db_session():
pass
class TestWithUserDatabase:
"""Tests for with_user_database decorator."""
def test_decorator_injects_db_session(self, app):
"""Test that decorator injects db_session as first argument."""
from local_deep_research.database.session_context import (
with_user_database,
)
@with_user_database
def test_func(db_session, arg1, arg2):
return (db_session, arg1, arg2)
with app.test_request_context():
from flask import session as flask_session
flask_session["username"] = "testuser"
with patch(
"local_deep_research.database.session_context.get_user_db_session"
) as mock_ctx:
mock_session = Mock()
mock_ctx.return_value.__enter__ = Mock(
return_value=mock_session
)
mock_ctx.return_value.__exit__ = Mock(return_value=False)
result = test_func("value1", "value2")
assert result == (mock_session, "value1", "value2")
def test_decorator_passes_kwargs(self, app):
"""Test that decorator passes keyword arguments."""
from local_deep_research.database.session_context import (
with_user_database,
)
@with_user_database
def test_func(db_session, key1=None, key2=None):
return {"session": db_session, "key1": key1, "key2": key2}
with app.test_request_context():
from flask import session as flask_session
flask_session["username"] = "testuser"
with patch(
"local_deep_research.database.session_context.get_user_db_session"
) as mock_ctx:
mock_session = Mock()
mock_ctx.return_value.__enter__ = Mock(
return_value=mock_session
)
mock_ctx.return_value.__exit__ = Mock(return_value=False)
result = test_func(key1="a", key2="b")
assert result["key1"] == "a"
assert result["key2"] == "b"
def test_decorator_extracts_special_kwargs(self, app):
"""Test that _username and _password are extracted from kwargs."""
from local_deep_research.database.session_context import (
with_user_database,
)
@with_user_database
def test_func(db_session):
return db_session
with app.app_context():
with patch(
"local_deep_research.database.session_context.get_user_db_session"
) as mock_ctx:
mock_session = Mock()
mock_ctx.return_value.__enter__ = Mock(
return_value=mock_session
)
mock_ctx.return_value.__exit__ = Mock(return_value=False)
test_func(_username="custom_user", _password="custom_pass")
# Verify get_user_db_session was called with the custom credentials
mock_ctx.assert_called_once_with("custom_user", "custom_pass")
class TestDatabaseAccessMixin:
"""Tests for DatabaseAccessMixin class."""
def test_get_db_session_raises_deprecation_warning(self, app):
"""Test that get_db_session raises DeprecationWarning.
The method was deprecated because it returned a closed session
(context manager exits before returning).
"""
from local_deep_research.database.session_context import (
DatabaseAccessMixin,
)
import pytest
class TestService(DatabaseAccessMixin):
pass
service = TestService()
with pytest.raises(DeprecationWarning) as exc_info:
service.get_db_session()
assert "deprecated" in str(exc_info.value).lower()
assert "get_user_db_session" in str(exc_info.value)
class TestUnencryptedDbPlaceholder:
"""Tests for UNENCRYPTED_DB_PLACEHOLDER constant."""
def test_placeholder_value(self):
"""Test the placeholder constant value."""
from local_deep_research.database.session_context import (
UNENCRYPTED_DB_PLACEHOLDER,
)
assert UNENCRYPTED_DB_PLACEHOLDER == "unencrypted-mode"
assert isinstance(UNENCRYPTED_DB_PLACEHOLDER, str)
class TestSafeRollback:
"""Tests for the safe_rollback helper introduced for issue #3827."""
def test_calls_session_rollback(self):
from local_deep_research.database.session_context import (
safe_rollback,
)
session = Mock()
safe_rollback(session, "ctx")
session.rollback.assert_called_once()
def test_swallows_rollback_exception(self):
"""Rollback errors must not propagate — call sites are exception
handlers themselves and a raise here would mask the original error.
"""
from local_deep_research.database.session_context import (
safe_rollback,
)
session = Mock()
session.rollback.side_effect = RuntimeError("simulated rollback fail")
# Must NOT raise.
safe_rollback(session, "ctx")
session.rollback.assert_called_once()
def test_works_without_context_argument(self):
from local_deep_research.database.session_context import (
safe_rollback,
)
session = Mock()
safe_rollback(session)
session.rollback.assert_called_once()