mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-03-11 09:04:54 +00:00
actions: Convert tests to pytest style
Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org> Reviewed-by: Joseph Nuthalapati <njoseph@thoughtworks.com>
This commit is contained in:
parent
e9adeed958
commit
05bf200906
@ -16,175 +16,169 @@
|
||||
#
|
||||
"""
|
||||
Test module for actions utilities that modify configuration.
|
||||
|
||||
Verify that privileged actions perform as expected. See actions.py for a full
|
||||
description of the expectations.
|
||||
|
||||
"""
|
||||
|
||||
import os
|
||||
import pathlib
|
||||
import shutil
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
import pytest
|
||||
|
||||
import apt_pkg
|
||||
from plinth import cfg
|
||||
from plinth.actions import run, superuser_run
|
||||
from plinth.errors import ActionError
|
||||
|
||||
euid = os.geteuid()
|
||||
|
||||
@pytest.fixture(scope='module', autouse=True)
|
||||
def actions_test_setup():
|
||||
"""Setup a temporary directory for testing actions.
|
||||
|
||||
Copy system commands ``echo`` and ``id`` into actions directory during
|
||||
testing.
|
||||
|
||||
"""
|
||||
with tempfile.TemporaryDirectory() as tmp_directory:
|
||||
old_actions_dir = cfg.actions_dir
|
||||
cfg.actions_dir = str(tmp_directory)
|
||||
|
||||
actions_dir = pathlib.Path(__file__).parent / '../../actions'
|
||||
shutil.copy(str(actions_dir / 'packages'), str(tmp_directory))
|
||||
shutil.copy(str(actions_dir / 'test_path'), str(tmp_directory))
|
||||
shutil.copy('/bin/echo', str(tmp_directory))
|
||||
shutil.copy('/usr/bin/id', str(tmp_directory))
|
||||
|
||||
yield
|
||||
cfg.actions_dir = old_actions_dir
|
||||
|
||||
|
||||
class TestActions(unittest.TestCase):
|
||||
"""Verify that privileged actions perform as expected.
|
||||
def notest_run_as_root():
|
||||
"""1. Privileged actions run as root. """
|
||||
assert superuser_run('id', ['-ur'])[0].strip() == '0' # user 0 is root
|
||||
|
||||
See actions.py for a full description of the expectations.
|
||||
|
||||
Symlink to ``echo`` and ``id`` during testing.
|
||||
def test_breakout_actions_dir():
|
||||
"""2. The actions directory can't be changed at run time.
|
||||
|
||||
Can't currently be tested, as the actions directory is hardcoded.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
"""Initial setup for all the classes."""
|
||||
cls.action_directory = tempfile.TemporaryDirectory()
|
||||
cls.actions_dir_factory = cfg.actions_dir
|
||||
cfg.actions_dir = cls.action_directory.name
|
||||
actions_dir = os.path.join(os.path.dirname(__file__), '..', '..',
|
||||
'actions')
|
||||
|
||||
shutil.copy(os.path.join(actions_dir, 'packages'), cfg.actions_dir)
|
||||
shutil.copy(os.path.join(actions_dir, 'test_path'), cfg.actions_dir)
|
||||
shutil.copy('/bin/echo', cfg.actions_dir)
|
||||
shutil.copy('/usr/bin/id', cfg.actions_dir)
|
||||
def test_breakout_up():
|
||||
"""3A. Users can't call actions above the actions directory.
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
"""Cleanup after all the tests are completed."""
|
||||
cls.action_directory.cleanup()
|
||||
cfg.actions_dir = cls.actions_dir_factory
|
||||
Tests both a relative and a literal path.
|
||||
"""
|
||||
for action in ('../echo', '/bin/echo'):
|
||||
with pytest.raises(ValueError):
|
||||
run(action, ['hi'])
|
||||
|
||||
def notest_run_as_root(self):
|
||||
"""1. Privileged actions run as root. """
|
||||
self.assertEqual(
|
||||
'0', # user 0 is root
|
||||
superuser_run('id', ['-ur'])[0].strip())
|
||||
|
||||
def test_breakout_actions_dir(self):
|
||||
"""2. The actions directory can't be changed at run time.
|
||||
def test_breakout_down():
|
||||
"""3B. Users can't call actions beneath the actions directory."""
|
||||
action = 'directory/echo'
|
||||
with pytest.raises(ValueError):
|
||||
superuser_run(action)
|
||||
|
||||
Can't currently be tested, as the actions directory is hardcoded.
|
||||
"""
|
||||
pass
|
||||
|
||||
def test_breakout_up(self):
|
||||
"""3A. Users can't call actions above the actions directory.
|
||||
def test_breakout_actions():
|
||||
"""3C. Actions can't be used to run other actions.
|
||||
|
||||
Tests both a relative and a literal path.
|
||||
"""
|
||||
for action in ('../echo', '/bin/echo'):
|
||||
with self.assertRaises(ValueError):
|
||||
run(action, ['hi'])
|
||||
If multiple actions are specified, bail out.
|
||||
"""
|
||||
# Counting is safer than actual badness.
|
||||
actions = ('echo ""; echo $((1+1))', 'echo "" && echo $((1+1))',
|
||||
'echo "" || echo $((1+1))')
|
||||
options = ('good', '')
|
||||
|
||||
def test_breakout_down(self):
|
||||
"""3B. Users can't call actions beneath the actions directory."""
|
||||
action = 'directory/echo'
|
||||
|
||||
self.assertRaises(ValueError, superuser_run, action)
|
||||
|
||||
def test_breakout_actions(self):
|
||||
"""3C. Actions can't be used to run other actions.
|
||||
|
||||
If multiple actions are specified, bail out.
|
||||
"""
|
||||
# Counting is safer than actual badness.
|
||||
actions = ('echo ""; echo $((1+1))', 'echo "" && echo $((1+1))',
|
||||
'echo "" || echo $((1+1))')
|
||||
options = ('good', '')
|
||||
|
||||
for action in actions:
|
||||
for option in options:
|
||||
with self.assertRaises(ValueError):
|
||||
run(action, [option])
|
||||
|
||||
def test_breakout_option_string(self):
|
||||
"""3D. Option strings can't be used to run other actions.
|
||||
|
||||
Verify that shell control characters aren't interpreted.
|
||||
"""
|
||||
options = ('; echo hello', '&& echo hello', '|| echo hello',
|
||||
'& echo hello', r'\; echo hello', '| echo hello',
|
||||
r':;!&\/$%@`"~#*(){}[]|+=')
|
||||
for action in actions:
|
||||
for option in options:
|
||||
output = run('echo', [option])
|
||||
output = output.rstrip('\n')
|
||||
self.assertEqual(option, output)
|
||||
with pytest.raises(ValueError):
|
||||
run(action, [option])
|
||||
|
||||
def test_breakout_option_list(self):
|
||||
"""3D. Option lists can't be used to run other actions.
|
||||
|
||||
Verify that shell control characters aren't interpreted in
|
||||
option lists.
|
||||
"""
|
||||
option_lists = (
|
||||
(';', 'echo', 'hello'),
|
||||
('&&', 'echo', 'hello'),
|
||||
('||', 'echo', 'hello'),
|
||||
('&', 'echo', 'hello'),
|
||||
(r'\;', 'echo'
|
||||
'hello'),
|
||||
('|', 'echo', 'hello'),
|
||||
('', 'echo', '', 'hello'), # Empty option argument
|
||||
tuple(r':;!&\/$%@`"~#*(){}[]|+='))
|
||||
for options in option_lists:
|
||||
output = run('echo', options)
|
||||
output = output.rstrip('\n')
|
||||
expected_output = ' '.join(options)
|
||||
self.assertEqual(output, expected_output)
|
||||
def test_breakout_option_string():
|
||||
"""3D. Option strings can't be used to run other actions.
|
||||
|
||||
def test_multiple_options_and_output(self):
|
||||
"""4. Multiple options can be provided as a list or as a tuple.
|
||||
|
||||
5. Output is returned from the command.
|
||||
"""
|
||||
options = '1 2 3 4 5 6 7 8 9'
|
||||
|
||||
output = run('echo', options.split())
|
||||
Verify that shell control characters aren't interpreted.
|
||||
"""
|
||||
options = ('; echo hello', '&& echo hello', '|| echo hello',
|
||||
'& echo hello', r'\; echo hello', '| echo hello',
|
||||
r':;!&\/$%@`"~#*(){}[]|+=')
|
||||
for option in options:
|
||||
output = run('echo', [option])
|
||||
output = output.rstrip('\n')
|
||||
self.assertEqual(options, output)
|
||||
assert option == output
|
||||
|
||||
output = run('echo', tuple(options.split()))
|
||||
|
||||
def test_breakout_option_list():
|
||||
"""3D. Option lists can't be used to run other actions.
|
||||
|
||||
Verify that shell control characters aren't interpreted in
|
||||
option lists.
|
||||
"""
|
||||
option_lists = (
|
||||
(';', 'echo', 'hello'),
|
||||
('&&', 'echo', 'hello'),
|
||||
('||', 'echo', 'hello'),
|
||||
('&', 'echo', 'hello'),
|
||||
(r'\;', 'echo'
|
||||
'hello'),
|
||||
('|', 'echo', 'hello'),
|
||||
('', 'echo', '', 'hello'), # Empty option argument
|
||||
tuple(r':;!&\/$%@`"~#*(){}[]|+='))
|
||||
for options in option_lists:
|
||||
output = run('echo', options)
|
||||
output = output.rstrip('\n')
|
||||
self.assertEqual(options, output)
|
||||
expected_output = ' '.join(options)
|
||||
assert output == expected_output
|
||||
|
||||
@unittest.skipUnless(euid == 0, 'Needs to be root')
|
||||
def test_is_package_manager_busy(self):
|
||||
"""Test the behavior of `is-package-manager-busy` in both locked and
|
||||
unlocked states of the dpkg lock file."""
|
||||
|
||||
apt_pkg.init() # initialize apt_pkg module
|
||||
def test_multiple_options_and_output():
|
||||
"""4. Multiple options can be provided as a list or as a tuple.
|
||||
|
||||
# In the locked state, the lsof command returns 0.
|
||||
# Hence no error is thrown.
|
||||
with apt_pkg.SystemLock():
|
||||
superuser_run('packages', ['is-package-manager-busy'])
|
||||
5. Output is returned from the command.
|
||||
"""
|
||||
options = '1 2 3 4 5 6 7 8 9'
|
||||
|
||||
# In the unlocked state, the lsof command returns 1.
|
||||
# An ActionError is raised in this case.
|
||||
with self.assertRaises(ActionError):
|
||||
superuser_run('packages', ['is-package-manager-busy'])
|
||||
output = run('echo', options.split())
|
||||
output = output.rstrip('\n')
|
||||
assert options == output
|
||||
|
||||
@unittest.skipUnless(euid == 0, 'Needs to be root')
|
||||
def test_action_path(self):
|
||||
"""Test that in development mode, python action scripts get the
|
||||
correct PYTHONPATH"""
|
||||
try:
|
||||
cfg.develop = True
|
||||
self._clear_env()
|
||||
plinth_path = run('test_path').strip()
|
||||
su_plinth_path = superuser_run('test_path').strip()
|
||||
self.assertTrue(plinth_path.startswith(cfg.root))
|
||||
self.assertEquals(plinth_path, su_plinth_path)
|
||||
finally:
|
||||
cfg.develop = False
|
||||
output = run('echo', tuple(options.split()))
|
||||
output = output.rstrip('\n')
|
||||
assert options == output
|
||||
|
||||
def _clear_env(self):
|
||||
try:
|
||||
del os.environ['PYTHONPATH']
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
@pytest.mark.usefixtures('needs_root')
|
||||
def test_is_package_manager_busy():
|
||||
"""Test the behavior of `is-package-manager-busy` in both locked and
|
||||
unlocked states of the dpkg lock file."""
|
||||
|
||||
apt_pkg.init() # initialize apt_pkg module
|
||||
|
||||
# In the locked state, the lsof command returns 0.
|
||||
# Hence no error is thrown.
|
||||
with apt_pkg.SystemLock():
|
||||
superuser_run('packages', ['is-package-manager-busy'])
|
||||
|
||||
# In the unlocked state, the lsof command returns 1.
|
||||
# An ActionError is raised in this case.
|
||||
with pytest.raises(ActionError):
|
||||
superuser_run('packages', ['is-package-manager-busy'])
|
||||
|
||||
|
||||
@pytest.mark.usefixtures('develop_mode', 'needs_root')
|
||||
def test_action_path(monkeypatch):
|
||||
"""Test that in development mode, python action scripts get the
|
||||
correct PYTHONPATH"""
|
||||
monkeypatch.setitem(os.environ, 'PYTHONPATH', '')
|
||||
plinth_path = run('test_path').strip()
|
||||
su_plinth_path = superuser_run('test_path').strip()
|
||||
assert plinth_path.startswith(cfg.root)
|
||||
assert plinth_path == su_plinth_path
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user