In this commit I added a few unittests to account for Foundations. There were
[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 SET1_MIGRATIONS = {}
65
66 #######################################################
67 # Migration set 2: A few migrations and new model
68 #######################################################
69
70 Base2 = declarative_base(cls=GMGTableBase)
71
72 class 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)
78 magical_powers = relationship("CreaturePower2")
79
80 class 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)
88 hitpower = Column(Integer, nullable=False)
89
90 class Level2(Base2):
91 __tablename__ = "level"
92
93 id = Column(Unicode, primary_key=True)
94 name = Column(Unicode)
95 description = Column(Unicode)
96
97 class LevelExit2(Base2):
98 __tablename__ = "level_exit"
99
100 id = Column(Integer, primary_key=True)
101 name = Column(Unicode)
102 from_level = Column(
103 Unicode, ForeignKey('level.id'), nullable=False)
104 to_level = Column(
105 Unicode, ForeignKey('level.id'), nullable=False)
106
107 SET2_MODELS = [Creature2, CreaturePower2, Level2, LevelExit2]
108
109
110 @RegisterMigration(1, FULL_MIGRATIONS)
111 def creature_remove_is_demon(db_conn):
112 """
113 Remove the is_demon field from the creature model. We don't need
114 it!
115 """
116 # :( Commented out 'cuz of:
117 # http://code.google.com/p/sqlalchemy-migrate/issues/detail?id=143&thanks=143&ts=1327882242
118
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
126
127 @RegisterMigration(2, FULL_MIGRATIONS)
128 def creature_powers_new_table(db_conn):
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 """
134 metadata = MetaData(bind=db_conn.bind)
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
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))
150 metadata.create_all(db_conn.bind)
151
152
153 @RegisterMigration(3, FULL_MIGRATIONS)
154 def level_exits_new_table(db_conn):
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 """
159 # First, create the table
160 # -----------------------
161 metadata = MetaData(bind=db_conn.bind)
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
173 level_exits = Table(
174 'level_exit', metadata,
175 Column('id', Integer, primary_key=True),
176 Column('name', Unicode),
177 Column('from_level',
178 Unicode, ForeignKey('level.id'), nullable=False),
179 Column('to_level',
180 Unicode, ForeignKey('level.id'), nullable=False))
181 metadata.create_all(db_conn.bind)
182
183 # And now, convert all the old exit pickles to new level exits
184 # ------------------------------------------------------------
185
186 # query over and insert
187 result = db_conn.execute(
188 select([levels], levels.c.exits!=None))
189
190 for level in result:
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))
199
200 # Finally, drop the old level exits pickle table
201 # ----------------------------------------------
202 levels.drop_column('exits')
203
204
205 # A hack! At this point we freeze-fame and get just a partial list of
206 # migrations
207
208 SET2_MIGRATIONS = copy.copy(FULL_MIGRATIONS)
209
210 #######################################################
211 # Migration set 3: Final migrations
212 #######################################################
213
214 Base3 = declarative_base(cls=GMGTableBase)
215
216 class 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)
222 magical_powers = relationship("CreaturePower3")
223
224 class 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)
232 hitpower = Column(Float, nullable=False)
233
234 class Level3(Base3):
235 __tablename__ = "level"
236
237 id = Column(Unicode, primary_key=True)
238 name = Column(Unicode)
239 description = Column(Unicode)
240
241 class LevelExit3(Base3):
242 __tablename__ = "level_exit"
243
244 id = Column(Integer, primary_key=True)
245 name = Column(Unicode)
246 from_level = Column(
247 Unicode, ForeignKey('level.id'), nullable=False, index=True)
248 to_level = Column(
249 Unicode, ForeignKey('level.id'), nullable=False, index=True)
250
251
252 SET3_MODELS = [Creature3, CreaturePower3, Level3, LevelExit3]
253 SET3_MIGRATIONS = FULL_MIGRATIONS
254
255
256 @RegisterMigration(4, FULL_MIGRATIONS)
257 def creature_num_legs_to_num_limbs(db_conn):
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 """
263 metadata = MetaData(bind=db_conn.bind)
264 creature_table = Table(
265 'creature', metadata,
266 autoload=True, autoload_with=db_conn.bind)
267 creature_table.c.num_legs.alter(name=u"num_limbs")
268
269
270 @RegisterMigration(5, FULL_MIGRATIONS)
271 def level_exit_index_from_and_to_level(db_conn):
272 """
273 Index the from and to levels of the level exit table.
274 """
275 metadata = MetaData(bind=db_conn.bind)
276 level_exit = Table(
277 'level_exit', metadata,
278 autoload=True, autoload_with=db_conn.bind)
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)
283
284
285 @RegisterMigration(6, FULL_MIGRATIONS)
286 def creature_power_index_creature(db_conn):
287 """
288 Index our foreign key relationship to the creatures
289 """
290 metadata = MetaData(bind=db_conn.bind)
291 creature_power = Table(
292 'creature_power', metadata,
293 autoload=True, autoload_with=db_conn.bind)
294 Index('ix_creature_power_creature',
295 creature_power.c.creature).create(db_conn.bind)
296
297
298 @RegisterMigration(7, FULL_MIGRATIONS)
299 def creature_power_hitpower_to_float(db_conn):
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 """
307 metadata = MetaData(bind=db_conn.bind)
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
315 creature_power = Table(
316 'creature_power', metadata,
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
325 creature_power.c.hitpower.alter(type=Float)
326
327
328 @RegisterMigration(8, FULL_MIGRATIONS)
329 def 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
350 def _insert_migration1_objects(session):
351 """
352 Test objects to insert for the first set of things
353 """
354 # Insert creatures
355 session.add_all(
356 [Creature1(name=u'centipede',
357 num_legs=100,
358 is_demon=False),
359 Creature1(name=u'wolf',
360 num_legs=4,
361 is_demon=False),
362 # don't ask me what a wizardsnake is.
363 Creature1(name=u'wizardsnake',
364 num_legs=0,
365 is_demon=True)])
366
367 # Insert levels
368 session.add_all(
369 [Level1(id=u'necroplex',
370 name=u'The Necroplex',
371 description=u'A complex full of pure deathzone.',
372 exits={
373 u'deathwell': u'evilstorm',
374 u'portal': u'central_park'}),
375 Level1(id=u'evilstorm',
376 name=u'Evil Storm',
377 description=u'A storm full of pure evil.',
378 exits={}), # you can't escape the evilstorm
379 Level1(id=u'central_park',
380 name=u'Central Park, NY, NY',
381 description=u"New York's friendly Central Park.",
382 exits={
383 u'portal': u'necroplex'})])
384
385 session.commit()
386
387
388 def _insert_migration2_objects(session):
389 """
390 Test objects to insert for the second set of things
391 """
392 # Insert creatures
393 session.add_all(
394 [Creature2(
395 name=u'centipede',
396 num_legs=100),
397 Creature2(
398 name=u'wolf',
399 num_legs=4,
400 magical_powers = [
401 CreaturePower2(
402 name=u"ice breath",
403 description=u"A blast of icy breath!",
404 hitpower=20),
405 CreaturePower2(
406 name=u"death stare",
407 description=u"A frightening stare, for sure!",
408 hitpower=45)]),
409 Creature2(
410 name=u'wizardsnake',
411 num_legs=0,
412 magical_powers=[
413 CreaturePower2(
414 name=u'death_rattle',
415 description=u'A rattle... of DEATH!',
416 hitpower=1000),
417 CreaturePower2(
418 name=u'sneaky_stare',
419 description=u"The sneakiest stare you've ever seen!",
420 hitpower=300),
421 CreaturePower2(
422 name=u'slithery_smoke',
423 description=u"A blast of slithery, slithery smoke.",
424 hitpower=10),
425 CreaturePower2(
426 name=u'treacherous_tremors',
427 description=u"The ground shakes beneath footed animals!",
428 hitpower=0)])])
429
430 # Insert levels
431 session.add_all(
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.',
438 exits=[]), # you can't escape the evilstorm
439 Level2(id=u'central_park',
440 name=u'Central Park, NY, NY',
441 description=u"New York's friendly Central Park.")])
442
443 # necroplex exits
444 session.add_all(
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')])
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(
457 [LevelExit2(name=u'portal',
458 from_level=u'central_park',
459 to_level=u'necroplex')])
460
461 session.commit()
462
463
464 def _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(
471 name=u'centipede',
472 num_limbs=100),
473 Creature3(
474 name=u'wolf',
475 num_limbs=4,
476 magical_powers = [
477 CreaturePower3(
478 name=u"ice breath",
479 description=u"A blast of icy breath!",
480 hitpower=20.0),
481 CreaturePower3(
482 name=u"death stare",
483 description=u"A frightening stare, for sure!",
484 hitpower=45.0)]),
485 Creature3(
486 name=u'wizardsnake',
487 num_limbs=0,
488 magical_powers=[
489 CreaturePower3(
490 name=u'death_rattle',
491 description=u'A rattle... of DEATH!',
492 hitpower=1000.0),
493 CreaturePower3(
494 name=u'sneaky_stare',
495 description=u"The sneakiest stare you've ever seen!",
496 hitpower=300.0),
497 CreaturePower3(
498 name=u'slithery_smoke',
499 description=u"A blast of slithery, slithery smoke.",
500 hitpower=10.0),
501 CreaturePower3(
502 name=u'treacherous_tremors',
503 description=u"The ground shakes beneath footed animals!",
504 hitpower=0.0)])],
505 # annnnnd one more to test a floating point hitpower
506 Creature3(
507 name=u'deity',
508 numb_limbs=30,
509 magical_powers=[
510 CreaturePower3(
511 name=u'smite',
512 description=u'Smitten by holy wrath!',
513 hitpower=9999.9)]))
514
515 # Insert levels
516 session.add_all(
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.',
523 exits=[]), # you can't escape the evilstorm
524 Level3(id=u'central_park',
525 name=u'Central Park, NY, NY',
526 description=u"New York's friendly Central Park.")])
527
528 # necroplex exits
529 session.add_all(
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')])
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(
542 [LevelExit3(name=u'portal',
543 from_level=u'central_park',
544 to_level=u'necroplex')])
545
546 session.commit()
547
548 def create_test_engine():
549 from sqlalchemy import create_engine
550 engine = create_engine('sqlite:///:memory:', echo=False)
551 Session = sessionmaker(bind=engine)
552 return engine, Session
553
554
555 def assert_col_type(column, this_class):
556 assert isinstance(column.type, this_class)
557
558
559 def _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
566 def test_set1_to_set3():
567 # Create / connect to database
568 # ----------------------------
569
570 engine, Session = create_test_engine()
571
572 # Create tables by migrating on empty initial set
573 # -----------------------------------------------
574
575 printer = CollectingPrinter()
576 migration_manager = MigrationManager(
577 u'__main__', SET1_MODELS, FOUNDATIONS, SET1_MIGRATIONS, Session(),
578 printer)
579
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()
585
586 # Make sure output was "inited"
587 assert result == u'inited'
588 # Check output
589 assert printer.combined_string == (
590 "-> Initializing main mediagoblin tables... done.\n" + \
591 " + Laying foundations for Creature1 table\n" )
592 # Check version in database
593 assert migration_manager.latest_migration == 0
594 assert migration_manager.database_current_migration == 0
595
596
597 # Install the initial set
598 # -----------------------
599
600 _insert_migration1_objects(Session())
601
602 # Try to "re-migrate" with same manager settings... nothing should happen
603 migration_manager = MigrationManager(
604 u'__main__', SET1_MODELS, FOUNDATIONS, SET1_MIGRATIONS,
605 Session(), printer)
606 assert migration_manager.init_or_migrate() == None
607
608 # Check version in database
609 assert migration_manager.latest_migration == 0
610 assert migration_manager.database_current_migration == 0
611
612 # Sanity check a few things in the database...
613 metadata = MetaData(bind=engine)
614
615 # Check the structure of the creature table
616 creature_table = Table(
617 'creature', metadata,
618 autoload=True, autoload_with=engine)
619 assert set(creature_table.c.keys()) == set(
620 ['id', 'name', 'num_legs', 'is_demon'])
621 assert_col_type(creature_table.c.id, Integer)
622 assert_col_type(creature_table.c.name, VARCHAR)
623 assert creature_table.c.name.nullable is False
624 #assert creature_table.c.name.index is True
625 #assert creature_table.c.name.unique is True
626 assert_col_type(creature_table.c.num_legs, Integer)
627 assert creature_table.c.num_legs.nullable is False
628 assert_col_type(creature_table.c.is_demon, Boolean)
629
630 # Check the structure of the level table
631 level_table = Table(
632 'level', metadata,
633 autoload=True, autoload_with=engine)
634 assert set(level_table.c.keys()) == set(
635 ['id', 'name', 'description', 'exits'])
636 assert_col_type(level_table.c.id, VARCHAR)
637 assert level_table.c.id.primary_key is True
638 assert_col_type(level_table.c.name, VARCHAR)
639 assert_col_type(level_table.c.description, VARCHAR)
640 # Skipping exits... Not sure if we can detect pickletype, not a
641 # big deal regardless.
642
643 # Now check to see if stuff seems to be in there.
644 session = Session()
645
646 # Check the creation of the foundation rows on the creature table
647 creature = session.query(Creature1).filter_by(
648 name=u'goblin').one()
649 assert creature.num_legs == 2
650 assert creature.is_demon == False
651
652 creature = session.query(Creature1).filter_by(
653 name=u'cerberus').one()
654 assert creature.num_legs == 4
655 assert creature.is_demon == True
656
657
658 # Check the creation of the inserted rows on the creature and levels tables
659
660 creature = session.query(Creature1).filter_by(
661 name=u'centipede').one()
662 assert creature.num_legs == 100
663 assert creature.is_demon == False
664
665 creature = session.query(Creature1).filter_by(
666 name=u'wolf').one()
667 assert creature.num_legs == 4
668 assert creature.is_demon == False
669
670 creature = session.query(Creature1).filter_by(
671 name=u'wizardsnake').one()
672 assert creature.num_legs == 0
673 assert creature.is_demon == True
674
675 level = session.query(Level1).filter_by(
676 id=u'necroplex').one()
677 assert level.name == u'The Necroplex'
678 assert level.description == u'A complex full of pure deathzone.'
679 assert level.exits == {
680 'deathwell': 'evilstorm',
681 'portal': 'central_park'}
682
683 level = session.query(Level1).filter_by(
684 id=u'evilstorm').one()
685 assert level.name == u'Evil Storm'
686 assert level.description == u'A storm full of pure evil.'
687 assert level.exits == {} # You still can't escape the evilstorm!
688
689 level = session.query(Level1).filter_by(
690 id=u'central_park').one()
691 assert level.name == u'Central Park, NY, NY'
692 assert level.description == u"New York's friendly Central Park."
693 assert level.exits == {
694 'portal': 'necroplex'}
695
696 # Create new migration manager, but make sure the db migration
697 # isn't said to be updated yet
698 printer = CollectingPrinter()
699 migration_manager = MigrationManager(
700 u'__main__', SET3_MODELS, FOUNDATIONS, SET3_MIGRATIONS, Session(),
701 printer)
702
703 assert migration_manager.latest_migration == 8
704 assert migration_manager.database_current_migration == 0
705
706 # Migrate
707 result = migration_manager.init_or_migrate()
708
709 # Make sure result was "migrated"
710 assert result == u'migrated'
711
712 # TODO: Check output to user
713 assert printer.combined_string == """\
714 -> Updating main mediagoblin tables:
715 + Running migration 1, "creature_remove_is_demon"... done.
716 + Running migration 2, "creature_powers_new_table"... done.
717 + Running migration 3, "level_exits_new_table"... done.
718 + Running migration 4, "creature_num_legs_to_num_limbs"... done.
719 + Running migration 5, "level_exit_index_from_and_to_level"... done.
720 + Running migration 6, "creature_power_index_creature"... done.
721 + Running migration 7, "creature_power_hitpower_to_float"... done.
722 + Running migration 8, "creature_power_name_creature_unique"... done.
723 """
724
725 # Make sure version matches expected
726 migration_manager = MigrationManager(
727 u'__main__', SET3_MODELS, FOUNDATIONS, SET3_MIGRATIONS, Session(),
728 printer)
729 assert migration_manager.latest_migration == 8
730 assert migration_manager.database_current_migration == 8
731
732 # Check all things in database match expected
733
734 # Check the creature table
735 metadata = MetaData(bind=engine)
736 creature_table = Table(
737 'creature', metadata,
738 autoload=True, autoload_with=engine)
739 # assert set(creature_table.c.keys()) == set(
740 # ['id', 'name', 'num_limbs'])
741 assert set(creature_table.c.keys()) == set(
742 [u'id', 'name', u'num_limbs', u'is_demon'])
743 assert_col_type(creature_table.c.id, Integer)
744 assert_col_type(creature_table.c.name, VARCHAR)
745 assert creature_table.c.name.nullable is False
746 #assert creature_table.c.name.index is True
747 #assert creature_table.c.name.unique is True
748 assert_col_type(creature_table.c.num_limbs, Integer)
749 assert creature_table.c.num_limbs.nullable is False
750
751 # Check the CreaturePower table
752 creature_power_table = Table(
753 'creature_power', metadata,
754 autoload=True, autoload_with=engine)
755 assert set(creature_power_table.c.keys()) == set(
756 ['id', 'creature', 'name', 'description', 'hitpower'])
757 assert_col_type(creature_power_table.c.id, Integer)
758 assert_col_type(creature_power_table.c.creature, Integer)
759 assert creature_power_table.c.creature.nullable is False
760 assert_col_type(creature_power_table.c.name, VARCHAR)
761 assert_col_type(creature_power_table.c.description, VARCHAR)
762 assert_col_type(creature_power_table.c.hitpower, Float)
763 assert creature_power_table.c.hitpower.nullable is False
764
765 # Check the structure of the level table
766 level_table = Table(
767 'level', metadata,
768 autoload=True, autoload_with=engine)
769 assert set(level_table.c.keys()) == set(
770 ['id', 'name', 'description'])
771 assert_col_type(level_table.c.id, VARCHAR)
772 assert level_table.c.id.primary_key is True
773 assert_col_type(level_table.c.name, VARCHAR)
774 assert_col_type(level_table.c.description, VARCHAR)
775
776 # Check the structure of the level_exits table
777 level_exit_table = Table(
778 'level_exit', metadata,
779 autoload=True, autoload_with=engine)
780 assert set(level_exit_table.c.keys()) == set(
781 ['id', 'name', 'from_level', 'to_level'])
782 assert_col_type(level_exit_table.c.id, Integer)
783 assert_col_type(level_exit_table.c.name, VARCHAR)
784 assert_col_type(level_exit_table.c.from_level, VARCHAR)
785 assert level_exit_table.c.from_level.nullable is False
786 #assert level_exit_table.c.from_level.index is True
787 assert_col_type(level_exit_table.c.to_level, VARCHAR)
788 assert level_exit_table.c.to_level.nullable is False
789 #assert level_exit_table.c.to_level.index is True
790
791 # Now check to see if stuff seems to be in there.
792 session = Session()
793 creature = session.query(Creature3).filter_by(
794 name=u'centipede').one()
795 assert creature.num_limbs == 100.0
796 assert creature.magical_powers == []
797
798 creature = session.query(Creature3).filter_by(
799 name=u'wolf').one()
800 assert creature.num_limbs == 4.0
801 assert creature.magical_powers == []
802
803 creature = session.query(Creature3).filter_by(
804 name=u'wizardsnake').one()
805 assert creature.num_limbs == 0.0
806 assert creature.magical_powers == []
807
808 level = session.query(Level3).filter_by(
809 id=u'necroplex').one()
810 assert level.name == u'The Necroplex'
811 assert level.description == u'A complex full of pure deathzone.'
812 level_exits = _get_level3_exits(session, level)
813 assert level_exits == {
814 u'deathwell': u'evilstorm',
815 u'portal': u'central_park'}
816
817 level = session.query(Level3).filter_by(
818 id=u'evilstorm').one()
819 assert level.name == u'Evil Storm'
820 assert level.description == u'A storm full of pure evil.'
821 level_exits = _get_level3_exits(session, level)
822 assert level_exits == {} # You still can't escape the evilstorm!
823
824 level = session.query(Level3).filter_by(
825 id=u'central_park').one()
826 assert level.name == u'Central Park, NY, NY'
827 assert level.description == u"New York's friendly Central Park."
828 level_exits = _get_level3_exits(session, level)
829 assert level_exits == {
830 'portal': 'necroplex'}
831
832
833 #def test_set2_to_set3():
834 # Create / connect to database
835 # Create tables by migrating on empty initial set
836
837 # Install the initial set
838 # Check version in database
839 # Sanity check a few things in the database
840
841 # Migrate
842 # Make sure version matches expected
843 # Check all things in database match expected
844 # pass
845
846
847 #def test_set1_to_set2_to_set3():
848 # Create / connect to database
849 # Create tables by migrating on empty initial set
850
851 # Install the initial set
852 # Check version in database
853 # Sanity check a few things in the database
854
855 # Migrate
856 # Make sure version matches expected
857 # Check all things in database match expected
858
859 # Migrate again
860 # Make sure version matches expected again
861 # Check all things in database match expected again
862
863 ##### Set2
864 # creature_table = Table(
865 # 'creature', metadata,
866 # autoload=True, autoload_with=db_conn.bind)
867 # assert set(creature_table.c.keys()) == set(
868 # ['id', 'name', 'num_legs'])
869 # assert_col_type(creature_table.c.id, Integer)
870 # assert_col_type(creature_table.c.name, VARCHAR)
871 # assert creature_table.c.name.nullable is False
872 # assert creature_table.c.name.index is True
873 # assert creature_table.c.name.unique is True
874 # assert_col_type(creature_table.c.num_legs, Integer)
875 # assert creature_table.c.num_legs.nullable is False
876
877 # # Check the CreaturePower table
878 # creature_power_table = Table(
879 # 'creature_power', metadata,
880 # autoload=True, autoload_with=db_conn.bind)
881 # assert set(creature_power_table.c.keys()) == set(
882 # ['id', 'creature', 'name', 'description', 'hitpower'])
883 # assert_col_type(creature_power_table.c.id, Integer)
884 # assert_col_type(creature_power_table.c.creature, Integer)
885 # assert creature_power_table.c.creature.nullable is False
886 # assert_col_type(creature_power_table.c.name, VARCHAR)
887 # assert_col_type(creature_power_table.c.description, VARCHAR)
888 # assert_col_type(creature_power_table.c.hitpower, Integer)
889 # assert creature_power_table.c.hitpower.nullable is False
890
891 # # Check the structure of the level table
892 # level_table = Table(
893 # 'level', metadata,
894 # autoload=True, autoload_with=db_conn.bind)
895 # assert set(level_table.c.keys()) == set(
896 # ['id', 'name', 'description'])
897 # assert_col_type(level_table.c.id, VARCHAR)
898 # assert level_table.c.id.primary_key is True
899 # assert_col_type(level_table.c.name, VARCHAR)
900 # assert_col_type(level_table.c.description, VARCHAR)
901
902 # # Check the structure of the level_exits table
903 # level_exit_table = Table(
904 # 'level_exit', metadata,
905 # autoload=True, autoload_with=db_conn.bind)
906 # assert set(level_exit_table.c.keys()) == set(
907 # ['id', 'name', 'from_level', 'to_level'])
908 # assert_col_type(level_exit_table.c.id, Integer)
909 # assert_col_type(level_exit_table.c.name, VARCHAR)
910 # assert_col_type(level_exit_table.c.from_level, VARCHAR)
911 # assert level_exit_table.c.from_level.nullable is False
912 # assert_col_type(level_exit_table.c.to_level, VARCHAR)
913
914 # pass