build-flow.api/tests/BuildFlow.File.Tests/UploadFileCommandHandlerTests.cs
xiangning 6c110b579a refactor(dependencies): 移除未使用的域实体引用
- 从文件模块的多部分上传处理器中移除未使用的实体引用
- 从即时通讯模块的群组成员处理器中移除未使用的实体引用
- 从RAG模块的AI模型配置中移除未使用的实体引用
- 从监控模块的告警规则配置中移除未使用的实体引用
- 从平台模块的角色权限处理器中移除未使用的实体引用
- 从脚本模块的执行日志实体引用中进行清理
- 从多个基础设施配置类中移除未使用的依赖项
- 优化测试文件中的导入声明,移除不必要的引用
2026-07-19 14:09:47 +08:00

107 lines
4.6 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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.Domain.Entities.FileMeta;
using File.Domain.Exceptions;
using File.Infrastructure.Persistence;
using File.Infrastructure.Storage;
using FluentAssertions;
using Microsoft.EntityFrameworkCore;
using Moq;
using Xunit;
namespace BuildFlow.File.Tests;
/// <summary>
/// 直传上传处理器测试。验证缺陷 C 修复:直传完成后落 FileMetaEntity,使直传路径参与秒传。
/// </summary>
public class UploadFileCommandHandlerTests
{
private const string OwnerId = "user-001";
private const string Sha = "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824";
/// <summary>测试用 bucket 解析器,固定返回 "files"。</summary>
private static IBucketResolver BucketResolver(string bucket = "files")
{
var mock = new Mock<IBucketResolver>();
mock.Setup(r => r.GetDefaultBucketAsync(It.IsAny<CancellationToken>())).ReturnsAsync(bucket);
return mock.Object;
}
private static (FileDbContext db, Mock<IS3StorageService> s3, Mock<ICurrentUserContext> user) CreateDeps(string dbName)
{
var user = new Mock<ICurrentUserContext>();
user.SetupGet(u => u.UserId).Returns(OwnerId);
user.SetupGet(u => u.IPAddress).Returns("127.0.0.1");
var s3 = new Mock<IS3StorageService>();
var options = new DbContextOptionsBuilder<FileDbContext>()
.UseInMemoryDatabase(dbName)
.AddInterceptors(new AuditInterceptor(user.Object))
.Options;
return (new FileDbContext(options), s3, user);
}
[Fact]
public async Task Upload_成功后落FileMeta_含ContentHash_使直传参与秒传()
{
// 缺陷C核心:直传必须落 FileMetaEntity,否则直传的文件永远无法被秒传
var (db, s3, user) = CreateDeps(nameof(Upload_成功后落FileMeta_含ContentHash_使直传参与秒传));
var data = new MemoryStream([1, 2, 3]);
var handler = new UploadFileCommandHandler(s3.Object, db, user.Object, BucketResolver(), new Mock<MediatR.IMediator>().Object);
var result = await handler.Handle(
new UploadFileCommand("obj-key", data, "application/octet-stream", 3, Sha),
CancellationToken.None);
result.ObjectKey.Should().Be("obj-key");
s3.Verify(x => x.UploadFileAsync("files", "obj-key", It.IsAny<Stream>(), "application/octet-stream", It.IsAny<CancellationToken>()), Times.Once);
// FileMetaEntity 应被创建,含 ContentHash,FolderId=null(根目录散落)
var meta = await db.Files.SingleAsync();
meta.S3Key.Should().Be("obj-key");
meta.S3Bucket.Should().Be("files");
meta.Size.Should().Be(3);
meta.ContentHash.Should().Be(Sha);
meta.FolderId.Should().BeNull();
meta.OwnerId.Should().Be(OwnerId);
}
[Fact]
public async Task Upload_超过500MiB_抛TooLarge()
{
var (db, s3, user) = CreateDeps(nameof(Upload_超过500MiB_抛TooLarge));
var handler = new UploadFileCommandHandler(s3.Object, db, user.Object, BucketResolver(), new Mock<MediatR.IMediator>().Object);
var act = () => handler.Handle(
new UploadFileCommand("big", new MemoryStream(), "application/octet-stream", 501 * 1024 * 1024),
CancellationToken.None);
await act.Should().ThrowAsync<TooLargeException>();
s3.Verify(x => x.UploadFileAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<Stream>(), It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Never);
}
[Fact]
public async Task Upload_同对象已存在FileMeta_不重复落库()
{
// 幂等:同一 (bucket, key) 已有 FileMetaEntity,再次上传不重复 Add
var (db, s3, user) = CreateDeps(nameof(Upload_同对象已存在FileMeta_不重复落库));
db.Files.Add(new FileMetaEntity
{
Name = "obj-key", S3Key = "obj-key", S3Bucket = "files",
Size = 3, ContentType = "application/octet-stream", ContentHash = Sha, OwnerId = OwnerId,
});
await db.SaveChangesAsync();
var handler = new UploadFileCommandHandler(s3.Object, db, user.Object, BucketResolver(), new Mock<MediatR.IMediator>().Object);
await handler.Handle(
new UploadFileCommand("obj-key", new MemoryStream([1, 2, 3]), "application/octet-stream", 3, Sha),
CancellationToken.None);
// 仍只有一条记录
var count = await db.Files.CountAsync();
count.Should().Be(1);
}
}