mirror of
https://github.com/dw-0/kiauh.git
synced 2026-08-03 04:47:56 +05:00
159 lines
5.8 KiB
Python
159 lines
5.8 KiB
Python
from __future__ import annotations
|
|
|
|
from typing import Any, List
|
|
|
|
import pytest
|
|
from utils.input_utils import (
|
|
format_question,
|
|
get_confirm,
|
|
get_number_input,
|
|
get_selection_input,
|
|
get_string_input,
|
|
validate_number_input,
|
|
)
|
|
|
|
|
|
def _input_sequence(answers: List[str]):
|
|
it = iter(answers)
|
|
|
|
def _input(_prompt: str = "") -> str:
|
|
return next(it)
|
|
|
|
return _input
|
|
|
|
|
|
class TestGetConfirm:
|
|
def test_accepts_yes(self, monkeypatch) -> None:
|
|
monkeypatch.setattr("builtins.input", _input_sequence(["y"]))
|
|
assert get_confirm("go?") is True
|
|
|
|
def test_accepts_no(self, monkeypatch) -> None:
|
|
monkeypatch.setattr("builtins.input", _input_sequence(["n"]))
|
|
assert get_confirm("go?") is False
|
|
|
|
def test_default_yes_on_empty(self, monkeypatch) -> None:
|
|
monkeypatch.setattr("builtins.input", _input_sequence([""]))
|
|
assert get_confirm("go?", default_choice=True) is True
|
|
|
|
def test_default_no_on_empty(self, monkeypatch) -> None:
|
|
monkeypatch.setattr("builtins.input", _input_sequence([""]))
|
|
assert get_confirm("go?", default_choice=False) is False
|
|
|
|
def test_handles_invalid_then_valid(self, monkeypatch) -> None:
|
|
monkeypatch.setattr("builtins.input", _input_sequence(["maybe", "yes"]))
|
|
assert get_confirm("go?") is True
|
|
|
|
def test_go_back_returns_none(self, monkeypatch) -> None:
|
|
monkeypatch.setattr("builtins.input", _input_sequence(["b"]))
|
|
assert get_confirm("go?", allow_go_back=True) is None
|
|
|
|
|
|
class TestGetNumberInput:
|
|
def test_returns_valid(self, monkeypatch) -> None:
|
|
monkeypatch.setattr("builtins.input", _input_sequence(["5"]))
|
|
assert get_number_input("count?", 1, 10) == 5
|
|
|
|
def test_uses_default(self, monkeypatch) -> None:
|
|
monkeypatch.setattr("builtins.input", _input_sequence([""]))
|
|
assert get_number_input("count?", 1, default=3) == 3
|
|
|
|
def test_enforces_minimum(self, monkeypatch) -> None:
|
|
monkeypatch.setattr("builtins.input", _input_sequence(["0", "2"]))
|
|
assert get_number_input("count?", 1) == 2
|
|
|
|
def test_enforces_maximum(self, monkeypatch) -> None:
|
|
monkeypatch.setattr("builtins.input", _input_sequence(["11", "9"]))
|
|
assert get_number_input("count?", 1, 10) == 9
|
|
|
|
def test_go_back_returns_none(self, monkeypatch) -> None:
|
|
monkeypatch.setattr("builtins.input", _input_sequence(["b"]))
|
|
assert get_number_input("count?", 1, allow_go_back=True) is None
|
|
|
|
|
|
class TestGetStringInput:
|
|
def test_accepts_alphanumeric(self, monkeypatch) -> None:
|
|
monkeypatch.setattr("builtins.input", _input_sequence(["abc123"]))
|
|
assert get_string_input("name?") == "abc123"
|
|
|
|
def test_rejects_empty(self, monkeypatch) -> None:
|
|
monkeypatch.setattr("builtins.input", _input_sequence(["", "value"]))
|
|
assert get_string_input("name?") == "value"
|
|
|
|
def test_uses_default(self, monkeypatch) -> None:
|
|
monkeypatch.setattr("builtins.input", _input_sequence([""]))
|
|
assert get_string_input("name?", default="fallback") == "fallback"
|
|
|
|
def test_validates_regex(self, monkeypatch) -> None:
|
|
monkeypatch.setattr("builtins.input", _input_sequence(["@", "#"]))
|
|
assert get_string_input("name?", regex=r"^#+$") == "#"
|
|
|
|
def test_rejects_excluded(self, monkeypatch) -> None:
|
|
monkeypatch.setattr("builtins.input", _input_sequence(["taken", "free"]))
|
|
assert get_string_input("name?", exclude=["taken"]) == "free"
|
|
|
|
def test_allows_special_chars(self, monkeypatch) -> None:
|
|
monkeypatch.setattr("builtins.input", _input_sequence(["a-b_c"]))
|
|
assert get_string_input("name?", allow_special_chars=True) == "a-b_c"
|
|
|
|
def test_allows_empty_with_special_chars(self, monkeypatch) -> None:
|
|
monkeypatch.setattr("builtins.input", _input_sequence([""]))
|
|
assert (
|
|
get_string_input("name?", allow_empty=True, allow_special_chars=True) == ""
|
|
)
|
|
|
|
|
|
class TestGetSelectionInput:
|
|
def test_from_list(self, monkeypatch) -> None:
|
|
monkeypatch.setattr("builtins.input", _input_sequence(["b"]))
|
|
assert get_selection_input("pick?", ["a", "b", "c"]) == "b"
|
|
|
|
def test_from_dict(self, monkeypatch) -> None:
|
|
monkeypatch.setattr("builtins.input", _input_sequence(["two"]))
|
|
assert get_selection_input("pick?", {"one": 1, "two": 2}) == "two"
|
|
|
|
def test_invalid_then_valid(self, monkeypatch) -> None:
|
|
monkeypatch.setattr("builtins.input", _input_sequence(["z", "a"]))
|
|
assert get_selection_input("pick?", ["a", "b"]) == "a"
|
|
|
|
def test_invalid_type_raises(self, monkeypatch) -> None:
|
|
monkeypatch.setattr("builtins.input", _input_sequence(["x"]))
|
|
with pytest.raises(ValueError):
|
|
get_selection_input("pick?", 123) # type: ignore[arg-type]
|
|
|
|
|
|
class TestFormatQuestion:
|
|
def test_includes_default(self) -> None:
|
|
assert "default=5" in format_question("count", 5)
|
|
|
|
def test_no_default(self) -> None:
|
|
assert "count" in format_question("count")
|
|
assert "default" not in format_question("count")
|
|
|
|
|
|
class TestValidateNumberInput:
|
|
@pytest.mark.parametrize(
|
|
"value,min_count,max_count,expected",
|
|
[
|
|
("5", 1, 10, 5),
|
|
("1", 1, 10, 1),
|
|
("10", 1, 10, 10),
|
|
("3", 1, None, 3),
|
|
],
|
|
)
|
|
def test_valid(
|
|
self, value: str, min_count: int, max_count: Any, expected: int
|
|
) -> None:
|
|
assert validate_number_input(value, min_count, max_count) == expected
|
|
|
|
@pytest.mark.parametrize(
|
|
"value,min_count,max_count",
|
|
[
|
|
("0", 1, 10),
|
|
("11", 1, 10),
|
|
("-1", 0, None),
|
|
],
|
|
)
|
|
def test_raises(self, value: str, min_count: int, max_count: Any) -> None:
|
|
with pytest.raises(ValueError):
|
|
validate_number_input(value, min_count, max_count)
|