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