|
3 | 3 | from django.test import TestCase |
4 | 4 | from rest_framework.test import APITestCase |
5 | 5 | from datasets.models import Data |
6 | | -from factories import UserFactory, DataFactory |
| 6 | +from factories import ( |
| 7 | + UserFactory, |
| 8 | + DataFactory, |
| 9 | + CompetitionFactory, |
| 10 | + PhaseFactory, |
| 11 | + TaskFactory, |
| 12 | + SubmissionFactory |
| 13 | +) |
7 | 14 | from utils.data import pretty_bytes, gb_to_bytes |
8 | 15 | from unittest.mock import patch |
9 | 16 |
|
@@ -306,3 +313,87 @@ def test_cannot_create_dataset_unauthenticated(self): |
306 | 313 | 'file_size': 1234, |
307 | 314 | }) |
308 | 315 | self.assertEqual(resp.status_code, 403) |
| 316 | + |
| 317 | + |
| 318 | +class DatasetDeleteTests(APITestCase): |
| 319 | + def setUp(self): |
| 320 | + self.user = UserFactory(username='user', password='user') |
| 321 | + self.other_user = UserFactory(username='other', password='other') |
| 322 | + self.client.login(username='user', password='user') |
| 323 | + |
| 324 | + self.dataset1 = DataFactory(created_by=self.user, name='dataset1') |
| 325 | + self.dataset2 = DataFactory(created_by=self.user, name='dataset2') |
| 326 | + self.other_dataset = DataFactory(created_by=self.other_user, name='other_dataset') |
| 327 | + |
| 328 | + def test_delete_own_dataset_success(self): |
| 329 | + """User can delete their own dataset.""" |
| 330 | + url = reverse("data-detail", args=[self.dataset1.pk]) |
| 331 | + resp = self.client.delete(url) |
| 332 | + self.assertEqual(resp.status_code, 204) |
| 333 | + self.assertFalse(Data.objects.filter(pk=self.dataset1.pk).exists()) |
| 334 | + |
| 335 | + def test_cannot_delete_others_dataset(self): |
| 336 | + """User cannot delete someone else’s dataset.""" |
| 337 | + url = reverse("data-detail", args=[self.other_dataset.pk]) |
| 338 | + resp = self.client.delete(url) |
| 339 | + self.assertEqual(resp.status_code, 404) |
| 340 | + self.assertTrue(Data.objects.filter(pk=self.other_dataset.pk).exists()) |
| 341 | + |
| 342 | + def test_cannot_delete_dataset_in_use(self): |
| 343 | + """If dataset is in use by a competition, it cannot be deleted.""" |
| 344 | + # Set up in use dataset |
| 345 | + in_use_dataset = DataFactory(type=Data.INPUT_DATA, created_by=self.user, name="in_use_dataset") |
| 346 | + task = TaskFactory(input_data=in_use_dataset) |
| 347 | + phase = PhaseFactory() |
| 348 | + phase.tasks.add(task) |
| 349 | + competition = CompetitionFactory(created_by=self.user) |
| 350 | + competition.phases.set([phase]) |
| 351 | + |
| 352 | + url = reverse("data-detail", args=[in_use_dataset.pk]) |
| 353 | + resp = self.client.delete(url) |
| 354 | + |
| 355 | + self.assertEqual(resp.status_code, 400) |
| 356 | + self.assertEqual(resp.data["error"], "Cannot delete dataset: dataset is in use") |
| 357 | + self.assertTrue(Data.objects.filter(pk=in_use_dataset.pk).exists()) |
| 358 | + |
| 359 | + def test_bulk_delete_success(self): |
| 360 | + """Multiple datasets deleted successfully.""" |
| 361 | + ids = [self.dataset1.pk, self.dataset2.pk] |
| 362 | + resp = self.client.post(reverse("data-delete-many"), ids, format="json") |
| 363 | + self.assertEqual(resp.status_code, 200) |
| 364 | + self.assertEqual(resp.data["detail"], "Datasets deleted successfully") |
| 365 | + self.assertFalse(Data.objects.filter(pk__in=ids).exists()) |
| 366 | + |
| 367 | + def test_bulk_delete_with_errors(self): |
| 368 | + """Bulk delete should fail entirely if one dataset is not deletable.""" |
| 369 | + # include one dataset from another user |
| 370 | + ids = [self.dataset1.pk, self.other_dataset.pk] |
| 371 | + resp = self.client.post(reverse("data-delete-many"), ids, format="json") |
| 372 | + |
| 373 | + # Since one dataset is not deletable, expect a 400 response |
| 374 | + self.assertEqual(resp.status_code, 400) |
| 375 | + self.assertIn("other_dataset", resp.data) |
| 376 | + self.assertEqual(resp.data["other_dataset"], "Cannot delete a dataset that is not yours") |
| 377 | + |
| 378 | + # None should be deleted since the operation failed |
| 379 | + self.assertTrue(Data.objects.filter(pk=self.dataset1.pk).exists()) |
| 380 | + self.assertTrue(Data.objects.filter(pk=self.other_dataset.pk).exists()) |
| 381 | + |
| 382 | + def test_cannot_delete_dataset_associated_with_a_submission_in_competition(self): |
| 383 | + """If a dataset is a submission linked to a competition phase, it cannot be deleted.""" |
| 384 | + # Setup a submission dataset |
| 385 | + phase = PhaseFactory() |
| 386 | + competition = CompetitionFactory(created_by=self.user) |
| 387 | + competition.phases.set([phase]) |
| 388 | + submission_dataset = DataFactory(type=Data.SUBMISSION, created_by=self.user, name="submission_dataset") |
| 389 | + SubmissionFactory(owner=self.user, phase=phase, data=submission_dataset) |
| 390 | + |
| 391 | + url = reverse("data-detail", args=[submission_dataset.pk]) |
| 392 | + resp = self.client.delete(url) |
| 393 | + |
| 394 | + self.assertEqual(resp.status_code, 400) |
| 395 | + self.assertEqual( |
| 396 | + resp.data["error"], |
| 397 | + "Cannot delete submission: submission belongs to an existing competition. Please visit the competition and delete your submission from there." |
| 398 | + ) |
| 399 | + self.assertTrue(Data.objects.filter(pk=submission_dataset.pk).exists()) |
0 commit comments