Initial commit

This commit is contained in:
2026-07-13 09:25:40 +07:00
parent c08ff54676
commit bccfb156d7
1938 changed files with 641646 additions and 0 deletions

View File

@@ -0,0 +1,42 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<LangVersion>latest</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="8.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.3.0" />
<PackageReference Include="Moq" Version="4.20.72" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Commons\RobotNet10.StorageManager\RobotNet10.StorageManager.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,428 @@
using Minio;
using RobotNet10.StorageManager;
using Xunit;
namespace RobotNet10.StorageManager.Test;
public class StorageManagerTests
{
private readonly string _testFolder = Path.Combine(Path.GetTempPath(), "StorageManagerTests");
public StorageManagerTests()
{
// Clean up test folder before each test
if (Directory.Exists(_testFolder))
{
Directory.Delete(_testFolder, true);
}
Directory.CreateDirectory(_testFolder);
}
[Fact]
public void Constructor_WithNullConfig_ThrowsArgumentNullException()
{
// Act & Assert
Assert.Throws<ArgumentNullException>(() => new StorageManager(null!));
}
[Fact]
public void Constructor_WithMinioButNoConfig_ThrowsArgumentException()
{
// Arrange
var config = new StorageConfig
{
UsingLocal = false,
MinioConfig = null
};
// Act & Assert
var ex = Assert.Throws<ArgumentException>(() => new StorageManager(config));
Assert.Contains("MinioConfig is required", ex.Message);
}
[Fact]
public void Constructor_WithMinioButEmptyEndpoint_ThrowsArgumentException()
{
// Arrange
var config = new StorageConfig
{
UsingLocal = false,
MinioConfig = new MinioConfig { Endpoint = string.Empty },
Bucket = "test-bucket"
};
// Act & Assert
var ex = Assert.Throws<ArgumentException>(() => new StorageManager(config));
Assert.Contains("Endpoint", ex.Message);
}
[Fact]
public void Constructor_WithMinioButEmptyBucket_ThrowsArgumentException()
{
// Arrange
var config = new StorageConfig
{
UsingLocal = false,
MinioConfig = new MinioConfig { Endpoint = "localhost:9000" },
Bucket = string.Empty
};
// Act & Assert
var ex = Assert.Throws<ArgumentException>(() => new StorageManager(config));
Assert.Contains("Bucket", ex.Message);
}
[Fact]
public async Task UploadAsync_WithNullPath_ThrowsArgumentException()
{
// Arrange
var config = new StorageConfig { UsingLocal = true, LocalFolder = _testFolder };
var manager = new StorageManager(config);
var data = new MemoryStream(new byte[] { 1, 2, 3 });
// Act & Assert
await Assert.ThrowsAsync<ArgumentException>(() =>
manager.UploadAsync(null!, "test", data, 3, "image/png", CancellationToken.None));
}
[Fact]
public async Task UploadAsync_WithPathContainingDotDot_ThrowsArgumentException()
{
// Arrange
var config = new StorageConfig { UsingLocal = true, LocalFolder = _testFolder };
var manager = new StorageManager(config);
var data = new MemoryStream(new byte[] { 1, 2, 3 });
// Act & Assert
await Assert.ThrowsAsync<ArgumentException>(() =>
manager.UploadAsync("../test", "test", data, 3, "image/png", CancellationToken.None));
}
[Fact]
public async Task UploadAsync_WithNullData_ThrowsArgumentNullException()
{
// Arrange
var config = new StorageConfig { UsingLocal = true, LocalFolder = _testFolder };
var manager = new StorageManager(config);
// Act & Assert
await Assert.ThrowsAsync<ArgumentNullException>(() =>
manager.UploadAsync("test", "test", null!, 3, "image/png", CancellationToken.None));
}
[Fact]
public async Task UploadAsync_WithNegativeSize_ThrowsArgumentException()
{
// Arrange
var config = new StorageConfig { UsingLocal = true, LocalFolder = _testFolder };
var manager = new StorageManager(config);
var data = new MemoryStream(new byte[] { 1, 2, 3 });
// Act & Assert
await Assert.ThrowsAsync<ArgumentException>(() =>
manager.UploadAsync("test", "test", data, -1, "image/png", CancellationToken.None));
}
[Fact]
public async Task UploadAsync_Local_WithPngContentType_CreatesPngFile()
{
// Arrange
var config = new StorageConfig { UsingLocal = true, LocalFolder = _testFolder };
var manager = new StorageManager(config);
var data = new MemoryStream(new byte[] { 1, 2, 3 });
var path = "test";
var objectName = "image";
// Act
await manager.UploadAsync(path, objectName, data, 3, "image/png", CancellationToken.None);
// Assert
var expectedPath = Path.Combine(_testFolder, path, $"{objectName}.png");
Assert.True(File.Exists(expectedPath));
}
[Fact]
public async Task UploadAsync_Local_WithJpegContentType_CreatesJpgFile()
{
// Arrange
var config = new StorageConfig { UsingLocal = true, LocalFolder = _testFolder };
var manager = new StorageManager(config);
var data = new MemoryStream(new byte[] { 1, 2, 3 });
var path = "test";
var objectName = "image";
// Act
await manager.UploadAsync(path, objectName, data, 3, "image/jpeg", CancellationToken.None);
// Assert
var expectedPath = Path.Combine(_testFolder, path, $"{objectName}.jpg");
Assert.True(File.Exists(expectedPath));
}
[Fact]
public async Task UploadAsync_Local_WithPdfContentType_CreatesPdfFile()
{
// Arrange
var config = new StorageConfig { UsingLocal = true, LocalFolder = _testFolder };
var manager = new StorageManager(config);
var data = new MemoryStream(new byte[] { 1, 2, 3 });
var path = "test";
var objectName = "document";
// Act
await manager.UploadAsync(path, objectName, data, 3, "application/pdf", CancellationToken.None);
// Assert
var expectedPath = Path.Combine(_testFolder, path, $"{objectName}.pdf");
Assert.True(File.Exists(expectedPath));
}
[Fact]
public async Task UploadAsync_Local_CreatesBackupFile()
{
// Arrange
var config = new StorageConfig { UsingLocal = true, LocalFolder = _testFolder };
var manager = new StorageManager(config);
var data1 = new MemoryStream(new byte[] { 1, 2, 3 });
var data2 = new MemoryStream(new byte[] { 4, 5, 6 });
var path = "test";
var objectName = "image";
// Act - Upload first time
await manager.UploadAsync(path, objectName, data1, 3, "image/png", CancellationToken.None);
var filePath = Path.Combine(_testFolder, path, $"{objectName}.png");
Assert.True(File.Exists(filePath));
// Upload second time - should create backup
await manager.UploadAsync(path, objectName, data2, 3, "image/png", CancellationToken.None);
// Assert
Assert.True(File.Exists(filePath));
var backupPath = $"{filePath}.bk";
Assert.True(File.Exists(backupPath));
}
[Fact]
public async Task GetUrlAsync_Local_WithExistingFile_ReturnsPath()
{
// Arrange
var config = new StorageConfig { UsingLocal = true, LocalFolder = _testFolder };
var manager = new StorageManager(config);
var path = "test";
var objectName = "image";
var filePath = Path.Combine(_testFolder, path, $"{objectName}.png");
Directory.CreateDirectory(Path.GetDirectoryName(filePath)!);
File.WriteAllBytes(filePath, new byte[] { 1, 2, 3 });
// Act
var url = await manager.GetUrlAsync(path, objectName, CancellationToken.None);
// Assert
Assert.Equal(filePath, url);
}
[Fact]
public async Task GetUrlAsync_Local_WithNonExistingFile_ReturnsEmpty()
{
// Arrange
var config = new StorageConfig { UsingLocal = true, LocalFolder = _testFolder };
var manager = new StorageManager(config);
// Act
var url = await manager.GetUrlAsync("test", "nonexistent", CancellationToken.None);
// Assert
Assert.Equal(string.Empty, url);
}
[Fact]
public async Task DeleteAsync_Local_DeletesFile()
{
// Arrange
var config = new StorageConfig { UsingLocal = true, LocalFolder = _testFolder };
var manager = new StorageManager(config);
var path = "test";
var objectName = "image";
var filePath = Path.Combine(_testFolder, path, $"{objectName}.png");
Directory.CreateDirectory(Path.GetDirectoryName(filePath)!);
File.WriteAllBytes(filePath, new byte[] { 1, 2, 3 });
// Act
await manager.DeleteAsync(path, objectName, CancellationToken.None);
// Assert
Assert.False(File.Exists(filePath));
}
[Fact]
public async Task ExistsAsync_Local_WithExistingFile_ReturnsTrue()
{
// Arrange
var config = new StorageConfig { UsingLocal = true, LocalFolder = _testFolder };
var manager = new StorageManager(config);
var path = "test";
var objectName = "image";
var filePath = Path.Combine(_testFolder, path, $"{objectName}.png");
Directory.CreateDirectory(Path.GetDirectoryName(filePath)!);
File.WriteAllBytes(filePath, new byte[] { 1, 2, 3 });
// Act
var exists = await manager.ExistsAsync(path, objectName, CancellationToken.None);
// Assert
Assert.True(exists);
}
[Fact]
public async Task ExistsAsync_Local_WithNonExistingFile_ReturnsFalse()
{
// Arrange
var config = new StorageConfig { UsingLocal = true, LocalFolder = _testFolder };
var manager = new StorageManager(config);
// Act
var exists = await manager.ExistsAsync("test", "nonexistent", CancellationToken.None);
// Assert
Assert.False(exists);
}
[Fact]
public async Task ListAsync_Local_NonRecursive_ReturnsFilesInPath()
{
// Arrange
var config = new StorageConfig { UsingLocal = true, LocalFolder = _testFolder };
var manager = new StorageManager(config);
var path = "test";
var folderPath = Path.Combine(_testFolder, path);
Directory.CreateDirectory(folderPath);
File.WriteAllBytes(Path.Combine(folderPath, "file1.png"), new byte[] { 1 });
File.WriteAllBytes(Path.Combine(folderPath, "file2.jpg"), new byte[] { 2 });
// Act
var files = await manager.ListAsync(path, false, CancellationToken.None);
// Assert
Assert.Equal(2, files.Count);
Assert.Contains("file1.png", files);
Assert.Contains("file2.jpg", files);
}
[Fact]
public async Task ListAsync_Local_Recursive_ReturnsAllFiles()
{
// Arrange
var config = new StorageConfig { UsingLocal = true, LocalFolder = _testFolder };
var manager = new StorageManager(config);
var path = "test";
var folderPath = Path.Combine(_testFolder, path);
Directory.CreateDirectory(folderPath);
var subFolder = Path.Combine(folderPath, "sub");
Directory.CreateDirectory(subFolder);
File.WriteAllBytes(Path.Combine(folderPath, "file1.png"), new byte[] { 1 });
File.WriteAllBytes(Path.Combine(subFolder, "file2.jpg"), new byte[] { 2 });
// Act
var files = await manager.ListAsync(path, true, CancellationToken.None);
// Assert
Assert.Equal(2, files.Count);
Assert.Contains("file1.png", files);
Assert.Contains("sub/file2.jpg", files);
}
[Fact]
public async Task CopyAsync_Local_CopiesFile()
{
// Arrange
var config = new StorageConfig { UsingLocal = true, LocalFolder = _testFolder };
var manager = new StorageManager(config);
var sourcePath = "source";
var destPath = "dest";
var objectName = "file";
var sourceFile = Path.Combine(_testFolder, sourcePath, $"{objectName}.png");
Directory.CreateDirectory(Path.GetDirectoryName(sourceFile)!);
File.WriteAllBytes(sourceFile, new byte[] { 1, 2, 3 });
// Act
await manager.CopyAsync(sourcePath, destPath, objectName, CancellationToken.None);
// Assert
var destFile = Path.Combine(_testFolder, destPath, $"{objectName}.png");
Assert.True(File.Exists(destFile));
Assert.True(File.Exists(sourceFile)); // Source should still exist
}
[Fact]
public async Task GetMetadataAsync_Local_ReturnsMetadata()
{
// Arrange
var config = new StorageConfig { UsingLocal = true, LocalFolder = _testFolder };
var manager = new StorageManager(config);
var path = "test";
var objectName = "image";
var filePath = Path.Combine(_testFolder, path, $"{objectName}.png");
Directory.CreateDirectory(Path.GetDirectoryName(filePath)!);
var fileData = new byte[] { 1, 2, 3, 4, 5 };
File.WriteAllBytes(filePath, fileData);
// Act
var metadata = await manager.GetMetadataAsync(path, objectName, CancellationToken.None);
// Assert
Assert.Equal(path, metadata.Path);
Assert.Equal(objectName, metadata.ObjectName);
Assert.Equal(fileData.Length, metadata.Size);
Assert.Equal("image/png", metadata.ContentType);
Assert.NotNull(metadata.LastModified);
}
[Fact]
public async Task GetMetadataAsync_Local_WithNonExistingFile_ThrowsFileNotFoundException()
{
// Arrange
var config = new StorageConfig { UsingLocal = true, LocalFolder = _testFolder };
var manager = new StorageManager(config);
// Act & Assert
await Assert.ThrowsAsync<FileNotFoundException>(() =>
manager.GetMetadataAsync("test", "nonexistent", CancellationToken.None));
}
[Fact]
public void Dispose_DisposesMinioClient()
{
// Arrange
var config = new StorageConfig
{
UsingLocal = false,
MinioConfig = new MinioConfig
{
Endpoint = "localhost:9000",
User = "minioadmin",
Password = "minioadmin"
},
Bucket = "test-bucket"
};
var manager = new StorageManager(config);
// Act
manager.Dispose();
// Assert - Should not throw
Assert.True(true);
}
}