chore: import upstream snapshot with attribution
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
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
This commit is contained in:
@@ -0,0 +1,264 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user