class ApiGetUploadTestCase(TestCase): def setUp(self): # Set up an initial file upload self.upload_id = _get_file_id() self.file_id = _get_file_id() self.file_content = ('This is some test file data for an ' 'uploaded file.') self.fn = 'my_test_file.txt' self.test_target_filename = '/test_storage/testfile.txt' self.su = StoredUpload(upload_id=self.upload_id, file=self.test_target_filename[1:], uploaded=timezone.now()) self.su.save() # Create file self.file_store_path = getattr(local_settings, 'FILE_STORE_PATH', None) file_full_path = os.path.join(self.file_store_path, self.test_target_filename[1:]) if not os.path.exists(os.path.dirname(file_full_path)): os.mkdir(os.path.dirname(file_full_path)) with open(file_full_path, 'w') as f: f.write(self.file_content) def test_store_upload_unset_file_store_path(self): (filename, bytes_io) = get_stored_upload_file_data(self.su) file_data = bytes_io.read().decode() self.assertEqual(file_data, self.file_content, 'Returned file content not correct.') self.assertEqual(filename, os.path.basename(self.test_target_filename), 'Returned file name is not correct.') def test_get_local_stored_upload_no_filestore(self): fsp = local_settings.FILE_STORE_PATH local_settings.FILE_STORE_PATH = None with self.assertRaisesMessage( ConfigurationError, 'The file upload settings are not configured correctly.'): get_stored_upload_file_data(self.su) local_settings.FILE_STORE_PATH = fsp def test_get_remote_stored_upload_data(self): # Set up the mock_storage_backend.open to return the file content mock_storage_backend = self._setup_mock_storage_backend() mock_storage_backend.open.return_value = BytesIO( self.file_content.encode()) mock_storage_backend.exists.return_value = True (filename, bytes_io) = get_stored_upload_file_data(self.su) file_data = bytes_io.read().decode() local_settings.STORAGES_BACKEND = None django_drf_filepond.api.storage_backend = None self.assertEqual(file_data, self.file_content, 'Returned file content not correct.') self.assertEqual(filename, os.path.basename(self.test_target_filename), 'Returned file name is not correct.') def test_storage_backend_initialised_when_required(self): mock_storage_backend = self._setup_mock_storage_backend() mock_storage_backend.open.return_value = BytesIO( self.file_content.encode()) mock_storage_backend.exists.return_value = True with patch('django_drf_filepond.api._init_storage_backend') as m: django_drf_filepond.api.storage_backend_initialised = False get_stored_upload_file_data(self.su) local_settings.STORAGES_BACKEND = None django_drf_filepond.api.storage_backend = None try: m.assert_called_once() # No assert_called_once on Python 3.5 except AttributeError: self.assertEqual( m.call_count, 1, ('Expected _init_storage_backend to be called once but ' 'it has been called %s times.' % m.call_count)) def test_get_remote_upload_not_on_remote_store(self): # File store path for remote testing should be '' file_store_path = '' mock_storage_backend = self._setup_mock_storage_backend() mock_storage_backend.exists.return_value = False file_path = os.path.join(file_store_path, self.su.file.name) with self.assertRaisesMessage( FileNotFoundError, ('File [%s] for upload_id [%s] not found on remote ' 'file store.' % (file_path, self.su.upload_id))): get_stored_upload_file_data(self.su) local_settings.STORAGES_BACKEND = None django_drf_filepond.api.storage_backend = None def _setup_mock_storage_backend(self): # Set storage backend to sftp storage local_settings.STORAGES_BACKEND = \ 'storages.backends.sftpstorage.SFTPStorage' # Set up mocked objects for storage backend testing # Setting up a mock for the storage backend based on examples at # https://docs.python.org/3.7/library/unittest.mock-examples.html# \ # applying-the-same-patch-to-every-test-method patcher = patch('storages.backends.sftpstorage.SFTPStorage') patcher.start() self.addCleanup(patcher.stop) # Mock the temporary storage object so that we're not unnecessarily # saving files to the local file system. patcher2 = patch('django.core.files.storage.FileSystemStorage') patcher2.start() self.addCleanup(patcher2.stop) # Set the backend initialisation flag to false to force re-init django_drf_filepond.api.storage_backend_initialised = False django_drf_filepond.api._init_storage_backend() mock_storage_backend = django_drf_filepond.api.storage_backend return mock_storage_backend def tearDown(self): # Delete stored upload self.su.delete() # Delete files filestore_base = getattr(local_settings, 'FILE_STORE_PATH', None) test_stored_file = os.path.join(filestore_base, self.test_target_filename[1:]) test_target_dir = os.path.dirname(test_stored_file) # Remove test_stored_file if (os.path.exists(test_stored_file) and os.path.isfile(test_stored_file)): LOG.debug('Removing test stored file: <%s>' % test_stored_file) os.remove(test_stored_file) # Remove directory if (os.path.exists(test_target_dir) and os.path.isdir(test_target_dir)): LOG.debug('Removing test target dir: <%s>' % test_target_dir) os.rmdir(test_target_dir)
class ApiDeleteTestCase(TestCase): def setUp(self): self.storage_backend = local_settings.STORAGES_BACKEND # Set storage backend to sftp storage local_settings.STORAGES_BACKEND = \ 'storages.backends.sftpstorage.SFTPStorage' # Setting up a mock for the storage backend based on examples at # https://docs.python.org/3.7/library/unittest.mock-examples.html# \ # applying-the-same-patch-to-every-test-method patcher = patch('storages.backends.sftpstorage.SFTPStorage') patcher.start() self.addCleanup(patcher.stop) # Mock the temporary storage object so that we're not unnecessarily # saving files to the local file system. patcher2 = patch('django.core.files.storage.FileSystemStorage') patcher2.start() self.addCleanup(patcher2.stop) # Reinitialse stored upload after we changed the STORAGES_BACKEND # class name above. This ensures that we're looking at the mocked # backend class. import django_drf_filepond.api self.api = django_drf_filepond.api django_drf_filepond.api.storage_backend_initialised = False django_drf_filepond.api._init_storage_backend() self.mock_storage_backend = django_drf_filepond.api.storage_backend self.delete_upload = django_drf_filepond.api.delete_stored_upload # Check that we're using a mocked storage backend self.assertTrue( isinstance(django_drf_filepond.api.storage_backend, MagicMock), ('The created storage backend should be mocked but it is not of ' 'type unittest.mock.MagicMock...')) # Set up a "StoredUpload" self.upload_id = _get_file_id() self.file_id = _get_file_id() # self.file_content = ('This is some test file data for an ' # 'uploaded file.') self.fn = 'my_test_file.txt' self.test_target_filepath = os.path.join('test_storage', self.fn) self.su = StoredUpload(upload_id=self.upload_id, file=self.test_target_filepath, uploaded=timezone.now(), stored=timezone.now()) self.su.save() def test_delete_stored_upload_local(self): # Make an API call to delete the stored upload and check that delete # is called on the FileSystemStorage backend. # Set the storage backend to None to force use of local storage. local_settings.STORAGES_BACKEND = None self.api.storage_backend_initialised = False # Get the target file path that we want to check that delete has been # called with. This is the file path from the DB plus the base file # system storage location. base_store_location = local_settings.FILE_STORE_PATH file_location = os.path.join(base_store_location, self.test_target_filepath) with patch('os.remove') as os_patcher: with patch('os.path.exists') as exists: with patch('os.path.isfile') as isfile: exists.return_value = True isfile.return_value = True self.delete_upload(self.upload_id, delete_file=True) with self.assertRaises(StoredUpload.DoesNotExist): StoredUpload.objects.get(upload_id=self.upload_id) os_patcher.assert_called_once_with(file_location) def test_delete_stored_upload_remote(self): self.mock_storage_backend.exists.return_value = True self.delete_upload(self.upload_id, delete_file=True) self.mock_storage_backend.delete.assert_called_once_with( self.test_target_filepath) def test_delete_stored_upload_local_keepfile(self): # Make an API call to delete the stored upload and check that delete # is called on the FileSystemStorage backend. # Set the storage backend to None to force use of local storage. local_settings.STORAGES_BACKEND = None self.api.storage_backend_initialised = False with patch('os.remove') as os_patcher: with patch('os.path.exists') as exists: with patch('os.path.isdir') as isdir: exists.return_value = True isdir.return_value = True self.delete_upload(self.upload_id, delete_file=False) with self.assertRaises(StoredUpload.DoesNotExist): StoredUpload.objects.get(upload_id=self.upload_id) os_patcher.assert_not_called() exists.assert_not_called() isdir.assert_not_called() def test_delete_stored_upload_remote_keepfile(self): self.delete_upload(self.upload_id, delete_file=False) self.mock_storage_backend.exists.assert_not_called() self.mock_storage_backend.delete.assert_not_called() def test_delete_stored_upload_invalid_id(self): test_id = 'abcdefghijklmnopqrstuv' ('No stored upload found with the specified ID [%s].' % (test_id)) with self.assertRaisesMessage( StoredUpload.DoesNotExist, 'StoredUpload matching query does not exist.'): self.delete_upload(test_id) def test_delete_stored_upload_local_no_filestore_path(self): fsp = local_settings.FILE_STORE_PATH local_settings.FILE_STORE_PATH = None # Need to set storage backend to None and make it reinitialise to # ensure that we're not using a remote backend for this test. local_settings.STORAGES_BACKEND = None self.api.storage_backend_initialised = False with self.assertRaisesMessage( ConfigurationError, 'The file upload settings are not configured correctly.'): self.delete_upload(self.upload_id, delete_file=True) local_settings.FILE_STORE_PATH = fsp def test_delete_stored_upload_local_not_exists(self): # Need to set storage backend to None and make it reinitialise to # ensure that we're not using a remote backend for this test. local_settings.STORAGES_BACKEND = None self.api.storage_backend_initialised = False with patch('os.path.exists') as exists: with patch('os.path.isdir') as isdir: exists.return_value = False isdir.return_value = True with self.assertRaisesMessage( ConfigurationError, ('The file upload settings are not configured ' 'correctly.')): self.delete_upload(self.upload_id, delete_file=True) def test_delete_stored_upload_local_not_isdir(self): # Need to set storage backend to None and make it reinitialise to # ensure that we're not using a remote backend for this test. local_settings.STORAGES_BACKEND = None self.api.storage_backend_initialised = False with patch('os.path.exists') as exists: with patch('os.path.isdir') as isdir: exists.return_value = True isdir.return_value = False with self.assertRaisesMessage( ConfigurationError, ('The file upload settings are not configured ' 'correctly.')): self.delete_upload(self.upload_id, delete_file=True) def test_delete_stored_upload_remote_file_missing(self): self.mock_storage_backend.exists.return_value = False with self.assertRaisesMessage( FileNotFoundError, ('File [%s] for stored upload with id [%s] not found on ' 'remote file store.' % (self.test_target_filepath, self.upload_id))): self.delete_upload(self.upload_id, delete_file=True) def test_delete_stored_upload_local_file_missing(self): local_settings.STORAGES_BACKEND = None self.api.storage_backend_initialised = False target_filepath = os.path.join(local_settings.FILE_STORE_PATH, self.test_target_filepath) with patch('os.path.exists') as exists: exists.side_effect = [True, False] with self.assertRaisesMessage( FileNotFoundError, ('File [%s] to delete was not found on the local disk' % (target_filepath))): self.delete_upload(self.upload_id, delete_file=True) def test_delete_stored_upload_local_remove_fails(self): # Make an API call to delete the stored upload and check that delete # is called on the FileSystemStorage backend. # Set the storage backend to None to force use of local storage. local_settings.STORAGES_BACKEND = None self.api.storage_backend_initialised = False # Get the target file path that we want to check that delete has been # called with. This is the file path from the DB plus the base file # system storage location. base_store_location = local_settings.FILE_STORE_PATH file_location = os.path.join(base_store_location, self.test_target_filepath) with patch('os.remove') as os_patcher: with patch('os.path.exists') as exists: with patch('os.path.isfile') as isfile: exists.return_value = True isfile.return_value = True os_patcher.side_effect = OSError('Error deleting file') with self.assertRaisesMessage(OSError, 'Error deleting file'): self.delete_upload(self.upload_id, delete_file=True) os_patcher.assert_called_once_with(file_location) def tearDown(self): local_settings.STORAGES_BACKEND = self.storage_backend self.su.delete() self.api.storage_backend_initialised = False