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
289 lines
9.8 KiB
Python
289 lines
9.8 KiB
Python
"""Tests for db_utils module."""
|
|
|
|
import threading
|
|
from unittest.mock import Mock, patch
|
|
|
|
import pytest
|
|
|
|
|
|
class TestGetDbSession:
|
|
"""Tests for get_db_session function."""
|
|
|
|
def test_raises_error_from_background_thread_without_context(self):
|
|
"""Should raise error when called from background thread without app context."""
|
|
from local_deep_research.utilities.db_utils import get_db_session
|
|
|
|
error_raised = [False]
|
|
error_message = [""]
|
|
|
|
def background_task():
|
|
try:
|
|
# Ensure has_app_context returns False in this thread
|
|
with patch(
|
|
"local_deep_research.utilities.db_utils.has_app_context",
|
|
return_value=False,
|
|
):
|
|
get_db_session()
|
|
except RuntimeError as e:
|
|
error_raised[0] = True
|
|
error_message[0] = str(e)
|
|
|
|
# Run in a background thread
|
|
thread = threading.Thread(
|
|
target=background_task, name="TestBackgroundThread"
|
|
)
|
|
thread.start()
|
|
thread.join()
|
|
|
|
assert error_raised[0], (
|
|
"Should raise RuntimeError from background thread"
|
|
)
|
|
assert "background thread" in error_message[0].lower()
|
|
|
|
def test_returns_session_when_username_provided(self):
|
|
"""Should return session when username is explicitly provided."""
|
|
from local_deep_research.utilities.db_utils import get_db_session
|
|
|
|
mock_session = Mock()
|
|
|
|
with patch(
|
|
"local_deep_research.utilities.db_utils.db_manager"
|
|
) as mock_manager:
|
|
with patch(
|
|
"local_deep_research.utilities.db_utils.has_app_context",
|
|
return_value=True,
|
|
):
|
|
mock_manager.get_session.return_value = mock_session
|
|
|
|
result = get_db_session(username="testuser")
|
|
|
|
assert result == mock_session
|
|
mock_manager.get_session.assert_called_with("testuser")
|
|
|
|
def test_raises_error_when_no_db_for_user(self):
|
|
"""Should raise error when no database found for user."""
|
|
from local_deep_research.utilities.db_utils import get_db_session
|
|
|
|
with patch(
|
|
"local_deep_research.utilities.db_utils.db_manager"
|
|
) as mock_manager:
|
|
with patch(
|
|
"local_deep_research.utilities.db_utils.has_app_context",
|
|
return_value=True,
|
|
):
|
|
mock_manager.get_session.return_value = None
|
|
|
|
with pytest.raises(RuntimeError, match="No database found"):
|
|
get_db_session(username="nonexistent")
|
|
|
|
def test_returns_session_from_flask_g(self):
|
|
"""Should return session from Flask g object if present (via lazy helper)."""
|
|
from local_deep_research.utilities.db_utils import get_db_session
|
|
|
|
mock_session = Mock()
|
|
|
|
with patch(
|
|
"local_deep_research.database.session_context.get_g_db_session",
|
|
return_value=mock_session,
|
|
):
|
|
with patch(
|
|
"local_deep_research.utilities.db_utils.has_app_context",
|
|
return_value=True,
|
|
):
|
|
with patch(
|
|
"local_deep_research.utilities.db_utils.flask_session",
|
|
{"username": None},
|
|
):
|
|
result = get_db_session()
|
|
|
|
assert result == mock_session
|
|
|
|
def test_checks_flask_g_for_session(self):
|
|
"""Should check Flask g object for db_session."""
|
|
# This tests that the function checks g.db_session
|
|
from local_deep_research.utilities.db_utils import get_db_session
|
|
|
|
mock_session = Mock()
|
|
mock_g = Mock()
|
|
mock_g.db_session = mock_session
|
|
|
|
with patch(
|
|
"local_deep_research.utilities.db_utils.has_app_context",
|
|
return_value=True,
|
|
):
|
|
with patch("local_deep_research.utilities.db_utils.g", mock_g):
|
|
with patch(
|
|
"local_deep_research.utilities.db_utils.flask_session",
|
|
{"username": None},
|
|
):
|
|
result = get_db_session()
|
|
# Should return a session (the mock)
|
|
assert result is not None
|
|
# Verify g.db_session was accessed
|
|
assert hasattr(mock_g, "db_session")
|
|
|
|
|
|
class TestGetSettingsManager:
|
|
"""Tests for get_settings_manager function."""
|
|
|
|
def test_returns_settings_manager(self):
|
|
"""Should return a SettingsManager instance."""
|
|
from local_deep_research.utilities.db_utils import get_settings_manager
|
|
|
|
mock_manager = Mock()
|
|
|
|
with patch(
|
|
"local_deep_research.utilities.db_utils.has_app_context",
|
|
return_value=False,
|
|
):
|
|
with patch(
|
|
"local_deep_research.utilities.db_utils.get_db_session",
|
|
side_effect=RuntimeError("No session"),
|
|
):
|
|
with patch(
|
|
"local_deep_research.settings.SettingsManager",
|
|
return_value=mock_manager,
|
|
):
|
|
result = get_settings_manager()
|
|
|
|
# Should return a SettingsManager (mocked)
|
|
assert result is not None
|
|
|
|
def test_uses_provided_db_session(self):
|
|
"""Should use provided db_session."""
|
|
from local_deep_research.utilities.db_utils import get_settings_manager
|
|
|
|
mock_session = Mock()
|
|
mock_manager = Mock()
|
|
|
|
with patch(
|
|
"local_deep_research.settings.SettingsManager",
|
|
return_value=mock_manager,
|
|
):
|
|
result = get_settings_manager(db_session=mock_session)
|
|
|
|
# The function should return a SettingsManager initialized with the session
|
|
assert result is not None
|
|
|
|
def test_handles_no_session(self):
|
|
"""Should handle case when no session available."""
|
|
from local_deep_research.utilities.db_utils import get_settings_manager
|
|
|
|
with patch(
|
|
"local_deep_research.utilities.db_utils.has_app_context",
|
|
return_value=False,
|
|
):
|
|
with patch(
|
|
"local_deep_research.utilities.db_utils.get_db_session",
|
|
side_effect=RuntimeError("No session"),
|
|
):
|
|
result = get_settings_manager()
|
|
|
|
# Should still return a SettingsManager (with None session)
|
|
assert result is not None
|
|
|
|
|
|
class TestNoDbSettings:
|
|
"""Tests for no_db_settings decorator."""
|
|
|
|
def test_disables_db_during_function_execution(self):
|
|
"""Should disable DB session during function execution."""
|
|
from local_deep_research.utilities.db_utils import no_db_settings
|
|
|
|
mock_session = Mock()
|
|
mock_manager = Mock()
|
|
mock_manager.db_session = mock_session
|
|
|
|
session_during_call = []
|
|
|
|
@no_db_settings
|
|
def test_func():
|
|
session_during_call.append(mock_manager.db_session)
|
|
return "result"
|
|
|
|
with patch(
|
|
"local_deep_research.utilities.db_utils.get_settings_manager",
|
|
return_value=mock_manager,
|
|
):
|
|
result = test_func()
|
|
|
|
assert result == "result"
|
|
assert session_during_call[0] is None
|
|
# Session should be restored after
|
|
assert mock_manager.db_session == mock_session
|
|
|
|
def test_restores_db_on_exception(self):
|
|
"""Should restore DB session even if function raises."""
|
|
from local_deep_research.utilities.db_utils import no_db_settings
|
|
|
|
mock_session = Mock()
|
|
mock_manager = Mock()
|
|
mock_manager.db_session = mock_session
|
|
|
|
@no_db_settings
|
|
def failing_func():
|
|
raise ValueError("Test error")
|
|
|
|
with patch(
|
|
"local_deep_research.utilities.db_utils.get_settings_manager",
|
|
return_value=mock_manager,
|
|
):
|
|
with pytest.raises(ValueError):
|
|
failing_func()
|
|
|
|
# Session should still be restored
|
|
assert mock_manager.db_session == mock_session
|
|
|
|
def test_preserves_function_metadata(self):
|
|
"""Should preserve function name and docstring."""
|
|
from local_deep_research.utilities.db_utils import no_db_settings
|
|
|
|
@no_db_settings
|
|
def my_function():
|
|
"""My docstring."""
|
|
pass
|
|
|
|
assert my_function.__name__ == "my_function"
|
|
assert my_function.__doc__ == "My docstring."
|
|
|
|
|
|
class TestThreadSafety:
|
|
"""Tests for thread safety mechanisms."""
|
|
|
|
def test_thread_name_included_in_error(self):
|
|
"""Error message should include thread name."""
|
|
from local_deep_research.utilities.db_utils import get_db_session
|
|
|
|
error_message = [""]
|
|
|
|
def background_task():
|
|
try:
|
|
# Ensure has_app_context returns False so the thread check triggers
|
|
with patch(
|
|
"local_deep_research.utilities.db_utils.has_app_context",
|
|
return_value=False,
|
|
):
|
|
get_db_session()
|
|
except RuntimeError as e:
|
|
error_message[0] = str(e)
|
|
|
|
thread = threading.Thread(
|
|
target=background_task, name="CustomThreadName"
|
|
)
|
|
thread.start()
|
|
thread.join()
|
|
|
|
assert "CustomThreadName" in error_message[0]
|
|
|
|
def test_main_thread_not_blocked_without_context(self):
|
|
"""MainThread should not be blocked by thread safety check."""
|
|
# Verify that MainThread can proceed past the thread check
|
|
# even without app context (unlike background threads)
|
|
import threading
|
|
|
|
# This test runs in MainThread
|
|
assert threading.current_thread().name == "MainThread"
|
|
|
|
# The function should not raise RuntimeError for MainThread
|
|
# (it may fail later for other reasons, but not the thread check)
|