work-flow/tests/Workflow.Tests/Handlers/EdgeCommandHandlerTests.cs
2026-05-21 14:46:32 +08:00

233 lines
6.9 KiB
C#

using FluentAssertions;
using Microsoft.EntityFrameworkCore;
using Workflow.Application.Features.WorkflowDefinitions.Commands;
using Workflow.Domain.Entities;
using Workflow.Domain.Enums;
using Workflow.Domain.Exceptions;
using Workflow.Infrastructure.Persistence;
using Xunit;
namespace Workflow.Tests.Handlers;
public class EdgeCommandHandlerTests
{
private static WorkflowDbContext CreateDbContext()
{
var options = new DbContextOptionsBuilder<WorkflowDbContext>()
.UseInMemoryDatabase(Guid.NewGuid().ToString())
.Options;
return new WorkflowDbContext(options);
}
private static async Task<(WorkflowDefinition Definition, WorkflowNode SourceNode, WorkflowNode TargetNode)> SeedDefinitionWithNodes(WorkflowDbContext db)
{
var definition = new WorkflowDefinition
{
Id = Guid.NewGuid(),
Name = "Test Workflow",
Code = $"test-wf-{Guid.NewGuid():N}",
Status = DefinitionStatus.Draft,
Version = 1
};
db.WorkflowDefinitions.Add(definition);
var sourceNode = new WorkflowNode
{
Id = Guid.NewGuid(),
DefinitionId = definition.Id,
NodeType = NodeType.Start,
Name = "Start",
PositionX = 0,
PositionY = 0
};
var targetNode = new WorkflowNode
{
Id = Guid.NewGuid(),
DefinitionId = definition.Id,
NodeType = NodeType.End,
Name = "End",
PositionX = 300,
PositionY = 0
};
db.WorkflowNodes.AddRange(sourceNode, targetNode);
await db.SaveChangesAsync();
return (definition, sourceNode, targetNode);
}
#region CreateEdge
[Fact]
public async Task CreateEdge_ReturnsEdgeDto()
{
// Arrange
await using var db = CreateDbContext();
var (definition, sourceNode, targetNode) = await SeedDefinitionWithNodes(db);
var handler = new CreateEdgeCommandHandler(db);
var command = new CreateEdgeCommand(
DefinitionId: definition.Id,
SourceNodeId: sourceNode.Id,
TargetNodeId: targetNode.Id,
EdgeType: EdgeType.Normal,
Label: "Normal Flow",
Condition: null,
Order: 1
);
// Act
var result = await handler.Handle(command, CancellationToken.None);
// Assert
result.Should().NotBeNull();
result.SourceNodeId.Should().Be(sourceNode.Id);
result.TargetNodeId.Should().Be(targetNode.Id);
result.EdgeType.Should().Be(EdgeType.Normal);
result.Label.Should().Be("Normal Flow");
result.Condition.Should().BeNull();
result.Order.Should().Be(1);
result.Id.Should().NotBeEmpty();
var entity = await db.WorkflowEdges.FindAsync(result.Id);
entity.Should().NotBeNull();
entity!.DefinitionId.Should().Be(definition.Id);
}
[Fact]
public async Task CreateEdge_WithMissingSource_ThrowsNotFoundException()
{
// Arrange
await using var db = CreateDbContext();
var (definition, _, targetNode) = await SeedDefinitionWithNodes(db);
var handler = new CreateEdgeCommandHandler(db);
var command = new CreateEdgeCommand(
DefinitionId: definition.Id,
SourceNodeId: Guid.NewGuid(), // Non-existent source
TargetNodeId: targetNode.Id,
EdgeType: EdgeType.Normal,
Label: null,
Condition: null,
Order: 0
);
// Act
var act = () => handler.Handle(command, CancellationToken.None);
// Assert
await act.Should().ThrowAsync<NotFoundException>();
}
#endregion
#region UpdateEdge
[Fact]
public async Task UpdateEdge_UpdatesLabelAndCondition()
{
// Arrange
await using var db = CreateDbContext();
var (definition, sourceNode, targetNode) = await SeedDefinitionWithNodes(db);
var edgeId = Guid.NewGuid();
db.WorkflowEdges.Add(new WorkflowEdge
{
Id = edgeId,
DefinitionId = definition.Id,
SourceNodeId = sourceNode.Id,
TargetNodeId = targetNode.Id,
EdgeType = EdgeType.Normal,
Label = "Old Label",
Condition = null,
Order = 0
});
await db.SaveChangesAsync();
var handler = new UpdateEdgeCommandHandler(db);
var command = new UpdateEdgeCommand(
EdgeId: edgeId,
EdgeType: EdgeType.Approved,
Label: "Approved Path",
Condition: "amount > 5000",
Order: 2
);
// Act
var result = await handler.Handle(command, CancellationToken.None);
// Assert
result.Should().NotBeNull();
result.Id.Should().Be(edgeId);
result.Label.Should().Be("Approved Path");
result.Condition.Should().Be("amount > 5000");
result.EdgeType.Should().Be(EdgeType.Approved);
result.Order.Should().Be(2);
result.SourceNodeId.Should().Be(sourceNode.Id);
result.TargetNodeId.Should().Be(targetNode.Id);
var entity = await db.WorkflowEdges.FindAsync(edgeId);
entity.Should().NotBeNull();
entity!.Label.Should().Be("Approved Path");
entity.Condition.Should().Be("amount > 5000");
}
[Fact]
public async Task UpdateEdge_WithMissingEdge_ThrowsNotFoundException()
{
// Arrange
await using var db = CreateDbContext();
var handler = new UpdateEdgeCommandHandler(db);
var command = new UpdateEdgeCommand(
EdgeId: Guid.NewGuid(),
EdgeType: EdgeType.Normal,
Label: "Ghost",
Condition: null,
Order: 0
);
// Act
var act = () => handler.Handle(command, CancellationToken.None);
// Assert
await act.Should().ThrowAsync<NotFoundException>();
}
#endregion
#region DeleteEdge
[Fact]
public async Task DeleteEdge_RemovesEdge()
{
// Arrange
await using var db = CreateDbContext();
var (definition, sourceNode, targetNode) = await SeedDefinitionWithNodes(db);
var edgeId = Guid.NewGuid();
db.WorkflowEdges.Add(new WorkflowEdge
{
Id = edgeId,
DefinitionId = definition.Id,
SourceNodeId = sourceNode.Id,
TargetNodeId = targetNode.Id,
EdgeType = EdgeType.Normal,
Label = "To Delete",
Condition = null,
Order = 0
});
await db.SaveChangesAsync();
var handler = new DeleteEdgeCommandHandler(db);
var command = new DeleteEdgeCommand(EdgeId: edgeId);
// Act
await handler.Handle(command, CancellationToken.None);
// Assert
var edges = await db.WorkflowEdges.ToListAsync();
edges.Should().BeEmpty();
}
#endregion
}