Skip to content

Commit 94dcdfd

Browse files
authored
Actor reminders should return null if not registered (#1468)
* The implementation didn't check the status code for whether the actor reminder request failed or not. This updates it to return null if the reminder isn't present. Signed-off-by: Whit Waldo <[email protected]> * Fixed spelling error in unit test name Signed-off-by: Whit Waldo <[email protected]> * Added unit tests to validate GetReminderAsync request Signed-off-by: Whit Waldo <[email protected]> * Updated to reflect changed interface Signed-off-by: Whit Waldo <[email protected]> * Added unit test to validate null response coming through from mocked ActorTimerManager Signed-off-by: Whit Waldo <[email protected]> * Updated tests throughout class to use `const string` instead of `var` for constants Signed-off-by: Whit Waldo <[email protected]> * Added unit tests to prove out previously implemented reminder retrieval Signed-off-by: Whit Waldo <[email protected]> --------- Signed-off-by: Whit Waldo <[email protected]>
1 parent c94b61e commit 94dcdfd

File tree

7 files changed

+175
-61
lines changed

7 files changed

+175
-61
lines changed

src/Dapr.Actors/DaprHttpInteractor.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ HttpRequestMessage RequestFunc()
265265
return this.SendAsync(RequestFunc, relativeUrl, cancellationToken);
266266
}
267267

268-
public async Task<Stream> GetReminderAsync(string actorType, string actorId, string reminderName, CancellationToken cancellationToken = default)
268+
public async Task<HttpResponseMessage> GetReminderAsync(string actorType, string actorId, string reminderName, CancellationToken cancellationToken = default)
269269
{
270270
var relativeUrl = string.Format(CultureInfo.InvariantCulture, Constants.ActorReminderRelativeUrlFormat, actorType, actorId, reminderName);
271271

@@ -278,8 +278,7 @@ HttpRequestMessage RequestFunc()
278278
return request;
279279
}
280280

281-
var response = await this.SendAsync(RequestFunc, relativeUrl, cancellationToken);
282-
return await response.Content.ReadAsStreamAsync();
281+
return await this.SendAsync(RequestFunc, relativeUrl, cancellationToken);
283282
}
284283

285284
public Task UnregisterReminderAsync(string actorType, string actorId, string reminderName, CancellationToken cancellationToken = default)

src/Dapr.Actors/IDaprInteractor.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
// limitations under the License.
1212
// ------------------------------------------------------------------------
1313

14+
using System.Net.Http;
15+
1416
namespace Dapr.Actors
1517
{
1618
using System.IO;
@@ -81,8 +83,8 @@ internal interface IDaprInteractor
8183
/// <param name="actorId">ActorId.</param>
8284
/// <param name="reminderName">Name of reminder to unregister.</param>
8385
/// <param name="cancellationToken">Cancels the operation.</param>
84-
/// <returns>A <see cref="Task"/> representing the result of the asynchronous operation.</returns>
85-
Task<Stream> GetReminderAsync(string actorType, string actorId, string reminderName, CancellationToken cancellationToken = default);
86+
/// <returns>A <see cref="Task"/> containing the response of the asynchronous HTTP operation.</returns>
87+
Task<HttpResponseMessage> GetReminderAsync(string actorType, string actorId, string reminderName, CancellationToken cancellationToken = default);
8688

8789
/// <summary>
8890
/// Unregisters a reminder.

src/Dapr.Actors/Runtime/DefaultActorTimerManager.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
using System.Text.Json;
1616
using System.Threading.Tasks;
1717
using System.IO;
18+
using Grpc.Core;
1819

1920
namespace Dapr.Actors.Runtime
2021
{
@@ -45,9 +46,14 @@ public override async Task<IActorReminder> GetReminderAsync(ActorReminderToken t
4546
throw new ArgumentNullException(nameof(token));
4647
}
4748

48-
var responseStream = await this.interactor.GetReminderAsync(token.ActorType, token.ActorId.ToString(), token.Name);
49-
var reminder = await DeserializeReminderAsync(responseStream, token);
50-
return reminder;
49+
var response = await this.interactor.GetReminderAsync(token.ActorType, token.ActorId.ToString(), token.Name);
50+
if ((int)response.StatusCode == 500)
51+
{
52+
return null;
53+
}
54+
55+
var responseStream = await response.Content.ReadAsStreamAsync();
56+
return await DeserializeReminderAsync(responseStream, token);
5157
}
5258

5359
public override async Task UnregisterReminderAsync(ActorReminderToken reminder)

test/Dapr.Actors.Test/ActorUnitTestTests.cs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ public async Task CanTestStartingAndStoppingTimer()
6969
}
7070

7171
[Fact]
72-
public async Task CanTestStartingAndStoppinReminder()
72+
public async Task CanTestStartingAndStoppingReminder()
7373
{
7474
var reminders = new List<ActorReminder>();
7575
IActorReminder getReminder = null;
@@ -120,6 +120,22 @@ public async Task CanTestStartingAndStoppinReminder()
120120
Assert.Empty(reminders);
121121
}
122122

123+
[Fact]
124+
public async Task ReminderReturnsNullIfNotAvailable()
125+
{
126+
var timerManager = new Mock<ActorTimerManager>(MockBehavior.Strict);
127+
timerManager
128+
.Setup(tm => tm.GetReminderAsync(It.IsAny<ActorReminderToken>()))
129+
.Returns(() => Task.FromResult<IActorReminder>(null));
130+
131+
var host = ActorHost.CreateForTest<CoolTestActor>(new ActorTestOptions() { TimerManager = timerManager.Object, });
132+
var actor = new CoolTestActor(host);
133+
134+
//There is no starting reminder, so this should always return null
135+
var retrievedReminder = await actor.GetReminderAsync();
136+
Assert.Null(retrievedReminder);
137+
}
138+
123139
public interface ICoolTestActor : IActor
124140
{
125141
}

test/Dapr.Actors.Test/DaprHttpInteractorTest.cs

Lines changed: 74 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,9 @@ public async Task GetState_ValidateRequest()
3434
{
3535
await using var client = TestClient.CreateForDaprHttpInterator();
3636

37-
var actorType = "ActorType_Test";
38-
var actorId = "ActorId_Test";
39-
var keyName = "StateKey_Test";
37+
const string actorType = "ActorType_Test";
38+
const string actorId = "ActorId_Test";
39+
const string keyName = "StateKey_Test";
4040

4141
var request = await client.CaptureHttpRequestAsync(async httpInteractor =>
4242
{
@@ -57,9 +57,9 @@ public async Task SaveStateTransactionally_ValidateRequest()
5757
{
5858
await using var client = TestClient.CreateForDaprHttpInterator();
5959

60-
var actorType = "ActorType_Test";
61-
var actorId = "ActorId_Test";
62-
var data = "StateData";
60+
const string actorType = "ActorType_Test";
61+
const string actorId = "ActorId_Test";
62+
const string data = "StateData";
6363

6464
var request = await client.CaptureHttpRequestAsync(async httpInteractor =>
6565
{
@@ -80,10 +80,10 @@ public async Task InvokeActorMethodWithoutRemoting_ValidateRequest()
8080
{
8181
await using var client = TestClient.CreateForDaprHttpInterator();
8282

83-
var actorType = "ActorType_Test";
84-
var actorId = "ActorId_Test";
85-
var methodName = "MethodName";
86-
var payload = "JsonData";
83+
const string actorType = "ActorType_Test";
84+
const string actorId = "ActorId_Test";
85+
const string methodName = "MethodName";
86+
const string payload = "JsonData";
8787

8888
var request = await client.CaptureHttpRequestAsync(async httpInteractor =>
8989
{
@@ -104,10 +104,10 @@ public async Task RegisterReminder_ValidateRequest()
104104
{
105105
await using var client = TestClient.CreateForDaprHttpInterator();
106106

107-
var actorType = "ActorType_Test";
108-
var actorId = "ActorId_Test";
109-
var reminderName = "ReminderName";
110-
var payload = "JsonData";
107+
const string actorType = "ActorType_Test";
108+
const string actorId = "ActorId_Test";
109+
const string reminderName = "ReminderName";
110+
const string payload = "JsonData";
111111

112112
var request = await client.CaptureHttpRequestAsync(async httpInteractor =>
113113
{
@@ -128,9 +128,9 @@ public async Task UnregisterReminder_ValidateRequest()
128128
{
129129
await using var client = TestClient.CreateForDaprHttpInterator();
130130

131-
var actorType = "ActorType_Test";
132-
var actorId = "ActorId_Test";
133-
var reminderName = "ReminderName";
131+
const string actorType = "ActorType_Test";
132+
const string actorId = "ActorId_Test";
133+
const string reminderName = "ReminderName";
134134

135135
var request = await client.CaptureHttpRequestAsync(async httpInteractor =>
136136
{
@@ -146,15 +146,39 @@ public async Task UnregisterReminder_ValidateRequest()
146146
request.Request.Method.ShouldBe(HttpMethod.Delete);
147147
}
148148

149+
[Fact]
150+
public async Task GetReminder_ValidateRequest()
151+
{
152+
await using var client = TestClient.CreateForDaprHttpInterator();
153+
154+
const string actorType = "ActorType_Test";
155+
const string actorId = "ActorId_Test";
156+
const string reminderName = "ReminderName";
157+
158+
var request = await client.CaptureHttpRequestAsync(async httpInteractor =>
159+
{
160+
await httpInteractor.GetReminderAsync(actorType, actorId, reminderName);
161+
});
162+
163+
request.Dismiss();
164+
165+
var actualPath = request.Request.RequestUri.LocalPath.TrimStart('/');
166+
var expectedPath = string.Format(CultureInfo.InvariantCulture, Constants.ActorReminderRelativeUrlFormat,
167+
actorType, actorId, reminderName);
168+
169+
actualPath.ShouldBe(expectedPath);
170+
request.Request.Method.ShouldBe(HttpMethod.Get);
171+
}
172+
149173
[Fact]
150174
public async Task RegisterTimer_ValidateRequest()
151175
{
152176
await using var client = TestClient.CreateForDaprHttpInterator();
153177

154-
var actorType = "ActorType_Test";
155-
var actorId = "ActorId_Test";
156-
var timerName = "TimerName";
157-
var payload = "JsonData";
178+
const string actorType = "ActorType_Test";
179+
const string actorId = "ActorId_Test";
180+
const string timerName = "TimerName";
181+
const string payload = "JsonData";
158182

159183
var request = await client.CaptureHttpRequestAsync(async httpInteractor =>
160184
{
@@ -198,9 +222,9 @@ public async Task Call_WithApiTokenSet()
198222
{
199223
await using var client = TestClient.CreateForDaprHttpInterator(apiToken: "test_token");
200224

201-
var actorType = "ActorType_Test";
202-
var actorId = "ActorId_Test";
203-
var timerName = "TimerName";
225+
const string actorType = "ActorType_Test";
226+
const string actorId = "ActorId_Test";
227+
const string timerName = "TimerName";
204228

205229
var request = await client.CaptureHttpRequestAsync(async httpInteractor =>
206230
{
@@ -219,9 +243,9 @@ public async Task Call_WithoutApiToken()
219243
{
220244
await using var client = TestClient.CreateForDaprHttpInterator();
221245

222-
var actorType = "ActorType_Test";
223-
var actorId = "ActorId_Test";
224-
var timerName = "TimerName";
246+
const string actorType = "ActorType_Test";
247+
const string actorId = "ActorId_Test";
248+
const string timerName = "TimerName";
225249

226250
var request = await client.CaptureHttpRequestAsync(async httpInteractor =>
227251
{
@@ -239,9 +263,9 @@ public async Task Call_ValidateUnsuccessfulResponse()
239263
{
240264
await using var client = TestClient.CreateForDaprHttpInterator();
241265

242-
var actorType = "ActorType_Test";
243-
var actorId = "ActorId_Test";
244-
var timerName = "TimerName";
266+
const string actorType = "ActorType_Test";
267+
const string actorId = "ActorId_Test";
268+
const string timerName = "TimerName";
245269

246270
var request = await client.CaptureHttpRequestAsync(async httpInteractor =>
247271
{
@@ -272,9 +296,9 @@ public async Task Call_ValidateUnsuccessful404Response()
272296
{
273297
await using var client = TestClient.CreateForDaprHttpInterator();
274298

275-
var actorType = "ActorType_Test";
276-
var actorId = "ActorId_Test";
277-
var timerName = "TimerName";
299+
const string actorType = "ActorType_Test";
300+
const string actorId = "ActorId_Test";
301+
const string timerName = "TimerName";
278302

279303
var request = await client.CaptureHttpRequestAsync(async httpInteractor =>
280304
{
@@ -294,9 +318,9 @@ public async Task Call_ValidateUnauthorizedResponse()
294318
{
295319
await using var client = TestClient.CreateForDaprHttpInterator();
296320

297-
var actorType = "ActorType_Test";
298-
var actorId = "ActorId_Test";
299-
var timerName = "TimerName";
321+
const string actorType = "ActorType_Test";
322+
const string actorId = "ActorId_Test";
323+
const string timerName = "TimerName";
300324

301325
var request = await client.CaptureHttpRequestAsync(async httpInteractor =>
302326
{
@@ -316,10 +340,10 @@ public async Task InvokeActorMethodAddsReentrancyIdIfSet_ValidateHeaders()
316340
{
317341
await using var client = TestClient.CreateForDaprHttpInterator();
318342

319-
var actorType = "ActorType_Test";
320-
var actorId = "ActorId_Test";
321-
var methodName = "MethodName";
322-
var payload = "JsonData";
343+
const string actorType = "ActorType_Test";
344+
const string actorId = "ActorId_Test";
345+
const string methodName = "MethodName";
346+
const string payload = "JsonData";
323347

324348
ActorReentrancyContextAccessor.ReentrancyContext = "1";
325349
var request = await client.CaptureHttpRequestAsync(async httpInteractor =>
@@ -337,10 +361,10 @@ public async Task InvokeActorMethodOmitsReentrancyIdIfNotSet_ValidateHeaders()
337361
{
338362
await using var client = TestClient.CreateForDaprHttpInterator();
339363

340-
var actorType = "ActorType_Test";
341-
var actorId = "ActorId_Test";
342-
var methodName = "MethodName";
343-
var payload = "JsonData";
364+
const string actorType = "ActorType_Test";
365+
const string actorId = "ActorId_Test";
366+
const string methodName = "MethodName";
367+
const string payload = "JsonData";
344368

345369
var request = await client.CaptureHttpRequestAsync(async httpInteractor =>
346370
{
@@ -356,9 +380,9 @@ public async Task GetState_TTLExpireTimeExists()
356380
{
357381
await using var client = TestClient.CreateForDaprHttpInterator();
358382

359-
var actorType = "ActorType_Test";
360-
var actorId = "ActorId_Test";
361-
var keyName = "StateKey_Test";
383+
const string actorType = "ActorType_Test";
384+
const string actorId = "ActorId_Test";
385+
const string keyName = "StateKey_Test";
362386

363387
var request = await client.CaptureHttpRequestAsync(async httpInteractor =>
364388
{
@@ -385,9 +409,9 @@ public async Task GetState_TTLExpireTimeNotExists()
385409
{
386410
await using var client = TestClient.CreateForDaprHttpInterator();
387411

388-
var actorType = "ActorType_Test";
389-
var actorId = "ActorId_Test";
390-
var keyName = "StateKey_Test";
412+
const string actorType = "ActorType_Test";
413+
const string actorId = "ActorId_Test";
414+
const string keyName = "StateKey_Test";
391415

392416
var request = await client.CaptureHttpRequestAsync(async httpInteractor =>
393417
{

0 commit comments

Comments
 (0)