import os import sqlite3 import sys import tempfile import types sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) _TEST_ROOT = tempfile.mkdtemp(prefix="pinvault-test-") def _install_sqlcipher_shim(): """Fall back to plain sqlite3 when pysqlcipher3 is not installed. The SQLCipher-specific `PRAGMA key` statement is swallowed so the app's full endpoint logic can be exercised without the native library. """ try: import pysqlcipher3.dbapi2 # noqa: F401 return except ImportError: pass class _Connection: def __init__(self, raw): self._raw = raw @property def row_factory(self): return self._raw.row_factory @row_factory.setter def row_factory(self, value): self._raw.row_factory = value def execute(self, sql, *args): if sql.strip().upper().startswith("PRAGMA KEY"): return self._raw.execute("SELECT 1", *args) return self._raw.execute(sql, *args) def commit(self): self._raw.commit() def close(self): self._raw.close() def __enter__(self): return self def __exit__(self, *exc): self._raw.close() def connect(path, *args, **kwargs): raw = sqlite3.connect(path, *args, **kwargs) return _Connection(raw) dbapi = types.ModuleType("pysqlcipher3.dbapi2") dbapi.connect = connect pkg = types.ModuleType("pysqlcipher3") pkg.dbapi2 = dbapi sys.modules["pysqlcipher3"] = pkg sys.modules["pysqlcipher3.dbapi2"] = dbapi _install_sqlcipher_shim() os.environ.setdefault("PINVAULT_MASTER_HASH", "$2b$12$" + "a" * 53) os.environ.setdefault("PINVAULT_DB", os.path.join(_TEST_ROOT, "pinvault.db")) os.environ.setdefault("PINVAULT_LOCAL_BACKUP_DIR", os.path.join(_TEST_ROOT, "backups")) os.environ.setdefault("PINVAULT_NAS_BACKUP_DIR", "") os.environ.setdefault("PINVAULT_API_KEY", "test-api-key-0123456789abcdef0123456789abcd") import app as pinvault # noqa: E402 import pytest # noqa: E402 AUTH = {"Authorization": f"Bearer {os.environ['PINVAULT_API_KEY']}"} def create_pin(client, label="test", lock_days=30): resp = client.post("/api/pins", json={"label": label, "lock_days": lock_days}, headers=AUTH) assert resp.status_code == 201, resp.get_json() return resp.get_json() @pytest.fixture() def client(): pinvault._pin_attempt_locks.clear() return pinvault.app.test_client()