FreedomBox/plinth/tests/test_config.py
Sunil Mohan Adapa 465e452daf
diagnostics: Refactor check IDs, tests and background checks
- Ensure that each diagnostic test category can be identified by easy prefix
matching on the test ID.

- Give a different unique IDs each different kind of test. More specific tests
of a type get a different kind of ID.

- Make comparison of diagnostic test results in test cases more comprehensive.

- Simplify code that shows the number if issues identified.

- In many languages, there is complex logic to write plural forms. Plurals
can't be handled by assuming singular = 1 item and plural is > 1. Translation of
messages in Notification does not support plurals properly. Avoid this for now
by using sometimes incorrect plural form.

- For i18n we should avoid joining phrases/words. Words don't always maintain
order after translation.

- Notify about the total number of issues in diagnostics and not just the most
severe category. This is likely to draw more attention and avoid i18n
complexity.

- Dismiss the diagnostic notification if the latest run succeeded completely.

Tests:

- Unit tests pass.

- Diagnostics for following apps works: networks (drop-in config),
apache (daemon, listen address, internal firewall, external firewall),
tor (netcat), torproxy (internal only firewall, torproxy url, torproxy using
tor), privoxy (privoxy url, package available, package latest),

- Untested: Is release file available method in upgrades app.

Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
2023-10-07 04:52:22 +09:00

232 lines
8.3 KiB
Python

# SPDX-License-Identifier: AGPL-3.0-or-later
"""
Test the component that manages drop-in configuration.
"""
from unittest.mock import Mock, patch
import pytest
from plinth.app import App
from plinth.config import DropinConfigs
from plinth.modules.diagnostics.check import DiagnosticCheck, Result
pytestmark = pytest.mark.usefixtures('mock_privileged')
privileged_modules_to_mock = ['plinth.privileged.config']
@pytest.fixture(name='dropin_configs')
def fixture_dropin_configs():
"""Fixture to create a basic drop-in configs component."""
class AppTest(App):
app_id = 'test-app'
app = AppTest()
component = DropinConfigs('test-component',
['/etc/test/path1', '/etc/path2'])
app.add(component)
return component
@pytest.fixture(autouse=True)
def fixture_assert_dropin_config():
"""Mock asserting dropin config path."""
with patch('plinth.privileged.config._assert_managed_dropin_config'):
yield
def test_dropin_configs_init(dropin_configs):
"""Test initialization of drop-in configs component."""
assert dropin_configs.component_id == 'test-component'
assert dropin_configs.etc_paths[0] == '/etc/test/path1'
assert dropin_configs.etc_paths[1] == '/etc/path2'
assert not dropin_configs.copy_only
component = DropinConfigs('test-component', [], copy_only=False)
assert not component.copy_only
component = DropinConfigs('test-component', [], copy_only=True)
assert component.copy_only
def _assert_symlinks(component, tmp_path, should_exist, copy_only=False):
"""Assert that symlinks exists and they point correctly."""
for path in component.etc_paths:
full_path = tmp_path / path.lstrip('/')
if should_exist:
target = tmp_path / 'usr/share/freedombox' / path.lstrip('/')
if copy_only:
assert full_path.is_file()
assert full_path.read_text() == target.read_text()
else:
assert full_path.is_symlink()
assert full_path.resolve() == target
else:
assert not full_path.exists()
def test_dropin_configs_setup(dropin_configs, tmp_path):
"""Test setup for dropin configs component."""
with patch('plinth.config.DropinConfigs.ROOT', new=tmp_path):
is_enabled = Mock()
App.get('test-app').is_enabled = is_enabled
is_enabled.return_value = False
dropin_configs.setup(old_version=0)
_assert_symlinks(dropin_configs, tmp_path, should_exist=False)
is_enabled.return_value = True
dropin_configs.setup(old_version=0)
_assert_symlinks(dropin_configs, tmp_path, should_exist=True)
def test_dropin_configs_enable_disable_symlinks(dropin_configs, tmp_path):
"""Test enable/disable for dropin configs component for symlinks."""
with patch('plinth.config.DropinConfigs.ROOT', new=tmp_path):
# Enable when nothing exists
dropin_configs.enable()
_assert_symlinks(dropin_configs, tmp_path, should_exist=True)
# Disable
dropin_configs.disable()
_assert_symlinks(dropin_configs, tmp_path, should_exist=False)
# Enable when a file already exists
dropin_configs.disable()
etc_path = DropinConfigs._get_etc_path('/etc/test/path1')
etc_path.touch()
dropin_configs.enable()
_assert_symlinks(dropin_configs, tmp_path, should_exist=True)
# When symlink already exists to wrong location
dropin_configs.disable()
etc_path.symlink_to('/blah')
dropin_configs.enable()
_assert_symlinks(dropin_configs, tmp_path, should_exist=True)
# When symlink already exists to correct location
dropin_configs.disable()
target_path = DropinConfigs._get_target_path('/etc/test/path1')
etc_path.symlink_to(target_path)
dropin_configs.enable()
_assert_symlinks(dropin_configs, tmp_path, should_exist=True)
def test_dropin_configs_enable_disable_copy_only(dropin_configs, tmp_path):
"""Test enable/disable for dropin configs component for copying."""
with patch('plinth.config.DropinConfigs.ROOT', new=tmp_path):
dropin_configs.copy_only = True
for path in ['/etc/test/path1', '/etc/path2']:
target = DropinConfigs._get_target_path(path)
target.parent.mkdir(parents=True, exist_ok=True)
target.write_text('test-config-content')
# Enable when nothing exists
dropin_configs.enable()
_assert_symlinks(dropin_configs, tmp_path, should_exist=True,
copy_only=True)
# Disable
dropin_configs.disable()
_assert_symlinks(dropin_configs, tmp_path, should_exist=False,
copy_only=True)
# Enable when a file already exists with wrong content
dropin_configs.disable()
etc_path = DropinConfigs._get_etc_path('/etc/test/path1')
etc_path.write_text('x-invalid-content')
dropin_configs.enable()
_assert_symlinks(dropin_configs, tmp_path, should_exist=True,
copy_only=True)
# When the file is a symlink
dropin_configs.disable()
etc_path.symlink_to('/blah')
dropin_configs.enable()
_assert_symlinks(dropin_configs, tmp_path, should_exist=True,
copy_only=True)
# When copy already exists with correct content
dropin_configs.disable()
etc_path.write_text('test-config-content')
dropin_configs.enable()
_assert_symlinks(dropin_configs, tmp_path, should_exist=True,
copy_only=True)
def test_dropin_config_diagnose_symlinks(dropin_configs, tmp_path):
"""Test diagnosing dropin configs for symlinks."""
with patch('plinth.config.DropinConfigs.ROOT', new=tmp_path):
# Nothing exists
results = dropin_configs.diagnose()
assert results == [
DiagnosticCheck(
f'dropin-config-{tmp_path}/etc/test/path1',
f'Static configuration {tmp_path}/etc/test/path1 is setup '
'properly', Result.FAILED),
DiagnosticCheck(
f'dropin-config-{tmp_path}/etc/path2',
f'Static configuration {tmp_path}/etc/path2 is setup properly',
Result.FAILED),
]
# Proper symlinks exist
dropin_configs.enable()
results = dropin_configs.diagnose()
assert results[0].result == 'passed'
assert results[1].result == 'passed'
# A file exists instead of symlink
dropin_configs.disable()
etc_path = DropinConfigs._get_etc_path('/etc/test/path1')
etc_path.touch()
results = dropin_configs.diagnose()
assert results[0].result == 'failed'
# Symlink points to wrong location
dropin_configs.disable()
etc_path.symlink_to('/blah')
results = dropin_configs.diagnose()
assert results[0].result == 'failed'
# Symlink is recreated
dropin_configs.enable()
results = dropin_configs.diagnose()
assert results[0].result == 'passed'
def test_dropin_config_diagnose_copy_only(dropin_configs, tmp_path):
"""Test diagnosing dropin configs."""
with patch('plinth.config.DropinConfigs.ROOT', new=tmp_path):
dropin_configs.copy_only = True
for path in ['/etc/test/path1', '/etc/path2']:
target = DropinConfigs._get_target_path(path)
target.parent.mkdir(parents=True, exist_ok=True)
target.write_text('test-config-content')
# Nothing exists
results = dropin_configs.diagnose()
assert results[0].result == 'failed'
assert results[1].result == 'failed'
# Proper copies exist
dropin_configs.enable()
results = dropin_configs.diagnose()
assert results[0].result == 'passed'
assert results[1].result == 'passed'
# A symlink exists instead of a copied file
dropin_configs.disable()
etc_path = DropinConfigs._get_etc_path('/etc/test/path1')
etc_path.symlink_to('/blah')
results = dropin_configs.diagnose()
assert results[0].result == 'failed'
# Copied file contains wrong contents
dropin_configs.disable()
etc_path.write_text('x-invalid-contents')
results = dropin_configs.diagnose()
assert results[0].result == 'failed'