Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 92 additions & 1 deletion src/apps/api/tests/test_datasets.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,14 @@
from django.test import TestCase
from rest_framework.test import APITestCase
from datasets.models import Data
from factories import UserFactory, DataFactory
from factories import (
UserFactory,
DataFactory,
CompetitionFactory,
PhaseFactory,
TaskFactory,
SubmissionFactory
)
from utils.data import pretty_bytes, gb_to_bytes
from unittest.mock import patch

Expand Down Expand Up @@ -306,3 +313,87 @@ def test_cannot_create_dataset_unauthenticated(self):
'file_size': 1234,
})
self.assertEqual(resp.status_code, 403)


class DatasetDeleteTests(APITestCase):
def setUp(self):
self.user = UserFactory(username='user', password='user')
self.other_user = UserFactory(username='other', password='other')
self.client.login(username='user', password='user')

self.dataset1 = DataFactory(created_by=self.user, name='dataset1')
self.dataset2 = DataFactory(created_by=self.user, name='dataset2')
self.other_dataset = DataFactory(created_by=self.other_user, name='other_dataset')

def test_delete_own_dataset_success(self):
"""User can delete their own dataset."""
url = reverse("data-detail", args=[self.dataset1.pk])
resp = self.client.delete(url)
self.assertEqual(resp.status_code, 204)
self.assertFalse(Data.objects.filter(pk=self.dataset1.pk).exists())

def test_cannot_delete_others_dataset(self):
"""User cannot delete someone else’s dataset."""
url = reverse("data-detail", args=[self.other_dataset.pk])
resp = self.client.delete(url)
self.assertEqual(resp.status_code, 404)
self.assertTrue(Data.objects.filter(pk=self.other_dataset.pk).exists())

def test_cannot_delete_dataset_in_use(self):
"""If dataset is in use by a competition, it cannot be deleted."""
# Set up in use dataset
in_use_dataset = DataFactory(type=Data.INPUT_DATA, created_by=self.user, name="in_use_dataset")
task = TaskFactory(input_data=in_use_dataset)
phase = PhaseFactory()
phase.tasks.add(task)
competition = CompetitionFactory(created_by=self.user)
competition.phases.set([phase])

url = reverse("data-detail", args=[in_use_dataset.pk])
resp = self.client.delete(url)

self.assertEqual(resp.status_code, 400)
self.assertEqual(resp.data["error"], "Cannot delete dataset: dataset is in use")
self.assertTrue(Data.objects.filter(pk=in_use_dataset.pk).exists())

def test_bulk_delete_success(self):
"""Multiple datasets deleted successfully."""
ids = [self.dataset1.pk, self.dataset2.pk]
resp = self.client.post(reverse("data-delete-many"), ids, format="json")
self.assertEqual(resp.status_code, 200)
self.assertEqual(resp.data["detail"], "Datasets deleted successfully")
self.assertFalse(Data.objects.filter(pk__in=ids).exists())

def test_bulk_delete_with_errors(self):
"""Bulk delete should fail entirely if one dataset is not deletable."""
# include one dataset from another user
ids = [self.dataset1.pk, self.other_dataset.pk]
resp = self.client.post(reverse("data-delete-many"), ids, format="json")

# Since one dataset is not deletable, expect a 400 response
self.assertEqual(resp.status_code, 400)
self.assertIn("other_dataset", resp.data)
self.assertEqual(resp.data["other_dataset"], "Cannot delete a dataset that is not yours")

# None should be deleted since the operation failed
self.assertTrue(Data.objects.filter(pk=self.dataset1.pk).exists())
self.assertTrue(Data.objects.filter(pk=self.other_dataset.pk).exists())

def test_cannot_delete_dataset_associated_with_a_submission_in_competition(self):
"""If a dataset is a submission linked to a competition phase, it cannot be deleted."""
# Setup a submission dataset
phase = PhaseFactory()
competition = CompetitionFactory(created_by=self.user)
competition.phases.set([phase])
submission_dataset = DataFactory(type=Data.SUBMISSION, created_by=self.user, name="submission_dataset")
SubmissionFactory(owner=self.user, phase=phase, data=submission_dataset)

url = reverse("data-detail", args=[submission_dataset.pk])
resp = self.client.delete(url)

self.assertEqual(resp.status_code, 400)
self.assertEqual(
resp.data["error"],
"Cannot delete submission: submission belongs to an existing competition. Please visit the competition and delete your submission from there."
)
self.assertTrue(Data.objects.filter(pk=submission_dataset.pk).exists())
8 changes: 2 additions & 6 deletions src/apps/api/views/datasets.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,17 +144,13 @@ def create(self, request, *args, **kwargs):
return Response(context, status=status.HTTP_201_CREATED, headers=headers)

def destroy(self, request, *args, **kwargs):
# TODO: Confirm this has a test
instance = self.get_object()

error = self.check_delete_permissions(request, instance)

dataset = self.get_object()
error = self.check_delete_permissions(request, dataset)
if error:
return Response(
{'error': error},
status=status.HTTP_400_BAD_REQUEST
)

return super().destroy(request, *args, **kwargs)

@action(detail=False, methods=('POST',))
Expand Down