Commit | Line | Data |
---|---|---|
0f10058f | 1 | # GNU MediaGoblin -- federated, autonomous media hosting |
89694d6d | 2 | # Copyright (C) 2012, 2012 MediaGoblin contributors. See AUTHORS. |
0f10058f CAW |
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 | ||
386c9c7c | 17 | import six |
88ed537a BP |
18 | import pytest |
19 | ||
20 | pytestmark = pytest.mark.skipif(six.PY3, reason='needs sqlalchemy.migrate') | |
21 | ||
22 | import copy | |
386c9c7c | 23 | |
0f10058f | 24 | from sqlalchemy import ( |
5de0f4da | 25 | Table, Column, MetaData, Index, |
89694d6d | 26 | Integer, Float, Unicode, UnicodeText, DateTime, Boolean, |
c7fa585b | 27 | ForeignKey, UniqueConstraint, PickleType, VARCHAR) |
d74a9483 | 28 | from sqlalchemy.orm import sessionmaker, relationship |
0f10058f | 29 | from sqlalchemy.ext.declarative import declarative_base |
89694d6d | 30 | from sqlalchemy.sql import select, insert |
88ed537a BP |
31 | if six.PY2: |
32 | from migrate import changeset | |
0f10058f | 33 | |
39dc3bf8 | 34 | from mediagoblin.db.base import GMGTableBase |
c130e3ee | 35 | from mediagoblin.db.migration_tools import MigrationManager, RegisterMigration |
35a24fc2 | 36 | from mediagoblin.tools.common import CollectingPrinter |
0f10058f CAW |
37 | |
38 | ||
39 | # This one will get filled with local migrations | |
40 | FULL_MIGRATIONS = {} | |
41 | ||
42 | ||
43 | ####################################################### | |
44 | # Migration set 1: Define initial models, no migrations | |
45 | ####################################################### | |
46 | ||
47 | Base1 = declarative_base(cls=GMGTableBase) | |
48 | ||
49 | class Creature1(Base1): | |
50 | __tablename__ = "creature" | |
51 | ||
52 | id = Column(Integer, primary_key=True) | |
53 | name = Column(Unicode, unique=True, nullable=False, index=True) | |
54 | num_legs = Column(Integer, nullable=False) | |
55 | is_demon = Column(Boolean) | |
56 | ||
57 | class Level1(Base1): | |
58 | __tablename__ = "level" | |
59 | ||
64d28064 | 60 | id = Column(Unicode, primary_key=True) |
caed154a | 61 | name = Column(Unicode) |
40f0996a | 62 | description = Column(Unicode) |
0f10058f CAW |
63 | exits = Column(PickleType) |
64 | ||
65 | SET1_MODELS = [Creature1, Level1] | |
66 | ||
860fa806 | 67 | FOUNDATIONS = {Creature1:[{'name':u'goblin','num_legs':2,'is_demon':False}, |
84c1cd7c | 68 | {'name':u'cerberus','num_legs':4,'is_demon':True}] |
69 | } | |
63c3ca28 | 70 | |
a5e03db6 | 71 | SET1_MIGRATIONS = {} |
0f10058f CAW |
72 | |
73 | ####################################################### | |
74 | # Migration set 2: A few migrations and new model | |
75 | ####################################################### | |
76 | ||
77 | Base2 = declarative_base(cls=GMGTableBase) | |
78 | ||
79 | class Creature2(Base2): | |
80 | __tablename__ = "creature" | |
81 | ||
82 | id = Column(Integer, primary_key=True) | |
83 | name = Column(Unicode, unique=True, nullable=False, index=True) | |
84 | num_legs = Column(Integer, nullable=False) | |
d74a9483 | 85 | magical_powers = relationship("CreaturePower2") |
0f10058f CAW |
86 | |
87 | class CreaturePower2(Base2): | |
88 | __tablename__ = "creature_power" | |
89 | ||
90 | id = Column(Integer, primary_key=True) | |
91 | creature = Column( | |
92 | Integer, ForeignKey('creature.id'), nullable=False) | |
93 | name = Column(Unicode) | |
94 | description = Column(Unicode) | |
89694d6d | 95 | hitpower = Column(Integer, nullable=False) |
0f10058f CAW |
96 | |
97 | class Level2(Base2): | |
98 | __tablename__ = "level" | |
99 | ||
64d28064 | 100 | id = Column(Unicode, primary_key=True) |
0f10058f | 101 | name = Column(Unicode) |
40f0996a | 102 | description = Column(Unicode) |
0f10058f CAW |
103 | |
104 | class LevelExit2(Base2): | |
105 | __tablename__ = "level_exit" | |
106 | ||
107 | id = Column(Integer, primary_key=True) | |
108 | name = Column(Unicode) | |
109 | from_level = Column( | |
64d28064 | 110 | Unicode, ForeignKey('level.id'), nullable=False) |
0f10058f | 111 | to_level = Column( |
64d28064 | 112 | Unicode, ForeignKey('level.id'), nullable=False) |
0f10058f CAW |
113 | |
114 | SET2_MODELS = [Creature2, CreaturePower2, Level2, LevelExit2] | |
115 | ||
116 | ||
117 | @RegisterMigration(1, FULL_MIGRATIONS) | |
118 | def creature_remove_is_demon(db_conn): | |
356654de CAW |
119 | """ |
120 | Remove the is_demon field from the creature model. We don't need | |
121 | it! | |
122 | """ | |
7f3ec607 CAW |
123 | # :( Commented out 'cuz of: |
124 | # http://code.google.com/p/sqlalchemy-migrate/issues/detail?id=143&thanks=143&ts=1327882242 | |
125 | ||
78d17b80 CAW |
126 | # metadata = MetaData(bind=db_conn.bind) |
127 | # creature_table = Table( | |
128 | # 'creature', metadata, | |
129 | # autoload=True, autoload_with=db_conn.bind) | |
130 | # creature_table.drop_column('is_demon') | |
131 | pass | |
132 | ||
0f10058f CAW |
133 | |
134 | @RegisterMigration(2, FULL_MIGRATIONS) | |
135 | def creature_powers_new_table(db_conn): | |
356654de CAW |
136 | """ |
137 | Add a new table for creature powers. Nothing needs to go in it | |
138 | yet though as there wasn't anything that previously held this | |
139 | information | |
140 | """ | |
e920b968 | 141 | metadata = MetaData(bind=db_conn.bind) |
78d17b80 CAW |
142 | |
143 | # We have to access the creature table so sqlalchemy can make the | |
144 | # foreign key relationship | |
145 | creature_table = Table( | |
146 | 'creature', metadata, | |
147 | autoload=True, autoload_with=db_conn.bind) | |
148 | ||
89694d6d CAW |
149 | creature_powers = Table( |
150 | 'creature_power', metadata, | |
151 | Column('id', Integer, primary_key=True), | |
152 | Column('creature', | |
153 | Integer, ForeignKey('creature.id'), nullable=False), | |
154 | Column('name', Unicode), | |
155 | Column('description', Unicode), | |
156 | Column('hitpower', Integer, nullable=False)) | |
e920b968 | 157 | metadata.create_all(db_conn.bind) |
89694d6d | 158 | |
0f10058f CAW |
159 | |
160 | @RegisterMigration(3, FULL_MIGRATIONS) | |
161 | def level_exits_new_table(db_conn): | |
356654de CAW |
162 | """ |
163 | Make a new table for level exits and move the previously pickled | |
164 | stuff over to here (then drop the old unneeded table) | |
165 | """ | |
89694d6d CAW |
166 | # First, create the table |
167 | # ----------------------- | |
e920b968 | 168 | metadata = MetaData(bind=db_conn.bind) |
78d17b80 CAW |
169 | |
170 | # Minimal representation of level table. | |
171 | # Not auto-introspecting here because of pickle table. I'm not | |
172 | # sure sqlalchemy can auto-introspect pickle columns. | |
173 | levels = Table( | |
174 | 'level', metadata, | |
175 | Column('id', Unicode, primary_key=True), | |
176 | Column('name', Unicode), | |
177 | Column('description', Unicode), | |
178 | Column('exits', PickleType)) | |
179 | ||
89694d6d CAW |
180 | level_exits = Table( |
181 | 'level_exit', metadata, | |
182 | Column('id', Integer, primary_key=True), | |
183 | Column('name', Unicode), | |
184 | Column('from_level', | |
78d17b80 | 185 | Unicode, ForeignKey('level.id'), nullable=False), |
89694d6d | 186 | Column('to_level', |
78d17b80 | 187 | Unicode, ForeignKey('level.id'), nullable=False)) |
e920b968 | 188 | metadata.create_all(db_conn.bind) |
89694d6d CAW |
189 | |
190 | # And now, convert all the old exit pickles to new level exits | |
191 | # ------------------------------------------------------------ | |
192 | ||
89694d6d CAW |
193 | # query over and insert |
194 | result = db_conn.execute( | |
195 | select([levels], levels.c.exits!=None)) | |
196 | ||
197 | for level in result: | |
78d17b80 | 198 | |
386c9c7c | 199 | for exit_name, to_level in six.iteritems(level['exits']): |
78d17b80 CAW |
200 | # Insert the level exit |
201 | db_conn.execute( | |
202 | level_exits.insert().values( | |
203 | name=exit_name, | |
204 | from_level=level.id, | |
205 | to_level=to_level)) | |
89694d6d CAW |
206 | |
207 | # Finally, drop the old level exits pickle table | |
208 | # ---------------------------------------------- | |
473e0605 | 209 | levels.drop_column('exits') |
0f10058f CAW |
210 | |
211 | ||
212 | # A hack! At this point we freeze-fame and get just a partial list of | |
213 | # migrations | |
214 | ||
129c36be | 215 | SET2_MIGRATIONS = copy.copy(FULL_MIGRATIONS) |
0f10058f CAW |
216 | |
217 | ####################################################### | |
218 | # Migration set 3: Final migrations | |
219 | ####################################################### | |
220 | ||
221 | Base3 = declarative_base(cls=GMGTableBase) | |
222 | ||
223 | class Creature3(Base3): | |
224 | __tablename__ = "creature" | |
225 | ||
226 | id = Column(Integer, primary_key=True) | |
227 | name = Column(Unicode, unique=True, nullable=False, index=True) | |
228 | num_limbs= Column(Integer, nullable=False) | |
0f3526c6 | 229 | magical_powers = relationship("CreaturePower3") |
0f10058f CAW |
230 | |
231 | class CreaturePower3(Base3): | |
232 | __tablename__ = "creature_power" | |
233 | ||
234 | id = Column(Integer, primary_key=True) | |
235 | creature = Column( | |
236 | Integer, ForeignKey('creature.id'), nullable=False, index=True) | |
237 | name = Column(Unicode) | |
238 | description = Column(Unicode) | |
89694d6d | 239 | hitpower = Column(Float, nullable=False) |
0f10058f CAW |
240 | |
241 | class Level3(Base3): | |
242 | __tablename__ = "level" | |
243 | ||
64d28064 | 244 | id = Column(Unicode, primary_key=True) |
0f10058f | 245 | name = Column(Unicode) |
40f0996a | 246 | description = Column(Unicode) |
0f10058f CAW |
247 | |
248 | class LevelExit3(Base3): | |
249 | __tablename__ = "level_exit" | |
250 | ||
251 | id = Column(Integer, primary_key=True) | |
252 | name = Column(Unicode) | |
253 | from_level = Column( | |
64d28064 | 254 | Unicode, ForeignKey('level.id'), nullable=False, index=True) |
0f10058f | 255 | to_level = Column( |
64d28064 | 256 | Unicode, ForeignKey('level.id'), nullable=False, index=True) |
0f10058f CAW |
257 | |
258 | ||
259 | SET3_MODELS = [Creature3, CreaturePower3, Level3, LevelExit3] | |
caed154a | 260 | SET3_MIGRATIONS = FULL_MIGRATIONS |
0f10058f CAW |
261 | |
262 | ||
263 | @RegisterMigration(4, FULL_MIGRATIONS) | |
264 | def creature_num_legs_to_num_limbs(db_conn): | |
d6cdf64b CAW |
265 | """ |
266 | Turns out we're tracking all sorts of limbs, not "legs" | |
267 | specifically. Humans would be 4 here, for instance. So we | |
268 | renamed the column. | |
269 | """ | |
e920b968 | 270 | metadata = MetaData(bind=db_conn.bind) |
248b5061 CAW |
271 | creature_table = Table( |
272 | 'creature', metadata, | |
e920b968 | 273 | autoload=True, autoload_with=db_conn.bind) |
94eff10d | 274 | creature_table.c.num_legs.alter(name=u"num_limbs") |
248b5061 | 275 | |
0f10058f CAW |
276 | |
277 | @RegisterMigration(5, FULL_MIGRATIONS) | |
278 | def level_exit_index_from_and_to_level(db_conn): | |
d6cdf64b CAW |
279 | """ |
280 | Index the from and to levels of the level exit table. | |
281 | """ | |
e920b968 | 282 | metadata = MetaData(bind=db_conn.bind) |
248b5061 CAW |
283 | level_exit = Table( |
284 | 'level_exit', metadata, | |
e920b968 | 285 | autoload=True, autoload_with=db_conn.bind) |
78d17b80 CAW |
286 | Index('ix_level_exit_from_level', |
287 | level_exit.c.from_level).create(db_conn.bind) | |
288 | Index('ix_level_exit_to_level', | |
289 | level_exit.c.to_level).create(db_conn.bind) | |
248b5061 | 290 | |
0f10058f CAW |
291 | |
292 | @RegisterMigration(6, FULL_MIGRATIONS) | |
293 | def creature_power_index_creature(db_conn): | |
d6cdf64b CAW |
294 | """ |
295 | Index our foreign key relationship to the creatures | |
296 | """ | |
e920b968 | 297 | metadata = MetaData(bind=db_conn.bind) |
248b5061 CAW |
298 | creature_power = Table( |
299 | 'creature_power', metadata, | |
e920b968 | 300 | autoload=True, autoload_with=db_conn.bind) |
78d17b80 CAW |
301 | Index('ix_creature_power_creature', |
302 | creature_power.c.creature).create(db_conn.bind) | |
248b5061 | 303 | |
89694d6d CAW |
304 | |
305 | @RegisterMigration(7, FULL_MIGRATIONS) | |
306 | def creature_power_hitpower_to_float(db_conn): | |
d6cdf64b CAW |
307 | """ |
308 | Convert hitpower column on creature power table from integer to | |
309 | float. | |
310 | ||
311 | Turns out we want super precise values of how much hitpower there | |
312 | really is. | |
313 | """ | |
e920b968 | 314 | metadata = MetaData(bind=db_conn.bind) |
78d17b80 CAW |
315 | |
316 | # We have to access the creature table so sqlalchemy can make the | |
317 | # foreign key relationship | |
318 | creature_table = Table( | |
319 | 'creature', metadata, | |
320 | autoload=True, autoload_with=db_conn.bind) | |
321 | ||
248b5061 CAW |
322 | creature_power = Table( |
323 | 'creature_power', metadata, | |
78d17b80 CAW |
324 | Column('id', Integer, primary_key=True), |
325 | Column('creature', Integer, | |
326 | ForeignKey('creature.id'), nullable=False, | |
327 | index=True), | |
328 | Column('name', Unicode), | |
329 | Column('description', Unicode), | |
330 | Column('hitpower', Integer, nullable=False)) | |
331 | ||
248b5061 | 332 | creature_power.c.hitpower.alter(type=Float) |
64d28064 CAW |
333 | |
334 | ||
52539aca CAW |
335 | @RegisterMigration(8, FULL_MIGRATIONS) |
336 | def creature_power_name_creature_unique(db_conn): | |
337 | """ | |
338 | Add a unique constraint to name and creature on creature_power. | |
339 | ||
340 | We don't want multiple creature powers with the same name per creature! | |
341 | """ | |
342 | # Note: We don't actually check to see if this constraint is set | |
343 | # up because at present there's no way to do so in sqlalchemy :\ | |
344 | ||
345 | metadata = MetaData(bind=db_conn.bind) | |
346 | ||
347 | creature_power = Table( | |
348 | 'creature_power', metadata, | |
349 | autoload=True, autoload_with=db_conn.bind) | |
350 | ||
351 | cons = changeset.constraint.UniqueConstraint( | |
352 | 'name', 'creature', table=creature_power) | |
353 | ||
354 | cons.create() | |
355 | ||
356 | ||
64d28064 | 357 | def _insert_migration1_objects(session): |
356654de CAW |
358 | """ |
359 | Test objects to insert for the first set of things | |
360 | """ | |
64d28064 CAW |
361 | # Insert creatures |
362 | session.add_all( | |
94eff10d | 363 | [Creature1(name=u'centipede', |
64d28064 CAW |
364 | num_legs=100, |
365 | is_demon=False), | |
94eff10d | 366 | Creature1(name=u'wolf', |
64d28064 CAW |
367 | num_legs=4, |
368 | is_demon=False), | |
369 | # don't ask me what a wizardsnake is. | |
94eff10d | 370 | Creature1(name=u'wizardsnake', |
64d28064 CAW |
371 | num_legs=0, |
372 | is_demon=True)]) | |
373 | ||
d74a9483 | 374 | # Insert levels |
64d28064 | 375 | session.add_all( |
94eff10d CAW |
376 | [Level1(id=u'necroplex', |
377 | name=u'The Necroplex', | |
378 | description=u'A complex full of pure deathzone.', | |
64d28064 | 379 | exits={ |
c7dfd4fb BS |
380 | u'deathwell': u'evilstorm', |
381 | u'portal': u'central_park'}), | |
94eff10d CAW |
382 | Level1(id=u'evilstorm', |
383 | name=u'Evil Storm', | |
384 | description=u'A storm full of pure evil.', | |
64d28064 | 385 | exits={}), # you can't escape the evilstorm |
caed154a | 386 | Level1(id=u'central_park', |
94eff10d CAW |
387 | name=u'Central Park, NY, NY', |
388 | description=u"New York's friendly Central Park.", | |
64d28064 | 389 | exits={ |
c7dfd4fb | 390 | u'portal': u'necroplex'})]) |
64d28064 CAW |
391 | |
392 | session.commit() | |
393 | ||
d74a9483 CAW |
394 | |
395 | def _insert_migration2_objects(session): | |
356654de CAW |
396 | """ |
397 | Test objects to insert for the second set of things | |
398 | """ | |
d74a9483 CAW |
399 | # Insert creatures |
400 | session.add_all( | |
401 | [Creature2( | |
94eff10d | 402 | name=u'centipede', |
d74a9483 CAW |
403 | num_legs=100), |
404 | Creature2( | |
94eff10d | 405 | name=u'wolf', |
d74a9483 CAW |
406 | num_legs=4, |
407 | magical_powers = [ | |
408 | CreaturePower2( | |
94eff10d CAW |
409 | name=u"ice breath", |
410 | description=u"A blast of icy breath!", | |
d74a9483 CAW |
411 | hitpower=20), |
412 | CreaturePower2( | |
94eff10d CAW |
413 | name=u"death stare", |
414 | description=u"A frightening stare, for sure!", | |
d74a9483 CAW |
415 | hitpower=45)]), |
416 | Creature2( | |
94eff10d | 417 | name=u'wizardsnake', |
d74a9483 CAW |
418 | num_legs=0, |
419 | magical_powers=[ | |
420 | CreaturePower2( | |
94eff10d CAW |
421 | name=u'death_rattle', |
422 | description=u'A rattle... of DEATH!', | |
d74a9483 CAW |
423 | hitpower=1000), |
424 | CreaturePower2( | |
94eff10d | 425 | name=u'sneaky_stare', |
caed154a | 426 | description=u"The sneakiest stare you've ever seen!", |
d74a9483 CAW |
427 | hitpower=300), |
428 | CreaturePower2( | |
94eff10d CAW |
429 | name=u'slithery_smoke', |
430 | description=u"A blast of slithery, slithery smoke.", | |
d74a9483 CAW |
431 | hitpower=10), |
432 | CreaturePower2( | |
94eff10d CAW |
433 | name=u'treacherous_tremors', |
434 | description=u"The ground shakes beneath footed animals!", | |
d74a9483 CAW |
435 | hitpower=0)])]) |
436 | ||
437 | # Insert levels | |
438 | session.add_all( | |
94eff10d CAW |
439 | [Level2(id=u'necroplex', |
440 | name=u'The Necroplex', | |
441 | description=u'A complex full of pure deathzone.'), | |
442 | Level2(id=u'evilstorm', | |
443 | name=u'Evil Storm', | |
444 | description=u'A storm full of pure evil.', | |
d74a9483 | 445 | exits=[]), # you can't escape the evilstorm |
caed154a | 446 | Level2(id=u'central_park', |
94eff10d CAW |
447 | name=u'Central Park, NY, NY', |
448 | description=u"New York's friendly Central Park.")]) | |
d74a9483 CAW |
449 | |
450 | # necroplex exits | |
451 | session.add_all( | |
94eff10d CAW |
452 | [LevelExit2(name=u'deathwell', |
453 | from_level=u'necroplex', | |
454 | to_level=u'evilstorm'), | |
455 | LevelExit2(name=u'portal', | |
456 | from_level=u'necroplex', | |
457 | to_level=u'central_park')]) | |
d74a9483 CAW |
458 | |
459 | # there are no evilstorm exits because there is no exit from the | |
460 | # evilstorm | |
461 | ||
462 | # central park exits | |
463 | session.add_all( | |
94eff10d CAW |
464 | [LevelExit2(name=u'portal', |
465 | from_level=u'central_park', | |
466 | to_level=u'necroplex')]) | |
d74a9483 CAW |
467 | |
468 | session.commit() | |
dc3db468 CAW |
469 | |
470 | ||
471 | def _insert_migration3_objects(session): | |
472 | """ | |
473 | Test objects to insert for the third set of things | |
474 | """ | |
475 | # Insert creatures | |
476 | session.add_all( | |
477 | [Creature3( | |
94eff10d | 478 | name=u'centipede', |
dc3db468 CAW |
479 | num_limbs=100), |
480 | Creature3( | |
94eff10d | 481 | name=u'wolf', |
dc3db468 CAW |
482 | num_limbs=4, |
483 | magical_powers = [ | |
484 | CreaturePower3( | |
94eff10d CAW |
485 | name=u"ice breath", |
486 | description=u"A blast of icy breath!", | |
dc3db468 CAW |
487 | hitpower=20.0), |
488 | CreaturePower3( | |
94eff10d CAW |
489 | name=u"death stare", |
490 | description=u"A frightening stare, for sure!", | |
dc3db468 CAW |
491 | hitpower=45.0)]), |
492 | Creature3( | |
94eff10d | 493 | name=u'wizardsnake', |
dc3db468 CAW |
494 | num_limbs=0, |
495 | magical_powers=[ | |
496 | CreaturePower3( | |
94eff10d CAW |
497 | name=u'death_rattle', |
498 | description=u'A rattle... of DEATH!', | |
dc3db468 CAW |
499 | hitpower=1000.0), |
500 | CreaturePower3( | |
94eff10d | 501 | name=u'sneaky_stare', |
caed154a | 502 | description=u"The sneakiest stare you've ever seen!", |
dc3db468 CAW |
503 | hitpower=300.0), |
504 | CreaturePower3( | |
94eff10d CAW |
505 | name=u'slithery_smoke', |
506 | description=u"A blast of slithery, slithery smoke.", | |
dc3db468 CAW |
507 | hitpower=10.0), |
508 | CreaturePower3( | |
94eff10d CAW |
509 | name=u'treacherous_tremors', |
510 | description=u"The ground shakes beneath footed animals!", | |
dc3db468 CAW |
511 | hitpower=0.0)])], |
512 | # annnnnd one more to test a floating point hitpower | |
513 | Creature3( | |
94eff10d | 514 | name=u'deity', |
dc3db468 | 515 | numb_limbs=30, |
caed154a | 516 | magical_powers=[ |
dc3db468 | 517 | CreaturePower3( |
94eff10d CAW |
518 | name=u'smite', |
519 | description=u'Smitten by holy wrath!', | |
caed154a | 520 | hitpower=9999.9)])) |
dc3db468 CAW |
521 | |
522 | # Insert levels | |
523 | session.add_all( | |
94eff10d CAW |
524 | [Level3(id=u'necroplex', |
525 | name=u'The Necroplex', | |
526 | description=u'A complex full of pure deathzone.'), | |
527 | Level3(id=u'evilstorm', | |
528 | name=u'Evil Storm', | |
529 | description=u'A storm full of pure evil.', | |
dc3db468 | 530 | exits=[]), # you can't escape the evilstorm |
caed154a | 531 | Level3(id=u'central_park', |
94eff10d CAW |
532 | name=u'Central Park, NY, NY', |
533 | description=u"New York's friendly Central Park.")]) | |
dc3db468 CAW |
534 | |
535 | # necroplex exits | |
536 | session.add_all( | |
94eff10d CAW |
537 | [LevelExit3(name=u'deathwell', |
538 | from_level=u'necroplex', | |
539 | to_level=u'evilstorm'), | |
540 | LevelExit3(name=u'portal', | |
541 | from_level=u'necroplex', | |
542 | to_level=u'central_park')]) | |
dc3db468 CAW |
543 | |
544 | # there are no evilstorm exits because there is no exit from the | |
545 | # evilstorm | |
546 | ||
547 | # central park exits | |
548 | session.add_all( | |
94eff10d CAW |
549 | [LevelExit3(name=u'portal', |
550 | from_level=u'central_park', | |
551 | to_level=u'necroplex')]) | |
dc3db468 CAW |
552 | |
553 | session.commit() | |
554 | ||
09e2c487 CAW |
555 | def create_test_engine(): |
556 | from sqlalchemy import create_engine | |
557 | engine = create_engine('sqlite:///:memory:', echo=False) | |
40f0996a CAW |
558 | Session = sessionmaker(bind=engine) |
559 | return engine, Session | |
560 | ||
561 | ||
caed154a CAW |
562 | def assert_col_type(column, this_class): |
563 | assert isinstance(column.type, this_class) | |
09e2c487 CAW |
564 | |
565 | ||
e8e52b3a CAW |
566 | def _get_level3_exits(session, level): |
567 | return dict( | |
568 | [(level_exit.name, level_exit.to_level) | |
569 | for level_exit in | |
570 | session.query(LevelExit3).filter_by(from_level=level.id)]) | |
571 | ||
572 | ||
09e2c487 CAW |
573 | def test_set1_to_set3(): |
574 | # Create / connect to database | |
94eff10d CAW |
575 | # ---------------------------- |
576 | ||
40f0996a | 577 | engine, Session = create_test_engine() |
94eff10d | 578 | |
09e2c487 | 579 | # Create tables by migrating on empty initial set |
94eff10d CAW |
580 | # ----------------------------------------------- |
581 | ||
ecb4cc89 | 582 | printer = CollectingPrinter() |
40f0996a | 583 | migration_manager = MigrationManager( |
860fa806 | 584 | u'__main__', SET1_MODELS, FOUNDATIONS, SET1_MIGRATIONS, Session(), |
40f0996a | 585 | printer) |
94eff10d | 586 | |
40f0996a CAW |
587 | # Check latest migration and database current migration |
588 | assert migration_manager.latest_migration == 0 | |
589 | assert migration_manager.database_current_migration == None | |
590 | ||
591 | result = migration_manager.init_or_migrate() | |
94eff10d | 592 | |
40f0996a CAW |
593 | # Make sure output was "inited" |
594 | assert result == u'inited' | |
595 | # Check output | |
94eff10d | 596 | assert printer.combined_string == ( |
63c3ca28 | 597 | "-> Initializing main mediagoblin tables... done.\n" + \ |
598 | " + Laying foundations for Creature1 table\n" ) | |
40f0996a CAW |
599 | # Check version in database |
600 | assert migration_manager.latest_migration == 0 | |
601 | assert migration_manager.database_current_migration == 0 | |
09e2c487 | 602 | |
860fa806 | 603 | |
09e2c487 | 604 | # Install the initial set |
94eff10d CAW |
605 | # ----------------------- |
606 | ||
40f0996a | 607 | _insert_migration1_objects(Session()) |
94eff10d | 608 | |
40f0996a CAW |
609 | # Try to "re-migrate" with same manager settings... nothing should happen |
610 | migration_manager = MigrationManager( | |
860fa806 | 611 | u'__main__', SET1_MODELS, FOUNDATIONS, SET1_MIGRATIONS, |
63c3ca28 | 612 | Session(), printer) |
40f0996a CAW |
613 | assert migration_manager.init_or_migrate() == None |
614 | ||
09e2c487 | 615 | # Check version in database |
40f0996a CAW |
616 | assert migration_manager.latest_migration == 0 |
617 | assert migration_manager.database_current_migration == 0 | |
618 | ||
619 | # Sanity check a few things in the database... | |
caed154a | 620 | metadata = MetaData(bind=engine) |
40f0996a CAW |
621 | |
622 | # Check the structure of the creature table | |
623 | creature_table = Table( | |
624 | 'creature', metadata, | |
caed154a | 625 | autoload=True, autoload_with=engine) |
40f0996a CAW |
626 | assert set(creature_table.c.keys()) == set( |
627 | ['id', 'name', 'num_legs', 'is_demon']) | |
628 | assert_col_type(creature_table.c.id, Integer) | |
c7fa585b | 629 | assert_col_type(creature_table.c.name, VARCHAR) |
40f0996a | 630 | assert creature_table.c.name.nullable is False |
c7fa585b CAW |
631 | #assert creature_table.c.name.index is True |
632 | #assert creature_table.c.name.unique is True | |
40f0996a CAW |
633 | assert_col_type(creature_table.c.num_legs, Integer) |
634 | assert creature_table.c.num_legs.nullable is False | |
635 | assert_col_type(creature_table.c.is_demon, Boolean) | |
636 | ||
637 | # Check the structure of the level table | |
638 | level_table = Table( | |
639 | 'level', metadata, | |
caed154a | 640 | autoload=True, autoload_with=engine) |
40f0996a CAW |
641 | assert set(level_table.c.keys()) == set( |
642 | ['id', 'name', 'description', 'exits']) | |
c7fa585b | 643 | assert_col_type(level_table.c.id, VARCHAR) |
94eff10d | 644 | assert level_table.c.id.primary_key is True |
c7fa585b CAW |
645 | assert_col_type(level_table.c.name, VARCHAR) |
646 | assert_col_type(level_table.c.description, VARCHAR) | |
40f0996a CAW |
647 | # Skipping exits... Not sure if we can detect pickletype, not a |
648 | # big deal regardless. | |
649 | ||
650 | # Now check to see if stuff seems to be in there. | |
caed154a CAW |
651 | session = Session() |
652 | ||
860fa806 | 653 | # Check the creation of the foundation rows on the creature table |
654 | creature = session.query(Creature1).filter_by( | |
655 | name=u'goblin').one() | |
656 | assert creature.num_legs == 2 | |
657 | assert creature.is_demon == False | |
658 | ||
659 | creature = session.query(Creature1).filter_by( | |
660 | name=u'cerberus').one() | |
661 | assert creature.num_legs == 4 | |
662 | assert creature.is_demon == True | |
663 | ||
664 | ||
665 | # Check the creation of the inserted rows on the creature and levels tables | |
666 | ||
94eff10d CAW |
667 | creature = session.query(Creature1).filter_by( |
668 | name=u'centipede').one() | |
669 | assert creature.num_legs == 100 | |
670 | assert creature.is_demon == False | |
671 | ||
672 | creature = session.query(Creature1).filter_by( | |
673 | name=u'wolf').one() | |
674 | assert creature.num_legs == 4 | |
675 | assert creature.is_demon == False | |
676 | ||
677 | creature = session.query(Creature1).filter_by( | |
678 | name=u'wizardsnake').one() | |
679 | assert creature.num_legs == 0 | |
680 | assert creature.is_demon == True | |
681 | ||
682 | level = session.query(Level1).filter_by( | |
e8e52b3a | 683 | id=u'necroplex').one() |
94eff10d | 684 | assert level.name == u'The Necroplex' |
505c0db1 | 685 | assert level.description == u'A complex full of pure deathzone.' |
94eff10d CAW |
686 | assert level.exits == { |
687 | 'deathwell': 'evilstorm', | |
688 | 'portal': 'central_park'} | |
689 | ||
690 | level = session.query(Level1).filter_by( | |
e8e52b3a | 691 | id=u'evilstorm').one() |
94eff10d CAW |
692 | assert level.name == u'Evil Storm' |
693 | assert level.description == u'A storm full of pure evil.' | |
694 | assert level.exits == {} # You still can't escape the evilstorm! | |
695 | ||
696 | level = session.query(Level1).filter_by( | |
e8e52b3a | 697 | id=u'central_park').one() |
94eff10d CAW |
698 | assert level.name == u'Central Park, NY, NY' |
699 | assert level.description == u"New York's friendly Central Park." | |
700 | assert level.exits == { | |
701 | 'portal': 'necroplex'} | |
702 | ||
703 | # Create new migration manager, but make sure the db migration | |
704 | # isn't said to be updated yet | |
ecb4cc89 | 705 | printer = CollectingPrinter() |
94eff10d | 706 | migration_manager = MigrationManager( |
860fa806 | 707 | u'__main__', SET3_MODELS, FOUNDATIONS, SET3_MIGRATIONS, Session(), |
94eff10d CAW |
708 | printer) |
709 | ||
52539aca | 710 | assert migration_manager.latest_migration == 8 |
94eff10d | 711 | assert migration_manager.database_current_migration == 0 |
09e2c487 CAW |
712 | |
713 | # Migrate | |
94eff10d CAW |
714 | result = migration_manager.init_or_migrate() |
715 | ||
40f0996a | 716 | # Make sure result was "migrated" |
94eff10d CAW |
717 | assert result == u'migrated' |
718 | ||
719 | # TODO: Check output to user | |
720 | assert printer.combined_string == """\ | |
78d17b80 | 721 | -> Updating main mediagoblin tables: |
94eff10d CAW |
722 | + Running migration 1, "creature_remove_is_demon"... done. |
723 | + Running migration 2, "creature_powers_new_table"... done. | |
78d17b80 CAW |
724 | + Running migration 3, "level_exits_new_table"... done. |
725 | + Running migration 4, "creature_num_legs_to_num_limbs"... done. | |
726 | + Running migration 5, "level_exit_index_from_and_to_level"... done. | |
727 | + Running migration 6, "creature_power_index_creature"... done. | |
728 | + Running migration 7, "creature_power_hitpower_to_float"... done. | |
52539aca | 729 | + Running migration 8, "creature_power_name_creature_unique"... done. |
78d17b80 | 730 | """ |
94eff10d | 731 | |
09e2c487 | 732 | # Make sure version matches expected |
94eff10d | 733 | migration_manager = MigrationManager( |
860fa806 | 734 | u'__main__', SET3_MODELS, FOUNDATIONS, SET3_MIGRATIONS, Session(), |
94eff10d | 735 | printer) |
52539aca CAW |
736 | assert migration_manager.latest_migration == 8 |
737 | assert migration_manager.database_current_migration == 8 | |
94eff10d | 738 | |
09e2c487 | 739 | # Check all things in database match expected |
94eff10d CAW |
740 | |
741 | # Check the creature table | |
78d17b80 | 742 | metadata = MetaData(bind=engine) |
94eff10d CAW |
743 | creature_table = Table( |
744 | 'creature', metadata, | |
caed154a | 745 | autoload=True, autoload_with=engine) |
78d17b80 CAW |
746 | # assert set(creature_table.c.keys()) == set( |
747 | # ['id', 'name', 'num_limbs']) | |
94eff10d | 748 | assert set(creature_table.c.keys()) == set( |
78d17b80 | 749 | [u'id', 'name', u'num_limbs', u'is_demon']) |
94eff10d | 750 | assert_col_type(creature_table.c.id, Integer) |
c7fa585b | 751 | assert_col_type(creature_table.c.name, VARCHAR) |
94eff10d | 752 | assert creature_table.c.name.nullable is False |
c7fa585b CAW |
753 | #assert creature_table.c.name.index is True |
754 | #assert creature_table.c.name.unique is True | |
78d17b80 CAW |
755 | assert_col_type(creature_table.c.num_limbs, Integer) |
756 | assert creature_table.c.num_limbs.nullable is False | |
94eff10d CAW |
757 | |
758 | # Check the CreaturePower table | |
759 | creature_power_table = Table( | |
760 | 'creature_power', metadata, | |
caed154a | 761 | autoload=True, autoload_with=engine) |
94eff10d CAW |
762 | assert set(creature_power_table.c.keys()) == set( |
763 | ['id', 'creature', 'name', 'description', 'hitpower']) | |
764 | assert_col_type(creature_power_table.c.id, Integer) | |
765 | assert_col_type(creature_power_table.c.creature, Integer) | |
766 | assert creature_power_table.c.creature.nullable is False | |
c7fa585b CAW |
767 | assert_col_type(creature_power_table.c.name, VARCHAR) |
768 | assert_col_type(creature_power_table.c.description, VARCHAR) | |
690b51fa | 769 | assert_col_type(creature_power_table.c.hitpower, Float) |
94eff10d CAW |
770 | assert creature_power_table.c.hitpower.nullable is False |
771 | ||
772 | # Check the structure of the level table | |
773 | level_table = Table( | |
774 | 'level', metadata, | |
caed154a | 775 | autoload=True, autoload_with=engine) |
94eff10d CAW |
776 | assert set(level_table.c.keys()) == set( |
777 | ['id', 'name', 'description']) | |
c7fa585b | 778 | assert_col_type(level_table.c.id, VARCHAR) |
94eff10d | 779 | assert level_table.c.id.primary_key is True |
c7fa585b CAW |
780 | assert_col_type(level_table.c.name, VARCHAR) |
781 | assert_col_type(level_table.c.description, VARCHAR) | |
94eff10d CAW |
782 | |
783 | # Check the structure of the level_exits table | |
784 | level_exit_table = Table( | |
785 | 'level_exit', metadata, | |
caed154a | 786 | autoload=True, autoload_with=engine) |
94eff10d CAW |
787 | assert set(level_exit_table.c.keys()) == set( |
788 | ['id', 'name', 'from_level', 'to_level']) | |
789 | assert_col_type(level_exit_table.c.id, Integer) | |
c7fa585b CAW |
790 | assert_col_type(level_exit_table.c.name, VARCHAR) |
791 | assert_col_type(level_exit_table.c.from_level, VARCHAR) | |
94eff10d | 792 | assert level_exit_table.c.from_level.nullable is False |
c7fa585b CAW |
793 | #assert level_exit_table.c.from_level.index is True |
794 | assert_col_type(level_exit_table.c.to_level, VARCHAR) | |
94eff10d | 795 | assert level_exit_table.c.to_level.nullable is False |
c7fa585b | 796 | #assert level_exit_table.c.to_level.index is True |
94eff10d CAW |
797 | |
798 | # Now check to see if stuff seems to be in there. | |
caed154a | 799 | session = Session() |
84c1cd7c | 800 | |
801 | ||
802 | # Start with making sure that the foundations did not run again | |
803 | assert session.query(Creature3).filter_by( | |
804 | name=u'goblin').count() == 1 | |
805 | assert session.query(Creature3).filter_by( | |
806 | name=u'cerberus').count() == 1 | |
807 | ||
808 | # Then make sure the models have been migrated correctly | |
e8e52b3a | 809 | creature = session.query(Creature3).filter_by( |
690b51fa | 810 | name=u'centipede').one() |
5de0f4da | 811 | assert creature.num_limbs == 100.0 |
78d17b80 | 812 | assert creature.magical_powers == [] |
690b51fa | 813 | |
e8e52b3a | 814 | creature = session.query(Creature3).filter_by( |
690b51fa | 815 | name=u'wolf').one() |
5de0f4da | 816 | assert creature.num_limbs == 4.0 |
78d17b80 | 817 | assert creature.magical_powers == [] |
690b51fa | 818 | |
e8e52b3a | 819 | creature = session.query(Creature3).filter_by( |
690b51fa | 820 | name=u'wizardsnake').one() |
5de0f4da | 821 | assert creature.num_limbs == 0.0 |
78d17b80 | 822 | assert creature.magical_powers == [] |
94eff10d | 823 | |
e8e52b3a CAW |
824 | level = session.query(Level3).filter_by( |
825 | id=u'necroplex').one() | |
826 | assert level.name == u'The Necroplex' | |
505c0db1 | 827 | assert level.description == u'A complex full of pure deathzone.' |
e8e52b3a CAW |
828 | level_exits = _get_level3_exits(session, level) |
829 | assert level_exits == { | |
830 | u'deathwell': u'evilstorm', | |
831 | u'portal': u'central_park'} | |
832 | ||
833 | level = session.query(Level3).filter_by( | |
834 | id=u'evilstorm').one() | |
835 | assert level.name == u'Evil Storm' | |
836 | assert level.description == u'A storm full of pure evil.' | |
837 | level_exits = _get_level3_exits(session, level) | |
838 | assert level_exits == {} # You still can't escape the evilstorm! | |
839 | ||
840 | level = session.query(Level3).filter_by( | |
841 | id=u'central_park').one() | |
842 | assert level.name == u'Central Park, NY, NY' | |
843 | assert level.description == u"New York's friendly Central Park." | |
844 | level_exits = _get_level3_exits(session, level) | |
845 | assert level_exits == { | |
846 | 'portal': 'necroplex'} | |
09e2c487 CAW |
847 | |
848 | ||
d2506eeb | 849 | #def test_set2_to_set3(): |
09e2c487 CAW |
850 | # Create / connect to database |
851 | # Create tables by migrating on empty initial set | |
852 | ||
853 | # Install the initial set | |
854 | # Check version in database | |
855 | # Sanity check a few things in the database | |
856 | ||
857 | # Migrate | |
858 | # Make sure version matches expected | |
859 | # Check all things in database match expected | |
d2506eeb | 860 | # pass |
09e2c487 CAW |
861 | |
862 | ||
d2506eeb | 863 | #def test_set1_to_set2_to_set3(): |
09e2c487 CAW |
864 | # Create / connect to database |
865 | # Create tables by migrating on empty initial set | |
866 | ||
867 | # Install the initial set | |
868 | # Check version in database | |
869 | # Sanity check a few things in the database | |
870 | ||
871 | # Migrate | |
872 | # Make sure version matches expected | |
873 | # Check all things in database match expected | |
874 | ||
875 | # Migrate again | |
876 | # Make sure version matches expected again | |
877 | # Check all things in database match expected again | |
690b51fa CAW |
878 | |
879 | ##### Set2 | |
880 | # creature_table = Table( | |
881 | # 'creature', metadata, | |
e920b968 | 882 | # autoload=True, autoload_with=db_conn.bind) |
690b51fa CAW |
883 | # assert set(creature_table.c.keys()) == set( |
884 | # ['id', 'name', 'num_legs']) | |
885 | # assert_col_type(creature_table.c.id, Integer) | |
c7fa585b | 886 | # assert_col_type(creature_table.c.name, VARCHAR) |
690b51fa CAW |
887 | # assert creature_table.c.name.nullable is False |
888 | # assert creature_table.c.name.index is True | |
889 | # assert creature_table.c.name.unique is True | |
890 | # assert_col_type(creature_table.c.num_legs, Integer) | |
891 | # assert creature_table.c.num_legs.nullable is False | |
892 | ||
893 | # # Check the CreaturePower table | |
894 | # creature_power_table = Table( | |
895 | # 'creature_power', metadata, | |
e920b968 | 896 | # autoload=True, autoload_with=db_conn.bind) |
690b51fa CAW |
897 | # assert set(creature_power_table.c.keys()) == set( |
898 | # ['id', 'creature', 'name', 'description', 'hitpower']) | |
899 | # assert_col_type(creature_power_table.c.id, Integer) | |
900 | # assert_col_type(creature_power_table.c.creature, Integer) | |
901 | # assert creature_power_table.c.creature.nullable is False | |
c7fa585b CAW |
902 | # assert_col_type(creature_power_table.c.name, VARCHAR) |
903 | # assert_col_type(creature_power_table.c.description, VARCHAR) | |
690b51fa CAW |
904 | # assert_col_type(creature_power_table.c.hitpower, Integer) |
905 | # assert creature_power_table.c.hitpower.nullable is False | |
906 | ||
907 | # # Check the structure of the level table | |
908 | # level_table = Table( | |
909 | # 'level', metadata, | |
e920b968 | 910 | # autoload=True, autoload_with=db_conn.bind) |
690b51fa CAW |
911 | # assert set(level_table.c.keys()) == set( |
912 | # ['id', 'name', 'description']) | |
c7fa585b | 913 | # assert_col_type(level_table.c.id, VARCHAR) |
690b51fa | 914 | # assert level_table.c.id.primary_key is True |
c7fa585b CAW |
915 | # assert_col_type(level_table.c.name, VARCHAR) |
916 | # assert_col_type(level_table.c.description, VARCHAR) | |
690b51fa CAW |
917 | |
918 | # # Check the structure of the level_exits table | |
919 | # level_exit_table = Table( | |
920 | # 'level_exit', metadata, | |
e920b968 | 921 | # autoload=True, autoload_with=db_conn.bind) |
690b51fa CAW |
922 | # assert set(level_exit_table.c.keys()) == set( |
923 | # ['id', 'name', 'from_level', 'to_level']) | |
924 | # assert_col_type(level_exit_table.c.id, Integer) | |
c7fa585b CAW |
925 | # assert_col_type(level_exit_table.c.name, VARCHAR) |
926 | # assert_col_type(level_exit_table.c.from_level, VARCHAR) | |
690b51fa | 927 | # assert level_exit_table.c.from_level.nullable is False |
c7fa585b | 928 | # assert_col_type(level_exit_table.c.to_level, VARCHAR) |
690b51fa | 929 | |
d2506eeb | 930 | # pass |