using FluentAssertions; using MediatR; using Workflow.Application.Integration.Commands; using Workflow.Application.Integration.Handlers; using Workflow.Application.Integration.Queries; using Workflow.Application.Features.WorkflowInstances.Commands; using Workflow.Domain.Entities; using Workflow.Domain.Enums; using Xunit; namespace BuildFlow.Workflow.Tests.Integration; /// /// AI/RAG 触发工作流的 CQRS 契约测试,禁止回退到 shared service bridge。 /// public class WorkflowTriggerCqrsTests { [Fact] public async Task StartWorkflowByNameCommand_ShouldDispatchStartInstanceCommand() { await using var db = TestDbFactory.Create(); var definition = new WorkflowDefinition { Id = Guid.NewGuid(), Name = "报销审批", Code = "expense-approval", Status = DefinitionStatus.Published, Version = 1, IsEnabled = true, }; db.WorkflowDefinitions.Add(definition); await db.SaveChangesAsync(); var instanceId = Guid.NewGuid(); var sender = new TestSender(instanceId); var handler = new StartWorkflowByNameCommandHandler(db, sender); var result = await handler.Handle( new StartWorkflowByNameCommand("报销", Guid.NewGuid(), """{"amount":1200}"""), CancellationToken.None); result.Success.Should().BeTrue(); result.InstanceId.Should().Be(instanceId); result.Title.Should().Be("报销"); sender.StartCommand.Should().NotBeNull(); sender.StartCommand!.DefinitionCode.Should().Be("expense-approval"); sender.StartCommand.Title.Should().StartWith("报销-"); sender.StartCommand.Variables.Should().Be("""{"amount":1200}"""); } [Fact] public async Task GetTriggerableWorkflowDefinitionsQuery_ShouldReturnPublishedDefinitions() { await using var db = TestDbFactory.Create(); db.WorkflowDefinitions.AddRange( new WorkflowDefinition { Id = Guid.NewGuid(), Name = "报销审批", Code = "expense-approval", Status = DefinitionStatus.Published, Version = 1, IsEnabled = true, }, new WorkflowDefinition { Id = Guid.NewGuid(), Name = "草稿流程", Code = "draft-flow", Status = DefinitionStatus.Draft, Version = 1, IsEnabled = true, }); await db.SaveChangesAsync(); var handler = new GetTriggerableWorkflowDefinitionsQueryHandler(db); var result = await handler.Handle(new GetTriggerableWorkflowDefinitionsQuery(), CancellationToken.None); result.Should().ContainSingle(); result[0].Key.Should().Be("报销审批"); result[0].Name.Should().Be("报销审批"); } private sealed class TestSender(Guid instanceId) : ISender { public StartWorkflowInstanceCommand? StartCommand { get; private set; } public Task Send(IRequest request, CancellationToken cancellationToken = default) { if (request is StartWorkflowInstanceCommand command && typeof(TResponse) == typeof(Guid)) { StartCommand = command; return Task.FromResult((TResponse)(object)instanceId); } throw new NotSupportedException($"Unexpected request {request.GetType().Name}"); } public Task Send(TRequest request, CancellationToken cancellationToken = default) where TRequest : IRequest => throw new NotSupportedException($"Unexpected request {typeof(TRequest).Name}"); public Task Send(object request, CancellationToken cancellationToken = default) => throw new NotSupportedException($"Unexpected request {request.GetType().Name}"); public IAsyncEnumerable CreateStream( IStreamRequest request, CancellationToken cancellationToken = default) => throw new NotSupportedException($"Unexpected stream request {request.GetType().Name}"); public IAsyncEnumerable CreateStream(object request, CancellationToken cancellationToken = default) => throw new NotSupportedException($"Unexpected stream request {request.GetType().Name}"); } }