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