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