241 lines
8.5 KiB
Python
241 lines
8.5 KiB
Python
import os
|
|
from unittest.mock import patch
|
|
|
|
import pytest
|
|
|
|
from ray.serve._private.constants_utils import (
|
|
_validate_name,
|
|
get_env_bool,
|
|
get_env_float,
|
|
get_env_float_non_negative,
|
|
get_env_float_positive,
|
|
get_env_int,
|
|
get_env_int_non_negative,
|
|
get_env_int_positive,
|
|
get_env_str,
|
|
parse_latency_buckets,
|
|
str_to_list,
|
|
)
|
|
|
|
|
|
class TestStrToList:
|
|
def test_str_to_list_basic(self):
|
|
assert str_to_list("a,b,c") == ["a", "b", "c"]
|
|
|
|
def test_str_to_list_with_whitespace(self):
|
|
assert str_to_list(" a , b , c ") == ["a", "b", "c"]
|
|
|
|
def test_str_to_list_empty_string(self):
|
|
assert str_to_list("") == []
|
|
|
|
def test_str_to_list_with_empty_entries(self):
|
|
assert str_to_list("a,,b,c,") == ["a", "b", "c"]
|
|
|
|
def test_str_to_list_only_whitespace(self):
|
|
assert str_to_list(" ") == []
|
|
|
|
def test_str_to_list_single_entry(self):
|
|
assert str_to_list("single") == ["single"]
|
|
|
|
def test_str_to_list_only_commas(self):
|
|
assert str_to_list(",,,,") == []
|
|
|
|
def test_str_to_list_whitespace_entries(self):
|
|
assert str_to_list("a, ,b") == ["a", "b"]
|
|
|
|
|
|
class TestParseLatencyBuckets:
|
|
def test_parse_latency_buckets(self):
|
|
# Test valid inputs with different formats
|
|
assert parse_latency_buckets("1,2,3", []) == [1.0, 2.0, 3.0]
|
|
assert parse_latency_buckets("1,2,3,4 ", []) == [1.0, 2.0, 3.0, 4.0]
|
|
assert parse_latency_buckets(" 1,2,3,4,5", []) == [1.0, 2.0, 3.0, 4.0, 5.0]
|
|
assert parse_latency_buckets(" 1, 2,3 ,4,5 ,6 ", []) == [
|
|
1.0,
|
|
2.0,
|
|
3.0,
|
|
4.0,
|
|
5.0,
|
|
6.0,
|
|
]
|
|
|
|
# Test decimal numbers
|
|
assert parse_latency_buckets("0.5,1.5,2.5", []) == [0.5, 1.5, 2.5]
|
|
|
|
def test_parse_latency_buckets_invalid(self):
|
|
# Test negative numbers
|
|
with pytest.raises(ValueError, match=".*must be positive.*"):
|
|
parse_latency_buckets("-1,1,2,3,4", [])
|
|
|
|
# Test non-ascending order
|
|
with pytest.raises(ValueError, match=".*be in strictly ascending order*"):
|
|
parse_latency_buckets("4,3,2,1", [])
|
|
|
|
# Test duplicate values
|
|
with pytest.raises(ValueError, match=".*be in strictly ascending order.*"):
|
|
parse_latency_buckets("1,2,2,3,4", [])
|
|
|
|
# Test invalid number format
|
|
with pytest.raises(ValueError, match=".*Invalid.*format.*"):
|
|
parse_latency_buckets("1,2,3,4,a", [])
|
|
|
|
# Test empty list
|
|
with pytest.raises(ValueError, match=".*could not convert.*"):
|
|
parse_latency_buckets(",,,", [])
|
|
|
|
# Test invalid separators
|
|
with pytest.raises(ValueError, match=".*could not convert.*"):
|
|
parse_latency_buckets("1;2;3;4", [])
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_environ():
|
|
with patch.dict(os.environ, {}, clear=True) as mock_env:
|
|
yield mock_env
|
|
|
|
|
|
class TestEnvValueFunctions:
|
|
def test_get_env_int(self, mock_environ):
|
|
assert get_env_int("RAY_SERVE_TEST_VAR", 0) == 0
|
|
|
|
mock_environ["RAY_SERVE_TEST_VAR"] = "42"
|
|
assert get_env_int("RAY_SERVE_TEST_VAR", 0) == 42
|
|
|
|
mock_environ["RAY_SERVE_TEST_VAR"] = "-1"
|
|
assert get_env_int("RAY_SERVE_TEST_VAR", 0) == -1
|
|
|
|
mock_environ["RAY_SERVE_TEST_VAR"] = "0.1"
|
|
with pytest.raises(ValueError, match=".*`0.1` cannot be converted to `int`!*"):
|
|
get_env_int_positive("RAY_SERVE_TEST_VAR", 5)
|
|
|
|
mock_environ["RAY_SERVE_TEST_VAR"] = "abc"
|
|
with pytest.raises(ValueError, match=".*`abc` cannot be converted to `int`!*"):
|
|
get_env_int_positive("RAY_SERVE_TEST_VAR", 5)
|
|
|
|
with pytest.raises(ValueError, match=".*require prefix `RAY_SERVE_`*"):
|
|
get_env_int_positive("NO_PREFIX", 5)
|
|
|
|
def test_get_env_int_positive(self, mock_environ):
|
|
assert get_env_int_positive("RAY_SERVE_TEST_VAR", 1) == 1
|
|
|
|
mock_environ["RAY_SERVE_TEST_VAR"] = "42"
|
|
assert get_env_int_positive("RAY_SERVE_TEST_VAR", 1) == 42
|
|
|
|
mock_environ["RAY_SERVE_TEST_VAR"] = "-1"
|
|
with pytest.raises(ValueError, match=".*Expected positive `int`.*"):
|
|
get_env_int_positive("RAY_SERVE_TEST_VAR", 5)
|
|
|
|
def test_get_env_int_non_negative(self, mock_environ):
|
|
assert get_env_int_non_negative("RAY_SERVE_TEST_VAR", 0) == 0
|
|
assert get_env_int_non_negative("RAY_SERVE_TEST_VAR", 1) == 1
|
|
|
|
mock_environ["RAY_SERVE_TEST_VAR"] = "42"
|
|
assert get_env_int_non_negative("RAY_SERVE_TEST_VAR", 0) == 42
|
|
|
|
mock_environ["RAY_SERVE_TEST_VAR"] = "-1"
|
|
with pytest.raises(ValueError, match=".*Expected non negative `int`.*"):
|
|
get_env_int_non_negative("RAY_SERVE_TEST_VAR", 5)
|
|
|
|
with pytest.raises(ValueError, match=".*Expected non negative `int`.*"):
|
|
get_env_int_non_negative("RAY_SERVE_TEST_VAR_FROM_DEFAULT", -1)
|
|
|
|
def test_get_env_float(self, mock_environ):
|
|
assert get_env_float("RAY_SERVE_TEST_VAR", 0.0) == 0.0
|
|
|
|
mock_environ["RAY_SERVE_TEST_VAR"] = "3.14"
|
|
assert get_env_float("RAY_SERVE_TEST_VAR", 0.0) == 3.14
|
|
|
|
mock_environ["RAY_SERVE_TEST_VAR"] = "-2.5"
|
|
assert get_env_float("RAY_SERVE_TEST_VAR", 0.0) == -2.5
|
|
|
|
mock_environ["RAY_SERVE_TEST_VAR"] = "abc"
|
|
with pytest.raises(
|
|
ValueError, match=".*`abc` cannot be converted to `float`!*"
|
|
):
|
|
get_env_float("RAY_SERVE_TEST_VAR", 0.0)
|
|
|
|
def test_get_env_float_positive(self, mock_environ):
|
|
assert get_env_float_positive("RAY_SERVE_TEST_VAR", 1.5) == 1.5
|
|
assert get_env_float_positive("RAY_SERVE_TEST_VAR", None) is None
|
|
|
|
mock_environ["RAY_SERVE_TEST_VAR"] = "42.5"
|
|
assert get_env_float_positive("RAY_SERVE_TEST_VAR", 1.0) == 42.5
|
|
|
|
mock_environ["RAY_SERVE_TEST_VAR"] = "-1.2"
|
|
with pytest.raises(ValueError, match=".*Expected positive `float`.*"):
|
|
get_env_float_positive("RAY_SERVE_TEST_VAR", 5.0)
|
|
|
|
with pytest.raises(ValueError, match=".*Expected positive `float`.*"):
|
|
get_env_float_positive("RAY_SERVE_TEST_VAR_FROM_DEFAULT", 0.0)
|
|
|
|
with pytest.raises(ValueError, match=".*Expected positive `float`.*"):
|
|
get_env_float_positive("RAY_SERVE_TEST_VAR_FROM_DEFAULT", -1)
|
|
|
|
def test_get_env_float_non_negative(self, mock_environ):
|
|
assert get_env_float_non_negative("RAY_SERVE_TEST_VAR", 0.0) == 0.0
|
|
assert get_env_float_non_negative("RAY_SERVE_TEST_VAR", 1.5) == 1.5
|
|
|
|
mock_environ["RAY_SERVE_TEST_VAR"] = "42.5"
|
|
assert get_env_float_non_negative("RAY_SERVE_TEST_VAR", 0.0) == 42.5
|
|
|
|
mock_environ["RAY_SERVE_TEST_VAR"] = "-1.2"
|
|
with pytest.raises(ValueError, match=".*Expected non negative `float`.*"):
|
|
get_env_float_non_negative("RAY_SERVE_TEST_VAR", 5.0)
|
|
|
|
def test_get_env_str(self, mock_environ):
|
|
mock_environ["RAY_SERVE_TEST_STR"] = "hello"
|
|
assert get_env_str("RAY_SERVE_TEST_STR", "default") == "hello"
|
|
|
|
assert get_env_str("RAY_SERVE_NONEXISTENT_VAR", "default_str") == "default_str"
|
|
|
|
assert get_env_str("RAY_SERVE_NONEXISTENT_VAR", None) is None
|
|
|
|
def test_get_env_bool(self, mock_environ):
|
|
mock_environ["RAY_SERVE_TEST_BOOL_TRUE"] = "1"
|
|
assert get_env_bool("RAY_SERVE_TEST_BOOL_TRUE", "0") is True
|
|
|
|
# Test with any other value (False)
|
|
mock_environ["RAY_SERVE_TEST_BOOL_FALSE"] = "true"
|
|
assert get_env_bool("RAY_SERVE_TEST_BOOL_FALSE", "0") is False
|
|
mock_environ["RAY_SERVE_TEST_BOOL_FALSE2"] = "yes"
|
|
assert get_env_bool("RAY_SERVE_TEST_BOOL_FALSE2", "0") is False
|
|
|
|
# Test with default when environment variable not set
|
|
assert get_env_bool("RAY_SERVE_NONEXISTENT_VAR", "1") is True
|
|
assert get_env_bool("RAY_SERVE_NONEXISTENT_VAR", "0") is False
|
|
|
|
|
|
class TestValidation:
|
|
@pytest.mark.parametrize(
|
|
"name",
|
|
[
|
|
"RAY_SERVE_FOO",
|
|
"RAY_SERVE__DOUBLE_UNDERSCORE",
|
|
"RAY_SERVE_123",
|
|
"RAY_SERVE_VAR_NAME",
|
|
],
|
|
)
|
|
def test_validate_name_accepts_valid_prefix(self, name):
|
|
# Should not raise
|
|
assert _validate_name(name) is None
|
|
|
|
@pytest.mark.parametrize(
|
|
"name",
|
|
[
|
|
"",
|
|
"RAY_SERVE", # missing trailing underscore and name
|
|
"SERVE_VAR",
|
|
"ray_SERVE_BAR",
|
|
"RAY_service_VAR",
|
|
],
|
|
)
|
|
def test_validate_name_rejects_invalid_prefix(self, name):
|
|
with pytest.raises(ValueError, match=".*require prefix `RAY_SERVE_`*"):
|
|
_validate_name(name)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
import sys
|
|
|
|
sys.exit(pytest.main(["-v", "-s", __file__]))
|