1 # GNU MediaGoblin -- federated, autonomous media hosting
2 # Copyright (C) 2012, 2012 MediaGoblin contributors. See AUTHORS.
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.
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.
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/>.
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
28 from mediagoblin
.db
.sql
.base
import GMGTableBase
29 from mediagoblin
.db
.sql
.util
import MigrationManager
, RegisterMigration
32 # This one will get filled with local migrations
36 #######################################################
37 # Migration set 1: Define initial models, no migrations
38 #######################################################
40 Base1
= declarative_base(cls
=GMGTableBase
)
42 class Creature1(Base1
):
43 __tablename__
= "creature"
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
)
51 __tablename__
= "level"
53 id = Column(Unicode
, primary_key
=True)
54 name
= Column(Unicode
)
55 description
= Column(Unicode
)
56 exits
= Column(PickleType
)
58 SET1_MODELS
= [Creature1
, Level1
]
62 #######################################################
63 # Migration set 2: A few migrations and new model
64 #######################################################
66 Base2
= declarative_base(cls
=GMGTableBase
)
68 class Creature2(Base2
):
69 __tablename__
= "creature"
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")
76 class CreaturePower2(Base2
):
77 __tablename__
= "creature_power"
79 id = Column(Integer
, primary_key
=True)
81 Integer
, ForeignKey('creature.id'), nullable
=False)
82 name
= Column(Unicode
)
83 description
= Column(Unicode
)
84 hitpower
= Column(Integer
, nullable
=False)
87 __tablename__
= "level"
89 id = Column(Unicode
, primary_key
=True)
90 name
= Column(Unicode
)
91 description
= Column(Unicode
)
93 class LevelExit2(Base2
):
94 __tablename__
= "level_exit"
96 id = Column(Integer
, primary_key
=True)
97 name
= Column(Unicode
)
99 Unicode
, ForeignKey('level.id'), nullable
=False)
101 Unicode
, ForeignKey('level.id'), nullable
=False)
103 SET2_MODELS
= [Creature2
, CreaturePower2
, Level2
, LevelExit2
]
106 @RegisterMigration(1, FULL_MIGRATIONS
)
107 def creature_remove_is_demon(db_conn
):
109 Remove the is_demon field from the creature model. We don't need
112 # :( Commented out 'cuz of:
113 # http://code.google.com/p/sqlalchemy-migrate/issues/detail?id=143&thanks=143&ts=1327882242
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')
123 @RegisterMigration(2, FULL_MIGRATIONS
)
124 def creature_powers_new_table(db_conn
):
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
130 metadata
= MetaData(bind
=db_conn
.bind
)
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
)
138 creature_powers
= Table(
139 'creature_power', metadata
,
140 Column('id', Integer
, primary_key
=True),
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
)
149 @RegisterMigration(3, FULL_MIGRATIONS
)
150 def level_exits_new_table(db_conn
):
152 Make a new table for level exits and move the previously pickled
153 stuff over to here (then drop the old unneeded table)
155 # First, create the table
156 # -----------------------
157 metadata
= MetaData(bind
=db_conn
.bind
)
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.
164 Column('id', Unicode
, primary_key
=True),
165 Column('name', Unicode
),
166 Column('description', Unicode
),
167 Column('exits', PickleType
))
170 'level_exit', metadata
,
171 Column('id', Integer
, primary_key
=True),
172 Column('name', Unicode
),
174 Unicode
, ForeignKey('level.id'), nullable
=False),
176 Unicode
, ForeignKey('level.id'), nullable
=False))
177 metadata
.create_all(db_conn
.bind
)
179 # And now, convert all the old exit pickles to new level exits
180 # ------------------------------------------------------------
182 # query over and insert
183 result
= db_conn
.execute(
184 select([levels
], levels
.c
.exits
!=None))
188 for exit_name
, to_level
in level
['exits'].iteritems():
189 # Insert the level exit
191 level_exits
.insert().values(
196 # Finally, drop the old level exits pickle table
197 # ----------------------------------------------
198 levels
.drop_column('exits')
201 # A hack! At this point we freeze-fame and get just a partial list of
204 SET2_MIGRATIONS
= copy
.copy(FULL_MIGRATIONS
)
206 #######################################################
207 # Migration set 3: Final migrations
208 #######################################################
210 Base3
= declarative_base(cls
=GMGTableBase
)
212 class Creature3(Base3
):
213 __tablename__
= "creature"
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")
220 class CreaturePower3(Base3
):
221 __tablename__
= "creature_power"
223 id = Column(Integer
, primary_key
=True)
225 Integer
, ForeignKey('creature.id'), nullable
=False, index
=True)
226 name
= Column(Unicode
)
227 description
= Column(Unicode
)
228 hitpower
= Column(Float
, nullable
=False)
231 __tablename__
= "level"
233 id = Column(Unicode
, primary_key
=True)
234 name
= Column(Unicode
)
235 description
= Column(Unicode
)
237 class LevelExit3(Base3
):
238 __tablename__
= "level_exit"
240 id = Column(Integer
, primary_key
=True)
241 name
= Column(Unicode
)
243 Unicode
, ForeignKey('level.id'), nullable
=False, index
=True)
245 Unicode
, ForeignKey('level.id'), nullable
=False, index
=True)
248 SET3_MODELS
= [Creature3
, CreaturePower3
, Level3
, LevelExit3
]
249 SET3_MIGRATIONS
= FULL_MIGRATIONS
252 @RegisterMigration(4, FULL_MIGRATIONS
)
253 def creature_num_legs_to_num_limbs(db_conn
):
255 Turns out we're tracking all sorts of limbs, not "legs"
256 specifically. Humans would be 4 here, for instance. So we
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")
266 @RegisterMigration(5, FULL_MIGRATIONS
)
267 def level_exit_index_from_and_to_level(db_conn
):
269 Index the from and to levels of the level exit table.
271 metadata
= MetaData(bind
=db_conn
.bind
)
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
)
281 @RegisterMigration(6, FULL_MIGRATIONS
)
282 def creature_power_index_creature(db_conn
):
284 Index our foreign key relationship to the creatures
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
)
294 @RegisterMigration(7, FULL_MIGRATIONS
)
295 def creature_power_hitpower_to_float(db_conn
):
297 Convert hitpower column on creature power table from integer to
300 Turns out we want super precise values of how much hitpower there
303 metadata
= MetaData(bind
=db_conn
.bind
)
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
)
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,
317 Column('name', Unicode
),
318 Column('description', Unicode
),
319 Column('hitpower', Integer
, nullable
=False))
321 creature_power
.c
.hitpower
.alter(type=Float
)
324 def _insert_migration1_objects(session
):
326 Test objects to insert for the first set of things
330 [Creature1(name
=u
'centipede',
333 Creature1(name
=u
'wolf',
336 # don't ask me what a wizardsnake is.
337 Creature1(name
=u
'wizardsnake',
343 [Level1(id=u
'necroplex',
344 name
=u
'The Necroplex',
345 description
=u
'A complex full of pure deathzone.',
347 u
'deathwell': u
'evilstorm',
348 u
'portal': u
'central_park'}),
349 Level1(id=u
'evilstorm',
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.",
357 u
'portal': u
'necroplex'})])
362 def _insert_migration2_objects(session
):
364 Test objects to insert for the second set of things
377 description
=u
"A blast of icy breath!",
381 description
=u
"A frightening stare, for sure!",
388 name
=u
'death_rattle',
389 description
=u
'A rattle... of DEATH!',
392 name
=u
'sneaky_stare',
393 description
=u
"The sneakiest stare you've ever seen!",
396 name
=u
'slithery_smoke',
397 description
=u
"A blast of slithery, slithery smoke.",
400 name
=u
'treacherous_tremors',
401 description
=u
"The ground shakes beneath footed animals!",
406 [Level2(id=u
'necroplex',
407 name
=u
'The Necroplex',
408 description
=u
'A complex full of pure deathzone.'),
409 Level2(id=u
'evilstorm',
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.")])
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')])
426 # there are no evilstorm exits because there is no exit from the
431 [LevelExit2(name
=u
'portal',
432 from_level
=u
'central_park',
433 to_level
=u
'necroplex')])
438 def _insert_migration3_objects(session
):
440 Test objects to insert for the third set of things
453 description
=u
"A blast of icy breath!",
457 description
=u
"A frightening stare, for sure!",
464 name
=u
'death_rattle',
465 description
=u
'A rattle... of DEATH!',
468 name
=u
'sneaky_stare',
469 description
=u
"The sneakiest stare you've ever seen!",
472 name
=u
'slithery_smoke',
473 description
=u
"A blast of slithery, slithery smoke.",
476 name
=u
'treacherous_tremors',
477 description
=u
"The ground shakes beneath footed animals!",
479 # annnnnd one more to test a floating point hitpower
486 description
=u
'Smitten by holy wrath!',
491 [Level3(id=u
'necroplex',
492 name
=u
'The Necroplex',
493 description
=u
'A complex full of pure deathzone.'),
494 Level3(id=u
'evilstorm',
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.")])
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')])
511 # there are no evilstorm exits because there is no exit from the
516 [LevelExit3(name
=u
'portal',
517 from_level
=u
'central_park',
518 to_level
=u
'necroplex')])
523 class CollectingPrinter(object):
527 def __call__(self
, string
):
528 self
.collection
.append(string
)
531 def combined_string(self
):
532 return u
''.join(self
.collection
)
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
542 def assert_col_type(column
, this_class
):
543 assert isinstance(column
.type, this_class
)
546 def _get_level3_exits(session
, level
):
548 [(level_exit
.name
, level_exit
.to_level
)
550 session
.query(LevelExit3
).filter_by(from_level
=level
.id)])
553 def test_set1_to_set3():
554 # Create / connect to database
555 # ----------------------------
557 engine
, Session
= create_test_engine()
559 # Create tables by migrating on empty initial set
560 # -----------------------------------------------
562 printer
= CollectingPrinter()
563 migration_manager
= MigrationManager(
564 u
'__main__', SET1_MODELS
, SET1_MIGRATIONS
, Session(),
567 # Check latest migration and database current migration
568 assert migration_manager
.latest_migration
== 0
569 assert migration_manager
.database_current_migration
== None
571 result
= migration_manager
.init_or_migrate()
573 # Make sure output was "inited"
574 assert result
== u
'inited'
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
582 # Install the initial set
583 # -----------------------
585 _insert_migration1_objects(Session())
587 # Try to "re-migrate" with same manager settings... nothing should happen
588 migration_manager
= MigrationManager(
589 u
'__main__', SET1_MODELS
, SET1_MIGRATIONS
, Session(),
591 assert migration_manager
.init_or_migrate() == None
593 # Check version in database
594 assert migration_manager
.latest_migration
== 0
595 assert migration_manager
.database_current_migration
== 0
597 # Sanity check a few things in the database...
598 metadata
= MetaData(bind
=engine
)
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
)
615 # Check the structure of the level table
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.
628 # Now check to see if stuff seems to be in there.
631 creature
= session
.query(Creature1
).filter_by(
632 name
=u
'centipede').one()
633 assert creature
.num_legs
== 100
634 assert creature
.is_demon
== False
636 creature
= session
.query(Creature1
).filter_by(
638 assert creature
.num_legs
== 4
639 assert creature
.is_demon
== False
641 creature
= session
.query(Creature1
).filter_by(
642 name
=u
'wizardsnake').one()
643 assert creature
.num_legs
== 0
644 assert creature
.is_demon
== True
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'}
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!
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'}
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 u
'__main__', SET3_MODELS
, SET3_MIGRATIONS
, Session(),
674 assert migration_manager
.latest_migration
== 7
675 assert migration_manager
.database_current_migration
== 0
678 result
= migration_manager
.init_or_migrate()
680 # Make sure result was "migrated"
681 assert result
== u
'migrated'
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.
695 # Make sure version matches expected
696 migration_manager
= MigrationManager(
697 u
'__main__', SET3_MODELS
, SET3_MIGRATIONS
, Session(),
699 assert migration_manager
.latest_migration
== 7
700 assert migration_manager
.database_current_migration
== 7
702 # Check all things in database match expected
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
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
735 # Check the structure of the level table
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
)
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
761 # Now check to see if stuff seems to be in there.
763 creature
= session
.query(Creature3
).filter_by(
764 name
=u
'centipede').one()
765 assert creature
.num_limbs
== 100.0
766 assert creature
.magical_powers
== []
768 creature
= session
.query(Creature3
).filter_by(
770 assert creature
.num_limbs
== 4.0
771 assert creature
.magical_powers
== []
773 creature
= session
.query(Creature3
).filter_by(
774 name
=u
'wizardsnake').one()
775 assert creature
.num_limbs
== 0.0
776 assert creature
.magical_powers
== []
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'}
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!
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'}
803 #def test_set2_to_set3():
804 # Create / connect to database
805 # Create tables by migrating on empty initial set
807 # Install the initial set
808 # Check version in database
809 # Sanity check a few things in the database
812 # Make sure version matches expected
813 # Check all things in database match expected
817 #def test_set1_to_set2_to_set3():
818 # Create / connect to database
819 # Create tables by migrating on empty initial set
821 # Install the initial set
822 # Check version in database
823 # Sanity check a few things in the database
826 # Make sure version matches expected
827 # Check all things in database match expected
830 # Make sure version matches expected again
831 # Check all things in database match expected again
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
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
861 # # Check the structure of the level table
862 # level_table = Table(
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)
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)