Merge branch 'master' into merge-python3-port
[mediagoblin.git] / mediagoblin / tests / test_sql_migrations.py
CommitLineData
0f10058f 1# GNU MediaGoblin -- federated, autonomous media hosting
89694d6d 2# Copyright (C) 2012, 2012 MediaGoblin contributors. See AUTHORS.
0f10058f
CAW
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
386c9c7c 17import six
88ed537a
BP
18import pytest
19
20pytestmark = pytest.mark.skipif(six.PY3, reason='needs sqlalchemy.migrate')
21
22import copy
386c9c7c 23
0f10058f 24from sqlalchemy import (
5de0f4da 25 Table, Column, MetaData, Index,
89694d6d 26 Integer, Float, Unicode, UnicodeText, DateTime, Boolean,
c7fa585b 27 ForeignKey, UniqueConstraint, PickleType, VARCHAR)
d74a9483 28from sqlalchemy.orm import sessionmaker, relationship
0f10058f 29from sqlalchemy.ext.declarative import declarative_base
89694d6d 30from sqlalchemy.sql import select, insert
88ed537a
BP
31if six.PY2:
32 from migrate import changeset
0f10058f 33
39dc3bf8 34from mediagoblin.db.base import GMGTableBase
c130e3ee 35from mediagoblin.db.migration_tools import MigrationManager, RegisterMigration
35a24fc2 36from mediagoblin.tools.common import CollectingPrinter
0f10058f
CAW
37
38
39# This one will get filled with local migrations
40FULL_MIGRATIONS = {}
41
42
43#######################################################
44# Migration set 1: Define initial models, no migrations
45#######################################################
46
47Base1 = declarative_base(cls=GMGTableBase)
48
49class 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
57class Level1(Base1):
58 __tablename__ = "level"
59
64d28064 60 id = Column(Unicode, primary_key=True)
caed154a 61 name = Column(Unicode)
40f0996a 62 description = Column(Unicode)
0f10058f
CAW
63 exits = Column(PickleType)
64
65SET1_MODELS = [Creature1, Level1]
66
860fa806 67FOUNDATIONS = {Creature1:[{'name':u'goblin','num_legs':2,'is_demon':False},
84c1cd7c 68 {'name':u'cerberus','num_legs':4,'is_demon':True}]
69 }
63c3ca28 70
a5e03db6 71SET1_MIGRATIONS = {}
0f10058f
CAW
72
73#######################################################
74# Migration set 2: A few migrations and new model
75#######################################################
76
77Base2 = declarative_base(cls=GMGTableBase)
78
79class 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)
d74a9483 85 magical_powers = relationship("CreaturePower2")
0f10058f
CAW
86
87class 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)
89694d6d 95 hitpower = Column(Integer, nullable=False)
0f10058f
CAW
96
97class Level2(Base2):
98 __tablename__ = "level"
99
64d28064 100 id = Column(Unicode, primary_key=True)
0f10058f 101 name = Column(Unicode)
40f0996a 102 description = Column(Unicode)
0f10058f
CAW
103
104class LevelExit2(Base2):
105 __tablename__ = "level_exit"
106
107 id = Column(Integer, primary_key=True)
108 name = Column(Unicode)
109 from_level = Column(
64d28064 110 Unicode, ForeignKey('level.id'), nullable=False)
0f10058f 111 to_level = Column(
64d28064 112 Unicode, ForeignKey('level.id'), nullable=False)
0f10058f
CAW
113
114SET2_MODELS = [Creature2, CreaturePower2, Level2, LevelExit2]
115
116
117@RegisterMigration(1, FULL_MIGRATIONS)
118def creature_remove_is_demon(db_conn):
356654de
CAW
119 """
120 Remove the is_demon field from the creature model. We don't need
121 it!
122 """
7f3ec607
CAW
123 # :( Commented out 'cuz of:
124 # http://code.google.com/p/sqlalchemy-migrate/issues/detail?id=143&thanks=143&ts=1327882242
125
78d17b80
CAW
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
0f10058f
CAW
133
134@RegisterMigration(2, FULL_MIGRATIONS)
135def creature_powers_new_table(db_conn):
356654de
CAW
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 """
e920b968 141 metadata = MetaData(bind=db_conn.bind)
78d17b80
CAW
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
89694d6d
CAW
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))
e920b968 157 metadata.create_all(db_conn.bind)
89694d6d 158
0f10058f
CAW
159
160@RegisterMigration(3, FULL_MIGRATIONS)
161def level_exits_new_table(db_conn):
356654de
CAW
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 """
89694d6d
CAW
166 # First, create the table
167 # -----------------------
e920b968 168 metadata = MetaData(bind=db_conn.bind)
78d17b80
CAW
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
89694d6d
CAW
180 level_exits = Table(
181 'level_exit', metadata,
182 Column('id', Integer, primary_key=True),
183 Column('name', Unicode),
184 Column('from_level',
78d17b80 185 Unicode, ForeignKey('level.id'), nullable=False),
89694d6d 186 Column('to_level',
78d17b80 187 Unicode, ForeignKey('level.id'), nullable=False))
e920b968 188 metadata.create_all(db_conn.bind)
89694d6d
CAW
189
190 # And now, convert all the old exit pickles to new level exits
191 # ------------------------------------------------------------
192
89694d6d
CAW
193 # query over and insert
194 result = db_conn.execute(
195 select([levels], levels.c.exits!=None))
196
197 for level in result:
78d17b80 198
386c9c7c 199 for exit_name, to_level in six.iteritems(level['exits']):
78d17b80
CAW
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))
89694d6d
CAW
206
207 # Finally, drop the old level exits pickle table
208 # ----------------------------------------------
473e0605 209 levels.drop_column('exits')
0f10058f
CAW
210
211
212# A hack! At this point we freeze-fame and get just a partial list of
213# migrations
214
129c36be 215SET2_MIGRATIONS = copy.copy(FULL_MIGRATIONS)
0f10058f
CAW
216
217#######################################################
218# Migration set 3: Final migrations
219#######################################################
220
221Base3 = declarative_base(cls=GMGTableBase)
222
223class 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)
0f3526c6 229 magical_powers = relationship("CreaturePower3")
0f10058f
CAW
230
231class 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)
89694d6d 239 hitpower = Column(Float, nullable=False)
0f10058f
CAW
240
241class Level3(Base3):
242 __tablename__ = "level"
243
64d28064 244 id = Column(Unicode, primary_key=True)
0f10058f 245 name = Column(Unicode)
40f0996a 246 description = Column(Unicode)
0f10058f
CAW
247
248class LevelExit3(Base3):
249 __tablename__ = "level_exit"
250
251 id = Column(Integer, primary_key=True)
252 name = Column(Unicode)
253 from_level = Column(
64d28064 254 Unicode, ForeignKey('level.id'), nullable=False, index=True)
0f10058f 255 to_level = Column(
64d28064 256 Unicode, ForeignKey('level.id'), nullable=False, index=True)
0f10058f
CAW
257
258
259SET3_MODELS = [Creature3, CreaturePower3, Level3, LevelExit3]
caed154a 260SET3_MIGRATIONS = FULL_MIGRATIONS
0f10058f
CAW
261
262
263@RegisterMigration(4, FULL_MIGRATIONS)
264def creature_num_legs_to_num_limbs(db_conn):
d6cdf64b
CAW
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 """
e920b968 270 metadata = MetaData(bind=db_conn.bind)
248b5061
CAW
271 creature_table = Table(
272 'creature', metadata,
e920b968 273 autoload=True, autoload_with=db_conn.bind)
94eff10d 274 creature_table.c.num_legs.alter(name=u"num_limbs")
248b5061 275
0f10058f
CAW
276
277@RegisterMigration(5, FULL_MIGRATIONS)
278def level_exit_index_from_and_to_level(db_conn):
d6cdf64b
CAW
279 """
280 Index the from and to levels of the level exit table.
281 """
e920b968 282 metadata = MetaData(bind=db_conn.bind)
248b5061
CAW
283 level_exit = Table(
284 'level_exit', metadata,
e920b968 285 autoload=True, autoload_with=db_conn.bind)
78d17b80
CAW
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)
248b5061 290
0f10058f
CAW
291
292@RegisterMigration(6, FULL_MIGRATIONS)
293def creature_power_index_creature(db_conn):
d6cdf64b
CAW
294 """
295 Index our foreign key relationship to the creatures
296 """
e920b968 297 metadata = MetaData(bind=db_conn.bind)
248b5061
CAW
298 creature_power = Table(
299 'creature_power', metadata,
e920b968 300 autoload=True, autoload_with=db_conn.bind)
78d17b80
CAW
301 Index('ix_creature_power_creature',
302 creature_power.c.creature).create(db_conn.bind)
248b5061 303
89694d6d
CAW
304
305@RegisterMigration(7, FULL_MIGRATIONS)
306def creature_power_hitpower_to_float(db_conn):
d6cdf64b
CAW
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 """
e920b968 314 metadata = MetaData(bind=db_conn.bind)
78d17b80
CAW
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
248b5061
CAW
322 creature_power = Table(
323 'creature_power', metadata,
78d17b80
CAW
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
248b5061 332 creature_power.c.hitpower.alter(type=Float)
64d28064
CAW
333
334
52539aca
CAW
335@RegisterMigration(8, FULL_MIGRATIONS)
336def 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
64d28064 357def _insert_migration1_objects(session):
356654de
CAW
358 """
359 Test objects to insert for the first set of things
360 """
64d28064
CAW
361 # Insert creatures
362 session.add_all(
94eff10d 363 [Creature1(name=u'centipede',
64d28064
CAW
364 num_legs=100,
365 is_demon=False),
94eff10d 366 Creature1(name=u'wolf',
64d28064
CAW
367 num_legs=4,
368 is_demon=False),
369 # don't ask me what a wizardsnake is.
94eff10d 370 Creature1(name=u'wizardsnake',
64d28064
CAW
371 num_legs=0,
372 is_demon=True)])
373
d74a9483 374 # Insert levels
64d28064 375 session.add_all(
94eff10d
CAW
376 [Level1(id=u'necroplex',
377 name=u'The Necroplex',
378 description=u'A complex full of pure deathzone.',
64d28064 379 exits={
c7dfd4fb
BS
380 u'deathwell': u'evilstorm',
381 u'portal': u'central_park'}),
94eff10d
CAW
382 Level1(id=u'evilstorm',
383 name=u'Evil Storm',
384 description=u'A storm full of pure evil.',
64d28064 385 exits={}), # you can't escape the evilstorm
caed154a 386 Level1(id=u'central_park',
94eff10d
CAW
387 name=u'Central Park, NY, NY',
388 description=u"New York's friendly Central Park.",
64d28064 389 exits={
c7dfd4fb 390 u'portal': u'necroplex'})])
64d28064
CAW
391
392 session.commit()
393
d74a9483
CAW
394
395def _insert_migration2_objects(session):
356654de
CAW
396 """
397 Test objects to insert for the second set of things
398 """
d74a9483
CAW
399 # Insert creatures
400 session.add_all(
401 [Creature2(
94eff10d 402 name=u'centipede',
d74a9483
CAW
403 num_legs=100),
404 Creature2(
94eff10d 405 name=u'wolf',
d74a9483
CAW
406 num_legs=4,
407 magical_powers = [
408 CreaturePower2(
94eff10d
CAW
409 name=u"ice breath",
410 description=u"A blast of icy breath!",
d74a9483
CAW
411 hitpower=20),
412 CreaturePower2(
94eff10d
CAW
413 name=u"death stare",
414 description=u"A frightening stare, for sure!",
d74a9483
CAW
415 hitpower=45)]),
416 Creature2(
94eff10d 417 name=u'wizardsnake',
d74a9483
CAW
418 num_legs=0,
419 magical_powers=[
420 CreaturePower2(
94eff10d
CAW
421 name=u'death_rattle',
422 description=u'A rattle... of DEATH!',
d74a9483
CAW
423 hitpower=1000),
424 CreaturePower2(
94eff10d 425 name=u'sneaky_stare',
caed154a 426 description=u"The sneakiest stare you've ever seen!",
d74a9483
CAW
427 hitpower=300),
428 CreaturePower2(
94eff10d
CAW
429 name=u'slithery_smoke',
430 description=u"A blast of slithery, slithery smoke.",
d74a9483
CAW
431 hitpower=10),
432 CreaturePower2(
94eff10d
CAW
433 name=u'treacherous_tremors',
434 description=u"The ground shakes beneath footed animals!",
d74a9483
CAW
435 hitpower=0)])])
436
437 # Insert levels
438 session.add_all(
94eff10d
CAW
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.',
d74a9483 445 exits=[]), # you can't escape the evilstorm
caed154a 446 Level2(id=u'central_park',
94eff10d
CAW
447 name=u'Central Park, NY, NY',
448 description=u"New York's friendly Central Park.")])
d74a9483
CAW
449
450 # necroplex exits
451 session.add_all(
94eff10d
CAW
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')])
d74a9483
CAW
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(
94eff10d
CAW
464 [LevelExit2(name=u'portal',
465 from_level=u'central_park',
466 to_level=u'necroplex')])
d74a9483
CAW
467
468 session.commit()
dc3db468
CAW
469
470
471def _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(
94eff10d 478 name=u'centipede',
dc3db468
CAW
479 num_limbs=100),
480 Creature3(
94eff10d 481 name=u'wolf',
dc3db468
CAW
482 num_limbs=4,
483 magical_powers = [
484 CreaturePower3(
94eff10d
CAW
485 name=u"ice breath",
486 description=u"A blast of icy breath!",
dc3db468
CAW
487 hitpower=20.0),
488 CreaturePower3(
94eff10d
CAW
489 name=u"death stare",
490 description=u"A frightening stare, for sure!",
dc3db468
CAW
491 hitpower=45.0)]),
492 Creature3(
94eff10d 493 name=u'wizardsnake',
dc3db468
CAW
494 num_limbs=0,
495 magical_powers=[
496 CreaturePower3(
94eff10d
CAW
497 name=u'death_rattle',
498 description=u'A rattle... of DEATH!',
dc3db468
CAW
499 hitpower=1000.0),
500 CreaturePower3(
94eff10d 501 name=u'sneaky_stare',
caed154a 502 description=u"The sneakiest stare you've ever seen!",
dc3db468
CAW
503 hitpower=300.0),
504 CreaturePower3(
94eff10d
CAW
505 name=u'slithery_smoke',
506 description=u"A blast of slithery, slithery smoke.",
dc3db468
CAW
507 hitpower=10.0),
508 CreaturePower3(
94eff10d
CAW
509 name=u'treacherous_tremors',
510 description=u"The ground shakes beneath footed animals!",
dc3db468
CAW
511 hitpower=0.0)])],
512 # annnnnd one more to test a floating point hitpower
513 Creature3(
94eff10d 514 name=u'deity',
dc3db468 515 numb_limbs=30,
caed154a 516 magical_powers=[
dc3db468 517 CreaturePower3(
94eff10d
CAW
518 name=u'smite',
519 description=u'Smitten by holy wrath!',
caed154a 520 hitpower=9999.9)]))
dc3db468
CAW
521
522 # Insert levels
523 session.add_all(
94eff10d
CAW
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.',
dc3db468 530 exits=[]), # you can't escape the evilstorm
caed154a 531 Level3(id=u'central_park',
94eff10d
CAW
532 name=u'Central Park, NY, NY',
533 description=u"New York's friendly Central Park.")])
dc3db468
CAW
534
535 # necroplex exits
536 session.add_all(
94eff10d
CAW
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')])
dc3db468
CAW
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(
94eff10d
CAW
549 [LevelExit3(name=u'portal',
550 from_level=u'central_park',
551 to_level=u'necroplex')])
dc3db468
CAW
552
553 session.commit()
554
09e2c487
CAW
555def create_test_engine():
556 from sqlalchemy import create_engine
557 engine = create_engine('sqlite:///:memory:', echo=False)
40f0996a
CAW
558 Session = sessionmaker(bind=engine)
559 return engine, Session
560
561
caed154a
CAW
562def assert_col_type(column, this_class):
563 assert isinstance(column.type, this_class)
09e2c487
CAW
564
565
e8e52b3a
CAW
566def _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
09e2c487
CAW
573def test_set1_to_set3():
574 # Create / connect to database
94eff10d
CAW
575 # ----------------------------
576
40f0996a 577 engine, Session = create_test_engine()
94eff10d 578
09e2c487 579 # Create tables by migrating on empty initial set
94eff10d
CAW
580 # -----------------------------------------------
581
ecb4cc89 582 printer = CollectingPrinter()
40f0996a 583 migration_manager = MigrationManager(
860fa806 584 u'__main__', SET1_MODELS, FOUNDATIONS, SET1_MIGRATIONS, Session(),
40f0996a 585 printer)
94eff10d 586
40f0996a
CAW
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()
94eff10d 592
40f0996a
CAW
593 # Make sure output was "inited"
594 assert result == u'inited'
595 # Check output
94eff10d 596 assert printer.combined_string == (
63c3ca28 597 "-> Initializing main mediagoblin tables... done.\n" + \
598 " + Laying foundations for Creature1 table\n" )
40f0996a
CAW
599 # Check version in database
600 assert migration_manager.latest_migration == 0
601 assert migration_manager.database_current_migration == 0
09e2c487 602
860fa806 603
09e2c487 604 # Install the initial set
94eff10d
CAW
605 # -----------------------
606
40f0996a 607 _insert_migration1_objects(Session())
94eff10d 608
40f0996a
CAW
609 # Try to "re-migrate" with same manager settings... nothing should happen
610 migration_manager = MigrationManager(
860fa806 611 u'__main__', SET1_MODELS, FOUNDATIONS, SET1_MIGRATIONS,
63c3ca28 612 Session(), printer)
40f0996a
CAW
613 assert migration_manager.init_or_migrate() == None
614
09e2c487 615 # Check version in database
40f0996a
CAW
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...
caed154a 620 metadata = MetaData(bind=engine)
40f0996a
CAW
621
622 # Check the structure of the creature table
623 creature_table = Table(
624 'creature', metadata,
caed154a 625 autoload=True, autoload_with=engine)
40f0996a
CAW
626 assert set(creature_table.c.keys()) == set(
627 ['id', 'name', 'num_legs', 'is_demon'])
628 assert_col_type(creature_table.c.id, Integer)
c7fa585b 629 assert_col_type(creature_table.c.name, VARCHAR)
40f0996a 630 assert creature_table.c.name.nullable is False
c7fa585b
CAW
631 #assert creature_table.c.name.index is True
632 #assert creature_table.c.name.unique is True
40f0996a
CAW
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,
caed154a 640 autoload=True, autoload_with=engine)
40f0996a
CAW
641 assert set(level_table.c.keys()) == set(
642 ['id', 'name', 'description', 'exits'])
c7fa585b 643 assert_col_type(level_table.c.id, VARCHAR)
94eff10d 644 assert level_table.c.id.primary_key is True
c7fa585b
CAW
645 assert_col_type(level_table.c.name, VARCHAR)
646 assert_col_type(level_table.c.description, VARCHAR)
40f0996a
CAW
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.
caed154a
CAW
651 session = Session()
652
860fa806 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
94eff10d
CAW
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(
e8e52b3a 683 id=u'necroplex').one()
94eff10d 684 assert level.name == u'The Necroplex'
505c0db1 685 assert level.description == u'A complex full of pure deathzone.'
94eff10d
CAW
686 assert level.exits == {
687 'deathwell': 'evilstorm',
688 'portal': 'central_park'}
689
690 level = session.query(Level1).filter_by(
e8e52b3a 691 id=u'evilstorm').one()
94eff10d
CAW
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(
e8e52b3a 697 id=u'central_park').one()
94eff10d
CAW
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
ecb4cc89 705 printer = CollectingPrinter()
94eff10d 706 migration_manager = MigrationManager(
860fa806 707 u'__main__', SET3_MODELS, FOUNDATIONS, SET3_MIGRATIONS, Session(),
94eff10d
CAW
708 printer)
709
52539aca 710 assert migration_manager.latest_migration == 8
94eff10d 711 assert migration_manager.database_current_migration == 0
09e2c487
CAW
712
713 # Migrate
94eff10d
CAW
714 result = migration_manager.init_or_migrate()
715
40f0996a 716 # Make sure result was "migrated"
94eff10d
CAW
717 assert result == u'migrated'
718
719 # TODO: Check output to user
720 assert printer.combined_string == """\
78d17b80 721-> Updating main mediagoblin tables:
94eff10d
CAW
722 + Running migration 1, "creature_remove_is_demon"... done.
723 + Running migration 2, "creature_powers_new_table"... done.
78d17b80
CAW
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.
52539aca 729 + Running migration 8, "creature_power_name_creature_unique"... done.
78d17b80 730"""
94eff10d 731
09e2c487 732 # Make sure version matches expected
94eff10d 733 migration_manager = MigrationManager(
860fa806 734 u'__main__', SET3_MODELS, FOUNDATIONS, SET3_MIGRATIONS, Session(),
94eff10d 735 printer)
52539aca
CAW
736 assert migration_manager.latest_migration == 8
737 assert migration_manager.database_current_migration == 8
94eff10d 738
09e2c487 739 # Check all things in database match expected
94eff10d
CAW
740
741 # Check the creature table
78d17b80 742 metadata = MetaData(bind=engine)
94eff10d
CAW
743 creature_table = Table(
744 'creature', metadata,
caed154a 745 autoload=True, autoload_with=engine)
78d17b80
CAW
746 # assert set(creature_table.c.keys()) == set(
747 # ['id', 'name', 'num_limbs'])
94eff10d 748 assert set(creature_table.c.keys()) == set(
78d17b80 749 [u'id', 'name', u'num_limbs', u'is_demon'])
94eff10d 750 assert_col_type(creature_table.c.id, Integer)
c7fa585b 751 assert_col_type(creature_table.c.name, VARCHAR)
94eff10d 752 assert creature_table.c.name.nullable is False
c7fa585b
CAW
753 #assert creature_table.c.name.index is True
754 #assert creature_table.c.name.unique is True
78d17b80
CAW
755 assert_col_type(creature_table.c.num_limbs, Integer)
756 assert creature_table.c.num_limbs.nullable is False
94eff10d
CAW
757
758 # Check the CreaturePower table
759 creature_power_table = Table(
760 'creature_power', metadata,
caed154a 761 autoload=True, autoload_with=engine)
94eff10d
CAW
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
c7fa585b
CAW
767 assert_col_type(creature_power_table.c.name, VARCHAR)
768 assert_col_type(creature_power_table.c.description, VARCHAR)
690b51fa 769 assert_col_type(creature_power_table.c.hitpower, Float)
94eff10d
CAW
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,
caed154a 775 autoload=True, autoload_with=engine)
94eff10d
CAW
776 assert set(level_table.c.keys()) == set(
777 ['id', 'name', 'description'])
c7fa585b 778 assert_col_type(level_table.c.id, VARCHAR)
94eff10d 779 assert level_table.c.id.primary_key is True
c7fa585b
CAW
780 assert_col_type(level_table.c.name, VARCHAR)
781 assert_col_type(level_table.c.description, VARCHAR)
94eff10d
CAW
782
783 # Check the structure of the level_exits table
784 level_exit_table = Table(
785 'level_exit', metadata,
caed154a 786 autoload=True, autoload_with=engine)
94eff10d
CAW
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)
c7fa585b
CAW
790 assert_col_type(level_exit_table.c.name, VARCHAR)
791 assert_col_type(level_exit_table.c.from_level, VARCHAR)
94eff10d 792 assert level_exit_table.c.from_level.nullable is False
c7fa585b
CAW
793 #assert level_exit_table.c.from_level.index is True
794 assert_col_type(level_exit_table.c.to_level, VARCHAR)
94eff10d 795 assert level_exit_table.c.to_level.nullable is False
c7fa585b 796 #assert level_exit_table.c.to_level.index is True
94eff10d
CAW
797
798 # Now check to see if stuff seems to be in there.
caed154a 799 session = Session()
84c1cd7c 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
e8e52b3a 809 creature = session.query(Creature3).filter_by(
690b51fa 810 name=u'centipede').one()
5de0f4da 811 assert creature.num_limbs == 100.0
78d17b80 812 assert creature.magical_powers == []
690b51fa 813
e8e52b3a 814 creature = session.query(Creature3).filter_by(
690b51fa 815 name=u'wolf').one()
5de0f4da 816 assert creature.num_limbs == 4.0
78d17b80 817 assert creature.magical_powers == []
690b51fa 818
e8e52b3a 819 creature = session.query(Creature3).filter_by(
690b51fa 820 name=u'wizardsnake').one()
5de0f4da 821 assert creature.num_limbs == 0.0
78d17b80 822 assert creature.magical_powers == []
94eff10d 823
e8e52b3a
CAW
824 level = session.query(Level3).filter_by(
825 id=u'necroplex').one()
826 assert level.name == u'The Necroplex'
505c0db1 827 assert level.description == u'A complex full of pure deathzone.'
e8e52b3a
CAW
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'}
09e2c487
CAW
847
848
d2506eeb 849#def test_set2_to_set3():
09e2c487
CAW
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
d2506eeb 860 # pass
09e2c487
CAW
861
862
d2506eeb 863#def test_set1_to_set2_to_set3():
09e2c487
CAW
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
690b51fa
CAW
878
879 ##### Set2
880 # creature_table = Table(
881 # 'creature', metadata,
e920b968 882 # autoload=True, autoload_with=db_conn.bind)
690b51fa
CAW
883 # assert set(creature_table.c.keys()) == set(
884 # ['id', 'name', 'num_legs'])
885 # assert_col_type(creature_table.c.id, Integer)
c7fa585b 886 # assert_col_type(creature_table.c.name, VARCHAR)
690b51fa
CAW
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,
e920b968 896 # autoload=True, autoload_with=db_conn.bind)
690b51fa
CAW
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
c7fa585b
CAW
902 # assert_col_type(creature_power_table.c.name, VARCHAR)
903 # assert_col_type(creature_power_table.c.description, VARCHAR)
690b51fa
CAW
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,
e920b968 910 # autoload=True, autoload_with=db_conn.bind)
690b51fa
CAW
911 # assert set(level_table.c.keys()) == set(
912 # ['id', 'name', 'description'])
c7fa585b 913 # assert_col_type(level_table.c.id, VARCHAR)
690b51fa 914 # assert level_table.c.id.primary_key is True
c7fa585b
CAW
915 # assert_col_type(level_table.c.name, VARCHAR)
916 # assert_col_type(level_table.c.description, VARCHAR)
690b51fa
CAW
917
918 # # Check the structure of the level_exits table
919 # level_exit_table = Table(
920 # 'level_exit', metadata,
e920b968 921 # autoload=True, autoload_with=db_conn.bind)
690b51fa
CAW
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)
c7fa585b
CAW
925 # assert_col_type(level_exit_table.c.name, VARCHAR)
926 # assert_col_type(level_exit_table.c.from_level, VARCHAR)
690b51fa 927 # assert level_exit_table.c.from_level.nullable is False
c7fa585b 928 # assert_col_type(level_exit_table.c.to_level, VARCHAR)
690b51fa 929
d2506eeb 930 # pass