mirror of
https://github.com/allaunthefox/Research-Stack.git
synced 2026-07-31 03:05:21 +00:00
438 lines
14 KiB
Python
438 lines
14 KiB
Python
# SPDX-License-Identifier: Apache-2.0
|
|
# Copyright (c) 2025 NoDupeLabs
|
|
|
|
"""Tests to achieve 100% coverage on query.py module.
|
|
|
|
This test file targets the missing coverage in:
|
|
- query.py: Query optimization paths, batch operations, performance monitoring
|
|
"""
|
|
|
|
from unittest.mock import MagicMock
|
|
|
|
import pytest
|
|
|
|
from nodupe.tools.databases.query import (
|
|
DatabaseBackup,
|
|
DatabaseBatch,
|
|
DatabaseIntegrity,
|
|
DatabaseMigration,
|
|
DatabaseOptimization,
|
|
DatabasePerformance,
|
|
DatabaseQuery,
|
|
DatabaseRecovery,
|
|
)
|
|
|
|
|
|
class TestDatabaseQueryCoverage:
|
|
"""Tests for DatabaseQuery class to achieve 100% coverage."""
|
|
|
|
def test_execute_with_get_connection(self):
|
|
"""Test execute when db has get_connection method."""
|
|
mock_db = MagicMock()
|
|
mock_conn = MagicMock()
|
|
mock_cursor = MagicMock()
|
|
|
|
mock_db.get_connection.return_value = mock_conn
|
|
mock_conn.cursor.return_value = mock_cursor
|
|
mock_cursor.fetchall.return_value = [('row1_col1', 'row1_col2'), ('row2_col1', 'row2_col2')]
|
|
mock_cursor.description = [('col1',), ('col2',)]
|
|
|
|
query = DatabaseQuery(mock_db)
|
|
results = query.execute("SELECT * FROM test")
|
|
|
|
assert len(results) == 2
|
|
assert results[0] == {'col1': 'row1_col1', 'col2': 'row1_col2'}
|
|
assert results[1] == {'col1': 'row2_col1', 'col2': 'row2_col2'}
|
|
mock_db.get_connection.assert_called_once()
|
|
|
|
def test_execute_with_connect(self):
|
|
"""Test execute when db has connect method (no get_connection)."""
|
|
mock_db = MagicMock()
|
|
del mock_db.get_connection # Remove get_connection attribute
|
|
|
|
mock_conn = MagicMock()
|
|
mock_cursor = MagicMock()
|
|
|
|
mock_db.connect.return_value = mock_conn
|
|
mock_conn.cursor.return_value = mock_cursor
|
|
mock_cursor.fetchall.return_value = []
|
|
mock_cursor.description = [('col1',)]
|
|
|
|
query = DatabaseQuery(mock_db)
|
|
results = query.execute("SELECT * FROM test")
|
|
|
|
assert results == []
|
|
mock_db.connect.assert_called_once()
|
|
|
|
def test_execute_with_params(self):
|
|
"""Test execute with parameters."""
|
|
mock_db = MagicMock()
|
|
mock_conn = MagicMock()
|
|
mock_cursor = MagicMock()
|
|
|
|
mock_db.get_connection.return_value = mock_conn
|
|
mock_conn.cursor.return_value = mock_cursor
|
|
mock_cursor.fetchall.return_value = []
|
|
mock_cursor.description = [('col1',)]
|
|
|
|
query = DatabaseQuery(mock_db)
|
|
query.execute("SELECT * FROM test WHERE id = ?", (123,))
|
|
|
|
mock_cursor.execute.assert_called_once_with("SELECT * FROM test WHERE id = ?", (123,))
|
|
|
|
def test_execute_no_params(self):
|
|
"""Test execute without parameters (uses empty tuple)."""
|
|
mock_db = MagicMock()
|
|
mock_conn = MagicMock()
|
|
mock_cursor = MagicMock()
|
|
|
|
mock_db.get_connection.return_value = mock_conn
|
|
mock_conn.cursor.return_value = mock_cursor
|
|
mock_cursor.fetchall.return_value = []
|
|
mock_cursor.description = [('col1',)]
|
|
|
|
query = DatabaseQuery(mock_db)
|
|
query.execute("SELECT * FROM test")
|
|
|
|
mock_cursor.execute.assert_called_once_with("SELECT * FROM test", ())
|
|
|
|
|
|
class TestDatabaseBatchCoverage:
|
|
"""Tests for DatabaseBatch class to achieve 100% coverage."""
|
|
|
|
def test_execute_batch_with_get_connection(self):
|
|
"""Test execute_batch when db has get_connection method."""
|
|
mock_db = MagicMock()
|
|
mock_conn = MagicMock()
|
|
mock_cursor = MagicMock()
|
|
|
|
mock_db.get_connection.return_value = mock_conn
|
|
mock_conn.cursor.return_value = mock_cursor
|
|
|
|
batch = DatabaseBatch(mock_db)
|
|
operations = [
|
|
("INSERT INTO test VALUES (?, ?)", (1, 'a')),
|
|
("UPDATE test SET col = ?", ('b',)),
|
|
]
|
|
batch.execute_batch(operations)
|
|
|
|
assert mock_cursor.execute.call_count == 2
|
|
mock_conn.commit.assert_called_once()
|
|
|
|
def test_execute_batch_with_connect(self):
|
|
"""Test execute_batch when db has connect method."""
|
|
mock_db = MagicMock()
|
|
del mock_db.get_connection
|
|
|
|
mock_conn = MagicMock()
|
|
mock_cursor = MagicMock()
|
|
|
|
mock_db.connect.return_value = mock_conn
|
|
mock_conn.cursor.return_value = mock_cursor
|
|
|
|
batch = DatabaseBatch(mock_db)
|
|
batch.execute_batch([("INSERT INTO test VALUES (1)", ())])
|
|
|
|
mock_db.connect.assert_called_once()
|
|
|
|
def test_execute_transaction_batch_success(self):
|
|
"""Test execute_transaction_batch successful commit."""
|
|
mock_db = MagicMock()
|
|
mock_conn = MagicMock()
|
|
mock_cursor = MagicMock()
|
|
|
|
mock_db.get_connection.return_value = mock_conn
|
|
mock_conn.cursor.return_value = mock_cursor
|
|
|
|
batch = DatabaseBatch(mock_db)
|
|
operations = [("INSERT INTO test VALUES (?)", (1,))]
|
|
batch.execute_transaction_batch(operations)
|
|
|
|
mock_conn.commit.assert_called_once()
|
|
mock_conn.rollback.assert_not_called()
|
|
|
|
def test_execute_transaction_batch_rollback(self):
|
|
"""Test execute_transaction_batch rollback on exception."""
|
|
mock_db = MagicMock()
|
|
mock_conn = MagicMock()
|
|
mock_cursor = MagicMock()
|
|
|
|
mock_db.get_connection.return_value = mock_conn
|
|
mock_conn.cursor.return_value = mock_cursor
|
|
mock_cursor.execute.side_effect = Exception("Query failed")
|
|
|
|
batch = DatabaseBatch(mock_db)
|
|
operations = [("INSERT INTO test VALUES (?)", (1,))]
|
|
|
|
with pytest.raises(Exception, match="Query failed"):
|
|
batch.execute_transaction_batch(operations)
|
|
|
|
mock_conn.rollback.assert_called_once()
|
|
mock_conn.commit.assert_not_called()
|
|
|
|
|
|
class TestDatabasePerformanceCoverage:
|
|
"""Tests for DatabasePerformance class to achieve 100% coverage."""
|
|
|
|
def test_get_metrics(self):
|
|
"""Test get_metrics returns metrics dict."""
|
|
mock_db = MagicMock()
|
|
perf = DatabasePerformance(mock_db)
|
|
|
|
metrics = perf.get_metrics()
|
|
|
|
assert 'metrics' in metrics
|
|
assert metrics['metrics']['queries'] == 0
|
|
assert metrics['metrics']['total_time'] == 0.0
|
|
assert metrics['metrics']['avg_time'] == 0.0
|
|
|
|
def test_record_query_first(self):
|
|
"""Test record_query for first query."""
|
|
mock_db = MagicMock()
|
|
perf = DatabasePerformance(mock_db)
|
|
|
|
perf.record_query(0.5)
|
|
|
|
assert perf._metrics['queries'] == 1
|
|
assert perf._metrics['total_time'] == 0.5
|
|
assert perf._metrics['avg_time'] == 0.5
|
|
|
|
def test_record_query_multiple(self):
|
|
"""Test record_query for multiple queries."""
|
|
mock_db = MagicMock()
|
|
perf = DatabasePerformance(mock_db)
|
|
|
|
perf.record_query(0.3)
|
|
perf.record_query(0.5)
|
|
perf.record_query(0.4)
|
|
|
|
assert perf._metrics['queries'] == 3
|
|
assert perf._metrics['total_time'] == pytest.approx(1.2)
|
|
assert perf._metrics['avg_time'] == pytest.approx(0.4)
|
|
|
|
def test_monitor_performance(self):
|
|
"""Test monitor_performance returns db.monitoring."""
|
|
mock_db = MagicMock()
|
|
mock_monitoring = MagicMock()
|
|
mock_db.monitoring = mock_monitoring
|
|
|
|
perf = DatabasePerformance(mock_db)
|
|
result = perf.monitor_performance()
|
|
|
|
assert result is mock_monitoring
|
|
|
|
def test_get_results(self):
|
|
"""Test get_results returns db.monitoring.get_metrics()."""
|
|
mock_db = MagicMock()
|
|
mock_monitoring = MagicMock()
|
|
mock_monitoring.get_metrics.return_value = {'test': 'metrics'}
|
|
mock_db.monitoring = mock_monitoring
|
|
|
|
perf = DatabasePerformance(mock_db)
|
|
result = perf.get_results()
|
|
|
|
assert result == {'test': 'metrics'}
|
|
mock_monitoring.get_metrics.assert_called_once()
|
|
|
|
|
|
class TestDatabaseIntegrityCoverage:
|
|
"""Tests for DatabaseIntegrity class to achieve 100% coverage."""
|
|
|
|
def test_validate(self):
|
|
"""Test validate returns expected format."""
|
|
mock_db = MagicMock()
|
|
integrity = DatabaseIntegrity(mock_db)
|
|
|
|
result = integrity.validate()
|
|
|
|
assert 'valid' in result
|
|
assert 'errors' in result
|
|
assert 'tables' in result
|
|
assert result['valid'] is True
|
|
assert result['errors'] == []
|
|
assert result['tables'] == []
|
|
|
|
def test_check_integrity(self):
|
|
"""Test check_integrity returns expected format with indexes."""
|
|
mock_db = MagicMock()
|
|
integrity = DatabaseIntegrity(mock_db)
|
|
|
|
result = integrity.check_integrity()
|
|
|
|
assert 'valid' in result
|
|
assert 'errors' in result
|
|
assert 'tables' in result
|
|
assert 'indexes' in result
|
|
assert result['valid'] is True
|
|
assert result['errors'] == []
|
|
assert result['tables'] == []
|
|
assert result['indexes'] == []
|
|
|
|
|
|
class TestDatabaseBackupCoverage:
|
|
"""Tests for DatabaseBackup class to achieve 100% coverage."""
|
|
|
|
def test_create_backup(self, tmp_path):
|
|
"""Test create_backup copies database file."""
|
|
mock_db = MagicMock()
|
|
source_db = tmp_path / "source.db"
|
|
source_db.write_bytes(b"database content")
|
|
mock_db.path = str(source_db)
|
|
|
|
backup_path = tmp_path / "backup.db"
|
|
|
|
backup = DatabaseBackup(mock_db)
|
|
backup.create_backup(str(backup_path))
|
|
|
|
assert backup_path.exists()
|
|
assert backup_path.read_bytes() == b"database content"
|
|
|
|
def test_restore_backup(self, tmp_path):
|
|
"""Test restore_backup copies backup to restore path."""
|
|
mock_db = MagicMock()
|
|
backup_file = tmp_path / "backup.db"
|
|
backup_file.write_bytes(b"backup content")
|
|
restore_path = tmp_path / "restored.db"
|
|
|
|
backup = DatabaseBackup(mock_db)
|
|
backup.restore_backup(str(backup_file), str(restore_path))
|
|
|
|
assert restore_path.exists()
|
|
assert restore_path.read_bytes() == b"backup content"
|
|
|
|
|
|
class TestDatabaseMigrationCoverage:
|
|
"""Tests for DatabaseMigration class to achieve 100% coverage."""
|
|
|
|
def test_migrate_schema(self):
|
|
"""Test migrate_schema (pass-through implementation)."""
|
|
mock_db = MagicMock()
|
|
migration = DatabaseMigration(mock_db)
|
|
|
|
migrations = {
|
|
'table1': {'add': ['col1'], 'remove': []},
|
|
}
|
|
|
|
# Should not raise (pass-through implementation)
|
|
migration.migrate_schema(migrations)
|
|
|
|
def test_migrate_data(self):
|
|
"""Test migrate_data (pass-through implementation)."""
|
|
mock_db = MagicMock()
|
|
migration = DatabaseMigration(mock_db)
|
|
|
|
transformations = {'col1': 'UPPER(col1)'}
|
|
new_columns = ['col2']
|
|
|
|
# Should not raise (pass-through implementation)
|
|
migration.migrate_data('table1', transformations, new_columns)
|
|
|
|
def test_migrate_data_no_new_columns(self):
|
|
"""Test migrate_data without new_columns parameter."""
|
|
mock_db = MagicMock()
|
|
migration = DatabaseMigration(mock_db)
|
|
|
|
transformations = {'col1': 'UPPER(col1)'}
|
|
|
|
# Should not raise (pass-through implementation)
|
|
migration.migrate_data('table1', transformations)
|
|
|
|
|
|
class TestDatabaseRecoveryCoverage:
|
|
"""Tests for DatabaseRecovery class to achieve 100% coverage."""
|
|
|
|
def test_handle_errors_success(self):
|
|
"""Test handle_errors when integrity check passes."""
|
|
mock_db = MagicMock()
|
|
mock_integrity = MagicMock()
|
|
mock_integrity.check_integrity.return_value = {'valid': True, 'errors': []}
|
|
mock_db.integrity = mock_integrity
|
|
|
|
recovery = DatabaseRecovery(mock_db)
|
|
result = recovery.handle_errors(raise_on_error=False)
|
|
|
|
assert result is True
|
|
|
|
def test_handle_errors_invalid_not_raise(self):
|
|
"""Test handle_errors when integrity check fails (not raising)."""
|
|
mock_db = MagicMock()
|
|
mock_integrity = MagicMock()
|
|
mock_integrity.check_integrity.return_value = {'valid': False, 'errors': ['error1']}
|
|
mock_db.integrity = mock_integrity
|
|
|
|
recovery = DatabaseRecovery(mock_db)
|
|
result = recovery.handle_errors(raise_on_error=False)
|
|
|
|
assert result is False
|
|
|
|
def test_handle_errors_invalid_raise(self):
|
|
"""Test handle_errors when integrity check fails (raising)."""
|
|
mock_db = MagicMock()
|
|
mock_integrity = MagicMock()
|
|
mock_integrity.check_integrity.return_value = {'valid': False, 'errors': ['error1']}
|
|
mock_db.integrity = mock_integrity
|
|
|
|
recovery = DatabaseRecovery(mock_db)
|
|
|
|
with pytest.raises(Exception, match="Database integrity check failed"):
|
|
recovery.handle_errors(raise_on_error=True)
|
|
|
|
def test_handle_errors_exception_not_raise(self):
|
|
"""Test handle_errors when exception occurs (not raising)."""
|
|
mock_db = MagicMock()
|
|
mock_db.integrity.check_integrity.side_effect = Exception("DB error")
|
|
|
|
recovery = DatabaseRecovery(mock_db)
|
|
result = recovery.handle_errors(raise_on_error=False)
|
|
|
|
assert result is False
|
|
|
|
def test_handle_errors_exception_raise(self):
|
|
"""Test handle_errors when exception occurs (raising)."""
|
|
mock_db = MagicMock()
|
|
mock_db.integrity.check_integrity.side_effect = Exception("DB error")
|
|
|
|
recovery = DatabaseRecovery(mock_db)
|
|
|
|
with pytest.raises(Exception, match="DB error"):
|
|
recovery.handle_errors(raise_on_error=True)
|
|
|
|
|
|
class TestDatabaseOptimizationCoverage:
|
|
"""Tests for DatabaseOptimization class to achieve 100% coverage."""
|
|
|
|
def test_optimize_query_basic(self):
|
|
"""Test optimize_query strips and removes trailing semicolon."""
|
|
mock_db = MagicMock()
|
|
optimization = DatabaseOptimization(mock_db)
|
|
|
|
result = optimization.optimize_query("SELECT * FROM test; ")
|
|
|
|
assert result == "SELECT * FROM test"
|
|
|
|
def test_optimize_query_no_semicolon(self):
|
|
"""Test optimize_query when no trailing semicolon."""
|
|
mock_db = MagicMock()
|
|
optimization = DatabaseOptimization(mock_db)
|
|
|
|
result = optimization.optimize_query("SELECT * FROM test")
|
|
|
|
assert result == "SELECT * FROM test"
|
|
|
|
def test_optimize_query_whitespace_only(self):
|
|
"""Test optimize_query with whitespace-only query."""
|
|
mock_db = MagicMock()
|
|
optimization = DatabaseOptimization(mock_db)
|
|
|
|
result = optimization.optimize_query(" ; ")
|
|
|
|
assert result == ""
|
|
|
|
def test_optimize_query_empty(self):
|
|
"""Test optimize_query with empty query."""
|
|
mock_db = MagicMock()
|
|
optimization = DatabaseOptimization(mock_db)
|
|
|
|
result = optimization.optimize_query("")
|
|
|
|
assert result == ""
|