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