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
333 lines
12 KiB
Python
333 lines
12 KiB
Python
"""Comprehensive tests for DatabaseWithFileBackupStorage."""
|
|
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
|
|
from local_deep_research.storage.database_with_file_backup import (
|
|
DatabaseWithFileBackupStorage,
|
|
)
|
|
|
|
MODULE = "local_deep_research.storage.database_with_file_backup"
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# __init__
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class TestInit:
|
|
"""Tests for __init__."""
|
|
|
|
@patch(f"{MODULE}.DatabaseReportStorage")
|
|
def test_without_file_storage_file_storage_is_none(
|
|
self, mock_db_cls, mock_session
|
|
):
|
|
"""file_storage should be None when enable_file_storage is False."""
|
|
storage = DatabaseWithFileBackupStorage(mock_session)
|
|
|
|
assert storage.file_storage is None
|
|
assert storage.enable_file_storage is False
|
|
|
|
@patch(f"{MODULE}.FileReportStorage")
|
|
@patch(f"{MODULE}.DatabaseReportStorage")
|
|
def test_with_file_storage_creates_file_report_storage(
|
|
self, mock_db_cls, mock_file_cls, mock_session
|
|
):
|
|
"""file_storage should be a FileReportStorage when enabled."""
|
|
storage = DatabaseWithFileBackupStorage(
|
|
mock_session, enable_file_storage=True
|
|
)
|
|
|
|
assert storage.enable_file_storage is True
|
|
mock_file_cls.assert_called_once_with()
|
|
assert storage.file_storage is mock_file_cls.return_value
|
|
|
|
@patch(f"{MODULE}.DatabaseReportStorage")
|
|
def test_db_storage_created_with_session(self, mock_db_cls, mock_session):
|
|
"""db_storage should be created by passing session to DatabaseReportStorage."""
|
|
storage = DatabaseWithFileBackupStorage(mock_session)
|
|
|
|
mock_db_cls.assert_called_once_with(mock_session)
|
|
assert storage.db_storage is mock_db_cls.return_value
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# save_report
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class TestSaveReport:
|
|
"""Tests for save_report."""
|
|
|
|
def test_db_success_no_file_storage_returns_true(
|
|
self, mock_session, sample_report_content
|
|
):
|
|
"""Should return True and only call db_storage when file storage disabled."""
|
|
storage = DatabaseWithFileBackupStorage(mock_session)
|
|
storage.db_storage = MagicMock()
|
|
storage.db_storage.save_report.return_value = True
|
|
|
|
result = storage.save_report("r1", sample_report_content)
|
|
|
|
assert result is True
|
|
storage.db_storage.save_report.assert_called_once()
|
|
# file_storage is None, nothing else should happen
|
|
|
|
def test_db_fails_returns_false_immediately(
|
|
self, mock_session, sample_report_content
|
|
):
|
|
"""Should return False immediately when db save fails; no file attempt."""
|
|
storage = DatabaseWithFileBackupStorage(
|
|
mock_session, enable_file_storage=True
|
|
)
|
|
storage.db_storage = MagicMock()
|
|
storage.db_storage.save_report.return_value = False
|
|
storage.file_storage = MagicMock()
|
|
|
|
result = storage.save_report("r1", sample_report_content)
|
|
|
|
assert result is False
|
|
storage.file_storage.save_report.assert_not_called()
|
|
|
|
def test_db_success_file_success_returns_true(
|
|
self, mock_session, sample_report_content
|
|
):
|
|
"""Should return True when both db and file saves succeed."""
|
|
storage = DatabaseWithFileBackupStorage(
|
|
mock_session, enable_file_storage=True
|
|
)
|
|
storage.db_storage = MagicMock()
|
|
storage.db_storage.save_report.return_value = True
|
|
storage.file_storage = MagicMock()
|
|
storage.file_storage.save_report.return_value = True
|
|
|
|
result = storage.save_report("r1", sample_report_content)
|
|
|
|
assert result is True
|
|
storage.file_storage.save_report.assert_called_once()
|
|
|
|
def test_db_success_file_returns_false_still_returns_true(
|
|
self, mock_session, sample_report_content
|
|
):
|
|
"""Should return True even when file save returns False (db succeeded)."""
|
|
storage = DatabaseWithFileBackupStorage(
|
|
mock_session, enable_file_storage=True
|
|
)
|
|
storage.db_storage = MagicMock()
|
|
storage.db_storage.save_report.return_value = True
|
|
storage.file_storage = MagicMock()
|
|
storage.file_storage.save_report.return_value = False
|
|
|
|
result = storage.save_report("r1", sample_report_content)
|
|
|
|
assert result is True
|
|
|
|
def test_db_success_file_raises_exception_still_returns_true(
|
|
self, mock_session, sample_report_content
|
|
):
|
|
"""Should return True even when file save raises an exception."""
|
|
storage = DatabaseWithFileBackupStorage(
|
|
mock_session, enable_file_storage=True
|
|
)
|
|
storage.db_storage = MagicMock()
|
|
storage.db_storage.save_report.return_value = True
|
|
storage.file_storage = MagicMock()
|
|
storage.file_storage.save_report.side_effect = OSError("disk full")
|
|
|
|
result = storage.save_report("r1", sample_report_content)
|
|
|
|
assert result is True
|
|
|
|
def test_passes_all_arguments_to_db_storage(
|
|
self, mock_session, sample_report_content, sample_metadata
|
|
):
|
|
"""Should forward research_id, content, metadata, username to db_storage."""
|
|
storage = DatabaseWithFileBackupStorage(mock_session)
|
|
storage.db_storage = MagicMock()
|
|
storage.db_storage.save_report.return_value = True
|
|
|
|
storage.save_report(
|
|
"r1", sample_report_content, sample_metadata, "alice"
|
|
)
|
|
|
|
storage.db_storage.save_report.assert_called_once_with(
|
|
"r1", sample_report_content, sample_metadata, "alice"
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# get_report
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class TestGetReport:
|
|
"""Tests for get_report."""
|
|
|
|
def test_delegates_to_db_storage(self, mock_session, sample_report_content):
|
|
"""Should delegate to db_storage.get_report and return its result."""
|
|
storage = DatabaseWithFileBackupStorage(mock_session)
|
|
storage.db_storage = MagicMock()
|
|
storage.db_storage.get_report.return_value = sample_report_content
|
|
|
|
result = storage.get_report("r1", username="bob")
|
|
|
|
assert result == sample_report_content
|
|
storage.db_storage.get_report.assert_called_once_with("r1", "bob")
|
|
|
|
def test_never_calls_file_storage_even_when_enabled(
|
|
self, mock_session, sample_report_content
|
|
):
|
|
"""Should never read from file storage, even when it is enabled."""
|
|
storage = DatabaseWithFileBackupStorage(
|
|
mock_session, enable_file_storage=True
|
|
)
|
|
storage.db_storage = MagicMock()
|
|
storage.db_storage.get_report.return_value = sample_report_content
|
|
storage.file_storage = MagicMock()
|
|
|
|
storage.get_report("r1")
|
|
|
|
storage.file_storage.get_report.assert_not_called()
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# delete_report
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class TestDeleteReport:
|
|
"""Tests for delete_report."""
|
|
|
|
def test_db_success_no_file_storage_returns_true(self, mock_session):
|
|
"""Should return True when db delete succeeds and no file storage."""
|
|
storage = DatabaseWithFileBackupStorage(mock_session)
|
|
storage.db_storage = MagicMock()
|
|
storage.db_storage.delete_report.return_value = True
|
|
|
|
result = storage.delete_report("r1")
|
|
|
|
assert result is True
|
|
storage.db_storage.delete_report.assert_called_once()
|
|
|
|
def test_db_success_file_storage_calls_file_delete(self, mock_session):
|
|
"""Should call file_storage.delete_report when db delete succeeds."""
|
|
storage = DatabaseWithFileBackupStorage(
|
|
mock_session, enable_file_storage=True
|
|
)
|
|
storage.db_storage = MagicMock()
|
|
storage.db_storage.delete_report.return_value = True
|
|
storage.file_storage = MagicMock()
|
|
|
|
storage.delete_report("r1", username="carol")
|
|
|
|
storage.file_storage.delete_report.assert_called_once_with(
|
|
"r1", "carol"
|
|
)
|
|
|
|
def test_db_fails_returns_false_file_not_called(self, mock_session):
|
|
"""Should return False and skip file delete when db delete fails."""
|
|
storage = DatabaseWithFileBackupStorage(
|
|
mock_session, enable_file_storage=True
|
|
)
|
|
storage.db_storage = MagicMock()
|
|
storage.db_storage.delete_report.return_value = False
|
|
storage.file_storage = MagicMock()
|
|
|
|
result = storage.delete_report("r1")
|
|
|
|
assert result is False
|
|
storage.file_storage.delete_report.assert_not_called()
|
|
|
|
def test_db_success_file_raises_exception_still_returns_true(
|
|
self, mock_session
|
|
):
|
|
"""Should return True even when file delete raises an exception."""
|
|
storage = DatabaseWithFileBackupStorage(
|
|
mock_session, enable_file_storage=True
|
|
)
|
|
storage.db_storage = MagicMock()
|
|
storage.db_storage.delete_report.return_value = True
|
|
storage.file_storage = MagicMock()
|
|
storage.file_storage.delete_report.side_effect = OSError(
|
|
"permission denied"
|
|
)
|
|
|
|
result = storage.delete_report("r1")
|
|
|
|
assert result is True
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# list_reports
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class TestListReports:
|
|
"""Tests for list_reports."""
|
|
|
|
def test_delegates_to_db_storage(self, mock_session):
|
|
"""Should delegate to db_storage.list_reports and return its result."""
|
|
storage = DatabaseWithFileBackupStorage(mock_session)
|
|
storage.db_storage = MagicMock()
|
|
expected = [{"id": "r1"}, {"id": "r2"}]
|
|
storage.db_storage.list_reports.return_value = expected
|
|
|
|
result = storage.list_reports(username="dave")
|
|
|
|
assert result == expected
|
|
storage.db_storage.list_reports.assert_called_once_with("dave")
|
|
|
|
def test_delegates_without_username(self, mock_session):
|
|
"""Should delegate with None username when not provided."""
|
|
storage = DatabaseWithFileBackupStorage(mock_session)
|
|
storage.db_storage = MagicMock()
|
|
storage.db_storage.list_reports.return_value = []
|
|
|
|
result = storage.list_reports()
|
|
|
|
assert result == []
|
|
storage.db_storage.list_reports.assert_called_once_with(None)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# get_report_with_metadata
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class TestGetReportWithMetadata:
|
|
"""Tests for get_report_with_metadata."""
|
|
|
|
def test_delegates_to_db_storage(self, mock_session):
|
|
"""Should delegate to db_storage.get_report_with_metadata."""
|
|
storage = DatabaseWithFileBackupStorage(mock_session)
|
|
storage.db_storage = MagicMock()
|
|
expected = {"content": "report text", "metadata": {"key": "value"}}
|
|
storage.db_storage.get_report_with_metadata.return_value = expected
|
|
|
|
result = storage.get_report_with_metadata("r1", username="eve")
|
|
|
|
assert result == expected
|
|
storage.db_storage.get_report_with_metadata.assert_called_once_with(
|
|
"r1", "eve"
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# report_exists
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class TestReportExists:
|
|
"""Tests for report_exists."""
|
|
|
|
def test_delegates_to_db_storage(self, mock_session):
|
|
"""Should delegate to db_storage.report_exists and return its result."""
|
|
storage = DatabaseWithFileBackupStorage(mock_session)
|
|
storage.db_storage = MagicMock()
|
|
storage.db_storage.report_exists.return_value = True
|
|
|
|
result = storage.report_exists("r1", username="frank")
|
|
|
|
assert result is True
|
|
storage.db_storage.report_exists.assert_called_once_with("r1", "frank")
|