From a17c227b3bfd6dfbd8a94a383c022be0fe1077d8 Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo Date: Fri, 7 Jun 2024 21:25:25 +0200 Subject: [PATCH 01/13] Added test project --- all.sln | 20 +++++-------- .../DemoActor.UnitTest.csproj | 28 +++++++++++++++++++ .../Actor/DemoActor.UnitTest/UnitTest1.cs | 11 ++++++++ examples/Actor/README.md | 2 +- properties/IsExternalInit.cs | 2 +- 5 files changed, 48 insertions(+), 15 deletions(-) create mode 100644 examples/Actor/DemoActor.UnitTest/DemoActor.UnitTest.csproj create mode 100644 examples/Actor/DemoActor.UnitTest/UnitTest1.cs diff --git a/all.sln b/all.sln index 0b95478f3..d14c3f102 100644 --- a/all.sln +++ b/all.sln @@ -117,12 +117,18 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dapr.E2E.Test.Actors.Genera EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cryptography", "examples\Client\Cryptography\Cryptography.csproj", "{C74FBA78-13E8-407F-A173-4555AEE41FF3}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DemoActor.UnitTest", "examples\Actor\DemoActor.UnitTest\DemoActor.UnitTest.csproj", "{67EB5D79-B31D-4CDB-8AA2-BC50AB82828A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution + {67EB5D79-B31D-4CDB-8AA2-BC50AB82828A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {67EB5D79-B31D-4CDB-8AA2-BC50AB82828A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {67EB5D79-B31D-4CDB-8AA2-BC50AB82828A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {67EB5D79-B31D-4CDB-8AA2-BC50AB82828A}.Release|Any CPU.Build.0 = Release|Any CPU {C2DB4B64-B7C3-4FED-8753-C040F677C69A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C2DB4B64-B7C3-4FED-8753-C040F677C69A}.Debug|Any CPU.Build.0 = Debug|Any CPU {C2DB4B64-B7C3-4FED-8753-C040F677C69A}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -294,6 +300,7 @@ Global HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution + {67EB5D79-B31D-4CDB-8AA2-BC50AB82828A} = {02374BD0-BF0B-40F8-A04A-C4C4D61D4992} {C2DB4B64-B7C3-4FED-8753-C040F677C69A} = {27C5D71D-0721-4221-9286-B94AB07B58CF} {41BF4392-54BD-4FE7-A3EB-CD045F88CA9A} = {DD020B34-460F-455F-8D17-CF4A949F100B} {B9C12532-0969-4DAC-A2F8-CA9208D7A901} = {27C5D71D-0721-4221-9286-B94AB07B58CF} @@ -347,16 +354,3 @@ Global SolutionGuid = {65220BF2-EAE1-4CB2-AA58-EBE80768CB40} EndGlobalSection EndGlobal -8-446B-AECD-DCC2CC871F73} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {65220BF2-EAE1-4CB2-AA58-EBE80768CB40} - EndGlobalSection -EndGlobal -C991940} = {DD020B34-460F-455F-8D17-CF4A949F100B} - {C74FBA78-13E8-407F-A173-4555AEE41FF3} = {A7F41094-8648-446B-AECD-DCC2CC871F73} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {65220BF2-EAE1-4CB2-AA58-EBE80768CB40} - EndGlobalSection -EndGlobal diff --git a/examples/Actor/DemoActor.UnitTest/DemoActor.UnitTest.csproj b/examples/Actor/DemoActor.UnitTest/DemoActor.UnitTest.csproj new file mode 100644 index 000000000..4bbf9e19c --- /dev/null +++ b/examples/Actor/DemoActor.UnitTest/DemoActor.UnitTest.csproj @@ -0,0 +1,28 @@ + + + + net6.0 + enable + enable + + false + true + + + + + + + + + + + + + + + + + + + diff --git a/examples/Actor/DemoActor.UnitTest/UnitTest1.cs b/examples/Actor/DemoActor.UnitTest/UnitTest1.cs new file mode 100644 index 000000000..d30f773d3 --- /dev/null +++ b/examples/Actor/DemoActor.UnitTest/UnitTest1.cs @@ -0,0 +1,11 @@ +namespace DemoActor.UnitTest +{ + public class UnitTest1 + { + [Fact] + public void Test1() + { + + } + } +} \ No newline at end of file diff --git a/examples/Actor/README.md b/examples/Actor/README.md index ddc42cecf..921379c96 100644 --- a/examples/Actor/README.md +++ b/examples/Actor/README.md @@ -4,7 +4,7 @@ The Actor example shows how to create a virtual actor (`DemoActor`) and invoke i ## Prerequisites -- [.NET Core 3.1 or .NET 5+](https://dotnet.microsoft.com/download) installed +- [.NET 6+](https://dotnet.microsoft.com/download) installed - [Dapr CLI](https://docs.dapr.io/getting-started/install-dapr-cli/) - [Initialized Dapr environment](https://docs.dapr.io/getting-started/install-dapr-selfhost/) - [Dapr .NET SDK](https://github.com/dapr/dotnet-sdk/) diff --git a/properties/IsExternalInit.cs b/properties/IsExternalInit.cs index 34357c39a..28e38a0c8 100644 --- a/properties/IsExternalInit.cs +++ b/properties/IsExternalInit.cs @@ -13,5 +13,5 @@ namespace System.Runtime.CompilerServices internal static class IsExternalInit { } - + } From 3e3cb4053d4ba8754059d9640b2d9b549b0dedcf Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo Date: Fri, 7 Jun 2024 23:22:01 +0200 Subject: [PATCH 02/13] Updated readme with powershelll commands --- examples/Actor/README.md | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/examples/Actor/README.md b/examples/Actor/README.md index 921379c96..f33dcba40 100644 --- a/examples/Actor/README.md +++ b/examples/Actor/README.md @@ -22,7 +22,7 @@ The Actor example shows how to create a virtual actor (`DemoActor`) and invoke i To run the actor service locally run this command in `DemoActor` directory: ```sh - dapr run --dapr-http-port 3500 --app-id demo_actor --app-port 5010 dotnet run +dapr run --dapr-http-port 3500 --app-id demo_actor --app-port 5010 --config ./previewConfig.yaml dotnet run ``` The `DemoActor` service will listen on port `5010` for HTTP. @@ -54,15 +54,14 @@ Following curl call will save data for actor id "abc" On Linux, MacOS: -```sh +``` bash curl -X POST http://127.0.0.1:3500/v1.0/actors/DemoActor/abc/method/SaveData -d '{ "PropertyA": "ValueA", "PropertyB": "ValueB" }' ``` On Windows: -```sh -curl -X POST http://127.0.0.1:3500/v1.0/actors/DemoActor/abc/method/SaveData -d "{ \"PropertyA\": \"ValueA\", \"PropertyB\": \"ValueB\" }" - +``` powershell +Invoke-WebRequest -Method POST -Uri http://127.0.0.1:3500/v1.0/actors/DemoActor/abc/method/SaveData -ContentType "application/json" -Body '{ "data": {"PropertyA": "ValueA", "PropertyB": "ValueB" }, "ttl": "00:10:00" }' ``` **Get Data** @@ -71,12 +70,12 @@ Following curl call will get data for actor id "abc" On Linux, MacOS: -```sh +``` bash curl -X POST http://127.0.0.1:3500/v1.0/actors/DemoActor/abc/method/GetData ``` On Windows: -```sh -curl -X POST http://127.0.0.1:3500/v1.0/actors/DemoActor/abc/method/GetData +``` powershell +Invoke-WebRequest -Method POST -Uri http://127.0.0.1:3500/v1.0/actors/DemoActor/abc/method/GetData ``` From ee356cd2832f955c7ab10fe9b2e398da830d47b9 Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo Date: Fri, 7 Jun 2024 23:22:26 +0200 Subject: [PATCH 03/13] Removed Startup.cs --- examples/Actor/DemoActor/Program.cs | 42 +++++++++++--------- examples/Actor/DemoActor/Startup.cs | 59 ----------------------------- 2 files changed, 25 insertions(+), 76 deletions(-) delete mode 100644 examples/Actor/DemoActor/Startup.cs diff --git a/examples/Actor/DemoActor/Program.cs b/examples/Actor/DemoActor/Program.cs index a56681fdb..5915a6d7d 100644 --- a/examples/Actor/DemoActor/Program.cs +++ b/examples/Actor/DemoActor/Program.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------ +// ------------------------------------------------------------------------ // Copyright 2021 The Dapr Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -11,23 +11,31 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace DaprDemoActor +using DaprDemoActor; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddSingleton(); +builder.Services.AddActors(options => { - using Microsoft.AspNetCore.Hosting; - using Microsoft.Extensions.Hosting; + options.Actors.RegisterActor(); +}); - public class Program - { - public static void Main(string[] args) - { - CreateHostBuilder(args).Build().Run(); - } +var app = builder.Build(); - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(webBuilder => - { - webBuilder.UseStartup(); - }); - } +if (app.Environment.IsDevelopment()) +{ + app.UseDeveloperExceptionPage(); } +else +{ + app.UseHsts(); +} + +app.UseRouting(); +app.MapActorsHandlers(); + +await app.RunAsync(); diff --git a/examples/Actor/DemoActor/Startup.cs b/examples/Actor/DemoActor/Startup.cs deleted file mode 100644 index c04dfdcba..000000000 --- a/examples/Actor/DemoActor/Startup.cs +++ /dev/null @@ -1,59 +0,0 @@ -// ------------------------------------------------------------------------ -// Copyright 2021 The Dapr Authors -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// http://www.apache.org/licenses/LICENSE-2.0 -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ------------------------------------------------------------------------ - -namespace DaprDemoActor -{ - using Microsoft.AspNetCore.Builder; - using Microsoft.AspNetCore.Hosting; - using Microsoft.Extensions.Configuration; - using Microsoft.Extensions.DependencyInjection; - using Microsoft.Extensions.Hosting; - - public class Startup - { - public Startup(IConfiguration configuration) - { - this.Configuration = configuration; - } - - public IConfiguration Configuration { get; } - - public void ConfigureServices(IServiceCollection services) - { - services.AddSingleton(); - services.AddActors(options => - { - options.Actors.RegisterActor(); - }); - } - - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) - { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - else - { - app.UseHsts(); - } - - app.UseRouting(); - - app.UseEndpoints(endpoints => - { - endpoints.MapActorsHandlers(); - }); - } - } -} From 6dd919c1a98b30d24f5f213ddddd55dfa957e799 Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo Date: Fri, 7 Jun 2024 23:24:14 +0200 Subject: [PATCH 04/13] Added previewConfig.yaml to enable the preview function ActorStateTTL, because it is used in the example --- examples/Actor/DemoActor/previewConfig.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 examples/Actor/DemoActor/previewConfig.yaml diff --git a/examples/Actor/DemoActor/previewConfig.yaml b/examples/Actor/DemoActor/previewConfig.yaml new file mode 100644 index 000000000..cf8869c8f --- /dev/null +++ b/examples/Actor/DemoActor/previewConfig.yaml @@ -0,0 +1,8 @@ +apiVersion: dapr.io/v1alpha1 +kind: Configuration +metadata: + name: featureconfig +spec: + features: + - name: ActorStateTTL + enabled: true \ No newline at end of file From 97ea22dfd4eb5180a264a9f416d53c00d33bfecc Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo Date: Thu, 13 Jun 2024 19:55:01 +0200 Subject: [PATCH 05/13] Added missing parameter in readme --- examples/Actor/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/Actor/README.md b/examples/Actor/README.md index f33dcba40..bc05db019 100644 --- a/examples/Actor/README.md +++ b/examples/Actor/README.md @@ -55,7 +55,7 @@ Following curl call will save data for actor id "abc" On Linux, MacOS: ``` bash -curl -X POST http://127.0.0.1:3500/v1.0/actors/DemoActor/abc/method/SaveData -d '{ "PropertyA": "ValueA", "PropertyB": "ValueB" }' +curl -X POST http://127.0.0.1:3500/v1.0/actors/DemoActor/abc/method/SaveData -d '{ "data": {"PropertyA": "ValueA", "PropertyB": "ValueB" }, "ttl": "00:10:00" }' ``` On Windows: From 82df84e9935d4e19c99a738f84d5c3bb86007481 Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo Date: Thu, 13 Jun 2024 19:56:20 +0200 Subject: [PATCH 06/13] Added readme to the solution --- all.sln | 3 +++ 1 file changed, 3 insertions(+) diff --git a/all.sln b/all.sln index d14c3f102..0da28a237 100644 --- a/all.sln +++ b/all.sln @@ -57,6 +57,9 @@ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControllerSample", "examples\AspNetCore\ControllerSample\ControllerSample.csproj", "{3160CC92-1D6E-42CB-AE89-9401C8CEC5CB}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Actor", "Actor", "{02374BD0-BF0B-40F8-A04A-C4C4D61D4992}" + ProjectSection(SolutionItems) = preProject + examples\Actor\README.md = examples\Actor\README.md + EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IDemoActor", "examples\Actor\IDemoActor\IDemoActor.csproj", "{7957E852-1291-4FAA-9034-FB66CE817FF1}" EndProject From d113838d9533da1b359edefacceee0e45d1349ba Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo Date: Thu, 13 Jun 2024 21:52:47 +0200 Subject: [PATCH 07/13] Added IActorStateManager actorStateManager parameter to the contructor of DemoActor --- .../DemoActor.UnitTest.csproj | 44 ++++++++++--------- examples/Actor/DemoActor/DemoActor.cs | 24 ++++++++-- 2 files changed, 44 insertions(+), 24 deletions(-) diff --git a/examples/Actor/DemoActor.UnitTest/DemoActor.UnitTest.csproj b/examples/Actor/DemoActor.UnitTest/DemoActor.UnitTest.csproj index 4bbf9e19c..6e33826cc 100644 --- a/examples/Actor/DemoActor.UnitTest/DemoActor.UnitTest.csproj +++ b/examples/Actor/DemoActor.UnitTest/DemoActor.UnitTest.csproj @@ -1,28 +1,32 @@ - - net6.0 - enable - enable + + net6.0 + enable + enable - false - true - + false + true + - - - - - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + - - - + + + - - - + + + diff --git a/examples/Actor/DemoActor/DemoActor.cs b/examples/Actor/DemoActor/DemoActor.cs index da780d517..39441080d 100644 --- a/examples/Actor/DemoActor/DemoActor.cs +++ b/examples/Actor/DemoActor/DemoActor.cs @@ -33,12 +33,28 @@ public class DemoActor : Actor, IDemoActor, IBankActor, IRemindable private readonly BankService bank; - public DemoActor(ActorHost host, BankService bank) + /// + /// Initializes a new instance of . + /// + /// ActorHost. + /// BankService. + /// ActorStateManager used in UnitTests. + public DemoActor( + ActorHost host, + BankService bank, + IActorStateManager actorStateManager = null) : base(host) { // BankService is provided by dependency injection. // See Program.cs this.bank = bank; + + // Assign ActorStateManager when passed as parameter. + // This is used in UnitTests. + if (actorStateManager != null) + { + this.StateManager = actorStateManager; + } } public async Task SaveData(MyData data, TimeSpan ttl) @@ -74,12 +90,12 @@ public async Task RegisterReminderWithTtl(TimeSpan ttl) { await this.RegisterReminderAsync("TestReminder", null, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5), ttl); } - + public async Task RegisterReminderWithRepetitions(int repetitions) { await this.RegisterReminderAsync("TestReminder", null, TimeSpan.FromSeconds(0), TimeSpan.FromSeconds(1), repetitions); } - + public async Task RegisterReminderWithTtlAndRepetitions(TimeSpan ttl, int repetitions) { await this.RegisterReminderAsync("TestReminder", null, TimeSpan.FromSeconds(0), TimeSpan.FromSeconds(1), repetitions, ttl); @@ -98,7 +114,7 @@ public async Task GetReminder() } : null; } - + public Task UnregisterReminder() { return this.UnregisterReminderAsync("TestReminder"); From e2653084b34f0ec44527652961350da4ae7b2225 Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo Date: Mon, 15 Jul 2024 14:52:30 +0200 Subject: [PATCH 08/13] Updated the signature of Method SaveData(MyData data, TimeSpan ttl); --- examples/Actor/ActorClient/Program.cs | 14 +++++++----- examples/Actor/DemoActor/DemoActor.cs | 19 ++++++++++++++-- examples/Actor/IDemoActor/IDemoActor.cs | 29 ++++++++++++++++++++----- 3 files changed, 49 insertions(+), 13 deletions(-) diff --git a/examples/Actor/ActorClient/Program.cs b/examples/Actor/ActorClient/Program.cs index bae5d2ec2..7d657a059 100644 --- a/examples/Actor/ActorClient/Program.cs +++ b/examples/Actor/ActorClient/Program.cs @@ -32,10 +32,14 @@ public class Program /// A representing the asynchronous operation. public static async Task Main(string[] args) { - var data = new MyData() + var data = new MyDataWithTTL() { - PropertyA = "ValueA", - PropertyB = "ValueB", + MyData = new MyData + { + PropertyA = "ValueA", + PropertyB = "ValueB", + }, + TTL = TimeSpan.FromMinutes(10), }; // Create an actor Id. @@ -46,7 +50,7 @@ public static async Task Main(string[] args) var proxy = ActorProxy.Create(actorId, "DemoActor"); Console.WriteLine("Making call using actor proxy to save data."); - await proxy.SaveData(data, TimeSpan.FromMinutes(10)); + await proxy.SaveData(data); Console.WriteLine("Making call using actor proxy to get data."); var receivedData = await proxy.GetData(); Console.WriteLine($"Received data is {receivedData}."); @@ -103,7 +107,7 @@ public static async Task Main(string[] args) await proxy.UnregisterTimer(); Console.WriteLine("Deregistering reminder. Reminders are durable and would not stop until an explicit deregistration or the actor is deleted."); await proxy.UnregisterReminder(); - + Console.WriteLine("Registering reminder with repetitions - The reminder will repeat 3 times."); await proxy.RegisterReminderWithRepetitions(3); Console.WriteLine("Waiting so the reminder can be triggered"); diff --git a/examples/Actor/DemoActor/DemoActor.cs b/examples/Actor/DemoActor/DemoActor.cs index 39441080d..a9b2b35e8 100644 --- a/examples/Actor/DemoActor/DemoActor.cs +++ b/examples/Actor/DemoActor/DemoActor.cs @@ -57,50 +57,59 @@ public DemoActor( } } - public async Task SaveData(MyData data, TimeSpan ttl) + /// + public async Task SaveData(MyDataWithTTL data) { Console.WriteLine($"This is Actor id {this.Id} with data {data}."); // Set State using StateManager, state is saved after the method execution. - await this.StateManager.SetStateAsync(StateName, data, ttl); + await this.StateManager.SetStateAsync(StateName, data.MyData, data.TTL); } + /// public Task GetData() { // Get state using StateManager. return this.StateManager.GetStateAsync(StateName); } + /// public Task TestThrowException() { throw new NotImplementedException(); } + /// public Task TestNoArgumentNoReturnType() { return Task.CompletedTask; } + /// public async Task RegisterReminder() { await this.RegisterReminderAsync("TestReminder", null, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5)); } + /// public async Task RegisterReminderWithTtl(TimeSpan ttl) { await this.RegisterReminderAsync("TestReminder", null, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5), ttl); } + /// public async Task RegisterReminderWithRepetitions(int repetitions) { await this.RegisterReminderAsync("TestReminder", null, TimeSpan.FromSeconds(0), TimeSpan.FromSeconds(1), repetitions); } + /// public async Task RegisterReminderWithTtlAndRepetitions(TimeSpan ttl, int repetitions) { await this.RegisterReminderAsync("TestReminder", null, TimeSpan.FromSeconds(0), TimeSpan.FromSeconds(1), repetitions, ttl); } + /// public async Task GetReminder() { var reminder = await this.GetReminderAsync("TestReminder"); @@ -115,11 +124,13 @@ public async Task GetReminder() : null; } + /// public Task UnregisterReminder() { return this.UnregisterReminderAsync("TestReminder"); } + /// public async Task ReceiveReminderAsync(string reminderName, byte[] state, TimeSpan dueTime, TimeSpan period) { // This method is invoked when an actor reminder is fired. @@ -147,6 +158,7 @@ public Task RegisterTimer() return this.RegisterTimerAsync("TestTimer", nameof(this.TimerCallback), serializedTimerParams, TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(3)); } + /// public Task RegisterTimerWithTtl(TimeSpan ttl) { var timerParams = new TimerParams @@ -159,6 +171,7 @@ public Task RegisterTimerWithTtl(TimeSpan ttl) return this.RegisterTimerAsync("TestTimer", nameof(this.TimerCallback), serializedTimerParams, TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(3), ttl); } + /// public Task UnregisterTimer() { return this.UnregisterTimerAsync("TestTimer"); @@ -195,6 +208,7 @@ public async Task TimerCallback(byte[] data) Console.WriteLine("Timer parameter2: " + timerParams.StringParam); } + /// public async Task GetAccountBalance() { var starting = new AccountBalance() @@ -207,6 +221,7 @@ public async Task GetAccountBalance() return balance; } + /// public async Task Withdraw(WithdrawRequest withdraw) { var starting = new AccountBalance() diff --git a/examples/Actor/IDemoActor/IDemoActor.cs b/examples/Actor/IDemoActor/IDemoActor.cs index 25ce09370..8cb1bec4f 100644 --- a/examples/Actor/IDemoActor/IDemoActor.cs +++ b/examples/Actor/IDemoActor/IDemoActor.cs @@ -16,7 +16,6 @@ namespace IDemoActorInterface using System; using System.Threading.Tasks; using Dapr.Actors; - using Dapr.Actors.Runtime; /// /// Interface for Actor method. @@ -26,10 +25,9 @@ public interface IDemoActor : IActor /// /// Method to save data. /// - /// DAta to save. - /// TTL of state key. + /// Data to save with its TTL. /// A task that represents the asynchronous save operation. - Task SaveData(MyData data, TimeSpan ttl); + Task SaveData(MyDataWithTTL data); /// /// Method to get data. @@ -80,14 +78,14 @@ public interface IDemoActor : IActor /// Optional TimeSpan that dictates when the timer expires. /// A task that represents the asynchronous save operation. Task RegisterTimerWithTtl(TimeSpan ttl); - + /// /// Registers a reminder with repetitions. /// /// The number of repetitions for which the reminder should be invoked. /// A task that represents the asynchronous save operation. Task RegisterReminderWithRepetitions(int repetitions); - + /// /// Registers a reminder with ttl and repetitions. /// @@ -134,6 +132,25 @@ public override string ToString() } } + /// + /// Variant of MyData with TTL. + /// + public class MyDataWithTTL + { + /// + /// Gets or sets the MyData value. + /// + public MyData MyData { get; set; } + + /// + /// Duration for which the state is valid. + /// + public TimeSpan TTL { get; set; } + } + + /// + /// Object to hold reminder data. + /// public class ActorReminderData { public string Name { get; set; } From a4f1313ff636ccf61bb423aab4c0c84ccd63dbd5 Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo Date: Mon, 15 Jul 2024 14:59:27 +0200 Subject: [PATCH 09/13] Updated README.md --- examples/Actor/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/Actor/README.md b/examples/Actor/README.md index aacfec705..bc205badb 100644 --- a/examples/Actor/README.md +++ b/examples/Actor/README.md @@ -55,13 +55,13 @@ Following curl call will save data for actor id "abc" On Linux, MacOS: ``` bash -curl -X POST http://127.0.0.1:3500/v1.0/actors/DemoActor/abc/method/SaveData -d '{ "data": {"PropertyA": "ValueA", "PropertyB": "ValueB" }, "ttl": "00:10:00" }' +curl -X POST http://127.0.0.1:3500/v1.0/actors/DemoActor/abc/method/SaveData -d '{ "mydata": {"PropertyA": "ValueA", "PropertyB": "ValueB" }, "ttl": "00:10:00" }' ``` On Windows: ``` powershell -Invoke-WebRequest -Method POST -Uri http://127.0.0.1:3500/v1.0/actors/DemoActor/abc/method/SaveData -ContentType "application/json" -Body '{ "data": {"PropertyA": "ValueA", "PropertyB": "ValueB" }, "ttl": "00:10:00" }' +Invoke-WebRequest -Method POST -Uri http://127.0.0.1:3500/v1.0/actors/DemoActor/abc/method/SaveData -ContentType "application/json" -Body '{ "mydata": {"PropertyA": "ValueA", "PropertyB": "ValueB" }, "ttl": "00:10:00" }' ``` **Get Data** From 54929610bb2878c9190b0a14dbc0935841fa440f Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo Date: Mon, 15 Jul 2024 15:17:42 +0200 Subject: [PATCH 10/13] Updated deps --- .../Actor/DemoActor.UnitTest/DemoActor.UnitTest.csproj | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/examples/Actor/DemoActor.UnitTest/DemoActor.UnitTest.csproj b/examples/Actor/DemoActor.UnitTest/DemoActor.UnitTest.csproj index 6e33826cc..86018688e 100644 --- a/examples/Actor/DemoActor.UnitTest/DemoActor.UnitTest.csproj +++ b/examples/Actor/DemoActor.UnitTest/DemoActor.UnitTest.csproj @@ -7,6 +7,8 @@ false true + + true @@ -14,7 +16,10 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + From 0142cd1e6181a6a84c1504b64c2912a146e141be Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo Date: Mon, 15 Jul 2024 15:24:43 +0200 Subject: [PATCH 11/13] Updated target frameworks of new test project --- examples/Actor/DemoActor.UnitTest/DemoActor.UnitTest.csproj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/Actor/DemoActor.UnitTest/DemoActor.UnitTest.csproj b/examples/Actor/DemoActor.UnitTest/DemoActor.UnitTest.csproj index 86018688e..1bf34685e 100644 --- a/examples/Actor/DemoActor.UnitTest/DemoActor.UnitTest.csproj +++ b/examples/Actor/DemoActor.UnitTest/DemoActor.UnitTest.csproj @@ -1,7 +1,8 @@ - net6.0 + net6;net7;net8 + enable enable From 644d6e7f204727fd25adf3e8625a25f3657d7dad Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo Date: Mon, 15 Jul 2024 15:29:11 +0200 Subject: [PATCH 12/13] Added test logger in DemoActor.UnitTest --- examples/Actor/DemoActor.UnitTest/DemoActor.UnitTest.csproj | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/examples/Actor/DemoActor.UnitTest/DemoActor.UnitTest.csproj b/examples/Actor/DemoActor.UnitTest/DemoActor.UnitTest.csproj index 1bf34685e..5c8318bdb 100644 --- a/examples/Actor/DemoActor.UnitTest/DemoActor.UnitTest.csproj +++ b/examples/Actor/DemoActor.UnitTest/DemoActor.UnitTest.csproj @@ -2,14 +2,11 @@ net6;net7;net8 - enable enable false true - - true @@ -21,6 +18,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive + From 3ab8ee77c86a7ad2c297d493feba94913ccb6c35 Mon Sep 17 00:00:00 2001 From: Manuel Menegazzo Date: Mon, 15 Jul 2024 22:35:36 +0200 Subject: [PATCH 13/13] Created simple unittests of set and get state --- .../Actor/DemoActor.UnitTest/UnitTest1.cs | 86 ++++++++++++++++++- 1 file changed, 83 insertions(+), 3 deletions(-) diff --git a/examples/Actor/DemoActor.UnitTest/UnitTest1.cs b/examples/Actor/DemoActor.UnitTest/UnitTest1.cs index d30f773d3..8f5a92c63 100644 --- a/examples/Actor/DemoActor.UnitTest/UnitTest1.cs +++ b/examples/Actor/DemoActor.UnitTest/UnitTest1.cs @@ -1,11 +1,91 @@ +using Dapr.Actors.Runtime; +using DaprDemoActor; +using IDemoActorInterface; +using Moq; + namespace DemoActor.UnitTest { - public class UnitTest1 + public class DemoActorTests { [Fact] - public void Test1() + public async Task SaveData_CorrectlyPersistDataWithGiveTTL() + { + // arrange + // Name of the state to be saved in the actor + var actorStateName = "my_data"; + // Create a mock actor state manager to simulate the actor state + var mockStateManager = new Mock(MockBehavior.Strict); + // Prepare other dependencies + var bankService = new BankService(); + // Create an actor host for testing + var host = ActorHost.CreateForTest(); + // Create an actor instance with the mock state manager and its dependencies + var storageActor = new DaprDemoActor.DemoActor(host, bankService, mockStateManager.Object); + // Prepare test data to be saved + var data = new MyDataWithTTL + { + MyData = new MyData + { + PropertyA = "PropA", + PropertyB = "PropB", + }, + TTL = TimeSpan.FromSeconds(10) + }; + // Setup the mock state manager to enable the actor to save the state with the SetStateAsync method, and return + // a completed task when the state is saved, so that the actor can continue with the test. + // When MockBehavior.Strict is used, the test will fail if the actor does not call SetStateAsync or + // calls other methods on the state manager. + mockStateManager + .Setup(x => x.SetStateAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(Task.CompletedTask); + + // act + await storageActor.SaveData(data); + + // assert + // Verify that the state manager is called with the correct state name and data, only one time. + mockStateManager.Verify(x => x.SetStateAsync( + actorStateName, + It.Is(x => x.PropertyA == "PropA" && x.PropertyB == "PropB"), + It.Is(x => x.TotalSeconds == 10), + It.IsAny()), + Times.Once); + } + + [Fact] + public async Task GetData_CorrectlyRetrieveData() { + // arrange + // Name of the state to be saved in the actor + var actorStateName = "my_data"; + // Create a mock actor state manager to simulate the actor state + var mockStateManager = new Mock(MockBehavior.Strict); + // Prepare other dependencies + var bankService = new BankService(); + // Create an actor host for testing + var host = ActorHost.CreateForTest(); + // Create an actor instance with the mock state manager and its dependencies + var storageActor = new DaprDemoActor.DemoActor(host, bankService, mockStateManager.Object); + // Prepare prepare the state to be returned by the state manager + var state = new MyData + { + PropertyA = "PropA", + PropertyB = "PropB", + }; + // Setup the mock state manager to return the state when the actor calls GetStateAsync. + mockStateManager + .Setup(x => x.GetStateAsync(actorStateName, It.IsAny())) + .Returns(Task.FromResult(state)); + + // act + var result = await storageActor.GetData(); + // assert + // Verify that the state manager is called with the correct state name, only one time. + mockStateManager.Verify(x => x.GetStateAsync(actorStateName, It.IsAny()), Times.Once); + // Verify that the actor returns the correct data. + Assert.Equal("PropA", result.PropertyA); + Assert.Equal("PropB", result.PropertyB); } } -} \ No newline at end of file +}