test_spool_usage_spool_migration.py 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071
  1. """spool_usage_history.spool_id becomes nullable, ON DELETE SET NULL, on existing SQLite databases.
  2. Deleting a spool must keep its usage (the costs its prints are recalculated
  3. from), so the column has to take NULL on tables made before. (PostgreSQL is
  4. checked by scripts/check_postgres_upgrade.py.)
  5. """
  6. import pytest
  7. from sqlalchemy import text
  8. from sqlalchemy.ext.asyncio import create_async_engine
  9. from backend.app.core import database
  10. # As create_all made the table from the old model ...
  11. CREATED_BY_THE_MODEL = """
  12. CREATE TABLE spool_usage_history (
  13. \tid INTEGER NOT NULL,
  14. \tspool_id INTEGER NOT NULL,
  15. \tprinter_id INTEGER,
  16. \tprint_name VARCHAR(500),
  17. \tarchive_id INTEGER,
  18. \tweight_used FLOAT NOT NULL,
  19. \tPRIMARY KEY (id),
  20. \tFOREIGN KEY(spool_id) REFERENCES spool (id) ON DELETE CASCADE,
  21. \tFOREIGN KEY(printer_id) REFERENCES printers (id) ON DELETE SET NULL,
  22. \tFOREIGN KEY(archive_id) REFERENCES print_archives (id) ON DELETE SET NULL
  23. )
  24. """
  25. # ... and as the usage-history migration's own CREATE TABLE made it.
  26. CREATED_BY_THE_MIGRATION = """
  27. CREATE TABLE spool_usage_history (
  28. id INTEGER PRIMARY KEY AUTOINCREMENT,
  29. spool_id INTEGER NOT NULL REFERENCES spool(id) ON DELETE CASCADE,
  30. printer_id INTEGER REFERENCES printers(id) ON DELETE SET NULL,
  31. print_name VARCHAR(500),
  32. weight_used REAL NOT NULL DEFAULT 0
  33. )
  34. """
  35. @pytest.fixture(autouse=True)
  36. def sqlite_dialect(monkeypatch):
  37. monkeypatch.setattr(database, "is_sqlite", lambda: True)
  38. @pytest.mark.asyncio
  39. @pytest.mark.parametrize("legacy", [CREATED_BY_THE_MODEL, CREATED_BY_THE_MIGRATION], ids=["model", "migration"])
  40. async def test_the_spool_column_takes_null_and_says_set_null(legacy):
  41. engine = create_async_engine("sqlite+aiosqlite:///:memory:")
  42. try:
  43. async with engine.begin() as conn:
  44. await conn.execute(text(legacy))
  45. await conn.execute(text("INSERT INTO spool_usage_history (id, spool_id, weight_used) VALUES (1, 7, 5)"))
  46. await database._migrate_spool_usage_outlives_its_spool(conn)
  47. await database._migrate_spool_usage_outlives_its_spool(conn) # a restart changes nothing
  48. await conn.execute(text("UPDATE spool_usage_history SET spool_id = NULL WHERE id = 1"))
  49. assert (await conn.execute(text("SELECT spool_id, weight_used FROM spool_usage_history"))).one() == (
  50. None,
  51. 5,
  52. )
  53. sql = (
  54. await conn.execute(text("SELECT sql FROM sqlite_master WHERE name = 'spool_usage_history'"))
  55. ).scalar_one()
  56. assert "CASCADE" not in sql
  57. # Only the spool column changed.
  58. assert "weight_used" in sql and "NOT NULL" in sql.split("weight_used", 1)[1].split(",", 1)[0]
  59. finally:
  60. await engine.dispose()