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

203 lines
7.0 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.
"""Coverage tests for encrypted_db.py using regular SQLite (no SQLCipher required).
Covers:
- DatabaseManager initialisation helpers
- _is_valid_encryption_key
- is_user_connected
- get_connected_usernames
- get_memory_usage
- close_user_database / close_all_databases
- check_database_integrity success/failure paths
- get_pool_kwargs
"""
import threading
from contextlib import contextmanager
from unittest.mock import MagicMock, patch
import pytest # noqa: F401
MODULE = "local_deep_research.database.encrypted_db"
# ---------------------------------------------------------------------------
# Helpers create a DatabaseManager with encryption disabled
# ---------------------------------------------------------------------------
@contextmanager
def _unencrypted_manager():
"""Yield a DatabaseManager that has encryption disabled (SQLCipher not available)."""
with (
patch(
f"{MODULE}.get_data_directory",
return_value=MagicMock(
__truediv__=lambda self, other: MagicMock(
mkdir=MagicMock(),
__truediv__=lambda self2, other2: MagicMock(),
)
),
),
patch(f"{MODULE}.get_env_setting", return_value=True),
patch(
f"{MODULE}.get_sqlcipher_module",
side_effect=ImportError("no sqlcipher"),
),
):
from local_deep_research.database.encrypted_db import DatabaseManager
mgr = DatabaseManager.__new__(DatabaseManager)
mgr.connections = {}
mgr._connections_lock = threading.RLock()
# __init__ is bypassed here; mirror its per-user init-lock dict so the
# close_*/open paths that reference mgr._init_locks don't AttributeError.
mgr._init_locks = {}
mgr.has_encryption = False
import os
mgr._use_static_pool = bool(os.environ.get("TESTING"))
from sqlalchemy.pool import StaticPool, QueuePool
mgr._pool_class = StaticPool if mgr._use_static_pool else QueuePool
yield mgr
# ---------------------------------------------------------------------------
# _is_valid_encryption_key
# ---------------------------------------------------------------------------
class TestIsValidEncryptionKey:
def test_none_is_invalid(self):
with _unencrypted_manager() as mgr:
assert mgr._is_valid_encryption_key(None) is False
def test_empty_string_is_invalid(self):
with _unencrypted_manager() as mgr:
assert mgr._is_valid_encryption_key("") is False
def test_whitespace_only_is_invalid(self):
with _unencrypted_manager() as mgr:
assert mgr._is_valid_encryption_key(" ") is False
def test_valid_password(self):
with _unencrypted_manager() as mgr:
assert mgr._is_valid_encryption_key("secret123") is True
def test_single_char_password(self):
with _unencrypted_manager() as mgr:
assert mgr._is_valid_encryption_key("x") is True
# ---------------------------------------------------------------------------
# is_user_connected / get_connected_usernames
# ---------------------------------------------------------------------------
class TestConnectionState:
def test_is_user_connected_false_when_absent(self):
with _unencrypted_manager() as mgr:
assert mgr.is_user_connected("alice") is False
def test_is_user_connected_true_when_present(self):
with _unencrypted_manager() as mgr:
mgr.connections["alice"] = MagicMock()
assert mgr.is_user_connected("alice") is True
def test_get_connected_usernames_empty(self):
with _unencrypted_manager() as mgr:
assert mgr.get_connected_usernames() == set()
def test_get_connected_usernames_snapshot(self):
with _unencrypted_manager() as mgr:
mgr.connections["alice"] = MagicMock()
mgr.connections["bob"] = MagicMock()
names = mgr.get_connected_usernames()
assert names == {"alice", "bob"}
# ---------------------------------------------------------------------------
# get_memory_usage
# ---------------------------------------------------------------------------
class TestGetMemoryUsage:
def test_empty_state(self):
with _unencrypted_manager() as mgr:
stats = mgr.get_memory_usage()
assert stats["active_connections"] == 0
assert "thread_engines" not in stats
assert stats["estimated_memory_mb"] == 0.0
def test_with_connections(self):
with _unencrypted_manager() as mgr:
mgr.connections["user1"] = MagicMock()
mgr.connections["user2"] = MagicMock()
stats = mgr.get_memory_usage()
assert stats["active_connections"] == 2
assert stats["estimated_memory_mb"] == pytest.approx(2 * 3.5)
# ---------------------------------------------------------------------------
# _get_pool_kwargs
# ---------------------------------------------------------------------------
class TestGetPoolKwargs:
def test_static_pool_returns_empty(self):
with _unencrypted_manager() as mgr:
mgr._use_static_pool = True
result = mgr._get_pool_kwargs()
assert result == {}
def test_queue_pool_returns_kwargs(self):
with _unencrypted_manager() as mgr:
mgr._use_static_pool = False
result = mgr._get_pool_kwargs()
assert "pool_size" in result
assert "max_overflow" in result
# ---------------------------------------------------------------------------
# close_user_database
# ---------------------------------------------------------------------------
class TestCloseUserDatabase:
def test_closes_and_removes_connection(self):
with _unencrypted_manager() as mgr:
mock_engine = MagicMock()
mgr.connections["alice"] = mock_engine
mgr.close_user_database("alice")
mock_engine.dispose.assert_called_once()
assert "alice" not in mgr.connections
def test_no_error_when_user_not_connected(self):
with _unencrypted_manager() as mgr:
mgr.close_user_database("nonexistent")
def test_dispose_error_handled_gracefully(self):
with _unencrypted_manager() as mgr:
mock_engine = MagicMock()
mock_engine.dispose.side_effect = RuntimeError("dispose failed")
mgr.connections["alice"] = mock_engine
mgr.close_user_database("alice")
assert "alice" not in mgr.connections
# ---------------------------------------------------------------------------
# close_all_databases
# ---------------------------------------------------------------------------
class TestCloseAllDatabases:
def test_disposes_all_engines(self):
with _unencrypted_manager() as mgr:
e1, e2 = MagicMock(), MagicMock()
mgr.connections["a"] = e1
mgr.connections["b"] = e2
mgr.close_all_databases()
e1.dispose.assert_called_once()
e2.dispose.assert_called_once()
assert mgr.connections == {}