def test_get_signal_function(self):
        # check cache
        f = FileFieldAutoChangeDelete('file_field')
        a = f.get_signal_function()
        b = f.get_signal_function()
        self.assertIs(a, b)

        # with file_field
        mock = MagicMock()
        a(None, mock)
        self.assertIn(call.is_dirty, mock.mock_calls)
        self.assertIn(call.get_dirty_fields().get('file_field', None),
                      mock.mock_calls)
        self.assertIn(call.get_dirty_fields().get().delete(save=False),
                      mock.mock_calls)

        mock = MagicMock()
        mock.get_dirty_fields.return_value = {}
        a(None, mock)
        self.assertNotIn(call.get_dirty_fields().get().delete(save=False),
                         mock.mock_calls)
 def setup_method(self, method):
     self.decorator = FileFieldAutoChangeDelete()
class TestFileFieldAutoChangeDelete(TestCase):
    def setup_method(self, method):
        self.decorator = FileFieldAutoChangeDelete()

    def get_model(self):
        class Model(DirtyFieldsMixin, models.Model):
            file_field = models.FileField(upload_to='foo')
            foo_field = models.CharField(max_length=32)

            class Meta(object):
                app_label = 'foo'

        return Model

    def get_empty_model(self):
        class ModelEmpty(DirtyFieldsMixin, models.Model):
            class Meta(object):
                app_label = 'foo'

        return ModelEmpty

    def test_init(self):
        decorator = FileFieldAutoChangeDelete()

        assert decorator.field_names == '*'
        assert decorator.signal is models.signals.post_save
        assert decorator.signal_name_pattern == 'post_save_{model.__name__}_changedelete_{field}'

    def test_validate_model_no_is_dirty_implementation(self):
        self.decorator.to_wrap = self.get_empty_model()
        self.decorator.to_wrap.is_dirty = None
        self.decorator.to_wrap.get_dirty_fields = None

        with pytest.raises(ValueError):
            self.decorator.validate_model()

    def test_validate_model(self):
        self.decorator.to_wrap = self.get_empty_model()

        assert self.decorator.validate_model() is None

    @mock.patch.object(DirtyFieldsMixin, 'get_dirty_fields')
    @mock.patch.object(DirtyFieldsMixin, 'is_dirty')
    def test_get_signal_function_not_dirty(
            self, mock_is_dirty, mock_get_dirty_fields):
        mock_is_dirty.return_value = False
        model = self.get_model()
        self.decorator.to_wrap = model
        self.decorator.fields = ['file_field']

        signal = self.decorator.get_signal_function()

        assert callable(signal)

        instance = model()

        assert signal(model, instance) is None
        mock_is_dirty.assert_called_once_with()
        assert not mock_get_dirty_fields.called

    @mock.patch.object(DirtyFieldsMixin, 'get_dirty_fields')
    @mock.patch.object(DirtyFieldsMixin, 'is_dirty')
    def test_get_signal_function_dirty_but_no_file(
            self, mock_is_dirty, mock_get_dirty_fields):
        mock_is_dirty.return_value = True
        mock_get_dirty_fields.return_value = {}
        model = self.get_model()
        self.decorator.to_wrap = model
        self.decorator.fields = [FieldSpec('file_field', None)]

        signal = self.decorator.get_signal_function()

        assert callable(signal)

        instance = model()

        assert signal(model, instance) is None
        mock_is_dirty.assert_called_once_with()
        mock_get_dirty_fields.assert_called_once_with()

    @mock.patch.object(DirtyFieldsMixin, 'get_dirty_fields')
    @mock.patch.object(DirtyFieldsMixin, 'is_dirty')
    def test_get_signal_function(
            self, mock_is_dirty, mock_get_dirty_fields):
        f = mock.MagicMock()
        field = mock.MagicMock()
        mock_is_dirty.return_value = True
        mock_get_dirty_fields.return_value = {'file_field': f}
        model = self.get_model()
        self.decorator.to_wrap = model
        self.decorator.fields = [FieldSpec('file_field', field)]

        signal = self.decorator.get_signal_function()

        assert callable(signal)

        instance = model(file_field='foo')

        assert signal(model, instance) is None
        mock_is_dirty.assert_called_once_with()
        mock_get_dirty_fields.assert_called_once_with()
        f.delete.assert_called_once_with(save=False)
        assert f.instance == instance
        assert f.field == field
        assert f.storage == field.storage
        assert instance.file_field == 'foo'
 def setUpClass(cls):
     cls.f = FileFieldAutoChangeDelete.to_decorator()('file_field')