def test_with_bad_app(self): """Testing AddField with application not in signature""" mutation = AddField('TestModel', 'char_field1', models.CharField) message = ( 'Cannot add the field "char_field1" to model "badapp.TestModel". ' 'The application could not be found in the signature.') with self.assertRaisesMessage(SimulationFailure, message): mutation.run_simulation(app_label='badapp', project_sig=ProjectSignature(), database_state=None)
def test_with_bad_app(self): """Testing AddField with application not in signature""" mutation = AddField('TestModel', 'char_field1', models.CharField) message = ( 'Cannot add the field "char_field1" to model "badapp.TestModel". ' 'The application could not be found in the signature.' ) with self.assertRaisesMessage(SimulationFailure, message): mutation.run_simulation(app_label='badapp', project_sig=ProjectSignature(), database_state=None)
def test_with_bad_model(self): """Testing AddField with model not in signature""" mutation = AddField('TestModel', 'char_field1', models.CharField) project_sig = ProjectSignature() project_sig.add_app_sig(AppSignature(app_id='tests')) message = ( 'Cannot add the field "char_field1" to model "tests.TestModel". ' 'The model could not be found in the signature.') with self.assertRaisesMessage(SimulationFailure, message): mutation.run_simulation(app_label='tests', project_sig=project_sig, database_state=None)
def test_add_unique_column(self): """Testing AddField with unique column""" class DestModel(models.Model): char_field = models.CharField(max_length=20) int_field = models.IntegerField() added_field = models.IntegerField(unique=True, null=True) self.assertFalse( has_index_with_columns(self.database_sig, 'tests_testmodel', ['added_field'])) self.perform_evolution_tests(DestModel, [ AddField('TestModel', 'added_field', models.IntegerField, unique=True, null=True), ], ("In model tests.TestModel:\n" " Field 'added_field' has been added"), [ "AddField('TestModel', 'added_field', models.IntegerField," " unique=True, null=True)", ], 'AddUniqueColumnModel') self.assertTrue( has_index_with_columns(self.test_database_sig, 'tests_testmodel', ['added_field']))
def test_add_rename_field_with_db_column(self): """Testing pre-processing AddField + RenameField with RenameField.db_column """ class DestModel(models.Model): my_id = models.AutoField(primary_key=True) char_field = models.CharField(max_length=20) renamed_field = models.CharField(max_length=50, null=True, db_column='added_field') self.perform_evolution_tests(DestModel, [ AddField('TestModel', 'added_field', models.CharField, max_length=50, null=True), RenameField('TestModel', 'added_field', 'renamed_field', db_column='added_field'), ], ("In model tests.TestModel:\n" " Field 'renamed_field' has been added"), [ "AddField('TestModel', 'renamed_field', models.CharField," " max_length=50, null=True, db_column='added_field')", ], 'add_rename_field_with_db_column')
def test_excludes_unchanged_models(self): """Testing get_app_pending_mutations excludes mutations for unchanged models """ self.ensure_evolution_models() latest_version = Version.objects.current_version() old_project_sig = latest_version.signature # Make a change in the new signature for Evolution, so it will be # considered for pending mutations. new_project_sig = old_project_sig.clone() model_sig = ( new_project_sig .get_app_sig('django_evolution') .get_model_sig('Evolution') ) model_sig.get_field_sig('app_label').field_attrs['max_length'] = 500 # Only the first mutation should match. mutations = [ ChangeField('Evolution', 'app_label', models.CharField, max_length=500), AddField('Version', 'new_field', models.BooleanField, default=True), ] pending_mutations = get_app_pending_mutations( app=get_app('django_evolution'), old_project_sig=old_project_sig, project_sig=new_project_sig, mutations=mutations) self.assertEqual(pending_mutations, [mutations[0]])
def test_add_change_change_field(self): """Testing pre-processing AddField + ChangeField + ChangeField""" class DestModel(models.Model): my_id = models.AutoField(primary_key=True) char_field = models.CharField(max_length=20) added_field = models.CharField(max_length=50, null=True) self.perform_evolution_tests(DestModel, [ AddField('TestModel', 'added_field', models.CharField, initial='foo', max_length=20), ChangeField('TestModel', 'added_field', null=True, initial='bar', max_length=30), ChangeField( 'TestModel', 'added_field', initial='bar', max_length=50), ], ("In model tests.TestModel:\n" " Field 'added_field' has been added"), [ "AddField('TestModel', 'added_field', models.CharField," " max_length=50, null=True)", ], 'add_change_field')
def test_with_bad_model(self): """Testing AddField with model not in signature""" mutation = AddField('TestModel', 'char_field1', models.CharField) project_sig = ProjectSignature() project_sig.add_app_sig(AppSignature(app_id='tests')) message = ( 'Cannot add the field "char_field1" to model "tests.TestModel". ' 'The model could not be found in the signature.' ) with self.assertRaisesMessage(SimulationFailure, message): mutation.run_simulation(app_label='tests', project_sig=project_sig, database_state=None)
def test_add_unique_indexed_column(self): """Testing AddField with unique indexed column""" class DestModel(models.Model): char_field = models.CharField(max_length=20) int_field = models.IntegerField() added_field = models.IntegerField(unique=True, db_index=True, null=True) self.assertIsNone( self.database_state.find_index(table_name='tests_testmodel', columns=['added_field'], unique=True)) self.perform_evolution_tests(DestModel, [ AddField('TestModel', 'added_field', models.IntegerField, unique=True, null=True, db_index=True), ], ("In model tests.TestModel:\n" " Field 'added_field' has been added"), [ "AddField('TestModel', 'added_field', models.IntegerField," " db_index=True, null=True, unique=True)", ], 'AddUniqueIndexedModel') self.assertIsNotNone( self.test_database_state.find_index(table_name='tests_testmodel', columns=['added_field'], unique=True))
def test_add_with_custom_table_name(self): """Testing AddField with custom table name""" class DestModel(BaseTestModel): value = models.IntegerField() alt_value = models.CharField(max_length=20) added_field = models.IntegerField(null=True) class Meta(BaseTestModel.Meta): db_table = 'custom_table_name' def create_test_data(db_name): CustomAddTableModel.objects.create(value=100, alt_value='test') self.set_base_model(CustomAddTableModel, name='CustomTableModel') self.perform_evolution_tests(DestModel, [ AddField('CustomTableModel', 'added_field', models.IntegerField, null=True), ], ("In model tests.CustomTableModel:\n" " Field 'added_field' has been added"), [ "AddField('CustomTableModel', 'added_field'," " models.IntegerField, null=True)", ], 'AddColumnCustomTableModel', model_name='CustomTableModel', create_test_data_func=create_test_data)
def test_add_with_custom_database(self): """Testing AddField with custom database""" class DestModel(models.Model): char_field = models.CharField(max_length=20) int_field = models.IntegerField() added_field = models.ForeignKey(AddAnchor1, null=True, on_delete=models.CASCADE) self.set_base_model(self.default_base_model, extra_models=self.default_extra_models, db_name='db_multi') self.perform_evolution_tests(DestModel, [ AddField('TestModel', 'added_field', models.ForeignKey, null=True, related_model='tests.AddAnchor1'), ], ("In model tests.TestModel:\n" " Field 'added_field' has been added"), [ "AddField('TestModel', 'added_field', models.ForeignKey," " null=True, related_model='tests.AddAnchor1')", ], 'AddForeignKeyModel', db_name='db_multi')
def test_add_primary_key_with_delete_old_fails(self): """Testing AddField with primary key and deleting old key fails""" class DestModel(models.Model): my_primary_key = models.AutoField(primary_key=True) char_field = models.CharField(max_length=20) int_field = models.IntegerField() message = ( 'Cannot delete the field "id" on model "tests.TestModel". The ' 'field is a primary key and cannot be deleted.') with self.assertRaisesMessage(SimulationFailure, message): self.perform_evolution_tests(DestModel, [ AddField('TestModel', 'my_primary_key', models.AutoField, initial=AddSequenceFieldInitial('AddPrimaryKeyModel'), primary_key=True), DeleteField('TestModel', 'id'), ], ("In model tests.TestModel:\n" " Field 'my_primary_key' has been added\n" " Field 'id' has been deleted"), [ "AddField('TestModel', 'my_primary_key', models.AutoField," " initial=<<USER VALUE REQUIRED>>, primary_key=True)", "DeleteField('TestModel', 'id')", ], None)
def evolution(self): "Generate an evolution that would neutralize the diff" mutations = {} for app_label, app_changes in self.changed.items(): for model_name, change in app_changes.get('changed', {}).items(): for field_name in change.get('added',{}): field_sig = self.current_sig[app_label][model_name]['fields'][field_name] add_params = [(key,field_sig[key]) for key in field_sig.keys() if key in ATTRIBUTE_DEFAULTS.keys()] add_params.append(('field_type', field_sig['field_type'])) if (field_sig['field_type'] != models.ManyToManyField and not field_sig.get('null', ATTRIBUTE_DEFAULTS['null'])): add_params.append( ('initial', get_initial_value(app_label, model_name, field_name))) if 'related_model' in field_sig: add_params.append(('related_model', '%s' % field_sig['related_model'])) mutations.setdefault(app_label,[]).append( AddField(model_name, field_name, **dict(add_params))) for field_name in change.get('deleted',[]): mutations.setdefault(app_label,[]).append( DeleteField(model_name, field_name)) for field_name,field_change in change.get('changed',{}).items(): changed_attrs = {} current_field_sig = self.current_sig[app_label][model_name]['fields'][field_name] for prop in field_change: if prop == 'related_model': changed_attrs[prop] = current_field_sig[prop] else: changed_attrs[prop] = \ current_field_sig.get(prop, ATTRIBUTE_DEFAULTS[prop]) if (changed_attrs.has_key('null') and current_field_sig['field_type'] != models.ManyToManyField and not current_field_sig.get('null', ATTRIBUTE_DEFAULTS['null'])): changed_attrs['initial'] = \ get_initial_value(app_label, model_name, field_name) mutations.setdefault(app_label,[]).append( ChangeField(model_name, field_name,current_field_sig, **changed_attrs)) #j----------add for model_name in app_changes.get('deleted',{}): mutations.setdefault(app_label,[]).append( DeleteModel(model_name)) return mutations
def test_evolve(self): """Testing Evolver.evolve""" model_sig = ModelSignature.from_model(EvolverTestModel) model_sig.get_field_sig('value').field_attrs['max_length'] = 50 app_sig = AppSignature(app_id='tests') app_sig.add_model_sig(model_sig) orig_version = Version.objects.current_version() orig_version.signature.add_app_sig(app_sig) orig_version.save() with ensure_test_db(model_entries=[('TestModel', EvolverTestModel)]): evolver = Evolver() evolver.queue_task( EvolveAppTask(evolver=evolver, app=evo_test, evolutions=[ { 'label': 'my_evolution1', 'mutations': [ ChangeField('TestModel', 'value', max_length=200), ], }, { 'label': 'my_evolution2', 'mutations': [ AddField('TestModel', 'new_field', models.BooleanField, null=True), ], }, ])) evolver.evolve() self.assertTrue(evolver.evolved) version = Version.objects.current_version() self.assertNotEqual(version, orig_version) self.assertFalse(version.is_hinted()) evolutions = list(version.evolutions.all()) self.assertEqual(len(evolutions), 2) self.assertEqual(evolutions[0].app_label, 'tests') self.assertEqual(evolutions[0].label, 'my_evolution1') self.assertEqual(evolutions[1].app_label, 'tests') self.assertEqual(evolutions[1].label, 'my_evolution2') model_sig = ( version.signature.get_app_sig('tests').get_model_sig('TestModel')) self.assertEqual( model_sig.get_field_sig('value').field_attrs['max_length'], 200) self.assertIsNotNone(model_sig.get_field_sig('new_field'))
def test_add_delete_add_field(self): """Testing pre-processing AddField + DeleteField + AddField""" class DestModel(models.Model): my_id = models.AutoField(primary_key=True) char_field = models.CharField(max_length=20) added_field = models.IntegerField() self.perform_evolution_tests(DestModel, [ AddField('TestModel', 'added_field', models.CharField, initial='', max_length=20), DeleteField('TestModel', 'added_field'), AddField( 'TestModel', 'added_field', models.IntegerField, initial=42) ], ("In model tests.TestModel:\n" " Field 'added_field' has been added"), [ "AddField('TestModel', 'added_field', models.IntegerField," " initial=<<USER VALUE REQUIRED>>)", ], 'add_delete_add_field')
def tst_add_null_column(self): """Testing AddField with NULL column""" class DestModel(models.Model): char_field = models.CharField(max_length=20) int_field = models.IntegerField() added_field = models.IntegerField(null=True) self.perform_evolution_tests(DestModel, [ AddField('TestModel', 'added_field', models.CharField, null=True), ], self.DIFF_TEXT, [ "AddField('TestModel', 'added_field', models.CharField," " null=True)", ], 'AddNullColumnModel')
def test_with_dependencies_and_custom_evolutions(self): """Testing get_evolution_dependencies with dependencies and custom evolutions """ self.assertEqual( get_evolution_dependencies( app=get_app('evolution_deps_app'), evolution_label='test_custom_evolution', custom_evolutions=[ { 'label': 'test_custom_evolution', 'after_evolutions': [ ('other_app1', 'other_evolution1'), ('other_app1', 'other_evolution2'), ], 'after_migrations': [ ('other_app2', '0001_migration'), ('other_app2', '0002_migration'), ], 'before_evolutions': [ ('other_app3', 'other_evolution3'), ('other_app3', 'other_evolution4'), ], 'before_migrations': [ ('other_app4', '0003_migration'), ('other_app4', '0004_migration'), ], 'mutations': [ AddField('MyModel', 'new_field', models.BooleanField), ], }, ]), { 'after_evolutions': { ('other_app1', 'other_evolution1'), ('other_app1', 'other_evolution2'), }, 'after_migrations': { ('other_app2', '0001_migration'), ('other_app2', '0002_migration'), }, 'before_evolutions': { ('other_app3', 'other_evolution3'), ('other_app3', 'other_evolution4'), }, 'before_migrations': { ('other_app4', '0003_migration'), ('other_app4', '0004_migration'), }, })
def test_add_with_default(self): """Testing AddField with default value""" class DestModel(models.Model): char_field = models.CharField(max_length=20) int_field = models.IntegerField() added_field = models.IntegerField(default=42) self.perform_evolution_tests(DestModel, [ AddField( 'TestModel', 'added_field', models.IntegerField, initial=42), ], self.DIFF_TEXT, [ "AddField('TestModel', 'added_field', models.IntegerField," " initial=42)", ], 'AddDefaultColumnModel')
def test_add_non_null_column_with_initial(self): """Testing AddField with non-NULL column with initial value""" class DestModel(models.Model): char_field = models.CharField(max_length=20) int_field = models.IntegerField() added_field = models.IntegerField() self.perform_evolution_tests(DestModel, [ AddField( 'TestModel', 'added_field', models.IntegerField, initial=1), ], self.DIFF_TEXT, [ "AddField('TestModel', 'added_field', models.IntegerField," " initial=<<USER VALUE REQUIRED>>)", ], 'AddNonNullNonCallableColumnModel')
def test_with_bad_field(self): """Testing AddField with field already in signature""" mutation = AddField('TestModel', 'char_field1', models.CharField) model_sig = ModelSignature(model_name='TestModel', table_name='tests_testmodel') model_sig.add_field_sig( FieldSignature(field_name='char_field1', field_type=models.CharField)) app_sig = AppSignature(app_id='tests') app_sig.add_model_sig(model_sig) project_sig = ProjectSignature() project_sig.add_app_sig(app_sig) message = ( 'Cannot add the field "char_field1" to model "tests.TestModel". ' 'A field with this name already exists.') with self.assertRaisesMessage(SimulationFailure, message): mutation.run_simulation(app_label='tests', project_sig=project_sig, database_state=None)
def test_with_bad_field(self): """Testing AddField with field already in signature""" mutation = AddField('TestModel', 'char_field1', models.CharField) model_sig = ModelSignature(model_name='TestModel', table_name='tests_testmodel') model_sig.add_field_sig(FieldSignature(field_name='char_field1', field_type=models.CharField)) app_sig = AppSignature(app_id='tests') app_sig.add_model_sig(model_sig) project_sig = ProjectSignature() project_sig.add_app_sig(app_sig) message = ( 'Cannot add the field "char_field1" to model "tests.TestModel". ' 'A field with this name already exists.' ) with self.assertRaisesMessage(SimulationFailure, message): mutation.run_simulation(app_label='tests', project_sig=project_sig, database_state=None)
def test_add_rename_field_rename_model(self): """Testing pre-processing AddField + RenameField + RenameModel""" class RenamedReffedPreprocModel(models.Model): value = models.IntegerField() class Meta: db_table = 'tests_reffedpreprocmodel' class DestModel(models.Model): my_id = models.AutoField(primary_key=True) char_field = models.CharField(max_length=20) renamed_field = models.ForeignKey(RenamedReffedPreprocModel, null=True) self.set_base_model(self.default_base_model, extra_models=[('ReffedPreprocModel', ReffedPreprocModel)]) # Prepare the renamed model in the end signature. end, end_sig = self.make_end_signatures(DestModel, 'TestModel') end_tests_sig = end_sig['tests'] end_tests_sig['RenamedReffedPreprocModel'] = \ end_tests_sig.pop('ReffedPreprocModel') fields_sig = end_tests_sig['TestModel']['fields'] fields_sig['renamed_field']['related_model'] = \ 'tests.RenamedReffedPreprocModel' self.perform_evolution_tests(DestModel, [ AddField('TestModel', 'added_field', models.ForeignKey, null=True, related_model='tests.ReffedPreprocModel'), RenameField('TestModel', 'added_field', 'renamed_field'), RenameModel('ReffedPreprocModel', 'RenamedReffedPreprocModel', db_table='tests_reffedpreprocmodel'), ], ("The model tests.ReffedPreprocModel has been deleted\n" "In model tests.TestModel:\n" " Field 'renamed_field' has been added"), [ "AddField('TestModel', 'renamed_field', models.ForeignKey," " null=True, related_model='tests.RenamedReffedPreprocModel')", "DeleteModel('ReffedPreprocModel')", ], 'add_rename_field_rename_model', end=end, end_sig=end_sig)
def test_add_datetime_field(self): """Testing AddField with DateTimeField""" class DestModel(models.Model): char_field = models.CharField(max_length=20) int_field = models.IntegerField() added_field = models.DateTimeField() self.perform_evolution_tests(DestModel, [ AddField('TestModel', 'added_field', models.DateTimeField, initial=datetime(2007, 12, 13, 16, 42, 0)), ], self.DIFF_TEXT, [ "AddField('TestModel', 'added_field', models.DateTimeField," " initial=<<USER VALUE REQUIRED>>)", ], 'AddDateColumnModel')
def test_add_many_to_many_field(self): """Testing AddField with ManyToManyField""" class DestModel(BaseTestModel): char_field = models.CharField(max_length=20) int_field = models.IntegerField() added_field = models.ManyToManyField(AddAnchor1) self.perform_evolution_tests(DestModel, [ AddField('TestModel', 'added_field', models.ManyToManyField, related_model='tests.AddAnchor1'), ], ("In model tests.TestModel:\n" " Field 'added_field' has been added"), [ "AddField('TestModel', 'added_field', models.ManyToManyField," " related_model='tests.AddAnchor1')", ], 'AddManyToManyDatabaseTableModel')
def test_add_with_initial_string(self): """Testing AddField with string-based initial value""" class DestModel(models.Model): char_field = models.CharField(max_length=20) int_field = models.IntegerField() added_field = models.CharField(max_length=10) self.perform_evolution_tests(DestModel, [ AddField('TestModel', 'added_field', models.CharField, initial="abc's xyz", max_length=10), ], self.DIFF_TEXT, [ "AddField('TestModel', 'added_field', models.CharField," " initial=<<USER VALUE REQUIRED>>, max_length=10)" ], 'AddStringColumnModel')
def test_add_with_blank_initial_string(self): """Testing AddField with blank string initial value""" class DestModel(models.Model): char_field = models.CharField(max_length=20) int_field = models.IntegerField() added_field = models.CharField(max_length=10, blank=True) self.perform_evolution_tests(DestModel, [ AddField('TestModel', 'added_field', models.CharField, initial='', max_length=10), ], self.DIFF_TEXT, [ "AddField('TestModel', 'added_field', models.CharField," " initial='', max_length=10)" ], 'AddBlankStringColumnModel')
def test_add_null_column_with_initial(self): """Testing AddField with NULL column with initial value""" class DestModel(BaseTestModel): char_field = models.CharField(max_length=20) int_field = models.IntegerField() added_field = models.IntegerField(null=True) self.perform_evolution_tests(DestModel, [ AddField('TestModel', 'added_field', models.IntegerField, initial=1, null=True), ], self.DIFF_TEXT, [ "AddField('TestModel', 'added_field', models.IntegerField," " null=True)" ], 'AddNullColumnWithInitialColumnModel')
def test_add_many_to_many_field_to_self(self): """Testing AddField with ManyToManyField to self""" class DestModel(models.Model): char_field = models.CharField(max_length=20) int_field = models.IntegerField() added_field = models.ManyToManyField('self') self.perform_evolution_tests(DestModel, [ AddField('TestModel', 'added_field', models.ManyToManyField, related_model='tests.TestModel'), ], ("In model tests.TestModel:\n" " Field 'added_field' has been added"), [ "AddField('TestModel', 'added_field', models.ManyToManyField," " related_model='tests.TestModel')", ], 'AddManyToManySelf')
def test_add_delete_field(self): """Testing pre-processing AddField + DeleteField""" class DestModel(models.Model): my_id = models.AutoField(primary_key=True) char_field = models.CharField(max_length=20) self.perform_evolution_tests(DestModel, [ AddField('TestModel', 'added_field', models.CharField, initial='', max_length=20), DeleteField('TestModel', 'added_field'), ], '', [], 'noop', expect_noop=True)
def test_add_with_empty_string_default(self): """Testing AddField with empty string as default value""" class DestModel(models.Model): char_field = models.CharField(max_length=20) int_field = models.IntegerField() added_field = models.CharField(max_length=20, default='') self.perform_evolution_tests(DestModel, [ AddField('TestModel', 'added_field', models.CharField, initial='', max_length=20), ], self.DIFF_TEXT, [ "AddField('TestModel', 'added_field', models.CharField," " initial='', max_length=20)", ], 'AddEmptyStringDefaultColumnModel')
def test_add_boolean_field_with_different_initial(self): """Testing AddField with BooleanField and initial value different from model definition """ class DestModel(models.Model): char_field = models.CharField(max_length=20) int_field = models.IntegerField() added_field = models.BooleanField(default=True) self.perform_evolution_tests(DestModel, [ AddField( 'TestModel', 'added_field', models.BooleanField, initial=False), ], self.DIFF_TEXT, [ "AddField('TestModel', 'added_field', models.BooleanField," " initial=True)", ], 'AddMismatchInitialBoolColumnModel')
def test_add_binaryfield_with_initial(self): """Testing AddField with BinaryField and initial value""" class DestModel(BaseTestModel): char_field = models.CharField(max_length=20) int_field = models.IntegerField() added_field = models.BinaryField(null=False, default=b'test') self.perform_evolution_tests(DestModel, [ AddField('TestModel', 'added_field', models.BinaryField, initial=b'test', null=False), ], self.DIFF_TEXT, [ "AddField('TestModel', 'added_field', models.BinaryField," " initial='test')" ], 'AddBinaryFieldWithInitialColumnModel')
def test_add_with_custom_column_name(self): """Testing AddField with custom column name""" class DestModel(models.Model): char_field = models.CharField(max_length=20) int_field = models.IntegerField() added_field = models.IntegerField(db_column='non-default_column', null=True) self.perform_evolution_tests(DestModel, [ AddField('TestModel', 'added_field', models.IntegerField, null=True, db_column='non-default_column'), ], self.DIFF_TEXT, [ "AddField('TestModel', 'added_field', models.IntegerField," " db_column='non-default_column', null=True)", ], 'NonDefaultColumnModel')