Skip to content

Commit 1ffbbc3

Browse files
alisan3claude
andcommitted
Keep OnFault as the name for saga fault transitions
An earlier commit on this branch renamed OnFault to OnReplyFault because it only covers fault replies. That encoded a coverage gap into the name. The extensions layer names concepts over the channel-level primitives of the core descriptor: OnTimeout is OnRequest<SagaTimedOutEvent> and is not called OnRequestTimeout. OnFault belongs to that vocabulary, and not covering publish is a missing feature rather than a naming boundary. The narrower name also survives neither plausible future. If publish faults are delivered as a NotAcknowledgedEvent on the reply endpoint, the same route and the same transition catch them and OnReplyFault becomes too narrow. If fan-in semantics warrant a separate hook, the symmetric pair is OnSendFault and OnPublishFault, where a channel name sits oddly beside a verb name. The scope is documented instead. Neither a failing subscriber of a published event nor a failing saga transition produces a reply, so neither reaches this transition, and the summary now says so. This removes the public API rename from the branch: OnFault keeps the signature it has on main, and only its body and documentation change. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 636ce3a commit 1ffbbc3

10 files changed

Lines changed: 27 additions & 26 deletions

File tree

src/Mocha/src/Examples/Sagas/ParallelSaga/ParallelSaga.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ protected override void Configure(ISagaDescriptor<ReturnSagaState> descriptor)
171171
// Fault during inspection: compensate by marking failed
172172
descriptor
173173
.During(AwaitingInspection)
174-
.OnReplyFault()
174+
.OnFault()
175175
.Then((state, fault) => state.FailureReason = $"Inspection failed: {fault.ErrorMessage}")
176176
.TransitionTo(Failed);
177177

@@ -207,7 +207,7 @@ protected override void Configure(ISagaDescriptor<ReturnSagaState> descriptor)
207207
// Fault while waiting for either reply: compensation
208208
descriptor
209209
.During(AwaitingBothReplies)
210-
.OnReplyFault()
210+
.OnFault()
211211
.Then((state, fault) => state.FailureReason = $"Parallel step failed: {fault.ErrorMessage}")
212212
.TransitionTo(Failed);
213213

src/Mocha/src/Mocha/Sagas/Descriptors/SagaStateDescriptorExtensions.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,14 @@ namespace Mocha.Sagas;
99
public static class SagaStateDescriptorExtensions
1010
{
1111
/// <summary>
12-
/// Registers a transition triggered by a fault reply to a message the saga dispatched. It does not
13-
/// cover failures of events the saga published.
12+
/// Registers a transition triggered by a fault reply to a message the saga sent. It covers neither
13+
/// failures of events the saga published nor failures of the saga's own transitions, since neither
14+
/// produces a reply.
1415
/// </summary>
1516
/// <typeparam name="TState">The saga state type.</typeparam>
1617
/// <param name="descriptor">The state descriptor to configure.</param>
1718
/// <returns>A descriptor for configuring the fault transition.</returns>
18-
public static ISagaTransitionDescriptor<TState, NotAcknowledgedEvent> OnReplyFault<TState>(
19+
public static ISagaTransitionDescriptor<TState, NotAcknowledgedEvent> OnFault<TState>(
1920
this ISagaStateDescriptor<TState> descriptor)
2021
where TState : SagaStateBase
2122
{
@@ -24,7 +25,7 @@ public static ISagaTransitionDescriptor<TState, NotAcknowledgedEvent> OnReplyFau
2425

2526
/// <summary>
2627
/// Registers a transition triggered by any successful reply. A state that declares this must also
27-
/// handle fault replies through <c>OnReplyFault</c>, either on the state itself or through
28+
/// handle fault replies through <c>OnFault</c>, either on the state itself or through
2829
/// <c>DuringAny</c>.
2930
/// </summary>
3031
/// <typeparam name="TState">The saga state type.</typeparam>

src/Mocha/src/Mocha/Sagas/Initialization/SagaValidator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,6 @@ private static void ValidateFaultHandling(Saga saga, string stateName, SagaState
108108
throw new SagaInitializationException(
109109
saga,
110110
$"State '{stateName}' handles any reply but does not handle faults. "
111-
+ "Add '.OnReplyFault()' to this state, or '.DuringAny().OnReplyFault()' to the saga.");
111+
+ "Add '.OnFault()' to this state, or '.DuringAny().OnFault()' to the saga.");
112112
}
113113
}

src/Mocha/test/Mocha.Sagas.Tests/IntegrationTests.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -312,10 +312,10 @@ public async Task RpcReply_Should_NotSelectSaga_When_SameResponseTypeRequestedDi
312312
}
313313

314314
[Fact]
315-
public async Task Saga_Should_ReceiveFault_When_SendUsedWithOnReplyFault()
315+
public async Task Saga_Should_ReceiveFault_When_SendUsedWithOnFault()
316316
{
317317
// A saga that uses .Send to dispatch a request whose handler fails terminally routes the
318-
// fault reply back to its OnReplyFault transition, correlated by the saga header the fault reply
318+
// fault reply back to its OnFault transition, correlated by the saga header the fault reply
319319
// carries. Without it the saga waits in its send state until it times out.
320320

321321
// arrange
@@ -336,7 +336,7 @@ public async Task Saga_Should_ReceiveFault_When_SendUsedWithOnReplyFault()
336336
// assert - the handler ran and threw, proving the request was delivered
337337
await handlerFaulted.Task.WaitAsync(s_timeout, TestContext.Current.CancellationToken);
338338

339-
// assert - the fault routed back to the saga and drove its OnReplyFault transition
339+
// assert - the fault routed back to the saga and drove its OnFault transition
340340
var fault = await FaultSaga.Observed.Task.WaitAsync(s_timeout, TestContext.Current.CancellationToken);
341341
Assert.Equal(ErrorCodes.Exception, fault.ErrorCode);
342342
}
@@ -472,7 +472,7 @@ protected override void Configure(ISagaDescriptor<RequestResponseState> descript
472472
.TransitionTo("AwaitingResponse");
473473

474474
descriptor.During("AwaitingResponse").OnAnyReply().TransitionTo("Completed");
475-
descriptor.During("AwaitingResponse").OnReplyFault().TransitionTo("Completed");
475+
descriptor.During("AwaitingResponse").OnFault().TransitionTo("Completed");
476476

477477
descriptor.Finally("Completed");
478478
}
@@ -495,15 +495,15 @@ protected override void Configure(ISagaDescriptor<RequestResponseState> descript
495495
.TransitionTo("AwaitingResponse");
496496

497497
descriptor.During("AwaitingResponse").OnAnyReply().TransitionTo("Completed");
498-
descriptor.During("AwaitingResponse").OnReplyFault().TransitionTo("Completed");
498+
descriptor.During("AwaitingResponse").OnFault().TransitionTo("Completed");
499499

500500
descriptor.Finally("Completed");
501501
}
502502
}
503503

504504
/// <summary>
505505
/// Fault saga: StartFaultEvent -> AwaitingResponse (sends FaultingRequest) ->
506-
/// OnReplyFault -> Failed (final)
506+
/// OnFault -> Failed (final)
507507
/// </summary>
508508
public sealed class FaultSaga : Saga<RequestResponseState>
509509
{
@@ -520,7 +520,7 @@ protected override void Configure(ISagaDescriptor<RequestResponseState> descript
520520

521521
descriptor
522522
.During("AwaitingResponse")
523-
.OnReplyFault()
523+
.OnFault()
524524
.Then((_, fault) => Observed.TrySetResult(fault))
525525
.TransitionTo("Failed");
526526

src/Mocha/test/Mocha.Sagas.Tests/ReplyDiagnosticsTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ protected override void Configure(ISagaDescriptor<DiagnosticsState> descriptor)
129129
.Send((_, _) => new DiagnosticsRequest())
130130
.TransitionTo("Awaiting");
131131

132-
descriptor.During("Awaiting").OnReplyFault().TransitionTo("Failed");
132+
descriptor.During("Awaiting").OnFault().TransitionTo("Failed");
133133

134134
descriptor.Finally("Failed");
135135
}
@@ -147,7 +147,7 @@ protected override void Configure(ISagaDescriptor<DiagnosticsState> descriptor)
147147
.TransitionTo("Awaiting");
148148

149149
descriptor.During("Awaiting").OnAnyReply().TransitionTo("Done");
150-
descriptor.During("Awaiting").OnReplyFault().TransitionTo("Done");
150+
descriptor.During("Awaiting").OnFault().TransitionTo("Done");
151151

152152
descriptor.Finally("Done");
153153
}

src/Mocha/test/Mocha.Sagas.Tests/SagaPublishFaultTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ protected override void Configure(ISagaDescriptor<PublishState> descriptor)
152152
.Publish((_, state) => new ErrorQueueEvent(state.Id))
153153
.TransitionTo("Awaiting");
154154

155-
descriptor.During("Awaiting").OnReplyFault().TransitionTo("Failed");
155+
descriptor.During("Awaiting").OnFault().TransitionTo("Failed");
156156

157157
descriptor.Finally("Failed");
158158
}
@@ -186,7 +186,7 @@ protected override void Configure(ISagaDescriptor<PublishState> descriptor)
186186

187187
descriptor
188188
.During("Awaiting")
189-
.OnReplyFault()
189+
.OnFault()
190190
.Then((_, fault) => s_faultObserved.TrySetResult(fault))
191191
.TransitionTo("Failed");
192192

src/Mocha/test/Mocha.Sagas.Tests/SagaRouteConditionTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public void Configure_Should_GateOnSagaIdOnly_When_OnAnyReply()
2626
}
2727

2828
[Fact]
29-
public void Configure_Should_GateOnSagaIdAndMessageType_When_OnReplyFault()
29+
public void Configure_Should_GateOnSagaIdAndMessageType_When_OnFault()
3030
{
3131
// arrange & act
3232
var runtime = CreateRuntime(b => b.AddSaga<AnyReplySaga>());
@@ -122,7 +122,7 @@ protected override void Configure(ISagaDescriptor<ReplyState> descriptor)
122122
.TransitionTo("Awaiting");
123123

124124
descriptor.During("Awaiting").OnAnyReply().TransitionTo("Done");
125-
descriptor.During("Awaiting").OnReplyFault().TransitionTo("Done");
125+
descriptor.During("Awaiting").OnFault().TransitionTo("Done");
126126

127127
descriptor.Finally("Done");
128128
}

src/Mocha/test/Mocha.Sagas.Tests/SagaSendCommandTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ protected override void Configure(ISagaDescriptor<CommandState> descriptor)
128128

129129
descriptor
130130
.During("Awaiting")
131-
.OnReplyFault()
131+
.OnFault()
132132
.Then((_, fault) => s_faultObserved.TrySetResult(fault))
133133
.TransitionTo("Failed");
134134

@@ -153,7 +153,7 @@ protected override void Configure(ISagaDescriptor<CommandState> descriptor)
153153
.Then((_, reply) => s_replyObserved.TrySetResult(reply))
154154
.TransitionTo("Done");
155155

156-
descriptor.During("Awaiting").OnReplyFault().TransitionTo("Done");
156+
descriptor.During("Awaiting").OnFault().TransitionTo("Done");
157157

158158
descriptor.Finally("Done");
159159
}

src/Mocha/test/Mocha.Sagas.Tests/SagaStateMachineTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -415,7 +415,7 @@ public async Task Saga_Should_Fallback_When_AnyEvent()
415415

416416
x.During("Started").OnAnyReply().TransitionTo("Triggered");
417417

418-
x.During("Started").OnReplyFault().TransitionTo("Ended");
418+
x.During("Started").OnFault().TransitionTo("Ended");
419419

420420
x.During("Triggered").OnEvent<End>().TransitionTo("Ended");
421421

src/Mocha/test/Mocha.Sagas.Tests/SagaValidationTests.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ public void Initialize_Should_Throw_When_StateHandlesAnyReplyWithoutFault()
189189
// assert
190190
Assert.Equal(
191191
"State 'Awaiting' handles any reply but does not handle faults. "
192-
+ "Add '.OnReplyFault()' to this state, or '.DuringAny().OnReplyFault()' to the saga.",
192+
+ "Add '.OnFault()' to this state, or '.DuringAny().OnFault()' to the saga.",
193193
exception.Message);
194194
}
195195

@@ -205,7 +205,7 @@ public void Initialize_Should_PassValidation_When_StateHandlesAnyReplyAndFault()
205205
.TransitionTo("Awaiting")
206206
.StateFactory(_ => new TestState(Guid.NewGuid(), "Awaiting"));
207207
descriptor.During("Awaiting").OnAnyReply().TransitionTo("Success");
208-
descriptor.During("Awaiting").OnReplyFault().TransitionTo("Success");
208+
descriptor.During("Awaiting").OnFault().TransitionTo("Success");
209209
descriptor.Finally("Success");
210210
});
211211

@@ -227,7 +227,7 @@ public void Initialize_Should_PassValidation_When_FaultHandledByDuringAny()
227227
.TransitionTo("Awaiting")
228228
.StateFactory(_ => new TestState(Guid.NewGuid(), "Awaiting"));
229229
descriptor.During("Awaiting").OnAnyReply().TransitionTo("Success");
230-
descriptor.DuringAny().OnReplyFault().TransitionTo("Success");
230+
descriptor.DuringAny().OnFault().TransitionTo("Success");
231231
descriptor.Finally("Success");
232232
});
233233

0 commit comments

Comments
 (0)