233 lines
8.0 KiB
Python
233 lines
8.0 KiB
Python
"""
|
|
Tests for the database connection module.
|
|
|
|
NOTE: These tests have been updated to minimize file system interactions by:
|
|
1. Using in-memory databases wherever possible
|
|
2. Mocking file system interactions when testing file-based modes
|
|
3. Ensuring proper cleanup of database connections between tests
|
|
|
|
However, due to the complexity of SQLite's file interactions through the peewee driver,
|
|
these tests may still sometimes create files in the real .ra-aid directory during execution.
|
|
"""
|
|
|
|
import os
|
|
from pathlib import Path
|
|
from unittest.mock import patch, MagicMock
|
|
|
|
import peewee
|
|
import pytest
|
|
|
|
from ra_aid.database.connection import (
|
|
DatabaseManager,
|
|
close_db,
|
|
db_var,
|
|
get_db,
|
|
init_db,
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def cleanup_db():
|
|
"""
|
|
Fixture to clean up database connections between tests.
|
|
|
|
This ensures that we don't leak database connections between tests
|
|
and that the db_var contextvar is reset.
|
|
"""
|
|
# Run the test
|
|
yield
|
|
|
|
# Clean up after the test
|
|
db = db_var.get()
|
|
if db is not None:
|
|
# Clean up attributes we may have added
|
|
if hasattr(db, "_is_in_memory"):
|
|
delattr(db, "_is_in_memory")
|
|
if hasattr(db, "_message_shown"):
|
|
delattr(db, "_message_shown")
|
|
|
|
# Close the connection if it's open
|
|
if not db.is_closed():
|
|
db.close()
|
|
|
|
# Reset the contextvar
|
|
db_var.set(None)
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_logger():
|
|
"""Mock the logger to test for output messages."""
|
|
with patch("ra_aid.database.connection.logger") as mock:
|
|
yield mock
|
|
|
|
|
|
class TestInitDb:
|
|
"""Tests for the init_db function."""
|
|
|
|
# Use in-memory=True for all file-based tests to avoid file system interactions
|
|
def test_init_db_default(self, cleanup_db):
|
|
"""Test init_db with default parameters."""
|
|
# Initialize the database with in-memory=True for testing
|
|
db = init_db(in_memory=True)
|
|
|
|
# Override the _is_in_memory attribute to test as if it were a file-based database
|
|
db._is_in_memory = False
|
|
|
|
# Verify database was initialized correctly
|
|
assert isinstance(db, peewee.SqliteDatabase)
|
|
assert not db.is_closed()
|
|
assert hasattr(db, "_is_in_memory")
|
|
assert db._is_in_memory is False # We set this manually
|
|
|
|
def test_init_db_in_memory(self, cleanup_db):
|
|
"""Test init_db with in_memory=True."""
|
|
db = init_db(in_memory=True)
|
|
assert isinstance(db, peewee.SqliteDatabase)
|
|
assert not db.is_closed()
|
|
assert hasattr(db, "_is_in_memory")
|
|
assert db._is_in_memory is True
|
|
|
|
def test_init_db_reuses_connection(self, cleanup_db):
|
|
"""Test that init_db reuses an existing connection."""
|
|
db1 = init_db(in_memory=True)
|
|
db2 = init_db(in_memory=True)
|
|
assert db1 is db2
|
|
|
|
def test_init_db_reopens_closed_connection(self, cleanup_db):
|
|
"""Test that init_db reopens a closed connection."""
|
|
db1 = init_db(in_memory=True)
|
|
db1.close()
|
|
assert db1.is_closed()
|
|
db2 = init_db(in_memory=True)
|
|
assert db1 is db2
|
|
assert not db1.is_closed()
|
|
|
|
def test_in_memory_mode_no_directory_created(self, cleanup_db):
|
|
"""Test that when using in_memory mode, no database file is created."""
|
|
# Use a mock to verify that os.path.exists is not called for database files
|
|
with patch("os.path.exists") as mock_exists:
|
|
# Initialize the database with in_memory=True
|
|
db = init_db(in_memory=True)
|
|
|
|
# Verify it's really in-memory
|
|
assert hasattr(db, "_is_in_memory")
|
|
assert db._is_in_memory is True
|
|
|
|
# Verify os.path.exists was not called
|
|
mock_exists.assert_not_called()
|
|
|
|
|
|
class TestGetDb:
|
|
"""Tests for the get_db function."""
|
|
|
|
def test_get_db_creates_connection(self, cleanup_db):
|
|
"""Test that get_db creates a new connection if none exists."""
|
|
# Reset the contextvar to ensure no connection exists
|
|
db_var.set(None)
|
|
|
|
# We'll mock init_db and verify it gets called by get_db() with the default parameters
|
|
with patch("ra_aid.database.connection.init_db") as mock_init_db:
|
|
# Set up the mock to return a dummy database
|
|
mock_db = MagicMock(spec=peewee.SqliteDatabase)
|
|
mock_db.is_closed.return_value = False
|
|
mock_db._is_in_memory = False
|
|
mock_init_db.return_value = mock_db
|
|
|
|
# Get a connection
|
|
db = get_db()
|
|
|
|
# Verify init_db was called with in_memory=False and base_dir=None
|
|
mock_init_db.assert_called_once_with(in_memory=False, base_dir=None)
|
|
|
|
# Verify the database was returned correctly
|
|
assert db is mock_db
|
|
|
|
def test_get_db_reuses_connection(self, cleanup_db):
|
|
"""Test that get_db reuses an existing connection."""
|
|
db1 = init_db(in_memory=True)
|
|
db2 = get_db()
|
|
assert db1 is db2
|
|
|
|
def test_get_db_reopens_closed_connection(self, cleanup_db):
|
|
"""Test that get_db reopens a closed connection."""
|
|
db1 = init_db(in_memory=True)
|
|
db1.close()
|
|
assert db1.is_closed()
|
|
db2 = get_db()
|
|
assert db1 is db2
|
|
assert not db1.is_closed()
|
|
|
|
|
|
class TestCloseDb:
|
|
"""Tests for the close_db function."""
|
|
|
|
def test_close_db(self, cleanup_db):
|
|
"""Test that close_db closes an open connection."""
|
|
db = init_db(in_memory=True)
|
|
assert not db.is_closed()
|
|
close_db()
|
|
assert db.is_closed()
|
|
|
|
def test_close_db_no_connection(self, cleanup_db):
|
|
"""Test that close_db handles the case where no connection exists."""
|
|
# Reset the contextvar to ensure no connection exists
|
|
db_var.set(None)
|
|
# This should not raise an exception
|
|
close_db()
|
|
|
|
def test_close_db_already_closed(self, cleanup_db):
|
|
"""Test that close_db handles the case where the connection is already closed."""
|
|
db = init_db(in_memory=True)
|
|
db.close()
|
|
assert db.is_closed()
|
|
# This should not raise an exception
|
|
close_db()
|
|
|
|
|
|
class TestDatabaseManager:
|
|
"""Tests for the DatabaseManager class."""
|
|
|
|
def test_database_manager_default(self, cleanup_db):
|
|
"""Test DatabaseManager with default parameters."""
|
|
# Use in-memory=True but test with _is_in_memory=False
|
|
with DatabaseManager(in_memory=True) as db:
|
|
# Override the attribute for testing
|
|
db._is_in_memory = False
|
|
|
|
# Verify the database connection
|
|
assert isinstance(db, peewee.SqliteDatabase)
|
|
assert not db.is_closed()
|
|
assert hasattr(db, "_is_in_memory")
|
|
assert db._is_in_memory is False # We set this manually
|
|
|
|
# Store the connection for later assertions
|
|
db_in_context = db
|
|
|
|
# Verify the connection is closed after exiting the context
|
|
assert db_in_context.is_closed()
|
|
|
|
def test_database_manager_in_memory(self, cleanup_db):
|
|
"""Test DatabaseManager with in_memory=True."""
|
|
with DatabaseManager(in_memory=True) as db:
|
|
assert isinstance(db, peewee.SqliteDatabase)
|
|
assert not db.is_closed()
|
|
assert hasattr(db, "_is_in_memory")
|
|
assert db._is_in_memory is True
|
|
|
|
# Store the connection for later assertions
|
|
db_in_context = db
|
|
|
|
# Verify the connection is closed after exiting the context
|
|
assert db_in_context.is_closed()
|
|
|
|
def test_database_manager_exception_handling(self, cleanup_db):
|
|
"""Test that DatabaseManager properly handles exceptions."""
|
|
try:
|
|
with DatabaseManager(in_memory=True) as db:
|
|
assert not db.is_closed()
|
|
raise ValueError("Test exception")
|
|
except ValueError:
|
|
# The exception should be propagated
|
|
pass
|
|
# Verify the connection is closed even if an exception occurred
|
|
assert db.is_closed() |