test:options: update tests results to pydantic parsing

This commit is contained in:
axolotle 2023-04-17 15:37:56 +02:00
parent ec5da99a79
commit c428ba616a

View file

@ -20,7 +20,6 @@ from yunohost.utils.form import (
BaseChoicesOption,
BaseInputOption,
BaseReadonlyOption,
PasswordOption,
DomainOption,
WebPathOption,
BooleanOption,
@ -436,6 +435,10 @@ class BaseTest:
@classmethod
def _test_basic_attrs(self):
raw_option = self.get_raw_option(optional=True)
if raw_option["type"] == "select":
raw_option["choices"] = ["one"]
id_ = raw_option["id"]
option, value = _fill_or_prompt_one_option(raw_option, None)
@ -481,6 +484,7 @@ class BaseTest:
base_raw_option = prefill_data["raw_option"]
prefill = prefill_data["prefill"]
# FIXME could patch prompt with prefill if we switch to "do not apply default if value is None|''"
with patch_prompt("") as prompt:
raw_option = self.get_raw_option(
raw_option=base_raw_option,
@ -583,9 +587,7 @@ class TestAlert(TestDisplayText):
(None, None, {"ask": "Some text\na new line"}),
(None, None, {"ask": {"en": "Some text\na new line", "fr": "Un peu de texte\nune nouvelle ligne"}}),
*[(None, None, {"ask": "question", "style": style}) for style in ("success", "info", "warning", "danger")],
*xpass(scenarios=[
(None, None, {"ask": "question", "style": "nimp"}),
], reason="Should fail, wrong style"),
(None, FAIL, {"ask": "question", "style": "nimp"}),
]
# fmt: on
@ -643,11 +645,15 @@ class TestString(BaseTest):
scenarios = [
*nones(None, "", output=""),
# basic typed values
*unchanged(False, True, 0, 1, -1, 1337, 13.37, [], ["one"], {}, raw_option={"optional": True}), # FIXME should output as str?
(False, "False"),
(True, "True"),
(0, "0"),
(1, "1"),
(-1, "-1"),
(1337, "1337"),
(13.37, "13.37"),
*all_fails([], ["one"], {}),
*unchanged("none", "_none", "False", "True", "0", "1", "-1", "1337", "13.37", "[]", ",", "['one']", "one,two", r"{}", "value", raw_option={"optional": True}),
*xpass(scenarios=[
([], []),
], reason="Should fail"),
# test strip
("value", "value"),
("value\n", "value"),
@ -660,7 +666,7 @@ class TestString(BaseTest):
(" ##value \n \tvalue\n ", "##value \n \tvalue"),
], reason=r"should fail or without `\n`?"),
# readonly
("overwrite", "expected value", {"readonly": True, "current_value": "expected value"}),
("overwrite", "expected value", {"readonly": True, "default": "expected value"}), # FIXME do we want to fail instead?
]
# fmt: on
@ -680,11 +686,15 @@ class TestText(BaseTest):
scenarios = [
*nones(None, "", output=""),
# basic typed values
*unchanged(False, True, 0, 1, -1, 1337, 13.37, [], ["one"], {}, raw_option={"optional": True}), # FIXME should fail or output as str?
(False, "False"),
(True, "True"),
(0, "0"),
(1, "1"),
(-1, "-1"),
(1337, "1337"),
(13.37, "13.37"),
*all_fails([], ["one"], {}),
*unchanged("none", "_none", "False", "True", "0", "1", "-1", "1337", "13.37", "[]", ",", "['one']", "one,two", r"{}", "value", raw_option={"optional": True}),
*xpass(scenarios=[
([], [])
], reason="Should fail"),
("value", "value"),
("value\n value", "value\n value"),
# test no strip
@ -697,7 +707,7 @@ class TestText(BaseTest):
(r" ##value \n \tvalue\n ", r"##value \n \tvalue\n"),
], reason="Should not be stripped"),
# readonly
("overwrite", "expected value", {"readonly": True, "current_value": "expected value"}),
("overwrite", "expected value", {"readonly": True, "default": "expected value"}),
]
# fmt: on
@ -715,7 +725,7 @@ class TestPassword(BaseTest):
}
# fmt: off
scenarios = [
*all_fails(False, True, 0, 1, -1, 1337, 13.37, raw_option={"optional": True}, error=TypeError), # FIXME those fails with TypeError
*all_fails(False, True, 0, 1, -1, 1337, 13.37, raw_option={"optional": True}),
*all_fails([], ["one"], {}, raw_option={"optional": True}, error=AttributeError), # FIXME those fails with AttributeError
*all_fails("none", "_none", "False", "True", "0", "1", "-1", "1337", "13.37", "[]", ",", "['one']", "one,two", r"{}", "value", "value\n", raw_option={"optional": True}),
*nones(None, "", output=""),
@ -729,9 +739,9 @@ class TestPassword(BaseTest):
], reason="Should output exactly the same"),
("s3cr3t!!", "s3cr3t!!"),
("secret", FAIL),
*[("supersecret" + char, FAIL) for char in PasswordOption.forbidden_chars], # FIXME maybe add ` \n` to the list?
*[("supersecret" + char, FAIL) for char in FORBIDDEN_PASSWORD_CHARS], # FIXME maybe add ` \n` to the list?
# readonly
("s3cr3t!!", YunohostError, {"readonly": True, "current_value": "isforbidden"}), # readonly is forbidden
("s3cr3t!!", FAIL, {"readonly": True, "current_value": "isforbidden"}), # readonly is forbidden
]
# fmt: on
@ -744,35 +754,31 @@ class TestPassword(BaseTest):
class TestColor(BaseTest):
raw_option = {"type": "color", "id": "color_id"}
prefill = {
"raw_option": {"default": "#ff0000"},
"prefill": "#ff0000",
# "intake": "#ff00ff",
"raw_option": {"default": "red"},
"prefill": "red",
}
# fmt: off
scenarios = [
*all_fails(False, True, 0, 1, -1, 1337, 13.37, [], ["one"], {}, raw_option={"optional": True}),
*all_fails("none", "_none", "False", "True", "0", "1", "-1", "1337", "13.37", "[]", ",", "['one']", "one,two", r"{}", "value", "value\n", raw_option={"optional": True}),
*all_fails("none", "_none", "False", "True", "0", "1", "-1", "13.37", "[]", ",", "['one']", "one,two", r"{}", "value", "value\n", raw_option={"optional": True}),
*nones(None, "", output=""),
# custom valid
("#000000", "#000000"),
(" #fe1 ", "#fe1"),
("#000000", "#000"),
("#000", "#000"),
("#fe100", "#fe100"),
(" #fe100 ", "#fe100"),
("#ABCDEF", "#ABCDEF"),
("#ABCDEF", "#abcdef"),
('1337', "#1337"), # rgba=(17, 51, 51, 0.47)
("000000", "#000"),
("#feaf", "#fea"), # `#feaf` is `#fea` with alpha at `f|100%` -> equivalent to `#fea`
# named
("red", "#f00"),
("yellow", "#ff0"),
# custom fail
*xpass(scenarios=[
("#feaf", "#feaf"),
], reason="Should fail; not a legal color value"),
("000000", FAIL),
("#12", FAIL),
("#gggggg", FAIL),
("#01010101af", FAIL),
*xfail(scenarios=[
("red", "#ff0000"),
("yellow", "#ffff00"),
], reason="Should work with pydantic"),
# readonly
("#ffff00", "#fe100", {"readonly": True, "current_value": "#fe100"}),
("#ffff00", "#000", {"readonly": True, "default": "#000"}),
]
# fmt: on
@ -796,10 +802,8 @@ class TestNumber(BaseTest):
*nones(None, "", output=None),
*unchanged(0, 1, -1, 1337),
*xpass(scenarios=[(False, False)], reason="should fail or output as `0`"),
*xpass(scenarios=[(True, True)], reason="should fail or output as `1`"),
*all_as("0", 0, output=0),
*all_as("1", 1, output=1),
*all_as(False, "0", 0, output=0), # FIXME should `False` fail instead?
*all_as(True, "1", 1, output=1), # FIXME should `True` fail instead?
*all_as("1337", 1337, output=1337),
*xfail(scenarios=[
("-1", -1)
@ -814,7 +818,7 @@ class TestNumber(BaseTest):
(-10, -10, {"default": 10}),
(-10, -10, {"default": 10, "optional": True}),
# readonly
(1337, 10000, {"readonly": True, "current_value": 10000}),
(1337, 10000, {"readonly": True, "default": "10000"}),
]
# fmt: on
# FIXME should `step` be some kind of "multiple of"?
@ -839,14 +843,20 @@ class TestBoolean(BaseTest):
*all_fails("none", "None"), # FIXME should output as `0` (default) like other none values when required?
*all_as(None, "", output=0, raw_option={"optional": True}), # FIXME should output as `None`?
*all_as("none", "None", output=None, raw_option={"optional": True}),
# FIXME even if default is explicity `None|""`, it ends up with class_default `0`
*all_as(None, "", output=0, raw_option={"default": None}), # FIXME this should fail, default is `None`
*all_as(None, "", output=0, raw_option={"optional": True, "default": None}), # FIXME even if default is explicity None, it ends up with class_default
*all_as(None, "", output=0, raw_option={"default": ""}), # FIXME this should fail, default is `""`
*all_as(None, "", output=0, raw_option={"optional": True, "default": ""}), # FIXME even if default is explicity None, it ends up with class_default
# With "none" behavior is ok
*all_fails(None, "", raw_option={"default": "none"}),
*all_as(None, "", output=None, raw_option={"optional": True, "default": "none"}),
{
"raw_options": [
{"default": None},
{"default": ""},
{"default": "none"},
{"default": "None"}
],
"scenarios": [
# All none values fails if default is overriden
*all_fails(None, "", "none", "None"),
# All none values ends up as None if default is overriden
*all_as(None, "", "none", "None", output=None, raw_option={"optional": True}),
]
},
# Unhandled types should fail
*all_fails(1337, "1337", "string", [], "[]", ",", "one,two"),
*all_fails(1337, "1337", "string", [], "[]", ",", "one,two", {"optional": True}),
@ -879,7 +889,7 @@ class TestBoolean(BaseTest):
"scenarios": all_fails("", "y", "n", error=AssertionError),
},
# readonly
(1, 0, {"readonly": True, "current_value": 0}),
(1, 0, {"readonly": True, "default": 0}),
]
@ -896,8 +906,12 @@ class TestDate(BaseTest):
}
# fmt: off
scenarios = [
*all_fails(False, True, 0, 1, -1, 1337, 13.37, [], ["one"], {}, raw_option={"optional": True}),
*all_fails("none", "_none", "False", "True", "0", "1", "-1", "1337", "13.37", "[]", ",", "['one']", "one,two", r"{}", "value", "value\n", raw_option={"optional": True}),
# Those passes since False|True are parsed as 0|1 then int|float are considered a timestamp in seconds which ends up as default Unix date
*all_as(False, True, 0, 1, 1337, 13.37, "0", "1", "1337", "13.37", output="1970-01-01"),
# Those are negative one second timestamp ending up as Unix date - 1 sec (so day change)
*all_as(-1, "-1", output="1969-12-31"),
*all_fails([], ["one"], {}, raw_option={"optional": True}),
*all_fails("none", "_none", "False", "True", "[]", ",", "['one']", "one,two", r"{}", "value", "value\n", raw_option={"optional": True}),
*nones(None, "", output=""),
# custom valid
("2070-12-31", "2070-12-31"),
@ -906,18 +920,16 @@ class TestDate(BaseTest):
("2025-06-15T13:45:30", "2025-06-15"),
("2025-06-15 13:45:30", "2025-06-15")
], reason="iso date repr should be valid and extra data striped"),
*xfail(scenarios=[
(1749938400, "2025-06-15"),
(1749938400.0, "2025-06-15"),
("1749938400", "2025-06-15"),
("1749938400.0", "2025-06-15"),
], reason="timestamp could be an accepted value"),
(1749938400, "2025-06-14"),
(1749938400.0, "2025-06-14"),
("1749938400", "2025-06-14"),
("1749938400.0", "2025-06-14"),
# custom invalid
("29-12-2070", FAIL),
("12-01-10", FAIL),
("2022-02-29", FAIL),
# readonly
("2070-12-31", "2024-02-29", {"readonly": True, "current_value": "2024-02-29"}),
("2070-12-31", "2024-02-29", {"readonly": True, "default": "2024-02-29"}),
]
# fmt: on
@ -935,22 +947,26 @@ class TestTime(BaseTest):
}
# fmt: off
scenarios = [
*all_fails(False, True, 0, 1, -1, 1337, 13.37, [], ["one"], {}, raw_option={"optional": True}),
*all_fails("none", "_none", "False", "True", "0", "1", "-1", "1337", "13.37", "[]", ",", "['one']", "one,two", r"{}", "value", "value\n", raw_option={"optional": True}),
# Those passes since False|True are parsed as 0|1 then int|float are considered a timestamp in seconds but we don't take seconds into account so -> 00:00
*all_as(False, True, 0, 1, 13.37, "0", "1", "13.37", output="00:00"),
# 1337 seconds == 22 minutes
*all_as(1337, "1337", output="00:22"),
# Negative timestamp fails
*all_fails(-1, "-1", error=OverflowError), # FIXME should handle that as a validation error
# *all_fails(False, True, 0, 1, -1, 1337, 13.37, [], ["one"], {}, raw_option={"optional": True}),
*all_fails("none", "_none", "False", "True", "[]", ",", "['one']", "one,two", r"{}", "value", "value\n", raw_option={"optional": True}),
*nones(None, "", output=""),
# custom valid
*unchanged("00:00", "08:00", "12:19", "20:59", "23:59"),
("3:00", "3:00"), # FIXME should fail or output as `"03:00"`?
*xfail(scenarios=[
("22:35:05", "22:35"),
("22:35:03.514", "22:35"),
], reason="time as iso format could be valid"),
("3:00", "03:00"),
("23:1", "23:01"),
("22:35:05", "22:35"),
("22:35:03.514", "22:35"),
# custom invalid
("24:00", FAIL),
("23:1", FAIL),
("23:005", FAIL),
# readonly
("00:00", "08:00", {"readonly": True, "current_value": "08:00"}),
("00:00", "08:00", {"readonly": True, "default": "08:00"}),
]
# fmt: on
@ -973,72 +989,75 @@ class TestEmail(BaseTest):
*nones(None, "", output=""),
("\n Abc@example.tld ", "Abc@example.tld"),
*xfail(scenarios=[("admin@ynh.local", "admin@ynh.local")], reason="Should this pass?"),
# readonly
("Abc@example.tld", "admin@ynh.local", {"readonly": True, "current_value": "admin@ynh.local"}),
("Abc@example.tld", "admin@ynh.org", {"readonly": True, "default": "admin@ynh.org"}),
# Next examples are from https://github.com/JoshData/python-email-validator/blob/main/tests/test_syntax.py
# valid email values
("Abc@example.tld", "Abc@example.tld"),
("Abc.123@test-example.com", "Abc.123@test-example.com"),
("user+mailbox/department=shipping@example.tld", "user+mailbox/department=shipping@example.tld"),
("伊昭傑@郵件.商務", "伊昭傑@郵件.商務"),
("राम@मोहन.ईन्फो", "राम@मोहन.ईन्फो"),
("юзер@екзампл.ком", "юзер@екзампл.ком"),
("θσερ@εχαμπλε.ψομ", "θσερ@εχαμπλε.ψομ"),
("葉士豪@臺網中心.tw", "葉士豪@臺網中心.tw"),
("jeff@臺網中心.tw", "jeff@臺網中心.tw"),
("葉士豪@臺網中心.台灣", "葉士豪@臺網中心.台灣"),
("jeff葉@臺網中心.tw", "jeff葉@臺網中心.tw"),
("ñoñó@example.tld", "ñoñó@example.tld"),
("甲斐黒川日本@example.tld", "甲斐黒川日本@example.tld"),
("чебурашкаящик-с-апельсинами.рф@example.tld", "чебурашкаящик-с-апельсинами.рф@example.tld"),
("उदाहरण.परीक्ष@domain.with.idn.tld", "उदाहरण.परीक्ष@domain.with.idn.tld"),
("ιωάννης@εεττ.gr", "ιωάννης@εεττ.gr"),
*unchanged(
"Abc@example.tld",
"Abc.123@test-example.com",
"user+mailbox/department=shipping@example.tld",
"伊昭傑@郵件.商務",
"राम@मोहन.ईन्फो",
"юзер@екзампл.ком",
"θσερ@εχαμπλε.ψομ",
"葉士豪@臺網中心.tw",
"jeff@臺網中心.tw",
"葉士豪@臺網中心.台灣",
"jeff葉@臺網中心.tw",
"ñoñó@example.tld",
"甲斐黒川日本@example.tld",
"чебурашкаящик-с-апельсинами.рф@example.tld",
"उदाहरण.परीक्ष@domain.with.idn.tld",
"ιωάννης@εεττ.gr",
),
# invalid email (Hiding because our current regex is very permissive)
# ("my@localhost", FAIL),
# ("my@.leadingdot.com", FAIL),
# ("my@leadingfwdot.com", FAIL),
# ("my@twodots..com", FAIL),
# ("my@twofwdots.com", FAIL),
# ("my@trailingdot.com.", FAIL),
# ("my@trailingfwdot.com", FAIL),
# ("me@-leadingdash", FAIL),
# ("me@leadingdashfw", FAIL),
# ("me@trailingdash-", FAIL),
# ("me@trailingdashfw", FAIL),
# ("my@baddash.-.com", FAIL),
# ("my@baddash.-a.com", FAIL),
# ("my@baddash.b-.com", FAIL),
# ("my@baddashfw..com", FAIL),
# ("my@baddashfw.a.com", FAIL),
# ("my@baddashfw.b.com", FAIL),
# ("my@example.com\n", FAIL),
# ("my@example\n.com", FAIL),
# ("me@x!", FAIL),
# ("me@x ", FAIL),
# (".leadingdot@domain.com", FAIL),
# ("twodots..here@domain.com", FAIL),
# ("trailingdot.@domain.email", FAIL),
# ("me@⒈wouldbeinvalid.com", FAIL),
("@example.com", FAIL),
# ("\nmy@example.com", FAIL),
("m\ny@example.com", FAIL),
("my\n@example.com", FAIL),
# ("11111111112222222222333333333344444444445555555555666666666677777@example.com", FAIL),
# ("111111111122222222223333333333444444444455555555556666666666777777@example.com", FAIL),
# ("me@1111111111222222222233333333334444444444555555555.6666666666777777777788888888889999999999000000000.1111111111222222222233333333334444444444555555555.6666666666777777777788888888889999999999000000000.111111111122222222223333333333444444444455555555556.com", FAIL),
# ("me@1111111111222222222233333333334444444444555555555.6666666666777777777788888888889999999999000000000.1111111111222222222233333333334444444444555555555.6666666666777777777788888888889999999999000000000.1111111111222222222233333333334444444444555555555566.com", FAIL),
# ("me@中1111111111222222222233333333334444444444555555555.6666666666777777777788888888889999999999000000000.1111111111222222222233333333334444444444555555555.6666666666777777777788888888889999999999000000000.1111111111222222222233333333334444444444555555555566.com", FAIL),
# ("my.long.address@1111111111222222222233333333334444444444555555555.6666666666777777777788888888889999999999000000000.1111111111222222222233333333334444444444555555555.6666666666777777777788888888889999999999000000000.11111111112222222222333333333344444.info", FAIL),
# ("my.long.address@λ111111111222222222233333333334444444444555555555.6666666666777777777788888888889999999999000000000.1111111111222222222233333333334444444444555555555.6666666666777777777788888888889999999999000000000.11111111112222222222333333.info", FAIL),
# ("my.long.address@λ111111111222222222233333333334444444444555555555.6666666666777777777788888888889999999999000000000.1111111111222222222233333333334444444444555555555.6666666666777777777788888888889999999999000000000.1111111111222222222233333333334444.info", FAIL),
# ("my.λong.address@1111111111222222222233333333334444444444555555555.6666666666777777777788888888889999999999000000000.1111111111222222222233333333334444444444555555555.6666666666777777777788888888889999999999000000000.111111111122222222223333333333444.info", FAIL),
# ("my.λong.address@1111111111222222222233333333334444444444555555555.6666666666777777777788888888889999999999000000000.1111111111222222222233333333334444444444555555555.6666666666777777777788888888889999999999000000000.1111111111222222222233333333334444.info", FAIL),
# ("me@bad-tld-1", FAIL),
# ("me@bad.tld-2", FAIL),
# ("me@xn--0.tld", FAIL),
# ("me@yy--0.tld", FAIL),
# ("me@yy0.tld", FAIL),
*all_fails(
"my@localhost",
"my@.leadingdot.com",
"my@leadingfwdot.com",
"my@twodots..com",
"my@twofwdots.com",
"my@trailingdot.com.",
"my@trailingfwdot.com",
"me@-leadingdash",
"me@leadingdashfw",
"me@trailingdash-",
"me@trailingdashfw",
"my@baddash.-.com",
"my@baddash.-a.com",
"my@baddash.b-.com",
"my@baddashfw..com",
"my@baddashfw.a.com",
"my@baddashfw.b.com",
"my@example\n.com",
"me@x!",
"me@x ",
".leadingdot@domain.com",
"twodots..here@domain.com",
"trailingdot.@domain.email",
"me@⒈wouldbeinvalid.com",
"@example.com",
"m\ny@example.com",
"my\n@example.com",
"11111111112222222222333333333344444444445555555555666666666677777@example.com",
"111111111122222222223333333333444444444455555555556666666666777777@example.com",
"me@1111111111222222222233333333334444444444555555555.6666666666777777777788888888889999999999000000000.1111111111222222222233333333334444444444555555555.6666666666777777777788888888889999999999000000000.111111111122222222223333333333444444444455555555556.com",
"me@1111111111222222222233333333334444444444555555555.6666666666777777777788888888889999999999000000000.1111111111222222222233333333334444444444555555555.6666666666777777777788888888889999999999000000000.1111111111222222222233333333334444444444555555555566.com",
"me@中1111111111222222222233333333334444444444555555555.6666666666777777777788888888889999999999000000000.1111111111222222222233333333334444444444555555555.6666666666777777777788888888889999999999000000000.1111111111222222222233333333334444444444555555555566.com",
"my.long.address@1111111111222222222233333333334444444444555555555.6666666666777777777788888888889999999999000000000.1111111111222222222233333333334444444444555555555.6666666666777777777788888888889999999999000000000.11111111112222222222333333333344444.info",
"my.long.address@λ111111111222222222233333333334444444444555555555.6666666666777777777788888888889999999999000000000.1111111111222222222233333333334444444444555555555.6666666666777777777788888888889999999999000000000.11111111112222222222333333.info",
"my.long.address@λ111111111222222222233333333334444444444555555555.6666666666777777777788888888889999999999000000000.1111111111222222222233333333334444444444555555555.6666666666777777777788888888889999999999000000000.1111111111222222222233333333334444.info",
"my.λong.address@1111111111222222222233333333334444444444555555555.6666666666777777777788888888889999999999000000000.1111111111222222222233333333334444444444555555555.6666666666777777777788888888889999999999000000000.111111111122222222223333333333444.info",
"my.λong.address@1111111111222222222233333333334444444444555555555.6666666666777777777788888888889999999999000000000.1111111111222222222233333333334444444444555555555.6666666666777777777788888888889999999999000000000.1111111111222222222233333333334444.info",
"me@bad-tld-1",
"me@bad.tld-2",
"me@xn--0.tld",
"me@yy--0.tld",
"me@yy0.tld",
)
]
# fmt: on
@ -1087,7 +1106,7 @@ class TestWebPath(BaseTest):
("https://example.com/folder", "/https://example.com/folder")
], reason="Should fail or scheme+domain removed"),
# readonly
("/overwrite", "/value", {"readonly": True, "current_value": "/value"}),
("/overwrite", "/value", {"readonly": True, "default": "/value"}),
# FIXME should path have forbidden_chars?
]
# fmt: on
@ -1111,21 +1130,17 @@ class TestUrl(BaseTest):
*nones(None, "", output=""),
("http://some.org/folder/file.txt", "http://some.org/folder/file.txt"),
(' https://www.example.com \n', 'https://www.example.com'),
# readonly
("https://overwrite.org", "https://example.org", {"readonly": True, "current_value": "https://example.org"}),
("https://overwrite.org", "https://example.org", {"readonly": True, "default": "https://example.org"}),
# rest is taken from https://github.com/pydantic/pydantic/blob/main/tests/test_networks.py
# valid
*unchanged(
# Those are valid but not sure how they will output with pydantic
'http://example.org',
'http://test',
'http://localhost',
'https://example.org/whatever/next/',
'https://example.org',
'http://localhost',
'http://localhost/',
'http://localhost:8000',
'http://localhost:8000/',
'https://foo_bar.example.com/',
'http://example.co.jp',
'http://www.example.com/a%C2%B1b',
@ -1149,29 +1164,31 @@ class TestUrl(BaseTest):
'http://twitter.com/@handle/',
'http://11.11.11.11.example.com/action',
'http://abc.11.11.11.11.example.com/action',
'http://example#',
'http://example/#',
'http://example/#fragment',
'http://example/?#',
'http://example.org/path#',
'http://example.org/path#fragment',
'http://example.org/path?query#',
'http://example.org/path?query#fragment',
'https://foo_bar.example.com/',
'https://exam_ple.com/',
'HTTP://EXAMPLE.ORG',
'https://example.org',
'https://example.org?a=1&b=2',
'https://example.org#a=3;b=3',
'https://example.xn--p1ai',
'https://example.xn--vermgensberatung-pwb',
'https://example.xn--zfr164b',
),
# Pydantic default parsing add a final `/`
('https://foo_bar.example.com/', 'https://foo_bar.example.com/'),
('https://exam_ple.com/', 'https://exam_ple.com/'),
*xfail(scenarios=[
(' https://www.example.com \n', 'https://www.example.com/'),
('HTTP://EXAMPLE.ORG', 'http://example.org/'),
('https://example.org', 'https://example.org/'),
('https://example.org?a=1&b=2', 'https://example.org/?a=1&b=2'),
('https://example.org#a=3;b=3', 'https://example.org/#a=3;b=3'),
('https://example.xn--p1ai', 'https://example.xn--p1ai/'),
('https://example.xn--vermgensberatung-pwb', 'https://example.xn--vermgensberatung-pwb/'),
('https://example.xn--zfr164b', 'https://example.xn--zfr164b/'),
], reason="pydantic default behavior would append a final `/`"),
('http://test', 'http://test'),
('http://localhost', 'http://localhost'),
('http://localhost/', 'http://localhost/'),
('http://localhost:8000', 'http://localhost:8000'),
('http://localhost:8000/', 'http://localhost:8000/'),
('http://example#', 'http://example#'),
('http://example/#', 'http://example/#'),
('http://example/#fragment', 'http://example/#fragment'),
('http://example/?#', 'http://example/?#'),
], reason="Should this be valid?"),
# invalid
*all_fails(
'ftp://example.com/',
@ -1182,15 +1199,13 @@ class TestUrl(BaseTest):
"/",
"+http://example.com/",
"ht*tp://example.com/",
"http:///",
"http://??",
"https://example.org more",
"http://2001:db8::ff00:42:8329",
"http://[192.168.1.1]:8329",
"http://example.com:99999",
),
*xpass(scenarios=[
("http:///", "http:///"),
("http://??", "http://??"),
("https://example.org more", "https://example.org more"),
("http://2001:db8::ff00:42:8329", "http://2001:db8::ff00:42:8329"),
("http://[192.168.1.1]:8329", "http://[192.168.1.1]:8329"),
("http://example.com:99999", "http://example.com:99999"),
], reason="Should fail"),
]
# fmt: on
@ -1361,7 +1376,6 @@ class TestSelect(BaseTest):
# [-1, 0, 1]
"raw_options": [
{"choices": [-1, 0, 1, 10]},
{"choices": {-1: "verbose -one", 0: "verbose zero", 1: "verbose one", 10: "verbose ten"}},
],
"scenarios": [
*nones(None, "", output=""),
@ -1375,6 +1389,18 @@ class TestSelect(BaseTest):
*all_fails("100", 100),
]
},
{
"raw_options": [
{"choices": {-1: "verbose -one", 0: "verbose zero", 1: "verbose one", 10: "verbose ten"}},
{"choices": {"-1": "verbose -one", "0": "verbose zero", "1": "verbose one", "10": "verbose ten"}},
],
"scenarios": [
*nones(None, "", output=""),
*all_fails(-1, 0, 1, 10), # Should pass? converted to str?
*unchanged("-1", "0", "1", "10"),
*all_fails("100", 100),
]
},
# [True, False, None]
*unchanged(True, False, raw_option={"choices": [True, False, None]}), # FIXME we should probably forbid None in choices
(None, FAIL, {"choices": [True, False, None]}),
@ -1402,7 +1428,7 @@ class TestSelect(BaseTest):
]
},
# readonly
("one", "two", {"readonly": True, "choices": ["one", "two"], "current_value": "two"}),
("one", "two", {"readonly": True, "choices": ["one", "two"], "default": "two"}),
]
# fmt: on
@ -1411,7 +1437,7 @@ class TestSelect(BaseTest):
# │ TAGS │
# ╰───────────────────────────────────────────────────────╯
# [], ["one"], {}
class TestTags(BaseTest):
raw_option = {"type": "tags", "id": "tags_id"}
prefill = {
@ -1420,12 +1446,7 @@ class TestTags(BaseTest):
}
# fmt: off
scenarios = [
*nones(None, [], "", output=""),
# FIXME `","` could be considered a none value which kinda already is since it fail when required
(",", FAIL),
*xpass(scenarios=[
(",", ",", {"optional": True})
], reason="Should output as `''`? ie: None"),
*nones(None, [], "", ",", output=""),
{
"raw_options": [
{},
@ -1450,7 +1471,7 @@ class TestTags(BaseTest):
*all_fails(*([t] for t in [False, True, -1, 0, 1, 1337, 13.37, [], ["one"], {}]), raw_option={"choices": [False, True, -1, 0, 1, 1337, 13.37, [], ["one"], {}]}),
*all_fails(*([str(t)] for t in [False, True, -1, 0, 1, 1337, 13.37, [], ["one"], {}]), raw_option={"choices": [False, True, -1, 0, 1, 1337, 13.37, [], ["one"], {}]}),
# readonly
("one", "one,two", {"readonly": True, "choices": ["one", "two"], "current_value": "one,two"}),
("one", "one,two", {"readonly": True, "choices": ["one", "two"], "default": "one,two"}),
]
# fmt: on
@ -1566,8 +1587,7 @@ class TestApp(BaseTest):
],
"scenarios": [
# FIXME there are currently 3 different nones (`None`, `""` and `_none`), choose one?
*nones(None, output=None), # FIXME Should return chosen none?
*nones("", output=""), # FIXME Should return chosen none?
*nones(None, "", output=""), # FIXME Should return chosen none?
*xpass(scenarios=[
("_none", "_none"),
("_none", "_none", {"default": "_none"}),
@ -1590,7 +1610,7 @@ class TestApp(BaseTest):
(installed_webapp["id"], installed_webapp["id"], {"filter": "is_webapp"}),
(installed_webapp["id"], FAIL, {"filter": "is_webapp == false"}),
(installed_webapp["id"], FAIL, {"filter": "id != 'my_webapp'"}),
(None, None, {"filter": "id == 'fake_app'", "optional": True}),
(None, "", {"filter": "id == 'fake_app'", "optional": True}),
]
},
{
@ -1800,7 +1820,7 @@ class TestGroup(BaseTest):
("", "custom_group", {"default": "custom_group"}),
], reason="Should throw 'default must be in (None, 'all_users', 'visitors', 'admins')"),
# readonly
("admins", YunohostError, {"readonly": True}), # readonly is forbidden
("admins", "all_users", {"readonly": True}), # readonly is forbidden (default is "all_users")
]
},
]
@ -1880,12 +1900,12 @@ def test_options_query_string():
"string_id": "string",
"text_id": "text\ntext",
"password_id": "sUpRSCRT",
"color_id": "#ffff00",
"color_id": "#ff0",
"number_id": 10,
"boolean_id": 1,
"date_id": "2030-03-06",
"time_id": "20:55",
"email_id": "coucou@ynh.local",
"email_id": "coucou@ynh.org",
"path_id": "/ynh-dev",
"url_id": "https://yunohost.org",
"file_id": file_content1,
@ -1908,7 +1928,7 @@ def test_options_query_string():
"&boolean_id=y"
"&date_id=2030-03-06"
"&time_id=20:55"
"&email_id=coucou@ynh.local"
"&email_id=coucou@ynh.org"
"&path_id=ynh-dev/"
"&url_id=https://yunohost.org"
f"&file_id={file_repr}"