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