Skip to content

Commit 8d480f5

Browse files
committed
initial commit
1 parent 750b249 commit 8d480f5

20 files changed

+1304
-2
lines changed

FlexFlow.Tests/BranchingWorkflow.cs

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using FlexFlow.Interfaces;
2+
3+
namespace FlexFlow.Tests;
4+
5+
public class BranchingWorkflow : IWorkflow<string, ValidatedMessage>
6+
{
7+
public void Build(IWorkflowBuilder<string, ValidatedMessage> builder)
8+
{
9+
builder
10+
.StartWith(input => new ParseStep().ExecuteAsync(input))
11+
.Then(parsed => new ValidateStep().ExecuteAsync(parsed))
12+
.Branch<ValidatedMessage>(
13+
message => Task.FromResult(message.IsValid),
14+
trueBuilder => Task.FromResult(trueBuilder
15+
.StartWith(message => new ProcessValidStep().ExecuteAsync(message))),
16+
falseBuilder => Task.FromResult(falseBuilder
17+
.StartWith(message => new ProcessInvalidStep().ExecuteAsync(message)))
18+
);
19+
}
20+
}
+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
using FluentAssertions;
2+
3+
4+
namespace FlexFlow.Tests;
5+
6+
[TestFixture]
7+
public class BranchingWorkflowTests
8+
{
9+
private WorkflowBuilder<string, ValidatedMessage> _builder;
10+
private Workflow<string, ValidatedMessage> _workflow;
11+
12+
[SetUp]
13+
public async Task Setup()
14+
{
15+
_builder = new WorkflowBuilder<string, ValidatedMessage>();
16+
var workflowDefinition = new BranchingWorkflow();
17+
workflowDefinition.Build(_builder);
18+
_workflow = await _builder.BuildAsync();
19+
}
20+
21+
[Test]
22+
public async Task Workflow_Should_Process_Valid_Message_Successfully()
23+
{
24+
// Arrange
25+
var input = "Valid message from Device-1";
26+
27+
// Act
28+
var result = await _workflow.ExecuteAsync(input);
29+
30+
// Assert
31+
result.IsSuccess.Should().BeTrue();
32+
result.Value.Should().NotBeNull();
33+
result.Value.IsValid.Should().BeTrue();
34+
result.Value.Content.Should().Be(input);
35+
result.Value.DeviceId.Should().Be("Device-1");
36+
}
37+
38+
[Test]
39+
public async Task Workflow_Should_Process_Invalid_Message_As_Failure()
40+
{
41+
// Arrange
42+
var input = "Invalid message from Device-2";
43+
44+
// Act
45+
var result = await _workflow.ExecuteAsync(input);
46+
47+
// Assert
48+
result.IsSuccess.Should().BeFalse();
49+
result.Error.Should().Be("Invalid message processed");
50+
}
51+
52+
[Test]
53+
public async Task Workflow_Should_Handle_Edge_Case_Messages()
54+
{
55+
// Arrange
56+
var edgeCaseInput = "Edge case Valid message from Device-3";
57+
58+
// Act
59+
var result = await _workflow.ExecuteAsync(edgeCaseInput);
60+
61+
// Assert
62+
result.IsSuccess.Should().BeTrue();
63+
result.Value.Should().NotBeNull();
64+
result.Value.IsValid.Should().BeTrue();
65+
result.Value.Content.Should().Be(edgeCaseInput);
66+
result.Value.DeviceId.Should().Be("Device-1"); // Because it contains "Valid"
67+
}
68+
69+
[Test]
70+
public async Task Workflow_Should_Handle_Empty_Input()
71+
{
72+
// Arrange
73+
var emptyInput = "";
74+
75+
// Act
76+
var result = await _workflow.ExecuteAsync(emptyInput);
77+
78+
// Assert
79+
result.IsSuccess.Should().BeFalse();
80+
result.Error.Should().Be("Invalid message processed");
81+
}
82+
}

FlexFlow.Tests/ComplexWorkflow.cs

+121
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
using FlexFlow.Interfaces;
2+
using Microsoft.Extensions.Logging;
3+
4+
namespace FlexFlow.Tests;
5+
6+
public class Order
7+
{
8+
public int Id { get; set; }
9+
public decimal TotalAmount { get; set; }
10+
public bool IsProcessed { get; set; }
11+
public bool IsShipped { get; set; }
12+
public int RetryCount { get; set; }
13+
}
14+
15+
public class ProcessingException(string message) : Exception(message)
16+
{
17+
}
18+
19+
public class ComplexWorkflow(ILogger<ComplexWorkflow> logger) : IWorkflow<int, Order>
20+
{
21+
public void Build(IWorkflowBuilder<int, Order> builder)
22+
{
23+
builder
24+
.StartWith(CreateOrder)
25+
.Then(ValidateOrder)
26+
.Branch<Order>(
27+
order => Task.FromResult(order.TotalAmount > 1000),
28+
highValueBranch => Task.FromResult(highValueBranch
29+
.Then(ApplyDiscount)
30+
.Then(ProcessHighValueOrder)),
31+
lowValueBranch => Task.FromResult(lowValueBranch
32+
.Then(ProcessLowValueOrder))
33+
)
34+
.Then(ProcessPayment)
35+
.Retry(3, TimeSpan.FromSeconds(1))
36+
.Catch<ProcessingException>(HandleProcessingException)
37+
.If(
38+
order => Task.FromResult(!order.IsProcessed),
39+
retryBranch => Task.FromResult(retryBranch.Then(RetryProcessing))
40+
)
41+
.While(
42+
order => Task.FromResult(!order.IsShipped),
43+
shippingBranch => Task.FromResult(shippingBranch.Then(AttemptShipping))
44+
)
45+
.Map(FinalizeOrder)
46+
.WithLogging(logger);
47+
}
48+
49+
private Task<Result<Order>> CreateOrder(int orderId)
50+
{
51+
var order = new Order { Id = orderId, TotalAmount = new Random().Next(500, 2000) };
52+
return Task.FromResult(Result<Order>.Success(order));
53+
}
54+
55+
private Task<Result<Order>> ValidateOrder(Order order)
56+
{
57+
if (order.TotalAmount <= 0)
58+
{
59+
return Task.FromResult(Result<Order>.Failure("Invalid order amount"));
60+
}
61+
return Task.FromResult(Result<Order>.Success(order));
62+
}
63+
64+
private Task<Result<Order>> ApplyDiscount(Order order)
65+
{
66+
order.TotalAmount *= 0.9m;
67+
return Task.FromResult(Result<Order>.Success(order));
68+
}
69+
70+
private async Task<Result<Order>> ProcessHighValueOrder(Order order)
71+
{
72+
// Simulate complex processing
73+
await Task.Delay(100);
74+
order.IsProcessed = true;
75+
return Result<Order>.Success(order);
76+
}
77+
78+
private Task<Result<Order>> ProcessLowValueOrder(Order order)
79+
{
80+
// Simulate simple processing
81+
order.IsProcessed = true;
82+
return Task.FromResult(Result<Order>.Success(order));
83+
}
84+
85+
private Task<Result<Order>> ProcessPayment(Order order)
86+
{
87+
if (new Random().Next(0, 10) < 3) // 80% chance of failure
88+
{
89+
throw new ProcessingException("Payment processing failed");
90+
}
91+
return Task.FromResult(Result<Order>.Success(order));
92+
}
93+
94+
private Task<Result<Order>> HandleProcessingException(ProcessingException ex)
95+
{
96+
return Task.FromResult(Result<Order>.Failure($"Handled processing exception: {ex.Message}"));
97+
}
98+
99+
private Task<Result<Order>> RetryProcessing(Order order)
100+
{
101+
order.RetryCount++;
102+
order.IsProcessed = true;
103+
return Task.FromResult(Result<Order>.Success(order));
104+
}
105+
106+
private Task<Result<Order>> AttemptShipping(Order order)
107+
{
108+
if (new Random().Next(0, 10) < 8) // 80% chance of successful shipping
109+
{
110+
order.IsShipped = true;
111+
}
112+
return Task.FromResult(Result<Order>.Success(order));
113+
}
114+
115+
private Order FinalizeOrder(Order order)
116+
{
117+
order.IsProcessed = true;
118+
order.IsShipped = true;
119+
return order;
120+
}
121+
}
+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
using Microsoft.Extensions.Logging;
2+
using Moq;
3+
using FluentAssertions;
4+
5+
namespace FlexFlow.Tests;
6+
7+
[TestFixture]
8+
public class ComplexWorkflowTests
9+
{
10+
private Mock<ILogger<ComplexWorkflow>> _loggerMock;
11+
private ComplexWorkflow _workflow;
12+
private WorkflowBuilder<int, Order> _builder;
13+
14+
[SetUp]
15+
public void Setup()
16+
{
17+
_loggerMock = new Mock<ILogger<ComplexWorkflow>>();
18+
_workflow = new ComplexWorkflow(_loggerMock.Object);
19+
_builder = new WorkflowBuilder<int, Order>();
20+
}
21+
22+
[Test]
23+
public async Task ComplexWorkflow_ShouldProcessOrderSuccessfully()
24+
{
25+
// Arrange
26+
_workflow.Build(_builder);
27+
var builtWorkflow = await _builder.BuildAsync();
28+
29+
// Act
30+
var result = await builtWorkflow.ExecuteAsync(1);
31+
32+
// Assert
33+
result.IsSuccess.Should().BeTrue();
34+
result.Value.Should().NotBeNull();
35+
result.Value.Id.Should().Be(1);
36+
result.Value.IsProcessed.Should().BeTrue();
37+
result.Value.IsShipped.Should().BeTrue();
38+
39+
if (result.Value.TotalAmount > 1000)
40+
{
41+
result.Value.TotalAmount.Should().BeLessThan(2000 * 0.9m);
42+
}
43+
else
44+
{
45+
result.Value.TotalAmount.Should().BeLessThanOrEqualTo(1000);
46+
}
47+
}
48+
}

FlexFlow.Tests/FlexFlow.Tests.csproj

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net8.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
8+
<IsPackable>false</IsPackable>
9+
<IsTestProject>true</IsTestProject>
10+
</PropertyGroup>
11+
12+
<ItemGroup>
13+
<PackageReference Include="coverlet.collector" Version="6.0.2">
14+
<PrivateAssets>all</PrivateAssets>
15+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
16+
</PackageReference>
17+
<PackageReference Include="FluentAssertions" Version="6.12.0" />
18+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
19+
<PackageReference Include="Moq" Version="4.20.70" />
20+
<PackageReference Include="NUnit" Version="4.1.0" />
21+
<PackageReference Include="NUnit.Analyzers" Version="4.2.0">
22+
<PrivateAssets>all</PrivateAssets>
23+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
24+
</PackageReference>
25+
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0" />
26+
</ItemGroup>
27+
28+
<ItemGroup>
29+
<ProjectReference Include="..\FlexFlow\FlexFlow.csproj" />
30+
</ItemGroup>
31+
32+
<ItemGroup>
33+
<Using Include="NUnit.Framework" />
34+
</ItemGroup>
35+
36+
</Project>

0 commit comments

Comments
 (0)