pdf-quiz-generator/backend/tests/test_articles_migration.py
Daniel 325f7f400f feat: linked topic articles and card associations
Draft/published article library with stable section IDs, breadcrumbs, link remediation, question and card associations, manual card creation and side-by-side/mobile-drawer reading. Migration e8d4f1a27c93. Verified 42 deployed-image backend tests, 72 frontend tests/build and PostgreSQL migration round-trip.
2026-09-07 15:16:48 +02:00

44 lines
2.2 KiB
Python

"""Offline PostgreSQL DDL checks for the articles/cards migration; no database access."""
import io
import os
import unittest
from pathlib import Path
from unittest.mock import patch
os.environ['DATABASE_URL'] = 'sqlite:///:memory:'
from alembic import command
from alembic.config import Config
from alembic.script import ScriptDirectory
class ArticlesMigrationTests(unittest.TestCase):
def test_models_register_fresh_and_offline_upgrade_downgrade(self):
import subprocess
import sys
result = subprocess.run([sys.executable, '-c',
"import app.models; from app.database import Base; "
"assert {'articles','question_article_links','flashcard_question_links','flashcard_article_links'} <= set(Base.metadata.tables)"],
capture_output=True, text=True, env={**os.environ, 'DATABASE_URL': 'sqlite:///:memory:'})
self.assertEqual(result.returncode, 0, result.stderr)
output = io.StringIO()
config = Config(output_buffer=output)
config.set_main_option('script_location', str(Path(__file__).resolve().parents[1] / 'alembic'))
scripts = ScriptDirectory.from_config(config)
self.assertEqual(len(scripts.get_heads()), 1)
self.assertEqual(scripts.get_revision('e8d4f1a27c93').down_revision, 'd94a26b8f302')
with patch.dict(os.environ, {'DATABASE_URL': 'postgresql://unused@127.0.0.1/offline_only'}):
command.upgrade(config, 'd94a26b8f302:e8d4f1a27c93', sql=True)
sql = output.getvalue()
for table in ('articles', 'question_article_links', 'flashcard_question_links', 'flashcard_article_links'):
self.assertIn(f'CREATE TABLE IF NOT EXISTS {table}', sql)
self.assertIn('UNIQUE (question_id, article_id, section_id)', sql)
self.assertIn('ON DELETE CASCADE', sql)
output.seek(0)
output.truncate()
command.downgrade(config, 'e8d4f1a27c93:d94a26b8f302', sql=True)
for table in ('flashcard_article_links', 'flashcard_question_links', 'question_article_links', 'articles'):
self.assertIn(f'DROP TABLE IF EXISTS {table}', output.getvalue())
if __name__ == '__main__':
unittest.main()