Files
learningcircuit--local-deep…/tests/utilities/test_sqlcipher_compat_coverage.py
T
wehub-resource-sync 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
chore: import upstream snapshot with attribution
2026-07-13 13:08:55 +08:00

265 lines
10 KiB
Python
Raw 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.
# allow: no-sut-import — exercises sqlcipher_compat via importlib reload to cover ImportError paths (dynamic import)
"""
Comprehensive tests for local_deep_research.database.sqlcipher_compat module.
Tests the compatibility layer logic, feature detection, and ImportError
behavior when sqlcipher3 is unavailable.
"""
import importlib
import sys
import types
from unittest import mock
import pytest
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
MODULE_PATH = "local_deep_research.database.sqlcipher_compat"
def _fresh_import():
"""Re-import sqlcipher_compat so module-level code re-executes."""
mod = importlib.import_module(MODULE_PATH)
importlib.reload(mod)
return mod
# ---------------------------------------------------------------------------
# 1. Successful import path sqlcipher3 is available
# ---------------------------------------------------------------------------
class TestGetSqlcipherModuleSuccess:
"""When sqlcipher3 can be imported, get_sqlcipher_module should return it."""
def test_returns_sqlcipher3_module(self):
"""get_sqlcipher_module returns the sqlcipher3 module object."""
fake_sqlcipher3 = types.ModuleType("sqlcipher3")
fake_sqlcipher3.dbapi2 = object()
with mock.patch.dict(sys.modules, {"sqlcipher3": fake_sqlcipher3}):
mod = _fresh_import()
result = mod.get_sqlcipher_module()
assert result is fake_sqlcipher3
def test_returned_module_has_dbapi2(self):
"""The returned module should carry a dbapi2 attribute."""
fake_sqlcipher3 = types.ModuleType("sqlcipher3")
sentinel = object()
fake_sqlcipher3.dbapi2 = sentinel
with mock.patch.dict(sys.modules, {"sqlcipher3": fake_sqlcipher3}):
mod = _fresh_import()
result = mod.get_sqlcipher_module()
assert hasattr(result, "dbapi2")
assert result.dbapi2 is sentinel
def test_multiple_calls_return_same_module(self):
"""Repeated calls should consistently return the same module."""
fake_sqlcipher3 = types.ModuleType("sqlcipher3")
fake_sqlcipher3.dbapi2 = object()
with mock.patch.dict(sys.modules, {"sqlcipher3": fake_sqlcipher3}):
mod = _fresh_import()
r1 = mod.get_sqlcipher_module()
r2 = mod.get_sqlcipher_module()
assert r1 is r2
def test_returns_module_with_connect_attribute(self):
"""Validates that a module with a connect() callable works fine."""
fake_sqlcipher3 = types.ModuleType("sqlcipher3")
fake_sqlcipher3.dbapi2 = types.ModuleType("sqlcipher3.dbapi2")
fake_sqlcipher3.connect = lambda db: None
with mock.patch.dict(sys.modules, {"sqlcipher3": fake_sqlcipher3}):
mod = _fresh_import()
result = mod.get_sqlcipher_module()
assert callable(result.connect)
# ---------------------------------------------------------------------------
# 2. ImportError path sqlcipher3 is NOT available
# ---------------------------------------------------------------------------
class TestGetSqlcipherModuleImportError:
"""When sqlcipher3 cannot be imported, get_sqlcipher_module must raise."""
def _block_sqlcipher3(self):
"""Return a context manager that makes `import sqlcipher3` fail."""
original_import = (
__builtins__.__import__
if hasattr(__builtins__, "__import__")
else __import__
)
def _guarded_import(name, *args, **kwargs):
if name == "sqlcipher3":
raise ImportError("No module named 'sqlcipher3'")
return original_import(name, *args, **kwargs)
return mock.patch("builtins.__import__", side_effect=_guarded_import)
def test_raises_import_error(self):
"""An ImportError must be raised when sqlcipher3 is absent."""
with self._block_sqlcipher3():
# Remove sqlcipher3 from sys.modules if cached
with mock.patch.dict(sys.modules, {"sqlcipher3": None}):
mod = _fresh_import()
with pytest.raises(ImportError):
mod.get_sqlcipher_module()
def test_error_message_mentions_sqlcipher3(self):
"""The error message should mention sqlcipher3."""
with self._block_sqlcipher3():
with mock.patch.dict(sys.modules, {"sqlcipher3": None}):
mod = _fresh_import()
with pytest.raises(ImportError, match="sqlcipher3"):
mod.get_sqlcipher_module()
def test_error_message_mentions_pdm_install(self):
"""The error message should mention pdm install."""
with self._block_sqlcipher3():
with mock.patch.dict(sys.modules, {"sqlcipher3": None}):
mod = _fresh_import()
with pytest.raises(ImportError, match="pdm install"):
mod.get_sqlcipher_module()
def test_error_message_mentions_system_library(self):
"""The error message should mention SQLCipher system library."""
with self._block_sqlcipher3():
with mock.patch.dict(sys.modules, {"sqlcipher3": None}):
mod = _fresh_import()
with pytest.raises(
ImportError, match="SQLCipher system library"
):
mod.get_sqlcipher_module()
# ---------------------------------------------------------------------------
# 3. Feature detection module attributes
# ---------------------------------------------------------------------------
class TestModuleAttributes:
"""Verify the public API surface of the compat module."""
def test_get_sqlcipher_module_is_callable(self):
"""get_sqlcipher_module must be a callable."""
mod = importlib.import_module(MODULE_PATH)
assert callable(mod.get_sqlcipher_module)
def test_module_has_docstring(self):
"""The module should have a docstring describing its purpose."""
mod = importlib.import_module(MODULE_PATH)
assert mod.__doc__ is not None
assert "SQLCipher" in mod.__doc__
def test_function_has_docstring(self):
"""get_sqlcipher_module should have a docstring."""
mod = importlib.import_module(MODULE_PATH)
assert mod.get_sqlcipher_module.__doc__ is not None
def test_docstring_documents_return_type(self):
"""The function docstring should mention Returns."""
mod = importlib.import_module(MODULE_PATH)
assert "Returns" in mod.get_sqlcipher_module.__doc__
def test_docstring_documents_raises(self):
"""The function docstring should mention Raises / ImportError."""
mod = importlib.import_module(MODULE_PATH)
assert "ImportError" in mod.get_sqlcipher_module.__doc__
# ---------------------------------------------------------------------------
# 4. Platform compat the module works regardless of which package provides
# the sqlcipher3 namespace (sqlcipher3-binary vs sqlcipher3 source build).
# ---------------------------------------------------------------------------
class TestPlatformCompatibility:
"""Simulates different package backends behind the sqlcipher3 name."""
def test_binary_package(self):
"""Simulate sqlcipher3-binary providing the sqlcipher3 namespace."""
fake = types.ModuleType("sqlcipher3")
fake._binary = True # marker attribute
fake.dbapi2 = types.ModuleType("sqlcipher3.dbapi2")
with mock.patch.dict(sys.modules, {"sqlcipher3": fake}):
mod = _fresh_import()
result = mod.get_sqlcipher_module()
assert result._binary is True
def test_source_package(self):
"""Simulate sqlcipher3 built from source."""
fake = types.ModuleType("sqlcipher3")
fake._source = True
fake.dbapi2 = types.ModuleType("sqlcipher3.dbapi2")
with mock.patch.dict(sys.modules, {"sqlcipher3": fake}):
mod = _fresh_import()
result = mod.get_sqlcipher_module()
assert result._source is True
def test_module_identity_preserved(self):
"""The compat layer should not wrap or proxy the module."""
fake = types.ModuleType("sqlcipher3")
fake.dbapi2 = object()
with mock.patch.dict(sys.modules, {"sqlcipher3": fake}):
mod = _fresh_import()
result = mod.get_sqlcipher_module()
# Must be the exact same object, not a wrapper
assert result is fake
assert type(result) is types.ModuleType
# ---------------------------------------------------------------------------
# 5. Edge cases
# ---------------------------------------------------------------------------
class TestEdgeCases:
"""Assorted edge-case scenarios."""
def test_import_error_is_not_generic_exception(self):
"""The raised exception must be ImportError, not a generic Exception."""
with mock.patch.dict(sys.modules, {"sqlcipher3": None}):
mod = _fresh_import()
with pytest.raises(ImportError) as exc_info:
mod.get_sqlcipher_module()
assert type(exc_info.value) is ImportError
def test_no_side_effects_on_sys_modules(self):
"""Calling get_sqlcipher_module should not add unexpected entries."""
fake = types.ModuleType("sqlcipher3")
fake.dbapi2 = object()
before_keys = set(sys.modules.keys())
with mock.patch.dict(sys.modules, {"sqlcipher3": fake}):
mod = _fresh_import()
mod.get_sqlcipher_module()
after_keys = set(sys.modules.keys())
# Only the compat module itself (from reload) may appear; no others
unexpected = after_keys - before_keys - {MODULE_PATH}
# Filter out __pycache__ or bytecode artifacts
unexpected = {k for k in unexpected if "sqlcipher" in k.lower()}
assert not unexpected, f"Unexpected modules added: {unexpected}"
def test_reloadable(self):
"""The module should be safely reloadable."""
fake = types.ModuleType("sqlcipher3")
fake.dbapi2 = object()
with mock.patch.dict(sys.modules, {"sqlcipher3": fake}):
mod = _fresh_import()
# Reload again
importlib.reload(mod)
result = mod.get_sqlcipher_module()
assert result is fake