From 157abb023ab8fa10337d5e736d17eedfdad6c756 Mon Sep 17 00:00:00 2001 From: Sunil Mohan Adapa Date: Sun, 10 Mar 2024 11:54:07 -0700 Subject: [PATCH] tests: Merge actions related test files Signed-off-by: Sunil Mohan Adapa Reviewed-by: James Valleroy --- plinth/tests/test_actions.py | 173 +++++++++++++++++++++++++- plinth/tests/test_actions_actions.py | 176 --------------------------- 2 files changed, 172 insertions(+), 177 deletions(-) delete mode 100644 plinth/tests/test_actions_actions.py diff --git a/plinth/tests/test_actions.py b/plinth/tests/test_actions.py index 4a1f1dc1d..b4e8f3f3f 100644 --- a/plinth/tests/test_actions.py +++ b/plinth/tests/test_actions.py @@ -10,13 +10,16 @@ description of the expectations. import json import os import subprocess +import typing from unittest.mock import Mock, call, patch import pytest -from plinth import cfg +from plinth import actions, cfg from plinth.actions import privileged +actions_name = 'actions' + @pytest.fixture(name='popen') def fixture_popen(): @@ -140,3 +143,171 @@ def test_privileged_method_exceptions(get_module_name, popen): wrapped_func = privileged(func_with_exception) with pytest.raises(TypeError, match='type error'): wrapped_func() + + +@patch('importlib.import_module') +@patch('plinth.module_loader.get_module_import_path') +@patch('os.getuid') +def test_call_syntax_checks(getuid, get_module_import_path, import_module): + """Test that calling a method results in proper syntax checks.""" + privileged_call = actions._privileged_call + + # Module name validation + getuid.return_value = 0 + with pytest.raises(SyntaxError, match='Invalid module name'): + privileged_call('foo.bar', 'x-action', {}) + + # Module import test + get_module_import_path.return_value = 'plinth.modules.test_module' + import_module.side_effect = ModuleNotFoundError + with pytest.raises(SyntaxError, match='Specified module not found'): + privileged_call('test_module', 'x-action', {}) + + import_module.assert_has_calls( + [call('plinth.modules.test_module.privileged')]) + + # Finding action in a module + module = type('', (), {}) + import_module.side_effect = None + import_module.return_value = module + with pytest.raises(SyntaxError, match='Specified action not found'): + privileged_call('test_module', 'x-action', {}) + + # Checking if action is privileged + def unprivileged_func(): + pass + + setattr(module, 'func', unprivileged_func) + with pytest.raises(SyntaxError, + match='Specified action is not privileged action'): + privileged_call('test-module', 'func', {}) + + # Argument validation + @actions.privileged + def func(): + return 'foo' + + setattr(module, 'func', func) + with pytest.raises(SyntaxError, match='Invalid arguments format'): + privileged_call('test-module', 'func', {}) + + # Successful call + return_value = privileged_call('test-module', 'func', { + 'args': [], + 'kwargs': {} + }) + assert return_value == {'result': 'success', 'return': 'foo'} + + # Exception call + @actions.privileged + def exception_func(): + raise RuntimeError('foo exception') + + setattr(module, 'func', exception_func) + return_value = privileged_call('test-module', 'func', { + 'args': [], + 'kwargs': {} + }) + assert return_value['result'] == 'exception' + assert return_value['exception']['module'] == 'builtins' + assert return_value['exception']['name'] == 'RuntimeError' + assert return_value['exception']['args'] == ('foo exception', ) + assert isinstance(return_value['exception']['traceback'], list) + for line in return_value['exception']['traceback']: + assert isinstance(line, str) + + +def test_assert_valid_arguments(): + """Test that checking valid arguments works.""" + assert_valid = actions._privileged_assert_valid_arguments + + values = [ + None, [], 10, {}, { + 'args': [] + }, { + 'kwargs': {} + }, { + 'args': {}, + 'kwargs': {} + }, { + 'args': [], + 'kwargs': [] + } + ] + for value in values: + with pytest.raises(SyntaxError, match='Invalid arguments format'): + assert_valid(lambda: None, value) + + def func(a: int, b: str, c: int = 3, d: str = 'foo'): + pass + + with pytest.raises(SyntaxError, match='Too many arguments'): + assert_valid(func, {'args': [1, 2, 3], 'kwargs': {'c': 3, 'd': 4}}) + + with pytest.raises(SyntaxError, match='Too many arguments'): + assert_valid(func, {'args': [1, 2, 3, 4, 5], 'kwargs': {}}) + + with pytest.raises(SyntaxError, match='Too many arguments'): + assert_valid(func, { + 'args': [], + 'kwargs': { + 'a': 1, + 'b': '2', + 'c': 3, + 'd': '4', + 'e': 5 + } + }) + + with pytest.raises(SyntaxError, match='Argument not provided: b'): + assert_valid(func, {'args': [1], 'kwargs': {}}) + + with pytest.raises(SyntaxError, match='Unknown argument: e'): + assert_valid(func, {'args': [1, '2'], 'kwargs': {'e': 5}}) + + with pytest.raises(SyntaxError, match='Duplicate argument: c'): + assert_valid(func, {'args': [1, '2', 3], 'kwargs': {'c': 4}}) + + with pytest.raises(TypeError, match='Expected type str for arg #1'): + assert_valid(func, {'args': [1, 2], 'kwargs': {}}) + + with pytest.raises(TypeError, match='Expected type int for arg c'): + assert_valid(func, {'args': [1, '2'], 'kwargs': {'c': '3'}}) + + +def test_assert_valid_type(): + """Test that type validation works as expected.""" + assert_valid = actions._privileged_assert_valid_type + + assert_valid(None, None, typing.Any) + + # Invalid values for int, str, float and Optional + values = [[1, bool], ['foo', int], [1, str], [1, float], + [1, typing.Optional[str]], [1, str | None], + [1.1, typing.Union[int, str]], [1.1, int | str], [1, list], + [1, dict], [[1], list[str]], [{ + 'a': 'b' + }, dict[int, str]], [{ + 1: 2 + }, dict[int, str]]] + for value in values: + with pytest.raises(TypeError): + assert_valid('arg', *value) + + # Valid values + assert_valid('arg', True, bool) + assert_valid('arg', 1, int) + assert_valid('arg', '1', str) + assert_valid('arg', 1.1, float) + assert_valid('arg', None, typing.Optional[int]) + assert_valid('arg', 1, typing.Optional[int]) + assert_valid('arg', None, int | None) + assert_valid('arg', 1, int | None) + assert_valid('arg', 1, typing.Union[int, str]) + assert_valid('arg', '1', typing.Union[int, str]) + assert_valid('arg', 1, int | str) + assert_valid('arg', '1', int | str) + assert_valid('arg', [], list[int]) + assert_valid('arg', ['foo'], list[str]) + assert_valid('arg', {}, dict[int, str]) + assert_valid('arg', {1: 'foo'}, dict[int, str]) diff --git a/plinth/tests/test_actions_actions.py b/plinth/tests/test_actions_actions.py deleted file mode 100644 index 5ae5fa68a..000000000 --- a/plinth/tests/test_actions_actions.py +++ /dev/null @@ -1,176 +0,0 @@ -# SPDX-License-Identifier: AGPL-3.0-or-later -""" -Test module for code that runs methods are privileged actions. -""" - -import typing -from unittest.mock import call as mock_call -from unittest.mock import patch - -import pytest - -from plinth import actions - -actions_name = 'actions' - - -@patch('importlib.import_module') -@patch('plinth.module_loader.get_module_import_path') -@patch('os.getuid') -def test_call_syntax_checks(getuid, get_module_import_path, import_module): - """Test that calling a method results in proper syntax checks.""" - call = actions._privileged_call - - # Module name validation - getuid.return_value = 0 - with pytest.raises(SyntaxError, match='Invalid module name'): - call('foo.bar', 'x-action', {}) - - # Module import test - get_module_import_path.return_value = 'plinth.modules.test_module' - import_module.side_effect = ModuleNotFoundError - with pytest.raises(SyntaxError, match='Specified module not found'): - call('test_module', 'x-action', {}) - - import_module.assert_has_calls( - [mock_call('plinth.modules.test_module.privileged')]) - - # Finding action in a module - module = type('', (), {}) - import_module.side_effect = None - import_module.return_value = module - with pytest.raises(SyntaxError, match='Specified action not found'): - call('test_module', 'x-action', {}) - - # Checking if action is privileged - def unprivileged_func(): - pass - - setattr(module, 'func', unprivileged_func) - with pytest.raises(SyntaxError, - match='Specified action is not privileged action'): - call('test-module', 'func', {}) - - # Argument validation - @actions.privileged - def func(): - return 'foo' - - setattr(module, 'func', func) - with pytest.raises(SyntaxError, match='Invalid arguments format'): - call('test-module', 'func', {}) - - # Successful call - return_value = call('test-module', 'func', {'args': [], 'kwargs': {}}) - assert return_value == {'result': 'success', 'return': 'foo'} - - # Exception call - @actions.privileged - def exception_func(): - raise RuntimeError('foo exception') - - setattr(module, 'func', exception_func) - return_value = call('test-module', 'func', {'args': [], 'kwargs': {}}) - assert return_value['result'] == 'exception' - assert return_value['exception']['module'] == 'builtins' - assert return_value['exception']['name'] == 'RuntimeError' - assert return_value['exception']['args'] == ('foo exception', ) - assert isinstance(return_value['exception']['traceback'], list) - for line in return_value['exception']['traceback']: - assert isinstance(line, str) - - -def test_assert_valid_arguments(): - """Test that checking valid arguments works.""" - assert_valid = actions._privileged_assert_valid_arguments - - values = [ - None, [], 10, {}, { - 'args': [] - }, { - 'kwargs': {} - }, { - 'args': {}, - 'kwargs': {} - }, { - 'args': [], - 'kwargs': [] - } - ] - for value in values: - with pytest.raises(SyntaxError, match='Invalid arguments format'): - assert_valid(lambda: None, value) - - def func(a: int, b: str, c: int = 3, d: str = 'foo'): - pass - - with pytest.raises(SyntaxError, match='Too many arguments'): - assert_valid(func, {'args': [1, 2, 3], 'kwargs': {'c': 3, 'd': 4}}) - - with pytest.raises(SyntaxError, match='Too many arguments'): - assert_valid(func, {'args': [1, 2, 3, 4, 5], 'kwargs': {}}) - - with pytest.raises(SyntaxError, match='Too many arguments'): - assert_valid(func, { - 'args': [], - 'kwargs': { - 'a': 1, - 'b': '2', - 'c': 3, - 'd': '4', - 'e': 5 - } - }) - - with pytest.raises(SyntaxError, match='Argument not provided: b'): - assert_valid(func, {'args': [1], 'kwargs': {}}) - - with pytest.raises(SyntaxError, match='Unknown argument: e'): - assert_valid(func, {'args': [1, '2'], 'kwargs': {'e': 5}}) - - with pytest.raises(SyntaxError, match='Duplicate argument: c'): - assert_valid(func, {'args': [1, '2', 3], 'kwargs': {'c': 4}}) - - with pytest.raises(TypeError, match='Expected type str for arg #1'): - assert_valid(func, {'args': [1, 2], 'kwargs': {}}) - - with pytest.raises(TypeError, match='Expected type int for arg c'): - assert_valid(func, {'args': [1, '2'], 'kwargs': {'c': '3'}}) - - -def test_assert_valid_type(): - """Test that type validation works as expected.""" - assert_valid = actions._privileged_assert_valid_type - - assert_valid(None, None, typing.Any) - - # Invalid values for int, str, float and Optional - values = [[1, bool], ['foo', int], [1, str], [1, float], - [1, typing.Optional[str]], [1, str | None], - [1.1, typing.Union[int, str]], [1.1, int | str], [1, list], - [1, dict], [[1], list[str]], [{ - 'a': 'b' - }, dict[int, str]], [{ - 1: 2 - }, dict[int, str]]] - for value in values: - with pytest.raises(TypeError): - assert_valid('arg', *value) - - # Valid values - assert_valid('arg', True, bool) - assert_valid('arg', 1, int) - assert_valid('arg', '1', str) - assert_valid('arg', 1.1, float) - assert_valid('arg', None, typing.Optional[int]) - assert_valid('arg', 1, typing.Optional[int]) - assert_valid('arg', None, int | None) - assert_valid('arg', 1, int | None) - assert_valid('arg', 1, typing.Union[int, str]) - assert_valid('arg', '1', typing.Union[int, str]) - assert_valid('arg', 1, int | str) - assert_valid('arg', '1', int | str) - assert_valid('arg', [], list[int]) - assert_valid('arg', ['foo'], list[str]) - assert_valid('arg', {}, dict[int, str]) - assert_valid('arg', {1: 'foo'}, dict[int, str])