Skip to content

Commit ee2fba7

Browse files
Fix entity unit test (CallFaultyEntity) (#2408)
* reduce timing-dependence of entity unit test (CallFaultyEntity) to reduce spurious test failures * fix whitespace that breaks build
1 parent 57bd9a3 commit ee2fba7

File tree

2 files changed

+60
-23
lines changed

2 files changed

+60
-23
lines changed

test/Common/TestEntityClasses.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,10 @@ public static Task FaultyEntityFunction([EntityTrigger] IDurableEntityContext co
160160
context.DeleteState();
161161
return Task.CompletedTask;
162162
}
163+
else if (context.OperationName == "delay")
164+
{
165+
return Task.Delay(TimeSpan.FromSeconds(context.GetInput<int>()));
166+
}
163167

164168
return context.DispatchAsync<FaultyEntity>();
165169
}
@@ -177,6 +181,9 @@ public static Task FaultyEntityFunctionWithoutDispatch([EntityTrigger] IDurableE
177181
context.DeleteState();
178182
break;
179183

184+
case "delay":
185+
return Task.Delay(TimeSpan.FromSeconds(context.GetInput<int>()));
186+
180187
case "Get":
181188
if (!context.HasState)
182189
{

test/Common/TestOrchestrations.cs

Lines changed: 53 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -936,10 +936,31 @@ async Task ExpectException(Task t)
936936
Assert.True(await ctx.CallEntityAsync<bool>(entityId, "exists"));
937937
}
938938

939-
// send batch
940-
ctx.SignalEntity(entityId, "Set", 42);
941-
ctx.SignalEntity(entityId, "SetThenThrow", 333);
942-
ctx.SignalEntity(entityId, "DeleteThenThrow");
939+
// we use this utility function to try to enforce that a bunch of signals is delivered as a single batch.
940+
// This is required for some of the tests here to work, since the batching affects the entity state management.
941+
// The "enforcement" mechanism we use is not 100% failsafe (it still makes timing assumptions about the provider)
942+
// but it should be more reliable than the original version of this test which failed quite frequently, as it was
943+
// simply assuming that signals that are sent at the same time are always processed as a batch.
944+
async Task ProcessAllSignalsInSingleBatch(Action sendSignals)
945+
{
946+
// first issue a signal that, when delivered, keeps the entity busy for a second
947+
ctx.SignalEntity(entityId, "delay", 1);
948+
949+
// we now need to yield briefly so that the delay signal is sent before the others
950+
await ctx.CreateTimer(ctx.CurrentUtcDateTime + TimeSpan.FromMilliseconds(1), CancellationToken.None);
951+
952+
// now send the signals in the batch. These should all arrive and get queued (inside the storage provider)
953+
// while the entity is executing the delay operation. Therefore, after the delay operation finishes,
954+
// all of the signals are processed in a single batch.
955+
sendSignals();
956+
}
957+
958+
await ProcessAllSignalsInSingleBatch(() =>
959+
{
960+
ctx.SignalEntity(entityId, "Set", 42);
961+
ctx.SignalEntity(entityId, "SetThenThrow", 333);
962+
ctx.SignalEntity(entityId, "DeleteThenThrow");
963+
});
943964

944965
if (rollbackOnException)
945966
{
@@ -951,12 +972,14 @@ async Task ExpectException(Task t)
951972
ctx.SignalEntity(entityId, "Set", 42);
952973
}
953974

954-
// send batch
955-
ctx.SignalEntity(entityId, "Get");
956-
ctx.SignalEntity(entityId, "Set", 42);
957-
ctx.SignalEntity(entityId, "Delete");
958-
ctx.SignalEntity(entityId, "Set", 43);
959-
ctx.SignalEntity(entityId, "DeleteThenThrow");
975+
await ProcessAllSignalsInSingleBatch(() =>
976+
{
977+
ctx.SignalEntity(entityId, "Get");
978+
ctx.SignalEntity(entityId, "Set", 42);
979+
ctx.SignalEntity(entityId, "Delete");
980+
ctx.SignalEntity(entityId, "Set", 43);
981+
ctx.SignalEntity(entityId, "DeleteThenThrow");
982+
});
960983

961984
if (rollbackOnException)
962985
{
@@ -967,9 +990,11 @@ async Task ExpectException(Task t)
967990
Assert.False(await ctx.CallEntityAsync<bool>(entityId, "exists"));
968991
}
969992

970-
// send batch
971-
ctx.SignalEntity(entityId, "Set", 55);
972-
ctx.SignalEntity(entityId, "SetToUnserializable");
993+
await ProcessAllSignalsInSingleBatch(() =>
994+
{
995+
ctx.SignalEntity(entityId, "Set", 55);
996+
ctx.SignalEntity(entityId, "SetToUnserializable");
997+
});
973998

974999
if (rollbackOnException)
9751000
{
@@ -981,11 +1006,13 @@ async Task ExpectException(Task t)
9811006
await ctx.CallEntityAsync<bool>(entityId, "deletewithoutreading");
9821007
}
9831008

984-
// send batch
985-
ctx.SignalEntity(entityId, "Set", 56);
986-
ctx.SignalEntity(entityId, "SetToUnDeserializable");
987-
ctx.SignalEntity(entityId, "Set", 12);
988-
ctx.SignalEntity(entityId, "SetThenThrow", 999);
1009+
await ProcessAllSignalsInSingleBatch(() =>
1010+
{
1011+
ctx.SignalEntity(entityId, "Set", 56);
1012+
ctx.SignalEntity(entityId, "SetToUnDeserializable");
1013+
ctx.SignalEntity(entityId, "Set", 12);
1014+
ctx.SignalEntity(entityId, "SetThenThrow", 999);
1015+
});
9891016

9901017
if (rollbackOnException)
9911018
{
@@ -999,11 +1026,14 @@ async Task ExpectException(Task t)
9991026

10001027
await ctx.CallEntityAsync<bool>(entityId, "deletewithoutreading");
10011028

1002-
ctx.SignalEntity(entityId, "Set", 1);
1003-
ctx.SignalEntity(entityId, "Delete");
1004-
ctx.SignalEntity(entityId, "Set", 2);
1005-
ctx.SignalEntity(entityId, "Delete");
1006-
ctx.SignalEntity(entityId, "SetThenThrow", 3);
1029+
await ProcessAllSignalsInSingleBatch(() =>
1030+
{
1031+
ctx.SignalEntity(entityId, "Set", 1);
1032+
ctx.SignalEntity(entityId, "Delete");
1033+
ctx.SignalEntity(entityId, "Set", 2);
1034+
ctx.SignalEntity(entityId, "Delete");
1035+
ctx.SignalEntity(entityId, "SetThenThrow", 3);
1036+
});
10071037

10081038
if (rollbackOnException)
10091039
{

0 commit comments

Comments
 (0)