#!/usr/bin/env python3 """ Test backwards compatibility for encrypted databases. This test ensures that databases created with previous versions can still be opened with the current version. This prevents breaking changes like salt modifications from going undetected. The key tests are: 1. TestSaltStability - Fast tests that verify encryption constants haven't changed 2. TestBackwardsCompatibility - Slow test that actually installs previous PyPI version The salt stability tests run in CI and catch 99% of breaking changes. The full backwards compatibility test can be run manually with: pytest tests/performance/database/test_backwards_compatibility.py -m slow --run-slow """ import os import subprocess import sys import tempfile from pathlib import Path import pytest # Path to the script that creates a database using the installed package # This is in a separate file for better IDE support (syntax highlighting, linting) CREATE_DB_SCRIPT_PATH = ( Path(__file__).parent / "scripts" / "create_compat_db.py" ) class TestBackwardsCompatibility: """Test that current version can open databases from previous versions.""" @pytest.fixture def temp_dir(self): """Create a temporary directory for test databases.""" with tempfile.TemporaryDirectory() as tmpdir: yield Path(tmpdir) def get_previous_version(self) -> str: """Get the previous version number from PyPI.""" result = subprocess.run( [ sys.executable, "-m", "pip", "index", "versions", "local-deep-research", ], capture_output=True, text=True, ) if result.returncode != 0: # Fallback: try to get from pip show result = subprocess.run( [sys.executable, "-m", "pip", "show", "local-deep-research"], capture_output=True, text=True, ) # Can't determine previous version pytest.skip("Could not determine previous version from PyPI") # Parse versions from output # Output format: "local-deep-research (1.3.21)\nAvailable versions: 1.3.21, 1.3.20, ..." for line in result.stdout.split("\n"): if "Available versions:" in line: versions = line.split(":")[1].strip().split(", ") if len(versions) >= 2: return versions[1] # Second version is the previous one pytest.skip("Could not find previous version") @pytest.mark.slow @pytest.mark.timeout(600) # 10 minutes - installing PyPI package takes time @pytest.mark.skipif( os.environ.get("RUN_SLOW_TESTS") != "true", reason="Slow test - set RUN_SLOW_TESTS=true to run", ) def test_open_database_from_previous_version(self, temp_dir, monkeypatch): """ Test that we can open a database created with the previous PyPI version. This test: 1. Installs the previous version in an isolated venv 2. Creates a database using that version 3. Verifies the current version can open it and read data """ # Get previous version try: previous_version = self.get_previous_version() except Exception as e: pytest.skip(f"Could not get previous version: {e}") # Create isolated venv for previous version venv_dir = temp_dir / "prev_venv" db_dir = temp_dir / "databases" db_dir.mkdir() # Create venv result = subprocess.run( [sys.executable, "-m", "venv", str(venv_dir)], capture_output=True, text=True, ) if result.returncode != 0: pytest.skip(f"Could not create venv: {result.stderr}") # Get venv python if sys.platform == "win32": venv_python = venv_dir / "Scripts" / "python.exe" else: venv_python = venv_dir / "bin" / "python" # Install previous version (with timeout) # Package has many dependencies, needs sufficient time result = subprocess.run( [ str(venv_python), "-m", "pip", "install", f"local-deep-research=={previous_version}", "--quiet", ], capture_output=True, text=True, timeout=600, # 10 minute timeout - package has many dependencies ) if result.returncode != 0: pytest.skip(f"Could not install previous version: {result.stderr}") # Create database with previous version username = "compat_test_user" password = "CompatTestPass123!" # Copy the script to temp dir (script is in separate file for IDE support) script_file = temp_dir / "create_db.py" script_file.write_text(CREATE_DB_SCRIPT_PATH.read_text()) result = subprocess.run( [ str(venv_python), str(script_file), str(db_dir), username, password, ], capture_output=True, text=True, timeout=60, ) if result.returncode != 0: pytest.fail( f"Could not create database with previous version: {result.stderr}" ) # Now test opening with current version monkeypatch.setattr( "local_deep_research.database.encrypted_db.get_data_directory", lambda: db_dir, ) from local_deep_research.database.encrypted_db import ( DatabaseManager, ) from local_deep_research.database.models import UserSettings manager = DatabaseManager() manager.data_dir = db_dir / "encrypted_databases" # Try to open the database engine = manager.open_user_database(username, password) assert engine is not None, ( f"Failed to open database created with version {previous_version}. " "This likely means a breaking change was introduced (e.g., salt change)." ) # Verify we can read the data session = manager.get_session(username) setting = ( session.query(UserSettings) .filter_by(key="test.backwards_compat") .first() ) assert setting is not None, ( "Could not read data from previous version database" ) assert setting.value["version"] == "previous" assert setting.value["test"] is True session.close() manager.close_user_database(username) # Note: Salt stability tests have been moved to test_encryption_constants.py # This file now only contains the slow PyPI backwards compatibility test. if __name__ == "__main__": pytest.main([__file__, "-v"])