def test_trigger(): instance = TriggerConcurrentModel() assert instance.pk is None assert instance.version == 0 instance.username = next(nextname) instance.save() # insert instance = refetch(instance) assert instance.version == 1 instance.username = next(nextname) instance.save() # update assert instance.version == 2 instance.username = next(nextname) instance.save() # update assert instance.version == 3 instance.username = next(nextname) instance.save(refetch=True) # update assert instance.version == 4 copy = refetch(instance) copy.save() with pytest.raises(RecordModifiedError): instance.save()
def test_save_allowed(instance): # Scenario: batch change field NOT present in ConcurrencyMeta.check_fields # the user is ALLOWED to save batch_instance = instance.__class__.objects.get(pk=instance.pk) assert batch_instance.version == instance.version instance = refetch(instance) batch_instance = refetch(instance) batch_instance.field3 = 'aaaa' batch_instance.save() instance.save()
def test_disable_concurrency_instance(model_class=SimpleConcurrentModel): instance1 = model_class(username=next(nextname)) instance1.save() copy1 = refetch(instance1) copy1.save() instance2 = model_class(username=next(nextname)) instance2.save() copy2 = refetch(instance2) copy2.save() with disable_concurrency(instance1): instance1.save() with pytest.raises(RecordModifiedError): instance2.save()
def test_disable_concurrency_class(model_class=SimpleConcurrentModel): instance = model_class(username=next(nextname)) instance.save() copy = refetch(instance) copy.save() with disable_concurrency(SimpleConcurrentModel): instance.save()
def test_get_version(model_class=SimpleConcurrentModel): instance = model_class(username=next(nextname)) instance.save() copy = refetch(instance) copy.save() instance = get_version(instance, copy.version) assert instance.get_concurrency_version() == copy.get_concurrency_version()
def test_trigger_external_create(): with connection.cursor() as c: c.execute("INSERT INTO {} (username, count, cm_version_id) VALUES ('abc', 1, -1)".format( TriggerConcurrentModel._meta.db_table)) instance = TriggerConcurrentModel.objects.get(username='******') obj = refetch(instance) assert obj.version == -1
def test_threads(): if db.connection.vendor == 'sqlite': pytest.skip("in-memory sqlite db can't be used between threads") obj = TriggerConcurrentModel.objects.create() transaction.commit() @concurrently(25) def run(): for i in range(5): while True: x = refetch(obj) transaction.commit() x.count += 1 try: x.save() transaction.commit() except RecordModifiedError: # retry pass else: break run() assert refetch(obj).count == 5 * 25
def test_update_fields(model_class): """ Calling save with update_fields not containing version doesn't update the version. """ instance = model_class.objects.create(username='******') copy = refetch(instance) # do not update version instance.save(update_fields=['username']) # copy can be saved copy.username = '******' copy.save() assert refetch(instance).username, 'def' assert refetch(instance).version == copy.version
def test_trigger_external_create(): with connection.cursor() as c: c.execute( "INSERT INTO {} (username, count, cm_version_id) VALUES ('abc', 1, -1)" .format(TriggerConcurrentModel._meta.db_table)) instance = TriggerConcurrentModel.objects.get(username='******') obj = refetch(instance) assert obj.version == -1
def test_update(model_class): # Manager.update() does not change version number instance = model_class.objects.create(pk=next(unique_id), username=next(nextname).lower()) field_value = instance.username model_class.objects.filter(pk=instance.pk).update(username=instance.username.upper()) instance2 = refetch(instance) assert instance2.username == field_value.upper() assert instance2.get_concurrency_version() == instance.get_concurrency_version()
def test_get_or_create_with_pk(model_class): instance, __ = model_class.objects.get_or_create(pk=next(unique_id)) assert instance.get_concurrency_version() instance.save() copy = refetch(instance) copy.save() with pytest.raises(RecordModifiedError): instance.save() assert copy.get_concurrency_version() > instance.get_concurrency_version()
def inner(self, force_insert=False, force_update=False, using=None, **kwargs): reload = kwargs.pop('refetch', False) ret = func(self, force_insert, force_update, using, **kwargs) TriggerVersionField._increment_version_number(self) if reload: ret = refetch(self) setattr(self, self._concurrencymeta.field.attname, get_revision_of_object(ret)) return ret
def test_select_lock(settings): s1 = SimpleConcurrentModel.objects.create() s2 = refetch(s1) assert s1.version == s2.version s2.save() with pytest.raises(RecordModifiedError): _select_lock(s1) settings.CONCURRENCY_ENABLED = False _select_lock(s1)
def test_trigger_external_update(): instance = TriggerConcurrentModel() assert instance.pk is None assert instance.version == 0 instance.save() assert instance.version == 1 with connection.cursor() as c: c.execute("UPDATE {} SET username='******' WHERE id='{}'".format(instance._meta.db_table, instance.pk)) obj = refetch(instance) assert obj.version == 2
def test_apply_concurrency_check(): apply_concurrency_check(Group, 'version', IntegerVersionField) instance, __ = Group.objects.get_or_create(name=next(nextgroup)) instance.save() copy = refetch(instance) copy.save() with pytest.raises(RecordModifiedError): instance.save()
def test_update(model_class): # Manager.update() does not change version number instance = model_class.objects.create(pk=next(unique_id), username=next(nextname).lower()) field_value = instance.username model_class.objects.filter(pk=instance.pk).update( username=instance.username.upper()) instance2 = refetch(instance) assert instance2.username == field_value.upper() assert instance2.get_concurrency_version( ) == instance.get_concurrency_version()
def test_trigger_external_update(): instance = TriggerConcurrentModel() assert instance.pk is None assert instance.version == 0 instance.save() assert instance.version == 1 with connection.cursor() as c: c.execute("UPDATE {} SET username='******' WHERE id='{}'".format( instance._meta.db_table, instance.pk)) obj = refetch(instance) assert obj.version == 2
def test_update_fields_still_checks(model_class): """ Excluding the VersionField from update_fields should still check for conflicts. """ instance = model_class.objects.create(username='******') copy = refetch(instance) instance.save() copy.name = 'def' with pytest.raises(RecordModifiedError): copy.save(update_fields=['username'])
def test_conflict_no_version_and_no_skip_flag(model_class): """When IGNORE_DEFAULT is disabled, attempting to update a record with a default version number should fail.""" with override_settings(CONCURRENCY_IGNORE_DEFAULT=False): id = next(unique_id) instance, __ = model_class.objects.get_or_create(pk=id) instance.save() copy = refetch(instance) copy.version = 0 with pytest.raises(RecordModifiedError): copy.save()
def test_conflict(model_class, protocol, monkeypatch): monkeypatch.setattr(concurrency.config.conf, 'PROTOCOL', protocol) id = next(unique_id) instance = model_class.objects.get_or_create(pk=id)[0] instance.save() copy = refetch(instance) copy.save() with pytest.raises(RecordModifiedError): instance.save() assert copy.get_concurrency_version() > instance.get_concurrency_version()
def run(): for i in range(5): while True: x = refetch(obj) transaction.commit() x.count += 1 try: x.save() transaction.commit() except RecordModifiedError: # retry pass else: break
def test_do_not_check_if_no_version(model_class): id = next(unique_id) instance, __ = model_class.objects.get_or_create(pk=id) instance.save() copy = refetch(instance) copy.save() with pytest.raises(RecordModifiedError): _set_version(instance, 1) instance.version = 1 instance.save() _set_version(instance, 0) instance.save() assert instance.get_concurrency_version() > 0 assert instance.get_concurrency_version() != copy.get_concurrency_version()
def test_disable_concurrency_global(): instance1 = SimpleConcurrentModel(username=next(nextname)) instance2 = AutoIncConcurrentModel(username=next(nextname)) instance1.save() instance2.save() refetch(instance1).save() refetch(instance2).save() with disable_concurrency(): instance1.save() instance2.save() copy2 = refetch(instance2) refetch(instance2).save() with pytest.raises(RecordModifiedError): copy2.save()
def test_concurrency(self): id = 1 admin_register(ListEditableConcurrentModel, ActionsModelAdmin) model_admin = site._registry[ListEditableConcurrentModel] with attributes((ConcurrentModelAdmin, 'list_editable_policy', CONCURRENCY_LIST_EDITABLE_POLICY_SILENT), (ConcurrentModelAdmin, 'form', ConcurrentForm), ): obj, __ = ListEditableConcurrentModel.objects.get_or_create(pk=id) request1 = get_fake_request('pk=%s&_concurrency_version_1=2' % id) model_admin.save_model(request1, obj, None, True) self.assertIn(obj.pk, model_admin._get_conflicts(request1)) obj = refetch(obj) request2 = get_fake_request('pk=%s&_concurrency_version_1=%s' % (id, obj.version)) model_admin.save_model(request2, obj, None, True) self.assertNotIn(obj.pk, model_admin._get_conflicts(request2))
def test_do_not_check_if_no_version(model_class): with override_settings(CONCURRENCY_VERSION_FIELD_REQUIRED=False): id = next(unique_id) instance, __ = model_class.objects.get_or_create(pk=id) instance.save() copy = refetch(instance) copy.save() with pytest.raises(RecordModifiedError): _set_version(instance, 1) instance.version = 1 instance.save() _set_version(instance, 0) instance.save() assert instance.get_concurrency_version() > 0 assert instance.get_concurrency_version() != copy.get_concurrency_version()
def test_standard_save(instance): # only increment if checked field instance = refetch(instance) version1 = instance.get_concurrency_version() assert version1 == 1 # version2 > version1 instance.field1 = '2' instance.save() version2 = instance.get_concurrency_version() assert version2 == 2 # version2 > version1 instance.field3 = '3' instance.save() version3 = instance.get_concurrency_version() assert version3 == 2 # version3 == version2 instance.user = None instance.save() version4 = instance.get_concurrency_version() assert version4 == 3 # version4 > version2
def test_do_not_check_if_no_version(model_class): with override_settings(CONCURRENCY_VERSION_FIELD_REQUIRED=False): id = next(unique_id) instance, __ = model_class.objects.get_or_create(pk=id) instance.save() copy = refetch(instance) copy.save() with pytest.raises(RecordModifiedError): _set_version(instance, 1) instance.version = 1 instance.save() _set_version(instance, 0) instance.save() assert instance.get_concurrency_version() > 0 assert instance.get_concurrency_version( ) != copy.get_concurrency_version()
def test_concurrency(self): id = 1 admin_register(ListEditableConcurrentModel, ActionsModelAdmin) model_admin = site._registry[ListEditableConcurrentModel] with attributes( (ConcurrentModelAdmin, 'list_editable_policy', CONCURRENCY_LIST_EDITABLE_POLICY_SILENT), (ConcurrentModelAdmin, 'form', ConcurrentForm), ): obj, __ = ListEditableConcurrentModel.objects.get_or_create(pk=id) request1 = get_fake_request('pk=%s&_concurrency_version_1=2' % id) model_admin.save_model(request1, obj, None, True) self.assertIn(obj.pk, model_admin._get_conflicts(request1)) obj = refetch(obj) request2 = get_fake_request('pk=%s&_concurrency_version_1=%s' % (id, obj.version)) model_admin.save_model(request2, obj, None, True) self.assertNotIn(obj.pk, model_admin._get_conflicts(request2))
def test_is_changed(model_class=SimpleConcurrentModel): instance = model_class(username=next(nextname)) instance.save() copy = refetch(instance) copy.save() assert is_changed(instance)
def test_disable_concurrency_settings(settings): with override_settings(CONCURRENCY_ENABLED=False): instance1 = SimpleConcurrentModel(username=next(nextname)) instance1.save() refetch(instance1).save()
def test_standard_save(model_class): instance = model_class(username=concurrent_model.__name__) instance.save() assert instance.get_concurrency_version() > 0 instance = refetch(instance) assert instance.get_concurrency_version() > 0
def test1(): instance = SimpleConcurrentModel(username=next(nextname)) instance.save() copy = refetch(instance) copy.save() instance.save()