import base64
|
from unittest.mock import MagicMock
|
|
import pytest
|
from django.urls import reverse
|
from io_storages.proxy_api import ProjectResolveStorageUri, TaskResolveStorageUri
|
from projects.models import Project
|
from rest_framework import status
|
from rest_framework.test import APIRequestFactory, force_authenticate
|
from tasks.models import Task
|
from users.models import User
|
|
|
@pytest.mark.django_db
|
class TestTaskResolveStorageUri:
|
@pytest.fixture
|
def view(self):
|
view = TaskResolveStorageUri.as_view()
|
view.authentication_classes = []
|
view.permission_classes = []
|
return view
|
|
@pytest.fixture
|
def project(self):
|
project = Project(pk=1, title='testproject')
|
project.has_permission = MagicMock()
|
project.get_all_import_storage_objects = []
|
return project
|
|
@pytest.fixture
|
def task(self, project):
|
task = Task(pk=1, data={}, project=project)
|
task.resolve_storage_uri = MagicMock()
|
task.has_permission = MagicMock()
|
return task
|
|
@pytest.fixture
|
def user(self):
|
user = User.objects.create_user(username='testuser', email='testuser@email.com', password='testpassword')
|
return user
|
|
def test_missing_parameters(self, view, user):
|
request = APIRequestFactory().get(reverse('storages:task-storage-data-resolve', kwargs={'task_id': 1}))
|
|
request.user = user
|
force_authenticate(request, user)
|
response = view(request)
|
|
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
|
def test_task_not_found(self, view, user):
|
# Test case where task doesn't exist in database
|
request = APIRequestFactory().get(
|
reverse('storages:task-storage-data-resolve', kwargs={'task_id': 2}) + '?fileuri=fileuri'
|
)
|
request.user = user
|
force_authenticate(request, user)
|
response = view(request, task_id=2)
|
|
assert response.status_code == status.HTTP_404_NOT_FOUND
|
|
def test_task_resolve_returns_none(self, view, task, project, user, monkeypatch):
|
# Test case where task exists but resolve_storage_uri returns None
|
task.resolve_storage_uri.return_value = None
|
task.has_permission.return_value = True
|
task.project = project
|
|
def mock_task_get(*args, **kwargs):
|
if kwargs['pk'] == 1:
|
return task
|
else:
|
raise Task.DoesNotExist
|
|
obj = MagicMock()
|
obj.get = mock_task_get
|
monkeypatch.setattr('tasks.models.Task.objects', obj)
|
|
# Add a mock storage that will match the URI
|
mock_storage = MagicMock()
|
mock_storage.presign = True
|
|
def mock_can_resolve(url):
|
return True # Match any URL
|
|
mock_storage.can_resolve_url = MagicMock(side_effect=mock_can_resolve)
|
project.get_all_import_storage_objects = [mock_storage]
|
|
request = APIRequestFactory().get(
|
reverse('storages:task-storage-data-resolve', kwargs={'task_id': 1}) + '?fileuri=fileuri'
|
)
|
request.user = user
|
force_authenticate(request, user)
|
response = view(request, task_id=1)
|
|
assert response.status_code == status.HTTP_404_NOT_FOUND
|
|
def test_storage_resolution_fails(self, view, task, project, user, monkeypatch):
|
task.resolve_storage_uri.return_value = None
|
task.has_permission.return_value = True
|
project.get_all_import_storage_objects = []
|
task.project = project
|
|
def mock_task_get(*args, **kwargs):
|
if kwargs['pk'] == 1:
|
return task
|
else:
|
raise Task.DoesNotExist
|
|
obj = MagicMock()
|
obj.get = mock_task_get
|
monkeypatch.setattr('tasks.models.Task.objects', obj)
|
|
encoded_fileuri = base64.urlsafe_b64encode(b's3://valid/uri').decode()
|
request = APIRequestFactory().get(
|
reverse('storages:task-storage-data-resolve', kwargs={'task_id': 1}) + f'?fileuri={encoded_fileuri}'
|
)
|
request.user = user
|
force_authenticate(request, user)
|
response = view(request, task_id=1)
|
|
assert response.status_code == status.HTTP_404_NOT_FOUND
|
task.resolve_storage_uri.assert_not_called()
|
|
def test_file_uri_not_hashed(self, view, task, project, user, monkeypatch):
|
task.has_permission.return_value = True
|
project.get_all_import_storage_objects = []
|
task.project = project
|
|
def mock_task_get(*args, **kwargs):
|
if kwargs['pk'] == 1:
|
return task
|
else:
|
raise Task.DoesNotExist
|
|
obj = MagicMock()
|
obj.get = mock_task_get
|
monkeypatch.setattr('tasks.models.Task.objects', obj)
|
|
request = APIRequestFactory().get(
|
reverse('storages:task-storage-data-resolve', kwargs={'task_id': 1}) + '?fileuri=fileuri'
|
)
|
request.user = user
|
force_authenticate(request, user)
|
|
response = view(request, task_id=1)
|
|
assert response.status_code == status.HTTP_404_NOT_FOUND
|
task.resolve_storage_uri.assert_not_called()
|
|
def test_successful_request(self, view, task, project, user, monkeypatch):
|
valid_decoded_uri = "s3://hypertext-bucket/file with /spaces and' / ' / quotes.jpg"
|
encoded_fileuri = base64.urlsafe_b64encode(valid_decoded_uri.encode()).decode()
|
|
mock_storage = MagicMock()
|
mock_storage.presign = True
|
|
def mock_can_resolve(url):
|
return url == valid_decoded_uri
|
|
mock_storage.can_resolve_url = MagicMock(side_effect=mock_can_resolve)
|
|
project.get_all_import_storage_objects = [mock_storage]
|
task.project = project
|
|
task.resolve_storage_uri.return_value = dict(
|
url='https://presigned-url.com/fileuri',
|
presign_ttl=3600,
|
)
|
task.has_permission.return_value = True
|
|
def mock_task_get(*args, **kwargs):
|
if kwargs['pk'] == 1:
|
return task
|
else:
|
raise Task.DoesNotExist
|
|
obj = MagicMock()
|
obj.get = mock_task_get
|
monkeypatch.setattr('tasks.models.Task.objects', obj)
|
|
request = APIRequestFactory().get(
|
reverse('storages:task-storage-data-resolve', kwargs={'task_id': 1}) + f'?fileuri={encoded_fileuri}'
|
)
|
request.user = user
|
force_authenticate(request, user)
|
|
response = view(request, task_id=1)
|
|
assert response.status_code == status.HTTP_303_SEE_OTHER
|
assert response.url == 'https://presigned-url.com/fileuri'
|
mock_storage.can_resolve_url.assert_called_with(valid_decoded_uri)
|
task.resolve_storage_uri.assert_called_once_with(valid_decoded_uri)
|
|
def test_successful_request_with_long_fileuri(self, view, task, project, user, monkeypatch):
|
longest_allowable_cloud_storage_path = 'is/a/long/path/that/needs/to/be/1024/characters/long/so/that/it/gets/hashedis/a/long/path/that/needs/to/be/1024/characters/long/so/that/it/gets/hashedis/a/long/path/that/needs/to/be/1024/characters/long/so/that/it/gets/hashedis/a/long/path/that/needs/to/be/1024/characters/long/so/that/it/gets/hashedis/a/long/path/that/needs/to/be/1024/characters/long/so/that/it/gets/hashedis/a/long/path/that/needs/to/be/1024/characters/long/so/that/it/gets/hashedis/a/long/path/that/needs/to/be/1024/characters/long/so/that/it/gets/hashedis/a/long/path/that/needs/to/be/1024/characters/long/so/that/it/gets/hashedis/a/long/path/that/needs/to/be/1024/characters/long/so/that/it/gets/hashedis/a/long/path/that/needs/to/be/1024/characters/long/so/that/it/gets/hashedis/a/long/path/that/needs/to/be/1024/characters/long/so/that/it/gets/hashedis/a/long/path/that/needs/to/be/1024/characters/long/so/that/it/gets/hashedis/a/long/path/that/needs/to/be/1024/characters/long/so/that/it/gets/hashedis/long/path/that/needs/to/be/1024/characters.png'
|
longest_uri = f'aaaaa-bbbb://{longest_allowable_cloud_storage_path}'
|
base64_encoded_uri = base64.urlsafe_b64encode(longest_uri.encode()).decode()
|
|
mock_storage = MagicMock()
|
mock_storage.presign = True
|
|
def mock_can_resolve(url):
|
return url == longest_uri
|
|
mock_storage.can_resolve_url = MagicMock(side_effect=mock_can_resolve)
|
|
project.get_all_import_storage_objects = [mock_storage]
|
task.project = project
|
|
task.resolve_storage_uri.return_value = dict(
|
url='https://presigned-url.com/fileuri',
|
presign_ttl=3600,
|
)
|
task.has_permission.return_value = True
|
|
def mock_task_get(*args, **kwargs):
|
if kwargs['pk'] == 1:
|
return task
|
else:
|
raise Task.DoesNotExist
|
|
obj = MagicMock()
|
obj.get = mock_task_get
|
monkeypatch.setattr('tasks.models.Task.objects', obj)
|
|
longest_allowable_url_length = 2000
|
largest_allowable_task_key = 9223372036854775807
|
longest_resolve_path = f'/tasks/{largest_allowable_task_key}/resolve/?fileuri='
|
scheme_length = len('https://')
|
longest_resolve_path_length = len(longest_resolve_path)
|
longest_allowable_fileuri_hash_length = len(base64_encoded_uri)
|
remaining_url_origin_length = (
|
longest_allowable_url_length
|
- scheme_length
|
- longest_resolve_path_length
|
- longest_allowable_fileuri_hash_length
|
)
|
assert remaining_url_origin_length >= 512
|
|
request = APIRequestFactory().get(
|
reverse('storages:task-storage-data-resolve', kwargs={'task_id': 1}) + f'?fileuri={base64_encoded_uri}'
|
)
|
request.user = user
|
force_authenticate(request, user)
|
response = view(request, task_id=1)
|
|
assert response.status_code == status.HTTP_303_SEE_OTHER
|
assert response.url == 'https://presigned-url.com/fileuri'
|
mock_storage.can_resolve_url.assert_called_with(longest_uri)
|
task.resolve_storage_uri.assert_called_once_with(longest_uri)
|
|
|
@pytest.mark.django_db
|
class TestProjectResolveStorageUri:
|
@pytest.fixture
|
def view(self):
|
view = ProjectResolveStorageUri.as_view()
|
view.authentication_classes = []
|
view.permission_classes = []
|
return view
|
|
@pytest.fixture
|
def project(self):
|
project = Project(pk=1, title='testproject')
|
project.resolve_storage_uri = MagicMock()
|
project.has_permission = MagicMock()
|
project.get_all_import_storage_objects = []
|
return project
|
|
@pytest.fixture
|
def user(self):
|
user = User.objects.create_user(username='testuser', email='testuser@email.com', password='testpassword')
|
return user
|
|
def test_missing_parameters(self, view, user):
|
request = APIRequestFactory().get(reverse('storages:project-storage-data-resolve', kwargs={'project_id': 1}))
|
|
request.user = user
|
force_authenticate(request, user)
|
response = view(request)
|
|
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
|
def test_project_not_found(self, view, user):
|
# Test case where project doesn't exist in database
|
request = APIRequestFactory().get(
|
reverse('storages:project-storage-data-resolve', kwargs={'project_id': 2}) + '?fileuri=fileuri'
|
)
|
request.user = user
|
force_authenticate(request, user)
|
response = view(request, project_id=2)
|
|
assert response.status_code == status.HTTP_404_NOT_FOUND
|
|
def test_project_resolve_returns_none(self, view, project, user, monkeypatch):
|
# Test case where project exists but resolve_storage_uri returns None
|
project.resolve_storage_uri.return_value = None
|
project.has_permission.return_value = True
|
|
def mock_project_get(*args, **kwargs):
|
if kwargs['pk'] == 1:
|
return project
|
else:
|
raise Project.DoesNotExist
|
|
obj = MagicMock()
|
obj.get = mock_project_get
|
monkeypatch.setattr('projects.models.Project.objects', obj)
|
|
# Add a mock storage that will match the URI
|
mock_storage = MagicMock()
|
mock_storage.presign = True
|
|
def mock_can_resolve(url):
|
return True # Match any URL
|
|
mock_storage.can_resolve_url = MagicMock(side_effect=mock_can_resolve)
|
project.get_all_import_storage_objects = [mock_storage]
|
|
request = APIRequestFactory().get(
|
reverse('storages:project-storage-data-resolve', kwargs={'project_id': 1}) + '?fileuri=fileuri'
|
)
|
request.user = user
|
force_authenticate(request, user)
|
response = view(request, project_id=1)
|
|
assert response.status_code == status.HTTP_404_NOT_FOUND
|
|
def test_storage_resolution_fails(self, view, project, user, monkeypatch):
|
project.resolve_storage_uri.return_value = None
|
project.has_permission.return_value = True
|
|
def mock_project_get(*args, **kwargs):
|
if kwargs['pk'] == 1:
|
return project
|
else:
|
raise Project.DoesNotExist
|
|
obj = MagicMock()
|
obj.get = mock_project_get
|
monkeypatch.setattr('projects.models.Project.objects', obj)
|
|
encoded_fileuri = base64.urlsafe_b64encode(b's3://valid/uri').decode()
|
request = APIRequestFactory().get(
|
reverse('storages:project-storage-data-resolve', kwargs={'project_id': 1}) + f'?fileuri={encoded_fileuri}'
|
)
|
request.user = user
|
force_authenticate(request, user)
|
response = view(request, project_id=1)
|
|
assert response.status_code == status.HTTP_404_NOT_FOUND
|
project.resolve_storage_uri.assert_not_called()
|
|
def test_file_uri_not_hashed(self, view, project, user, monkeypatch):
|
project.has_permission.return_value = True
|
|
def mock_project_get(*args, **kwargs):
|
if kwargs['pk'] == 1:
|
return project
|
else:
|
raise Project.DoesNotExist
|
|
obj = MagicMock()
|
obj.get = mock_project_get
|
monkeypatch.setattr('projects.models.Project.objects', obj)
|
|
request = APIRequestFactory().get(
|
reverse('storages:project-storage-data-resolve', kwargs={'project_id': 1}) + '?fileuri=fileuri'
|
)
|
request.user = user
|
force_authenticate(request, user)
|
|
response = view(request, project_id=1)
|
|
assert response.status_code == status.HTTP_404_NOT_FOUND
|
project.resolve_storage_uri.assert_not_called()
|
|
def test_successful_request(self, view, project, user, monkeypatch):
|
valid_decoded_uri = "s3://hypertext-bucket/file with /spaces and' / ' / quotes.jpg"
|
encoded_fileuri = base64.urlsafe_b64encode(valid_decoded_uri.encode()).decode()
|
|
mock_storage = MagicMock()
|
mock_storage.presign = True
|
|
def mock_can_resolve(url):
|
return url == valid_decoded_uri
|
|
mock_storage.can_resolve_url = MagicMock(side_effect=mock_can_resolve)
|
|
project.get_all_import_storage_objects = [mock_storage]
|
|
project.resolve_storage_uri.return_value = dict(
|
url='https://presigned-url.com/fileuri',
|
presign_ttl=3600,
|
)
|
project.has_permission.return_value = True
|
|
def mock_project_get(*args, **kwargs):
|
if kwargs['pk'] == 1:
|
return project
|
else:
|
raise Project.DoesNotExist
|
|
obj = MagicMock()
|
obj.get = mock_project_get
|
monkeypatch.setattr('projects.models.Project.objects', obj)
|
|
request = APIRequestFactory().get(
|
reverse('storages:project-storage-data-resolve', kwargs={'project_id': 1}) + f'?fileuri={encoded_fileuri}'
|
)
|
request.user = user
|
force_authenticate(request, user)
|
response = view(request, project_id=1)
|
|
assert response.status_code == status.HTTP_303_SEE_OTHER
|
assert response.url == 'https://presigned-url.com/fileuri'
|
mock_storage.can_resolve_url.assert_called_with(valid_decoded_uri)
|
project.resolve_storage_uri.assert_called_once_with(valid_decoded_uri)
|
|
def test_successful_request_with_long_fileuri(self, view, project, user, monkeypatch):
|
longest_allowable_cloud_storage_path = 'is/a/long/path/that/needs/to/be/1024/characters/long/so/that/it/gets/hashedis/a/long/path/that/needs/to/be/1024/characters/long/so/that/it/gets/hashedis/a/long/path/that/needs/to/be/1024/characters/long/so/that/it/gets/hashedis/a/long/path/that/needs/to/be/1024/characters/long/so/that/it/gets/hashedis/a/long/path/that/needs/to/be/1024/characters/long/so/that/it/gets/hashedis/a/long/path/that/needs/to/be/1024/characters/long/so/that/it/gets/hashedis/a/long/path/that/needs/to/be/1024/characters/long/so/that/it/gets/hashedis/a/long/path/that/needs/to/be/1024/characters/long/so/that/it/gets/hashedis/a/long/path/that/needs/to/be/1024/characters/long/so/that/it/gets/hashedis/a/long/path/that/needs/to/be/1024/characters/long/so/that/it/gets/hashedis/a/long/path/that/needs/to/be/1024/characters/long/so/that/it/gets/hashedis/a/long/path/that/needs/to/be/1024/characters/long/so/that/it/gets/hashedis/a/long/path/that/needs/to/be/1024/characters/long/so/that/it/gets/hashedis/long/path/that/needs/to/be/1024/characters.png'
|
longest_uri = f'aaaaa-bbbb://{longest_allowable_cloud_storage_path}'
|
base64_encoded_uri = base64.urlsafe_b64encode(longest_uri.encode()).decode()
|
|
mock_storage = MagicMock()
|
mock_storage.presign = True
|
|
def mock_can_resolve(url):
|
return url == longest_uri
|
|
mock_storage.can_resolve_url = MagicMock(side_effect=mock_can_resolve)
|
|
project.get_all_import_storage_objects = [mock_storage]
|
|
project.resolve_storage_uri.return_value = dict(
|
url='https://presigned-url.com/fileuri',
|
presign_ttl=3600,
|
)
|
project.has_permission.return_value = True
|
|
def mock_project_get(*args, **kwargs):
|
if kwargs['pk'] == 1:
|
return project
|
else:
|
raise Project.DoesNotExist
|
|
obj = MagicMock()
|
obj.get = mock_project_get
|
monkeypatch.setattr('projects.models.Project.objects', obj)
|
|
longest_allowable_url_length = 2000
|
largest_allowable_project_key = 9223372036854775807
|
longest_resolve_path = f'/projects/{largest_allowable_project_key}/resolve/?fileuri='
|
scheme_length = len('https://')
|
longest_resolve_path_length = len(longest_resolve_path)
|
longest_allowable_fileuri_hash_length = len(base64_encoded_uri)
|
remaining_url_origin_length = (
|
longest_allowable_url_length
|
- scheme_length
|
- longest_resolve_path_length
|
- longest_allowable_fileuri_hash_length
|
)
|
assert remaining_url_origin_length >= 512
|
|
request = APIRequestFactory().get(
|
reverse('storages:project-storage-data-resolve', kwargs={'project_id': 1})
|
+ f'?fileuri={base64_encoded_uri}'
|
)
|
request.user = user
|
force_authenticate(request, user)
|
response = view(request, project_id=1)
|
|
assert response.status_code == status.HTTP_303_SEE_OTHER
|
assert response.url == 'https://presigned-url.com/fileuri'
|
mock_storage.can_resolve_url.assert_called_with(longest_uri)
|
project.resolve_storage_uri.assert_called_once_with(longest_uri)
|