diff --git a/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tencent.Tests/LINGYUN.Abp.BlobStoring.Tencent.Tests.csproj b/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tencent.Tests/LINGYUN.Abp.BlobStoring.Tencent.Tests.csproj
new file mode 100644
index 000000000..fd33ade61
--- /dev/null
+++ b/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tencent.Tests/LINGYUN.Abp.BlobStoring.Tencent.Tests.csproj
@@ -0,0 +1,20 @@
+
+
+
+ net9.0
+
+ false
+ Debug;Release;PostgreSQL
+ AnyCPU
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tencent.Tests/LINGYUN/Abp/BlobStoring/Tencent/AbpBlobStoringTencentTestBase.cs b/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tencent.Tests/LINGYUN/Abp/BlobStoring/Tencent/AbpBlobStoringTencentTestBase.cs
new file mode 100644
index 000000000..67fe9e7b4
--- /dev/null
+++ b/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tencent.Tests/LINGYUN/Abp/BlobStoring/Tencent/AbpBlobStoringTencentTestBase.cs
@@ -0,0 +1,8 @@
+using LINGYUN.Abp.Tests;
+
+namespace LINGYUN.Abp.BlobStoring.Tencent;
+
+public class AbpBlobStoringTencentTestBase : AbpTestsBase
+{
+
+}
diff --git a/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tencent.Tests/LINGYUN/Abp/BlobStoring/Tencent/AbpBlobStoringTencentTestModule.cs b/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tencent.Tests/LINGYUN/Abp/BlobStoring/Tencent/AbpBlobStoringTencentTestModule.cs
new file mode 100644
index 000000000..8a0b38295
--- /dev/null
+++ b/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tencent.Tests/LINGYUN/Abp/BlobStoring/Tencent/AbpBlobStoringTencentTestModule.cs
@@ -0,0 +1,97 @@
+using COSXML;
+using COSXML.Auth;
+using LINGYUN.Abp.Tencent.Features;
+using LINGYUN.Abp.Tencent.Settings;
+using LINGYUN.Abp.Tests;
+using LINGYUN.Abp.Tests.Features;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using System.Linq;
+using Volo.Abp;
+using Volo.Abp.Autofac;
+using Volo.Abp.BlobStoring;
+using Volo.Abp.Modularity;
+using Volo.Abp.Security.Encryption;
+
+namespace LINGYUN.Abp.BlobStoring.Tencent;
+
+[DependsOn(
+ typeof(AbpBlobStoringModule),
+ typeof(AbpBlobStoringTencentCloudModule),
+ typeof(AbpTestsBaseModule),
+ typeof(AbpAutofacModule)
+ )]
+public class AbpBlobStoringTencentTestModule : AbpModule
+{
+ private string _bucketName;
+ private string _secretId;
+ private string _secretKey;
+ private string _region;
+ private string _appId;
+
+ private IConfiguration _configuration;
+
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ _configuration = context.Services.GetConfiguration();
+
+ _appId = _configuration[TencentBlobProviderConfigurationNames.AppId];
+ _region = _configuration[TencentBlobProviderConfigurationNames.Region];
+ _bucketName = _configuration[TencentBlobProviderConfigurationNames.BucketName];
+
+ Configure(options =>
+ {
+ options.Map(TencentCloudFeatures.BlobStoring.MaximumStreamSize, (feature) => (int.MaxValue - 1024).ToString());
+ });
+
+ Configure(options =>
+ {
+ options.Containers.ConfigureAll((containerName, containerConfiguration) =>
+ {
+ containerConfiguration.UseTencentCloud(blob =>
+ {
+ blob.AppId = _appId;
+ blob.Region = _region;
+ blob.BucketName = _bucketName;
+ blob.CreateBucketIfNotExists = true;
+ });
+ });
+ });
+ }
+
+ public override void OnApplicationInitialization(ApplicationInitializationContext context)
+ {
+ var encryptionService = context.ServiceProvider.GetRequiredService();
+
+ _secretId = encryptionService.Decrypt(_configuration["Settings:" + TencentCloudSettingNames.SecretId]);
+ _secretKey = encryptionService.Decrypt(_configuration["Settings:" + TencentCloudSettingNames.SecretKey]);
+ }
+
+ public override void OnApplicationShutdown(ApplicationShutdownContext context)
+ {
+ var configBuilder = new CosXmlConfig.Builder();
+ configBuilder
+ .IsHttps(true)
+ .SetAppid(_appId)
+ .SetRegion(_region)
+ .SetDebugLog(true);
+
+ var cred = new DefaultQCloudCredentialProvider(
+ _secretId,
+ _secretKey,
+ 60);
+
+ var ossClient = new CosXmlServer(configBuilder.Build(), cred);
+ if (ossClient.DoesBucketExist(new COSXML.Model.Bucket.DoesBucketExistRequest(_bucketName)))
+ {
+ var bucket = ossClient.GetBucket(new COSXML.Model.Bucket.GetBucketRequest(_bucketName));
+ if (bucket.listBucket.contentsList.Any())
+ {
+ var request = new COSXML.Model.Object.DeleteMultiObjectRequest(_bucketName);
+ request.SetObjectKeys(bucket.listBucket.contentsList.Select(x => x.key).ToList());
+ ossClient.DeleteMultiObjects(request);
+ }
+ ossClient.DeleteBucket(new COSXML.Model.Bucket.DeleteBucketRequest(_bucketName));
+ }
+ }
+}
diff --git a/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tencent.Tests/LINGYUN/Abp/BlobStoring/Tencent/DefaultTencentBlobNamingNormalizerProvider_Tests.cs b/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tencent.Tests/LINGYUN/Abp/BlobStoring/Tencent/DefaultTencentBlobNamingNormalizerProvider_Tests.cs
new file mode 100644
index 000000000..0e1fedf57
--- /dev/null
+++ b/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tencent.Tests/LINGYUN/Abp/BlobStoring/Tencent/DefaultTencentBlobNamingNormalizerProvider_Tests.cs
@@ -0,0 +1,48 @@
+using Shouldly;
+using Volo.Abp.BlobStoring;
+using Xunit;
+
+namespace LINGYUN.Abp.BlobStoring.Tencent;
+
+public class DefaultTencentBlobNamingNormalizerProvider_Tests : AbpBlobStoringTencentTestBase
+{
+ private readonly IBlobNamingNormalizer _blobNamingNormalizer;
+
+ public DefaultTencentBlobNamingNormalizerProvider_Tests()
+ {
+ _blobNamingNormalizer = GetRequiredService();
+ }
+
+ [Fact]
+ public void NormalizeContainerName_Lowercase()
+ {
+ var filename = "ThisIsMyContainerName";
+ filename = _blobNamingNormalizer.NormalizeContainerName(filename);
+ filename.ShouldBe("thisismycontainername");
+ }
+
+ [Fact]
+ public void NormalizeContainerName_Only_Letters_Numbers_Dash()
+ {
+ var filename = ",./this-i,./s-my-c,./ont,./ai+*/.=!@#$n^&*er-name.+/";
+ filename = _blobNamingNormalizer.NormalizeContainerName(filename);
+ filename.ShouldBe("this-is-my-container-name");
+ }
+
+ [Fact]
+ public void NormalizeContainerName_Max_Length()
+ {
+ var filename = "abpabpabpabpabpabpabpabpabpabpabpabpabpabpabpabpabpabpabpabpabpabpabp";
+ filename = _blobNamingNormalizer.NormalizeContainerName(filename);
+ filename.Length.ShouldBeLessThanOrEqualTo(63);
+ }
+
+ [Fact]
+ public void NormalizeContainerName_Max_Length_Dash()
+ {
+ var filename = "-this-is-my-container-name-abpabpabpabpabpabpabpabp-a-b-p-a--b-p-";
+ filename = _blobNamingNormalizer.NormalizeContainerName(filename);
+ filename.ShouldBe("this-is-my-container-name-abpabpabpabpabpabpabpabp-a-b-p-a--");
+ }
+
+}
diff --git a/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tencent.Tests/LINGYUN/Abp/BlobStoring/Tencent/TencentBlobContainer_Tests.cs b/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tencent.Tests/LINGYUN/Abp/BlobStoring/Tencent/TencentBlobContainer_Tests.cs
new file mode 100644
index 000000000..bf1e9b332
--- /dev/null
+++ b/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tencent.Tests/LINGYUN/Abp/BlobStoring/Tencent/TencentBlobContainer_Tests.cs
@@ -0,0 +1,12 @@
+namespace LINGYUN.Abp.BlobStoring.Tencent;
+
+
+//Please set the correct connection string in secrets.json and continue the test.
+public class TencentBlobContainer_Tests : BlobContainer_Tests
+{
+ public TencentBlobContainer_Tests()
+ {
+
+
+ }
+}
diff --git a/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tencent.Tests/LINGYUN/Abp/BlobStoring/Tencent/TencentBlobNameCalculator_Tests.cs b/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tencent.Tests/LINGYUN/Abp/BlobStoring/Tencent/TencentBlobNameCalculator_Tests.cs
new file mode 100644
index 000000000..8dc65e2db
--- /dev/null
+++ b/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tencent.Tests/LINGYUN/Abp/BlobStoring/Tencent/TencentBlobNameCalculator_Tests.cs
@@ -0,0 +1,55 @@
+using Shouldly;
+using System;
+using Volo.Abp.BlobStoring;
+using Volo.Abp.MultiTenancy;
+using Xunit;
+
+namespace LINGYUN.Abp.BlobStoring.Tencent;
+
+public class TencentBlobNameCalculator_Tests : AbpBlobStoringTencentTestBase
+{
+ private readonly ITencentBlobNameCalculator _calculator;
+ private readonly ICurrentTenant _currentTenant;
+
+ private const string AliyunSeparator = "/";
+
+ public TencentBlobNameCalculator_Tests()
+ {
+ _calculator = GetRequiredService();
+ _currentTenant = GetRequiredService();
+ }
+
+ [Fact]
+ public void Default_Settings()
+ {
+ _calculator.Calculate(
+ GetArgs("my-container", "my-blob")
+ ).ShouldBe($"host{AliyunSeparator}my-blob");
+ }
+
+ [Fact]
+ public void Default_Settings_With_TenantId()
+ {
+ var tenantId = Guid.NewGuid();
+
+ using (_currentTenant.Change(tenantId))
+ {
+ _calculator.Calculate(
+ GetArgs("my-container", "my-blob")
+ ).ShouldBe($"tenants{AliyunSeparator}{tenantId:D}{AliyunSeparator}my-blob");
+ }
+ }
+
+ private static BlobProviderArgs GetArgs(
+ string containerName,
+ string blobName)
+ {
+ return new BlobProviderGetArgs(
+ containerName,
+ new BlobContainerConfiguration().UseTencentCloud(x =>
+ {
+ }),
+ blobName
+ );
+ }
+}
diff --git a/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tests/LINGYUN.Abp.BlobStoring.Tests.csproj b/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tests/LINGYUN.Abp.BlobStoring.Tests.csproj
new file mode 100644
index 000000000..79464a180
--- /dev/null
+++ b/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tests/LINGYUN.Abp.BlobStoring.Tests.csproj
@@ -0,0 +1,20 @@
+
+
+
+ net9.0
+
+ false
+ Debug;Release;PostgreSQL
+ AnyCPU
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tests/LINGYUN/Abp/BlobStoring/AbpBlobStoringOptions_Tests.cs b/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tests/LINGYUN/Abp/BlobStoring/AbpBlobStoringOptions_Tests.cs
new file mode 100644
index 000000000..f8112fa02
--- /dev/null
+++ b/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tests/LINGYUN/Abp/BlobStoring/AbpBlobStoringOptions_Tests.cs
@@ -0,0 +1,39 @@
+using LINGYUN.Abp.BlobStoring.Fakes;
+using LINGYUN.Abp.BlobStoring.TestObjects;
+using Shouldly;
+using Volo.Abp.BlobStoring;
+using Xunit;
+
+namespace LINGYUN.Abp.BlobStoring;
+
+public class AbpBlobStoringOptions_Tests : AbpBlobStoringTestBase
+{
+ private readonly IBlobContainerConfigurationProvider _configurationProvider;
+
+ public AbpBlobStoringOptions_Tests()
+ {
+ _configurationProvider = GetRequiredService();
+ }
+
+ [Fact]
+ public void Should_Property_Set_And_Get_Options_For_Different_Containers()
+ {
+ var testContainer1Config = _configurationProvider.Get();
+ testContainer1Config.ProviderType.ShouldBe(typeof(FakeBlobProvider1));
+ testContainer1Config.GetConfigurationOrDefault("TestConfig1").ShouldBe("TestValue1");
+ testContainer1Config.GetConfigurationOrDefault("TestConfigDefault").ShouldBe("TestValueDefault");
+
+ var testContainer2Config = _configurationProvider.Get();
+ testContainer2Config.ProviderType.ShouldBe(typeof(FakeBlobProvider2));
+ testContainer2Config.GetConfigurationOrNull("TestConfig2").ShouldBe("TestValue2");
+ testContainer2Config.GetConfigurationOrNull("TestConfigDefault").ShouldBe("TestValueDefault");
+ }
+
+ [Fact]
+ public void Should_Fallback_To_Default_Configuration_If_Not_Specialized()
+ {
+ var config = _configurationProvider.Get();
+ config.ProviderType.ShouldBe(typeof(FakeBlobProvider1));
+ config.GetConfigurationOrNull("TestConfigDefault").ShouldBe("TestValueDefault");
+ }
+}
diff --git a/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tests/LINGYUN/Abp/BlobStoring/AbpBlobStoringTestBase.cs b/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tests/LINGYUN/Abp/BlobStoring/AbpBlobStoringTestBase.cs
new file mode 100644
index 000000000..1da6f37bc
--- /dev/null
+++ b/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tests/LINGYUN/Abp/BlobStoring/AbpBlobStoringTestBase.cs
@@ -0,0 +1,12 @@
+using Volo.Abp;
+using Volo.Abp.Testing;
+
+namespace LINGYUN.Abp.BlobStoring;
+
+public abstract class AbpBlobStoringTestBase : AbpIntegratedTest
+{
+ protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options)
+ {
+ options.UseAutofac();
+ }
+}
diff --git a/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tests/LINGYUN/Abp/BlobStoring/AbpBlobStoringTestModule.cs b/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tests/LINGYUN/Abp/BlobStoring/AbpBlobStoringTestModule.cs
new file mode 100644
index 000000000..b40fd6dcf
--- /dev/null
+++ b/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tests/LINGYUN/Abp/BlobStoring/AbpBlobStoringTestModule.cs
@@ -0,0 +1,44 @@
+using LINGYUN.Abp.BlobStoring.Fakes;
+using LINGYUN.Abp.BlobStoring.TestObjects;
+using LINGYUN.Abp.Tests;
+using Microsoft.Extensions.DependencyInjection;
+using NSubstitute;
+using Volo.Abp.Autofac;
+using Volo.Abp.BlobStoring;
+using Volo.Abp.Modularity;
+
+namespace LINGYUN.Abp.BlobStoring;
+
+[DependsOn(
+ typeof(AbpBlobStoringModule),
+ typeof(AbpTestsBaseModule),
+ typeof(AbpAutofacModule)
+ )]
+public class AbpBlobStoringTestModule : AbpModule
+{
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ context.Services.AddSingleton(Substitute.For());
+ context.Services.AddSingleton(Substitute.For());
+
+ Configure(options =>
+ {
+ options.Containers
+ .ConfigureDefault(container =>
+ {
+ container.SetConfiguration("TestConfigDefault", "TestValueDefault");
+ container.ProviderType = typeof(FakeBlobProvider1);
+ })
+ .Configure(container =>
+ {
+ container.SetConfiguration("TestConfig1", "TestValue1");
+ container.ProviderType = typeof(FakeBlobProvider1);
+ })
+ .Configure(container =>
+ {
+ container.SetConfiguration("TestConfig2", "TestValue2");
+ container.ProviderType = typeof(FakeBlobProvider2);
+ });
+ });
+ }
+}
diff --git a/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tests/LINGYUN/Abp/BlobStoring/BlobContainerFactory_Tests.cs b/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tests/LINGYUN/Abp/BlobStoring/BlobContainerFactory_Tests.cs
new file mode 100644
index 000000000..758a36976
--- /dev/null
+++ b/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tests/LINGYUN/Abp/BlobStoring/BlobContainerFactory_Tests.cs
@@ -0,0 +1,76 @@
+using LINGYUN.Abp.BlobStoring.Fakes;
+using LINGYUN.Abp.BlobStoring.TestObjects;
+using NSubstitute;
+using System.Threading.Tasks;
+using Volo.Abp.BlobStoring;
+using Xunit;
+
+namespace LINGYUN.Abp.BlobStoring;
+
+public class BlobContainerFactory_Tests : AbpBlobStoringTestBase
+{
+ private readonly IBlobContainerFactory _factory;
+ private readonly FakeProviders _fakeProviders;
+
+ public BlobContainerFactory_Tests()
+ {
+ _factory = GetRequiredService();
+ _fakeProviders = GetRequiredService();
+ }
+
+ [Fact]
+ public async Task Should_Create_Containers_With_Configured_Providers()
+ {
+ // TestContainer1 with FakeBlobProvider1
+
+ await _fakeProviders.Provider1
+ .DidNotReceiveWithAnyArgs()
+ .ExistsAsync(default);
+
+ await _factory
+ .Create()
+ .ExistsAsync("TestBlob1");
+
+ await _fakeProviders.Provider1
+ .Received(1)
+ .ExistsAsync(Arg.Is(args =>
+ args.ContainerName == BlobContainerNameAttribute.GetContainerName() &&
+ args.BlobName == "TestBlob1"
+ )
+ );
+
+ // TestContainer2 with FakeBlobProvider2
+
+ await _fakeProviders.Provider2
+ .DidNotReceiveWithAnyArgs()
+ .ExistsAsync(default);
+
+ await _factory
+ .Create()
+ .ExistsAsync("TestBlob2");
+
+ await _fakeProviders.Provider2
+ .Received(1)
+ .ExistsAsync(Arg.Is(args =>
+ args.ContainerName == BlobContainerNameAttribute.GetContainerName() &&
+ args.BlobName == "TestBlob2"
+ )
+ );
+
+ // TestContainer3 with FakeBlobProvider1 (default provider)
+
+ _fakeProviders.Provider1.ClearReceivedCalls();
+
+ await _factory
+ .Create()
+ .ExistsAsync("TestBlob3");
+
+ await _fakeProviders.Provider1
+ .Received(1)
+ .ExistsAsync(Arg.Is(t =>
+ t.ContainerName == BlobContainerNameAttribute.GetContainerName() &&
+ t.BlobName == "TestBlob3"
+ )
+ );
+ }
+}
diff --git a/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tests/LINGYUN/Abp/BlobStoring/BlobContainerNameAttribute_Tests.cs b/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tests/LINGYUN/Abp/BlobStoring/BlobContainerNameAttribute_Tests.cs
new file mode 100644
index 000000000..ad52c33d2
--- /dev/null
+++ b/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tests/LINGYUN/Abp/BlobStoring/BlobContainerNameAttribute_Tests.cs
@@ -0,0 +1,25 @@
+using LINGYUN.Abp.BlobStoring.TestObjects;
+using Shouldly;
+using Volo.Abp.BlobStoring;
+using Xunit;
+
+namespace LINGYUN.Abp.BlobStoring;
+
+public class BlobContainerNameAttribute_Tests
+{
+ [Fact]
+ public void Should_Get_Specified_Name()
+ {
+ BlobContainerNameAttribute
+ .GetContainerName()
+ .ShouldBe("Test2");
+ }
+
+ [Fact]
+ public void Should_Get_Full_Class_Name_If_Not_Specified()
+ {
+ BlobContainerNameAttribute
+ .GetContainerName()
+ .ShouldBe(typeof(TestContainer1).FullName);
+ }
+}
diff --git a/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tests/LINGYUN/Abp/BlobStoring/BlobContainer_Injection_Tests.cs b/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tests/LINGYUN/Abp/BlobStoring/BlobContainer_Injection_Tests.cs
new file mode 100644
index 000000000..e5c5de339
--- /dev/null
+++ b/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tests/LINGYUN/Abp/BlobStoring/BlobContainer_Injection_Tests.cs
@@ -0,0 +1,32 @@
+using LINGYUN.Abp.BlobStoring.TestObjects;
+using Shouldly;
+using Volo.Abp.BlobStoring;
+using Xunit;
+
+namespace LINGYUN.Abp.BlobStoring;
+
+public class BlobContainer_Injection_Tests : AbpBlobStoringTestBase
+{
+ [Fact]
+ public void Should_Inject_DefaultContainer_For_Non_Generic_Interface()
+ {
+ GetRequiredService()
+ .ShouldBeOfType>();
+ }
+
+ [Fact]
+ public void Should_Inject_Specified_Container_For_Generic_Interface()
+ {
+ GetRequiredService>()
+ .ShouldBeOfType>();
+
+ GetRequiredService>()
+ .ShouldBeOfType>();
+
+ GetRequiredService>()
+ .ShouldBeOfType>();
+
+ GetRequiredService>()
+ .ShouldBeOfType>();
+ }
+}
diff --git a/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tests/LINGYUN/Abp/BlobStoring/BlobContainer_Tests.cs b/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tests/LINGYUN/Abp/BlobStoring/BlobContainer_Tests.cs
new file mode 100644
index 000000000..ef457569b
--- /dev/null
+++ b/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tests/LINGYUN/Abp/BlobStoring/BlobContainer_Tests.cs
@@ -0,0 +1,143 @@
+using LINGYUN.Abp.BlobStoring.TestObjects;
+using Shouldly;
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+using Volo.Abp;
+using Volo.Abp.BlobStoring;
+using Volo.Abp.Modularity;
+using Volo.Abp.MultiTenancy;
+using Volo.Abp.Testing;
+using Xunit;
+
+namespace LINGYUN.Abp.BlobStoring;
+
+public abstract class BlobContainer_Tests : AbpIntegratedTest
+ where TStartupModule : IAbpModule
+{
+ protected IBlobContainer Container { get; }
+
+ protected ICurrentTenant CurrentTenant { get; }
+
+ protected BlobContainer_Tests()
+ {
+ Container = GetRequiredService>();
+ CurrentTenant = GetRequiredService();
+ }
+
+ protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options)
+ {
+ options.UseAutofac();
+ }
+
+ [Theory]
+ [InlineData("test-blob-1")]
+ [InlineData("test-blob-1.txt")]
+ [InlineData("test-folder/test-blob-1")]
+ public async Task Should_Save_And_Get_Blobs(string blobName)
+ {
+ var testContent = "test content".GetBytes();
+ await Container.SaveAsync(blobName, testContent);
+
+ var result = await Container.GetAllBytesAsync(blobName);
+ result.SequenceEqual(testContent).ShouldBeTrue();
+ }
+
+ [Fact]
+ public async Task Should_Save_And_Get_Blobs_In_Different_Tenant()
+ {
+ var blobName = "test-blob-1";
+ var testContent = "test content".GetBytes();
+
+ using (CurrentTenant.Change(Guid.NewGuid()))
+ {
+ await Container.SaveAsync(blobName, testContent);
+ (await Container.GetAllBytesAsync(blobName)).SequenceEqual(testContent).ShouldBeTrue();
+ }
+
+ using (CurrentTenant.Change(Guid.NewGuid()))
+ {
+ await Container.SaveAsync(blobName, testContent);
+ (await Container.GetAllBytesAsync(blobName)).SequenceEqual(testContent).ShouldBeTrue();
+
+ using (CurrentTenant.Change(null))
+ {
+ // Could not find the requested BLOB...
+ await Assert.ThrowsAsync(async () =>
+ await Container.GetAllBytesAsync(blobName)
+ );
+ }
+ }
+
+ using (CurrentTenant.Change(null))
+ {
+ await Container.SaveAsync(blobName, testContent);
+ (await Container.GetAllBytesAsync(blobName)).SequenceEqual(testContent).ShouldBeTrue();
+ }
+ }
+
+ [Fact]
+ public async Task Should_Overwrite_Pre_Saved_Blob_If_Requested()
+ {
+ var blobName = "test-blob-1";
+
+ var testContent = "test content".GetBytes();
+ await Container.SaveAsync(blobName, testContent);
+
+ var testContentOverwritten = "test content overwritten".GetBytes();
+ await Container.SaveAsync(blobName, testContentOverwritten, true);
+
+ var result = await Container.GetAllBytesAsync(blobName);
+ result.SequenceEqual(testContentOverwritten).ShouldBeTrue();
+ }
+
+ [Fact]
+ public async Task Should_Not_Allow_To_Overwrite_Pre_Saved_Blob_By_Default()
+ {
+ var blobName = "test-blob-1";
+
+ var testContent = "test content".GetBytes();
+ await Container.SaveAsync(blobName, testContent);
+
+ var testContentOverwritten = "test content overwritten".GetBytes();
+ await Assert.ThrowsAsync(() =>
+ Container.SaveAsync(blobName, testContentOverwritten)
+ );
+ }
+
+ [Theory]
+ [InlineData("test-blob-1")]
+ [InlineData("test-blob-1.txt")]
+ [InlineData("test-folder/test-blob-1")]
+ public async Task Should_Delete_Saved_Blobs(string blobName)
+ {
+ await Container.SaveAsync(blobName, "test content".GetBytes());
+ (await Container.GetAllBytesAsync(blobName)).ShouldNotBeNull();
+
+ await Container.DeleteAsync(blobName);
+ (await Container.GetAllBytesOrNullAsync(blobName)).ShouldBeNull();
+ }
+
+ [Theory]
+ [InlineData("test-blob-1")]
+ [InlineData("test-blob-1.txt")]
+ [InlineData("test-folder/test-blob-1")]
+ public async Task Saved_Blobs_Should_Exists(string blobName)
+ {
+ await Container.SaveAsync(blobName, "test content".GetBytes());
+ (await Container.ExistsAsync(blobName)).ShouldBeTrue();
+
+ await Container.DeleteAsync(blobName);
+ (await Container.ExistsAsync(blobName)).ShouldBeFalse();
+ }
+
+ [Theory]
+ [InlineData("test-blob-1")]
+ [InlineData("test-blob-1.txt")]
+ [InlineData("test-folder/test-blob-1")]
+ public async Task Unknown_Blobs_Should_Not_Exists(string blobName)
+ {
+ await Container.DeleteAsync(blobName);
+ (await Container.ExistsAsync(blobName)).ShouldBeFalse();
+ }
+}
diff --git a/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tests/LINGYUN/Abp/BlobStoring/BlobProviderSelector_Tests.cs b/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tests/LINGYUN/Abp/BlobStoring/BlobProviderSelector_Tests.cs
new file mode 100644
index 000000000..cbbb54fff
--- /dev/null
+++ b/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tests/LINGYUN/Abp/BlobStoring/BlobProviderSelector_Tests.cs
@@ -0,0 +1,30 @@
+using LINGYUN.Abp.BlobStoring.Fakes;
+using LINGYUN.Abp.BlobStoring.TestObjects;
+using Shouldly;
+using Volo.Abp.BlobStoring;
+using Xunit;
+
+namespace LINGYUN.Abp.BlobStoring;
+
+public class BlobProviderSelector_Tests : AbpBlobStoringTestBase
+{
+ private readonly IBlobProviderSelector _selector;
+
+ public BlobProviderSelector_Tests()
+ {
+ _selector = GetRequiredService();
+ }
+
+ [Fact]
+ public void Should_Select_Default_Provider_If_Not_Configured()
+ {
+ _selector.Get().ShouldBeAssignableTo();
+ }
+
+ [Fact]
+ public void Should_Select_Configured_Provider()
+ {
+ _selector.Get().ShouldBeAssignableTo();
+ _selector.Get().ShouldBeAssignableTo();
+ }
+}
diff --git a/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tests/LINGYUN/Abp/BlobStoring/Fakes/FakeBlobProvider1.cs b/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tests/LINGYUN/Abp/BlobStoring/Fakes/FakeBlobProvider1.cs
new file mode 100644
index 000000000..57850d959
--- /dev/null
+++ b/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tests/LINGYUN/Abp/BlobStoring/Fakes/FakeBlobProvider1.cs
@@ -0,0 +1,33 @@
+using System.IO;
+using System.Threading.Tasks;
+using Volo.Abp.BlobStoring;
+
+namespace LINGYUN.Abp.BlobStoring.Fakes;
+
+public class FakeBlobProvider1 : IBlobProvider
+{
+ public virtual Task SaveAsync(BlobProviderSaveArgs args)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ public virtual Task DeleteAsync(BlobProviderDeleteArgs args)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ public virtual Task ExistsAsync(BlobProviderExistsArgs args)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ public virtual Task GetAsync(BlobProviderGetArgs args)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ public virtual Task GetOrNullAsync(BlobProviderGetArgs args)
+ {
+ throw new System.NotImplementedException();
+ }
+}
diff --git a/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tests/LINGYUN/Abp/BlobStoring/Fakes/FakeBlobProvider2.cs b/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tests/LINGYUN/Abp/BlobStoring/Fakes/FakeBlobProvider2.cs
new file mode 100644
index 000000000..93bf525b6
--- /dev/null
+++ b/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tests/LINGYUN/Abp/BlobStoring/Fakes/FakeBlobProvider2.cs
@@ -0,0 +1,33 @@
+using System.IO;
+using System.Threading.Tasks;
+using Volo.Abp.BlobStoring;
+
+namespace LINGYUN.Abp.BlobStoring.Fakes;
+
+public class FakeBlobProvider2 : IBlobProvider
+{
+ public virtual Task SaveAsync(BlobProviderSaveArgs args)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ public virtual Task DeleteAsync(BlobProviderDeleteArgs args)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ public virtual Task ExistsAsync(BlobProviderExistsArgs args)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ public virtual Task GetAsync(BlobProviderGetArgs args)
+ {
+ throw new System.NotImplementedException();
+ }
+
+ public virtual Task GetOrNullAsync(BlobProviderGetArgs args)
+ {
+ throw new System.NotImplementedException();
+ }
+}
diff --git a/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tests/LINGYUN/Abp/BlobStoring/Fakes/FakeProviders.cs b/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tests/LINGYUN/Abp/BlobStoring/Fakes/FakeProviders.cs
new file mode 100644
index 000000000..4a6864241
--- /dev/null
+++ b/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tests/LINGYUN/Abp/BlobStoring/Fakes/FakeProviders.cs
@@ -0,0 +1,19 @@
+using System.Collections.Generic;
+using System.Linq;
+using Volo.Abp.BlobStoring;
+using Volo.Abp.DependencyInjection;
+
+namespace LINGYUN.Abp.BlobStoring.Fakes;
+
+public class FakeProviders : ISingletonDependency
+{
+ public FakeBlobProvider1 Provider1 { get; }
+
+ public FakeBlobProvider2 Provider2 { get; }
+
+ public FakeProviders(IEnumerable providers)
+ {
+ Provider1 = providers.OfType().Single();
+ Provider2 = providers.OfType().Single();
+ }
+}
diff --git a/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tests/LINGYUN/Abp/BlobStoring/TestObjects/TestContainer1.cs b/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tests/LINGYUN/Abp/BlobStoring/TestObjects/TestContainer1.cs
new file mode 100644
index 000000000..26cfbe94d
--- /dev/null
+++ b/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tests/LINGYUN/Abp/BlobStoring/TestObjects/TestContainer1.cs
@@ -0,0 +1,6 @@
+namespace LINGYUN.Abp.BlobStoring.TestObjects;
+
+public class TestContainer1
+{
+
+}
diff --git a/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tests/LINGYUN/Abp/BlobStoring/TestObjects/TestContainer2.cs b/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tests/LINGYUN/Abp/BlobStoring/TestObjects/TestContainer2.cs
new file mode 100644
index 000000000..c937b90ba
--- /dev/null
+++ b/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tests/LINGYUN/Abp/BlobStoring/TestObjects/TestContainer2.cs
@@ -0,0 +1,9 @@
+using Volo.Abp.BlobStoring;
+
+namespace LINGYUN.Abp.BlobStoring.TestObjects;
+
+[BlobContainerName("Test2")]
+public class TestContainer2
+{
+
+}
diff --git a/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tests/LINGYUN/Abp/BlobStoring/TestObjects/TestContainer3.cs b/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tests/LINGYUN/Abp/BlobStoring/TestObjects/TestContainer3.cs
new file mode 100644
index 000000000..21368073b
--- /dev/null
+++ b/aspnet-core/tests/LINGYUN.Abp.BlobStoring.Tests/LINGYUN/Abp/BlobStoring/TestObjects/TestContainer3.cs
@@ -0,0 +1,6 @@
+namespace LINGYUN.Abp.BlobStoring.TestObjects;
+
+public class TestContainer3
+{
+
+}
diff --git a/aspnet-core/tests/LINGYUN.Abp.Sms.Aliyun.Tests/LINGYUN/Abp/Sms/Aliyun/AliyunSmsVerifyCodeSender_Tests.cs b/aspnet-core/tests/LINGYUN.Abp.Sms.Aliyun.Tests/LINGYUN/Abp/Sms/Aliyun/AliyunSmsVerifyCodeSender_Tests.cs
index 04e15c5d8..ea63851ed 100644
--- a/aspnet-core/tests/LINGYUN.Abp.Sms.Aliyun.Tests/LINGYUN/Abp/Sms/Aliyun/AliyunSmsVerifyCodeSender_Tests.cs
+++ b/aspnet-core/tests/LINGYUN.Abp.Sms.Aliyun.Tests/LINGYUN/Abp/Sms/Aliyun/AliyunSmsVerifyCodeSender_Tests.cs
@@ -1,16 +1,19 @@
using Microsoft.Extensions.Configuration;
using System.Threading.Tasks;
+using Volo.Abp.Sms;
using Xunit;
namespace LINGYUN.Abp.Sms.Aliyun;
public class AliyunSmsVerifyCodeSender_Tests : AbpAliyunTestBase
{
- protected IAliyunSmsVerifyCodeSender SmsSender { get; }
+ protected IAliyunSmsVerifyCodeSender SmsVerifyCodeSender { get; }
+ protected ISmsSender SmsSender { get; }
protected IConfiguration Configuration { get; }
public AliyunSmsVerifyCodeSender_Tests()
{
- SmsSender = GetRequiredService();
+ SmsSender = GetRequiredService();
+ SmsVerifyCodeSender = GetRequiredService();
Configuration = GetRequiredService();
}
@@ -21,17 +24,39 @@ public class AliyunSmsVerifyCodeSender_Tests : AbpAliyunTestBase
///
[Theory]
[InlineData("123456")]
- public async Task Send_Test(string code)
+ public async Task Send_Sms_Verify_Code_Test(string code)
{
var signName = Configuration["Aliyun:Sms:Sender:SignName"];
var phone = Configuration["Aliyun:Sms:Sender:PhoneNumber"];
var template = Configuration["Aliyun:Sms:Sender:TemplateCode"];
- await SmsSender.SendAsync(
+ await SmsVerifyCodeSender.SendAsync(
new SmsVerifyCodeMessage(
phone,
new SmsVerifyCodeMessageParam(code),
signName,
template));
}
+
+ ///
+ /// 阿里云短信测试
+ ///
+ ///
+ ///
+ [Theory]
+ [InlineData("123456")]
+ public async Task Send_Sms_Test(string code)
+ {
+ var signName = Configuration["Aliyun:Sms:Sender:SignName"];
+ var phone = Configuration["Aliyun:Sms:Sender:PhoneNumber"];
+ var template = Configuration["Aliyun:Sms:Sender:TemplateCode"];
+
+ var message = new SmsMessage(phone, "test");
+ message.Properties.Add("SignName", signName);
+ message.Properties.Add("SmsVerifyCode", true);
+ message.Properties.Add("TemplateCode", template);
+ message.Properties.Add("code", code);
+
+ await SmsSender.SendAsync(message);
+ }
}