Files
wehub-resource-sync 94057c3d3e
PR Test (NPU) / check-changes (push) Has been cancelled
PR Test (NPU) / pr-gate (push) Has been cancelled
PR Test (NPU) / set-image-config (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-4-npu-a3 (push) Has been cancelled
PR Test (NPU) / stage-b-test-16-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-1-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-2-npu-a3 (push) Has been cancelled
PR Test (Arm64) / pr-gate (push) Has been cancelled
PR Test (Arm64) / check-changes (push) Has been cancelled
PR Test (Arm64) / build-test (push) Has been cancelled
PR Test (sgl-router) / gate (push) Has been cancelled
PR Test (sgl-router) / tier-1 — lint (push) Has been cancelled
PR Test (sgl-router) / tier-2 — build + test (push) Has been cancelled
PR Test (sgl-router) / tier-3 — docker (placeholder) (push) Has been cancelled
PR Test (sgl-router) / tier-3 — k8s integration (push) Has been cancelled
PR Test (sgl-router) / tier-3 — e2e (push) Has been cancelled
PR Test (sgl-router) / finish (push) Has been cancelled
PR Test (NPU) / single-node-poc (map[name:qwen3_6_27b_w8a8_1p_in64k_out1k_50ms runner:linux-aarch64-a3-2 test_case:test/registered/ascend/performance/qwen3_6_27b/test_npu_qwen3_6_27b_w8a8_1p_in64k_out1k_50ms.py test_type:perf]) (push) Has been cancelled
PR Test (NPU) / pr-test-npu-finish (push) Has been cancelled
PR Test (Xeon) / pr-gate (push) Has been cancelled
PR Test (Xeon) / check-changes (push) Has been cancelled
PR Test (Xeon) / build-test (, xeon-gnr, base-b-test-cpu) (push) Has been cancelled
PR Test (XPU) / check-changes (push) Has been cancelled
PR Test (XPU) / pr-gate (push) Has been cancelled
PR Test (XPU) / stage-a-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / wait-for-stage-a (push) Has been cancelled
PR Test (XPU) / stage-b-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / finish (push) Has been cancelled
CI Model Inventory / build-inventory (push) Has been cancelled
Lint / lint (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Compilation Check (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Manual Policy (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Request Processing (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Summary (push) Has been cancelled
PR Test (SMG) / build-wheel (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on windows (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (x86_64 - auto) (push) Has been cancelled
PR Test (SMG) / python-unit-tests (push) Has been cancelled
PR Test (SMG) / unit-tests (push) Has been cancelled
PR Test (SMG) / benchmarks (push) Has been cancelled
PR Test (SMG) / chat-completions (push) Has been cancelled
PR Test (SMG) / chat-completions-4gpu (push) Has been cancelled
PR Test (SMG) / e2e (push) Has been cancelled
PR Test (SMG) / docker-build-test (push) Has been cancelled
PR Test (SMG) / k8s-integration (push) Has been cancelled
PR Test (SMG) / finish (push) Has been cancelled
PR Test (SMG) / summarize-benchmarks (push) Has been cancelled
Release SGLang Model Gateway Docker Image / publish (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Build SDist (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Upload to PyPI (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (aarch64, 12.9, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (x86_64, 12.9, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu129 (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (aarch64, 13.0, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (x86_64, 13.0, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu130 (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 700) (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 720) (push) Has been cancelled
Release SGLang Kernels / release-rocm700 (push) Has been cancelled
Release SGLang Kernels / release-rocm720 (push) Has been cancelled
Release SGLang Kernels / build-musa43 (43, 3.10) (push) Has been cancelled
Release SGLang Kernels / release-musa43 (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:38:16 +08:00

233 lines
7.6 KiB
Python

"""
Test suite for S3 CloudStorage integration.
Tests verify file upload, cleanup, URL generation, and error handling.
"""
import asyncio
import importlib
import os
from types import SimpleNamespace
import pytest
import sglang.multimodal_gen.runtime.entrypoints.openai.storage as storage_mod
from sglang.multimodal_gen.runtime.entrypoints.openai.storage import CloudStorage
def _create_temp_file(tmp_path, name="test.png", content=b"\x89PNG\r\n\x1a\nfake"):
"""Create a temporary test file."""
p = tmp_path / name
p.write_bytes(content)
return str(p)
# UNIT TESTS
def test_upload_file_success(tmp_path):
"""Test successful upload with correct URL generation."""
file_path = _create_temp_file(tmp_path, "image.png")
storage_mod.cloud_storage.enabled = True
storage_mod.cloud_storage.bucket_name = "my-bucket"
storage_mod.cloud_storage.endpoint_url = "https://s3.example.com"
storage_mod.cloud_storage.region_name = None
called = {}
def fake_upload(local_path, bucket, key, ExtraArgs=None):
called["local_path"] = local_path
called["bucket"] = bucket
called["key"] = key
called["extra"] = ExtraArgs
storage_mod.cloud_storage.client = SimpleNamespace(upload_file=fake_upload)
url = asyncio.run(storage_mod.cloud_storage.upload_file(file_path, "image.png"))
assert url == "https://s3.example.com/my-bucket/image.png"
assert called["local_path"] == file_path
assert called["bucket"] == "my-bucket"
assert called["key"] == "image.png"
assert called["extra"]["ContentType"] == "image/png"
def test_upload_and_cleanup(tmp_path):
"""Test that local file is deleted after successful upload."""
file_path = _create_temp_file(tmp_path, "cleanup.png")
storage_mod.cloud_storage.enabled = True
storage_mod.cloud_storage.bucket_name = "my-bucket"
storage_mod.cloud_storage.endpoint_url = "https://s3.example.com"
storage_mod.cloud_storage.client = SimpleNamespace(
upload_file=lambda *args, **kwargs: None
)
assert os.path.exists(file_path)
url = asyncio.run(storage_mod.cloud_storage.upload_and_cleanup(file_path))
assert url == "https://s3.example.com/my-bucket/cleanup.png"
assert not os.path.exists(file_path)
def test_upload_failure_preserves_file(tmp_path):
"""Test that file is preserved when upload fails."""
file_path = _create_temp_file(tmp_path, "preserve.png")
storage_mod.cloud_storage.enabled = True
storage_mod.cloud_storage.bucket_name = "my-bucket"
storage_mod.cloud_storage.endpoint_url = "https://s3.example.com"
def fake_upload_raises(*args, **kwargs):
raise RuntimeError("simulated failure")
storage_mod.cloud_storage.client = SimpleNamespace(upload_file=fake_upload_raises)
result = asyncio.run(storage_mod.cloud_storage.upload_and_cleanup(file_path))
assert result is None
assert os.path.exists(file_path)
def test_disabled_storage_returns_none(tmp_path):
"""Test that disabled storage returns None."""
file_path = _create_temp_file(tmp_path, "test.png")
prev_enabled = storage_mod.cloud_storage.enabled
storage_mod.cloud_storage.enabled = False
try:
result = asyncio.run(
storage_mod.cloud_storage.upload_file(file_path, "test.png")
)
assert result is None
finally:
storage_mod.cloud_storage.enabled = prev_enabled
def test_aws_url_with_region(tmp_path):
"""Test AWS S3 URL generation with specific region."""
file_path = _create_temp_file(tmp_path, "aws.png")
storage_mod.cloud_storage.enabled = True
storage_mod.cloud_storage.bucket_name = "aws-bucket"
storage_mod.cloud_storage.endpoint_url = None
storage_mod.cloud_storage.region_name = "us-west-2"
storage_mod.cloud_storage.client = SimpleNamespace(
upload_file=lambda *args, **kwargs: None
)
url = asyncio.run(storage_mod.cloud_storage.upload_file(file_path, "aws.png"))
assert url == "https://aws-bucket.s3.us-west-2.amazonaws.com/aws.png"
def test_aws_url_default_region(tmp_path):
"""Test AWS S3 URL defaults to us-east-1 when region not specified."""
file_path = _create_temp_file(tmp_path, "default.png")
storage_mod.cloud_storage.enabled = True
storage_mod.cloud_storage.bucket_name = "default-bucket"
storage_mod.cloud_storage.endpoint_url = None
storage_mod.cloud_storage.region_name = None
storage_mod.cloud_storage.client = SimpleNamespace(
upload_file=lambda *args, **kwargs: None
)
url = asyncio.run(storage_mod.cloud_storage.upload_file(file_path, "default.png"))
assert url == "https://default-bucket.s3.us-east-1.amazonaws.com/default.png"
def test_custom_endpoint_url(tmp_path):
"""Test URL generation with custom endpoint (MinIO/OSS/COS)."""
file_path = _create_temp_file(tmp_path, "custom.png")
storage_mod.cloud_storage.enabled = True
storage_mod.cloud_storage.bucket_name = "custom-bucket"
storage_mod.cloud_storage.endpoint_url = "https://minio.example.com/"
storage_mod.cloud_storage.region_name = None
storage_mod.cloud_storage.client = SimpleNamespace(
upload_file=lambda *args, **kwargs: None
)
url = asyncio.run(storage_mod.cloud_storage.upload_file(file_path, "custom.png"))
# Verify trailing slash is stripped
assert url == "https://minio.example.com/custom-bucket/custom.png"
def test_content_type_detection(tmp_path):
"""Test Content-Type header for different file extensions."""
storage_mod.cloud_storage.enabled = True
storage_mod.cloud_storage.bucket_name = "test-bucket"
storage_mod.cloud_storage.endpoint_url = "https://s3.test"
test_cases = [
("image.png", "image/png"),
("image.jpg", "image/jpeg"),
("image.jpeg", "image/jpeg"),
("image.webp", "image/webp"),
("video.mp4", "video/mp4"),
("file.bin", "application/octet-stream"),
]
for filename, expected_type in test_cases:
called = {}
def fake_upload(local_path, bucket, key, ExtraArgs=None):
called["content_type"] = ExtraArgs.get("ContentType")
storage_mod.cloud_storage.client = SimpleNamespace(upload_file=fake_upload)
file_path = _create_temp_file(tmp_path, filename)
asyncio.run(storage_mod.cloud_storage.upload_file(file_path, filename))
assert called["content_type"] == expected_type
# requires moto and boto3
has_moto = (
importlib.util.find_spec("moto") is not None
and importlib.util.find_spec("boto3") is not None
)
@pytest.mark.skipif(not has_moto, reason="moto/boto3 not installed")
def test_integration_with_moto(tmp_path):
"""Integration test using moto to mock real S3 service."""
import boto3
from moto import mock_aws
os.environ["SGLANG_CLOUD_STORAGE_TYPE"] = "s3"
os.environ["SGLANG_S3_BUCKET_NAME"] = "integration-test"
os.environ["SGLANG_S3_REGION_NAME"] = "us-east-1"
with mock_aws():
s3 = boto3.client("s3", region_name="us-east-1")
s3.create_bucket(Bucket="integration-test")
storage = CloudStorage()
assert storage.is_enabled()
file_path = _create_temp_file(tmp_path, "integration.png", b"test_data")
url = asyncio.run(storage.upload_and_cleanup(file_path))
assert url is not None
assert "integration-test" in url
assert "integration.png" in url
assert not os.path.exists(file_path)
obj = s3.get_object(Bucket="integration-test", Key="integration.png")
assert obj["Body"].read() == b"test_data"
for key in [
"SGLANG_CLOUD_STORAGE_TYPE",
"SGLANG_S3_BUCKET_NAME",
"SGLANG_S3_REGION_NAME",
]:
os.environ.pop(key, None)