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
.base
import GMGTableBase
29 from mediagoblin
.db
.migration_tools
import MigrationManager
, RegisterMigration
30 from mediagoblin
.tools
.common
import CollectingPrinter
33 # This one will get filled with local migrations
37 #######################################################
38 # Migration set 1: Define initial models, no migrations
39 #######################################################
41 Base1
= declarative_base(cls
=GMGTableBase
)
43 class Creature1(Base1
):
44 __tablename__
= "creature"
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
)
52 __tablename__
= "level"
54 id = Column(Unicode
, primary_key
=True)
55 name
= Column(Unicode
)
56 description
= Column(Unicode
)
57 exits
= Column(PickleType
)
59 SET1_MODELS
= [Creature1
, Level1
]
61 FOUNDATIONS
= {Creature1
:[{'name':u
'goblin','num_legs':2,'is_demon':False},
62 {'name':u
'cerberus','num_legs':4,'is_demon':True}]}
66 #######################################################
67 # Migration set 2: A few migrations and new model
68 #######################################################
70 Base2
= declarative_base(cls
=GMGTableBase
)
72 class Creature2(Base2
):
73 __tablename__
= "creature"
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")
80 class CreaturePower2(Base2
):
81 __tablename__
= "creature_power"
83 id = Column(Integer
, primary_key
=True)
85 Integer
, ForeignKey('creature.id'), nullable
=False)
86 name
= Column(Unicode
)
87 description
= Column(Unicode
)
88 hitpower
= Column(Integer
, nullable
=False)
91 __tablename__
= "level"
93 id = Column(Unicode
, primary_key
=True)
94 name
= Column(Unicode
)
95 description
= Column(Unicode
)
97 class LevelExit2(Base2
):
98 __tablename__
= "level_exit"
100 id = Column(Integer
, primary_key
=True)
101 name
= Column(Unicode
)
103 Unicode
, ForeignKey('level.id'), nullable
=False)
105 Unicode
, ForeignKey('level.id'), nullable
=False)
107 SET2_MODELS
= [Creature2
, CreaturePower2
, Level2
, LevelExit2
]
110 @RegisterMigration(1, FULL_MIGRATIONS
)
111 def creature_remove_is_demon(db_conn
):
113 Remove the is_demon field from the creature model. We don't need
116 # :( Commented out 'cuz of:
117 # http://code.google.com/p/sqlalchemy-migrate/issues/detail?id=143&thanks=143&ts=1327882242
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')
127 @RegisterMigration(2, FULL_MIGRATIONS
)
128 def creature_powers_new_table(db_conn
):
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
134 metadata
= MetaData(bind
=db_conn
.bind
)
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
)
142 creature_powers
= Table(
143 'creature_power', metadata
,
144 Column('id', Integer
, primary_key
=True),
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
)
153 @RegisterMigration(3, FULL_MIGRATIONS
)
154 def level_exits_new_table(db_conn
):
156 Make a new table for level exits and move the previously pickled
157 stuff over to here (then drop the old unneeded table)
159 # First, create the table
160 # -----------------------
161 metadata
= MetaData(bind
=db_conn
.bind
)
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.
168 Column('id', Unicode
, primary_key
=True),
169 Column('name', Unicode
),
170 Column('description', Unicode
),
171 Column('exits', PickleType
))
174 'level_exit', metadata
,
175 Column('id', Integer
, primary_key
=True),
176 Column('name', Unicode
),
178 Unicode
, ForeignKey('level.id'), nullable
=False),
180 Unicode
, ForeignKey('level.id'), nullable
=False))
181 metadata
.create_all(db_conn
.bind
)
183 # And now, convert all the old exit pickles to new level exits
184 # ------------------------------------------------------------
186 # query over and insert
187 result
= db_conn
.execute(
188 select([levels
], levels
.c
.exits
!=None))
192 for exit_name
, to_level
in level
['exits'].iteritems():
193 # Insert the level exit
195 level_exits
.insert().values(
200 # Finally, drop the old level exits pickle table
201 # ----------------------------------------------
202 levels
.drop_column('exits')
205 # A hack! At this point we freeze-fame and get just a partial list of
208 SET2_MIGRATIONS
= copy
.copy(FULL_MIGRATIONS
)
210 #######################################################
211 # Migration set 3: Final migrations
212 #######################################################
214 Base3
= declarative_base(cls
=GMGTableBase
)
216 class Creature3(Base3
):
217 __tablename__
= "creature"
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")
224 class CreaturePower3(Base3
):
225 __tablename__
= "creature_power"
227 id = Column(Integer
, primary_key
=True)
229 Integer
, ForeignKey('creature.id'), nullable
=False, index
=True)
230 name
= Column(Unicode
)
231 description
= Column(Unicode
)
232 hitpower
= Column(Float
, nullable
=False)
235 __tablename__
= "level"
237 id = Column(Unicode
, primary_key
=True)
238 name
= Column(Unicode
)
239 description
= Column(Unicode
)
241 class LevelExit3(Base3
):
242 __tablename__
= "level_exit"
244 id = Column(Integer
, primary_key
=True)
245 name
= Column(Unicode
)
247 Unicode
, ForeignKey('level.id'), nullable
=False, index
=True)
249 Unicode
, ForeignKey('level.id'), nullable
=False, index
=True)
252 SET3_MODELS
= [Creature3
, CreaturePower3
, Level3
, LevelExit3
]
253 SET3_MIGRATIONS
= FULL_MIGRATIONS
256 @RegisterMigration(4, FULL_MIGRATIONS
)
257 def creature_num_legs_to_num_limbs(db_conn
):
259 Turns out we're tracking all sorts of limbs, not "legs"
260 specifically. Humans would be 4 here, for instance. So we
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")
270 @RegisterMigration(5, FULL_MIGRATIONS
)
271 def level_exit_index_from_and_to_level(db_conn
):
273 Index the from and to levels of the level exit table.
275 metadata
= MetaData(bind
=db_conn
.bind
)
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
)
285 @RegisterMigration(6, FULL_MIGRATIONS
)
286 def creature_power_index_creature(db_conn
):
288 Index our foreign key relationship to the creatures
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
)
298 @RegisterMigration(7, FULL_MIGRATIONS
)
299 def creature_power_hitpower_to_float(db_conn
):
301 Convert hitpower column on creature power table from integer to
304 Turns out we want super precise values of how much hitpower there
307 metadata
= MetaData(bind
=db_conn
.bind
)
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
)
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,
321 Column('name', Unicode
),
322 Column('description', Unicode
),
323 Column('hitpower', Integer
, nullable
=False))
325 creature_power
.c
.hitpower
.alter(type=Float
)
328 @RegisterMigration(8, FULL_MIGRATIONS
)
329 def creature_power_name_creature_unique(db_conn
):
331 Add a unique constraint to name and creature on creature_power.
333 We don't want multiple creature powers with the same name per creature!
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 :\
338 metadata
= MetaData(bind
=db_conn
.bind
)
340 creature_power
= Table(
341 'creature_power', metadata
,
342 autoload
=True, autoload_with
=db_conn
.bind
)
344 cons
= changeset
.constraint
.UniqueConstraint(
345 'name', 'creature', table
=creature_power
)
350 def _insert_migration1_objects(session
):
352 Test objects to insert for the first set of things
356 [Creature1(name
=u
'centipede',
359 Creature1(name
=u
'wolf',
362 # don't ask me what a wizardsnake is.
363 Creature1(name
=u
'wizardsnake',
369 [Level1(id=u
'necroplex',
370 name
=u
'The Necroplex',
371 description
=u
'A complex full of pure deathzone.',
373 u
'deathwell': u
'evilstorm',
374 u
'portal': u
'central_park'}),
375 Level1(id=u
'evilstorm',
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.",
383 u
'portal': u
'necroplex'})])
388 def _insert_migration2_objects(session
):
390 Test objects to insert for the second set of things
403 description
=u
"A blast of icy breath!",
407 description
=u
"A frightening stare, for sure!",
414 name
=u
'death_rattle',
415 description
=u
'A rattle... of DEATH!',
418 name
=u
'sneaky_stare',
419 description
=u
"The sneakiest stare you've ever seen!",
422 name
=u
'slithery_smoke',
423 description
=u
"A blast of slithery, slithery smoke.",
426 name
=u
'treacherous_tremors',
427 description
=u
"The ground shakes beneath footed animals!",
432 [Level2(id=u
'necroplex',
433 name
=u
'The Necroplex',
434 description
=u
'A complex full of pure deathzone.'),
435 Level2(id=u
'evilstorm',
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.")])
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')])
452 # there are no evilstorm exits because there is no exit from the
457 [LevelExit2(name
=u
'portal',
458 from_level
=u
'central_park',
459 to_level
=u
'necroplex')])
464 def _insert_migration3_objects(session
):
466 Test objects to insert for the third set of things
479 description
=u
"A blast of icy breath!",
483 description
=u
"A frightening stare, for sure!",
490 name
=u
'death_rattle',
491 description
=u
'A rattle... of DEATH!',
494 name
=u
'sneaky_stare',
495 description
=u
"The sneakiest stare you've ever seen!",
498 name
=u
'slithery_smoke',
499 description
=u
"A blast of slithery, slithery smoke.",
502 name
=u
'treacherous_tremors',
503 description
=u
"The ground shakes beneath footed animals!",
505 # annnnnd one more to test a floating point hitpower
512 description
=u
'Smitten by holy wrath!',
517 [Level3(id=u
'necroplex',
518 name
=u
'The Necroplex',
519 description
=u
'A complex full of pure deathzone.'),
520 Level3(id=u
'evilstorm',
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.")])
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')])
537 # there are no evilstorm exits because there is no exit from the
542 [LevelExit3(name
=u
'portal',
543 from_level
=u
'central_park',
544 to_level
=u
'necroplex')])
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
555 def assert_col_type(column
, this_class
):
556 assert isinstance(column
.type, this_class
)
559 def _get_level3_exits(session
, level
):
561 [(level_exit
.name
, level_exit
.to_level
)
563 session
.query(LevelExit3
).filter_by(from_level
=level
.id)])
566 def test_set1_to_set3():
567 # Create / connect to database
568 # ----------------------------
570 engine
, Session
= create_test_engine()
572 # Create tables by migrating on empty initial set
573 # -----------------------------------------------
575 printer
= CollectingPrinter()
576 migration_manager
= MigrationManager(
577 u
'__main__', SET1_MODELS
, FOUNDATIONS
, SET1_MIGRATIONS
, Session(),
580 # Check latest migration and database current migration
581 assert migration_manager
.latest_migration
== 0
582 assert migration_manager
.database_current_migration
== None
584 result
= migration_manager
.init_or_migrate()
586 # Make sure output was "inited"
587 assert result
== u
'inited'
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
597 # Install the initial set
598 # -----------------------
600 _insert_migration1_objects(Session())
602 # Try to "re-migrate" with same manager settings... nothing should happen
603 migration_manager
= MigrationManager(
604 u
'__main__', SET1_MODELS
, FOUNDATIONS
, SET1_MIGRATIONS
,
606 assert migration_manager
.init_or_migrate() == None
608 # Check version in database
609 assert migration_manager
.latest_migration
== 0
610 assert migration_manager
.database_current_migration
== 0
612 # Sanity check a few things in the database...
613 metadata
= MetaData(bind
=engine
)
615 # Check the structure of the creature table
616 creature_table
= Table(
617 'creature', metadata
,
618 autoload
=True, autoload_with
=engine
)
619 assert set(creature_table
.c
.keys()) == set(
620 ['id', 'name', 'num_legs', 'is_demon'])
621 assert_col_type(creature_table
.c
.id, Integer
)
622 assert_col_type(creature_table
.c
.name
, VARCHAR
)
623 assert creature_table
.c
.name
.nullable
is False
624 #assert creature_table.c.name.index is True
625 #assert creature_table.c.name.unique is True
626 assert_col_type(creature_table
.c
.num_legs
, Integer
)
627 assert creature_table
.c
.num_legs
.nullable
is False
628 assert_col_type(creature_table
.c
.is_demon
, Boolean
)
630 # Check the structure of the level table
633 autoload
=True, autoload_with
=engine
)
634 assert set(level_table
.c
.keys()) == set(
635 ['id', 'name', 'description', 'exits'])
636 assert_col_type(level_table
.c
.id, VARCHAR
)
637 assert level_table
.c
.id.primary_key
is True
638 assert_col_type(level_table
.c
.name
, VARCHAR
)
639 assert_col_type(level_table
.c
.description
, VARCHAR
)
640 # Skipping exits... Not sure if we can detect pickletype, not a
641 # big deal regardless.
643 # Now check to see if stuff seems to be in there.
646 # Check the creation of the foundation rows on the creature table
647 creature
= session
.query(Creature1
).filter_by(
648 name
=u
'goblin').one()
649 assert creature
.num_legs
== 2
650 assert creature
.is_demon
== False
652 creature
= session
.query(Creature1
).filter_by(
653 name
=u
'cerberus').one()
654 assert creature
.num_legs
== 4
655 assert creature
.is_demon
== True
658 # Check the creation of the inserted rows on the creature and levels tables
660 creature
= session
.query(Creature1
).filter_by(
661 name
=u
'centipede').one()
662 assert creature
.num_legs
== 100
663 assert creature
.is_demon
== False
665 creature
= session
.query(Creature1
).filter_by(
667 assert creature
.num_legs
== 4
668 assert creature
.is_demon
== False
670 creature
= session
.query(Creature1
).filter_by(
671 name
=u
'wizardsnake').one()
672 assert creature
.num_legs
== 0
673 assert creature
.is_demon
== True
675 level
= session
.query(Level1
).filter_by(
676 id=u
'necroplex').one()
677 assert level
.name
== u
'The Necroplex'
678 assert level
.description
== u
'A complex full of pure deathzone.'
679 assert level
.exits
== {
680 'deathwell': 'evilstorm',
681 'portal': 'central_park'}
683 level
= session
.query(Level1
).filter_by(
684 id=u
'evilstorm').one()
685 assert level
.name
== u
'Evil Storm'
686 assert level
.description
== u
'A storm full of pure evil.'
687 assert level
.exits
== {} # You still can't escape the evilstorm!
689 level
= session
.query(Level1
).filter_by(
690 id=u
'central_park').one()
691 assert level
.name
== u
'Central Park, NY, NY'
692 assert level
.description
== u
"New York's friendly Central Park."
693 assert level
.exits
== {
694 'portal': 'necroplex'}
696 # Create new migration manager, but make sure the db migration
697 # isn't said to be updated yet
698 printer
= CollectingPrinter()
699 migration_manager
= MigrationManager(
700 u
'__main__', SET3_MODELS
, FOUNDATIONS
, SET3_MIGRATIONS
, Session(),
703 assert migration_manager
.latest_migration
== 8
704 assert migration_manager
.database_current_migration
== 0
707 result
= migration_manager
.init_or_migrate()
709 # Make sure result was "migrated"
710 assert result
== u
'migrated'
712 # TODO: Check output to user
713 assert printer
.combined_string
== """\
714 -> Updating main mediagoblin tables:
715 + Running migration 1, "creature_remove_is_demon"... done.
716 + Running migration 2, "creature_powers_new_table"... done.
717 + Running migration 3, "level_exits_new_table"... done.
718 + Running migration 4, "creature_num_legs_to_num_limbs"... done.
719 + Running migration 5, "level_exit_index_from_and_to_level"... done.
720 + Running migration 6, "creature_power_index_creature"... done.
721 + Running migration 7, "creature_power_hitpower_to_float"... done.
722 + Running migration 8, "creature_power_name_creature_unique"... done.
725 # Make sure version matches expected
726 migration_manager
= MigrationManager(
727 u
'__main__', SET3_MODELS
, FOUNDATIONS
, SET3_MIGRATIONS
, Session(),
729 assert migration_manager
.latest_migration
== 8
730 assert migration_manager
.database_current_migration
== 8
732 # Check all things in database match expected
734 # Check the creature table
735 metadata
= MetaData(bind
=engine
)
736 creature_table
= Table(
737 'creature', metadata
,
738 autoload
=True, autoload_with
=engine
)
739 # assert set(creature_table.c.keys()) == set(
740 # ['id', 'name', 'num_limbs'])
741 assert set(creature_table
.c
.keys()) == set(
742 [u
'id', 'name', u
'num_limbs', u
'is_demon'])
743 assert_col_type(creature_table
.c
.id, Integer
)
744 assert_col_type(creature_table
.c
.name
, VARCHAR
)
745 assert creature_table
.c
.name
.nullable
is False
746 #assert creature_table.c.name.index is True
747 #assert creature_table.c.name.unique is True
748 assert_col_type(creature_table
.c
.num_limbs
, Integer
)
749 assert creature_table
.c
.num_limbs
.nullable
is False
751 # Check the CreaturePower table
752 creature_power_table
= Table(
753 'creature_power', metadata
,
754 autoload
=True, autoload_with
=engine
)
755 assert set(creature_power_table
.c
.keys()) == set(
756 ['id', 'creature', 'name', 'description', 'hitpower'])
757 assert_col_type(creature_power_table
.c
.id, Integer
)
758 assert_col_type(creature_power_table
.c
.creature
, Integer
)
759 assert creature_power_table
.c
.creature
.nullable
is False
760 assert_col_type(creature_power_table
.c
.name
, VARCHAR
)
761 assert_col_type(creature_power_table
.c
.description
, VARCHAR
)
762 assert_col_type(creature_power_table
.c
.hitpower
, Float
)
763 assert creature_power_table
.c
.hitpower
.nullable
is False
765 # Check the structure of the level table
768 autoload
=True, autoload_with
=engine
)
769 assert set(level_table
.c
.keys()) == set(
770 ['id', 'name', 'description'])
771 assert_col_type(level_table
.c
.id, VARCHAR
)
772 assert level_table
.c
.id.primary_key
is True
773 assert_col_type(level_table
.c
.name
, VARCHAR
)
774 assert_col_type(level_table
.c
.description
, VARCHAR
)
776 # Check the structure of the level_exits table
777 level_exit_table
= Table(
778 'level_exit', metadata
,
779 autoload
=True, autoload_with
=engine
)
780 assert set(level_exit_table
.c
.keys()) == set(
781 ['id', 'name', 'from_level', 'to_level'])
782 assert_col_type(level_exit_table
.c
.id, Integer
)
783 assert_col_type(level_exit_table
.c
.name
, VARCHAR
)
784 assert_col_type(level_exit_table
.c
.from_level
, VARCHAR
)
785 assert level_exit_table
.c
.from_level
.nullable
is False
786 #assert level_exit_table.c.from_level.index is True
787 assert_col_type(level_exit_table
.c
.to_level
, VARCHAR
)
788 assert level_exit_table
.c
.to_level
.nullable
is False
789 #assert level_exit_table.c.to_level.index is True
791 # Now check to see if stuff seems to be in there.
793 creature
= session
.query(Creature3
).filter_by(
794 name
=u
'centipede').one()
795 assert creature
.num_limbs
== 100.0
796 assert creature
.magical_powers
== []
798 creature
= session
.query(Creature3
).filter_by(
800 assert creature
.num_limbs
== 4.0
801 assert creature
.magical_powers
== []
803 creature
= session
.query(Creature3
).filter_by(
804 name
=u
'wizardsnake').one()
805 assert creature
.num_limbs
== 0.0
806 assert creature
.magical_powers
== []
808 level
= session
.query(Level3
).filter_by(
809 id=u
'necroplex').one()
810 assert level
.name
== u
'The Necroplex'
811 assert level
.description
== u
'A complex full of pure deathzone.'
812 level_exits
= _get_level3_exits(session
, level
)
813 assert level_exits
== {
814 u
'deathwell': u
'evilstorm',
815 u
'portal': u
'central_park'}
817 level
= session
.query(Level3
).filter_by(
818 id=u
'evilstorm').one()
819 assert level
.name
== u
'Evil Storm'
820 assert level
.description
== u
'A storm full of pure evil.'
821 level_exits
= _get_level3_exits(session
, level
)
822 assert level_exits
== {} # You still can't escape the evilstorm!
824 level
= session
.query(Level3
).filter_by(
825 id=u
'central_park').one()
826 assert level
.name
== u
'Central Park, NY, NY'
827 assert level
.description
== u
"New York's friendly Central Park."
828 level_exits
= _get_level3_exits(session
, level
)
829 assert level_exits
== {
830 'portal': 'necroplex'}
833 #def test_set2_to_set3():
834 # Create / connect to database
835 # Create tables by migrating on empty initial set
837 # Install the initial set
838 # Check version in database
839 # Sanity check a few things in the database
842 # Make sure version matches expected
843 # Check all things in database match expected
847 #def test_set1_to_set2_to_set3():
848 # Create / connect to database
849 # Create tables by migrating on empty initial set
851 # Install the initial set
852 # Check version in database
853 # Sanity check a few things in the database
856 # Make sure version matches expected
857 # Check all things in database match expected
860 # Make sure version matches expected again
861 # Check all things in database match expected again
864 # creature_table = Table(
865 # 'creature', metadata,
866 # autoload=True, autoload_with=db_conn.bind)
867 # assert set(creature_table.c.keys()) == set(
868 # ['id', 'name', 'num_legs'])
869 # assert_col_type(creature_table.c.id, Integer)
870 # assert_col_type(creature_table.c.name, VARCHAR)
871 # assert creature_table.c.name.nullable is False
872 # assert creature_table.c.name.index is True
873 # assert creature_table.c.name.unique is True
874 # assert_col_type(creature_table.c.num_legs, Integer)
875 # assert creature_table.c.num_legs.nullable is False
877 # # Check the CreaturePower table
878 # creature_power_table = Table(
879 # 'creature_power', metadata,
880 # autoload=True, autoload_with=db_conn.bind)
881 # assert set(creature_power_table.c.keys()) == set(
882 # ['id', 'creature', 'name', 'description', 'hitpower'])
883 # assert_col_type(creature_power_table.c.id, Integer)
884 # assert_col_type(creature_power_table.c.creature, Integer)
885 # assert creature_power_table.c.creature.nullable is False
886 # assert_col_type(creature_power_table.c.name, VARCHAR)
887 # assert_col_type(creature_power_table.c.description, VARCHAR)
888 # assert_col_type(creature_power_table.c.hitpower, Integer)
889 # assert creature_power_table.c.hitpower.nullable is False
891 # # Check the structure of the level table
892 # level_table = Table(
894 # autoload=True, autoload_with=db_conn.bind)
895 # assert set(level_table.c.keys()) == set(
896 # ['id', 'name', 'description'])
897 # assert_col_type(level_table.c.id, VARCHAR)
898 # assert level_table.c.id.primary_key is True
899 # assert_col_type(level_table.c.name, VARCHAR)
900 # assert_col_type(level_table.c.description, VARCHAR)
902 # # Check the structure of the level_exits table
903 # level_exit_table = Table(
904 # 'level_exit', metadata,
905 # autoload=True, autoload_with=db_conn.bind)
906 # assert set(level_exit_table.c.keys()) == set(
907 # ['id', 'name', 'from_level', 'to_level'])
908 # assert_col_type(level_exit_table.c.id, Integer)
909 # assert_col_type(level_exit_table.c.name, VARCHAR)
910 # assert_col_type(level_exit_table.c.from_level, VARCHAR)
911 # assert level_exit_table.c.from_level.nullable is False
912 # assert_col_type(level_exit_table.c.to_level, VARCHAR)