Starting to write unit tests...
[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 SET1_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, SET1_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 # Install the initial set
597 # -----------------------
598
599 _insert_migration1_objects(Session())
600
601 # Try to "re-migrate" with same manager settings... nothing should happen
602 migration_manager = MigrationManager(
603 u'__main__', SET1_MODELS, SET1_FOUNDATIONS, SET1_MIGRATIONS,
604 Session(), printer)
605 assert migration_manager.init_or_migrate() == None
606
607 # Check version in database
608 assert migration_manager.latest_migration == 0
609 assert migration_manager.database_current_migration == 0
610
611 # Sanity check a few things in the database...
612 metadata = MetaData(bind=engine)
613
614 # Check the structure of the creature table
615 creature_table = Table(
616 'creature', metadata,
617 autoload=True, autoload_with=engine)
618 assert set(creature_table.c.keys()) == set(
619 ['id', 'name', 'num_legs', 'is_demon'])
620 assert_col_type(creature_table.c.id, Integer)
621 assert_col_type(creature_table.c.name, VARCHAR)
622 assert creature_table.c.name.nullable is False
623 #assert creature_table.c.name.index is True
624 #assert creature_table.c.name.unique is True
625 assert_col_type(creature_table.c.num_legs, Integer)
626 assert creature_table.c.num_legs.nullable is False
627 assert_col_type(creature_table.c.is_demon, Boolean)
628
629 # Check the structure of the level table
630 level_table = Table(
631 'level', metadata,
632 autoload=True, autoload_with=engine)
633 assert set(level_table.c.keys()) == set(
634 ['id', 'name', 'description', 'exits'])
635 assert_col_type(level_table.c.id, VARCHAR)
636 assert level_table.c.id.primary_key is True
637 assert_col_type(level_table.c.name, VARCHAR)
638 assert_col_type(level_table.c.description, VARCHAR)
639 # Skipping exits... Not sure if we can detect pickletype, not a
640 # big deal regardless.
641
642 # Now check to see if stuff seems to be in there.
643 session = Session()
644
645 creature = session.query(Creature1).filter_by(
646 name=u'centipede').one()
647 assert creature.num_legs == 100
648 assert creature.is_demon == False
649
650 creature = session.query(Creature1).filter_by(
651 name=u'wolf').one()
652 assert creature.num_legs == 4
653 assert creature.is_demon == False
654
655 creature = session.query(Creature1).filter_by(
656 name=u'wizardsnake').one()
657 assert creature.num_legs == 0
658 assert creature.is_demon == True
659
660 level = session.query(Level1).filter_by(
661 id=u'necroplex').one()
662 assert level.name == u'The Necroplex'
663 assert level.description == u'A complex full of pure deathzone.'
664 assert level.exits == {
665 'deathwell': 'evilstorm',
666 'portal': 'central_park'}
667
668 level = session.query(Level1).filter_by(
669 id=u'evilstorm').one()
670 assert level.name == u'Evil Storm'
671 assert level.description == u'A storm full of pure evil.'
672 assert level.exits == {} # You still can't escape the evilstorm!
673
674 level = session.query(Level1).filter_by(
675 id=u'central_park').one()
676 assert level.name == u'Central Park, NY, NY'
677 assert level.description == u"New York's friendly Central Park."
678 assert level.exits == {
679 'portal': 'necroplex'}
680
681 # Create new migration manager, but make sure the db migration
682 # isn't said to be updated yet
683 printer = CollectingPrinter()
684 migration_manager = MigrationManager(
685 u'__main__', SET3_MODELS, SET3_MIGRATIONS, Session(),
686 printer)
687
688 assert migration_manager.latest_migration == 8
689 assert migration_manager.database_current_migration == 0
690
691 # Migrate
692 result = migration_manager.init_or_migrate()
693
694 # Make sure result was "migrated"
695 assert result == u'migrated'
696
697 # TODO: Check output to user
698 assert printer.combined_string == """\
699 -> Updating main mediagoblin tables:
700 + Running migration 1, "creature_remove_is_demon"... done.
701 + Running migration 2, "creature_powers_new_table"... done.
702 + Running migration 3, "level_exits_new_table"... done.
703 + Running migration 4, "creature_num_legs_to_num_limbs"... done.
704 + Running migration 5, "level_exit_index_from_and_to_level"... done.
705 + Running migration 6, "creature_power_index_creature"... done.
706 + Running migration 7, "creature_power_hitpower_to_float"... done.
707 + Running migration 8, "creature_power_name_creature_unique"... done.
708 """
709
710 # Make sure version matches expected
711 migration_manager = MigrationManager(
712 u'__main__', SET3_MODELS, SET3_MIGRATIONS, Session(),
713 printer)
714 assert migration_manager.latest_migration == 8
715 assert migration_manager.database_current_migration == 8
716
717 # Check all things in database match expected
718
719 # Check the creature table
720 metadata = MetaData(bind=engine)
721 creature_table = Table(
722 'creature', metadata,
723 autoload=True, autoload_with=engine)
724 # assert set(creature_table.c.keys()) == set(
725 # ['id', 'name', 'num_limbs'])
726 assert set(creature_table.c.keys()) == set(
727 [u'id', 'name', u'num_limbs', u'is_demon'])
728 assert_col_type(creature_table.c.id, Integer)
729 assert_col_type(creature_table.c.name, VARCHAR)
730 assert creature_table.c.name.nullable is False
731 #assert creature_table.c.name.index is True
732 #assert creature_table.c.name.unique is True
733 assert_col_type(creature_table.c.num_limbs, Integer)
734 assert creature_table.c.num_limbs.nullable is False
735
736 # Check the CreaturePower table
737 creature_power_table = Table(
738 'creature_power', metadata,
739 autoload=True, autoload_with=engine)
740 assert set(creature_power_table.c.keys()) == set(
741 ['id', 'creature', 'name', 'description', 'hitpower'])
742 assert_col_type(creature_power_table.c.id, Integer)
743 assert_col_type(creature_power_table.c.creature, Integer)
744 assert creature_power_table.c.creature.nullable is False
745 assert_col_type(creature_power_table.c.name, VARCHAR)
746 assert_col_type(creature_power_table.c.description, VARCHAR)
747 assert_col_type(creature_power_table.c.hitpower, Float)
748 assert creature_power_table.c.hitpower.nullable is False
749
750 # Check the structure of the level table
751 level_table = Table(
752 'level', metadata,
753 autoload=True, autoload_with=engine)
754 assert set(level_table.c.keys()) == set(
755 ['id', 'name', 'description'])
756 assert_col_type(level_table.c.id, VARCHAR)
757 assert level_table.c.id.primary_key is True
758 assert_col_type(level_table.c.name, VARCHAR)
759 assert_col_type(level_table.c.description, VARCHAR)
760
761 # Check the structure of the level_exits table
762 level_exit_table = Table(
763 'level_exit', metadata,
764 autoload=True, autoload_with=engine)
765 assert set(level_exit_table.c.keys()) == set(
766 ['id', 'name', 'from_level', 'to_level'])
767 assert_col_type(level_exit_table.c.id, Integer)
768 assert_col_type(level_exit_table.c.name, VARCHAR)
769 assert_col_type(level_exit_table.c.from_level, VARCHAR)
770 assert level_exit_table.c.from_level.nullable is False
771 #assert level_exit_table.c.from_level.index is True
772 assert_col_type(level_exit_table.c.to_level, VARCHAR)
773 assert level_exit_table.c.to_level.nullable is False
774 #assert level_exit_table.c.to_level.index is True
775
776 # Now check to see if stuff seems to be in there.
777 session = Session()
778 creature = session.query(Creature3).filter_by(
779 name=u'centipede').one()
780 assert creature.num_limbs == 100.0
781 assert creature.magical_powers == []
782
783 creature = session.query(Creature3).filter_by(
784 name=u'wolf').one()
785 assert creature.num_limbs == 4.0
786 assert creature.magical_powers == []
787
788 creature = session.query(Creature3).filter_by(
789 name=u'wizardsnake').one()
790 assert creature.num_limbs == 0.0
791 assert creature.magical_powers == []
792
793 level = session.query(Level3).filter_by(
794 id=u'necroplex').one()
795 assert level.name == u'The Necroplex'
796 assert level.description == u'A complex full of pure deathzone.'
797 level_exits = _get_level3_exits(session, level)
798 assert level_exits == {
799 u'deathwell': u'evilstorm',
800 u'portal': u'central_park'}
801
802 level = session.query(Level3).filter_by(
803 id=u'evilstorm').one()
804 assert level.name == u'Evil Storm'
805 assert level.description == u'A storm full of pure evil.'
806 level_exits = _get_level3_exits(session, level)
807 assert level_exits == {} # You still can't escape the evilstorm!
808
809 level = session.query(Level3).filter_by(
810 id=u'central_park').one()
811 assert level.name == u'Central Park, NY, NY'
812 assert level.description == u"New York's friendly Central Park."
813 level_exits = _get_level3_exits(session, level)
814 assert level_exits == {
815 'portal': 'necroplex'}
816
817
818 #def test_set2_to_set3():
819 # Create / connect to database
820 # Create tables by migrating on empty initial set
821
822 # Install the initial set
823 # Check version in database
824 # Sanity check a few things in the database
825
826 # Migrate
827 # Make sure version matches expected
828 # Check all things in database match expected
829 # pass
830
831
832 #def test_set1_to_set2_to_set3():
833 # Create / connect to database
834 # Create tables by migrating on empty initial set
835
836 # Install the initial set
837 # Check version in database
838 # Sanity check a few things in the database
839
840 # Migrate
841 # Make sure version matches expected
842 # Check all things in database match expected
843
844 # Migrate again
845 # Make sure version matches expected again
846 # Check all things in database match expected again
847
848 ##### Set2
849 # creature_table = Table(
850 # 'creature', metadata,
851 # autoload=True, autoload_with=db_conn.bind)
852 # assert set(creature_table.c.keys()) == set(
853 # ['id', 'name', 'num_legs'])
854 # assert_col_type(creature_table.c.id, Integer)
855 # assert_col_type(creature_table.c.name, VARCHAR)
856 # assert creature_table.c.name.nullable is False
857 # assert creature_table.c.name.index is True
858 # assert creature_table.c.name.unique is True
859 # assert_col_type(creature_table.c.num_legs, Integer)
860 # assert creature_table.c.num_legs.nullable is False
861
862 # # Check the CreaturePower table
863 # creature_power_table = Table(
864 # 'creature_power', metadata,
865 # autoload=True, autoload_with=db_conn.bind)
866 # assert set(creature_power_table.c.keys()) == set(
867 # ['id', 'creature', 'name', 'description', 'hitpower'])
868 # assert_col_type(creature_power_table.c.id, Integer)
869 # assert_col_type(creature_power_table.c.creature, Integer)
870 # assert creature_power_table.c.creature.nullable is False
871 # assert_col_type(creature_power_table.c.name, VARCHAR)
872 # assert_col_type(creature_power_table.c.description, VARCHAR)
873 # assert_col_type(creature_power_table.c.hitpower, Integer)
874 # assert creature_power_table.c.hitpower.nullable is False
875
876 # # Check the structure of the level table
877 # level_table = Table(
878 # 'level', metadata,
879 # autoload=True, autoload_with=db_conn.bind)
880 # assert set(level_table.c.keys()) == set(
881 # ['id', 'name', 'description'])
882 # assert_col_type(level_table.c.id, VARCHAR)
883 # assert level_table.c.id.primary_key is True
884 # assert_col_type(level_table.c.name, VARCHAR)
885 # assert_col_type(level_table.c.description, VARCHAR)
886
887 # # Check the structure of the level_exits table
888 # level_exit_table = Table(
889 # 'level_exit', metadata,
890 # autoload=True, autoload_with=db_conn.bind)
891 # assert set(level_exit_table.c.keys()) == set(
892 # ['id', 'name', 'from_level', 'to_level'])
893 # assert_col_type(level_exit_table.c.id, Integer)
894 # assert_col_type(level_exit_table.c.name, VARCHAR)
895 # assert_col_type(level_exit_table.c.from_level, VARCHAR)
896 # assert level_exit_table.c.from_level.nullable is False
897 # assert_col_type(level_exit_table.c.to_level, VARCHAR)
898
899 # pass