Files
Azornes-Comfyui-Model-Resolver/tests/test_request_utils.py
T

185 lines
6.2 KiB
Python

from types import SimpleNamespace
from unittest.mock import AsyncMock
import pytest
from core.request_utils import (
coerce_integer_identifier,
extract_request_sha256,
read_bool_field,
read_first_bool_field,
read_first_identifier_field,
read_first_text_field,
read_identifier_field,
read_int_field,
read_optional_object_payload,
read_optional_text_field,
read_text_field,
)
def test_extract_request_sha256_preserves_alias_precedence_and_normalization():
sha256 = "a" * 64
assert extract_request_sha256(
{
"sha256": "",
"hash": f"sha256:{sha256.upper()}",
"file_hash": "b" * 64,
},
keys=("sha256", "hash", "file_hash"),
) == sha256
assert extract_request_sha256(
{"sha256": "invalid", "hash": sha256},
keys=("sha256", "hash"),
) == ""
assert extract_request_sha256(
{"SHA256": sha256},
keys=("sha256", "hash", "SHA256"),
) == sha256
assert extract_request_sha256({}, keys=("sha256", "hash")) == ""
with pytest.raises(TypeError, match="Request sha256 must be a string"):
extract_request_sha256(
{"sha256": 123},
keys=("sha256", "hash"),
)
def test_read_text_field_preserves_optional_null_and_rejects_scalars():
assert read_text_field({}, "name", default="fallback") == "fallback"
assert read_text_field({"name": None}, "name", default="fallback") == (
"fallback"
)
assert read_text_field({"name": " value "}, "name") == "value"
with pytest.raises(TypeError, match="Request name must be a string"):
read_text_field({"name": 123}, "name")
def test_read_optional_text_field_preserves_missing_value_and_validates():
assert read_optional_text_field({}, "name") is None
assert read_optional_text_field({"name": " value "}, "name") == "value"
assert read_optional_text_field({"name": ""}, "name") is None
with pytest.raises(TypeError, match="Request name must be a string"):
read_optional_text_field({"name": 123}, "name")
def test_read_first_text_field_preserves_alias_order_and_rejects_scalars():
assert read_first_text_field(
{"url": "", "custom_url": " https://example.test/model "},
("url", "custom_url"),
) == "https://example.test/model"
with pytest.raises(TypeError, match="Request url must be a string"):
read_first_text_field(
{"url": 123, "custom_url": "https://example.test/model"},
("url", "custom_url"),
)
def test_read_identifier_field_accepts_only_string_or_integer_identifiers():
assert read_identifier_field({"id": " 12 "}, "id") == "12"
assert read_identifier_field({"id": 12}, "id") == 12
assert read_identifier_field({}, "id") is None
with pytest.raises(TypeError, match="Request id must be an integer or string"):
read_identifier_field({"id": True}, "id")
def test_coerce_integer_identifier_rejects_non_numeric_values():
assert coerce_integer_identifier(" 12 ", "id") == 12
assert coerce_integer_identifier(34, "id") == 34
assert coerce_integer_identifier("", "id") is None
with pytest.raises(ValueError, match="Request id must be an integer"):
coerce_integer_identifier("repo/name", "id")
def test_read_first_identifier_field_preserves_alias_order_and_rejects_scalars():
assert read_first_identifier_field(
{"model_id": "", "modelId": " 12 "},
("model_id", "modelId"),
) == "12"
with pytest.raises(
TypeError,
match="Request model_id must be an integer or string",
):
read_first_identifier_field(
{"model_id": {"value": 12}, "modelId": 12},
("model_id", "modelId"),
)
def test_read_bool_field_accepts_supported_forms_and_rejects_scalars():
assert read_bool_field({"value": "true"}, "value") is True
assert read_bool_field({"value": 0}, "value") is False
assert read_bool_field({}, "value", default=True) is True
with pytest.raises(TypeError, match="Request value must be a boolean"):
read_bool_field({"value": {"enabled": True}}, "value")
def test_read_first_bool_field_preserves_alias_order_and_rejects_scalars():
assert read_first_bool_field(
{"force": "", "force_rescan": "true"},
("force", "force_rescan"),
) is True
with pytest.raises(TypeError, match="Request force must be a boolean"):
read_first_bool_field(
{"force": {"enabled": True}, "force_rescan": True},
("force", "force_rescan"),
)
def test_read_int_field_rejects_boolean_and_invalid_numeric_values():
assert read_int_field({"value": " 12 "}, "value") == 12
assert read_int_field({}, "value", default=5) == 5
with pytest.raises(TypeError, match="Request value must be an integer"):
read_int_field({"value": True}, "value")
with pytest.raises(ValueError, match="Request value must be an integer"):
read_int_field({"value": "not-a-number"}, "value")
@pytest.mark.asyncio
@pytest.mark.parametrize(
("payload", "expected"),
[
({"force": True}, {"force": True}),
([], {}),
("invalid", {}),
(None, {}),
],
)
async def test_read_optional_object_payload_normalizes_json_values(
payload, expected
):
request = SimpleNamespace(
can_read_body=True,
json=AsyncMock(return_value=payload),
)
assert await read_optional_object_payload(request) == expected
@pytest.mark.asyncio
async def test_read_optional_object_payload_ignores_json_parse_errors():
request = SimpleNamespace(
can_read_body=True,
json=AsyncMock(side_effect=ValueError("invalid json")),
)
assert await read_optional_object_payload(request) == {}
@pytest.mark.asyncio
async def test_read_optional_object_payload_skips_unreadable_body():
request = SimpleNamespace(
can_read_body=False,
json=AsyncMock(),
)
assert await read_optional_object_payload(request) == {}
request.json.assert_not_awaited()
@pytest.mark.asyncio
async def test_read_optional_object_payload_supports_requests_without_body_flag():
request = SimpleNamespace(json=AsyncMock(return_value={"value": 1}))
assert await read_optional_object_payload(request) == {"value": 1}