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 # metadata = MetaData(bind=db_conn.bind)
113 # creature_table = Table(
114 # 'creature', metadata,
115 # autoload=True, autoload_with=db_conn.bind)
116 # creature_table.drop_column('is_demon')
120 @RegisterMigration(2, FULL_MIGRATIONS
)
121 def creature_powers_new_table(db_conn
):
123 Add a new table for creature powers. Nothing needs to go in it
124 yet though as there wasn't anything that previously held this
127 metadata
= MetaData(bind
=db_conn
.bind
)
129 # We have to access the creature table so sqlalchemy can make the
130 # foreign key relationship
131 creature_table
= Table(
132 'creature', metadata
,
133 autoload
=True, autoload_with
=db_conn
.bind
)
135 creature_powers
= Table(
136 'creature_power', metadata
,
137 Column('id', Integer
, primary_key
=True),
139 Integer
, ForeignKey('creature.id'), nullable
=False),
140 Column('name', Unicode
),
141 Column('description', Unicode
),
142 Column('hitpower', Integer
, nullable
=False))
143 metadata
.create_all(db_conn
.bind
)
146 @RegisterMigration(3, FULL_MIGRATIONS
)
147 def level_exits_new_table(db_conn
):
149 Make a new table for level exits and move the previously pickled
150 stuff over to here (then drop the old unneeded table)
152 # First, create the table
153 # -----------------------
154 metadata
= MetaData(bind
=db_conn
.bind
)
156 # Minimal representation of level table.
157 # Not auto-introspecting here because of pickle table. I'm not
158 # sure sqlalchemy can auto-introspect pickle columns.
161 Column('id', Unicode
, primary_key
=True),
162 Column('name', Unicode
),
163 Column('description', Unicode
),
164 Column('exits', PickleType
))
167 'level_exit', metadata
,
168 Column('id', Integer
, primary_key
=True),
169 Column('name', Unicode
),
171 Unicode
, ForeignKey('level.id'), nullable
=False),
173 Unicode
, ForeignKey('level.id'), nullable
=False))
174 metadata
.create_all(db_conn
.bind
)
176 # And now, convert all the old exit pickles to new level exits
177 # ------------------------------------------------------------
179 # query over and insert
180 result
= db_conn
.execute(
181 select([levels
], levels
.c
.exits
!=None))
185 for exit_name
, to_level
in level
['exits'].iteritems():
186 # Insert the level exit
188 level_exits
.insert().values(
193 # Finally, drop the old level exits pickle table
194 # ----------------------------------------------
195 levels
.drop_column('exits')
198 # A hack! At this point we freeze-fame and get just a partial list of
201 SET2_MIGRATIONS
= copy
.copy(FULL_MIGRATIONS
)
203 #######################################################
204 # Migration set 3: Final migrations
205 #######################################################
207 Base3
= declarative_base(cls
=GMGTableBase
)
209 class Creature3(Base3
):
210 __tablename__
= "creature"
212 id = Column(Integer
, primary_key
=True)
213 name
= Column(Unicode
, unique
=True, nullable
=False, index
=True)
214 num_limbs
= Column(Integer
, nullable
=False)
215 magical_powers
= relationship("CreaturePower3")
217 class CreaturePower3(Base3
):
218 __tablename__
= "creature_power"
220 id = Column(Integer
, primary_key
=True)
222 Integer
, ForeignKey('creature.id'), nullable
=False, index
=True)
223 name
= Column(Unicode
)
224 description
= Column(Unicode
)
225 hitpower
= Column(Float
, nullable
=False)
228 __tablename__
= "level"
230 id = Column(Unicode
, primary_key
=True)
231 name
= Column(Unicode
)
232 description
= Column(Unicode
)
234 class LevelExit3(Base3
):
235 __tablename__
= "level_exit"
237 id = Column(Integer
, primary_key
=True)
238 name
= Column(Unicode
)
240 Unicode
, ForeignKey('level.id'), nullable
=False, index
=True)
242 Unicode
, ForeignKey('level.id'), nullable
=False, index
=True)
245 SET3_MODELS
= [Creature3
, CreaturePower3
, Level3
, LevelExit3
]
246 SET3_MIGRATIONS
= FULL_MIGRATIONS
249 @RegisterMigration(4, FULL_MIGRATIONS
)
250 def creature_num_legs_to_num_limbs(db_conn
):
252 Turns out we're tracking all sorts of limbs, not "legs"
253 specifically. Humans would be 4 here, for instance. So we
256 metadata
= MetaData(bind
=db_conn
.bind
)
257 creature_table
= Table(
258 'creature', metadata
,
259 autoload
=True, autoload_with
=db_conn
.bind
)
260 creature_table
.c
.num_legs
.alter(name
=u
"num_limbs")
263 @RegisterMigration(5, FULL_MIGRATIONS
)
264 def level_exit_index_from_and_to_level(db_conn
):
266 Index the from and to levels of the level exit table.
268 metadata
= MetaData(bind
=db_conn
.bind
)
270 'level_exit', metadata
,
271 autoload
=True, autoload_with
=db_conn
.bind
)
272 Index('ix_level_exit_from_level',
273 level_exit
.c
.from_level
).create(db_conn
.bind
)
274 Index('ix_level_exit_to_level',
275 level_exit
.c
.to_level
).create(db_conn
.bind
)
278 @RegisterMigration(6, FULL_MIGRATIONS
)
279 def creature_power_index_creature(db_conn
):
281 Index our foreign key relationship to the creatures
283 metadata
= MetaData(bind
=db_conn
.bind
)
284 creature_power
= Table(
285 'creature_power', metadata
,
286 autoload
=True, autoload_with
=db_conn
.bind
)
287 Index('ix_creature_power_creature',
288 creature_power
.c
.creature
).create(db_conn
.bind
)
291 @RegisterMigration(7, FULL_MIGRATIONS
)
292 def creature_power_hitpower_to_float(db_conn
):
294 Convert hitpower column on creature power table from integer to
297 Turns out we want super precise values of how much hitpower there
300 metadata
= MetaData(bind
=db_conn
.bind
)
302 # We have to access the creature table so sqlalchemy can make the
303 # foreign key relationship
304 creature_table
= Table(
305 'creature', metadata
,
306 autoload
=True, autoload_with
=db_conn
.bind
)
308 creature_power
= Table(
309 'creature_power', metadata
,
310 Column('id', Integer
, primary_key
=True),
311 Column('creature', Integer
,
312 ForeignKey('creature.id'), nullable
=False,
314 Column('name', Unicode
),
315 Column('description', Unicode
),
316 Column('hitpower', Integer
, nullable
=False))
318 creature_power
.c
.hitpower
.alter(type=Float
)
321 def _insert_migration1_objects(session
):
323 Test objects to insert for the first set of things
327 [Creature1(name
=u
'centipede',
330 Creature1(name
=u
'wolf',
333 # don't ask me what a wizardsnake is.
334 Creature1(name
=u
'wizardsnake',
340 [Level1(id=u
'necroplex',
341 name
=u
'The Necroplex',
342 description
=u
'A complex full of pure deathzone.',
344 'deathwell': 'evilstorm',
345 'portal': 'central_park'}),
346 Level1(id=u
'evilstorm',
348 description
=u
'A storm full of pure evil.',
349 exits
={}), # you can't escape the evilstorm
350 Level1(id=u
'central_park',
351 name
=u
'Central Park, NY, NY',
352 description
=u
"New York's friendly Central Park.",
354 'portal': 'necroplex'})])
359 def _insert_migration2_objects(session
):
361 Test objects to insert for the second set of things
374 description
=u
"A blast of icy breath!",
378 description
=u
"A frightening stare, for sure!",
385 name
=u
'death_rattle',
386 description
=u
'A rattle... of DEATH!',
389 name
=u
'sneaky_stare',
390 description
=u
"The sneakiest stare you've ever seen!",
393 name
=u
'slithery_smoke',
394 description
=u
"A blast of slithery, slithery smoke.",
397 name
=u
'treacherous_tremors',
398 description
=u
"The ground shakes beneath footed animals!",
403 [Level2(id=u
'necroplex',
404 name
=u
'The Necroplex',
405 description
=u
'A complex full of pure deathzone.'),
406 Level2(id=u
'evilstorm',
408 description
=u
'A storm full of pure evil.',
409 exits
=[]), # you can't escape the evilstorm
410 Level2(id=u
'central_park',
411 name
=u
'Central Park, NY, NY',
412 description
=u
"New York's friendly Central Park.")])
416 [LevelExit2(name
=u
'deathwell',
417 from_level
=u
'necroplex',
418 to_level
=u
'evilstorm'),
419 LevelExit2(name
=u
'portal',
420 from_level
=u
'necroplex',
421 to_level
=u
'central_park')])
423 # there are no evilstorm exits because there is no exit from the
428 [LevelExit2(name
=u
'portal',
429 from_level
=u
'central_park',
430 to_level
=u
'necroplex')])
435 def _insert_migration3_objects(session
):
437 Test objects to insert for the third set of things
450 description
=u
"A blast of icy breath!",
454 description
=u
"A frightening stare, for sure!",
461 name
=u
'death_rattle',
462 description
=u
'A rattle... of DEATH!',
465 name
=u
'sneaky_stare',
466 description
=u
"The sneakiest stare you've ever seen!",
469 name
=u
'slithery_smoke',
470 description
=u
"A blast of slithery, slithery smoke.",
473 name
=u
'treacherous_tremors',
474 description
=u
"The ground shakes beneath footed animals!",
476 # annnnnd one more to test a floating point hitpower
483 description
=u
'Smitten by holy wrath!',
488 [Level3(id=u
'necroplex',
489 name
=u
'The Necroplex',
490 description
=u
'A complex full of pure deathzone.'),
491 Level3(id=u
'evilstorm',
493 description
=u
'A storm full of pure evil.',
494 exits
=[]), # you can't escape the evilstorm
495 Level3(id=u
'central_park',
496 name
=u
'Central Park, NY, NY',
497 description
=u
"New York's friendly Central Park.")])
501 [LevelExit3(name
=u
'deathwell',
502 from_level
=u
'necroplex',
503 to_level
=u
'evilstorm'),
504 LevelExit3(name
=u
'portal',
505 from_level
=u
'necroplex',
506 to_level
=u
'central_park')])
508 # there are no evilstorm exits because there is no exit from the
513 [LevelExit3(name
=u
'portal',
514 from_level
=u
'central_park',
515 to_level
=u
'necroplex')])
520 class CollectingPrinter(object):
524 def __call__(self
, string
):
525 self
.collection
.append(string
)
528 def combined_string(self
):
529 return u
''.join(self
.collection
)
532 def create_test_engine():
533 from sqlalchemy
import create_engine
534 engine
= create_engine('sqlite:///:memory:', echo
=False)
535 Session
= sessionmaker(bind
=engine
)
536 return engine
, Session
539 def assert_col_type(column
, this_class
):
540 assert isinstance(column
.type, this_class
)
543 def _get_level3_exits(session
, level
):
545 [(level_exit
.name
, level_exit
.to_level
)
547 session
.query(LevelExit3
).filter_by(from_level
=level
.id)])
550 def test_set1_to_set3():
551 # Create / connect to database
552 # ----------------------------
554 engine
, Session
= create_test_engine()
556 # Create tables by migrating on empty initial set
557 # -----------------------------------------------
559 printer
= CollectingPrinter()
560 migration_manager
= MigrationManager(
561 '__main__', SET1_MODELS
, SET1_MIGRATIONS
, Session(),
564 # Check latest migration and database current migration
565 assert migration_manager
.latest_migration
== 0
566 assert migration_manager
.database_current_migration
== None
568 result
= migration_manager
.init_or_migrate()
570 # Make sure output was "inited"
571 assert result
== u
'inited'
573 assert printer
.combined_string
== (
574 "-> Initializing main mediagoblin tables... done.\n")
575 # Check version in database
576 assert migration_manager
.latest_migration
== 0
577 assert migration_manager
.database_current_migration
== 0
579 # Install the initial set
580 # -----------------------
582 _insert_migration1_objects(Session())
584 # Try to "re-migrate" with same manager settings... nothing should happen
585 migration_manager
= MigrationManager(
586 '__main__', SET1_MODELS
, SET1_MIGRATIONS
, Session(),
588 assert migration_manager
.init_or_migrate() == None
590 # Check version in database
591 assert migration_manager
.latest_migration
== 0
592 assert migration_manager
.database_current_migration
== 0
594 # Sanity check a few things in the database...
595 metadata
= MetaData(bind
=engine
)
597 # Check the structure of the creature table
598 creature_table
= Table(
599 'creature', metadata
,
600 autoload
=True, autoload_with
=engine
)
601 assert set(creature_table
.c
.keys()) == set(
602 ['id', 'name', 'num_legs', 'is_demon'])
603 assert_col_type(creature_table
.c
.id, Integer
)
604 assert_col_type(creature_table
.c
.name
, VARCHAR
)
605 assert creature_table
.c
.name
.nullable
is False
606 #assert creature_table.c.name.index is True
607 #assert creature_table.c.name.unique is True
608 assert_col_type(creature_table
.c
.num_legs
, Integer
)
609 assert creature_table
.c
.num_legs
.nullable
is False
610 assert_col_type(creature_table
.c
.is_demon
, Boolean
)
612 # Check the structure of the level table
615 autoload
=True, autoload_with
=engine
)
616 assert set(level_table
.c
.keys()) == set(
617 ['id', 'name', 'description', 'exits'])
618 assert_col_type(level_table
.c
.id, VARCHAR
)
619 assert level_table
.c
.id.primary_key
is True
620 assert_col_type(level_table
.c
.name
, VARCHAR
)
621 assert_col_type(level_table
.c
.description
, VARCHAR
)
622 # Skipping exits... Not sure if we can detect pickletype, not a
623 # big deal regardless.
625 # Now check to see if stuff seems to be in there.
628 creature
= session
.query(Creature1
).filter_by(
629 name
=u
'centipede').one()
630 assert creature
.num_legs
== 100
631 assert creature
.is_demon
== False
633 creature
= session
.query(Creature1
).filter_by(
635 assert creature
.num_legs
== 4
636 assert creature
.is_demon
== False
638 creature
= session
.query(Creature1
).filter_by(
639 name
=u
'wizardsnake').one()
640 assert creature
.num_legs
== 0
641 assert creature
.is_demon
== True
643 level
= session
.query(Level1
).filter_by(
644 id=u
'necroplex').one()
645 assert level
.name
== u
'The Necroplex'
646 assert level
.description
== u
'A complex full of pure deathzone.'
647 assert level
.exits
== {
648 'deathwell': 'evilstorm',
649 'portal': 'central_park'}
651 level
= session
.query(Level1
).filter_by(
652 id=u
'evilstorm').one()
653 assert level
.name
== u
'Evil Storm'
654 assert level
.description
== u
'A storm full of pure evil.'
655 assert level
.exits
== {} # You still can't escape the evilstorm!
657 level
= session
.query(Level1
).filter_by(
658 id=u
'central_park').one()
659 assert level
.name
== u
'Central Park, NY, NY'
660 assert level
.description
== u
"New York's friendly Central Park."
661 assert level
.exits
== {
662 'portal': 'necroplex'}
664 # Create new migration manager, but make sure the db migration
665 # isn't said to be updated yet
666 printer
= CollectingPrinter()
667 migration_manager
= MigrationManager(
668 '__main__', SET3_MODELS
, SET3_MIGRATIONS
, Session(),
671 assert migration_manager
.latest_migration
== 7
672 assert migration_manager
.database_current_migration
== 0
675 result
= migration_manager
.init_or_migrate()
677 # Make sure result was "migrated"
678 assert result
== u
'migrated'
680 # TODO: Check output to user
681 assert printer
.combined_string
== """\
682 -> Updating main mediagoblin tables:
683 + Running migration 1, "creature_remove_is_demon"... done.
684 + Running migration 2, "creature_powers_new_table"... done.
685 + Running migration 3, "level_exits_new_table"... done.
686 + Running migration 4, "creature_num_legs_to_num_limbs"... done.
687 + Running migration 5, "level_exit_index_from_and_to_level"... done.
688 + Running migration 6, "creature_power_index_creature"... done.
689 + Running migration 7, "creature_power_hitpower_to_float"... done.
692 # Make sure version matches expected
693 migration_manager
= MigrationManager(
694 '__main__', SET3_MODELS
, SET3_MIGRATIONS
, Session(),
696 assert migration_manager
.latest_migration
== 7
697 assert migration_manager
.database_current_migration
== 7
699 # Check all things in database match expected
701 # Check the creature table
702 metadata
= MetaData(bind
=engine
)
703 creature_table
= Table(
704 'creature', metadata
,
705 autoload
=True, autoload_with
=engine
)
706 # assert set(creature_table.c.keys()) == set(
707 # ['id', 'name', 'num_limbs'])
708 assert set(creature_table
.c
.keys()) == set(
709 [u
'id', 'name', u
'num_limbs', u
'is_demon'])
710 assert_col_type(creature_table
.c
.id, Integer
)
711 assert_col_type(creature_table
.c
.name
, VARCHAR
)
712 assert creature_table
.c
.name
.nullable
is False
713 #assert creature_table.c.name.index is True
714 #assert creature_table.c.name.unique is True
715 assert_col_type(creature_table
.c
.num_limbs
, Integer
)
716 assert creature_table
.c
.num_limbs
.nullable
is False
718 # Check the CreaturePower table
719 creature_power_table
= Table(
720 'creature_power', metadata
,
721 autoload
=True, autoload_with
=engine
)
722 assert set(creature_power_table
.c
.keys()) == set(
723 ['id', 'creature', 'name', 'description', 'hitpower'])
724 assert_col_type(creature_power_table
.c
.id, Integer
)
725 assert_col_type(creature_power_table
.c
.creature
, Integer
)
726 assert creature_power_table
.c
.creature
.nullable
is False
727 assert_col_type(creature_power_table
.c
.name
, VARCHAR
)
728 assert_col_type(creature_power_table
.c
.description
, VARCHAR
)
729 assert_col_type(creature_power_table
.c
.hitpower
, Float
)
730 assert creature_power_table
.c
.hitpower
.nullable
is False
732 # Check the structure of the level table
735 autoload
=True, autoload_with
=engine
)
736 assert set(level_table
.c
.keys()) == set(
737 ['id', 'name', 'description'])
738 assert_col_type(level_table
.c
.id, VARCHAR
)
739 assert level_table
.c
.id.primary_key
is True
740 assert_col_type(level_table
.c
.name
, VARCHAR
)
741 assert_col_type(level_table
.c
.description
, VARCHAR
)
743 # Check the structure of the level_exits table
744 level_exit_table
= Table(
745 'level_exit', metadata
,
746 autoload
=True, autoload_with
=engine
)
747 assert set(level_exit_table
.c
.keys()) == set(
748 ['id', 'name', 'from_level', 'to_level'])
749 assert_col_type(level_exit_table
.c
.id, Integer
)
750 assert_col_type(level_exit_table
.c
.name
, VARCHAR
)
751 assert_col_type(level_exit_table
.c
.from_level
, VARCHAR
)
752 assert level_exit_table
.c
.from_level
.nullable
is False
753 #assert level_exit_table.c.from_level.index is True
754 assert_col_type(level_exit_table
.c
.to_level
, VARCHAR
)
755 assert level_exit_table
.c
.to_level
.nullable
is False
756 #assert level_exit_table.c.to_level.index is True
758 # Now check to see if stuff seems to be in there.
760 creature
= session
.query(Creature3
).filter_by(
761 name
=u
'centipede').one()
762 assert creature
.num_limbs
== 100.0
763 assert creature
.magical_powers
== []
765 creature
= session
.query(Creature3
).filter_by(
767 assert creature
.num_limbs
== 4.0
768 assert creature
.magical_powers
== []
770 creature
= session
.query(Creature3
).filter_by(
771 name
=u
'wizardsnake').one()
772 assert creature
.num_limbs
== 0.0
773 assert creature
.magical_powers
== []
775 level
= session
.query(Level3
).filter_by(
776 id=u
'necroplex').one()
777 assert level
.name
== u
'The Necroplex'
778 assert level
.description
== u
'A complex full of pure deathzone.'
779 level_exits
= _get_level3_exits(session
, level
)
780 assert level_exits
== {
781 u
'deathwell': u
'evilstorm',
782 u
'portal': u
'central_park'}
784 level
= session
.query(Level3
).filter_by(
785 id=u
'evilstorm').one()
786 assert level
.name
== u
'Evil Storm'
787 assert level
.description
== u
'A storm full of pure evil.'
788 level_exits
= _get_level3_exits(session
, level
)
789 assert level_exits
== {} # You still can't escape the evilstorm!
791 level
= session
.query(Level3
).filter_by(
792 id=u
'central_park').one()
793 assert level
.name
== u
'Central Park, NY, NY'
794 assert level
.description
== u
"New York's friendly Central Park."
795 level_exits
= _get_level3_exits(session
, level
)
796 assert level_exits
== {
797 'portal': 'necroplex'}
800 def test_set2_to_set3():
801 # Create / connect to database
802 # Create tables by migrating on empty initial set
804 # Install the initial set
805 # Check version in database
806 # Sanity check a few things in the database
809 # Make sure version matches expected
810 # Check all things in database match expected
814 def test_set1_to_set2_to_set3():
815 # Create / connect to database
816 # Create tables by migrating on empty initial set
818 # Install the initial set
819 # Check version in database
820 # Sanity check a few things in the database
823 # Make sure version matches expected
824 # Check all things in database match expected
827 # Make sure version matches expected again
828 # Check all things in database match expected again
831 # creature_table = Table(
832 # 'creature', metadata,
833 # autoload=True, autoload_with=db_conn.bind)
834 # assert set(creature_table.c.keys()) == set(
835 # ['id', 'name', 'num_legs'])
836 # assert_col_type(creature_table.c.id, Integer)
837 # assert_col_type(creature_table.c.name, VARCHAR)
838 # assert creature_table.c.name.nullable is False
839 # assert creature_table.c.name.index is True
840 # assert creature_table.c.name.unique is True
841 # assert_col_type(creature_table.c.num_legs, Integer)
842 # assert creature_table.c.num_legs.nullable is False
844 # # Check the CreaturePower table
845 # creature_power_table = Table(
846 # 'creature_power', metadata,
847 # autoload=True, autoload_with=db_conn.bind)
848 # assert set(creature_power_table.c.keys()) == set(
849 # ['id', 'creature', 'name', 'description', 'hitpower'])
850 # assert_col_type(creature_power_table.c.id, Integer)
851 # assert_col_type(creature_power_table.c.creature, Integer)
852 # assert creature_power_table.c.creature.nullable is False
853 # assert_col_type(creature_power_table.c.name, VARCHAR)
854 # assert_col_type(creature_power_table.c.description, VARCHAR)
855 # assert_col_type(creature_power_table.c.hitpower, Integer)
856 # assert creature_power_table.c.hitpower.nullable is False
858 # # Check the structure of the level table
859 # level_table = Table(
861 # autoload=True, autoload_with=db_conn.bind)
862 # assert set(level_table.c.keys()) == set(
863 # ['id', 'name', 'description'])
864 # assert_col_type(level_table.c.id, VARCHAR)
865 # assert level_table.c.id.primary_key is True
866 # assert_col_type(level_table.c.name, VARCHAR)
867 # assert_col_type(level_table.c.description, VARCHAR)
869 # # Check the structure of the level_exits table
870 # level_exit_table = Table(
871 # 'level_exit', metadata,
872 # autoload=True, autoload_with=db_conn.bind)
873 # assert set(level_exit_table.c.keys()) == set(
874 # ['id', 'name', 'from_level', 'to_level'])
875 # assert_col_type(level_exit_table.c.id, Integer)
876 # assert_col_type(level_exit_table.c.name, VARCHAR)
877 # assert_col_type(level_exit_table.c.from_level, VARCHAR)
878 # assert level_exit_table.c.from_level.nullable is False
879 # assert_col_type(level_exit_table.c.to_level, VARCHAR)