mirror of
https://github.com/PaddlePaddle/FastDeploy.git
synced 2026-04-23 00:17:25 +08:00
162 lines
5.4 KiB
Python
162 lines
5.4 KiB
Python
"""
|
|
# Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
"""
|
|
|
|
import httpx
|
|
import pytest
|
|
import respx
|
|
|
|
from fastdeploy.input.tokenizer_client import (
|
|
AsyncTokenizerClient,
|
|
ImageEncodeRequest,
|
|
VideoEncodeRequest,
|
|
)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
@respx.mock
|
|
async def test_encode_image_success():
|
|
base_url = "http://testserver"
|
|
client = AsyncTokenizerClient(base_url=base_url)
|
|
|
|
# Mock 创建任务接口
|
|
respx.post(f"{base_url}/image/encode").mock(
|
|
return_value=httpx.Response(200, json={"code": 0, "task_tag": "task123"})
|
|
)
|
|
# Mock 轮询接口,返回完成状态
|
|
mock_get_ret = {
|
|
"state": "Finished",
|
|
"result": {"feature_url": "bos://host:port/key", "feature_shape": [80, 45, 1563]},
|
|
}
|
|
respx.get(f"{base_url}/encode/get").mock(return_value=httpx.Response(200, json=mock_get_ret))
|
|
|
|
request = ImageEncodeRequest(
|
|
version="v1", req_id="req_img_001", is_gen=False, resolution=512, image_url="http://example.com/image.jpg"
|
|
)
|
|
|
|
result = await client.encode_image(request)
|
|
assert result["feature_url"] == "bos://host:port/key"
|
|
assert result["feature_shape"] == [80, 45, 1563]
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
@respx.mock
|
|
async def test_encode_video_failure():
|
|
base_url = "http://testserver"
|
|
client = AsyncTokenizerClient(base_url=base_url, max_wait=1)
|
|
|
|
respx.post(f"{base_url}/video/encode").mock(
|
|
return_value=httpx.Response(200, json={"code": 0, "task_tag": "task_vid_001"})
|
|
)
|
|
# 模拟轮询接口失败状态
|
|
respx.get(f"{base_url}/encode/get").mock(
|
|
return_value=httpx.Response(200, json={"state": "Error", "message": "Encode failed"})
|
|
)
|
|
|
|
request = VideoEncodeRequest(
|
|
version="v1",
|
|
req_id="req_vid_001",
|
|
is_gen=True,
|
|
resolution=720,
|
|
video_url="http://example.com/video.mp4",
|
|
start_ts=0.0,
|
|
end_ts=10.0,
|
|
frames=30,
|
|
vit_merge=True,
|
|
)
|
|
|
|
with pytest.raises(RuntimeError, match="Encode failed"):
|
|
await client.encode_video(request)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
@respx.mock
|
|
async def test_encode_timeout():
|
|
base_url = "http://testserver"
|
|
client = AsyncTokenizerClient(base_url=base_url, max_wait=1, poll_interval=0.1)
|
|
|
|
respx.post(f"{base_url}/image/encode").mock(
|
|
return_value=httpx.Response(200, json={"code": 0, "task_tag": "task_timeout"})
|
|
)
|
|
# 模拟轮询接口一直返回等待状态,导致超时
|
|
respx.get(f"{base_url}/encode/get").mock(return_value=httpx.Response(200, json={"status": "processing"}))
|
|
|
|
request = ImageEncodeRequest(
|
|
version="v1", req_id="req_img_timeout", is_gen=False, resolution=256, image_url="http://example.com/image.jpg"
|
|
)
|
|
|
|
with pytest.raises(TimeoutError):
|
|
await client.encode_image(request)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_encode_invalid_type():
|
|
"""Test invalid encode type raises ValueError (line 130).
|
|
NOTE: Public methods hardcode the type param, so we test the private method directly
|
|
to verify the validation boundary."""
|
|
base_url = "http://testserver"
|
|
client = AsyncTokenizerClient(base_url=base_url)
|
|
|
|
request = ImageEncodeRequest(
|
|
version="v1", req_id="req_invalid", is_gen=False, resolution=256, image_url="http://example.com/image.jpg"
|
|
)
|
|
|
|
with pytest.raises(ValueError, match="Invalid encode type"):
|
|
await client._async_encode_request("invalid_type", request.model_dump())
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_decode_invalid_type():
|
|
"""Test invalid decode type raises ValueError (line 186).
|
|
NOTE: Public methods hardcode the type param, so we test the private method directly
|
|
to verify the validation boundary."""
|
|
base_url = "http://testserver"
|
|
client = AsyncTokenizerClient(base_url=base_url)
|
|
|
|
with pytest.raises(ValueError, match="Invalid decode type"):
|
|
await client._async_decode_request("invalid_type", {})
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
@respx.mock
|
|
async def test_encode_network_error_continues_polling():
|
|
"""Test network error during polling is caught and logged (line 164)."""
|
|
base_url = "http://testserver"
|
|
client = AsyncTokenizerClient(base_url=base_url, max_wait=2, poll_interval=0.1)
|
|
|
|
# Mock create task
|
|
respx.post(f"{base_url}/image/encode").mock(
|
|
return_value=httpx.Response(200, json={"code": 0, "task_tag": "task_network_error"})
|
|
)
|
|
|
|
# First poll fails with network error, second succeeds
|
|
call_count = 0
|
|
|
|
def side_effect(request):
|
|
nonlocal call_count
|
|
call_count += 1
|
|
if call_count == 1:
|
|
raise httpx.RequestError("Network error")
|
|
return httpx.Response(200, json={"state": "Finished", "result": {"key": "value"}})
|
|
|
|
respx.get(f"{base_url}/encode/get").mock(side_effect=side_effect)
|
|
|
|
request = ImageEncodeRequest(
|
|
version="v1", req_id="req_network", is_gen=False, resolution=256, image_url="http://example.com/image.jpg"
|
|
)
|
|
|
|
result = await client.encode_image(request)
|
|
assert result["key"] == "value"
|