blob: f66a3574c3f76ee2262d1741feec07b929d1a668 [file] [log] [blame]
"""Tests for the logfiles REST API."""
import os
import shutil
import tempfile
import zipfile
from django.conf import settings
from django.core.files.storage import default_storage
from django.db import connection
from django.test import override_settings
from django.urls import reverse
from rest_framework import status
from crashreports.models import (
crashreport_file_name,
Device,
Crashreport,
LogFile,
)
from crashreports.tests.utils import (
Dummy,
RaceConditionsTestCase,
HiccupCrashreportsAPITestCase,
)
LIST_CREATE_URL = "api_v1_crashreports"
PUT_LOGFILE_URL = "api_v1_putlogfile_for_device_id"
@override_settings(MEDIA_ROOT=tempfile.mkdtemp(".hiccup-tests"))
class LogfileUploadTest(HiccupCrashreportsAPITestCase):
"""Test cases for upload of log files."""
# pylint: disable=too-many-ancestors
LIST_CREATE_URL = "api_v1_crashreports"
PUT_LOGFILE_URL = "api_v1_putlogfile_for_device_id"
POST_LOGFILE_URL = "api_v1_logfiles_by_id"
def setUp(self):
"""Call the super setup method and register a device."""
super().setUp()
self.device_uuid, self.user, _ = self._register_device()
def upload_crashreport(self, user, uuid):
"""
Upload dummy crashreport data.
Args:
user: The user which should be used for uploading the report
uuid: The uuid of the device to which the report should be uploaded
Returns: The local id of the device for which the report was uploaded.
"""
data = Dummy.crashreport_data(uuid=uuid)
response = user.post(reverse(LIST_CREATE_URL), data)
self.assertEqual(status.HTTP_201_CREATED, response.status_code)
self.assertTrue("device_local_id" in response.data)
device_local_id = response.data["device_local_id"]
return device_local_id
def _assert_zip_file_contents_equal(self, file1, file2):
"""Assert that the files within two zip files are equal."""
zip_file_1 = zipfile.ZipFile(file1)
zip_file_2 = zipfile.ZipFile(file2)
for file_name_1, file_name_2 in zip(
zip_file_1.filelist, zip_file_2.filelist
):
file_1 = zip_file_1.open(file_name_1)
file_2 = zip_file_2.open(file_name_2)
self.assertEqual(file_1.read(), file_2.read())
def upload_logfile(self, client, uuid, device_local_id):
"""Upload a log file and assert that it was created."""
logfile = open(Dummy.DEFAULT_LOG_FILE_PATHS[0], "rb")
logfile_name = os.path.basename(logfile.name)
response = client.post(
reverse(
PUT_LOGFILE_URL, args=[uuid, device_local_id, logfile_name]
),
{"file": logfile},
format="multipart",
)
logfile.close()
self.assertEqual(status.HTTP_201_CREATED, response.status_code)
return response
def _test_logfile_upload(self, user, uuid):
# Upload crashreport
device_local_id = self.upload_crashreport(user, uuid)
# Upload a logfile for the crashreport
self.upload_logfile(user, uuid, device_local_id)
logfile_instance = (
Device.objects.get(uuid=uuid)
.crashreports.get(device_local_id=device_local_id)
.logfiles.last()
)
uploaded_logfile_path = crashreport_file_name(
logfile_instance, os.path.basename(Dummy.DEFAULT_LOG_FILE_PATHS[0])
)
self.assertTrue(default_storage.exists(uploaded_logfile_path))
# The files are not 100% equal, because the server adds some extra
# bytes. However, we mainly care that the contents are equal:
self._assert_zip_file_contents_equal(
default_storage.path(uploaded_logfile_path),
Dummy.DEFAULT_LOG_FILE_PATHS[0],
)
def test_logfile_upload_as_user(self):
"""Test upload of logfiles as device owner."""
self._test_logfile_upload(self.user, self.device_uuid)
def test_logfile_upload_as_fp_staff(self):
"""Test upload of logfiles as Fairphone staff user."""
self._test_logfile_upload(self.fp_staff_client, self.device_uuid)
def test_logfile_deletion(self):
"""Test deletion of logfile instances."""
# Create a user, device and crashreport with logfile
device = Dummy.create_device(Dummy.create_user())
crashreport = Dummy.create_report(Crashreport, device)
logfile, logfile_path = Dummy.create_log_file_with_actual_file(
crashreport
)
# Assert that the crashreport and logfile have been created
self.assertEqual(Crashreport.objects.count(), 1)
self.assertEqual(LogFile.objects.count(), 1)
self.assertTrue(os.path.isfile(logfile_path))
# Delete the logfile
response = self.fp_staff_client.delete(
reverse(self.POST_LOGFILE_URL, args=[logfile.id])
)
self.assertEqual(status.HTTP_204_NO_CONTENT, response.status_code)
# Assert that the logfile has been deleted
self.assertEqual(LogFile.objects.count(), 0)
self.assertFalse(os.path.isfile(logfile_path))
def tearDown(self):
"""Remove the file and directories that were created for the test."""
shutil.rmtree(settings.MEDIA_ROOT)
class LogfileRaceConditionsTestCase(RaceConditionsTestCase):
"""Test cases for logfile race conditions."""
def test_create_multiple_logfiles(self):
"""Test that no race condition occurs when creating logfiles."""
uuid, user, _ = self._register_device()
device_local_id = LogfileUploadTest.upload_crashreport(self, user, uuid)
def upload_logfile(client, uuid, device_local_id):
LogfileUploadTest.upload_logfile(
self, client, uuid, device_local_id
)
connection.close()
argslist = [[user, uuid, device_local_id] for _ in range(10)]
self._test_create_multiple(
LogFile, upload_logfile, argslist, "crashreport_local_id"
)