mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-08-26 12:46:08 +00:00
Backup module: added some unittests; minor doc updates
- added unittests - introduced function backups.get_location_path() - minor docstring / comment and naming updates Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
This commit is contained in:
parent
7da361bbca
commit
d8fc2057d4
@ -18,7 +18,6 @@
|
|||||||
FreedomBox app to manage backup archives.
|
FreedomBox app to manage backup archives.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import errno
|
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
|
||||||
@ -26,6 +25,7 @@ from django.utils.text import get_valid_filename
|
|||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from plinth import actions
|
from plinth import actions
|
||||||
|
from plinth.errors import PlinthError
|
||||||
from plinth.menu import main_menu
|
from plinth.menu import main_menu
|
||||||
from plinth.modules import storage
|
from plinth.modules import storage
|
||||||
|
|
||||||
@ -127,6 +127,16 @@ def get_export_locations():
|
|||||||
return locations
|
return locations
|
||||||
|
|
||||||
|
|
||||||
|
def get_location_path(label, locations=None):
|
||||||
|
"""Returns the location path given a disk label"""
|
||||||
|
if locations is None:
|
||||||
|
locations = get_export_locations()
|
||||||
|
for (location_path, location_label) in locations:
|
||||||
|
if location_label == label:
|
||||||
|
return location_path
|
||||||
|
raise PlinthError("Could not find path of location %s" % label)
|
||||||
|
|
||||||
|
|
||||||
def get_export_files():
|
def get_export_files():
|
||||||
"""Return a dict of exported backup archives found in storage locations."""
|
"""Return a dict of exported backup archives found in storage locations."""
|
||||||
locations = get_export_locations()
|
locations = get_export_locations()
|
||||||
@ -145,14 +155,8 @@ def get_archive_path(location, archive_name):
|
|||||||
|
|
||||||
def find_exported_archive(disk_label, archive_name):
|
def find_exported_archive(disk_label, archive_name):
|
||||||
"""Return the full path for the exported archive file."""
|
"""Return the full path for the exported archive file."""
|
||||||
locations = get_export_locations()
|
location_path = get_location_path(disk_label)
|
||||||
for (location_path, location_name) in locations:
|
return get_archive_path(location_path, archive_name)
|
||||||
if location_name == disk_label:
|
|
||||||
return get_archive_path(location_path, archive_name)
|
|
||||||
|
|
||||||
|
|
||||||
raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT),
|
|
||||||
archive_name)
|
|
||||||
|
|
||||||
|
|
||||||
def get_export_apps(filename):
|
def get_export_apps(filename):
|
||||||
|
|||||||
@ -26,7 +26,7 @@ from django.core.validators import FileExtensionValidator
|
|||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from . import backups as backups_api
|
from . import backups as backups_api
|
||||||
from . import get_export_locations, get_archive_path
|
from . import get_export_locations, get_archive_path, get_location_path
|
||||||
|
|
||||||
|
|
||||||
class CreateArchiveForm(forms.Form):
|
class CreateArchiveForm(forms.Form):
|
||||||
@ -93,16 +93,21 @@ class UploadForm(forms.Form):
|
|||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
"""Initialize the form with location choices."""
|
"""Initialize the form with location choices."""
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
# TODO: write a test that assures the format of get_export_locations
|
locations = get_export_locations()
|
||||||
self.fields['location'].choices = get_export_locations()
|
# users should only be able to select a location name -- don't
|
||||||
|
# provide paths as a form input for security reasons
|
||||||
|
location_labels = [(location[1], location[1]) for location in locations]
|
||||||
|
self.fields['location'].choices = location_labels
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
"""Check that the uploaded file does not exist"""
|
"""Check that the uploaded file does not yet exist."""
|
||||||
cleaned_data = super().clean()
|
cleaned_data = super().clean()
|
||||||
file = cleaned_data.get('file')
|
file = cleaned_data.get('file')
|
||||||
location = cleaned_data.get('location')
|
location_label = cleaned_data.get('location')
|
||||||
|
location_path = get_location_path(location_label)
|
||||||
|
# if other errors occured before, 'file' won't be in cleaned_data
|
||||||
if (file and file.name):
|
if (file and file.name):
|
||||||
filepath = get_archive_path(location, file.name)
|
filepath = get_archive_path(location_path, file.name)
|
||||||
if os.path.exists(filepath):
|
if os.path.exists(filepath):
|
||||||
raise forms.ValidationError("File %s already exists" % file.name)
|
raise forms.ValidationError("File %s already exists" % file.name)
|
||||||
else:
|
else:
|
||||||
|
|||||||
@ -19,13 +19,18 @@ Tests for backups module.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import collections
|
import collections
|
||||||
|
from django.core.files.uploadedfile import SimpleUploadedFile
|
||||||
import unittest
|
import unittest
|
||||||
from unittest.mock import call, patch, MagicMock
|
from unittest.mock import call, patch, MagicMock
|
||||||
|
|
||||||
|
|
||||||
|
from plinth.errors import PlinthError
|
||||||
from plinth.module_loader import load_modules
|
from plinth.module_loader import load_modules
|
||||||
from ..backups import validate, Packet, backup_apps, restore_apps, \
|
from ..backups import validate, Packet, backup_apps, restore_apps, \
|
||||||
get_all_apps_for_backup, _get_apps_in_order, _get_manifests, \
|
get_all_apps_for_backup, _get_apps_in_order, _get_manifests, \
|
||||||
_lockdown_apps, _shutdown_services, _restore_services
|
_lockdown_apps, _shutdown_services, _restore_services
|
||||||
|
from .. import get_export_locations, get_location_path
|
||||||
|
from ..forms import UploadForm
|
||||||
|
|
||||||
|
|
||||||
def _get_test_manifest(name):
|
def _get_test_manifest(name):
|
||||||
@ -46,8 +51,8 @@ def _get_test_manifest(name):
|
|||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
class TestBackups(unittest.TestCase):
|
class TestBackupProcesses(unittest.TestCase):
|
||||||
"""Test cases for backups module."""
|
"""Test cases for backup processes"""
|
||||||
|
|
||||||
def test_packet_process_manifests(self):
|
def test_packet_process_manifests(self):
|
||||||
"""Test that directories/files are collected from manifests."""
|
"""Test that directories/files are collected from manifests."""
|
||||||
@ -83,6 +88,12 @@ class TestBackups(unittest.TestCase):
|
|||||||
assert isinstance(apps, list)
|
assert isinstance(apps, list)
|
||||||
# apps may be empty, if no apps supporting backup are installed.
|
# apps may be empty, if no apps supporting backup are installed.
|
||||||
|
|
||||||
|
def test_export_locations(self):
|
||||||
|
"""Check get_export_locations returns a list of tuples of length 2."""
|
||||||
|
locations = get_export_locations()
|
||||||
|
assert(len(locations))
|
||||||
|
assert(len(locations[0]) == 2)
|
||||||
|
|
||||||
def test__get_apps_in_order(self):
|
def test__get_apps_in_order(self):
|
||||||
"""Test that apps are listed in correct dependency order."""
|
"""Test that apps are listed in correct dependency order."""
|
||||||
load_modules()
|
load_modules()
|
||||||
@ -148,3 +159,34 @@ class TestBackups(unittest.TestCase):
|
|||||||
'app_name': 'b', 'app': None, 'was_running': False}
|
'app_name': 'b', 'app': None, 'was_running': False}
|
||||||
_restore_services(original_state)
|
_restore_services(original_state)
|
||||||
run.assert_called_once_with('service', ['start', 'a'])
|
run.assert_called_once_with('service', ['start', 'a'])
|
||||||
|
|
||||||
|
|
||||||
|
class TestBackupModule(unittest.TestCase):
|
||||||
|
"""Tests of the backups django module, like views or forms."""
|
||||||
|
|
||||||
|
def test_get_location_path(self):
|
||||||
|
"""Test the 'get_location_path' method"""
|
||||||
|
locations = [('/var/www', 'dummy location'), ('/etc', 'dangerous')]
|
||||||
|
location = get_location_path('dummy location', locations)
|
||||||
|
self.assertEquals(location, locations[0][0])
|
||||||
|
# verify that an unknown location raises an error
|
||||||
|
with self.assertRaises(PlinthError):
|
||||||
|
get_location_path('unknown location', locations)
|
||||||
|
|
||||||
|
def test_file_upload(self):
|
||||||
|
locations = get_export_locations()
|
||||||
|
location_name = locations[0][1]
|
||||||
|
post_data = {'location': location_name}
|
||||||
|
|
||||||
|
# posting a video should fail
|
||||||
|
video_file = SimpleUploadedFile("video.mp4", b"file_content",
|
||||||
|
content_type="video/mp4")
|
||||||
|
form = UploadForm(post_data, {'file': video_file})
|
||||||
|
self.assertFalse(form.is_valid())
|
||||||
|
|
||||||
|
# posting an archive file should work
|
||||||
|
archive_file = SimpleUploadedFile("backup.tar.gz", b"file_content",
|
||||||
|
content_type="application/gzip")
|
||||||
|
form = UploadForm(post_data, {'file': archive_file})
|
||||||
|
form.is_valid()
|
||||||
|
self.assertTrue(form.is_valid())
|
||||||
|
|||||||
@ -138,7 +138,7 @@ class UploadArchiveView(SuccessMessageMixin, FormView):
|
|||||||
prefix = 'backups'
|
prefix = 'backups'
|
||||||
template_name = 'backups_upload.html'
|
template_name = 'backups_upload.html'
|
||||||
success_url = reverse_lazy('backups:index')
|
success_url = reverse_lazy('backups:index')
|
||||||
success_message = _('Archive uploaded.')
|
success_message = _('Backup file uploaded.')
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
"""Return additional context for rendering the template."""
|
"""Return additional context for rendering the template."""
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user