Files
tiku-backend.net/Tiku.UnitTests/Storage/AliyunOssObjectStorageServiceTests.cs
xiong a517ffc6a7
Some checks failed
ci / release-gate (push) Has been cancelled
feat(dev): containerize local dependencies
2026-08-04 13:27:25 +08:00

194 lines
7.5 KiB
C#

using Microsoft.Extensions.Options;
using Tiku.Application.Storage;
using Tiku.Infrastructure.Storage;
namespace Tiku.UnitTests.Storage;
public sealed class AliyunOssObjectStorageServiceTests
{
private static readonly Guid TenantId = Guid.Parse("11111111-1111-1111-1111-111111111111");
[Fact]
public void Validate_object_key_requires_tenant_prefix_like_original_storage_service()
{
using var service = CreateConfiguredService();
var exception = Assert.Throws<ObjectStorageException>(() =>
service.ValidateObjectKey(TenantId, "other-tenant/file.pdf"));
Assert.Equal("OBJECT_KEY_TENANT_PREFIX_REQUIRED", exception.Code);
}
[Fact]
public void Validate_mime_type_and_size_use_configured_allow_list()
{
using var service = CreateConfiguredService(new ObjectStorageOptions
{
MaxUploadBytes = 100,
AllowedMimePrefixes = ["image/"],
AllowedMimeTypes = ["application/pdf"]
});
Assert.Equal("image/png", service.ValidateMimeType(" Image/PNG "));
Assert.Equal(99, service.ValidateFileSize(99));
Assert.Equal("MIME_TYPE_NOT_ALLOWED",
Assert.Throws<ObjectStorageException>(() => service.ValidateMimeType("text/html")).Code);
Assert.Equal("FILE_TOO_LARGE", Assert.Throws<ObjectStorageException>(() => service.ValidateFileSize(101)).Code);
}
[Fact]
public async Task Sign_download_requires_complete_aliyun_oss_options()
{
using var service = CreateUnconfiguredService();
var exception = await Assert.ThrowsAsync<ObjectStorageNotConfiguredException>(() =>
service.SignDownloadAsync(new ObjectStorageDownloadSignRequest(
TenantId,
ObjectStorageProviders.AliyunOss,
"bucket",
$"{TenantId:N}/path/file.pdf",
TimeSpan.FromMinutes(15))));
Assert.Contains("ALIYUN_OSS_ACCESS_KEY_ID", exception.Message, StringComparison.Ordinal);
}
[Fact]
public async Task Sign_download_generates_aliyun_oss_presigned_get_url()
{
using var service = CreateConfiguredService();
var signedUrl = await service.SignDownloadAsync(new ObjectStorageDownloadSignRequest(
TenantId,
ObjectStorageProviders.AliyunOss,
"tiku-assets",
$"{TenantId:N}/handbook.pdf",
TimeSpan.FromMinutes(15),
FileName: "handbook.pdf",
Disposition: "attachment"));
Assert.Equal(ObjectStorageProviders.AliyunOss, signedUrl.Provider);
Assert.Equal("GET", signedUrl.Method);
Assert.Equal("aliyun-oss-signature-url-v4", signedUrl.SignatureMode);
Assert.Contains("tiku-assets", signedUrl.Url.Host, StringComparison.Ordinal);
Assert.Contains($"{TenantId:N}/handbook.pdf", signedUrl.Url.AbsoluteUri, StringComparison.Ordinal);
Assert.Contains("x-oss-", signedUrl.Url.Query, StringComparison.OrdinalIgnoreCase);
}
[Fact]
public async Task Sign_upload_generates_aliyun_oss_presigned_put_url_with_content_type_header()
{
using var service = CreateConfiguredService();
var signedUrl = await service.SignUploadAsync(new ObjectStorageUploadSignRequest(
TenantId,
ObjectStorageProviders.AliyunOss,
"tiku-assets",
$"{TenantId:N}/upload.pdf",
"upload.pdf",
"application/pdf",
1024,
TimeSpan.FromMinutes(10)));
Assert.Equal("PUT", signedUrl.Method);
Assert.Contains("x-oss-", signedUrl.Url.Query, StringComparison.OrdinalIgnoreCase);
}
[Fact]
public async Task Cdn_url_download_returns_public_or_provider_managed_url_without_oss_signing()
{
using var service = CreateUnconfiguredService();
var signedUrl = await service.SignDownloadAsync(new ObjectStorageDownloadSignRequest(
TenantId,
ObjectStorageProviders.ExternalUrl,
null,
null,
TimeSpan.FromMinutes(5),
"https://cdn.example.com/file.pdf"));
Assert.Equal("public-or-provider-managed", signedUrl.SignatureMode);
Assert.Equal("https://cdn.example.com/file.pdf", signedUrl.Url.AbsoluteUri);
}
[Fact]
public void Aliyun_oss_options_allow_empty_optional_configuration()
{
Assert.True(AliyunOssOptions.BeValid(new AliyunOssOptions()));
Assert.False(AliyunOssOptions.BeValid(new AliyunOssOptions
{
PresignDefaultMinutes = 0
}));
}
[Fact]
public async Task Local_dev_round_trips_through_configured_s3_endpoint()
{
var endpoint = Environment.GetEnvironmentVariable("TIKU_S3_TEST_ENDPOINT");
if (string.IsNullOrWhiteSpace(endpoint)) return;
using var service = new AliyunOssObjectStorageService(
Options.Create(new ObjectStorageOptions { DefaultProvider = ObjectStorageProviders.LocalDev }),
Options.Create(new AliyunOssOptions()),
Options.Create(new S3CompatibleOptions
{
Endpoint = endpoint,
AccessKey = Environment.GetEnvironmentVariable("TIKU_S3_TEST_ACCESS_KEY") ?? "tiku",
SecretKey = Environment.GetEnvironmentVariable("TIKU_S3_TEST_SECRET_KEY") ?? "tiku_minio_dev",
Region = "us-east-1"
}));
var objectKey = $"{TenantId:N}/tests/{Guid.NewGuid():N}.json";
const string payload = "{\"storage\":\"minio\"}";
await using var input = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(payload));
var write = await service.WriteObjectAsync(new ObjectStorageWriteRequest(
TenantId,
ObjectStorageProviders.LocalDev,
"tiku-tests",
objectKey,
"application/json",
input,
input.Length));
var metadata = await service.HeadObjectAsync(new ObjectStorageHeadRequest(
TenantId,
ObjectStorageProviders.LocalDev,
"tiku-tests",
objectKey));
await using var output = await service.OpenReadAsync(new ObjectStorageReadRequest(
TenantId,
ObjectStorageProviders.LocalDev,
"tiku-tests",
objectKey));
using var reader = new StreamReader(output);
Assert.Equal("s3-compatible-put-object", write.VerificationSource);
Assert.Equal(input.Length, metadata.SizeBytes);
Assert.Equal(payload, await reader.ReadToEndAsync());
}
private static AliyunOssObjectStorageService CreateConfiguredService(ObjectStorageOptions? storageOptions = null)
{
return new AliyunOssObjectStorageService(
Options.Create(storageOptions ?? new ObjectStorageOptions()),
Options.Create(new AliyunOssOptions
{
Region = "cn-hangzhou",
AccessKeyId = "test-access-key-id",
AccessKeySecret = "test-access-key-secret"
}),
Options.Create(new S3CompatibleOptions
{
Endpoint = "127.0.0.1:9000",
AccessKey = "test-access-key",
SecretKey = "test-secret-key",
Region = "us-east-1"
}));
}
private static AliyunOssObjectStorageService CreateUnconfiguredService()
{
return new AliyunOssObjectStorageService(Options.Create(new ObjectStorageOptions()),
Options.Create(new AliyunOssOptions()),
Options.Create(new S3CompatibleOptions()));
}
}