From c428ba616a3f2d63a22257560345429f0af29729 Mon Sep 17 00:00:00 2001 From: axolotle Date: Mon, 17 Apr 2023 15:37:56 +0200 Subject: [PATCH] test:options: update tests results to pydantic parsing --- src/tests/test_questions.py | 374 +++++++++++++++++++----------------- 1 file changed, 197 insertions(+), 177 deletions(-) diff --git a/src/tests/test_questions.py b/src/tests/test_questions.py index 9eceedcaf..1f8667adf 100644 --- a/src/tests/test_questions.py +++ b/src/tests/test_questions.py @@ -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@yy--0.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@yy--0.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}"