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