142 lines
4.8 KiB
C#
142 lines
4.8 KiB
C#
using BuildFlow.Shared.Domain.Common;
|
|
using BuildFlow.Shared.Infrastructure;
|
|
using File.Application.Common;
|
|
using File.Application.Features.Files.Commands;
|
|
using File.Application.Features.Files.Handlers;
|
|
using File.Application.Features.Folders.Commands;
|
|
using File.Application.Features.Folders.Handlers;
|
|
using File.Application.Features.Multipart.Commands;
|
|
using File.Application.Features.Multipart.DTOs;
|
|
using File.Application.Features.Multipart.Handlers;
|
|
using File.Domain.Entities.FileMeta;
|
|
using File.Domain.Entities.MultipartUploadSession;
|
|
using File.Domain.Values;
|
|
using File.Infrastructure.Persistence;
|
|
using File.Infrastructure.Storage;
|
|
using FluentAssertions;
|
|
using MediatR;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.EntityFrameworkCore.Metadata;
|
|
using Moq;
|
|
using Xunit;
|
|
|
|
namespace BuildFlow.File.Tests;
|
|
|
|
/// <summary>文件大小确认状态在上传路径与 EF 模型中的持久化契约测试。</summary>
|
|
public class FileSizeVerifiedTests
|
|
{
|
|
private const string OwnerId = "user-001";
|
|
private const string Bucket = "files";
|
|
|
|
[Fact]
|
|
public async Task DirectZeroByteUploadMarksSizeVerified()
|
|
{
|
|
var (db, s3, user) = CreateDependencies();
|
|
var handler = new UploadFileCommandHandler(
|
|
s3.Object,
|
|
db,
|
|
user.Object,
|
|
CreateBucketResolver(),
|
|
new Mock<IMediator>().Object);
|
|
|
|
await handler.Handle(
|
|
new UploadFileCommand("empty.txt", Stream.Null, "text/plain", 0),
|
|
CancellationToken.None);
|
|
|
|
var file = await db.Files.SingleAsync();
|
|
file.Size.Should().Be(0);
|
|
file.SizeVerified.Should().BeTrue();
|
|
}
|
|
|
|
[Fact]
|
|
public async Task FolderUploadMarksSizeVerified()
|
|
{
|
|
var (db, s3, user) = CreateDependencies();
|
|
var handler = new UploadToFolderCommandHandler(
|
|
db,
|
|
s3.Object,
|
|
user.Object,
|
|
CreateBucketResolver());
|
|
|
|
await handler.Handle(
|
|
new UploadToFolderCommand(
|
|
Guid.NewGuid(),
|
|
"report.pdf",
|
|
new MemoryStream([1, 2, 3]),
|
|
"application/pdf",
|
|
3),
|
|
CancellationToken.None);
|
|
|
|
(await db.Files.SingleAsync()).SizeVerified.Should().BeTrue();
|
|
}
|
|
|
|
[Fact]
|
|
public async Task CompletedMultipartUploadMarksSizeVerified()
|
|
{
|
|
var (db, s3, user) = CreateDependencies();
|
|
db.UploadSessions.Add(new MultipartUploadSessionEntity
|
|
{
|
|
BucketName = Bucket,
|
|
ObjectKey = "multipart-key",
|
|
UploadId = "upload-1",
|
|
OwnerId = OwnerId,
|
|
Status = UploadSessionStatus.Active,
|
|
});
|
|
await db.SaveChangesAsync();
|
|
var handler = new CompleteMultipartCommandHandler(
|
|
s3.Object,
|
|
db,
|
|
user.Object,
|
|
CreateBucketResolver());
|
|
|
|
await handler.Handle(
|
|
new CompleteMultipartCommand(
|
|
"multipart-key",
|
|
"upload-1",
|
|
[new CompletedPartDto(1, "etag-1")],
|
|
"archive.zip",
|
|
1024),
|
|
CancellationToken.None);
|
|
|
|
(await db.Files.SingleAsync()).SizeVerified.Should().BeTrue();
|
|
}
|
|
|
|
[Fact]
|
|
public void ModelHasPendingSizeVerificationIndex()
|
|
{
|
|
var (db, _, _) = CreateDependencies();
|
|
var entity = db.Model.FindEntityType(typeof(FileMetaEntity));
|
|
|
|
entity.Should().NotBeNull();
|
|
var sizeVerified = entity!.FindProperty(nameof(FileMetaEntity.SizeVerified));
|
|
sizeVerified.Should().NotBeNull();
|
|
sizeVerified!.GetColumnName(StoreObjectIdentifier.Table("b_files", null))
|
|
.Should().Be("size_verified");
|
|
entity!.GetIndexes().Should().Contain(index =>
|
|
index.Properties.Select(property => property.Name)
|
|
.SequenceEqual(new[] { nameof(FileMetaEntity.SizeVerified), nameof(FileMetaEntity.CreatedAt) }));
|
|
}
|
|
|
|
private static (FileDbContext Db, Mock<IS3StorageService> S3, Mock<ICurrentUserContext> User)
|
|
CreateDependencies()
|
|
{
|
|
var user = new Mock<ICurrentUserContext>();
|
|
user.SetupGet(context => context.UserId).Returns(OwnerId);
|
|
user.SetupGet(context => context.IPAddress).Returns("127.0.0.1");
|
|
var options = new DbContextOptionsBuilder<FileDbContext>()
|
|
.UseInMemoryDatabase($"file-size-verified-{Guid.NewGuid():N}")
|
|
.AddInterceptors(new AuditInterceptor(user.Object))
|
|
.Options;
|
|
|
|
return (new FileDbContext(options), new Mock<IS3StorageService>(), user);
|
|
}
|
|
|
|
private static IBucketResolver CreateBucketResolver()
|
|
{
|
|
var resolver = new Mock<IBucketResolver>();
|
|
resolver.Setup(x => x.GetDefaultBucketAsync(It.IsAny<CancellationToken>()))
|
|
.ReturnsAsync(Bucket);
|
|
return resolver.Object;
|
|
}
|
|
}
|