test_check_postgres_upgrade.py 3.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. """The comparison in scripts/check_postgres_upgrade.py.
  2. The script itself needs a PostgreSQL server and runs in CI; these cover how it
  3. decides: what counts as drift, and that the allowlist can only shrink.
  4. """
  5. import importlib.util
  6. from pathlib import Path
  7. SCRIPT = Path(__file__).resolve().parents[3] / "scripts" / "check_postgres_upgrade.py"
  8. _spec = importlib.util.spec_from_file_location("check_postgres_upgrade", SCRIPT)
  9. assert _spec and _spec.loader
  10. check = importlib.util.module_from_spec(_spec)
  11. _spec.loader.exec_module(check)
  12. FK = "constraint print_log_entries: FOREIGN KEY (archive_id) REFERENCES print_archives(id) ON DELETE SET NULL"
  13. FRESH = {
  14. "column spool.weight_used_baseline": "double precision (udt float8, precision 53) NOT NULL default None",
  15. FK: "1x",
  16. }
  17. def test_identical_schemas_do_not_drift():
  18. assert check.drift(FRESH, dict(FRESH)) == set()
  19. def test_a_column_built_differently_drifts_with_both_definitions():
  20. upgraded = FRESH | {"column spool.weight_used_baseline": "real (udt float4, precision 24) NULL default 0"}
  21. assert check.drift(FRESH, upgraded) == {
  22. "column spool.weight_used_baseline | fresh install: double precision (udt float8, precision 53) NOT NULL "
  23. "default None; upgraded: real (udt float4, precision 24) NULL default 0"
  24. }
  25. def test_a_missing_constraint_drifts_and_so_does_an_extra_one():
  26. upgraded = {k: v for k, v in FRESH.items() if k != FK} | {"column legacy.only_upgraded": "text NULL default None"}
  27. assert check.drift(FRESH, upgraded) == {
  28. f"{FK} | fresh install: 1x; upgraded: missing",
  29. "column legacy.only_upgraded | fresh install: missing; upgraded: text NULL default None",
  30. }
  31. def test_a_duplicated_constraint_drifts():
  32. assert check.drift(FRESH, FRESH | {FK: "2x"}) == {f"{FK} | fresh install: 1x; upgraded: 2x"}
  33. def test_unlisted_drift_fails_and_listed_drift_passes():
  34. found = {"v0.2.3": {"column a.x | one way", "column a.y | another"}}
  35. assert check.compare(found, {"column a.x | one way", "column a.y | another"}) == []
  36. assert check.compare(found, {"column a.x | one way"}) == ["upgraded from v0.2.3: column a.y | another"]
  37. def test_a_listed_object_that_now_differs_in_another_way_fails():
  38. """The allowlist records how an object drifts, not just that it does: a new
  39. regression on a listed column must not hide behind its old entry."""
  40. found = {"v0.2.3": {"column a.x | fresh install: varchar(200); upgraded: varchar(30)"}}
  41. problems = check.compare(found, {"column a.x | fresh install: varchar(30); upgraded: varchar(30) NULL"})
  42. assert "upgraded from v0.2.3: column a.x | fresh install: varchar(200); upgraded: varchar(30)" in problems
  43. def test_a_listed_line_that_no_longer_drifts_from_any_release_fails():
  44. """So the list only ever shrinks: a fix must remove its line."""
  45. found = {"v0.2.3": {"column a.x | d"}, "v1.2.5.7": set()}
  46. problems = check.compare(found, {"column a.x | d", "column a.fixed | d"})
  47. assert problems == [f"no longer drifts, remove it from {check.ALLOWLIST.name}: column a.fixed | d"]
  48. def test_a_line_drifting_from_only_one_release_is_still_in_use():
  49. found = {"v0.2.3": {"column a.x | d"}, "v1.2.5.7": set()}
  50. assert check.compare(found, {"column a.x | d"}) == []
  51. def test_the_allowlist_skips_comment_lines_but_keeps_a_hash_inside_a_line(tmp_path):
  52. path = tmp_path / "drift.txt"
  53. path.write_text("# header\n\ncolumn a.x | default '#1'\n # indented comment\n", encoding="utf-8")
  54. assert check.read_allowlist(path) == {"column a.x | default '#1'"}
  55. def test_the_database_name_and_driver_are_replaced_in_the_url():
  56. url = "postgresql+asyncpg://u:p@db:5432/postgres?sslmode=disable"
  57. assert check.with_database(url, "x") == "postgresql://u:p@db:5432/x?sslmode=disable"
  58. assert check.with_database(url, "x", "asyncpg") == "postgresql+asyncpg://u:p@db:5432/x?sslmode=disable"