FreedomBox/plinth/modules/backups/tests/test_components.py
Sunil Mohan Adapa ac8dbcfc1c
backups: Add ability to cleanup files before restoring a backup
- Many times, merging old and new data folders is not ideal and could lead to
unexpected outcomes. Perhaps removing all the backup folders and files before
restore is ideal. However, this patch tries to introduce that approach slowly on
an experimental basis.

Tests:

- Unit tests work.

Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
2025-04-01 09:46:15 -04:00

300 lines
7.9 KiB
Python

# SPDX-License-Identifier: AGPL-3.0-or-later
"""
Test the App components provides by backups app.
"""
from unittest.mock import call, patch
import pytest
from plinth import kvstore
from .. import components
from ..components import BackupRestore
# pylint: disable=protected-access
@pytest.fixture(name='backup_restore')
def fixture_backup_restore():
"""Fixture to create a domain type after clearing all existing ones."""
value = {'files': ['a', 'b'], 'directories': ['a', 'b']}
services = ['service-1', {'type': 'system', 'name': 'service-2'}]
settings = ['setting-1', 'setting-2']
delete_before_restore = ['path1', 'path2']
return BackupRestore('test-backup-restore', config=value, data=value,
secrets=value, services=services, settings=settings,
delete_before_restore=delete_before_restore)
@pytest.mark.parametrize('section', [
None,
{
'directories': ['a', 'b']
},
{
'files': ['a', 'b']
},
{
'directories': ['a'],
'files': ['a']
},
{
'extra': 'value'
},
])
def test_valid_directories_and_files(section):
"""Test that valid values of files and directories."""
components._validate_directories_and_files(section)
@pytest.mark.parametrize('section', [
'invalid',
10,
['invalid'],
{
'files': None
},
{
'files': 10
},
{
'files': {}
},
{
'files': [10],
},
{
'files': [None],
},
{
'files': [[]],
},
{
'directories': None
},
{
'directories': [10],
},
])
def test_invalid_directories_and_files(section):
"""Test that invalid values of files and directories."""
with pytest.raises(AssertionError):
components._validate_directories_and_files(section)
@pytest.mark.parametrize('services', [
None,
[],
['service'],
[{
'type': 'uwsgi',
'name': 'service'
}],
[{
'type': 'system',
'name': 'service'
}],
[{
'type': 'apache',
'name': 'service',
'kind': 'config'
}],
[{
'type': 'apache',
'name': 'service',
'kind': 'site'
}],
[{
'type': 'apache',
'name': 'service',
'kind': 'module'
}],
])
def test_valid_services(services):
"""Test that valid values of services."""
components._validate_services(services)
@pytest.mark.parametrize('services', [
10,
'invalid',
[10],
[[]],
[{}],
[{
'type': 'invalid',
'name': 'service'
}],
[{
'type': 10,
'name': 'service'
}],
[{
'type': 'system',
'name': 10
}],
[{
'type': 'system',
'name': None
}],
[{
'type': 'system',
'name': []
}],
[{
'type': 'apache',
'name': 'service'
}],
[{
'type': 'apache',
'name': 'service',
'kind': 'invalid-kind'
}],
])
def test_invalid_services(services):
"""Test that invalid values of services."""
with pytest.raises((AssertionError, KeyError)):
components._validate_services(services)
@pytest.mark.parametrize('paths', [
10,
'invalid',
[None],
[10],
[[]],
[{}],
])
def test_invalid_paths(paths):
"""Test invalid values for paths."""
with pytest.raises(AssertionError):
components._validate_paths(paths)
def test_backup_restore_init_default_arguments():
"""Test initialization of the backup restore object."""
component = BackupRestore('test-backup-restore')
assert component.component_id == 'test-backup-restore'
assert component.config == {}
assert component.data == {}
assert component.secrets == {}
assert component.services == []
assert component.settings == []
assert not component.has_data
@pytest.mark.parametrize('key', ['config', 'data', 'secrets'])
def test_backup_restore_init(key):
"""Test initialization of the backup restore object."""
with pytest.raises(AssertionError):
BackupRestore('test-backup-restore', **{key: 'invalid-value'})
value = {'files': ['a', 'b'], 'directories': ['a', 'b']}
component = BackupRestore('test-backup-restore', **{key: value})
assert getattr(component, key) == value
assert component.has_data
def test_backup_restore_init_services():
"""Test initialization of the backup restore object."""
with pytest.raises(AssertionError):
BackupRestore('test-backup-restore', services='invalid-value')
services = ['service-1', {'type': 'system', 'name': 'service-2'}]
component = BackupRestore('test-backup-restore', services=services)
assert component.services == services
assert not component.has_data
def test_backup_restore_init_settings():
"""Test initialization of the backup restore object."""
with pytest.raises(AssertionError):
BackupRestore('test-backup-restore', settings='invalid-value')
settings = ['setting1', 'setting2']
component = BackupRestore('test-backup-restore', settings=settings)
assert component.settings == settings
assert component.has_data
assert component.data == {}
component.app_id = 'testapp'
settings_file = '/var/lib/plinth/backups-data/testapp-settings.json'
assert component.data == {'files': [settings_file]}
def test_backup_restore_equal(backup_restore):
"""Test equality operator on the backup restore object."""
assert backup_restore == BackupRestore('test-backup-restore')
assert backup_restore != BackupRestore('test-different')
def test_backup_restore_manifest(backup_restore):
"""Test manifest retrieval from backup restore object."""
manifest = backup_restore.manifest
assert isinstance(manifest, dict)
assert manifest['config'] == backup_restore.config
assert manifest['data'] == backup_restore.data
assert manifest['secrets'] == backup_restore.secrets
assert manifest['services'] == backup_restore.services
assert manifest['settings'] == backup_restore.settings
assert BackupRestore('test-backup-restore').manifest == {}
def test_backup_restore_hooks(backup_restore):
"""Test running hooks on backup restore object."""
packet = None
backup_restore.backup_post(packet)
@pytest.mark.django_db
@patch('plinth.modules.backups.privileged.dump_settings')
def test_backup_restore_backup_pre(dump_settings, backup_restore):
"""Test running backup-pre hook."""
packet = None
kvstore.set('setting-1', 'value-1')
backup_restore.app_id = 'testapp'
component = BackupRestore('test-backup-restore')
component.backup_pre(packet)
dump_settings.assert_has_calls([])
backup_restore.backup_pre(packet)
dump_settings.assert_has_calls([call('testapp', {'setting-1': 'value-1'})])
@patch('plinth.modules.backups.privileged.delete_before_restore')
def test_backup_restore_restore_pre(delete_before_restore, backup_restore):
"""Test running restore-pre hook."""
packet = None
backup_restore.app_id = 'testapp'
component = BackupRestore('test-backup-restore')
component.restore_pre(packet)
delete_before_restore.assert_has_calls([])
backup_restore.restore_pre(packet)
delete_before_restore.assert_has_calls([call('testapp')])
@pytest.mark.django_db
@patch('plinth.modules.backups.privileged.load_settings')
def test_backup_restore_restore_post(load_settings, backup_restore):
"""Test running restore-post hook."""
packet = None
backup_restore.app_id = 'testapp'
component = BackupRestore('test-backup-restore')
component.restore_post(packet)
load_settings.assert_has_calls([])
output = {'setting-1': 'value-1'}
load_settings.return_value = output
backup_restore.restore_post(packet)
load_settings.assert_has_calls([call('testapp')])
assert kvstore.get('setting-1') == 'value-1'
with pytest.raises(Exception):
kvstore.get('setting-2')