Initial release: DictIA v0.8.14-alpha (fork de Speakr, AGPL-3.0)

This commit is contained in:
InnovA AI
2026-03-16 21:47:37 +00:00
commit 42772a31ed
365 changed files with 103572 additions and 0 deletions

View File

@@ -0,0 +1,165 @@
"""
Integration test for database migrations against a real database engine.
Runs initialize_database() and verifies that all tables and critical columns
are created successfully. Works with both SQLite (default, for local runs)
and PostgreSQL (when TEST_DATABASE_URI env var is set).
IMPORTANT: This test uses TEST_DATABASE_URI (not SQLALCHEMY_DATABASE_URI) to
avoid accidentally connecting to and destroying a real application database.
Usage:
# Local (SQLite in-memory, safe):
python tests/test_postgres_migrations.py
# Against PostgreSQL (CI or explicit testing):
TEST_DATABASE_URI=postgresql://user:pass@localhost:5432/testdb \
python tests/test_postgres_migrations.py
"""
import os
import sys
import unittest
# Ensure project root is on the path
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from flask import Flask
from src.database import db
# Importing models registers them with SQLAlchemy so create_all() builds all tables
import src.models # noqa: F401
from src.init_db import initialize_database
def create_test_app():
"""Create a minimal Flask app for testing database operations.
Uses TEST_DATABASE_URI env var (NOT SQLALCHEMY_DATABASE_URI) to prevent
accidental connection to production/dev databases.
"""
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get(
'TEST_DATABASE_URI', 'sqlite://' # in-memory SQLite by default
)
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['TESTING'] = True
db.init_app(app)
return app
class TestDatabaseMigrations(unittest.TestCase):
"""Test that initialize_database() runs cleanly against the configured DB engine."""
@classmethod
def setUpClass(cls):
cls.app = create_test_app()
with cls.app.app_context():
initialize_database(cls.app)
@classmethod
def tearDownClass(cls):
with cls.app.app_context():
# Use raw DROP to avoid circular FK dependency errors in SQLAlchemy's
# drop_all() (user <-> naming_template have mutual foreign keys)
from sqlalchemy import inspect, text
tables = inspect(db.engine).get_table_names()
with db.engine.connect() as conn:
if db.engine.name == 'postgresql':
for table in tables:
conn.execute(text(f'DROP TABLE IF EXISTS "{table}" CASCADE'))
else:
conn.execute(text('PRAGMA foreign_keys = OFF'))
for table in tables:
conn.execute(text(f'DROP TABLE IF EXISTS "{table}"'))
conn.execute(text('PRAGMA foreign_keys = ON'))
conn.commit()
def _get_table_names(self):
from sqlalchemy import inspect
inspector = inspect(db.engine)
return inspector.get_table_names()
def _get_column_names(self, table):
from sqlalchemy import inspect
inspector = inspect(db.engine)
return [col['name'] for col in inspector.get_columns(table)]
def test_core_tables_exist(self):
"""Verify that all core tables were created."""
with self.app.app_context():
tables = self._get_table_names()
expected_tables = [
'user', 'recording', 'transcript_chunk', 'tag',
'folder', 'share', 'internal_share', 'system_setting',
'speaker', 'processing_job', 'group', 'group_membership',
]
for table in expected_tables:
self.assertIn(table, tables, f"Missing table: {table}")
def test_user_migration_columns(self):
"""Verify columns added by migrations exist on the user table."""
with self.app.app_context():
columns = self._get_column_names('user')
expected = [
'id', 'username', 'email', 'password',
'transcription_language', 'output_language', 'ui_language',
'summary_prompt', 'extract_events', 'name', 'job_title',
'company', 'diarize', 'sso_provider', 'sso_subject',
'can_share_publicly', 'monthly_token_budget',
'monthly_transcription_budget', 'email_verified',
'auto_speaker_labelling', 'auto_summarization',
]
for col in expected:
self.assertIn(col, columns, f"Missing user column: {col}")
def test_recording_migration_columns(self):
"""Verify columns added by migrations exist on the recording table."""
with self.app.app_context():
columns = self._get_column_names('recording')
expected = [
'id', 'is_inbox', 'is_highlighted', 'mime_type',
'completed_at', 'processing_time_seconds', 'error_message',
'folder_id', 'audio_deleted_at', 'deletion_exempt',
'speaker_embeddings',
]
for col in expected:
self.assertIn(col, columns, f"Missing recording column: {col}")
def test_tag_migration_columns(self):
"""Verify columns added by migrations exist on the tag table."""
with self.app.app_context():
columns = self._get_column_names('tag')
expected = [
'id', 'protect_from_deletion', 'group_id',
'retention_days', 'auto_share_on_apply',
'naming_template_id', 'export_template_id',
]
for col in expected:
self.assertIn(col, columns, f"Missing tag column: {col}")
def test_system_settings_initialized(self):
"""Verify that default system settings were created."""
with self.app.app_context():
from src.models import SystemSetting
expected_keys = [
'transcript_length_limit', 'max_file_size_mb',
'asr_timeout_seconds', 'disable_auto_summarization',
'enable_folders',
]
for key in expected_keys:
setting = SystemSetting.query.filter_by(key=key).first()
self.assertIsNotNone(setting, f"Missing system setting: {key}")
def test_engine_type_matches_expectation(self):
"""Sanity check: confirm we're testing against the expected engine."""
with self.app.app_context():
uri = self.app.config['SQLALCHEMY_DATABASE_URI']
engine_name = db.engine.name
if uri.startswith('postgresql'):
self.assertEqual(engine_name, 'postgresql')
else:
self.assertEqual(engine_name, 'sqlite')
if __name__ == '__main__':
unittest.main()