using System.Text.Json;
using FluentAssertions;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using global::BuildFlow.Shared.Domain.Common;
using Workflow.Application.Engine;
using Workflow.Domain.Engine;
using Workflow.Domain.Entities;
using Workflow.Domain.Enums;
using Workflow.Domain.Expressions;
using TaskStatus = global::Workflow.Domain.Enums.TaskStatus;
using InstanceStatus = global::Workflow.Domain.Enums.InstanceStatus;
using Xunit;
namespace BuildFlow.Workflow.Tests.Integration;
///
/// 钉钉式审批流全链路端到端测试:树 → 编译 → 图落库读回 → ProcessEngine 启动实例 → 审批完成。
/// 这条链路证明:前端树编辑器产出的树,经 TreeToGraphCompiler 编译后,能被真实 ProcessEngine
/// 加载运行并完成审批。是"钉钉式设计器重做"最权威的运行时验证。
///
public class TreeEndToEndTests
{
private static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web);
///
/// 构造一棵"发起人 → 依次审批(A,B) → 结束"的树,编译成图,启动实例,A 通过后建 B,
/// B 通过后实例完成。
///
[Fact]
public async Task Tree_ApprovalChain_RunsThroughEngine_ToCompletion()
{
// 1. 构造树
var userA = Guid.NewGuid();
var userB = Guid.NewGuid();
var tree = new
{
root = new
{
id = Guid.NewGuid().ToString(),
type = "Initiator",
config = new { title = "发起人" },
child = new
{
id = Guid.NewGuid().ToString(),
type = "Approval",
config = new
{
title = "依次审批",
approvalMode = "sequential",
assigneeRules = new[] { $"user:{userA}", $"user:{userB}" },
},
},
},
};
var treeJson = JsonSerializer.Serialize(tree, JsonOptions);
// 2. 编译成图
var parsedTree = JsonSerializer.Deserialize(treeJson, JsonOptions)!;
var compiledGraph = TreeToGraphCompiler.Compile(parsedTree);
var graphJson = JsonSerializer.Serialize(compiledGraph, JsonOptions);
// 编译产物必须通过发布校验
var act = () => WorkflowGraphValidator.Validate(compiledGraph);
act.Should().NotThrow();
// 3. 落库(DefinitionJson = 编译产物)并读回,模拟引擎从快照加载
var options = new DbContextOptionsBuilder()
.UseInMemoryDatabase($"tree-e2e-{Guid.NewGuid()}").Options;
var db = new WorkflowDbContext(options);
db.SavingChanges += (sender, _) =>
{
if (sender is not DbContext ctx) return;
foreach (var entry in ctx.ChangeTracker.Entries())
{
if (entry.Entity is IAuditable a) { a.CreatedBy ??= "test"; a.UpdatedBy ??= "test"; }
if (entry.Entity is IHasOperatorIP o) { o.OperatorIP ??= "127.0.0.1"; }
}
};
// 引擎 ProcessEngine.LoadGraphAsync 优先读 DefinitionJson(整图 JSON)。
// 把编译产物作为 WorkflowDefinition.DefinitionJson 存入。
var definition = new WorkflowDefinition
{
Id = Guid.NewGuid(),
Name = "依次审批流程",
Code = $"seq-e2e-{Guid.NewGuid():N}",
Status = DefinitionStatus.Published,
Version = 1,
IsEnabled = true,
DefinitionJson = graphJson,
DefinitionTree = treeJson,
CreatedBy = "test",
UpdatedBy = "test",
};
db.WorkflowDefinitions.Add(definition);
await db.SaveChangesAsync();
// 4. 启动实例
var engine = new ProcessEngine(db, new ServiceCollection().BuildServiceProvider(), new ConditionEvaluator());
var instance = new WorkflowInstance
{
Id = Guid.NewGuid(),
DefinitionId = definition.Id,
DefinitionCode = definition.Code,
DefinitionVersion = 1,
Title = "e2e-instance",
Status = InstanceStatus.Pending,
Variables = "{}",
InitiatorId = Guid.NewGuid(),
CreatedBy = "test",
UpdatedBy = "test",
};
db.WorkflowInstances.Add(instance);
await db.SaveChangesAsync();
await engine.StartAsync(instance);
// 启动后:依次审批只建 A 的任务(SequentialOrder=0)
var tasks = await db.WorkflowTasks
.Where(t => t.Status == TaskStatus.Pending)
.ToListAsync();
tasks.Should().HaveCount(1);
tasks[0].AssigneeId.Should().Be(userA, "依次审批首个任务应分配给 A");
tasks[0].SequentialOrder.Should().Be(0);
// 5. A 通过 → 建 B 的任务,实例未完成
await engine.CompleteTaskAsync(tasks[0], TaskResult.Approved);
var taskB = await db.WorkflowTasks
.SingleOrDefaultAsync(t => t.SequentialOrder == 1 && t.Status == TaskStatus.Pending);
taskB.Should().NotBeNull("A 通过后应建 B 的任务");
taskB!.AssigneeId.Should().Be(userB);
instance.Status.Should().NotBe(InstanceStatus.Completed, "B 未审完,实例不应完成");
// 6. B 通过 → 实例完成(token 到达 End)
await engine.CompleteTaskAsync(taskB, TaskResult.Approved);
instance.Status.Should().Be(InstanceStatus.Completed, "依次审批全部通过后实例应完成");
}
}