Research-Stack/5-Applications/nodupe/tests/core/test_database.py
2026-05-05 21:15:26 -05:00

997 lines
38 KiB
Python

"""Tests for database modules.
This module contains comprehensive unit tests for the database layer,
including DatabaseConnection, FileRepository, and related functionality.
"""
import pytest
import tempfile
import os
from typing import List, Dict, Any
from unittest.mock import Mock
import sqlite3
from nodupe.tools.databases.connection import DatabaseConnection
from nodupe.tools.databases.files import FileRepository, get_file_repository
from nodupe.tools.databases.repository_interface import DatabaseRepository
from nodupe.tools.databases.schema import DatabaseSchema
from nodupe.tools.databases.query import (DatabaseQuery, DatabaseBatch, DatabasePerformance,
DatabaseIntegrity, DatabaseBackup, DatabaseMigration,
DatabaseRecovery, DatabaseOptimization)
from nodupe.tools.databases.database import Database
def _init_full_schema(db: DatabaseConnection) -> None:
"""Helper to initialize the full 14-column schema for FileRepository tests."""
schema = DatabaseSchema(db.get_connection())
schema.create_schema()
class TestDatabaseConnection:
"""Test DatabaseConnection class functionality."""
def test_singleton_instance_creation(self):
"""Test that DatabaseConnection follows singleton pattern."""
with tempfile.NamedTemporaryFile(suffix='.db') as tmp1, \
tempfile.NamedTemporaryFile(suffix='.db') as tmp2:
db1 = DatabaseConnection.get_instance(tmp1.name)
db2 = DatabaseConnection.get_instance(tmp1.name) # Same path
db3 = DatabaseConnection.get_instance(tmp2.name) # Different path
assert db1 is db2 # Same path should return same instance
assert db1 is not db3 # Different paths should return different instances
assert isinstance(db1, DatabaseConnection)
# Clean up connections
db1.close()
db3.close()
def test_get_connection_creates_directory(self):
"""Test that get_connection creates database directory if it doesn't exist."""
with tempfile.TemporaryDirectory() as temp_dir:
db_path = os.path.join(temp_dir, "subdir", "test.db")
db = DatabaseConnection(db_path)
connection = db.get_connection()
assert isinstance(connection, sqlite3.Connection)
# Check that directory was created
assert os.path.exists(os.path.dirname(db_path))
# Clean up connection
db.close()
def test_execute_query_success(self):
"""Test successful query execution."""
with tempfile.NamedTemporaryFile(suffix='.db') as tmp:
db = DatabaseConnection(tmp.name)
db.get_connection()
# Initialize schema
_init_full_schema(db)
# Execute a simple query
cursor = db.execute(
"INSERT INTO files (path, size, modified_time, "
"created_time, scanned_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)",
("test.txt", 100, 12345, 12345, 12345, 12345)
)
assert cursor is not None
cursor = db.execute("SELECT * FROM files WHERE path = ?", ("test.txt",))
result = cursor.fetchone()
assert result is not None
assert result[1] == "test.txt" # path
assert result[2] == 100 # size
# Clean up connection
db.close()
def test_execute_query_without_params(self):
"""Test query execution without parameters."""
with tempfile.NamedTemporaryFile(suffix='.db') as tmp:
db = DatabaseConnection(tmp.name)
db.get_connection()
_init_full_schema(db)
cursor = db.execute("SELECT COUNT(*) FROM files")
result = cursor.fetchone()
assert result[0] == 0
# Clean up connection
db.close()
def test_executemany_batch_operations(self):
"""Test batch operations with executemany."""
with tempfile.NamedTemporaryFile(suffix='.db') as tmp:
db = DatabaseConnection(tmp.name)
db.get_connection()
_init_full_schema(db)
data = [
("file1.txt", 100, 12345, 12345, 12345, 12345),
("file2.txt", 200, 12346, 12345, 12345, 12345),
("file3.txt", 300, 12347, 12345, 12345, 12345)
]
cursor = db.executemany(
"INSERT INTO files (path, size, modified_time, "
"created_time, scanned_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)",
[tuple(item) for item in data]
)
assert cursor is not None
cursor = db.execute("SELECT COUNT(*) FROM files")
count = cursor.fetchone()[0]
assert count == 3
# Clean up connection
db.close()
def test_commit_and_rollback(self):
"""Test transaction commit and rollback functionality."""
with tempfile.NamedTemporaryFile(suffix='.db') as tmp:
db = DatabaseConnection(tmp.name)
db.get_connection()
_init_full_schema(db)
db.execute(
"INSERT INTO files (path, size, modified_time, "
"created_time, scanned_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)",
("test.txt", 100, 12345, 12345, 12345, 12345)
)
# Verify it's there
cursor = db.execute("SELECT COUNT(*) FROM files")
assert cursor.fetchone()[0] == 1
# Rollback should remove it
db.rollback()
cursor = db.execute("SELECT COUNT(*) FROM files")
assert cursor.fetchone()[0] == 0
# Insert again and commit
db.execute(
"INSERT INTO files (path, size, modified_time, "
"created_time, scanned_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)",
("test.txt", 100, 12345, 12345, 12345, 12345)
)
db.commit()
cursor = db.execute("SELECT COUNT(*) FROM files")
assert cursor.fetchone()[0] == 1
# Clean up connection
db.close()
def test_close_connection(self):
"""Test connection closing functionality."""
with tempfile.NamedTemporaryFile(suffix='.db') as tmp:
db = DatabaseConnection(tmp.name)
_ = db.get_connection() # Use underscore to indicate unused variable
# Close should clear the connection
db.close()
# Getting connection after close should work
_ = db.get_connection() # Use underscore to indicate unused variable
# Close should have cleaned up properly
db.close()
def test_initialize_database_schema(self):
"""Test database schema initialization."""
with tempfile.NamedTemporaryFile(suffix='.db') as tmp:
db = DatabaseConnection(tmp.name)
_init_full_schema(db)
# Test that tables exist
cursor = db.execute("SELECT name FROM sqlite_master WHERE type='table'")
tables = [row[0] for row in cursor.fetchall()]
assert "files" in tables
assert "embeddings" in tables
# Test that indexes exist
cursor = db.execute("SELECT name FROM sqlite_master WHERE type='index'")
indexes = [row[0] for row in cursor.fetchall()]
assert "idx_files_path" in indexes
assert "idx_files_hash" in indexes
assert "idx_files_size" in indexes
assert "idx_files_is_duplicate" in indexes
assert "idx_embeddings_file_id" in indexes
assert "idx_embeddings_model_version" in indexes
def test_get_connection_thread_local(self):
"""Test that connections are thread-local."""
with tempfile.NamedTemporaryFile(suffix='.db') as tmp:
db = DatabaseConnection(tmp.name)
def get_conn():
"""Helper to get database connection."""
return db.get_connection()
# Same thread should get same connection
conn1 = get_conn()
conn2 = get_conn()
assert conn1 is conn2
class TestFileRepository:
"""Test FileRepository class functionality."""
def test_initialization(self):
"""Test FileRepository initialization."""
mock_db = Mock()
repo = FileRepository(mock_db)
assert repo.db is mock_db
def test_add_file_success(self):
"""Test adding a file successfully."""
with tempfile.NamedTemporaryFile(suffix='.db') as tmp:
db = DatabaseConnection(tmp.name)
db.get_connection()
_init_full_schema(db)
repo = FileRepository(db)
file_id = repo.add_file("test.txt", 100, 12345, "abc123")
assert file_id is not None
# Verify file was added
cursor = db.execute("SELECT * FROM files WHERE id = ?", (file_id,))
result = cursor.fetchone()
assert result[1] == "test.txt" # path
assert result[2] == 100 # size
assert result[3] == 12345 # modified_time
assert result[8] == "abc123" # hash (full schema index)
# Clean up connection
db.close()
def test_get_file_by_id(self):
"""Test getting a file by ID."""
with tempfile.NamedTemporaryFile(suffix='.db') as tmp:
db = DatabaseConnection(tmp.name)
db.get_connection()
_init_full_schema(db)
repo = FileRepository(db)
# Add a file first
file_id = repo.add_file("test.txt", 100, 12345, "abc123")
assert file_id is not None # Ensure file was added successfully
# Get the file
file_data = repo.get_file(file_id)
assert file_data is not None
assert file_data['path'] == "test.txt"
assert file_data['size'] == 100
assert file_data['hash'] == "abc123"
assert file_data['is_duplicate'] is False
# Clean up connection
db.close()
def test_get_file_by_id_not_found(self):
"""Test getting a non-existent file."""
with tempfile.NamedTemporaryFile(suffix='.db') as tmp:
db = DatabaseConnection(tmp.name)
db.get_connection()
_init_full_schema(db)
repo = FileRepository(db)
file_data = repo.get_file(999)
assert file_data is None
# Close database connection to prevent resource warnings
db.close()
def test_get_file_by_path(self):
"""Test getting a file by path."""
with tempfile.NamedTemporaryFile(suffix='.db') as tmp:
db = DatabaseConnection(tmp.name)
_ = db.get_connection() # Use underscore to indicate unused variable
_init_full_schema(db)
repo = FileRepository(db)
# Add a file first
file_id = repo.add_file("test.txt", 100, 12345, "abc123")
assert file_id is not None # Ensure file was added successfully
# Get the file by path
file_data = repo.get_file_by_path("test.txt")
assert file_data is not None
assert file_data['id'] == file_id
assert file_data['size'] == 100
def test_update_file(self):
"""Test updating file data."""
with tempfile.NamedTemporaryFile(suffix='.db') as tmp:
db = DatabaseConnection(tmp.name)
_ = db.get_connection() # Use underscore to indicate unused variable
_init_full_schema(db)
repo = FileRepository(db)
# Add a file first
file_id = repo.add_file("test.txt", 100, 12345, "abc123")
assert file_id is not None # Ensure file was added successfully
# Update the file
success = repo.update_file(file_id, size=200, hash="def456")
assert success is True
# Verify update
file_data = repo.get_file(file_id)
assert file_data is not None
assert file_data['size'] == 200
assert file_data['hash'] == "def456"
def test_update_file_invalid_fields(self):
"""Test updating with invalid fields."""
with tempfile.NamedTemporaryFile(suffix='.db') as tmp:
db = DatabaseConnection(tmp.name)
_ = db.get_connection() # Use underscore to indicate unused variable
_init_full_schema(db)
repo = FileRepository(db)
file_id = repo.add_file("test.txt", 100, 12345, "abc123")
assert file_id is not None # Ensure file was added successfully
# Update with no valid fields should return False
success = repo.update_file(file_id, invalid_field="test")
assert success is False
def test_mark_as_duplicate(self):
"""Test marking a file as duplicate."""
with tempfile.NamedTemporaryFile(suffix='.db') as tmp:
db = DatabaseConnection(tmp.name)
_ = db.get_connection() # Use underscore to indicate unused variable
_init_full_schema(db)
repo = FileRepository(db)
# Add two files
original_id = repo.add_file("original.txt", 100, 12345, "abc123")
duplicate_id = repo.add_file("duplicate.txt", 10, 12346, "abc123")
assert original_id is not None # Ensure files were added successfully
assert duplicate_id is not None
# Mark one as duplicate of the other
success = repo.mark_as_duplicate(duplicate_id, original_id)
assert success is True
# Verify the duplicate was marked
dup_data = repo.get_file(duplicate_id)
assert dup_data is not None
assert dup_data['is_duplicate'] is True
assert dup_data['duplicate_of'] == original_id
def test_find_duplicates_by_hash(self):
"""Test finding files with same hash."""
with tempfile.NamedTemporaryFile(suffix='.db') as tmp:
db = DatabaseConnection(tmp.name)
_ = db.get_connection() # Use underscore to indicate unused variable
_init_full_schema(db)
repo = FileRepository(db)
# Add files with same hash
file1_id = repo.add_file("file1.txt", 100, 12345, "samehash")
file2_id = repo.add_file("file2.txt", 100, 12346, "samehash")
file3_id = repo.add_file("file3.txt", 200, 12347, "differenthash")
assert file1_id is not None # Ensure files were added successfully
assert file2_id is not None
assert file3_id is not None
duplicates = repo.find_duplicates_by_hash("samehash")
assert len(duplicates) == 2
assert all(f['hash'] == "samehash" for f in duplicates)
# Close database connection to prevent resource warnings
db.close()
def test_find_duplicates_by_size(self):
"""Test finding files with same size."""
with tempfile.NamedTemporaryFile(suffix='.db') as tmp:
db = DatabaseConnection(tmp.name)
_ = db.get_connection() # Use underscore to indicate unused variable
_init_full_schema(db)
repo = FileRepository(db)
# Add files with same size
file1_id = repo.add_file("file1.txt", 100, 12345, "hash1")
file2_id = repo.add_file("file2.txt", 100, 12346, "hash2")
file3_id = repo.add_file("file3.txt", 200, 12347, "hash3")
assert file1_id is not None # Ensure files were added successfully
assert file2_id is not None
assert file3_id is not None
same_size_files = repo.find_duplicates_by_size(100)
assert len(same_size_files) == 2
assert all(f['size'] == 100 for f in same_size_files)
def test_get_all_files(self):
"""Test getting all files."""
with tempfile.NamedTemporaryFile(suffix='.db') as tmp:
db = DatabaseConnection(tmp.name)
_ = db.get_connection() # Use underscore to indicate unused variable
_init_full_schema(db)
repo = FileRepository(db)
# Add some files
file1_id = repo.add_file("file1.txt", 100, 12345, "hash1")
file2_id = repo.add_file("file2.txt", 200, 12346, "hash2")
assert file1_id is not None # Ensure files were added successfully
assert file2_id is not None
all_files = repo.get_all_files()
assert len(all_files) == 2
assert {f['path'] for f in all_files} == {"file1.txt", "file2.txt"}
def test_delete_file(self):
"""Test deleting a file."""
with tempfile.NamedTemporaryFile(suffix='.db') as tmp:
db = DatabaseConnection(tmp.name)
_ = db.get_connection() # Use underscore to indicate unused variable
_init_full_schema(db)
repo = FileRepository(db)
# Add a file
file_id = repo.add_file("test.txt", 100, 12345, "abc123")
assert file_id is not None # Ensure file was added successfully
# Delete the file
success = repo.delete_file(file_id)
assert success is True
# Verify it's gone
file_data = repo.get_file(file_id)
assert file_data is None
def test_get_duplicate_files(self):
"""Test getting duplicate files."""
with tempfile.NamedTemporaryFile(suffix='.db') as tmp:
db = DatabaseConnection(tmp.name)
_ = db.get_connection() # Use underscore to indicate unused variable
_init_full_schema(db)
repo = FileRepository(db)
# Add files and mark some as duplicates
original_id = repo.add_file("original.txt", 10, 12345, "hash1")
dup1_id = repo.add_file("dup1.txt", 100, 12346, "hash1")
dup2_id = repo.add_file("dup2.txt", 100, 12347, "hash1")
normal_id = repo.add_file("normal.txt", 200, 12348, "hash2")
assert original_id is not None # Ensure files were added successfully
assert dup1_id is not None
assert dup2_id is not None
assert normal_id is not None
repo.mark_as_duplicate(dup1_id, original_id)
repo.mark_as_duplicate(dup2_id, original_id)
duplicates = repo.get_duplicate_files()
assert len(duplicates) == 2
assert all(f['is_duplicate'] is True for f in duplicates)
def test_get_original_files(self):
"""Test getting original (non-duplicate) files."""
with tempfile.NamedTemporaryFile(suffix='.db') as tmp:
db = DatabaseConnection(tmp.name)
_ = db.get_connection() # Use underscore to indicate unused variable
_init_full_schema(db)
repo = FileRepository(db)
# Add files and mark some as duplicates
original_id = repo.add_file("original.txt", 10, 12345, "hash1")
dup_id = repo.add_file("dup.txt", 10, 12346, "hash1")
assert original_id is not None # Ensure files were added successfully
assert dup_id is not None
repo.mark_as_duplicate(dup_id, original_id)
originals = repo.get_original_files()
assert len(originals) == 1
assert originals[0]['id'] == original_id
assert originals[0]['is_duplicate'] is False
def test_count_files(self):
"""Test counting total files."""
with tempfile.NamedTemporaryFile(suffix='.db') as tmp:
db = DatabaseConnection(tmp.name)
_ = db.get_connection() # Use underscore to indicate unused variable
_init_full_schema(db)
repo = FileRepository(db)
assert repo.count_files() == 0
file1_id = repo.add_file("file1.txt", 100, 12345, "hash1")
file2_id = repo.add_file("file2.txt", 200, 12346, "hash2")
assert file1_id is not None # Ensure files were added successfully
assert file2_id is not None
assert repo.count_files() == 2
def test_count_duplicates(self):
"""Test counting duplicate files."""
with tempfile.NamedTemporaryFile(suffix='.db') as tmp:
db = DatabaseConnection(tmp.name)
_ = db.get_connection() # Use underscore to indicate unused variable
_init_full_schema(db)
repo = FileRepository(db)
# Add files and mark one as duplicate
original_id = repo.add_file("original.txt", 10, 12345, "hash1")
dup_id = repo.add_file("dup.txt", 10, 12346, "hash1")
assert original_id is not None # Ensure files were added successfully
assert dup_id is not None
assert repo.count_duplicates() == 0
repo.mark_as_duplicate(dup_id, original_id)
assert repo.count_duplicates() == 1
def test_batch_add_files(self):
"""Test batch adding multiple files."""
with tempfile.NamedTemporaryFile(suffix='.db') as tmp:
db = DatabaseConnection(tmp.name)
_ = db.get_connection() # Use underscore to indicate unused variable
_init_full_schema(db)
repo = FileRepository(db)
files_data: List[Dict[str, Any]] = [
{"path": "file1.txt", "size": 100, "modified_time": 12345},
{"path": "file2.txt", "size": 200, "modified_time": 12346},
{"path": "file3.txt", "size": 300, "modified_time": 12347}
]
count = repo.batch_add_files(files_data)
assert count == 3
all_files = repo.get_all_files()
assert len(all_files) == 3
assert {f['path'] for f in all_files} == {"file1.txt", "file2.txt", "file3.txt"}
def test_clear_all_files(self):
"""Test clearing all files."""
with tempfile.NamedTemporaryFile(suffix='.db') as tmp:
db = DatabaseConnection(tmp.name)
_ = db.get_connection() # Use underscore to indicate unused variable
_init_full_schema(db)
repo = FileRepository(db)
# Add some files
file1_id = repo.add_file("file1.txt", 100, 12345, "hash1")
file2_id = repo.add_file("file2.txt", 200, 12346, "hash2")
assert file1_id is not None # Ensure files were added successfully
assert file2_id is not None
assert repo.count_files() == 2
# Clear all files
repo.clear_all_files()
assert repo.count_files() == 0
def test_get_file_repository_factory(self):
"""Test the factory function for getting file repository."""
with tempfile.NamedTemporaryFile(delete=False) as tmp:
repo = get_file_repository(tmp.name)
assert isinstance(repo, FileRepository)
assert isinstance(repo.db, DatabaseConnection)
os.unlink(tmp.name)
class TestDatabaseRepository:
"""Test DatabaseRepository class functionality."""
def test_initialization(self):
"""Test DatabaseRepository initialization."""
mock_connection = Mock()
repo = DatabaseRepository(mock_connection)
assert repo.connection is mock_connection
def test_create_method_raises_not_implemented(self):
"""Test that create method raises NotImplementedError."""
repo = DatabaseRepository(Mock())
with pytest.raises(NotImplementedError):
repo.create("table", {"data": "value"})
def test_read_method_raises_not_implemented(self):
"""Test that read method raises NotImplementedError."""
repo = DatabaseRepository(Mock())
with pytest.raises(NotImplementedError):
repo.read("table", 1)
def test_update_method_raises_not_implemented(self):
"""Test that update method raises NotImplementedError."""
repo = DatabaseRepository(Mock())
with pytest.raises(NotImplementedError):
repo.update("table", 1, {"data": "value"})
def test_delete_method_raises_not_implemented(self):
"""Test that delete method raises NotImplementedError."""
repo = DatabaseRepository(Mock())
with pytest.raises(NotImplementedError):
repo.delete("table", 1)
def test_database_integration():
"""Test full database integration with all components working together."""
with tempfile.NamedTemporaryFile(delete=False) as tmp:
try:
# Test with real database file
db = DatabaseConnection(tmp.name)
repo = FileRepository(db)
# Initialize database
_init_full_schema(db)
# Add some test files
file1_id = repo.add_file("/path/to/file1.txt", 1024, 1234567890, "hash123")
file2_id = repo.add_file("/path/to/file2.txt", 2048, 1234567891, "hash123") # Same hash
assert file1_id is not None # Ensure files were added successfully
assert file2_id is not None
# Mark second as duplicate
repo.mark_as_duplicate(file2_id, file1_id)
# Verify operations worked
assert repo.count_files() == 2
assert repo.count_duplicates() == 1
# Find duplicates by hash
duplicates = repo.find_duplicates_by_hash("hash123")
assert len(duplicates) == 2
# Get original files
originals = repo.get_original_files()
assert len(originals) == 1
# Update file
success = repo.update_file(file1_id, size=3072)
assert success is True
updated_file = repo.get_file(file1_id)
assert updated_file is not None
assert updated_file['size'] == 3072
finally:
os.unlink(tmp.name)
class TestDatabaseQueryComponents:
"""Test the new database query components."""
def test_database_query_execute(self):
"""Test DatabaseQuery execute method."""
with tempfile.NamedTemporaryFile(suffix='.db') as tmp:
db = DatabaseConnection(tmp.name)
_init_full_schema(db)
# Create DatabaseQuery instance
query = DatabaseQuery(db)
# Insert test data
db.execute(
"INSERT INTO files (path, size, modified_time, "
"created_time, scanned_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)",
("test.txt", 100, 12345, 12345, 12345, 12345)
)
# Test query execution
results = query.execute("SELECT * FROM files WHERE path = ?", ("test.txt",))
assert len(results) == 1
assert results[0]['path'] == "test.txt"
assert results[0]['size'] == 100
def test_database_batch_operations(self):
"""Test DatabaseBatch execute_batch method."""
with tempfile.NamedTemporaryFile(suffix='.db') as tmp:
db = DatabaseConnection(tmp.name)
_init_full_schema(db)
# Create DatabaseBatch instance
batch = DatabaseBatch(db)
# Test batch operations
operations = [
(
"INSERT INTO files (path, size, modified_time, "
"created_time, scanned_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)",
("file1.txt", 100, 12345, 12345, 12345, 12345)
),
(
"INSERT INTO files (path, size, modified_time, "
"created_time, scanned_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)",
("file2.txt", 200, 12346, 12345, 12345, 12345)
)
]
batch.execute_batch(operations)
# Verify batch execution
cursor = db.execute("SELECT COUNT(*) FROM files")
count = cursor.fetchone()[0]
assert count == 2
def test_database_batch_transaction(self):
"""Test DatabaseBatch execute_transaction_batch method."""
with tempfile.NamedTemporaryFile(suffix='.db') as tmp:
db = DatabaseConnection(tmp.name)
_init_full_schema(db)
# Create DatabaseBatch instance
batch = DatabaseBatch(db)
# Test transaction batch operations
operations = [
(
"INSERT INTO files (path, size, modified_time, "
"created_time, scanned_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)",
("file1.txt", 100, 12345, 12345, 12345, 12345)
),
(
"INSERT INTO files (path, size, modified_time, "
"created_time, scanned_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)",
("file2.txt", 200, 12346, 12345, 12345, 12345)
)
]
batch.execute_transaction_batch(operations)
# Verify transaction batch execution
cursor = db.execute("SELECT COUNT(*) FROM files")
count = cursor.fetchone()[0]
assert count == 2
def test_database_performance_monitoring(self):
"""Test DatabasePerformance monitoring methods."""
with tempfile.NamedTemporaryFile(suffix='.db') as tmp:
db = Database(tmp.name)
_init_full_schema(db.connection)
# Create DatabasePerformance instance
performance = DatabasePerformance(db)
# Test monitor_performance method
monitor = performance.monitor_performance()
assert monitor is not None
# Test get_results method
results = performance.get_results()
assert isinstance(results, dict)
assert 'metrics' in results or 'error' in results
def test_database_integrity_checking(self):
"""Test DatabaseIntegrity check_integrity method."""
with tempfile.NamedTemporaryFile(suffix='.db') as tmp:
db = Database(tmp.name)
_init_full_schema(db.connection)
# Create DatabaseIntegrity instance
integrity = DatabaseIntegrity(db)
# Test check_integrity method
results = integrity.check_integrity()
assert isinstance(results, dict)
assert 'tables' in results
assert 'indexes' in results
assert 'valid' in results
assert results['valid'] is True
def test_database_backup_functionality(self):
"""Test DatabaseBackup create_backup and restore_backup methods."""
with tempfile.NamedTemporaryFile(suffix='.db', delete=False) as tmp:
with tempfile.NamedTemporaryFile(suffix='_backup.db', delete=False) as backup:
try:
db = Database(tmp.name)
_init_full_schema(db.connection)
# Add test data
db.connection.execute(
"INSERT INTO files (path, size, modified_time, "
"created_time, scanned_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)",
("test.txt", 100, 12345, 12345, 12345, 12345)
)
# Create DatabaseBackup instance
backup_db = DatabaseBackup(db)
# Test create_backup method
backup_db.create_backup(backup.name)
# Verify backup was created
assert os.path.exists(backup.name)
# Test restore_backup method
restore_path = tmp.name + "_restored"
backup_db.restore_backup(backup.name, restore_path)
# Verify restore was created
assert os.path.exists(restore_path)
finally:
os.unlink(tmp.name)
if os.path.exists(backup.name):
os.unlink(backup.name)
if os.path.exists(tmp.name + "_restored"):
os.unlink(tmp.name + "_restored")
def test_database_migration_functionality(self):
"""Test DatabaseMigration migrate_schema method."""
with tempfile.NamedTemporaryFile(suffix='.db') as tmp:
db = Database(tmp.name)
_init_full_schema(db.connection)
# Create DatabaseMigration instance
migration = DatabaseMigration(db)
# Test migrate_schema method
migrations = {
"test_table": {
"add_columns": ["new_column TEXT"],
"add_indexes": ["CREATE INDEX idx_test_table_new ON test_table(new_column)"]
}
}
# This should not raise an error even if the table doesn't exist
migration.migrate_schema(migrations)
def test_database_recovery_functionality(self):
"""Test DatabaseRecovery handle_errors method."""
with tempfile.NamedTemporaryFile(suffix='.db') as tmp:
db = Database(tmp.name)
_init_full_schema(db.connection)
# Create DatabaseRecovery instance
recovery = DatabaseRecovery(db)
# Test handle_errors method with no errors
result = recovery.handle_errors(raise_on_error=False)
assert result is True
# Test handle_errors method with raise_on_error=True
result = recovery.handle_errors(raise_on_error=True)
assert result is True
def test_database_optimization_functionality(self):
"""Test DatabaseOptimization optimize_query method."""
with tempfile.NamedTemporaryFile(suffix='.db') as tmp:
db = DatabaseConnection(tmp.name)
_init_full_schema(db)
# Create DatabaseOptimization instance
optimization = DatabaseOptimization(db)
# Test optimize_query method
query = " SELECT * FROM files WHERE size > 100; "
optimized = optimization.optimize_query(query)
assert optimized == "SELECT * FROM files WHERE size > 100"
# Test with query that doesn't end with semicolon
query2 = "SELECT * FROM files"
optimized2 = optimization.optimize_query(query2)
assert optimized2 == "SELECT * FROM files"
class TestDatabaseIntegration:
"""Test full Database class integration."""
def test_database_initialization(self):
"""Test Database class initialization."""
with tempfile.NamedTemporaryFile(suffix='.db') as tmp:
# Test Database initialization
db = Database(tmp.name)
assert db.path == tmp.name
assert db.timeout == 30.0
assert hasattr(db, 'connection')
assert hasattr(db, 'schema')
assert hasattr(db, 'indexing')
assert hasattr(db, 'query')
assert hasattr(db, 'batch')
assert hasattr(db, 'transaction')
assert hasattr(db, 'performance')
assert hasattr(db, 'integrity')
assert hasattr(db, 'backup')
assert hasattr(db, 'migration')
assert hasattr(db, 'recovery')
assert hasattr(db, 'security')
assert hasattr(db, 'optimization')
# Test connection
conn = db.connect()
assert isinstance(conn, sqlite3.Connection)
# Test close
db.close()
def test_database_query_operations(self):
"""Test Database query operations."""
with tempfile.NamedTemporaryFile(suffix='.db') as tmp:
db = Database(tmp.name)
_init_full_schema(db.connection)
# Test create_table
db.create_table("test_table", "id INTEGER PRIMARY KEY, name TEXT")
# Test create
data_id = db.create("test_table", {"name": "test"})
assert data_id is not None
# Test read
results = db.read("SELECT * FROM test_table WHERE id = ?", (data_id,))
assert len(results) == 1
assert results[0]['name'] == "test"
# Test update
updated_count = db.update("UPDATE test_table SET name = ? WHERE id = ?", ("updated", data_id))
assert updated_count == 1
# Test delete
deleted_count = db.delete("DELETE FROM test_table WHERE id = ?", (data_id,))
assert deleted_count == 1
# Test close
db.close()
def test_database_batch_operations(self):
"""Test Database batch operations."""
with tempfile.NamedTemporaryFile(suffix='.db') as tmp:
db = Database(tmp.name)
_init_full_schema(db.connection)
# Test execute_batch
operations = [
("INSERT INTO files (path, size, modified_time, created_time, scanned_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)",
("file1.txt", 100, 12345, 12345, 12345, 12345)),
("INSERT INTO files (path, size, modified_time, created_time, scanned_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)",
("file2.txt", 200, 12346, 12345, 12345, 12345))
]
db.execute_batch(operations)
# Verify batch execution
results = db.read("SELECT COUNT(*) FROM files")
assert results[0]['COUNT(*)'] == 2
# Test close
db.close()
def test_database_context_manager(self):
"""Test Database context manager functionality."""
with tempfile.NamedTemporaryFile(suffix='.db') as tmp:
# Test context manager
with Database(tmp.name) as db:
_init_full_schema(db.connection)
assert isinstance(db, Database)
# Test operations within context
db.create_table("context_table", "id INTEGER PRIMARY KEY, value TEXT")
db.create("context_table", {"value": "test"})
# Context manager should have closed the connection
# Verify we can create a new database instance
db2 = Database(tmp.name)
results = db2.read("SELECT * FROM context_table")
assert len(results) == 1
db2.close()