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