1 # GNU MediaGoblin -- federated, autonomous media hosting
2 # Copyright (C) 2011 Free Software Foundation, Inc
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.
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.
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/>.
18 from pymongo
import Connection
20 from mediagoblin
.tests
.tools
import install_fixtures_simple
21 from mediagoblin
.db
.util
import RegisterMigration
, MigrationManager
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
= {}
28 MIGRATION_DB_NAME
= u
'__mediagoblin_test_migrations__'
31 ######################
32 # Fake test migrations
33 ######################
35 @RegisterMigration(1, TEST_MIGRATION_REGISTRY
)
36 def creature_add_magical_powers(database
):
38 Add lists of magical powers.
40 This defaults to [], an empty list. Since we haven't declared any
41 magical powers, all existing monsters should
43 database
['creatures'].update(
44 {'magical_powers': {'$exists': False}},
45 {'$set': {'magical_powers': []}})
48 @RegisterMigration(2, TEST_MIGRATION_REGISTRY
)
49 def creature_rename_num_legs_to_num_limbs(database
):
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.
55 database
['creatures'].update(
56 {'num_legs': {'$exists': True}},
57 {'$rename': {'num_legs': 'num_limbs'}})
60 @RegisterMigration(3, TEST_MIGRATION_REGISTRY
)
61 def creature_remove_is_demon(database
):
63 It turns out we don't care much about whether creatures are demons
66 database
['creatures'].update(
67 {'is_demon': {'$exists': True}},
68 {'$unset': {'is_demon': 1}})
71 @RegisterMigration(4, TEST_MIGRATION_REGISTRY
)
72 def level_exits_dict_to_list(database
):
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
78 {'big_door': 'castle_level_id',
79 'trapdoor': 'dungeon_level_id'}
84 'exits_to': 'castle_level_id'},
86 'exits_to': 'dungeon_level_id'}]
88 target
= database
['levels'].find(
89 {'exits': {'$type': 3}})
93 for exit_name
, exits_to
in level
['exits'].items():
96 'exits_to': exits_to
})
98 level
['exits'] = new_exits
99 database
['levels'].save(level
)
102 UNMIGRATED_DBDATA
= {
104 {'name': 'centipede',
110 # don't ask me what a wizardsnake is.
111 {'name': 'wizardsnake',
116 'name': 'The Necroplex',
117 'description': 'A complex full of pure deathzone.',
119 'deathwell': 'evilstorm',
120 'portal': 'central_park'}},
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.",
129 'portal': 'necroplex'}}]}
132 EXPECTED_POST_MIGRATION_UNMIGRATED_DBDATA
= {
134 {'name': 'centipede',
136 'magical_powers': []},
139 # kept around namely to check that it *isn't* removed!
140 'magical_powers': []},
141 {'name': 'wizardsnake',
143 'magical_powers': []}],
146 'name': 'The Necroplex',
147 'description': 'A complex full of pure deathzone.',
149 {'name': 'deathwell',
150 'exits_to': 'evilstorm'},
152 'exits_to': 'central_park'}]},
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.",
162 'exits_to': 'necroplex'}]}]}
164 # We want to make sure that if we're at migration 3, migration 3
165 # doesn't get re-run.
167 SEMI_MIGRATED_DBDATA
= {
169 {'name': 'centipede',
171 'magical_powers': []},
174 # kept around namely to check that it *isn't* removed!
177 'ice_breath', 'death_stare']},
178 {'name': 'wizardsnake',
181 'death_rattle', 'sneaky_stare',
182 'slithery_smoke', 'treacherous_tremors'],
186 'name': 'The Necroplex',
187 'description': 'A complex full of pure deathzone.',
189 'deathwell': 'evilstorm',
190 'portal': 'central_park'}},
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.",
199 'portal': 'necroplex'}}]}
202 EXPECTED_POST_MIGRATION_SEMI_MIGRATED_DBDATA
= {
204 {'name': 'centipede',
206 'magical_powers': []},
209 # kept around namely to check that it *isn't* removed!
212 'ice_breath', 'death_stare']},
213 {'name': 'wizardsnake',
216 'death_rattle', 'sneaky_stare',
217 'slithery_smoke', 'treacherous_tremors'],
221 'name': 'The Necroplex',
222 'description': 'A complex full of pure deathzone.',
224 {'name': 'deathwell',
225 'exits_to': 'evilstorm'},
227 'exits_to': 'central_park'}]},
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.",
237 'exits_to': 'necroplex'}]}]}
241 class TestMigrations(object):
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
)
253 self
.connection
.drop_database(MIGRATION_DB_NAME
)
255 def test_migrations_registered_and_sorted(self
):
257 Make sure that migrations get registered and are sorted right
258 in the migration manager
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
== []
272 def test_run_full_migrations(self
):
274 Make sure that running the full migration suite from 0 updates
279 def test_run_partial_migrations(self
):
281 Make sure that running full migration suite from 3 only runs
287 def test_migrations_recorded_as_latest(self
):
289 Make sure that if we don't have a migration_status
290 pre-recorded it's marked as the latest
294 def test_no_migrations_recorded_as_zero(self
):
296 Make sure that if we don't have a migration_status
297 but there *are* no migrations that it's marked as 0