from datetime import timedelta import pytest from app.commands import DurationError, format_duration, looks_like_duration, parse_duration from app.commands.arguments import ArgumentError, parse_quiet_args MAX = timedelta(days=7) @pytest.mark.parametrize("token, minutes", [ ("30m", 30), ("2h", 120), ("1d", 1440), ("90", 90), ("1h30m", 90), ("1D2H", 1560), ("2h30M", 150), ]) def test_valid_durations(token, minutes): assert parse_duration(token, MAX) == timedelta(minutes=minutes) @pytest.mark.parametrize("token", ["", "abc", "2x", "h2", "-5", "1.5h", "2h 30m", "0", "0m", "0h0m"]) def test_invalid_durations(token): with pytest.raises(DurationError): parse_duration(token, MAX) def test_maximum_enforced_with_message(): assert parse_duration("7d", MAX) == MAX with pytest.raises(DurationError, match="maximal 7 d"): parse_duration("8d", MAX) with pytest.raises(DurationError, match="maximal 1 h 30 min"): parse_duration("91", timedelta(minutes=90)) def test_huge_number_rejected_without_overflow(): with pytest.raises(DurationError): parse_duration("9" * 30 + "m", MAX) def test_looks_like_duration(): assert looks_like_duration("2h") and looks_like_duration("90") and looks_like_duration("1h30m") assert not looks_like_duration("web01") and not looks_like_duration("CPU") def test_format_duration(): assert format_duration(timedelta(minutes=150)) == "2 h 30 min" assert format_duration(timedelta(days=1)) == "1 d" def test_args_explicit_host_service_duration_comment(): parsed = parse_quiet_args(("web01", "CPU load", "2h", "Wartung", "Kernel"), MAX, service_required=True) assert (parsed.host, parsed.service, parsed.duration, parsed.comment) == ( "web01", "CPU load", timedelta(hours=2), "Wartung Kernel") def test_args_mute_host_only_and_reply_mode(): host_only = parse_quiet_args(("web*", "30m", "grund"), MAX, service_required=False) assert host_only.host == "web*" and host_only.service is None and host_only.comment == "grund" reply = parse_quiet_args(("2h", "weil", "gut"), MAX, service_required=False) assert reply.host is None and reply.duration == timedelta(hours=2) and reply.comment == "weil gut" def test_args_downtime_requires_service(): with pytest.raises(ArgumentError): parse_quiet_args(("web01", "2h"), MAX, service_required=True) with pytest.raises(ArgumentError): parse_quiet_args((), MAX, service_required=True) with pytest.raises(ArgumentError, match="maximal"): parse_quiet_args(("web01", "disk", "8d"), MAX, service_required=True)