A more reliable test, check against expected keys, rather than received
[mediagoblin.git] / mediagoblin / tests / test_sql_migrations.py
index 507a77252607b92beaa3785b70814a6323fbda8a..7e0569adcb349327f8943a9973f2d9fa1230d208 100644 (file)
 # You should have received a copy of the GNU Affero General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
+import six
+import pytest
+
+pytestmark = pytest.mark.skipif(six.PY3, reason='needs sqlalchemy.migrate')
+
 import copy
 
 from sqlalchemy import (
@@ -23,10 +28,12 @@ from sqlalchemy import (
 from sqlalchemy.orm import sessionmaker, relationship
 from sqlalchemy.ext.declarative import declarative_base
 from sqlalchemy.sql import select, insert
-from migrate import changeset
+if six.PY2:
+    from migrate import changeset
 
-from mediagoblin.db.sql.base import GMGTableBase
-from mediagoblin.db.sql.util import MigrationManager, RegisterMigration
+from mediagoblin.db.base import GMGTableBase
+from mediagoblin.db.migration_tools import MigrationManager, RegisterMigration
+from mediagoblin.tools.common import CollectingPrinter
 
 
 # This one will get filled with local migrations
@@ -57,6 +64,10 @@ class Level1(Base1):
 
 SET1_MODELS = [Creature1, Level1]
 
+FOUNDATIONS = {Creature1:[{'name':u'goblin','num_legs':2,'is_demon':False},
+                          {'name':u'cerberus','num_legs':4,'is_demon':True}]
+              }
+
 SET1_MIGRATIONS = {}
 
 #######################################################
@@ -185,7 +196,7 @@ def level_exits_new_table(db_conn):
 
     for level in result:
 
-        for exit_name, to_level in level['exits'].iteritems():
+        for exit_name, to_level in six.iteritems(level['exits']):
             # Insert the level exit
             db_conn.execute(
                 level_exits.insert().values(
@@ -321,6 +332,28 @@ def creature_power_hitpower_to_float(db_conn):
     creature_power.c.hitpower.alter(type=Float)
 
 
+@RegisterMigration(8, FULL_MIGRATIONS)
+def creature_power_name_creature_unique(db_conn):
+    """
+    Add a unique constraint to name and creature on creature_power.
+
+    We don't want multiple creature powers with the same name per creature!
+    """
+    # Note: We don't actually check to see if this constraint is set
+    # up because at present there's no way to do so in sqlalchemy :\
+
+    metadata = MetaData(bind=db_conn.bind)
+
+    creature_power = Table(
+        'creature_power', metadata,
+        autoload=True, autoload_with=db_conn.bind)
+        
+    cons = changeset.constraint.UniqueConstraint(
+        'name', 'creature', table=creature_power)
+
+    cons.create()
+
+
 def _insert_migration1_objects(session):
     """
     Test objects to insert for the first set of things
@@ -344,8 +377,8 @@ def _insert_migration1_objects(session):
                 name=u'The Necroplex',
                 description=u'A complex full of pure deathzone.',
                 exits={
-                    'deathwell': 'evilstorm',
-                    'portal': 'central_park'}),
+                    u'deathwell': u'evilstorm',
+                    u'portal': u'central_park'}),
          Level1(id=u'evilstorm',
                 name=u'Evil Storm',
                 description=u'A storm full of pure evil.',
@@ -354,7 +387,7 @@ def _insert_migration1_objects(session):
                 name=u'Central Park, NY, NY',
                 description=u"New York's friendly Central Park.",
                 exits={
-                    'portal': 'necroplex'})])
+                    u'portal': u'necroplex'})])
 
     session.commit()
 
@@ -519,19 +552,6 @@ def _insert_migration3_objects(session):
 
     session.commit()
 
-
-class CollectingPrinter(object):
-    def __init__(self):
-        self.collection = []
-    
-    def __call__(self, string):
-        self.collection.append(string)
-
-    @property
-    def combined_string(self):
-        return u''.join(self.collection)
-
-
 def create_test_engine():
     from sqlalchemy import create_engine
     engine = create_engine('sqlite:///:memory:', echo=False)
@@ -561,7 +581,7 @@ def test_set1_to_set3():
 
     printer = CollectingPrinter()
     migration_manager = MigrationManager(
-        '__main__', SET1_MODELS, SET1_MIGRATIONS, Session(),
+        u'__main__', SET1_MODELS, FOUNDATIONS, SET1_MIGRATIONS, Session(),
         printer)
 
     # Check latest migration and database current migration
@@ -574,11 +594,13 @@ def test_set1_to_set3():
     assert result == u'inited'
     # Check output
     assert printer.combined_string == (
-        "-> Initializing main mediagoblin tables... done.\n")
+        "-> Initializing main mediagoblin tables... done.\n" + \
+        "   + Laying foundations for Creature1 table\n"    )
     # Check version in database
     assert migration_manager.latest_migration == 0
     assert migration_manager.database_current_migration == 0
 
+
     # Install the initial set
     # -----------------------
 
@@ -586,8 +608,8 @@ def test_set1_to_set3():
 
     # Try to "re-migrate" with same manager settings... nothing should happen
     migration_manager = MigrationManager(
-        '__main__', SET1_MODELS, SET1_MIGRATIONS, Session(),
-        printer)
+        u'__main__', SET1_MODELS, FOUNDATIONS, SET1_MIGRATIONS, 
+        Session(), printer)
     assert migration_manager.init_or_migrate() == None
 
     # Check version in database
@@ -628,6 +650,20 @@ def test_set1_to_set3():
     # Now check to see if stuff seems to be in there.
     session = Session()
 
+    # Check the creation of the foundation rows on the creature table
+    creature = session.query(Creature1).filter_by(
+        name=u'goblin').one()
+    assert creature.num_legs == 2
+    assert creature.is_demon == False
+
+    creature = session.query(Creature1).filter_by(
+        name=u'cerberus').one()
+    assert creature.num_legs == 4
+    assert creature.is_demon == True
+
+    
+    # Check the creation of the inserted rows on the creature and levels tables
+  
     creature = session.query(Creature1).filter_by(
         name=u'centipede').one()
     assert creature.num_legs == 100
@@ -668,10 +704,10 @@ def test_set1_to_set3():
     # isn't said to be updated yet
     printer = CollectingPrinter()
     migration_manager = MigrationManager(
-        '__main__', SET3_MODELS, SET3_MIGRATIONS, Session(),
+        u'__main__', SET3_MODELS, FOUNDATIONS, SET3_MIGRATIONS, Session(),
         printer)
 
-    assert migration_manager.latest_migration == 7
+    assert migration_manager.latest_migration == 8
     assert migration_manager.database_current_migration == 0
 
     # Migrate
@@ -690,14 +726,15 @@ def test_set1_to_set3():
    + Running migration 5, "level_exit_index_from_and_to_level"... done.
    + Running migration 6, "creature_power_index_creature"... done.
    + Running migration 7, "creature_power_hitpower_to_float"... done.
+   + Running migration 8, "creature_power_name_creature_unique"... done.
 """
     
     # Make sure version matches expected
     migration_manager = MigrationManager(
-        '__main__', SET3_MODELS, SET3_MIGRATIONS, Session(),
+        u'__main__', SET3_MODELS, FOUNDATIONS, SET3_MIGRATIONS, Session(),
         printer)
-    assert migration_manager.latest_migration == 7
-    assert migration_manager.database_current_migration == 7
+    assert migration_manager.latest_migration == 8
+    assert migration_manager.database_current_migration == 8
 
     # Check all things in database match expected
 
@@ -760,6 +797,15 @@ def test_set1_to_set3():
 
     # Now check to see if stuff seems to be in there.
     session = Session()
+
+
+    # Start with making sure that the foundations did not run again
+    assert session.query(Creature3).filter_by(
+        name=u'goblin').count() == 1
+    assert session.query(Creature3).filter_by(
+        name=u'cerberus').count() == 1
+
+    # Then make sure the models have been migrated correctly
     creature = session.query(Creature3).filter_by(
         name=u'centipede').one()
     assert creature.num_limbs == 100.0