[fix] Question tests

This commit is contained in:
ljf 2021-09-07 20:09:27 +02:00
parent 6b3af5fa33
commit b007102842
3 changed files with 331 additions and 199 deletions

View file

@ -84,9 +84,12 @@ def pytest_cmdline_main(config):
class DummyInterface:
type = "test"
type = "cli"
def prompt(*args, **kwargs):
def prompt(self, *args, **kwargs):
raise NotImplementedError
def display(self, message, *args, **kwargs):
print(message)
Moulinette._interface = DummyInterface()

View file

@ -1,14 +1,19 @@
import sys
import pytest
import os
from mock import patch
from mock import patch, MagicMock
from io import StringIO
from collections import OrderedDict
from moulinette import Moulinette
from yunohost import domain, user
from yunohost.utils.config import parse_args_in_yunohost_format, PasswordQuestion
from yunohost.utils.config import (
parse_args_in_yunohost_format,
PasswordQuestion,
Question
)
from yunohost.utils.error import YunohostError
@ -70,7 +75,8 @@ def test_question_string_no_input():
]
answers = {}
with pytest.raises(YunohostError):
with pytest.raises(YunohostError), \
patch.object(os, "isatty", return_value=False):
parse_args_in_yunohost_format(answers, questions)
@ -84,7 +90,8 @@ def test_question_string_input():
answers = {}
expected_result = OrderedDict({"some_string": ("some_value", "string")})
with patch.object(Moulinette.interface, "prompt", return_value="some_value"):
with patch.object(Moulinette, "prompt", return_value="some_value"), \
patch.object(os, "isatty", return_value=True):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -97,7 +104,8 @@ def test_question_string_input_no_ask():
answers = {}
expected_result = OrderedDict({"some_string": ("some_value", "string")})
with patch.object(Moulinette.interface, "prompt", return_value="some_value"):
with patch.object(Moulinette, "prompt", return_value="some_value"), \
patch.object(os, "isatty", return_value=True):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -110,6 +118,7 @@ def test_question_string_no_input_optional():
]
answers = {}
expected_result = OrderedDict({"some_string": ("", "string")})
with patch.object(os, "isatty", return_value=False):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -124,7 +133,8 @@ def test_question_string_optional_with_input():
answers = {}
expected_result = OrderedDict({"some_string": ("some_value", "string")})
with patch.object(Moulinette.interface, "prompt", return_value="some_value"):
with patch.object(Moulinette, "prompt", return_value="some_value"), \
patch.object(os, "isatty", return_value=True):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -139,7 +149,8 @@ def test_question_string_optional_with_empty_input():
answers = {}
expected_result = OrderedDict({"some_string": ("", "string")})
with patch.object(Moulinette.interface, "prompt", return_value=""):
with patch.object(Moulinette, "prompt", return_value=""), \
patch.object(os, "isatty", return_value=True):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -153,7 +164,8 @@ def test_question_string_optional_with_input_without_ask():
answers = {}
expected_result = OrderedDict({"some_string": ("some_value", "string")})
with patch.object(Moulinette.interface, "prompt", return_value="some_value"):
with patch.object(Moulinette, "prompt", return_value="some_value"), \
patch.object(os, "isatty", return_value=True):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -167,6 +179,7 @@ def test_question_string_no_input_default():
]
answers = {}
expected_result = OrderedDict({"some_string": ("some_value", "string")})
with patch.object(os, "isatty", return_value=False):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -181,10 +194,13 @@ def test_question_string_input_test_ask():
answers = {}
with patch.object(
Moulinette.interface, "prompt", return_value="some_value"
) as prompt:
Moulinette, "prompt", return_value="some_value"
) as prompt, patch.object(os, "isatty", return_value=True):
parse_args_in_yunohost_format(answers, questions)
prompt.assert_called_with(ask_text, False)
prompt.assert_called_with(
message=ask_text, is_password=False, confirm=False,
prefill='', is_multiline=False
)
def test_question_string_input_test_ask_with_default():
@ -200,10 +216,14 @@ def test_question_string_input_test_ask_with_default():
answers = {}
with patch.object(
Moulinette.interface, "prompt", return_value="some_value"
) as prompt:
Moulinette, "prompt", return_value="some_value"
) as prompt, patch.object(os, "isatty", return_value=True):
parse_args_in_yunohost_format(answers, questions)
prompt.assert_called_with("%s (default: %s)" % (ask_text, default_text), False)
prompt.assert_called_with(
message=ask_text,
is_password=False, confirm=False,
prefill=default_text, is_multiline=False
)
@pytest.mark.skip # we should do something with this example
@ -220,11 +240,11 @@ def test_question_string_input_test_ask_with_example():
answers = {}
with patch.object(
Moulinette.interface, "prompt", return_value="some_value"
) as prompt:
Moulinette, "prompt", return_value="some_value"
) as prompt, patch.object(os, "isatty", return_value=True):
parse_args_in_yunohost_format(answers, questions)
assert ask_text in prompt.call_args[0][0]
assert example_text in prompt.call_args[0][0]
assert ask_text in prompt.call_args[1]['message']
assert example_text in prompt.call_args[1]['message']
@pytest.mark.skip # we should do something with this help
@ -241,11 +261,11 @@ def test_question_string_input_test_ask_with_help():
answers = {}
with patch.object(
Moulinette.interface, "prompt", return_value="some_value"
) as prompt:
Moulinette, "prompt", return_value="some_value"
) as prompt, patch.object(os, "isatty", return_value=True):
parse_args_in_yunohost_format(answers, questions)
assert ask_text in prompt.call_args[0][0]
assert help_text in prompt.call_args[0][0]
assert ask_text in prompt.call_args[1]['message']
assert help_text in prompt.call_args[1]['message']
def test_question_string_with_choice():
@ -259,7 +279,8 @@ def test_question_string_with_choice_prompt():
questions = [{"name": "some_string", "type": "string", "choices": ["fr", "en"]}]
answers = {"some_string": "fr"}
expected_result = OrderedDict({"some_string": ("fr", "string")})
with patch.object(Moulinette.interface, "prompt", return_value="fr"):
with patch.object(Moulinette, "prompt", return_value="fr"), \
patch.object(os, "isatty", return_value=True):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -267,7 +288,8 @@ def test_question_string_with_choice_bad():
questions = [{"name": "some_string", "type": "string", "choices": ["fr", "en"]}]
answers = {"some_string": "bad"}
with pytest.raises(YunohostError):
with pytest.raises(YunohostError), \
patch.object(os, "isatty", return_value=False):
assert parse_args_in_yunohost_format(answers, questions)
@ -283,12 +305,13 @@ def test_question_string_with_choice_ask():
]
answers = {}
with patch.object(Moulinette.interface, "prompt", return_value="ru") as prompt:
with patch.object(Moulinette, "prompt", return_value="ru") as prompt, \
patch.object(os, "isatty", return_value=True):
parse_args_in_yunohost_format(answers, questions)
assert ask_text in prompt.call_args[0][0]
assert ask_text in prompt.call_args[1]['message']
for choice in choices:
assert choice in prompt.call_args[0][0]
assert choice in prompt.call_args[1]['message']
def test_question_string_with_choice_default():
@ -302,6 +325,7 @@ def test_question_string_with_choice_default():
]
answers = {}
expected_result = OrderedDict({"some_string": ("en", "string")})
with patch.object(os, "isatty", return_value=False):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -314,6 +338,8 @@ def test_question_password():
]
answers = {"some_password": "some_value"}
expected_result = OrderedDict({"some_password": ("some_value", "password")})
Question.operation_logger = MagicMock()
with patch.object(Question.operation_logger, "data_to_redact", create=True):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -326,7 +352,8 @@ def test_question_password_no_input():
]
answers = {}
with pytest.raises(YunohostError):
with pytest.raises(YunohostError), \
patch.object(os, "isatty", return_value=False):
parse_args_in_yunohost_format(answers, questions)
@ -339,9 +366,13 @@ def test_question_password_input():
}
]
answers = {}
Question.operation_logger = { 'data_to_redact': [] }
expected_result = OrderedDict({"some_password": ("some_value", "password")})
with patch.object(Moulinette.interface, "prompt", return_value="some_value"):
Question.operation_logger = MagicMock()
with patch.object(Question.operation_logger, "data_to_redact", create=True), \
patch.object(Moulinette, "prompt", return_value="some_value"), \
patch.object(os, "isatty", return_value=True):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -355,7 +386,10 @@ def test_question_password_input_no_ask():
answers = {}
expected_result = OrderedDict({"some_password": ("some_value", "password")})
with patch.object(Moulinette.interface, "prompt", return_value="some_value"):
Question.operation_logger = MagicMock()
with patch.object(Question.operation_logger, "data_to_redact", create=True), \
patch.object(Moulinette, "prompt", return_value="some_value"), \
patch.object(os, "isatty", return_value=True):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -370,12 +404,18 @@ def test_question_password_no_input_optional():
answers = {}
expected_result = OrderedDict({"some_password": ("", "password")})
Question.operation_logger = MagicMock()
with patch.object(Question.operation_logger, "data_to_redact", create=True), \
patch.object(os, "isatty", return_value=False):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
questions = [
{"name": "some_password", "type": "password", "optional": True, "default": ""}
]
Question.operation_logger = MagicMock()
with patch.object(Question.operation_logger, "data_to_redact", create=True), \
patch.object(os, "isatty", return_value=False):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -391,7 +431,10 @@ def test_question_password_optional_with_input():
answers = {}
expected_result = OrderedDict({"some_password": ("some_value", "password")})
with patch.object(Moulinette.interface, "prompt", return_value="some_value"):
Question.operation_logger = MagicMock()
with patch.object(Question.operation_logger, "data_to_redact", create=True), \
patch.object(Moulinette, "prompt", return_value="some_value"), \
patch.object(os, "isatty", return_value=True):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -407,7 +450,10 @@ def test_question_password_optional_with_empty_input():
answers = {}
expected_result = OrderedDict({"some_password": ("", "password")})
with patch.object(Moulinette.interface, "prompt", return_value=""):
Question.operation_logger = MagicMock()
with patch.object(Question.operation_logger, "data_to_redact", create=True), \
patch.object(Moulinette, "prompt", return_value=""), \
patch.object(os, "isatty", return_value=True):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -422,7 +468,10 @@ def test_question_password_optional_with_input_without_ask():
answers = {}
expected_result = OrderedDict({"some_password": ("some_value", "password")})
with patch.object(Moulinette.interface, "prompt", return_value="some_value"):
Question.operation_logger = MagicMock()
with patch.object(Question.operation_logger, "data_to_redact", create=True), \
patch.object(Moulinette, "prompt", return_value="some_value"), \
patch.object(os, "isatty", return_value=True):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -438,7 +487,8 @@ def test_question_password_no_input_default():
answers = {}
# no default for password!
with pytest.raises(YunohostError):
with pytest.raises(YunohostError), \
patch.object(os, "isatty", return_value=False):
parse_args_in_yunohost_format(answers, questions)
@ -455,7 +505,8 @@ def test_question_password_no_input_example():
answers = {"some_password": "some_value"}
# no example for password!
with pytest.raises(YunohostError):
with pytest.raises(YunohostError), \
patch.object(os, "isatty", return_value=False):
parse_args_in_yunohost_format(answers, questions)
@ -470,11 +521,16 @@ def test_question_password_input_test_ask():
]
answers = {}
with patch.object(
Moulinette.interface, "prompt", return_value="some_value"
) as prompt:
Question.operation_logger = MagicMock()
with patch.object(Question.operation_logger, "data_to_redact", create=True), \
patch.object(Moulinette, "prompt", return_value="some_value") as prompt, \
patch.object(os, "isatty", return_value=True):
parse_args_in_yunohost_format(answers, questions)
prompt.assert_called_with(ask_text, True)
prompt.assert_called_with(
message=ask_text,
is_password=True, confirm=True,
prefill='', is_multiline=False
)
@pytest.mark.skip # we should do something with this example
@ -491,12 +547,13 @@ def test_question_password_input_test_ask_with_example():
]
answers = {}
with patch.object(
Moulinette.interface, "prompt", return_value="some_value"
) as prompt:
Question.operation_logger = MagicMock()
with patch.object(Question.operation_logger, "data_to_redact", create=True), \
patch.object(Moulinette, "prompt", return_value="some_value") as prompt, \
patch.object(os, "isatty", return_value=True):
parse_args_in_yunohost_format(answers, questions)
assert ask_text in prompt.call_args[0][0]
assert example_text in prompt.call_args[0][0]
assert ask_text in prompt.call_args[1]['message']
assert example_text in prompt.call_args[1]['message']
@pytest.mark.skip # we should do something with this help
@ -513,12 +570,13 @@ def test_question_password_input_test_ask_with_help():
]
answers = {}
with patch.object(
Moulinette.interface, "prompt", return_value="some_value"
) as prompt:
Question.operation_logger = MagicMock()
with patch.object(Question.operation_logger, "data_to_redact", create=True), \
patch.object(Moulinette, "prompt", return_value="some_value") as prompt, \
patch.object(os, "isatty", return_value=True):
parse_args_in_yunohost_format(answers, questions)
assert ask_text in prompt.call_args[0][0]
assert help_text in prompt.call_args[0][0]
assert ask_text in prompt.call_args[1]['message']
assert help_text in prompt.call_args[1]['message']
def test_question_password_bad_chars():
@ -532,7 +590,8 @@ def test_question_password_bad_chars():
]
for i in PasswordQuestion.forbidden_chars:
with pytest.raises(YunohostError):
with pytest.raises(YunohostError), \
patch.object(os, "isatty", return_value=False):
parse_args_in_yunohost_format({"some_password": i * 8}, questions)
@ -546,11 +605,13 @@ def test_question_password_strong_enough():
}
]
with pytest.raises(YunohostError):
with pytest.raises(YunohostError), \
patch.object(os, "isatty", return_value=False):
# too short
parse_args_in_yunohost_format({"some_password": "a"}, questions)
with pytest.raises(YunohostError):
with pytest.raises(YunohostError), \
patch.object(os, "isatty", return_value=False):
parse_args_in_yunohost_format({"some_password": "password"}, questions)
@ -564,11 +625,13 @@ def test_question_password_optional_strong_enough():
}
]
with pytest.raises(YunohostError):
with pytest.raises(YunohostError), \
patch.object(os, "isatty", return_value=False):
# too short
parse_args_in_yunohost_format({"some_password": "a"}, questions)
with pytest.raises(YunohostError):
with pytest.raises(YunohostError), \
patch.object(os, "isatty", return_value=False):
parse_args_in_yunohost_format({"some_password": "password"}, questions)
@ -593,7 +656,8 @@ def test_question_path_no_input():
]
answers = {}
with pytest.raises(YunohostError):
with pytest.raises(YunohostError), \
patch.object(os, "isatty", return_value=False):
parse_args_in_yunohost_format(answers, questions)
@ -608,7 +672,8 @@ def test_question_path_input():
answers = {}
expected_result = OrderedDict({"some_path": ("some_value", "path")})
with patch.object(Moulinette.interface, "prompt", return_value="some_value"):
with patch.object(Moulinette, "prompt", return_value="some_value"), \
patch.object(os, "isatty", return_value=True):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -622,7 +687,8 @@ def test_question_path_input_no_ask():
answers = {}
expected_result = OrderedDict({"some_path": ("some_value", "path")})
with patch.object(Moulinette.interface, "prompt", return_value="some_value"):
with patch.object(Moulinette, "prompt", return_value="some_value"), \
patch.object(os, "isatty", return_value=True):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -636,6 +702,7 @@ def test_question_path_no_input_optional():
]
answers = {}
expected_result = OrderedDict({"some_path": ("", "path")})
with patch.object(os, "isatty", return_value=False):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -651,7 +718,8 @@ def test_question_path_optional_with_input():
answers = {}
expected_result = OrderedDict({"some_path": ("some_value", "path")})
with patch.object(Moulinette.interface, "prompt", return_value="some_value"):
with patch.object(Moulinette, "prompt", return_value="some_value"), \
patch.object(os, "isatty", return_value=True):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -667,7 +735,8 @@ def test_question_path_optional_with_empty_input():
answers = {}
expected_result = OrderedDict({"some_path": ("", "path")})
with patch.object(Moulinette.interface, "prompt", return_value=""):
with patch.object(Moulinette, "prompt", return_value=""), \
patch.object(os, "isatty", return_value=True):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -682,7 +751,8 @@ def test_question_path_optional_with_input_without_ask():
answers = {}
expected_result = OrderedDict({"some_path": ("some_value", "path")})
with patch.object(Moulinette.interface, "prompt", return_value="some_value"):
with patch.object(Moulinette, "prompt", return_value="some_value"), \
patch.object(os, "isatty", return_value=True):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -697,6 +767,7 @@ def test_question_path_no_input_default():
]
answers = {}
expected_result = OrderedDict({"some_path": ("some_value", "path")})
with patch.object(os, "isatty", return_value=False):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -712,10 +783,14 @@ def test_question_path_input_test_ask():
answers = {}
with patch.object(
Moulinette.interface, "prompt", return_value="some_value"
) as prompt:
Moulinette, "prompt", return_value="some_value"
) as prompt, patch.object(os, "isatty", return_value=True):
parse_args_in_yunohost_format(answers, questions)
prompt.assert_called_with(ask_text, False)
prompt.assert_called_with(
message=ask_text,
is_password=False, confirm=False,
prefill='', is_multiline=False
)
def test_question_path_input_test_ask_with_default():
@ -732,10 +807,14 @@ def test_question_path_input_test_ask_with_default():
answers = {}
with patch.object(
Moulinette.interface, "prompt", return_value="some_value"
) as prompt:
Moulinette, "prompt", return_value="some_value"
) as prompt, patch.object(os, "isatty", return_value=True):
parse_args_in_yunohost_format(answers, questions)
prompt.assert_called_with("%s (default: %s)" % (ask_text, default_text), False)
prompt.assert_called_with(
message=ask_text,
is_password=False, confirm=False,
prefill=default_text, is_multiline=False
)
@pytest.mark.skip # we should do something with this example
@ -753,11 +832,11 @@ def test_question_path_input_test_ask_with_example():
answers = {}
with patch.object(
Moulinette.interface, "prompt", return_value="some_value"
) as prompt:
Moulinette, "prompt", return_value="some_value"
) as prompt, patch.object(os, "isatty", return_value=True):
parse_args_in_yunohost_format(answers, questions)
assert ask_text in prompt.call_args[0][0]
assert example_text in prompt.call_args[0][0]
assert ask_text in prompt.call_args[1]['message']
assert example_text in prompt.call_args[1]['message']
@pytest.mark.skip # we should do something with this help
@ -775,11 +854,11 @@ def test_question_path_input_test_ask_with_help():
answers = {}
with patch.object(
Moulinette.interface, "prompt", return_value="some_value"
) as prompt:
Moulinette, "prompt", return_value="some_value"
) as prompt, patch.object(os, "isatty", return_value=True):
parse_args_in_yunohost_format(answers, questions)
assert ask_text in prompt.call_args[0][0]
assert help_text in prompt.call_args[0][0]
assert ask_text in prompt.call_args[1]['message']
assert help_text in prompt.call_args[1]['message']
def test_question_boolean():
@ -913,6 +992,7 @@ def test_question_boolean_no_input():
answers = {}
expected_result = OrderedDict({"some_boolean": (0, "boolean")})
with patch.object(os, "isatty", return_value=False):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -925,7 +1005,8 @@ def test_question_boolean_bad_input():
]
answers = {"some_boolean": "stuff"}
with pytest.raises(YunohostError):
with pytest.raises(YunohostError), \
patch.object(os, "isatty", return_value=False):
parse_args_in_yunohost_format(answers, questions)
@ -940,11 +1021,13 @@ def test_question_boolean_input():
answers = {}
expected_result = OrderedDict({"some_boolean": (1, "boolean")})
with patch.object(Moulinette.interface, "prompt", return_value="y"):
with patch.object(Moulinette, "prompt", return_value="y"), \
patch.object(os, "isatty", return_value=True):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
expected_result = OrderedDict({"some_boolean": (0, "boolean")})
with patch.object(Moulinette.interface, "prompt", return_value="n"):
with patch.object(Moulinette, "prompt", return_value="n"), \
patch.object(os, "isatty", return_value=True):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -958,7 +1041,8 @@ def test_question_boolean_input_no_ask():
answers = {}
expected_result = OrderedDict({"some_boolean": (1, "boolean")})
with patch.object(Moulinette.interface, "prompt", return_value="y"):
with patch.object(Moulinette, "prompt", return_value="y"), \
patch.object(os, "isatty", return_value=True):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -972,6 +1056,7 @@ def test_question_boolean_no_input_optional():
]
answers = {}
expected_result = OrderedDict({"some_boolean": (0, "boolean")}) # default to false
with patch.object(os, "isatty", return_value=False):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -987,7 +1072,8 @@ def test_question_boolean_optional_with_input():
answers = {}
expected_result = OrderedDict({"some_boolean": (1, "boolean")})
with patch.object(Moulinette.interface, "prompt", return_value="y"):
with patch.object(Moulinette, "prompt", return_value="y"), \
patch.object(os, "isatty", return_value=True):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -1003,7 +1089,8 @@ def test_question_boolean_optional_with_empty_input():
answers = {}
expected_result = OrderedDict({"some_boolean": (0, "boolean")}) # default to false
with patch.object(Moulinette.interface, "prompt", return_value=""):
with patch.object(Moulinette, "prompt", return_value=""), \
patch.object(os, "isatty", return_value=True):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -1018,7 +1105,8 @@ def test_question_boolean_optional_with_input_without_ask():
answers = {}
expected_result = OrderedDict({"some_boolean": (0, "boolean")})
with patch.object(Moulinette.interface, "prompt", return_value="n"):
with patch.object(Moulinette, "prompt", return_value="n"), \
patch.object(os, "isatty", return_value=True):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -1033,6 +1121,7 @@ def test_question_boolean_no_input_default():
]
answers = {}
expected_result = OrderedDict({"some_boolean": (0, "boolean")})
with patch.object(os, "isatty", return_value=False):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -1061,9 +1150,14 @@ def test_question_boolean_input_test_ask():
]
answers = {}
with patch.object(Moulinette.interface, "prompt", return_value=0) as prompt:
with patch.object(Moulinette, "prompt", return_value=0) as prompt, \
patch.object(os, "isatty", return_value=True):
parse_args_in_yunohost_format(answers, questions)
prompt.assert_called_with(ask_text + " [yes | no] (default: no)", False)
prompt.assert_called_with(
message=ask_text + " [yes | no]",
is_password=False, confirm=False,
prefill='no', is_multiline=False
)
def test_question_boolean_input_test_ask_with_default():
@ -1079,9 +1173,14 @@ def test_question_boolean_input_test_ask_with_default():
]
answers = {}
with patch.object(Moulinette.interface, "prompt", return_value=1) as prompt:
with patch.object(Moulinette, "prompt", return_value=1) as prompt, \
patch.object(os, "isatty", return_value=True):
parse_args_in_yunohost_format(answers, questions)
prompt.assert_called_with("%s [yes | no] (default: yes)" % ask_text, False)
prompt.assert_called_with(
message=ask_text + " [yes | no]",
is_password=False, confirm=False,
prefill='yes', is_multiline=False
)
def test_question_domain_empty():
@ -1095,9 +1194,9 @@ def test_question_domain_empty():
expected_result = OrderedDict({"some_domain": (main_domain, "domain")})
answers = {}
with patch.object(
domain, "_get_maindomain", return_value="my_main_domain.com"
), patch.object(domain, "domain_list", return_value={"domains": [main_domain]}):
with patch.object(domain, "_get_maindomain", return_value="my_main_domain.com"),\
patch.object(domain, "domain_list", return_value={"domains": [main_domain]}), \
patch.object(os, "isatty", return_value=False):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -1164,7 +1263,8 @@ def test_question_domain_two_domains_wrong_answer():
with patch.object(
domain, "_get_maindomain", return_value=main_domain
), patch.object(domain, "domain_list", return_value={"domains": domains}):
with pytest.raises(YunohostError):
with pytest.raises(YunohostError), \
patch.object(os, "isatty", return_value=False):
parse_args_in_yunohost_format(answers, questions)
@ -1184,7 +1284,8 @@ def test_question_domain_two_domains_default_no_ask():
with patch.object(
domain, "_get_maindomain", return_value=main_domain
), patch.object(domain, "domain_list", return_value={"domains": domains}):
), patch.object(domain, "domain_list", return_value={"domains": domains}), \
patch.object(os, "isatty", return_value=False):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -1199,7 +1300,8 @@ def test_question_domain_two_domains_default():
with patch.object(
domain, "_get_maindomain", return_value=main_domain
), patch.object(domain, "domain_list", return_value={"domains": domains}):
), patch.object(domain, "domain_list", return_value={"domains": domains}), \
patch.object(os, "isatty", return_value=False):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -1213,13 +1315,14 @@ def test_question_domain_two_domains_default_input():
with patch.object(
domain, "_get_maindomain", return_value=main_domain
), patch.object(domain, "domain_list", return_value={"domains": domains}):
), patch.object(domain, "domain_list", return_value={"domains": domains}), \
patch.object(os, "isatty", return_value=True):
expected_result = OrderedDict({"some_domain": (main_domain, "domain")})
with patch.object(Moulinette.interface, "prompt", return_value=main_domain):
with patch.object(Moulinette, "prompt", return_value=main_domain):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
expected_result = OrderedDict({"some_domain": (other_domain, "domain")})
with patch.object(Moulinette.interface, "prompt", return_value=other_domain):
with patch.object(Moulinette, "prompt", return_value=other_domain):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -1243,7 +1346,8 @@ def test_question_user_empty():
answers = {}
with patch.object(user, "user_list", return_value={"users": users}):
with pytest.raises(YunohostError):
with pytest.raises(YunohostError), \
patch.object(os, "isatty", return_value=False):
parse_args_in_yunohost_format(answers, questions)
@ -1269,8 +1373,8 @@ def test_question_user():
expected_result = OrderedDict({"some_user": (username, "user")})
with patch.object(user, "user_list", return_value={"users": users}):
with patch.object(user, "user_info", return_value={}):
with patch.object(user, "user_list", return_value={"users": users}), \
patch.object(user, "user_info", return_value={}):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -1303,15 +1407,15 @@ def test_question_user_two_users():
answers = {"some_user": other_user}
expected_result = OrderedDict({"some_user": (other_user, "user")})
with patch.object(user, "user_list", return_value={"users": users}):
with patch.object(user, "user_info", return_value={}):
with patch.object(user, "user_list", return_value={"users": users}), \
patch.object(user, "user_info", return_value={}):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
answers = {"some_user": username}
expected_result = OrderedDict({"some_user": (username, "user")})
with patch.object(user, "user_list", return_value={"users": users}):
with patch.object(user, "user_info", return_value={}):
with patch.object(user, "user_list", return_value={"users": users}), \
patch.object(user, "user_info", return_value={}):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -1344,7 +1448,8 @@ def test_question_user_two_users_wrong_answer():
answers = {"some_user": "doesnt_exist.pouet"}
with patch.object(user, "user_list", return_value={"users": users}):
with pytest.raises(YunohostError):
with pytest.raises(YunohostError), \
patch.object(os, "isatty", return_value=False):
parse_args_in_yunohost_format(answers, questions)
@ -1372,7 +1477,8 @@ def test_question_user_two_users_no_default():
answers = {}
with patch.object(user, "user_list", return_value={"users": users}):
with pytest.raises(YunohostError):
with pytest.raises(YunohostError), \
patch.object(os, "isatty", return_value=False):
parse_args_in_yunohost_format(answers, questions)
@ -1399,17 +1505,18 @@ def test_question_user_two_users_default_input():
questions = [{"name": "some_user", "type": "user", "ask": "choose a user"}]
answers = {}
with patch.object(user, "user_list", return_value={"users": users}):
with patch.object(user, "user_list", return_value={"users": users}), \
patch.object(os, "isatty", return_value=True):
with patch.object(user, "user_info", return_value={}):
expected_result = OrderedDict({"some_user": (username, "user")})
with patch.object(Moulinette.interface, "prompt", return_value=username):
with patch.object(Moulinette, "prompt", return_value=username):
assert (
parse_args_in_yunohost_format(answers, questions)
== expected_result
)
expected_result = OrderedDict({"some_user": (other_user, "user")})
with patch.object(Moulinette.interface, "prompt", return_value=other_user):
with patch.object(Moulinette, "prompt", return_value=other_user):
assert (
parse_args_in_yunohost_format(answers, questions)
== expected_result
@ -1437,8 +1544,9 @@ def test_question_number_no_input():
]
answers = {}
expected_result = OrderedDict({"some_number": (0, "number")})
assert parse_args_in_yunohost_format(answers, questions) == expected_result
with pytest.raises(YunohostError), \
patch.object(os, "isatty", return_value=False):
parse_args_in_yunohost_format(answers, questions)
def test_question_number_bad_input():
@ -1450,11 +1558,13 @@ def test_question_number_bad_input():
]
answers = {"some_number": "stuff"}
with pytest.raises(YunohostError):
with pytest.raises(YunohostError), \
patch.object(os, "isatty", return_value=False):
parse_args_in_yunohost_format(answers, questions)
answers = {"some_number": 1.5}
with pytest.raises(YunohostError):
with pytest.raises(YunohostError), \
patch.object(os, "isatty", return_value=False):
parse_args_in_yunohost_format(answers, questions)
@ -1469,14 +1579,17 @@ def test_question_number_input():
answers = {}
expected_result = OrderedDict({"some_number": (1337, "number")})
with patch.object(Moulinette.interface, "prompt", return_value="1337"):
with patch.object(Moulinette, "prompt", return_value="1337"), \
patch.object(os, "isatty", return_value=True):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
with patch.object(Moulinette.interface, "prompt", return_value=1337):
with patch.object(Moulinette, "prompt", return_value=1337), \
patch.object(os, "isatty", return_value=True):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
expected_result = OrderedDict({"some_number": (0, "number")})
with patch.object(Moulinette.interface, "prompt", return_value=""):
with patch.object(Moulinette, "prompt", return_value="0"), \
patch.object(os, "isatty", return_value=True):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -1490,7 +1603,8 @@ def test_question_number_input_no_ask():
answers = {}
expected_result = OrderedDict({"some_number": (1337, "number")})
with patch.object(Moulinette.interface, "prompt", return_value="1337"):
with patch.object(Moulinette, "prompt", return_value="1337"), \
patch.object(os, "isatty", return_value=True):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -1503,7 +1617,8 @@ def test_question_number_no_input_optional():
}
]
answers = {}
expected_result = OrderedDict({"some_number": (0, "number")}) # default to 0
expected_result = OrderedDict({"some_number": (None, "number")}) # default to 0
with patch.object(os, "isatty", return_value=False):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -1519,7 +1634,8 @@ def test_question_number_optional_with_input():
answers = {}
expected_result = OrderedDict({"some_number": (1337, "number")})
with patch.object(Moulinette.interface, "prompt", return_value="1337"):
with patch.object(Moulinette, "prompt", return_value="1337"), \
patch.object(os, "isatty", return_value=True):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -1534,7 +1650,8 @@ def test_question_number_optional_with_input_without_ask():
answers = {}
expected_result = OrderedDict({"some_number": (0, "number")})
with patch.object(Moulinette.interface, "prompt", return_value="0"):
with patch.object(Moulinette, "prompt", return_value="0"), \
patch.object(os, "isatty", return_value=True):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -1549,6 +1666,7 @@ def test_question_number_no_input_default():
]
answers = {}
expected_result = OrderedDict({"some_number": (1337, "number")})
with patch.object(os, "isatty", return_value=False):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@ -1562,7 +1680,8 @@ def test_question_number_bad_default():
}
]
answers = {}
with pytest.raises(YunohostError):
with pytest.raises(YunohostError), \
patch.object(os, "isatty", return_value=False):
parse_args_in_yunohost_format(answers, questions)
@ -1577,9 +1696,14 @@ def test_question_number_input_test_ask():
]
answers = {}
with patch.object(Moulinette.interface, "prompt", return_value="1111") as prompt:
with patch.object(Moulinette, "prompt", return_value="1111") as prompt, \
patch.object(os, "isatty", return_value=True):
parse_args_in_yunohost_format(answers, questions)
prompt.assert_called_with("%s (default: 0)" % (ask_text), False)
prompt.assert_called_with(
message=ask_text,
is_password=False, confirm=False,
prefill='', is_multiline=False
)
def test_question_number_input_test_ask_with_default():
@ -1595,9 +1719,14 @@ def test_question_number_input_test_ask_with_default():
]
answers = {}
with patch.object(Moulinette.interface, "prompt", return_value="1111") as prompt:
with patch.object(Moulinette, "prompt", return_value="1111") as prompt, \
patch.object(os, "isatty", return_value=True):
parse_args_in_yunohost_format(answers, questions)
prompt.assert_called_with("%s (default: %s)" % (ask_text, default_value), False)
prompt.assert_called_with(
message=ask_text,
is_password=False, confirm=False,
prefill=str(default_value), is_multiline=False
)
@pytest.mark.skip # we should do something with this example
@ -1614,10 +1743,11 @@ def test_question_number_input_test_ask_with_example():
]
answers = {}
with patch.object(Moulinette.interface, "prompt", return_value="1111") as prompt:
with patch.object(Moulinette, "prompt", return_value="1111") as prompt, \
patch.object(os, "isatty", return_value=True):
parse_args_in_yunohost_format(answers, questions)
assert ask_text in prompt.call_args[0][0]
assert example_value in prompt.call_args[0][0]
assert ask_text in prompt.call_args[1]['message']
assert example_value in prompt.call_args[1]['message']
@pytest.mark.skip # we should do something with this help
@ -1634,16 +1764,18 @@ def test_question_number_input_test_ask_with_help():
]
answers = {}
with patch.object(Moulinette.interface, "prompt", return_value="1111") as prompt:
with patch.object(Moulinette, "prompt", return_value="1111") as prompt, \
patch.object(os, "isatty", return_value=True):
parse_args_in_yunohost_format(answers, questions)
assert ask_text in prompt.call_args[0][0]
assert help_value in prompt.call_args[0][0]
assert ask_text in prompt.call_args[1]['message']
assert help_value in prompt.call_args[1]['message']
def test_question_display_text():
questions = [{"name": "some_app", "type": "display_text", "ask": "foobar"}]
answers = {}
with patch.object(sys, "stdout", new_callable=StringIO) as stdout:
with patch.object(sys, "stdout", new_callable=StringIO) as stdout, \
patch.object(os, "isatty", return_value=True):
parse_args_in_yunohost_format(answers, questions)
assert "foobar" in stdout.getvalue()

View file

@ -403,9 +403,9 @@ class Question(object):
return value
def ask_if_needed(self):
while True:
for i in range(5):
# Display question if no value filled or if it's a readonly message
if Moulinette.interface.type == "cli":
if Moulinette.interface.type == "cli" and os.isatty(1):
text_for_user_input_in_cli = self._format_text_for_user_input_in_cli()
if getattr(self, "readonly", False):
Moulinette.display(text_for_user_input_in_cli)
@ -415,7 +415,7 @@ class Question(object):
if self.current_value is not None:
prefill = self.humanize(self.current_value, self)
elif self.default is not None:
prefill = self.default
prefill = self.humanize(self.default, self)
self.value = Moulinette.prompt(
message=text_for_user_input_in_cli,
is_password=self.hide_user_input_in_prompt,
@ -424,27 +424,33 @@ class Question(object):
is_multiline=(self.type == "text"),
)
# Normalization
# This is done to enforce a certain formating like for boolean
self.value = self.normalize(self.value, self)
# Apply default value
if self.value in [None, ""] and self.default is not None:
class_default= getattr(self, "default_value", None)
if self.value in [None, ""] and \
(self.default is not None or class_default is not None):
self.value = (
getattr(self, "default_value", None)
class_default
if self.default is None
else self.default
)
# Normalization
# This is done to enforce a certain formating like for boolean
self.value = self.normalize(self.value, self)
# Prevalidation
try:
self._prevalidate()
except YunohostValidationError as e:
if Moulinette.interface.type == "api":
raise
Moulinette.display(str(e), "error")
# If in interactive cli, re-ask the current question
if i < 4 and Moulinette.interface.type == "cli" and os.isatty(1):
logger.error(str(e))
self.value = None
continue
# Otherwise raise the ValidationError
raise
break
self.value = self._post_parse_value()
@ -561,7 +567,7 @@ class PasswordQuestion(Question):
def _prevalidate(self):
super()._prevalidate()
if self.value is not None:
if self.value not in [None, ""]:
if any(char in self.value for char in self.forbidden_chars):
raise YunohostValidationError(
"pattern_password_app", forbidden_chars=self.forbidden_chars
@ -580,7 +586,7 @@ class PathQuestion(Question):
class BooleanQuestion(Question):
argument_type = "boolean"
default_value = False
default_value = 0
yes_answers = ["1", "yes", "y", "true", "t", "on"]
no_answers = ["0", "no", "n", "false", "f", "off"]
@ -633,17 +639,13 @@ class BooleanQuestion(Question):
self.yes = question.get("yes", 1)
self.no = question.get("no", 0)
if self.default is None:
self.default = False
self.default = self.no
def _format_text_for_user_input_in_cli(self):
text_for_user_input_in_cli = _value_for_locale(self.ask)
text_for_user_input_in_cli = super()._format_text_for_user_input_in_cli()
text_for_user_input_in_cli += " [yes | no]"
if self.default is not None:
formatted_default = self.humanize(self.default)
text_for_user_input_in_cli += " (default: {0})".format(formatted_default)
return text_for_user_input_in_cli
def get(self, key, default=None):
@ -698,11 +700,7 @@ class UserQuestion(Question):
class NumberQuestion(Question):
argument_type = "number"
default_value = ""
@staticmethod
def humanize(value, option={}):
return str(value)
default_value = None
def __init__(self, question, user_answers):
super().__init__(question, user_answers)
@ -710,16 +708,25 @@ class NumberQuestion(Question):
self.max = question.get("max", None)
self.step = question.get("step", None)
@staticmethod
def normalize(value, option={}):
if isinstance(value, int):
return value
if isinstance(value, str) and value.isdigit():
return int(value)
if value in [None, ""]:
return value
raise YunohostValidationError(
"app_argument_invalid", name=option.name, error=m18n.n("invalid_number")
)
def _prevalidate(self):
super()._prevalidate()
if not isinstance(self.value, int) and not (
isinstance(self.value, str) and self.value.isdigit()
):
raise YunohostValidationError(
"app_argument_invalid",
name=self.name,
error=m18n.n("invalid_number"),
)
if self.value in [None, ""]:
return
if self.min is not None and int(self.value) < self.min:
raise YunohostValidationError(
@ -735,16 +742,6 @@ class NumberQuestion(Question):
error=m18n.n("invalid_number"),
)
def _post_parse_value(self):
if isinstance(self.value, int):
return super()._post_parse_value()
if isinstance(self.value, str) and self.value.isdigit():
return int(self.value)
raise YunohostValidationError(
"app_argument_invalid", name=self.name, error=m18n.n("invalid_number")
)
class DisplayTextQuestion(Question):
@ -755,10 +752,10 @@ class DisplayTextQuestion(Question):
super().__init__(question, user_answers)
self.optional = True
self.style = question.get("style", "info")
self.style = question.get("style", "info" if question['type'] == 'alert' else '')
def _format_text_for_user_input_in_cli(self):
text = self.ask["en"]
text = _value_for_locale(self.ask)
if self.style in ["success", "info", "warning", "danger"]:
color = {