| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126 |
- """The is_env_managed column has to reach databases that already exist (#2593).
- The model test covers a table freshly created from metadata, which is not how
- an upgrade arrives: an installed instance has an oidc_providers table without
- the column, and only run_migrations adds it there. Every boot re-runs the whole
- migration set, so adding it twice must be a no-op rather than an error.
- """
- from __future__ import annotations
- import pytest
- from sqlalchemy import text
- from sqlalchemy.ext.asyncio import create_async_engine
- from backend.app.core.database import Base, run_migrations
- def _register_all_models():
- from backend.app.models import ( # noqa: F401
- ams_history,
- ams_label,
- api_key,
- archive,
- color_catalog,
- external_link,
- filament,
- group,
- kprofile_note,
- library,
- maintenance,
- notification,
- notification_template,
- print_log,
- print_queue,
- printer,
- project,
- project_bom,
- settings,
- slot_preset,
- smart_plug,
- smart_plug_energy_snapshot,
- spool,
- spool_assignment,
- spool_catalog,
- spool_k_profile,
- spool_usage_history,
- spoolbuddy_device,
- user,
- user_email_pref,
- virtual_printer,
- )
- @pytest.fixture(autouse=True)
- def force_sqlite_dialect(monkeypatch):
- """Force the SQLite branch regardless of test env settings."""
- from backend.app.core import database as database_module, db_dialect
- monkeypatch.setattr(db_dialect, "is_sqlite", lambda: True)
- monkeypatch.setattr(db_dialect, "is_postgres", lambda: False)
- monkeypatch.setattr(database_module, "is_sqlite", lambda: True)
- @pytest.fixture
- async def engine():
- """A database as it stands before this change: every table created from the
- models, then the new column dropped again -- the model already declares it,
- so only removing it reproduces what an installed instance actually has."""
- _register_all_models()
- eng = create_async_engine("sqlite+aiosqlite:///:memory:", echo=False)
- async with eng.begin() as conn:
- await conn.run_sync(Base.metadata.create_all)
- await conn.execute(text("ALTER TABLE oidc_providers DROP COLUMN is_env_managed"))
- yield eng
- await eng.dispose()
- async def _columns(conn) -> set[str]:
- rows = await conn.execute(text("PRAGMA table_info(oidc_providers)"))
- return {r[1] for r in rows}
- @pytest.mark.asyncio
- async def test_migration_adds_the_column_to_an_existing_table(engine):
- async with engine.connect() as conn:
- assert "is_env_managed" not in await _columns(conn)
- async with engine.begin() as conn:
- await run_migrations(conn)
- async with engine.connect() as conn:
- assert "is_env_managed" in await _columns(conn)
- @pytest.mark.asyncio
- async def test_existing_rows_default_to_not_env_managed(engine):
- """A provider created through the UI before the upgrade must not come back
- locked -- is_env_managed decides whether the API refuses to edit it."""
- async with engine.begin() as conn:
- await conn.execute(
- text(
- "INSERT INTO oidc_providers"
- " (id, name, issuer_url, client_id, client_secret, scopes, is_enabled,"
- " auto_create_users, auto_link_existing_accounts, email_claim,"
- " require_email_verified)"
- " VALUES (1, 'UI provider', 'https://sso.example', 'app', 'enc',"
- " 'openid email profile', 1, 0, 0, 'email', 1)"
- )
- )
- await run_migrations(conn)
- async with engine.connect() as conn:
- row = await conn.execute(text("SELECT is_env_managed FROM oidc_providers WHERE id = 1"))
- assert not row.scalar()
- @pytest.mark.asyncio
- async def test_it_is_idempotent(engine):
- """Every boot re-runs the migration set."""
- for _ in range(2):
- async with engine.begin() as conn:
- await run_migrations(conn)
- async with engine.connect() as conn:
- assert "is_env_managed" in await _columns(conn)
|