Mock out tests, define expected results, add first test
[mediagoblin.git] / mediagoblin / tests / test_migrations.py
1 # GNU MediaGoblin -- federated, autonomous media hosting
2 # Copyright (C) 2011 Free Software Foundation, Inc
3 #
4 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License as published by
6 # the Free Software Foundation, either version 3 of the License, or
7 # (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU Affero General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16
17
18 from pymongo import Connection
19
20 from mediagoblin.tests.tools import install_fixtures_simple
21 from mediagoblin.db.util import RegisterMigration, MigrationManager
22
23 # This one will get filled with local migrations
24 TEST_MIGRATION_REGISTRY = {}
25 # this one won't get filled
26 TEST_EMPTY_MIGRATION_REGISTRY = {}
27
28 MIGRATION_DB_NAME = u'__mediagoblin_test_migrations__'
29
30
31 ######################
32 # Fake test migrations
33 ######################
34
35 @RegisterMigration(1, TEST_MIGRATION_REGISTRY)
36 def creature_add_magical_powers(database):
37 """
38 Add lists of magical powers.
39
40 This defaults to [], an empty list. Since we haven't declared any
41 magical powers, all existing monsters should
42 """
43 database['creatures'].update(
44 {'magical_powers': {'$exists': False}},
45 {'$set': {'magical_powers': []}})
46
47
48 @RegisterMigration(2, TEST_MIGRATION_REGISTRY)
49 def creature_rename_num_legs_to_num_limbs(database):
50 """
51 It turns out we want to track how many limbs a creature has, not
52 just how many legs. We don't care about the ambiguous distinction
53 between arms/legs currently.
54 """
55 database['creatures'].update(
56 {'num_legs': {'$exists': True}},
57 {'$rename': {'num_legs': 'num_limbs'}})
58
59
60 @RegisterMigration(3, TEST_MIGRATION_REGISTRY)
61 def creature_remove_is_demon(database):
62 """
63 It turns out we don't care much about whether creatures are demons
64 or not.
65 """
66 database['creatures'].update(
67 {'is_demon': {'$exists': True}},
68 {'$unset': {'is_demon': 1}})
69
70
71 @RegisterMigration(4, TEST_MIGRATION_REGISTRY)
72 def level_exits_dict_to_list(database):
73 """
74 For the sake of the indexes we want to write, and because we
75 intend to add more flexible fields, we want to move level exits
76 from like:
77
78 {'big_door': 'castle_level_id',
79 'trapdoor': 'dungeon_level_id'}
80
81 to like:
82
83 [{'name': 'big_door',
84 'exits_to': 'castle_level_id'},
85 {'name': 'trapdoor',
86 'exits_to': 'dungeon_level_id'}]
87 """
88 target = database['levels'].find(
89 {'exits': {'$type': 3}})
90
91 for level in target:
92 new_exits = []
93 for exit_name, exits_to in level['exits'].items():
94 new_exits.append(
95 {'name': exit_name,
96 'exits_to': exits_to})
97
98 level['exits'] = new_exits
99 database['levels'].save(level)
100
101
102 UNMIGRATED_DBDATA = {
103 'creatures': [
104 {'name': 'centipede',
105 'num_legs': 100,
106 'is_demon': False},
107 {'name': 'wolf',
108 'num_legs': 4,
109 'is_demon': False},
110 # don't ask me what a wizardsnake is.
111 {'name': 'wizardsnake',
112 'num_legs': 0,
113 'is_demon': True}],
114 'levels': [
115 {'_id': 'necroplex',
116 'name': 'The Necroplex',
117 'description': 'A complex full of pure deathzone.',
118 'exits': {
119 'deathwell': 'evilstorm',
120 'portal': 'central_park'}},
121 {'_id': 'evilstorm',
122 'name': 'Evil Storm',
123 'description': 'A storm full of pure evil.',
124 'exits': {}}, # you can't escape the evilstorm
125 {'_id': 'central_park',
126 'name': 'Central Park, NY, NY',
127 'description': "New York's friendly Central Park.",
128 'exits': {
129 'portal': 'necroplex'}}]}
130
131
132 EXPECTED_POST_MIGRATION_UNMIGRATED_DBDATA = {
133 'creatures': [
134 {'name': 'centipede',
135 'num_limbs': 100,
136 'magical_powers': []},
137 {'name': 'wolf',
138 'num_limbs': 4,
139 # kept around namely to check that it *isn't* removed!
140 'magical_powers': []},
141 {'name': 'wizardsnake',
142 'num_limbs': 0,
143 'magical_powers': []}],
144 'levels': [
145 {'_id': 'necroplex',
146 'name': 'The Necroplex',
147 'description': 'A complex full of pure deathzone.',
148 'exits': [
149 {'name': 'deathwell',
150 'exits_to': 'evilstorm'},
151 {'name': 'portal',
152 'exits_to': 'central_park'}]},
153 {'_id': 'evilstorm',
154 'name': 'Evil Storm',
155 'description': 'A storm full of pure evil.',
156 'exits': []}, # you can't escape the evilstorm
157 {'_id': 'central_park',
158 'name': 'Central Park, NY, NY',
159 'description': "New York's friendly Central Park.",
160 'exits': [
161 {'name': 'portal',
162 'exits_to': 'necroplex'}]}]}
163
164 # We want to make sure that if we're at migration 3, migration 3
165 # doesn't get re-run.
166
167 SEMI_MIGRATED_DBDATA = {
168 'creatures': [
169 {'name': 'centipede',
170 'num_limbs': 100,
171 'magical_powers': []},
172 {'name': 'wolf',
173 'num_limbs': 4,
174 # kept around namely to check that it *isn't* removed!
175 'is_demon': False,
176 'magical_powers': [
177 'ice_breath', 'death_stare']},
178 {'name': 'wizardsnake',
179 'num_limbs': 0,
180 'magical_powers': [
181 'death_rattle', 'sneaky_stare',
182 'slithery_smoke', 'treacherous_tremors'],
183 'is_demon': True}],
184 'levels': [
185 {'_id': 'necroplex',
186 'name': 'The Necroplex',
187 'description': 'A complex full of pure deathzone.',
188 'exits': {
189 'deathwell': 'evilstorm',
190 'portal': 'central_park'}},
191 {'_id': 'evilstorm',
192 'name': 'Evil Storm',
193 'description': 'A storm full of pure evil.',
194 'exits': {}}, # you can't escape the evilstorm
195 {'_id': 'central_park',
196 'name': 'Central Park, NY, NY',
197 'description': "New York's friendly Central Park.",
198 'exits': {
199 'portal': 'necroplex'}}]}
200
201
202 EXPECTED_POST_MIGRATION_SEMI_MIGRATED_DBDATA = {
203 'creatures': [
204 {'name': 'centipede',
205 'num_limbs': 100,
206 'magical_powers': []},
207 {'name': 'wolf',
208 'num_limbs': 4,
209 # kept around namely to check that it *isn't* removed!
210 'is_demon': False,
211 'magical_powers': [
212 'ice_breath', 'death_stare']},
213 {'name': 'wizardsnake',
214 'num_limbs': 0,
215 'magical_powers': [
216 'death_rattle', 'sneaky_stare',
217 'slithery_smoke', 'treacherous_tremors'],
218 'is_demon': True}],
219 'levels': [
220 {'_id': 'necroplex',
221 'name': 'The Necroplex',
222 'description': 'A complex full of pure deathzone.',
223 'exits': [
224 {'name': 'deathwell',
225 'exits_to': 'evilstorm'},
226 {'name': 'portal',
227 'exits_to': 'central_park'}]},
228 {'_id': 'evilstorm',
229 'name': 'Evil Storm',
230 'description': 'A storm full of pure evil.',
231 'exits': []}, # you can't escape the evilstorm
232 {'_id': 'central_park',
233 'name': 'Central Park, NY, NY',
234 'description': "New York's friendly Central Park.",
235 'exits': [
236 {'name': 'portal',
237 'exits_to': 'necroplex'}]}]}
238
239
240
241 class TestMigrations(object):
242 def setUp(self):
243 # Set up the connection, drop an existing possible database
244 self.connection = Connection()
245 self.connection.drop_database(MIGRATION_DB_NAME)
246 self.db = Connection()[MIGRATION_DB_NAME]
247 self.migration_manager = MigrationManager(
248 self.db, TEST_MIGRATION_REGISTRY)
249 self.empty_migration_manager = MigrationManager(
250 self.db, TEST_EMPTY_MIGRATION_REGISTRY)
251
252 def tearDown(self):
253 self.connection.drop_database(MIGRATION_DB_NAME)
254
255 def test_migrations_registered_and_sorted(self):
256 """
257 Make sure that migrations get registered and are sorted right
258 in the migration manager
259 """
260 assert TEST_MIGRATION_REGISTRY == {
261 1: creature_add_magical_powers,
262 2: creature_rename_num_legs_to_num_limbs,
263 3: creature_remove_is_demon,
264 4: level_exits_dict_to_list}
265 assert self.migration_manager.sorted_migrations == [
266 (1, creature_add_magical_powers),
267 (2, creature_rename_num_legs_to_num_limbs),
268 (3, creature_remove_is_demon),
269 (4, level_exits_dict_to_list)]
270 assert self.empty_migration_manager.sorted_migrations == []
271
272 def test_run_full_migrations(self):
273 """
274 Make sure that running the full migration suite from 0 updates
275 everything
276 """
277 pass
278
279 def test_run_partial_migrations(self):
280 """
281 Make sure that running full migration suite from 3 only runs
282 last migration
283 """
284
285 pass
286
287 def test_migrations_recorded_as_latest(self):
288 """
289 Make sure that if we don't have a migration_status
290 pre-recorded it's marked as the latest
291 """
292 pass
293
294 def test_no_migrations_recorded_as_zero(self):
295 """
296 Make sure that if we don't have a migration_status
297 but there *are* no migrations that it's marked as 0
298 """
299 pass