build-flow.api/tests/BuildFlow.File.Tests/FileSizeVerifiedTests.cs
2026-07-24 10:09:12 +08:00

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;
}
}