@@ -0,0 +1,126 @@
1 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ """Tests for navegador.graph.migrations — schema versioning and migration."""
2 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
3 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ from unittest.mock import MagicMock, call
4 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
5 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ import pytest
6 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
7 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ from navegador.graph.migrations import (
8 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ CURRENT_SCHEMA_VERSION,
9 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ _migrations,
10 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ get_schema_version,
11 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ migrate,
12 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ needs_migration,
13 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ set_schema_version,
14 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ )
15 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
16 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
17 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def _mock_store(version=None):
18 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ store = MagicMock()
19 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if version is None:
20 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ store.query.return_value = MagicMock(result_set=[])
21 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ else:
22 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ store.query.return_value = MagicMock(result_set=[[version]])
23 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ return store
24 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
25 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
26 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ # ── get_schema_version ───────────────────────────────────────────────────────
27 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
28 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ class TestGetSchemaVersion:
29 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def test_returns_zero_for_empty_graph(self):
30 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ store = _mock_store(version=None)
31 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert get_schema_version(store) == 0
32 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
33 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def test_returns_zero_for_null_version(self):
34 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ store = MagicMock()
35 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ store.query.return_value = MagicMock(result_set=[[None]])
36 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert get_schema_version(store) == 0
37 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
38 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def test_returns_stored_version(self):
39 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ store = _mock_store(version=2)
40 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert get_schema_version(store) == 2
41 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
42 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
43 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ # ── set_schema_version ──────────────────────────────────────────────────────
44 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
45 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ class TestSetSchemaVersion:
46 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def test_calls_query_with_merge(self):
47 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ store = MagicMock()
48 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ set_schema_version(store, 3)
49 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ store.query.assert_called_once()
50 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ cypher = store.query.call_args[0][0]
51 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert "MERGE" in cypher
52 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert store.query.call_args[0][1]["version"] == 3
53 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
54 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
55 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ # ── needs_migration ──────────────────────────────────────────────────────────
56 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
57 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ class TestNeedsMigration:
58 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def test_true_when_behind(self):
59 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ store = _mock_store(version=0)
60 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert needs_migration(store) is True
61 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
62 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def test_false_when_current(self):
63 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ store = _mock_store(version=CURRENT_SCHEMA_VERSION)
64 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert needs_migration(store) is False
65 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
66 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
67 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ # ── migrate ──────────────────────────────────────────────────────────────────
68 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
69 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ class TestMigrate:
70 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def test_applies_all_migrations_from_zero(self):
71 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ call_log = []
72 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
73 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def track_query(cypher, params=None):
74 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ call_log.append(cypher)
75 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ result = MagicMock()
76 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ # get_schema_version query returns no rows initially
77 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ if "Meta" in cypher and "RETURN" in cypher:
78 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ result.result_set = []
79 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ else:
80 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ result.result_set = []
81 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ return result
82 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
83 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ store = MagicMock()
84 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ store.query.side_effect = track_query
85 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
86 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ applied = migrate(store)
87 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert applied == list(range(1, CURRENT_SCHEMA_VERSION + 1))
88 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
89 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def test_no_op_when_already_current(self):
90 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ store = _mock_store(version=CURRENT_SCHEMA_VERSION)
91 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ applied = migrate(store)
92 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert applied == []
93 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
94 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def test_raises_on_missing_migration(self):
95 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ # Temporarily remove a migration to trigger the RuntimeError
96 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ saved = _migrations.pop(0)
97 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ try:
98 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ store = _mock_store(version=None)
99 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ with pytest.raises(RuntimeError, match="No migration registered"):
100 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ migrate(store)
101 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ finally:
102 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ _migrations[0] = saved
103 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
104 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
105 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ # ── migrations registry ─────────────────────────────────────────────────────
106 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
107 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ class TestMigrationsRegistry:
108 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def test_has_migration_for_each_version(self):
109 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ for v in range(CURRENT_SCHEMA_VERSION):
110 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert v in _migrations, f"Missing migration for version {v} -> {v + 1}"
111 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
112 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def test_current_version_is_positive(self):
113 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert CURRENT_SCHEMA_VERSION > 0
114 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
115 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def test_migration_0_to_1_runs(self):
116 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ store = MagicMock()
117 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ store.query.return_value = MagicMock(result_set=[])
118 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ _migrations[0](store)
119 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+
120 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ def test_migration_1_to_2_sets_content_hash(self):
121 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ store = MagicMock()
122 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ store.query.return_value = MagicMock(result_set=[])
123 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ _migrations[1](store)
124 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ store.query.assert_called_once()
125 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ cypher = store.query.call_args[0][0]
126 { copied = false; pop = false }, 1000)" :class="copied && 'copied'">Copy link Copied!
+ assert "content_hash" in cypher