build-flow.api/tests/BuildFlow.Scripting.Tests/ScriptCommandHandlerTests.cs
xiangning 3b13099378 refactor(domain): 重构领域实体结构并更新枚举命名
- 将通用 Entities 命名空间拆分为具体业务实体子目录
- 更新所有实体类型引用为具体的 Entity 后缀类型
- 将 ModelType、ModelProvider、GroupMemberRole 等枚举重命名为 Enum 后缀
- 重构 AiModel 相关配置和映射逻辑适配新的实体结构
- 更新测试代码中的实体类型引用和枚举使用
- 调整应用层处理器中的实体创建和查询逻辑
- 修改 appsettings.json 中的基础设施主机配置为动态占位符模式
2026-07-19 14:02:32 +08:00

186 lines
7.4 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.Exceptions;
using FluentAssertions;
using Microsoft.EntityFrameworkCore;
using Scripting.Application.Commands;
using Scripting.Application.Handlers;
using Scripting.Domain.Entities.ScriptExecutionLog;
using Scripting.Domain.Entities.ScriptFunction;
using Scripting.Domain.Enums;
using Scripting.Infrastructure.Persistence;
using Xunit;
namespace BuildFlow.Scripting.Tests;
/// <summary>
/// 脚本 CQRS 命令处理器测试(spec §13 #6 可编辑性、生命周期守卫)。
/// 用真实 InMemory ScriptingDbContext 直接实例化 handler,覆盖:
/// Create(唯一名校验 + 默认超时)、Update(Disabled 拒绝 + Published 自增版本)、
/// Delete(Published 拒绝 + Draft 软删除)、Publish(状态 + 版本)。
/// </summary>
public class ScriptCommandHandlerTests
{
// ── Create ─────────────────────────────────────────────────────────────
[Fact]
public async Task CreateScript_DuplicateName_ThrowsBusinessException()
{
await using var db = ScriptingTestDbFactory.Create();
await SeedAsync(db, name: "dup-fn");
var handler = new CreateScriptCommandHandler(db);
var act = () => handler.Handle(
new CreateScriptCommand("dup-fn", "显示名", null, "return 1;", null, null, 0),
CancellationToken.None);
await act.Should().ThrowAsync<BusinessException>().WithMessage("*已存在*");
}
[Fact]
public async Task CreateScript_DefaultsTimeoutTo5000()
{
await using var db = ScriptingTestDbFactory.Create();
var handler = new CreateScriptCommandHandler(db);
var dto = await handler.Handle(
new CreateScriptCommand("default-timeout", "默认超时", null, "return 1;", null, null, 0),
CancellationToken.None);
dto.TimeoutMs.Should().Be(5000);
dto.Status.Should().Be(ScriptStatusEnum.Draft);
dto.Version.Should().Be(1);
var entity = await db.Scripts.FindAsync(dto.Id);
entity.Should().NotBeNull();
entity!.TimeoutMs.Should().Be(5000);
}
[Fact]
public async Task CreateScript_NewName_PersistsEntity()
{
await using var db = ScriptingTestDbFactory.Create();
var handler = new CreateScriptCommandHandler(db);
var dto = await handler.Handle(
new CreateScriptCommand("unique-fn", "唯一函数", "desc", "return 42;", null, null, 3000),
CancellationToken.None);
dto.Name.Should().Be("unique-fn");
dto.Code.Should().Be("return 42;");
dto.TimeoutMs.Should().Be(3000);
}
// ── Update ─────────────────────────────────────────────────────────────
[Fact]
public async Task UpdateScript_DisabledScript_Throws()
{
await using var db = ScriptingTestDbFactory.Create();
var script = await SeedAsync(db, name: "disabled-fn", status: ScriptStatusEnum.Disabled);
var handler = new UpdateScriptCommandHandler(db);
var act = () => handler.Handle(
new UpdateScriptCommand(script.Id, "新名", null, "return 2;", null, null, 5000),
CancellationToken.None);
await act.Should().ThrowAsync<BusinessException>().WithMessage("*不能编辑*");
}
[Fact]
public async Task UpdateScript_Published_IncrementsVersion()
{
await using var db = ScriptingTestDbFactory.Create();
var script = await SeedAsync(db, name: "pub-fn", status: ScriptStatusEnum.Published, version: 3);
var handler = new UpdateScriptCommandHandler(db);
var dto = await handler.Handle(
new UpdateScriptCommand(script.Id, "改后", null, "return 99;", null, null, 5000),
CancellationToken.None);
dto.Version.Should().Be(4);
dto.DisplayName.Should().Be("改后");
dto.Code.Should().Be("return 99;");
dto.Status.Should().Be(ScriptStatusEnum.Published); // 保持已发布
}
[Fact]
public async Task UpdateScript_Draft_KeepsVersionAtOne()
{
await using var db = ScriptingTestDbFactory.Create();
var script = await SeedAsync(db, name: "draft-fn", status: ScriptStatusEnum.Draft);
var handler = new UpdateScriptCommandHandler(db);
var dto = await handler.Handle(
new UpdateScriptCommand(script.Id, "草稿改", null, "return 1;", null, null, 5000),
CancellationToken.None);
dto.Version.Should().Be(1); // 草稿编辑不递增版本
}
// ── Delete ─────────────────────────────────────────────────────────────
[Fact]
public async Task DeleteScript_Published_Throws()
{
await using var db = ScriptingTestDbFactory.Create();
var script = await SeedAsync(db, name: "del-pub", status: ScriptStatusEnum.Published);
var handler = new DeleteScriptCommandHandler(db);
var act = () => handler.Handle(new DeleteScriptCommand(script.Id), CancellationToken.None);
await act.Should().ThrowAsync<BusinessException>().WithMessage("*不能删除*");
}
[Fact]
public async Task DeleteScript_Draft_SoftDeletes()
{
await using var db = ScriptingTestDbFactory.Create();
var script = await SeedAsync(db, name: "del-draft", status: ScriptStatusEnum.Draft);
var handler = new DeleteScriptCommandHandler(db);
await handler.Handle(new DeleteScriptCommand(script.Id), CancellationToken.None);
var entity = await db.Scripts.IgnoreQueryFilters().FirstAsync(x => x.Id == script.Id);
entity.IsDeleted.Should().BeTrue();
}
// ── Publish ────────────────────────────────────────────────────────────
[Fact]
public async Task PublishScript_SetsPublishedAndIncrementsVersion()
{
await using var db = ScriptingTestDbFactory.Create();
var script = await SeedAsync(db, name: "to-publish", status: ScriptStatusEnum.Draft, version: 1);
var handler = new PublishScriptCommandHandler(db);
var dto = await handler.Handle(new PublishScriptCommand(script.Id), CancellationToken.None);
dto.Status.Should().Be(ScriptStatusEnum.Published);
dto.Version.Should().Be(2);
}
// ── 夹具 ───────────────────────────────────────────────────────────────
private static async Task<ScriptFunctionEntity> SeedAsync(
ScriptingDbContext db,
string name,
ScriptStatusEnum status = ScriptStatusEnum.Draft,
int version = 1)
{
var script = new ScriptFunctionEntity
{
Id = Guid.NewGuid(),
Name = name,
DisplayName = name,
Code = "return 1;",
Status = status,
Version = version,
TimeoutMs = 5000,
CreatedBy = "test",
UpdatedBy = "test",
OperatorIP = "127.0.0.1",
};
db.Scripts.Add(script);
await db.SaveChangesAsync();
return script;
}
}