diff --git a/.github/workflows/itests.yml b/.github/workflows/itests.yml index 4dcdfb95..0c96fa9c 100644 --- a/.github/workflows/itests.yml +++ b/.github/workflows/itests.yml @@ -48,8 +48,8 @@ jobs: GOOS: linux GOARCH: amd64 GOPROXY: https://proxy.golang.org - DAPR_CLI_VER: 1.14.0 - DAPR_RUNTIME_VER: 1.14.0 + DAPR_CLI_VER: 1.15.0 + DAPR_RUNTIME_VER: 1.15.3 DAPR_INSTALL_URL: https://raw.githubusercontent.com/dapr/cli/release-1.14/install/install.sh DAPR_CLI_REF: '' steps: diff --git a/Directory.Packages.props b/Directory.Packages.props index efb48fcc..e249a871 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -7,7 +7,6 @@ - @@ -24,8 +23,8 @@ - - + + @@ -43,10 +42,11 @@ + - + \ No newline at end of file diff --git a/daprdocs/content/en/dotnet-sdk-docs/dotnet-client/_index.md b/daprdocs/content/en/dotnet-sdk-docs/dotnet-client/_index.md index cab44468..93af91c4 100644 --- a/daprdocs/content/en/dotnet-sdk-docs/dotnet-client/_index.md +++ b/daprdocs/content/en/dotnet-sdk-docs/dotnet-client/_index.md @@ -282,26 +282,6 @@ namespace LockService } ``` -### Manage workflow instances (Alpha) - -```csharp -var daprClient = new DaprClientBuilder().Build(); - -string instanceId = "MyWorkflowInstance1"; -string workflowComponentName = "dapr"; // alternatively, this could be the name of a workflow component defined in yaml -string workflowName = "MyWorkflowDefinition"; -var input = new { name = "Billy", age = 30 }; // Any JSON-serializable value is OK - -// Start workflow -var startResponse = await daprClient.StartWorkflowAsync(instanceId, workflowComponentName, workflowName, input); - -// Terminate workflow -await daprClient.TerminateWorkflowAsync(instanceId, workflowComponentName); - -// Get workflow metadata -var getResponse = await daprClient.GetWorkflowAsync(instanceId, workflowComponentName, workflowName); -``` - ## Sidecar APIs ### Sidecar Health The .NET SDK provides a way to poll for the sidecar health, as well as a convenience method to wait for the sidecar to be ready. diff --git a/daprdocs/content/en/dotnet-sdk-docs/dotnet-jobs/_index.md b/daprdocs/content/en/dotnet-sdk-docs/dotnet-jobs/_index.md index 60f756aa..3b8d43fc 100644 --- a/daprdocs/content/en/dotnet-sdk-docs/dotnet-jobs/_index.md +++ b/daprdocs/content/en/dotnet-sdk-docs/dotnet-jobs/_index.md @@ -10,4 +10,4 @@ With the Dapr Job package, you can interact with the Dapr Job APIs from a .NET a to run according to a predefined schedule with an optional payload. To get started, walk through the [Dapr Jobs]({{< ref dotnet-jobs-howto.md >}}) how-to guide and refer to -[best practices documentation]({{< ref dotnet-jobs-usage.md >}}) for additional guidance. \ No newline at end of file +[best practices documentation]({{< ref dotnet-jobsclient-usage.md >}}) for additional guidance. diff --git a/daprdocs/content/en/dotnet-sdk-docs/dotnet-jobs/dotnet-jobs-howto.md b/daprdocs/content/en/dotnet-sdk-docs/dotnet-jobs/dotnet-jobs-howto.md index 974b2f5e..be4d2705 100644 --- a/daprdocs/content/en/dotnet-sdk-docs/dotnet-jobs/dotnet-jobs-howto.md +++ b/daprdocs/content/en/dotnet-sdk-docs/dotnet-jobs/dotnet-jobs-howto.md @@ -63,8 +63,8 @@ the dependency injection registration in `Program.cs`, add the following line: ```cs var builder = WebApplication.CreateBuilder(args); -//Add anywhere between these two -builder.Services.AddDaprJobsClient(); //That's it +//Add anywhere between these two lines +builder.Services.AddDaprJobsClient(); var app = builder.Build(); ``` @@ -203,7 +203,8 @@ public class MySampleClass It's easy to set up a jobs endpoint if you're at all familiar with [minimal APIs in ASP.NET Core](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis/overview) as the syntax is the same between the two. Once dependency injection registration has been completed, configure the application the same way you would to handle mapping an HTTP request via the minimal API functionality in ASP.NET Core. Implemented as an extension method, -pass the name of the job it should be responsive to and a delegate. Services can be injected into the delegate's arguments as you wish and you can optionally pass a `JobDetails` to get information about the job that has been triggered (e.g. access its scheduling setup or payload). +pass the name of the job it should be responsive to and a delegate. Services can be injected into the delegate's arguments as you wish and the job payload can be accessed from the `ReadOnlyMemory` originally provided to the +job registration. There are two delegates you can use here. One provides an `IServiceProvider` in case you need to inject other services into the handler: @@ -216,7 +217,7 @@ builder.Services.AddDaprJobsClient(); var app = builder.Build(); //Add our endpoint registration -app.MapDaprScheduledJob("myJob", (IServiceProvider serviceProvider, string? jobName, JobDetails? jobDetails) => { +app.MapDaprScheduledJob("myJob", (IServiceProvider serviceProvider, string jobName, ReadOnlyMemory jobPayload) => { var logger = serviceProvider.GetService(); logger?.LogInformation("Received trigger invocation for '{jobName}'", "myJob"); @@ -237,13 +238,34 @@ builder.Services.AddDaprJobsClient(); var app = builder.Build(); //Add our endpoint registration -app.MapDaprScheduledJob("myJob", (string? jobName, JobDetails? jobDetails) => { +app.MapDaprScheduledJob("myJob", (string jobName, ReadOnlyMemory jobPayload) => { //Do something... }); app.Run(); ``` +## Support cancellation tokens when processing mapped invocations +You may want to ensure that timeouts are handled on job invocations so that they don't indefinitely hang and use system resources. When setting up the job mapping, there's an optional `TimeSpan` parameter that can be +provided as the last argument to specify a timeout for the request. Every time the job mapping invocation is triggered, a new `CancellationTokenSource` will be created using this timeout parameter and a `CancellationToken` +will be created from it to put an upper bound on the processing of the request. If a timeout isn't provided, this defaults to `CancellationToken.None` and a timeout will not be automatically applied to the mapping. + +```cs +//We have this from the example above +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddDaprJobsClient(); + +var app = builder.Build(); + +//Add our endpoint registration +app.MapDaprScheduledJob("myJob", (string jobName, ReadOnlyMemory jobPayload) => { + //Do something... +}, TimeSpan.FromSeconds(15)); //Assigns a maximum timeout of 15 seconds for handling the invocation request + +app.Run(); +``` + ## Register the job Finally, we have to register the job we want scheduled. Note that from here, all SDK methods have cancellation token support and use a default token if not otherwise set. diff --git a/daprdocs/content/en/dotnet-sdk-docs/dotnet-jobs/dotnet-jobsclient-usage.md b/daprdocs/content/en/dotnet-sdk-docs/dotnet-jobs/dotnet-jobsclient-usage.md index bbdbbdbe..ddbf226e 100644 --- a/daprdocs/content/en/dotnet-sdk-docs/dotnet-jobs/dotnet-jobsclient-usage.md +++ b/daprdocs/content/en/dotnet-sdk-docs/dotnet-jobs/dotnet-jobsclient-usage.md @@ -165,31 +165,18 @@ var oneWeekFromNow = now.AddDays(7); await daprJobsClient.ScheduleOneTimeJobWithPayloadAsync("myOtherJob", oneWeekFromNow, "This is a test!"); ``` -The `JobDetails` type returns the data as a `ReadOnlyMemory?` so the developer has the freedom to deserialize +The delegate handling the job invocation expects at least two arguments to be present: +- A `string` that is populated with the `jobName`, providing the name of the invoked job +- A `ReadOnlyMemory` that is populated with the bytes originally provided during the job registration. + +Because the payload is stored as a `ReadOnlyMemory`, the developer has the freedom to serialize and deserialize as they wish, but there are again two helper extensions included that can deserialize this to either a JSON-compatible type or a string. Both methods assume that the developer encoded the originally scheduled job (perhaps using the helper serialization methods) as these methods will not force the bytes to represent something they're not. To deserialize the bytes to a string, the following helper method can be used: ```cs -if (jobDetails.Payload is not null) -{ - string payloadAsString = jobDetails.Payload.DeserializeToString(); //If successful, returns a string value with the value -} -``` - -To deserialize JSON-encoded UTF-8 bytes to the corresponding type, the following helper method can be used. An -overload argument is available that permits the developer to pass in their own `JsonSerializerOptions` to be applied -during deserialization. - -```cs -public sealed record Doodad (string Name, int Value); - -//... -if (jobDetails.Payload is not null) -{ - var deserializedDoodad = jobDetails.Payload.DeserializeFromJsonBytes(); -} +var payloadAsString = Encoding.UTF8.GetString(jobPayload.Span); //If successful, returns a string with the value ``` ## Error handling diff --git a/examples/AspNetCore/SecretStoreConfigurationProviderSample/README.md b/examples/AspNetCore/SecretStoreConfigurationProviderSample/README.md index 09422e47..a5d60c2f 100644 --- a/examples/AspNetCore/SecretStoreConfigurationProviderSample/README.md +++ b/examples/AspNetCore/SecretStoreConfigurationProviderSample/README.md @@ -20,7 +20,7 @@ To load secrets into configuration call the _AddDaprSecretStore_ extension metho Use Dapr to run the application: ```shell -dapr run --app-id SecretStoreConfigurationProviderSample --components-path ./components/ -- dotnet run +dapr run --app-id SecretStoreConfigurationProviderSample --resources-path ./components/ -- dotnet run ``` ### 2. Test the application diff --git a/examples/Client/ConfigurationApi/README.md b/examples/Client/ConfigurationApi/README.md index 7425a780..d73a29f9 100644 --- a/examples/Client/ConfigurationApi/README.md +++ b/examples/Client/ConfigurationApi/README.md @@ -147,7 +147,7 @@ cd examples/Client/ConfigurationApi To run the `ConfigurationExample`, execute the following command: ```bash -dapr run --app-id configexample --components-path ./Components -- dotnet run +dapr run --app-id configexample --resources-path ./Components -- dotnet run ``` ### Get Configuration diff --git a/examples/Client/Cryptography/Components/azurekeyvault.yaml b/examples/Client/Cryptography/Components/azurekeyvault.yaml deleted file mode 100644 index 5932e0bc..00000000 --- a/examples/Client/Cryptography/Components/azurekeyvault.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: azurekeyvault -spec: - type: crypto.azure.keyvault - metadata: - - name: vaultName - value: "" - - name: azureEnvironment - value: AZUREPUBLICCLOUD - - name: azureTenantId - secretKeyRef: - name: read_azure_tenant_id - key: read_azure_tenant_id - - name: azureClientId - secretKeyRef: - name: read_azure_client_id - key: read_azure_client_id - - name: azureClientSecret - secretKeyRef: - name: read_azure_client_secret - key: read_azure_client_secret -auth: - secureStore: envvar-secret-store \ No newline at end of file diff --git a/examples/Client/Cryptography/Components/env-secretstore.yaml b/examples/Client/Cryptography/Components/env-secretstore.yaml deleted file mode 100644 index fb191414..00000000 --- a/examples/Client/Cryptography/Components/env-secretstore.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: dapr.io/v1alpha1 -kind: Component -metadata: - name: envvar-secret-store -spec: - type: secretstores.local.env - version: v1 \ No newline at end of file diff --git a/examples/Client/Cryptography/Components/local-storage.yaml b/examples/Client/Cryptography/Components/local-storage.yaml new file mode 100644 index 00000000..4a6640fe --- /dev/null +++ b/examples/Client/Cryptography/Components/local-storage.yaml @@ -0,0 +1,11 @@ +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: localstorage +spec: + type: crypto.dapr.localstorage + version: v1 + metadata: + - name: path + # Path is relative to the folder where the example is located + value: ./keys diff --git a/examples/Client/Cryptography/Examples/EncryptDecryptFileStreamExample.cs b/examples/Client/Cryptography/Examples/EncryptDecryptFileStreamExample.cs index aa9c404a..19df0634 100644 --- a/examples/Client/Cryptography/Examples/EncryptDecryptFileStreamExample.cs +++ b/examples/Client/Cryptography/Examples/EncryptDecryptFileStreamExample.cs @@ -17,16 +17,13 @@ using Dapr.Client; namespace Cryptography.Examples { - internal class EncryptDecryptFileStreamExample : Example + internal class EncryptDecryptFileStreamExample(string componentName, string keyName) : Example { public override string DisplayName => "Use Cryptography to encrypt and decrypt a file"; public override async Task RunAsync(CancellationToken cancellationToken) { using var client = new DaprClientBuilder().Build(); - const string componentName = "azurekeyvault"; // Change this to match the name of the component containing your vault - const string keyName = "myKey"; - // The name of the file we're using as an example const string fileName = "file.txt"; @@ -35,7 +32,6 @@ namespace Cryptography.Examples { Console.WriteLine(line); } - Console.WriteLine(); //Encrypt from a file stream and buffer the resulting bytes to an in-memory buffer await using var encryptFs = new FileStream(fileName, FileMode.Open); @@ -48,8 +44,8 @@ namespace Cryptography.Examples bufferedEncryptedBytes.Write(bytes.Span); } - Console.WriteLine($"Encrypted bytes: {Convert.ToBase64String(bufferedEncryptedBytes.GetSpan())}"); - Console.WriteLine(); + Console.WriteLine("Encrypted bytes:"); + Console.WriteLine(Convert.ToBase64String(bufferedEncryptedBytes.WrittenMemory.ToArray())); //We'll write to a temporary file via a FileStream var tempDecryptedFile = Path.GetTempFileName(); @@ -67,7 +63,7 @@ namespace Cryptography.Examples //Let's confirm the value as written to the file var decryptedValue = await File.ReadAllTextAsync(tempDecryptedFile, cancellationToken); - Console.WriteLine($"Decrypted value: "); + Console.WriteLine("Decrypted value: "); Console.WriteLine(decryptedValue); //And some cleanup to delete our temp file diff --git a/examples/Client/Cryptography/Examples/EncryptDecryptStringExample.cs b/examples/Client/Cryptography/Examples/EncryptDecryptStringExample.cs index a37ca1b8..d29b24a6 100644 --- a/examples/Client/Cryptography/Examples/EncryptDecryptStringExample.cs +++ b/examples/Client/Cryptography/Examples/EncryptDecryptStringExample.cs @@ -17,17 +17,13 @@ using Dapr.Client; namespace Cryptography.Examples { - internal class EncryptDecryptStringExample : Example + internal class EncryptDecryptStringExample(string componentName, string keyName) : Example { public override string DisplayName => "Using Cryptography to encrypt and decrypt a string"; public override async Task RunAsync(CancellationToken cancellationToken) { using var client = new DaprClientBuilder().Build(); - - const string componentName = "azurekeyvault"; //Change this to match the name of the component containing your vault - const string keyName = "myKey"; //Change this to match the name of the key in your Vault - const string plaintextStr = "This is the value we're going to encrypt today"; Console.WriteLine($"Original string value: '{plaintextStr}'"); @@ -40,7 +36,7 @@ namespace Cryptography.Examples Console.WriteLine($"Encrypted bytes: '{Convert.ToBase64String(encryptedBytesResult.Span)}'"); //Decrypt the string - var decryptedBytes = await client.DecryptAsync(componentName, encryptedBytesResult, keyName, new DecryptionOptions(), cancellationToken); + var decryptedBytes = await client.DecryptAsync(componentName, encryptedBytesResult, keyName, cancellationToken); Console.WriteLine($"Decrypted string: '{Encoding.UTF8.GetString(decryptedBytes.ToArray())}'"); } } diff --git a/examples/Client/Cryptography/Program.cs b/examples/Client/Cryptography/Program.cs index da81bef8..5c63d736 100644 --- a/examples/Client/Cryptography/Program.cs +++ b/examples/Client/Cryptography/Program.cs @@ -17,10 +17,13 @@ namespace Cryptography { class Program { + private const string ComponentName = "localstorage"; + private const string KeyName = "rsa-private-key.pem"; //This should match the name of your generated key - this sample expects an RSA symmetrical key. + private static readonly Example[] Examples = new Example[] { - new EncryptDecryptStringExample(), - new EncryptDecryptFileStreamExample() + new EncryptDecryptStringExample(ComponentName, KeyName), + new EncryptDecryptFileStreamExample(ComponentName, KeyName) }; static async Task Main(string[] args) @@ -34,7 +37,7 @@ namespace Cryptography return 0; } - Console.WriteLine("Hello, please choose a sample to run:"); + Console.WriteLine("Hello, please choose a sample to run by passing your selection's number into the arguments, e.g. 'dotnet run 0':"); for (var i = 0; i < Examples.Length; i++) { Console.WriteLine($"{i}: {Examples[i].DisplayName}"); diff --git a/examples/Client/Cryptography/README.md b/examples/Client/Cryptography/README.md index c0c88436..883cd2b2 100644 --- a/examples/Client/Cryptography/README.md +++ b/examples/Client/Cryptography/README.md @@ -50,6 +50,21 @@ button. Ensuring that the "User, group or service principal" option is selected, Add to add this service principal to the list of members for the new role assignment and click Review + Assign twice to assign the role. This will take effect within a few seconds or minutes. This step ensures that while Dapr can authenticate as your service principal, that it also has permission to access and use the key in your Key Vault. +## Generating the Keys +This sample requires a private RSA key to be generated and placed in the `/keys` directory within the project. +If you have OpenSSL installed on your machine, you can generate the key by navigating first +into the project directory and then running the following command: + +```bash +# Generates a private RSA 40960-bit key named 'rsa-private-key.pem' +openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:4096 -out keys/rsa-private-key.pem +``` + +> **WARNING: This RSA key is included in this project strictly for demonstration and testing purposes.** +> - Do **NOT** use this key in any production environment or for any real-world applications. +> - This key is publicly available and should be considered compromised. +> - Generating and using your own secure keys is essential for maintaining security in your projects. + ## Running the example To run the sample locally, run this command in the DaprClient directory: diff --git a/examples/Client/Cryptography/keys/rsa-private-key.pem b/examples/Client/Cryptography/keys/rsa-private-key.pem new file mode 100644 index 00000000..f4508f7a --- /dev/null +++ b/examples/Client/Cryptography/keys/rsa-private-key.pem @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +MIIJQQIBADANBgkqhkiG9w0BAQEFAASCCSswggknAgEAAoICAQC0URLpxZCqDv7S +WfROh2Kei4VCEayNu/TK3NaD/QlIpip1rrsPKgTfTOZoRmkmG0Qj59srEJi2GEhL +xpjvRQpA/C/OS+KELU8AeGrqHw7uN/a99NkoAr+zYDCyY9yckPeC5wGxc0/Q6HQT +mWp+YcpR9wFO0PmTVlObssibagjjRNX7z/ZosecOOqjnAqlnYoHMavvoCD5fxM7y +cm7so0JWooXwVaZKgehBEBg1W5F0q5e9ssAQk3lY6IUd5sOskiylTNf/+3r1JU0j +YM8ik3a1/dyDALVXpLSfz7FM9VEj4QjiPF4UuXeBHPDFFiKWbiKfbjqvZ2Sz7Gl7 +c5rTk1Fozpr70E/wihrrv22Mxs0sEPdtemQgHXroQfRW8K4FhI0WHs7tR2gVxLHu +OAU9LzCngz4yITh1eixVDmm/B5ZtNVrTQmaY84vGqhrFp+asyFNiXbhUAcT7D/q6 +w/c4aQ635ntCFSPYpWvhKqrqVDsoanD/5AWfc3+6Ek2/GVMyEQq+9tnCMM10EVSX +8PsoAWHESDFude5zkHzn7IKy8mh6lfheEbBI5zN9z7WGexyiBgljmyUHXx6Pd8Uc +yxpLRm94kynkDXD9SapQLzXmz+D+X/OYeADMIDWlbdXiIb1+2Q62H1lo6n10KVP7 +oEr8BHvcMFY89kwK4lKscUupn8xkzwIDAQABAoICACDuu78Rc8Hzeivt/PZIuMTP +I5f1BWhffy571fwGP2dS3edfcc+rs3cbIuvBjFvG2BOcuYUsg0+isLWSQIVWvTAw +PwT1DBpq8gZad+Bpqr7sXrbD3NN3aQ64TzyNi5HW0jXIviDsOBQmGGkp+G67qol8 +zPLZrPNxbVS++u+Tlqr3fAOBMHZfo50QLp/+dvUoYx90HKz8sHOqTMewCb1Tdf6/ +sSm7YuMxxbr4VwuLvU2rN0wQtQ5x+NQ5p3JWHr/KdLf+CGc6xXK3jNaczEf62dAU +XO1aOESZEtorQy0Ukuy0IXy8XMx5MS/WGs1MJSYHWHB43+QARL6tu3guHYVt3wyv +W6YTglQsSKc6uuK4JTZOx1VYZjjnSdeY/xiUmZGYp4ZiC9p8b9NvXmZT2EwqhCVt +4OTcX4lkwGAsKcoEdLHi0K5CbBfYJsRgVVheDjP0xUFjCJCYqfqo2rE5YMXMTeY7 +clYEOXKGxwuy1Iu8nKqtWAV5r/eSmXBdxBqEBW9oxJfnnwNPG+yOk0Qkd1vaRj00 +mdKCOjgB2fOuPX2JRZ2z41Cem3gqhH0NQGrx3APV4egGrYAMClasgtZkUeUOIgK5 +xLlC/6svuHNyKXAKFpOubEy1FM8jz7111eNHxHRDP3+vH3u4CfAD2Sl+VDZdg51i +WmVpT+B/DrnlHVSP2/XNAoIBAQD7F49oSdveKuO/lAyqkE9iF61i09G0b0ouDGUI +qx+pd5/8vUcqi4upCxz+3AqMPWZRIqOyo8EUP7f4rSJrXn8U2SwnFfi4k2jiqmEA +Wr0b8z5P1q5MH6BtVDa0Sr1R8xI9s3UgIs4pUKgBoQu9+U4Du4NSucQFcea8nIVY +lLCqQcRhz8bCJPCNuHay5c77kK3Te197KPMasNurTNMOJcPMG95CZLB8Clf4A+pw +fixvA1/fE4mFo1L7Ymxoz5lFYVWOTY9hh50Kqz57wxw4laU4ii+MaJj+YHuNR83N +cO6FztUYKMR8BPgtl3/POTHTofSg7eIOiUYwcfRr6jbMWlsDAoIBAQC311xiMpho +Hvdcvp3/urrIp2QhdD05n6TnZOPkpnd9kwGku2RA+occDQOg/BzADVwJaR/aE97F +jbfRlfBesTZlUec0EwjKIFbeYh+QS/RmjQe9zpPQWMo1M7y0fMWU+yXRUcNBpcuy +R6KlphK0k4xFkIAdC3QHmJQ0XvOpqvrhFy3i/Prc5Wlg29FYBBTAF0WZCZ4uCG34 +D0eG0CNaf8w9g9ClbU6nGLBCMcgjEOPYfyrJaedM+jXennLDPG6ySytrGwnwLAQc +Okx+SrIiNHUpQGKteT88Kdpgo3F4KUX/pm84uGdxrOpDS7L0T9/G4CbjzCe1nHeS +fJJsw5JN+Z9FAoIBAGn5S6FsasudtnnI9n+WYKq564fmdn986QX+XTYHY1mXD4MQ +L9UZCFzUP+yg2iLOVzyvLf/bdUYijnb6O6itPV2DO0tTzqG4NXBVEJOhuGbvhsET +joS6ZG9AN8ZoNPc9a9l2wFxL1E9Dp2Ton5gSfIa+wXJMzRqvM/8u4Gi+eMGi+Et/ +8hdGl/B4hkCDFZS/P14el/HXGqONOWlXB0zVS4n9yRSkgogXpYEbxfqshfxkpDX2 +fPhWMlO++ppR5BKQPhfNTFKRdgpms/xwIJ0RK6ZtTBwqmUfjWMIMKCQpIcJ/xRhp +PGRLhKNZaawAK7Nyi1jQjbQs497WeZ6CP5aIHBkCggEALHyl83FQ5ilQLJZH/6E9 +H9854MqTIkWajxAgAa2yzqVrSWS7XuoBFe2kSimX/3V8Jx7UQV57kwy3RbVl5FQ3 +2I7YRwawItFulAPkpXNr4gEQtYKuzEUgMX2ilX54BZQ804lYmaM4Rp0FI9arQh1O +XWsZRW4HFut6Oa4cgptIeH22ce5L+nZdaL3oy8a5Cr7W7bChIXySt+tioKHvXC/+ +yYgDTnTECrVzuaD4UFv+9t3XCcRh34PQ010+YjZWhzifehyh7AeKuxX0er8ymgpd +q6zT9CyZ+8IZATer9qruMG4jDfO5vI1eZwiDdpF5klOdtZQqq80ANmeEu2McHVhh +jQKCAQBbohPxMb3QYdukGp8IsIF04GfnTgaDbRgl4KeUyzdBN3nzvCKK0HDluptR +4Ua64JksGG24gsTBy6yuQoGRCG0LJe0Ty3TRRnvZ8MpADoNMObspMSC8n8kk6ps+ +SoG1U9t6HYlIgQagvTc7mTmCmwYX1zlCoZp24yz5pDkKxqoPFDtrGlXxeUgOhpDT +Mzi+DNTz9sH9vod4ibQiOseUxITwQpXHTJVrtNfvva6xjlhq+GGCuKIUwkUKOvBC +ds7SR9demn69aWCyzXqD1cTnmxtn6bNPukwowg7a07ieUyKftcJ1icOWQ/bdQkEf +dV1dhNiQEnqs4vDBVn40dnTKSSG2 +-----END PRIVATE KEY----- diff --git a/examples/Client/DistributedLock/README.md b/examples/Client/DistributedLock/README.md index cdac6f91..6a1af3b3 100644 --- a/examples/Client/DistributedLock/README.md +++ b/examples/Client/DistributedLock/README.md @@ -24,7 +24,7 @@ cd examples/Client/DistributedLock In order to run the application that generates data for the workers to process, simply run the following command: ```bash -dapr run --components-path ./Components --app-id generator -- dotnet run +dapr run --resources-path ./Components --app-id generator -- dotnet run ``` This application will create a new file to process once every 10 seconds. The files are stored in `DistributedLock/tmp`. @@ -33,8 +33,8 @@ This application will create a new file to process once every 10 seconds. The fi In order to properly demonstrate locking, this application will be run more than once with the same App ID. However, the applications do need different ports in order to properly receive bindings. Run them with the command below: ```bash -dapr run --components-path ./Components --app-id worker --app-port 5000 -- dotnet run -dapr run --components-path ./Components --app-id worker --app-port 5001 -- dotnet run +dapr run --resources-path ./Components --app-id worker --app-port 5000 -- dotnet run +dapr run --resources-path ./Components --app-id worker --app-port 5001 -- dotnet run ``` After running the applications, they will attempt to process files. You should see output such as: diff --git a/examples/Jobs/JobsSample/Program.cs b/examples/Jobs/JobsSample/Program.cs index 30ca85ba..f19e375b 100644 --- a/examples/Jobs/JobsSample/Program.cs +++ b/examples/Jobs/JobsSample/Program.cs @@ -16,43 +16,36 @@ using System.Text; using Dapr.Jobs; using Dapr.Jobs.Extensions; using Dapr.Jobs.Models; -using Dapr.Jobs.Models.Responses; var builder = WebApplication.CreateBuilder(args); - builder.Services.AddDaprJobsClient(); +builder.Logging.ClearProviders(); +builder.Logging.AddConsole(); var app = builder.Build(); //Set a handler to deal with incoming jobs -var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(5)); -app.MapDaprScheduledJobHandler((string? jobName, DaprJobDetails? jobDetails, ILogger? logger, CancellationToken cancellationToken) => +app.MapDaprScheduledJobHandler(async (string jobName, ReadOnlyMemory jobPayload, ILogger? logger, CancellationToken cancellationToken) => { logger?.LogInformation("Received trigger invocation for job '{jobName}'", jobName); - if (jobDetails?.Payload is not null) - { - var deserializedPayload = Encoding.UTF8.GetString(jobDetails.Payload); - logger?.LogInformation("Received invocation for the job '{jobName}' with payload '{deserializedPayload}'", - jobName, deserializedPayload); - //Do something that needs the cancellation token - } - else - { - logger?.LogWarning("Failed to deserialize payload for job '{jobName}'", jobName); - } + + var deserializedPayload = Encoding.UTF8.GetString(jobPayload.Span); + logger?.LogInformation("Received invocation for the job '{jobName}' with payload '{deserializedPayload}'", + jobName, deserializedPayload); + await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken); + return Task.CompletedTask; -}, cancellationTokenSource.Token); +}, TimeSpan.FromSeconds(5)); -app.Run(); - -await using var scope = app.Services.CreateAsyncScope(); -var logger = scope.ServiceProvider.GetRequiredService(); +using var scope = app.Services.CreateScope(); +var logger = scope.ServiceProvider.GetRequiredService>(); var daprJobsClient = scope.ServiceProvider.GetRequiredService(); logger.LogInformation("Scheduling one-time job 'myJob' to execute 10 seconds from now"); -await daprJobsClient.ScheduleJobAsync("myJob", DaprJobSchedule.FromDateTime(DateTime.UtcNow.AddSeconds(10)), - Encoding.UTF8.GetBytes("This is a test")); +await daprJobsClient.ScheduleJobAsync("myJob", DaprJobSchedule.FromDuration(TimeSpan.FromSeconds(2)), + Encoding.UTF8.GetBytes("This is a test"), repeats: 10); logger.LogInformation("Scheduled one-time job 'myJob'"); +app.Run(); #pragma warning restore CS0618 // Type or member is obsolete diff --git a/src/Dapr.AI/Conversation/DaprConversationClient.cs b/src/Dapr.AI/Conversation/DaprConversationClient.cs index 2335197b..ed33e677 100644 --- a/src/Dapr.AI/Conversation/DaprConversationClient.cs +++ b/src/Dapr.AI/Conversation/DaprConversationClient.cs @@ -77,7 +77,11 @@ public sealed class DaprConversationClient : DaprAIClient if (options is not null) { - request.ContextID = options.ConversationId; + if (options.ConversationId is not null) + { + request.ContextID = options.ConversationId; + } + request.ScrubPII = options.ScrubPII; foreach (var (key, value) in options.Metadata) @@ -96,7 +100,7 @@ public sealed class DaprConversationClient : DaprAIClient request.Inputs.Add(new P.ConversationInput { ScrubPII = input.ScrubPII, - Message = input.Message, + Content = input.Content, Role = input.Role.GetValueFromEnumMember() }); } diff --git a/src/Dapr.AI/Conversation/DaprConversationInput.cs b/src/Dapr.AI/Conversation/DaprConversationInput.cs index 3485849c..2bda1a2f 100644 --- a/src/Dapr.AI/Conversation/DaprConversationInput.cs +++ b/src/Dapr.AI/Conversation/DaprConversationInput.cs @@ -16,7 +16,7 @@ namespace Dapr.AI.Conversation; /// /// Represents an input for the Dapr Conversational API. /// -/// The message to send to the LLM. +/// The content to send to the LLM. /// The role indicating the entity providing the message. /// If true, scrubs the data that goes into the LLM. -public sealed record DaprConversationInput(string Message, DaprConversationRole Role, bool ScrubPII = false); +public sealed record DaprConversationInput(string Content, DaprConversationRole Role, bool ScrubPII = false); diff --git a/src/Dapr.Actors/DaprHttpInteractor.cs b/src/Dapr.Actors/DaprHttpInteractor.cs index 4643ad2f..7dcbe70c 100644 --- a/src/Dapr.Actors/DaprHttpInteractor.cs +++ b/src/Dapr.Actors/DaprHttpInteractor.cs @@ -265,7 +265,7 @@ namespace Dapr.Actors return this.SendAsync(RequestFunc, relativeUrl, cancellationToken); } - public async Task GetReminderAsync(string actorType, string actorId, string reminderName, CancellationToken cancellationToken = default) + public async Task GetReminderAsync(string actorType, string actorId, string reminderName, CancellationToken cancellationToken = default) { var relativeUrl = string.Format(CultureInfo.InvariantCulture, Constants.ActorReminderRelativeUrlFormat, actorType, actorId, reminderName); @@ -278,8 +278,7 @@ namespace Dapr.Actors return request; } - var response = await this.SendAsync(RequestFunc, relativeUrl, cancellationToken); - return await response.Content.ReadAsStreamAsync(); + return await this.SendAsync(RequestFunc, relativeUrl, cancellationToken); } public Task UnregisterReminderAsync(string actorType, string actorId, string reminderName, CancellationToken cancellationToken = default) diff --git a/src/Dapr.Actors/Extensions/DurationExtensions.cs b/src/Dapr.Actors/Extensions/DurationExtensions.cs new file mode 100644 index 00000000..c3f1cafa --- /dev/null +++ b/src/Dapr.Actors/Extensions/DurationExtensions.cs @@ -0,0 +1,124 @@ +// ------------------------------------------------------------------------ +// Copyright 2025 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. +// ------------------------------------------------------------------------ + +using System; +using System.Globalization; +using System.Linq; +using System.Text.RegularExpressions; + +namespace Dapr.Actors.Extensions; + +internal static class DurationExtensions +{ + /// + /// Used to parse the duration string accompanying an @every expression. + /// + private static readonly Regex durationRegex = new(@"(?\d+(\.\d+)?)(?ns|us|µs|ms|s|m|h)", RegexOptions.Compiled | RegexOptions.IgnoreCase); + /// + /// A regular expression used to evaluate whether a given prefix period embodies an @every statement. + /// + private static readonly Regex isEveryExpression = new(@"^@every (\d+(\.\d+)?(ns|us|µs|ms|s|m|h))+$"); + /// + /// The various acceptable duration values for a period expression. + /// + private static readonly string[] acceptablePeriodValues = + { + "yearly", "monthly", "weekly", "daily", "midnight", "hourly" + }; + + private const string YearlyPrefixPeriod = "@yearly"; + private const string MonthlyPrefixPeriod = "@monthly"; + private const string WeeklyPrefixPeriod = "@weekly"; + private const string DailyPrefixPeriod = "@daily"; + private const string MidnightPrefixPeriod = "@midnight"; + private const string HourlyPrefixPeriod = "@hourly"; + private const string EveryPrefixPeriod = "@every"; + + /// + /// Indicates that the schedule represents a prefixed period expression. + /// + /// + /// + public static bool IsDurationExpression(this string expression) => expression.StartsWith('@') && + (isEveryExpression.IsMatch(expression) || + expression.EndsWithAny(acceptablePeriodValues, StringComparison.InvariantCulture)); + + /// + /// Creates a TimeSpan value from the prefixed period value. + /// + /// The prefixed period value to parse. + /// A TimeSpan value matching the provided period. + public static TimeSpan FromPrefixedPeriod(this string period) + { + if (period.StartsWith(YearlyPrefixPeriod)) + { + var dateTime = DateTime.UtcNow; + return dateTime.AddYears(1) - dateTime; + } + + if (period.StartsWith(MonthlyPrefixPeriod)) + { + var dateTime = DateTime.UtcNow; + return dateTime.AddMonths(1) - dateTime; + } + + if (period.StartsWith(MidnightPrefixPeriod)) + { + return new TimeSpan(); + } + + if (period.StartsWith(WeeklyPrefixPeriod)) + { + return TimeSpan.FromDays(7); + } + + if (period.StartsWith(DailyPrefixPeriod) || period.StartsWith(MidnightPrefixPeriod)) + { + return TimeSpan.FromDays(1); + } + + if (period.StartsWith(HourlyPrefixPeriod)) + { + return TimeSpan.FromHours(1); + } + + if (period.StartsWith(EveryPrefixPeriod)) + { + //A sequence of decimal numbers each with an optional fraction and unit suffix + //Valid time units are: 'ns', 'us'/'µs', 'ms', 's', 'm', and 'h' + double totalMilliseconds = 0; + var durationString = period.Split(' ').Last().Trim(); + + foreach (Match match in durationRegex.Matches(durationString)) + { + var value = double.Parse(match.Groups["value"].Value, CultureInfo.InvariantCulture); + var unit = match.Groups["unit"].Value.ToLower(); + + totalMilliseconds += unit switch + { + "ns" => value / 1_000_000, + "us" or "µs" => value / 1_000, + "ms" => value, + "s" => value * 1_000, + "m" => value * 1_000 * 60, + "h" => value * 1_000 * 60 * 60, + _ => throw new ArgumentException($"Unknown duration unit: {unit}") + }; + } + + return TimeSpan.FromMilliseconds(totalMilliseconds); + } + + throw new ArgumentException($"Unknown prefix period expression: {period}"); + } +} diff --git a/src/Dapr.Actors/Extensions/StringExtensions.cs b/src/Dapr.Actors/Extensions/StringExtensions.cs new file mode 100644 index 00000000..f2f69452 --- /dev/null +++ b/src/Dapr.Actors/Extensions/StringExtensions.cs @@ -0,0 +1,32 @@ +// ------------------------------------------------------------------------ +// Copyright 2025 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. +// ------------------------------------------------------------------------ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; + +namespace Dapr.Actors.Extensions; + +internal static class StringExtensions +{ + /// + /// Extension method that validates a string against a list of possible matches. + /// + /// The string value to evaluate. + /// The possible values to look for a match within. + /// The type of string comparison to perform. + /// True if the value ends with any of the possible values; otherwise false. + public static bool EndsWithAny(this string value, IReadOnlyList possibleValues, StringComparison comparisonType ) + => possibleValues.Any(val => value.EndsWith(val, comparisonType)); +} diff --git a/src/Dapr.Actors/IDaprInteractor.cs b/src/Dapr.Actors/IDaprInteractor.cs index 5849328a..e0d91c44 100644 --- a/src/Dapr.Actors/IDaprInteractor.cs +++ b/src/Dapr.Actors/IDaprInteractor.cs @@ -11,6 +11,8 @@ // limitations under the License. // ------------------------------------------------------------------------ +using System.Net.Http; + namespace Dapr.Actors { using System.IO; @@ -81,8 +83,8 @@ namespace Dapr.Actors /// ActorId. /// Name of reminder to unregister. /// Cancels the operation. - /// A representing the result of the asynchronous operation. - Task GetReminderAsync(string actorType, string actorId, string reminderName, CancellationToken cancellationToken = default); + /// A containing the response of the asynchronous HTTP operation. + Task GetReminderAsync(string actorType, string actorId, string reminderName, CancellationToken cancellationToken = default); /// /// Unregisters a reminder. diff --git a/src/Dapr.Actors/Runtime/ConverterUtils.cs b/src/Dapr.Actors/Runtime/ConverterUtils.cs index 80155cf3..94cfd3d3 100644 --- a/src/Dapr.Actors/Runtime/ConverterUtils.cs +++ b/src/Dapr.Actors/Runtime/ConverterUtils.cs @@ -11,138 +11,148 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Runtime +using Dapr.Actors.Extensions; + +namespace Dapr.Actors.Runtime; + +using System; +using System.Text; +using System.Text.RegularExpressions; + +internal static class ConverterUtils { - using System; - using System.Text; - using System.Text.RegularExpressions; - - internal class ConverterUtils + private static Regex regex = new("^(R(?\\d+)/)?P((?\\d+)Y)?((?\\d+)M)?((?\\d+)W)?((?\\d+)D)?(T((?\\d+)H)?((?\\d+)M)?((?\\d+)S)?)?$", RegexOptions.Compiled); + public static TimeSpan ConvertTimeSpanFromDaprFormat(string valueString) { - private static Regex regex = new Regex("^(R(?\\d+)/)?P((?\\d+)Y)?((?\\d+)M)?((?\\d+)W)?((?\\d+)D)?(T((?\\d+)H)?((?\\d+)M)?((?\\d+)S)?)?$", RegexOptions.Compiled); - public static TimeSpan ConvertTimeSpanFromDaprFormat(string valueString) + if (string.IsNullOrEmpty(valueString)) { - if (string.IsNullOrEmpty(valueString)) - { - var never = TimeSpan.FromMilliseconds(-1); - return never; - } - - // TimeSpan is a string. Format returned by Dapr is: 1h4m5s4ms4us4ns - // acceptable values are: m, s, ms, us(micro), ns - var spanOfValue = valueString.AsSpan(); - - // Change the value returned by Dapr runtime, so that it can be parsed with TimeSpan. - // Format returned by Dapr runtime: 4h15m50s60ms. It doesnt have days. - // Dapr runtime should handle timespans in ISO 8601 format. - // Replace ms before m & s. Also append 0 days for parsing correctly with TimeSpan - int hIndex = spanOfValue.IndexOf('h'); - int mIndex = spanOfValue.IndexOf('m'); - int sIndex = spanOfValue.IndexOf('s'); - int msIndex = spanOfValue.IndexOf("ms"); - - // handle days from hours. - var hoursSpan = spanOfValue.Slice(0, hIndex); - var hours = int.Parse(hoursSpan); - var days = hours / 24; - hours %= 24; - - var minutesSpan = spanOfValue[(hIndex + 1)..mIndex]; - var minutes = int.Parse(minutesSpan); - - var secondsSpan = spanOfValue[(mIndex + 1)..sIndex]; - var seconds = int.Parse(secondsSpan); - - var millisecondsSpan = spanOfValue[(sIndex + 1)..msIndex]; - var milliseconds = int.Parse(millisecondsSpan); - - return new TimeSpan(days, hours, minutes, seconds, milliseconds); + var never = TimeSpan.FromMilliseconds(-1); + return never; + } + + if (valueString.IsDurationExpression()) + { + return valueString.FromPrefixedPeriod(); } - public static string ConvertTimeSpanValueInDaprFormat(TimeSpan? value) - { - // write in format expected by Dapr, it only accepts h, m, s, ms, us(micro), ns - var stringValue = string.Empty; - if (value.Value >= TimeSpan.Zero) - { - var hours = (value.Value.Days * 24) + value.Value.Hours; - stringValue = FormattableString.Invariant($"{hours}h{value.Value.Minutes}m{value.Value.Seconds}s{value.Value.Milliseconds}ms"); - } + // TimeSpan is a string. Format returned by Dapr is: 1h4m5s4ms4us4ns + // acceptable values are: m, s, ms, us(micro), ns + var spanOfValue = valueString.AsSpan(); + // Change the value returned by Dapr runtime, so that it can be parsed with TimeSpan. + // Format returned by Dapr runtime: 4h15m50s60ms. It doesnt have days. + // Dapr runtime should handle timespans in ISO 8601 format. + // Replace ms before m & s. Also append 0 days for parsing correctly with TimeSpan + int hIndex = spanOfValue.IndexOf('h'); + int mIndex = spanOfValue.IndexOf('m'); + int sIndex = spanOfValue.IndexOf('s'); + int msIndex = spanOfValue.IndexOf("ms"); + + // handle days from hours. + var hoursSpan = spanOfValue.Slice(0, hIndex); + var hours = int.Parse(hoursSpan); + var days = hours / 24; + hours %= 24; + + var minutesSpan = spanOfValue[(hIndex + 1)..mIndex]; + var minutes = int.Parse(minutesSpan); + + var secondsSpan = spanOfValue[(mIndex + 1)..sIndex]; + var seconds = int.Parse(secondsSpan); + + var millisecondsSpan = spanOfValue[(sIndex + 1)..msIndex]; + var milliseconds = int.Parse(millisecondsSpan); + + return new TimeSpan(days, hours, minutes, seconds, milliseconds); + } + + public static string ConvertTimeSpanValueInDaprFormat(TimeSpan? value) + { + // write in format expected by Dapr, it only accepts h, m, s, ms, us(micro), ns + var stringValue = string.Empty; + if (value is null) return stringValue; - } - - public static string ConvertTimeSpanValueInISO8601Format(TimeSpan value, int? repetitions) + + if (value.Value >= TimeSpan.Zero) { - StringBuilder builder = new StringBuilder(); - - if (repetitions == null) - { - return ConvertTimeSpanValueInDaprFormat(value); - } - - if (value.Milliseconds > 0) - { - throw new ArgumentException("The TimeSpan value, combined with repetition cannot be in milliseconds.", nameof(value)); - } - - builder.AppendFormat("R{0}/P", repetitions); - - if(value.Days > 0) - { - builder.AppendFormat("{0}D", value.Days); - } - - builder.Append("T"); - - if(value.Hours > 0) - { - builder.AppendFormat("{0}H", value.Hours); - } - - if(value.Minutes > 0) - { - builder.AppendFormat("{0}M", value.Minutes); - } - - if(value.Seconds > 0) - { - builder.AppendFormat("{0}S", value.Seconds); - } - return builder.ToString(); + var hours = (value.Value.Days * 24) + value.Value.Hours; + stringValue = FormattableString.Invariant($"{hours}h{value.Value.Minutes}m{value.Value.Seconds}s{value.Value.Milliseconds}ms"); } + + return stringValue; + } - public static (TimeSpan, int?) ConvertTimeSpanValueFromISO8601Format(string valueString) + public static string ConvertTimeSpanValueInISO8601Format(TimeSpan value, int? repetitions) + { + StringBuilder builder = new StringBuilder(); + + if (repetitions == null) { - // ISO 8601 format can be Rn/PaYbMcHTdHeMfS or PaYbMcHTdHeMfS so if it does - // not start with R or P then assuming it to default Dapr format without repetition - if (!(valueString.StartsWith('R') || valueString.StartsWith('P'))) - { - return (ConvertTimeSpanFromDaprFormat(valueString), -1); - } - - var matches = regex.Match(valueString); - - var repetition = matches.Groups["repetition"].Success ? int.Parse(matches.Groups["repetition"].Value) : (int?)null; - - var days = 0; - var year = matches.Groups["year"].Success ? int.Parse(matches.Groups["year"].Value) : 0; - days = year * 365; - - var month = matches.Groups["month"].Success ? int.Parse(matches.Groups["month"].Value) : 0; - days += month * 30; - - var week = matches.Groups["week"].Success ? int.Parse(matches.Groups["week"].Value) : 0; - days += week * 7; - - var day = matches.Groups["day"].Success ? int.Parse(matches.Groups["day"].Value) : 0; - days += day; - - var hour = matches.Groups["hour"].Success ? int.Parse(matches.Groups["hour"].Value) : 0; - var minute = matches.Groups["minute"].Success ? int.Parse(matches.Groups["minute"].Value) : 0; - var second = matches.Groups["second"].Success ? int.Parse(matches.Groups["second"].Value) : 0; - - return (new TimeSpan(days, hour, minute, second), repetition); + return ConvertTimeSpanValueInDaprFormat(value); } + + if (value.Milliseconds > 0) + { + throw new ArgumentException("The TimeSpan value, combined with repetition cannot be in milliseconds.", nameof(value)); + } + + builder.Append($"R{repetitions}/P"); + + if(value.Days > 0) + { + builder.Append($"{value.Days}D"); + } + + builder.Append("T"); + + if(value.Hours > 0) + { + builder.Append($"{value.Hours}H"); + } + + if(value.Minutes > 0) + { + builder.Append($"{value.Minutes}M"); + } + + if(value.Seconds > 0) + { + builder.Append($"{value.Seconds}S"); + } + return builder.ToString(); + } + + public static (TimeSpan, int?) ConvertTimeSpanValueFromISO8601Format(string valueString) + { + // ISO 8601 format can be Rn/PaYbMcHTdHeMfS or PaYbMcHTdHeMfS so if it does + // not start with R or P then assuming it to default Dapr format without repetition + if (!(valueString.StartsWith('R') || valueString.StartsWith('P'))) + { + return (ConvertTimeSpanFromDaprFormat(valueString), -1); + } + + var matches = regex.Match(valueString); + + var repetition = matches.Groups["repetition"].Success ? int.Parse(matches.Groups["repetition"].Value) : (int?)null; + + + var days = 0; + var year = matches.Groups["year"].Success ? int.Parse(matches.Groups["year"].Value) : 0; + days = year * 365; + + var month = matches.Groups["month"].Success ? int.Parse(matches.Groups["month"].Value) : 0; + days += month * 30; + + var week = matches.Groups["week"].Success ? int.Parse(matches.Groups["week"].Value) : 0; + days += week * 7; + + var day = matches.Groups["day"].Success ? int.Parse(matches.Groups["day"].Value) : 0; + days += day; + + var hour = matches.Groups["hour"].Success ? int.Parse(matches.Groups["hour"].Value) : 0; + var minute = matches.Groups["minute"].Success ? int.Parse(matches.Groups["minute"].Value) : 0; + var second = matches.Groups["second"].Success ? int.Parse(matches.Groups["second"].Value) : 0; + + return (new TimeSpan(days, hour, minute, second), repetition); } } diff --git a/src/Dapr.Actors/Runtime/DefaultActorTimerManager.cs b/src/Dapr.Actors/Runtime/DefaultActorTimerManager.cs index 66f94f08..79f3eabc 100644 --- a/src/Dapr.Actors/Runtime/DefaultActorTimerManager.cs +++ b/src/Dapr.Actors/Runtime/DefaultActorTimerManager.cs @@ -15,6 +15,7 @@ using System; using System.Text.Json; using System.Threading.Tasks; using System.IO; +using Grpc.Core; namespace Dapr.Actors.Runtime { @@ -45,9 +46,14 @@ namespace Dapr.Actors.Runtime throw new ArgumentNullException(nameof(token)); } - var responseStream = await this.interactor.GetReminderAsync(token.ActorType, token.ActorId.ToString(), token.Name); - var reminder = await DeserializeReminderAsync(responseStream, token); - return reminder; + var response = await this.interactor.GetReminderAsync(token.ActorType, token.ActorId.ToString(), token.Name); + if ((int)response.StatusCode == 500) + { + return null; + } + + var responseStream = await response.Content.ReadAsStreamAsync(); + return await DeserializeReminderAsync(responseStream, token); } public override async Task UnregisterReminderAsync(ActorReminderToken reminder) @@ -84,24 +90,26 @@ namespace Dapr.Actors.Runtime await this.interactor.UnregisterTimerAsync(timer.ActorType, timer.ActorId.ToString(), timer.Name); } - private async ValueTask SerializeReminderAsync(ActorReminder reminder) + private static async ValueTask SerializeReminderAsync(ActorReminder reminder) { var info = new ReminderInfo(reminder.State, reminder.DueTime, reminder.Period, reminder.Repetitions, reminder.Ttl); return await info.SerializeAsync(); } - private async ValueTask DeserializeReminderAsync(Stream stream, ActorReminderToken token) + private static async ValueTask DeserializeReminderAsync(Stream stream, ActorReminderToken token) { if (stream == null) { throw new ArgumentNullException(nameof(stream)); } + var info = await ReminderInfo.DeserializeAsync(stream); - if(info == null) + if (info == null) { return null; } + var reminder = new ActorReminder(token.ActorType, token.ActorId, token.Name, info.Data, info.DueTime, info.Period); return reminder; diff --git a/src/Dapr.Actors/Runtime/ReminderInfo.cs b/src/Dapr.Actors/Runtime/ReminderInfo.cs index 447cf607..7c67b46d 100644 --- a/src/Dapr.Actors/Runtime/ReminderInfo.cs +++ b/src/Dapr.Actors/Runtime/ReminderInfo.cs @@ -11,99 +11,110 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Runtime +#nullable enable + +namespace Dapr.Actors.Runtime; + +using System; +using System.IO; +using System.Text; +using System.Text.Json; +using System.Threading.Tasks; + +// represents the wire format used by Dapr to store reminder info with the runtime +internal class ReminderInfo { - using System; - using System.IO; - using System.Text; - using System.Text.Json; - using System.Threading.Tasks; - - // represents the wire format used by Dapr to store reminder info with the runtime - internal class ReminderInfo + public ReminderInfo( + byte[] data, + TimeSpan dueTime, + TimeSpan period, + int? repetitions = null, + TimeSpan? ttl = null) { - public ReminderInfo( - byte[] data, - TimeSpan dueTime, - TimeSpan period, - int? repetitions = null, - TimeSpan? ttl = null) - { - this.Data = data; - this.DueTime = dueTime; - this.Period = period; - this.Ttl = ttl; - this.Repetitions = repetitions; - } + this.Data = data; + this.DueTime = dueTime; + this.Period = period; + this.Ttl = ttl; + this.Repetitions = repetitions; + } - public TimeSpan DueTime { get; private set; } + public TimeSpan DueTime { get; private set; } - public TimeSpan Period { get; private set; } + public TimeSpan Period { get; private set; } - public byte[] Data { get; private set; } + public byte[] Data { get; private set; } - public TimeSpan? Ttl { get; private set; } + public TimeSpan? Ttl { get; private set; } - public int? Repetitions { get; private set; } + public int? Repetitions { get; private set; } - internal static async Task DeserializeAsync(Stream stream) + internal static async Task DeserializeAsync(Stream stream) + { + var json = await JsonSerializer.DeserializeAsync(stream); + if(json.ValueKind == JsonValueKind.Null) { - var json = await JsonSerializer.DeserializeAsync(stream); - if(json.ValueKind == JsonValueKind.Null) - { - return null; - } - - var dueTime = default(TimeSpan); - var period = default(TimeSpan); - var data = default(byte[]); - int? repetition = null; - TimeSpan? ttl = null; - if (json.TryGetProperty("dueTime", out var dueTimeProperty)) - { - var dueTimeString = dueTimeProperty.GetString(); - dueTime = ConverterUtils.ConvertTimeSpanFromDaprFormat(dueTimeString); - } - - if (json.TryGetProperty("period", out var periodProperty)) - { - var periodString = periodProperty.GetString(); - (period, repetition) = ConverterUtils.ConvertTimeSpanValueFromISO8601Format(periodString); - } - - if (json.TryGetProperty("data", out var dataProperty) && dataProperty.ValueKind != JsonValueKind.Null) - { - data = dataProperty.GetBytesFromBase64(); - } - - if (json.TryGetProperty("ttl", out var ttlProperty)) - { - var ttlString = ttlProperty.GetString(); - ttl = ConverterUtils.ConvertTimeSpanFromDaprFormat(ttlString); - } - - return new ReminderInfo(data, dueTime, period, repetition, ttl); + return null; } - internal async ValueTask SerializeAsync() + var setAnyProperties = false; //Used to determine if anything was actually deserialized + var dueTime = TimeSpan.Zero; + var period = TimeSpan.Zero; + var data = Array.Empty(); + int? repetition = null; + TimeSpan? ttl = null; + if (json.TryGetProperty("dueTime", out var dueTimeProperty)) { - using var stream = new MemoryStream(); - using Utf8JsonWriter writer = new Utf8JsonWriter(stream); - - writer.WriteStartObject(); - writer.WriteString("dueTime", ConverterUtils.ConvertTimeSpanValueInDaprFormat(this.DueTime)); - writer.WriteString("period", ConverterUtils.ConvertTimeSpanValueInISO8601Format( - this.Period, this.Repetitions)); - writer.WriteBase64String("data", this.Data); - - if (Ttl != null) - { - writer.WriteString("ttl", ConverterUtils.ConvertTimeSpanValueInDaprFormat(Ttl)); - } - - writer.WriteEndObject(); - await writer.FlushAsync(); - return Encoding.UTF8.GetString(stream.ToArray()); + setAnyProperties = true; + var dueTimeString = dueTimeProperty.GetString(); + dueTime = ConverterUtils.ConvertTimeSpanFromDaprFormat(dueTimeString); } + + if (json.TryGetProperty("period", out var periodProperty)) + { + setAnyProperties = true; + var periodString = periodProperty.GetString(); + (period, repetition) = ConverterUtils.ConvertTimeSpanValueFromISO8601Format(periodString); + } + + if (json.TryGetProperty("data", out var dataProperty) && dataProperty.ValueKind != JsonValueKind.Null) + { + setAnyProperties = true; + data = dataProperty.GetBytesFromBase64(); + } + + if (json.TryGetProperty("ttl", out var ttlProperty)) + { + setAnyProperties = true; + var ttlString = ttlProperty.GetString(); + ttl = ConverterUtils.ConvertTimeSpanFromDaprFormat(ttlString); + } + + if (!setAnyProperties) + { + return null; //No properties were ever deserialized, so return null instead of default values + } + + return new ReminderInfo(data, dueTime, period, repetition, ttl); + } + + internal async ValueTask SerializeAsync() + { + using var stream = new MemoryStream(); + await using Utf8JsonWriter writer = new Utf8JsonWriter(stream); + + writer.WriteStartObject(); + writer.WriteString("dueTime", ConverterUtils.ConvertTimeSpanValueInDaprFormat(this.DueTime)); + writer.WriteString("period", ConverterUtils.ConvertTimeSpanValueInISO8601Format( + this.Period, this.Repetitions)); + writer.WriteBase64String("data", this.Data); + + if (Ttl != null) + { + writer.WriteString("ttl", ConverterUtils.ConvertTimeSpanValueInDaprFormat(Ttl)); + } + + writer.WriteEndObject(); + await writer.FlushAsync(); + return Encoding.UTF8.GetString(stream.ToArray()); } } diff --git a/src/Dapr.Client/DaprClientGrpc.cs b/src/Dapr.Client/DaprClientGrpc.cs index 40df4767..394b313e 100644 --- a/src/Dapr.Client/DaprClientGrpc.cs +++ b/src/Dapr.Client/DaprClientGrpc.cs @@ -1670,24 +1670,18 @@ internal class DaprClientGrpc : DaprClient ReadOnlyMemory plaintextBytes, string keyName, EncryptionOptions encryptionOptions, CancellationToken cancellationToken = default) { - if (MemoryMarshal.TryGetArray(plaintextBytes, out var plaintextSegment) && plaintextSegment.Array != null) + using var memoryStream = plaintextBytes.CreateMemoryStream(true); + + var encryptionResult = + await EncryptAsync(vaultResourceName, memoryStream, keyName, encryptionOptions, cancellationToken); + + var bufferedResult = new ArrayBufferWriter(); + await foreach (var item in encryptionResult.WithCancellation(cancellationToken)) { - var encryptionResult = await EncryptAsync(vaultResourceName, new MemoryStream(plaintextSegment.Array), - keyName, encryptionOptions, - cancellationToken); - - var bufferedResult = new ArrayBufferWriter(); - - await foreach (var item in encryptionResult.WithCancellation(cancellationToken)) - { - bufferedResult.Write(item.Span); - } - - return bufferedResult.WrittenMemory; + bufferedResult.Write(item.Span); } - throw new ArgumentException("The input instance doesn't have a valid underlying data store.", - nameof(plaintextBytes)); + return bufferedResult.WrittenMemory; } /// @@ -1895,22 +1889,18 @@ internal class DaprClientGrpc : DaprClient ReadOnlyMemory ciphertextBytes, string keyName, DecryptionOptions decryptionOptions, CancellationToken cancellationToken = default) { - if (MemoryMarshal.TryGetArray(ciphertextBytes, out var ciphertextSegment) && ciphertextSegment.Array != null) + using var memoryStream = ciphertextBytes.CreateMemoryStream(true); + + var decryptionResult = + await DecryptAsync(vaultResourceName, memoryStream, keyName, decryptionOptions, cancellationToken); + + var bufferedResult = new ArrayBufferWriter(); + await foreach (var item in decryptionResult.WithCancellation(cancellationToken)) { - var decryptionResult = await DecryptAsync(vaultResourceName, new MemoryStream(ciphertextSegment.Array), - keyName, decryptionOptions, cancellationToken); - - var bufferedResult = new ArrayBufferWriter(); - await foreach (var item in decryptionResult.WithCancellation(cancellationToken)) - { - bufferedResult.Write(item.Span); - } - - return bufferedResult.WrittenMemory; + bufferedResult.Write(item.Span); } - throw new ArgumentException("The input instance doesn't have a valid underlying data store", - nameof(ciphertextBytes)); + return bufferedResult.WrittenMemory; } /// diff --git a/src/Dapr.Client/Extensions/ReadOnlyMemoryExtensions.cs b/src/Dapr.Client/Extensions/ReadOnlyMemoryExtensions.cs new file mode 100644 index 00000000..928e2c3f --- /dev/null +++ b/src/Dapr.Client/Extensions/ReadOnlyMemoryExtensions.cs @@ -0,0 +1,36 @@ +// ------------------------------------------------------------------------ +// Copyright 2025 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. +// ------------------------------------------------------------------------ + +using System; +using System.IO; +using System.Runtime.InteropServices; + +namespace Dapr.Client; + +internal static class ReadOnlyMemoryExtensions +{ + public static MemoryStream CreateMemoryStream(this ReadOnlyMemory memory, bool isReadOnly) + { + if (memory.IsEmpty) + { + return new MemoryStream(Array.Empty(), !isReadOnly); + } + + if (MemoryMarshal.TryGetArray(memory, out ArraySegment segment)) + { + return new MemoryStream(segment.Array!, segment.Offset, segment.Count, !isReadOnly); + } + + throw new ArgumentException(nameof(memory), "Unable to create MemoryStream from provided memory value"); + } +} diff --git a/src/Dapr.Jobs/DaprJobsGrpcClient.cs b/src/Dapr.Jobs/DaprJobsGrpcClient.cs index b548290d..8743aa35 100644 --- a/src/Dapr.Jobs/DaprJobsGrpcClient.cs +++ b/src/Dapr.Jobs/DaprJobsGrpcClient.cs @@ -77,13 +77,23 @@ internal sealed class DaprJobsGrpcClient : DaprJobsClient ArgumentNullException.ThrowIfNull(jobName, nameof(jobName)); ArgumentNullException.ThrowIfNull(schedule, nameof(schedule)); - var job = new Autogenerated.Job { Name = jobName, Schedule = schedule.ExpressionValue }; + var job = new Autogenerated.Job { Name = jobName }; + //Set up the schedule (recurring or point in time) + if (schedule.IsPointInTimeExpression) + { + job.DueTime = schedule.ExpressionValue; + } + else if (schedule.IsCronExpression || schedule.IsPrefixedPeriodExpression || schedule.IsDurationExpression) + { + job.Schedule = schedule.ExpressionValue; + } + if (startingFrom is not null) { job.DueTime = ((DateTimeOffset)startingFrom).ToString("O"); } - + if (repeats is not null) { if (repeats < 0) @@ -153,11 +163,15 @@ internal sealed class DaprJobsGrpcClient : DaprJobsClient var envelope = new Autogenerated.GetJobRequest { Name = jobName }; var grpcCallOptions = DaprClientUtilities.ConfigureGrpcCallOptions(typeof(DaprJobsClient).Assembly, this.DaprApiToken, cancellationToken); var response = await Client.GetJobAlpha1Async(envelope, grpcCallOptions); - return new DaprJobDetails(new DaprJobSchedule(response.Job.Schedule)) + var schedule = DateTime.TryParse(response.Job.DueTime, out var dueTime) + ? DaprJobSchedule.FromDateTime(dueTime) + : new DaprJobSchedule(response.Job.Schedule); + + return new DaprJobDetails(schedule) { - DueTime = response.Job.DueTime is not null ? DateTime.Parse(response.Job.DueTime) : null, - Ttl = response.Job.Ttl is not null ? DateTime.Parse(response.Job.Ttl) : null, - RepeatCount = response.Job.Repeats == default ? null : (int?)response.Job.Repeats, + DueTime = !string.IsNullOrWhiteSpace(response.Job.DueTime) ? DateTime.Parse(response.Job.DueTime) : null, + Ttl = !string.IsNullOrWhiteSpace(response.Job.Ttl) ? DateTime.Parse(response.Job.Ttl) : null, + RepeatCount = (int?)response.Job.Repeats, Payload = response.Job.Data.ToByteArray() }; } diff --git a/src/Dapr.Jobs/Extensions/EndpointRouteBuilderExtensions.cs b/src/Dapr.Jobs/Extensions/EndpointRouteBuilderExtensions.cs index 26ef579c..133f0d5a 100644 --- a/src/Dapr.Jobs/Extensions/EndpointRouteBuilderExtensions.cs +++ b/src/Dapr.Jobs/Extensions/EndpointRouteBuilderExtensions.cs @@ -11,8 +11,6 @@ // limitations under the License. // ------------------------------------------------------------------------ -using System.Text.Json; -using Dapr.Jobs.Models.Responses; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Routing; @@ -29,41 +27,41 @@ public static class EndpointRouteBuilderExtensions /// /// The to add the route to. /// The asynchronous action provided by the developer that handles any inbound requests. The first two - /// parameters must be a nullable for the jobName and a nullable with the - /// payload details, but otherwise can be populated with additional services to be injected into the delegate. - /// Cancellation token that will be passed in as the last parameter to the delegate action. + /// parameters must be a for the jobName and the originally registered ReadOnlyMemory<byte> with the + /// payload value, but otherwise can be populated with additional services to be injected into the delegate. + /// Optional timeout to apply to a per-request cancellation token. public static IEndpointRouteBuilder MapDaprScheduledJobHandler(this IEndpointRouteBuilder endpoints, - Delegate action, CancellationToken cancellationToken = default) + Delegate action, TimeSpan? timeout = null) { ArgumentNullException.ThrowIfNull(endpoints, nameof(endpoints)); ArgumentNullException.ThrowIfNull(action, nameof(action)); endpoints.MapPost("/job/{jobName}", async context => { - var jobName = (string?)context.Request.RouteValues["jobName"]; - DaprJobDetails? jobPayload = null; - - if (context.Request.ContentLength is > 0) + //Retrieve the name of the job from the request path + var jobName = string.Empty; + if (context.Request.RouteValues.TryGetValue("jobName", out var capturedJobName)) { - using var reader = new StreamReader(context.Request.Body); - var body = await reader.ReadToEndAsync(); - - try - { - var deserializedJobPayload = JsonSerializer.Deserialize(body); - jobPayload = deserializedJobPayload?.ToType() ?? null; - } - catch (JsonException) - { - jobPayload = null; - } + jobName = (string)capturedJobName!; } - var parameters = new Dictionary + //Retrieve the job payload from the request body + ReadOnlyMemory payload = new(); + if (context.Request.ContentLength is > 0) + { + using var streamContent = new StreamContent(context.Request.Body); + payload = await streamContent.ReadAsByteArrayAsync(CancellationToken.None); + } + + using var cts = timeout.HasValue + ? new CancellationTokenSource(timeout.Value) + : new CancellationTokenSource(); + + var parameters = new Dictionary { { typeof(string), jobName }, - { typeof(DaprJobDetails), jobPayload }, - { typeof(CancellationToken), CancellationToken.None } + { typeof(ReadOnlyMemory), payload }, + { typeof(CancellationToken), cts.Token } }; var actionParameters = action.Method.GetParameters(); diff --git a/src/Dapr.Jobs/Extensions/TimeSpanExtensions.cs b/src/Dapr.Jobs/Extensions/TimeSpanExtensions.cs index 2c6b1af9..0be89bd6 100644 --- a/src/Dapr.Jobs/Extensions/TimeSpanExtensions.cs +++ b/src/Dapr.Jobs/Extensions/TimeSpanExtensions.cs @@ -67,7 +67,7 @@ internal static class TimeSpanExtensions /// True if the string represents a parseable interval duration; false if not. public static bool IsDurationString(this string interval) { - interval = interval.Replace("ms", "q"); + interval = interval.Replace("ms", "q").Replace("@every ", string.Empty); return hourRegex.Match(interval).Success || minuteRegex.Match(interval).Success || secondRegex.Match(interval).Success || diff --git a/src/Dapr.Jobs/Models/DaprJobSchedule.cs b/src/Dapr.Jobs/Models/DaprJobSchedule.cs index e00c77f4..f897bd6b 100644 --- a/src/Dapr.Jobs/Models/DaprJobSchedule.cs +++ b/src/Dapr.Jobs/Models/DaprJobSchedule.cs @@ -65,11 +65,8 @@ public sealed class DaprJobSchedule /// /// The date and time when the job should be triggered. /// - public static DaprJobSchedule FromDateTime(DateTimeOffset scheduledTime) - { - return new DaprJobSchedule(scheduledTime.ToString("O")); - } - + public static DaprJobSchedule FromDateTime(DateTimeOffset scheduledTime) => new(scheduledTime.ToString("O")); + /// /// Specifies a schedule using a Cron-like expression or '@' prefixed period strings. /// @@ -86,10 +83,7 @@ public sealed class DaprJobSchedule /// Specifies a schedule using a duration interval articulated via a . /// /// The duration interval. - public static DaprJobSchedule FromDuration(TimeSpan duration) - { - return new DaprJobSchedule(duration.ToDurationString()); - } + public static DaprJobSchedule FromDuration(TimeSpan duration) => new($"@every {duration.ToDurationString()}"); /// /// Specifies a schedule in which the job is triggered to run once a year. @@ -104,7 +98,7 @@ public sealed class DaprJobSchedule /// /// Specifies a schedule in which the job is triggered weekly. /// - public static DaprJobSchedule Weekly { get; } =new DaprJobSchedule("@weekly"); + public static DaprJobSchedule Weekly { get; } = new DaprJobSchedule("@weekly"); /// /// Specifies a schedule in which the job is triggered daily. diff --git a/src/Dapr.Jobs/Models/Responses/DaprJobDetails.cs b/src/Dapr.Jobs/Models/Responses/DaprJobDetails.cs index 9b940bee..1e53d95e 100644 --- a/src/Dapr.Jobs/Models/Responses/DaprJobDetails.cs +++ b/src/Dapr.Jobs/Models/Responses/DaprJobDetails.cs @@ -11,9 +11,6 @@ // limitations under the License. // ------------------------------------------------------------------------ -using System.Text.Json.Serialization; -using Dapr.Jobs.JsonConverters; - namespace Dapr.Jobs.Models.Responses; /// @@ -46,52 +43,3 @@ public sealed record DaprJobDetails(DaprJobSchedule Schedule) /// public byte[]? Payload { get; init; } = null; } - -/// -/// A deserializable version of the . -/// -internal sealed record DeserializableDaprJobDetails -{ - /// - /// Represents the schedule that triggers the job. - /// - public string? Schedule { get; init; } - - /// - /// Allows for jobs with fixed repeat counts. - /// - public int? RepeatCount { get; init; } = null; - - /// - /// Identifies a point-in-time representing when the job schedule should start from, - /// or as a "one-shot" time if other scheduling fields are not provided. - /// - [JsonConverter(typeof(Iso8601DateTimeJsonConverter))] - public DateTimeOffset? DueTime { get; init; } = null; - - /// - /// A point-in-time value representing with the job should expire. - /// - /// - /// This must be greater than if both are set. - /// - [JsonConverter(typeof(Iso8601DateTimeJsonConverter))] - public DateTimeOffset? Ttl { get; init; } = null; - - /// - /// Stores the main payload of the job which is passed to the trigger function. - /// - public byte[]? Payload { get; init; } = null; - - public DaprJobDetails ToType() - { - var schedule = DaprJobSchedule.FromExpression(Schedule ?? string.Empty); - return new DaprJobDetails(schedule) - { - DueTime = DueTime, - Payload = Payload, - RepeatCount = RepeatCount, - Ttl = Ttl - }; - } -} diff --git a/src/Dapr.Protos/Protos/dapr/proto/runtime/v1/dapr.proto b/src/Dapr.Protos/Protos/dapr/proto/runtime/v1/dapr.proto index d1d8658f..0ae51d6b 100644 --- a/src/Dapr.Protos/Protos/dapr/proto/runtime/v1/dapr.proto +++ b/src/Dapr.Protos/Protos/dapr/proto/runtime/v1/dapr.proto @@ -1225,7 +1225,6 @@ message Job { // Systemd timer style cron accepts 6 fields: // seconds | minutes | hours | day of month | month | day of week // 0-59 | 0-59 | 0-23 | 1-31 | 1-12/jan-dec | 0-6/sun-sat - // // "0 30 * * * *" - every hour on the half hour // "0 15 3 * * *" - every day at 03:15 @@ -1320,8 +1319,8 @@ message ConversationRequest { } message ConversationInput { - // The message to send to the llm - string message = 1; + // The content to send to the llm + string content = 1; // The role to set for the message optional string role = 2; @@ -1345,4 +1344,4 @@ message ConversationResponse { // An array of results. repeated ConversationResult outputs = 2; -} +} \ No newline at end of file diff --git a/test/Dapr.AI.Test/Dapr.AI.Test.csproj b/test/Dapr.AI.Test/Dapr.AI.Test.csproj index f937f64e..b4a07c1f 100644 --- a/test/Dapr.AI.Test/Dapr.AI.Test.csproj +++ b/test/Dapr.AI.Test/Dapr.AI.Test.csproj @@ -7,7 +7,6 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all - diff --git a/test/Dapr.Actors.AspNetCore.IntegrationTest/Dapr.Actors.AspNetCore.IntegrationTest.csproj b/test/Dapr.Actors.AspNetCore.IntegrationTest/Dapr.Actors.AspNetCore.IntegrationTest.csproj index 921e2dda..394cc9de 100644 --- a/test/Dapr.Actors.AspNetCore.IntegrationTest/Dapr.Actors.AspNetCore.IntegrationTest.csproj +++ b/test/Dapr.Actors.AspNetCore.IntegrationTest/Dapr.Actors.AspNetCore.IntegrationTest.csproj @@ -4,7 +4,6 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all - diff --git a/test/Dapr.Actors.AspNetCore.Test/Dapr.Actors.AspNetCore.Test.csproj b/test/Dapr.Actors.AspNetCore.Test/Dapr.Actors.AspNetCore.Test.csproj index c448e915..9f9e4311 100644 --- a/test/Dapr.Actors.AspNetCore.Test/Dapr.Actors.AspNetCore.Test.csproj +++ b/test/Dapr.Actors.AspNetCore.Test/Dapr.Actors.AspNetCore.Test.csproj @@ -9,7 +9,6 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all - diff --git a/test/Dapr.Actors.Test/ActorCodeBuilderTests.cs b/test/Dapr.Actors.Test/ActorCodeBuilderTests.cs index 2f2bb67d..b82c56fc 100644 --- a/test/Dapr.Actors.Test/ActorCodeBuilderTests.cs +++ b/test/Dapr.Actors.Test/ActorCodeBuilderTests.cs @@ -11,44 +11,43 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Test +namespace Dapr.Actors.Test; + +using System.Threading.Tasks; +using Dapr.Actors.Builder; +using Dapr.Actors.Communication; +using Dapr.Actors.Description; +using Dapr.Actors.Runtime; +using Xunit; + +/// +/// Test class for Actor Code builder. +/// +public class ActorCodeBuilderTests { - using System.Threading.Tasks; - using Dapr.Actors.Builder; - using Dapr.Actors.Communication; - using Dapr.Actors.Description; - using Dapr.Actors.Runtime; - using Xunit; - /// - /// Test class for Actor Code builder. + /// Tests Proxy Generation. /// - public class ActorCodeBuilderTests + [Fact] + public void TestBuildActorProxyGenerator() { - /// - /// Tests Proxy Generation. - /// - [Fact] - public void TestBuildActorProxyGenerator() - { - ActorCodeBuilder.GetOrCreateProxyGenerator(typeof(ITestActor)); - } + ActorCodeBuilder.GetOrCreateProxyGenerator(typeof(ITestActor)); + } - [Fact] - public async Task ActorCodeBuilder_BuildDispatcher() - { - var host = ActorHost.CreateForTest(); + [Fact] + public async Task ActorCodeBuilder_BuildDispatcher() + { + var host = ActorHost.CreateForTest(); - var dispatcher = ActorCodeBuilder.GetOrCreateMethodDispatcher(typeof(ITestActor)); - var methodId = MethodDescription.Create("test", typeof(ITestActor).GetMethod("GetCountAsync"), true).Id; + var dispatcher = ActorCodeBuilder.GetOrCreateMethodDispatcher(typeof(ITestActor)); + var methodId = MethodDescription.Create("test", typeof(ITestActor).GetMethod("GetCountAsync"), true).Id; - var impl = new TestActor(host); - var request = new ActorRequestMessageBody(0); - var response = new WrappedRequestMessageFactory(); + var impl = new TestActor(host); + var request = new ActorRequestMessageBody(0); + var response = new WrappedRequestMessageFactory(); - var body = (WrappedMessage)await dispatcher.DispatchAsync(impl, methodId, request, response, default); - dynamic bodyValue = body.Value; - Assert.Equal(5, (int)bodyValue.retVal); - } + var body = (WrappedMessage)await dispatcher.DispatchAsync(impl, methodId, request, response, default); + dynamic bodyValue = body.Value; + Assert.Equal(5, (int)bodyValue.retVal); } } diff --git a/test/Dapr.Actors.Test/ActorIdTests.cs b/test/Dapr.Actors.Test/ActorIdTests.cs index c54f4c35..dc1f8794 100644 --- a/test/Dapr.Actors.Test/ActorIdTests.cs +++ b/test/Dapr.Actors.Test/ActorIdTests.cs @@ -11,194 +11,193 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Test +namespace Dapr.Actors.Test; + +using System; +using System.Collections.Generic; +using Xunit; + +/// +/// Contains tests for Actor ID. +/// +public class ActorIdTests { - using System; - using System.Collections.Generic; - using Xunit; + public static readonly IEnumerable CompareToValues = new List + { + new object[] + { + new ActorId("1"), + null, + 1, + }, + new object[] + { + new ActorId("1"), + new ActorId("1"), + 0, + }, + new object[] + { + new ActorId("1"), + new ActorId("2"), + -1, + }, + new object[] + { + new ActorId("2"), + new ActorId("1"), + 1, + }, + }; + + public static readonly IEnumerable EqualsValues = new List + { + new object[] + { + new ActorId("1"), + null, + false, + }, + new object[] + { + new ActorId("1"), + new ActorId("1"), + true, + }, + new object[] + { + new ActorId("1"), + new ActorId("2"), + false, + }, + }; + + public static readonly IEnumerable EqualsOperatorValues = new List + { + new object[] + { + null, + null, + true, + }, + new object[] + { + new ActorId("1"), + null, + false, + }, + new object[] + { + null, + new ActorId("1"), + false, + }, + new object[] + { + new ActorId("1"), + new ActorId("1"), + true, + }, + new object[] + { + new ActorId("1"), + new ActorId("2"), + false, + }, + }; /// - /// Contains tests for Actor ID. + /// Throw exception if id is null. /// - public class ActorIdTests + [Theory] + [InlineData(null)] + [InlineData("")] + [InlineData(" ")] + public void Initialize_New_ActorId_Object_With_Null_Or_Whitespace_Id(string id) { - public static readonly IEnumerable CompareToValues = new List - { - new object[] - { - new ActorId("1"), - null, - 1, - }, - new object[] - { - new ActorId("1"), - new ActorId("1"), - 0, - }, - new object[] - { - new ActorId("1"), - new ActorId("2"), - -1, - }, - new object[] - { - new ActorId("2"), - new ActorId("1"), - 1, - }, - }; + Assert.Throws(() => new ActorId(id)); + } - public static readonly IEnumerable EqualsValues = new List - { - new object[] - { - new ActorId("1"), - null, - false, - }, - new object[] - { - new ActorId("1"), - new ActorId("1"), - true, - }, - new object[] - { - new ActorId("1"), - new ActorId("2"), - false, - }, - }; + [Theory] + [InlineData("one")] + [InlineData("123")] + public void Get_Id(string id) + { + ActorId actorId = new ActorId(id); + Assert.Equal(id, actorId.GetId()); + } - public static readonly IEnumerable EqualsOperatorValues = new List - { - new object[] - { - null, - null, - true, - }, - new object[] - { - new ActorId("1"), - null, - false, - }, - new object[] - { - null, - new ActorId("1"), - false, - }, - new object[] - { - new ActorId("1"), - new ActorId("1"), - true, - }, - new object[] - { - new ActorId("1"), - new ActorId("2"), - false, - }, - }; + [Theory] + [InlineData("one")] + [InlineData("123")] + public void Verify_ToString(string id) + { + ActorId actorId = new ActorId(id); + Assert.Equal(id, actorId.ToString()); + } - /// - /// Throw exception if id is null. - /// - [Theory] - [InlineData(null)] - [InlineData("")] - [InlineData(" ")] - public void Initialize_New_ActorId_Object_With_Null_Or_Whitespace_Id(string id) - { - Assert.Throws(() => new ActorId(id)); - } + /// + /// Verify Equals method by comparing two actorIds. + /// + /// The first actorId to compare. + /// The second actorId to compare, or null. + /// Expected value from comparison. + [Theory] + [MemberData(nameof(EqualsValues))] + public void Verify_Equals_By_Object(object id1, object id2, bool expectedValue) + { + Assert.Equal(expectedValue, id1.Equals(id2)); + } - [Theory] - [InlineData("one")] - [InlineData("123")] - public void Get_Id(string id) - { - ActorId actorId = new ActorId(id); - Assert.Equal(id, actorId.GetId()); - } + /// + /// Verify Equals method by comparing two actorIds. + /// + /// The first actorId to compare. + /// The second actorId to compare, or null. + /// Expected value from comparison. + [Theory] + [MemberData(nameof(EqualsValues))] + public void Verify_Equals_By_ActorId(ActorId id1, ActorId id2, bool expectedValue) + { + Assert.Equal(expectedValue, id1.Equals(id2)); + } - [Theory] - [InlineData("one")] - [InlineData("123")] - public void Verify_ToString(string id) - { - ActorId actorId = new ActorId(id); - Assert.Equal(id, actorId.ToString()); - } + /// + /// Verify equals operator by comparing two actorIds. + /// + /// The first actorId to compare, or null. + /// The second actorId to compare, or null. + /// Expected value from comparison. + [Theory] + [MemberData(nameof(EqualsOperatorValues))] + public void Verify_Equals_Operator(ActorId id1, ActorId id2, bool expectedValue) + { + Assert.Equal(expectedValue, id1 == id2); + } - /// - /// Verify Equals method by comparing two actorIds. - /// - /// The first actorId to compare. - /// The second actorId to compare, or null. - /// Expected value from comparison. - [Theory] - [MemberData(nameof(EqualsValues))] - public void Verify_Equals_By_Object(object id1, object id2, bool expectedValue) - { - Assert.Equal(expectedValue, id1.Equals(id2)); - } + /// + /// Verify not equals operator by comparing two actorIds. + /// + /// The first actorId to compare, or null. + /// The second actorId to compare, or null. + /// Expected value from comparison. + [Theory] + [MemberData(nameof(EqualsOperatorValues))] + public void Verify_Not_Equals_Operator(ActorId id1, ActorId id2, bool expectedValue) + { + Assert.Equal(!expectedValue, id1 != id2); + } - /// - /// Verify Equals method by comparing two actorIds. - /// - /// The first actorId to compare. - /// The second actorId to compare, or null. - /// Expected value from comparison. - [Theory] - [MemberData(nameof(EqualsValues))] - public void Verify_Equals_By_ActorId(ActorId id1, ActorId id2, bool expectedValue) - { - Assert.Equal(expectedValue, id1.Equals(id2)); - } - - /// - /// Verify equals operator by comparing two actorIds. - /// - /// The first actorId to compare, or null. - /// The second actorId to compare, or null. - /// Expected value from comparison. - [Theory] - [MemberData(nameof(EqualsOperatorValues))] - public void Verify_Equals_Operator(ActorId id1, ActorId id2, bool expectedValue) - { - Assert.Equal(expectedValue, id1 == id2); - } - - /// - /// Verify not equals operator by comparing two actorIds. - /// - /// The first actorId to compare, or null. - /// The second actorId to compare, or null. - /// Expected value from comparison. - [Theory] - [MemberData(nameof(EqualsOperatorValues))] - public void Verify_Not_Equals_Operator(ActorId id1, ActorId id2, bool expectedValue) - { - Assert.Equal(!expectedValue, id1 != id2); - } - - /// - /// Verify CompareTo method by comparing two actorIds. - /// - /// The first actorId to compare. - /// The second actorId to compare, or null. - /// Expected value from comparison. - [Theory] - [MemberData(nameof(CompareToValues))] - public void Verify_CompareTo(ActorId id1, ActorId id2, int expectedValue) - { - Assert.Equal(expectedValue, id1.CompareTo(id2)); - } + /// + /// Verify CompareTo method by comparing two actorIds. + /// + /// The first actorId to compare. + /// The second actorId to compare, or null. + /// Expected value from comparison. + [Theory] + [MemberData(nameof(CompareToValues))] + public void Verify_CompareTo(ActorId id1, ActorId id2, int expectedValue) + { + Assert.Equal(expectedValue, id1.CompareTo(id2)); } } diff --git a/test/Dapr.Actors.Test/ActorMethodInvocationExceptionTests.cs b/test/Dapr.Actors.Test/ActorMethodInvocationExceptionTests.cs index 1311a939..b34d1ded 100644 --- a/test/Dapr.Actors.Test/ActorMethodInvocationExceptionTests.cs +++ b/test/Dapr.Actors.Test/ActorMethodInvocationExceptionTests.cs @@ -11,51 +11,50 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Test +namespace Dapr.Actors.Test; + +using System; +using System.IO; +using Shouldly; +using Xunit; + +/// +/// Contains tests for Actor method invocation exceptions. +/// +public class ActorMethodInvocationExceptionTests { - using System; - using System.IO; - using FluentAssertions; - using Xunit; - /// - /// Contains tests for Actor method invocation exceptions. + /// This test will verify: + /// 1) the path for serialization and deserialization of the remote exception + /// 2) and validating the inner exception. /// - public class ActorMethodInvocationExceptionTests + [Fact] + public void TestThrowActorMethodInvocationException() { - /// - /// This test will verify: - /// 1) the path for serialization and deserialization of the remote exception - /// 2) and validating the inner exception. - /// - [Fact] - public void TestThrowActorMethodInvocationException() - { - // Create Remote Actor Method test Exception - var message = "Remote Actor encountered an exception"; - var innerMessage = "Bad time zone"; - var exception = new InvalidOperationException(message, new InvalidTimeZoneException(innerMessage)); + // Create Remote Actor Method test Exception + var message = "Remote Actor encountered an exception"; + var innerMessage = "Bad time zone"; + var exception = new InvalidOperationException(message, new InvalidTimeZoneException(innerMessage)); - // Create Serialized Exception - var serializedException = ActorInvokeException.FromException(exception); + // Create Serialized Exception + var serializedException = ActorInvokeException.FromException(exception); - // De Serialize Exception - var isDeserialzied = ActorInvokeException.ToException( - new MemoryStream(serializedException), - out var remoteMethodException); - isDeserialzied.Should().BeTrue(); - var ex = this.ThrowRemoteException(message, remoteMethodException); - ex.Should().BeOfType(); - ex.InnerException.Should().BeOfType(); - ((ActorInvokeException)ex.InnerException).ActualExceptionType.Should().Be("System.InvalidOperationException"); - ex.InnerException.InnerException.Should().BeNull(); - ex.Message.Should().Be(message); - ex.InnerException.Message.Should().Be(message); - } + // De Serialize Exception + var isDeserialzied = ActorInvokeException.ToException( + new MemoryStream(serializedException), + out var remoteMethodException); + isDeserialzied.ShouldBeTrue(); + var ex = this.ThrowRemoteException(message, remoteMethodException); + ex.ShouldBeOfType(); + ex.InnerException.ShouldBeOfType(); + ((ActorInvokeException)ex.InnerException).ActualExceptionType.ShouldBe("System.InvalidOperationException"); + ex.InnerException.InnerException.ShouldBeNull(); + ex.Message.ShouldBe(message); + ex.InnerException.Message.ShouldBe(message); + } - private Exception ThrowRemoteException(string message, Exception exception) - { - return new ActorMethodInvocationException(message, exception, false); - } + private Exception ThrowRemoteException(string message, Exception exception) + { + return new ActorMethodInvocationException(message, exception, false); } } diff --git a/test/Dapr.Actors.Test/ActorProxyOptionsTests.cs b/test/Dapr.Actors.Test/ActorProxyOptionsTests.cs index 9fab1c32..3a156c78 100644 --- a/test/Dapr.Actors.Test/ActorProxyOptionsTests.cs +++ b/test/Dapr.Actors.Test/ActorProxyOptionsTests.cs @@ -11,31 +11,30 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Client +namespace Dapr.Actors.Client; + +using System; +using Shouldly; +using Xunit; + +/// +/// Test class for Actor Code builder. +/// +public class ActorProxyOptionsTests { - using System; - using FluentAssertions; - using Xunit; - - /// - /// Test class for Actor Code builder. - /// - public class ActorProxyOptionsTests + [Fact] + public void DefaultConstructor_Succeeds() { - [Fact] - public void DefaultConstructor_Succeeds() - { - var options = new ActorProxyOptions(); - Assert.NotNull(options); - } + var options = new ActorProxyOptions(); + Assert.NotNull(options); + } - [Fact] - public void SerializerOptionsCantBeNull_Fails() - { - var options = new ActorProxyOptions(); - Action action = () => options.JsonSerializerOptions = null; + [Fact] + public void SerializerOptionsCantBeNull_Fails() + { + var options = new ActorProxyOptions(); + Action action = () => options.JsonSerializerOptions = null; - action.Should().Throw(); - } + action.ShouldThrow(); } } diff --git a/test/Dapr.Actors.Test/ActorProxyTests.cs b/test/Dapr.Actors.Test/ActorProxyTests.cs index 078f4b5f..4290b02c 100644 --- a/test/Dapr.Actors.Test/ActorProxyTests.cs +++ b/test/Dapr.Actors.Test/ActorProxyTests.cs @@ -11,113 +11,112 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Client +namespace Dapr.Actors.Client; + +using System; +using System.Text.Json; +using Dapr.Actors.Test; +using Shouldly; +using Xunit; + +/// +/// Test class for Actor Code builder. +/// +public class ActorProxyTests { - using System; - using System.Text.Json; - using Dapr.Actors.Test; - using FluentAssertions; - using Xunit; - /// - /// Test class for Actor Code builder. + /// Tests Proxy Creation. /// - public class ActorProxyTests + [Fact] + public void Create_WithIdAndActorTypeString_Succeeds() { - /// - /// Tests Proxy Creation. - /// - [Fact] - public void Create_WithIdAndActorTypeString_Succeeds() + var actorId = new ActorId("abc"); + var proxy = ActorProxy.Create(actorId, "TestActor"); + Assert.NotNull(proxy); + } + + [Fact] + public void Create_WithValidActorInterface_Succeeds() + { + var actorId = new ActorId("abc"); + var proxy = ActorProxy.Create(actorId, typeof(ITestActor), "TestActor"); + Assert.NotNull(proxy); + } + + [Fact] + public void Create_WithInvalidType_ThrowsArgumentException() + { + var actorId = new ActorId("abc"); + Action action = () => ActorProxy.Create(actorId, typeof(ActorId), "TestActor"); + action.ShouldThrow(); + } + + [Fact] + public void Create_WithCustomSerializerOnDefaultActorProxyFactory_Succeeds() + { + var factory = new ActorProxyFactory(); + factory.DefaultOptions.JsonSerializerOptions = new JsonSerializerOptions(); + + var actorId = new ActorId("abc"); + var proxy = (ActorProxy)factory.Create(actorId, "TestActor"); + + Assert.Same(factory.DefaultOptions.JsonSerializerOptions, proxy.JsonSerializerOptions); + } + + [Fact] + public void Create_WithCustomSerializerArgument_Succeeds() + { + var options = new ActorProxyOptions() { - var actorId = new ActorId("abc"); - var proxy = ActorProxy.Create(actorId, "TestActor"); - Assert.NotNull(proxy); - } + JsonSerializerOptions = new JsonSerializerOptions() + }; - [Fact] - public void Create_WithValidActorInterface_Succeeds() + var actorId = new ActorId("abc"); + var proxy = (ActorProxy)ActorProxy.Create(actorId, typeof(ITestActor), "TestActor", options); + + Assert.Same(options.JsonSerializerOptions, proxy.JsonSerializerOptions); + } + + [Fact] + public void CreateGeneric_WithValidActorInterface_Succeeds() + { + var actorId = new ActorId("abc"); + var proxy = ActorProxy.Create(actorId, "TestActor"); + Assert.NotNull(proxy); + } + + [Fact] + public void CreateGeneric_WithCustomSerializerOnDefaultActorProxyFactory_Succeeds() + { + var factory = new ActorProxyFactory(); + factory.DefaultOptions.JsonSerializerOptions = new JsonSerializerOptions(); + + var actorId = new ActorId("abc"); + var proxy = (ActorProxy)factory.CreateActorProxy(actorId, "TestActor"); + + Assert.Same(factory.DefaultOptions.JsonSerializerOptions, proxy.JsonSerializerOptions); + } + + [Fact] + public void CreateGeneric_WithCustomSerializerArgument_Succeeds() + { + var options = new ActorProxyOptions() { - var actorId = new ActorId("abc"); - var proxy = ActorProxy.Create(actorId, typeof(ITestActor), "TestActor"); - Assert.NotNull(proxy); - } + JsonSerializerOptions = new JsonSerializerOptions() + }; - [Fact] - public void Create_WithInvalidType_ThrowsArgumentException() - { - var actorId = new ActorId("abc"); - Action action = () => ActorProxy.Create(actorId, typeof(ActorId), "TestActor"); - action.Should().Throw(); - } + var actorId = new ActorId("abc"); + var proxy = (ActorProxy)ActorProxy.Create(actorId, "TestActor", options); - [Fact] - public void Create_WithCustomSerializerOnDefaultActorProxyFactory_Succeeds() - { - var factory = new ActorProxyFactory(); - factory.DefaultOptions.JsonSerializerOptions = new JsonSerializerOptions(); + Assert.Same(options.JsonSerializerOptions, proxy.JsonSerializerOptions); + } - var actorId = new ActorId("abc"); - var proxy = (ActorProxy)factory.Create(actorId, "TestActor"); + [Fact] + public void SetActorProxyFactoryDefaultOptions_ToNull_ThrowsArgumentNullException() + { + var factory = new ActorProxyFactory(); + Action action = () => factory.DefaultOptions = null; - Assert.Same(factory.DefaultOptions.JsonSerializerOptions, proxy.JsonSerializerOptions); - } - - [Fact] - public void Create_WithCustomSerializerArgument_Succeeds() - { - var options = new ActorProxyOptions() - { - JsonSerializerOptions = new JsonSerializerOptions() - }; - - var actorId = new ActorId("abc"); - var proxy = (ActorProxy)ActorProxy.Create(actorId, typeof(ITestActor), "TestActor", options); - - Assert.Same(options.JsonSerializerOptions, proxy.JsonSerializerOptions); - } - - [Fact] - public void CreateGeneric_WithValidActorInterface_Succeeds() - { - var actorId = new ActorId("abc"); - var proxy = ActorProxy.Create(actorId, "TestActor"); - Assert.NotNull(proxy); - } - - [Fact] - public void CreateGeneric_WithCustomSerializerOnDefaultActorProxyFactory_Succeeds() - { - var factory = new ActorProxyFactory(); - factory.DefaultOptions.JsonSerializerOptions = new JsonSerializerOptions(); - - var actorId = new ActorId("abc"); - var proxy = (ActorProxy)factory.CreateActorProxy(actorId, "TestActor"); - - Assert.Same(factory.DefaultOptions.JsonSerializerOptions, proxy.JsonSerializerOptions); - } - - [Fact] - public void CreateGeneric_WithCustomSerializerArgument_Succeeds() - { - var options = new ActorProxyOptions() - { - JsonSerializerOptions = new JsonSerializerOptions() - }; - - var actorId = new ActorId("abc"); - var proxy = (ActorProxy)ActorProxy.Create(actorId, "TestActor", options); - - Assert.Same(options.JsonSerializerOptions, proxy.JsonSerializerOptions); - } - - [Fact] - public void SetActorProxyFactoryDefaultOptions_ToNull_ThrowsArgumentNullException() - { - var factory = new ActorProxyFactory(); - Action action = () => factory.DefaultOptions = null; - - action.Should().Throw(); - } + action.ShouldThrow(); } } diff --git a/test/Dapr.Actors.Test/ActorReferenceTests.cs b/test/Dapr.Actors.Test/ActorReferenceTests.cs index 7450f616..b244ad8e 100644 --- a/test/Dapr.Actors.Test/ActorReferenceTests.cs +++ b/test/Dapr.Actors.Test/ActorReferenceTests.cs @@ -1,93 +1,105 @@ -using System; +// ------------------------------------------------------------------------ +// 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. +// ------------------------------------------------------------------------ + +using System; using System.Threading.Tasks; using Dapr.Actors.Client; using Dapr.Actors.Runtime; using Dapr.Actors.Test; using Xunit; -namespace Dapr.Actors +namespace Dapr.Actors; + +public class ActorReferenceTests { - public class ActorReferenceTests + [Fact] + public void Get_WhenActorIsNull_ReturnsNull() { - [Fact] - public void Get_WhenActorIsNull_ReturnsNull() - { - // Arrange - object actor = null; + // Arrange + object actor = null; - // Act - var result = ActorReference.Get(actor); + // Act + var result = ActorReference.Get(actor); - // Assert - Assert.Null(result); - } - - [Fact] - public void Get_FromActorProxy_ReturnsActorReference() - { - // Arrange - var expectedActorId = new ActorId("abc"); - var expectedActorType = "TestActor"; - var proxy = ActorProxy.Create(expectedActorId, typeof(ITestActor), expectedActorType); - - // Act - var actorReference = ActorReference.Get(proxy); - - // Assert - Assert.NotNull(actorReference); - Assert.Equal(expectedActorId, actorReference.ActorId); - Assert.Equal(expectedActorType, actorReference.ActorType); - } - - [Fact] - public async Task Get_FromActorImplementation_ReturnsActorReference() - { - // Arrange - var expectedActorId = new ActorId("abc"); - var expectedActorType = nameof(ActorReferenceTestActor); - var host = ActorHost.CreateForTest(new ActorTestOptions() { ActorId = expectedActorId }); - var actor = new ActorReferenceTestActor(host); - - // Act - var actorReference = await actor.GetActorReference(); - - // Assert - Assert.NotNull(actorReference); - Assert.Equal(expectedActorId, actorReference.ActorId); - Assert.Equal(expectedActorType, actorReference.ActorType); - } - - [Fact] - public void Get_WithInvalidObjectType_ThrowArgumentOutOfRangeException() - { - // Arrange - var actor = new object(); - - // Act - var act = () => ActorReference.Get(actor); - - // Assert - var exception = Assert.Throws(act); - Assert.Equal("actor", exception.ParamName); - Assert.Equal("Invalid actor object type. (Parameter 'actor')", exception.Message); - } + // Assert + Assert.Null(result); } - public interface IActorReferenceTestActor : IActor + [Fact] + public void Get_FromActorProxy_ReturnsActorReference() { - Task GetActorReference(); + // Arrange + var expectedActorId = new ActorId("abc"); + var expectedActorType = "TestActor"; + var proxy = ActorProxy.Create(expectedActorId, typeof(ITestActor), expectedActorType); + + // Act + var actorReference = ActorReference.Get(proxy); + + // Assert + Assert.NotNull(actorReference); + Assert.Equal(expectedActorId, actorReference.ActorId); + Assert.Equal(expectedActorType, actorReference.ActorType); } - public class ActorReferenceTestActor : Actor, IActorReferenceTestActor + [Fact] + public async Task Get_FromActorImplementation_ReturnsActorReference() { - public ActorReferenceTestActor(ActorHost host) - : base(host) - { - } + // Arrange + var expectedActorId = new ActorId("abc"); + var expectedActorType = nameof(ActorReferenceTestActor); + var host = ActorHost.CreateForTest(new ActorTestOptions() { ActorId = expectedActorId }); + var actor = new ActorReferenceTestActor(host); - public Task GetActorReference() - { - return Task.FromResult(ActorReference.Get(this)); - } + // Act + var actorReference = await actor.GetActorReference(); + + // Assert + Assert.NotNull(actorReference); + Assert.Equal(expectedActorId, actorReference.ActorId); + Assert.Equal(expectedActorType, actorReference.ActorType); + } + + [Fact] + public void Get_WithInvalidObjectType_ThrowArgumentOutOfRangeException() + { + // Arrange + var actor = new object(); + + // Act + var act = () => ActorReference.Get(actor); + + // Assert + var exception = Assert.Throws(act); + Assert.Equal("actor", exception.ParamName); + Assert.Equal("Invalid actor object type. (Parameter 'actor')", exception.Message); + } +} + +public interface IActorReferenceTestActor : IActor +{ + Task GetActorReference(); +} + +public class ActorReferenceTestActor : Actor, IActorReferenceTestActor +{ + public ActorReferenceTestActor(ActorHost host) + : base(host) + { + } + + public Task GetActorReference() + { + return Task.FromResult(ActorReference.Get(this)); } } diff --git a/test/Dapr.Actors.Test/ActorUnitTestTests.cs b/test/Dapr.Actors.Test/ActorUnitTestTests.cs index 891011de..79476199 100644 --- a/test/Dapr.Actors.Test/ActorUnitTestTests.cs +++ b/test/Dapr.Actors.Test/ActorUnitTestTests.cs @@ -69,7 +69,7 @@ namespace Dapr.Actors } [Fact] - public async Task CanTestStartingAndStoppinReminder() + public async Task CanTestStartingAndStoppingReminder() { var reminders = new List(); IActorReminder getReminder = null; @@ -120,6 +120,22 @@ namespace Dapr.Actors Assert.Empty(reminders); } + [Fact] + public async Task ReminderReturnsNullIfNotAvailable() + { + var timerManager = new Mock(MockBehavior.Strict); + timerManager + .Setup(tm => tm.GetReminderAsync(It.IsAny())) + .Returns(() => Task.FromResult(null)); + + var host = ActorHost.CreateForTest(new ActorTestOptions() { TimerManager = timerManager.Object, }); + var actor = new CoolTestActor(host); + + //There is no starting reminder, so this should always return null + var retrievedReminder = await actor.GetReminderAsync(); + Assert.Null(retrievedReminder); + } + public interface ICoolTestActor : IActor { } diff --git a/test/Dapr.Actors.Test/ApiTokenTests.cs b/test/Dapr.Actors.Test/ApiTokenTests.cs index 29ee955c..6e5860be 100644 --- a/test/Dapr.Actors.Test/ApiTokenTests.cs +++ b/test/Dapr.Actors.Test/ApiTokenTests.cs @@ -15,7 +15,7 @@ using System; using System.Threading; using System.Threading.Tasks; using Dapr.Actors.Client; -using FluentAssertions; +using Shouldly; using Xunit; namespace Dapr.Actors.Test @@ -43,7 +43,7 @@ namespace Dapr.Actors.Test request.Dismiss(); var headerValues = request.Request.Headers.GetValues("dapr-api-token"); - headerValues.Should().Contain("test_token"); + headerValues.ShouldContain("test_token"); } [Fact(Skip = "https://github.com/dapr/dotnet-sdk/issues/596")] @@ -89,7 +89,7 @@ namespace Dapr.Actors.Test request.Dismiss(); var headerValues = request.Request.Headers.GetValues("dapr-api-token"); - headerValues.Should().Contain("test_token"); + headerValues.ShouldContain("test_token"); } [Fact] diff --git a/test/Dapr.Actors.Test/ConverterUtilsTests.cs b/test/Dapr.Actors.Test/ConverterUtilsTests.cs new file mode 100644 index 00000000..55987939 --- /dev/null +++ b/test/Dapr.Actors.Test/ConverterUtilsTests.cs @@ -0,0 +1,35 @@ +// ------------------------------------------------------------------------ +// Copyright 2025 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. +// ------------------------------------------------------------------------ + +using System; +using Dapr.Actors.Runtime; +using Xunit; + +namespace Dapr.Actors; + +public class ConverterUtilsTests +{ + [Fact] + public void Deserialize_Period_Duration1() + { + var result = ConverterUtils.ConvertTimeSpanValueFromISO8601Format("@every 15m"); + Assert.Equal(TimeSpan.FromMinutes(15), result.Item1); + } + + [Fact] + public void Deserialize_Period_Duration2() + { + var result = ConverterUtils.ConvertTimeSpanValueFromISO8601Format("@hourly"); + Assert.Equal(TimeSpan.FromHours(1), result.Item1); + } +} diff --git a/test/Dapr.Actors.Test/Dapr.Actors.Test.csproj b/test/Dapr.Actors.Test/Dapr.Actors.Test.csproj index 9ef26cd1..f28cbaef 100644 --- a/test/Dapr.Actors.Test/Dapr.Actors.Test.csproj +++ b/test/Dapr.Actors.Test/Dapr.Actors.Test.csproj @@ -9,10 +9,10 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all - + all diff --git a/test/Dapr.Actors.Test/DaprHttpInteractorTest.cs b/test/Dapr.Actors.Test/DaprHttpInteractorTest.cs index 9c42b8fc..30ddb91d 100644 --- a/test/Dapr.Actors.Test/DaprHttpInteractorTest.cs +++ b/test/Dapr.Actors.Test/DaprHttpInteractorTest.cs @@ -21,7 +21,7 @@ namespace Dapr.Actors.Test using System.Security.Authentication; using System.Text.Json; using System.Threading.Tasks; - using FluentAssertions; + using Shouldly; using Xunit; /// @@ -34,9 +34,9 @@ namespace Dapr.Actors.Test { await using var client = TestClient.CreateForDaprHttpInterator(); - var actorType = "ActorType_Test"; - var actorId = "ActorId_Test"; - var keyName = "StateKey_Test"; + const string actorType = "ActorType_Test"; + const string actorId = "ActorId_Test"; + const string keyName = "StateKey_Test"; var request = await client.CaptureHttpRequestAsync(async httpInteractor => { @@ -48,8 +48,8 @@ namespace Dapr.Actors.Test var actualPath = request.Request.RequestUri.LocalPath.TrimStart('/'); var expectedPath = string.Format(CultureInfo.InvariantCulture, Constants.ActorStateKeyRelativeUrlFormat, actorType, actorId, keyName); - actualPath.Should().Be(expectedPath); - request.Request.Method.Should().Be(HttpMethod.Get); + actualPath.ShouldBe(expectedPath); + request.Request.Method.ShouldBe(HttpMethod.Get); } [Fact] @@ -57,9 +57,9 @@ namespace Dapr.Actors.Test { await using var client = TestClient.CreateForDaprHttpInterator(); - var actorType = "ActorType_Test"; - var actorId = "ActorId_Test"; - var data = "StateData"; + const string actorType = "ActorType_Test"; + const string actorId = "ActorId_Test"; + const string data = "StateData"; var request = await client.CaptureHttpRequestAsync(async httpInteractor => { @@ -71,8 +71,8 @@ namespace Dapr.Actors.Test var actualPath = request.Request.RequestUri.LocalPath.TrimStart('/'); var expectedPath = string.Format(CultureInfo.InvariantCulture, Constants.ActorStateRelativeUrlFormat, actorType, actorId); - actualPath.Should().Be(expectedPath); - request.Request.Method.Should().Be(HttpMethod.Put); + actualPath.ShouldBe(expectedPath); + request.Request.Method.ShouldBe(HttpMethod.Put); } [Fact] @@ -80,10 +80,10 @@ namespace Dapr.Actors.Test { await using var client = TestClient.CreateForDaprHttpInterator(); - var actorType = "ActorType_Test"; - var actorId = "ActorId_Test"; - var methodName = "MethodName"; - var payload = "JsonData"; + const string actorType = "ActorType_Test"; + const string actorId = "ActorId_Test"; + const string methodName = "MethodName"; + const string payload = "JsonData"; var request = await client.CaptureHttpRequestAsync(async httpInteractor => { @@ -95,8 +95,8 @@ namespace Dapr.Actors.Test var actualPath = request.Request.RequestUri.LocalPath.TrimStart('/'); var expectedPath = string.Format(CultureInfo.InvariantCulture, Constants.ActorMethodRelativeUrlFormat, actorType, actorId, methodName); - actualPath.Should().Be(expectedPath); - request.Request.Method.Should().Be(HttpMethod.Put); + actualPath.ShouldBe(expectedPath); + request.Request.Method.ShouldBe(HttpMethod.Put); } [Fact] @@ -104,10 +104,10 @@ namespace Dapr.Actors.Test { await using var client = TestClient.CreateForDaprHttpInterator(); - var actorType = "ActorType_Test"; - var actorId = "ActorId_Test"; - var reminderName = "ReminderName"; - var payload = "JsonData"; + const string actorType = "ActorType_Test"; + const string actorId = "ActorId_Test"; + const string reminderName = "ReminderName"; + const string payload = "JsonData"; var request = await client.CaptureHttpRequestAsync(async httpInteractor => { @@ -119,8 +119,8 @@ namespace Dapr.Actors.Test var actualPath = request.Request.RequestUri.LocalPath.TrimStart('/'); var expectedPath = string.Format(CultureInfo.InvariantCulture, Constants.ActorReminderRelativeUrlFormat, actorType, actorId, reminderName); - actualPath.Should().Be(expectedPath); - request.Request.Method.Should().Be(HttpMethod.Put); + actualPath.ShouldBe(expectedPath); + request.Request.Method.ShouldBe(HttpMethod.Put); } [Fact] @@ -128,9 +128,9 @@ namespace Dapr.Actors.Test { await using var client = TestClient.CreateForDaprHttpInterator(); - var actorType = "ActorType_Test"; - var actorId = "ActorId_Test"; - var reminderName = "ReminderName"; + const string actorType = "ActorType_Test"; + const string actorId = "ActorId_Test"; + const string reminderName = "ReminderName"; var request = await client.CaptureHttpRequestAsync(async httpInteractor => { @@ -142,8 +142,32 @@ namespace Dapr.Actors.Test var actualPath = request.Request.RequestUri.LocalPath.TrimStart('/'); var expectedPath = string.Format(CultureInfo.InvariantCulture, Constants.ActorReminderRelativeUrlFormat, actorType, actorId, reminderName); - actualPath.Should().Be(expectedPath); - request.Request.Method.Should().Be(HttpMethod.Delete); + actualPath.ShouldBe(expectedPath); + request.Request.Method.ShouldBe(HttpMethod.Delete); + } + + [Fact] + public async Task GetReminder_ValidateRequest() + { + await using var client = TestClient.CreateForDaprHttpInterator(); + + const string actorType = "ActorType_Test"; + const string actorId = "ActorId_Test"; + const string reminderName = "ReminderName"; + + var request = await client.CaptureHttpRequestAsync(async httpInteractor => + { + await httpInteractor.GetReminderAsync(actorType, actorId, reminderName); + }); + + request.Dismiss(); + + var actualPath = request.Request.RequestUri.LocalPath.TrimStart('/'); + var expectedPath = string.Format(CultureInfo.InvariantCulture, Constants.ActorReminderRelativeUrlFormat, + actorType, actorId, reminderName); + + actualPath.ShouldBe(expectedPath); + request.Request.Method.ShouldBe(HttpMethod.Get); } [Fact] @@ -151,10 +175,10 @@ namespace Dapr.Actors.Test { await using var client = TestClient.CreateForDaprHttpInterator(); - var actorType = "ActorType_Test"; - var actorId = "ActorId_Test"; - var timerName = "TimerName"; - var payload = "JsonData"; + const string actorType = "ActorType_Test"; + const string actorId = "ActorId_Test"; + const string timerName = "TimerName"; + const string payload = "JsonData"; var request = await client.CaptureHttpRequestAsync(async httpInteractor => { @@ -166,8 +190,8 @@ namespace Dapr.Actors.Test var actualPath = request.Request.RequestUri.LocalPath.TrimStart('/'); var expectedPath = string.Format(CultureInfo.InvariantCulture, Constants.ActorTimerRelativeUrlFormat, actorType, actorId, timerName); - actualPath.Should().Be(expectedPath); - request.Request.Method.Should().Be(HttpMethod.Put); + actualPath.ShouldBe(expectedPath); + request.Request.Method.ShouldBe(HttpMethod.Put); } [Fact] @@ -189,8 +213,8 @@ namespace Dapr.Actors.Test var actualPath = request.Request.RequestUri.LocalPath.TrimStart('/'); var expectedPath = string.Format(CultureInfo.InvariantCulture, Constants.ActorTimerRelativeUrlFormat, actorType, actorId, timerName); - actualPath.Should().Be(expectedPath); - request.Request.Method.Should().Be(HttpMethod.Delete); + actualPath.ShouldBe(expectedPath); + request.Request.Method.ShouldBe(HttpMethod.Delete); } [Fact] @@ -198,9 +222,9 @@ namespace Dapr.Actors.Test { await using var client = TestClient.CreateForDaprHttpInterator(apiToken: "test_token"); - var actorType = "ActorType_Test"; - var actorId = "ActorId_Test"; - var timerName = "TimerName"; + const string actorType = "ActorType_Test"; + const string actorId = "ActorId_Test"; + const string timerName = "TimerName"; var request = await client.CaptureHttpRequestAsync(async httpInteractor => { @@ -210,8 +234,8 @@ namespace Dapr.Actors.Test request.Dismiss(); request.Request.Headers.TryGetValues("dapr-api-token", out var headerValues); - headerValues.Count().Should().Be(1); - headerValues.First().Should().Be("test_token"); + headerValues.Count().ShouldBe(1); + headerValues.First().ShouldBe("test_token"); } [Fact] @@ -219,9 +243,9 @@ namespace Dapr.Actors.Test { await using var client = TestClient.CreateForDaprHttpInterator(); - var actorType = "ActorType_Test"; - var actorId = "ActorId_Test"; - var timerName = "TimerName"; + const string actorType = "ActorType_Test"; + const string actorId = "ActorId_Test"; + const string timerName = "TimerName"; var request = await client.CaptureHttpRequestAsync(async httpInteractor => { @@ -231,7 +255,7 @@ namespace Dapr.Actors.Test request.Dismiss(); request.Request.Headers.TryGetValues("dapr-api-token", out var headerValues); - headerValues.Should().BeNull(); + headerValues.ShouldBeNull(); } [Fact] @@ -239,9 +263,9 @@ namespace Dapr.Actors.Test { await using var client = TestClient.CreateForDaprHttpInterator(); - var actorType = "ActorType_Test"; - var actorId = "ActorId_Test"; - var timerName = "TimerName"; + const string actorType = "ActorType_Test"; + const string actorId = "ActorId_Test"; + const string timerName = "TimerName"; var request = await client.CaptureHttpRequestAsync(async httpInteractor => { @@ -272,9 +296,9 @@ namespace Dapr.Actors.Test { await using var client = TestClient.CreateForDaprHttpInterator(); - var actorType = "ActorType_Test"; - var actorId = "ActorId_Test"; - var timerName = "TimerName"; + const string actorType = "ActorType_Test"; + const string actorId = "ActorId_Test"; + const string timerName = "TimerName"; var request = await client.CaptureHttpRequestAsync(async httpInteractor => { @@ -294,9 +318,9 @@ namespace Dapr.Actors.Test { await using var client = TestClient.CreateForDaprHttpInterator(); - var actorType = "ActorType_Test"; - var actorId = "ActorId_Test"; - var timerName = "TimerName"; + const string actorType = "ActorType_Test"; + const string actorId = "ActorId_Test"; + const string timerName = "TimerName"; var request = await client.CaptureHttpRequestAsync(async httpInteractor => { @@ -316,10 +340,10 @@ namespace Dapr.Actors.Test { await using var client = TestClient.CreateForDaprHttpInterator(); - var actorType = "ActorType_Test"; - var actorId = "ActorId_Test"; - var methodName = "MethodName"; - var payload = "JsonData"; + const string actorType = "ActorType_Test"; + const string actorId = "ActorId_Test"; + const string methodName = "MethodName"; + const string payload = "JsonData"; ActorReentrancyContextAccessor.ReentrancyContext = "1"; var request = await client.CaptureHttpRequestAsync(async httpInteractor => @@ -337,10 +361,10 @@ namespace Dapr.Actors.Test { await using var client = TestClient.CreateForDaprHttpInterator(); - var actorType = "ActorType_Test"; - var actorId = "ActorId_Test"; - var methodName = "MethodName"; - var payload = "JsonData"; + const string actorType = "ActorType_Test"; + const string actorId = "ActorId_Test"; + const string methodName = "MethodName"; + const string payload = "JsonData"; var request = await client.CaptureHttpRequestAsync(async httpInteractor => { @@ -356,9 +380,9 @@ namespace Dapr.Actors.Test { await using var client = TestClient.CreateForDaprHttpInterator(); - var actorType = "ActorType_Test"; - var actorId = "ActorId_Test"; - var keyName = "StateKey_Test"; + const string actorType = "ActorType_Test"; + const string actorId = "ActorId_Test"; + const string keyName = "StateKey_Test"; var request = await client.CaptureHttpRequestAsync(async httpInteractor => { @@ -385,9 +409,9 @@ namespace Dapr.Actors.Test { await using var client = TestClient.CreateForDaprHttpInterator(); - var actorType = "ActorType_Test"; - var actorId = "ActorId_Test"; - var keyName = "StateKey_Test"; + const string actorType = "ActorType_Test"; + const string actorId = "ActorId_Test"; + const string keyName = "StateKey_Test"; var request = await client.CaptureHttpRequestAsync(async httpInteractor => { diff --git a/test/Dapr.Actors.Test/Description/ActorInterfaceDescriptionTests.cs b/test/Dapr.Actors.Test/Description/ActorInterfaceDescriptionTests.cs index 23579188..3193a795 100644 --- a/test/Dapr.Actors.Test/Description/ActorInterfaceDescriptionTests.cs +++ b/test/Dapr.Actors.Test/Description/ActorInterfaceDescriptionTests.cs @@ -1,20 +1,19 @@ -// ------------------------------------------------------------------------ -// 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. -// ------------------------------------------------------------------------ - -using System; +// ------------------------------------------------------------------------ +// 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. +// ------------------------------------------------------------------------ + +using System; using System.Threading.Tasks; -using FluentAssertions; -using FluentAssertions.Execution; +using Shouldly; using Xunit; namespace Dapr.Actors.Description @@ -31,13 +30,12 @@ namespace Dapr.Actors.Description var description = ActorInterfaceDescription.Create(type); // Assert - description.Should().NotBeNull(); + description.ShouldNotBeNull(); - using var _ = new AssertionScope(); - description.InterfaceType.Should().Be(type); - description.Id.Should().NotBe(0); - description.V1Id.Should().Be(0); - description.Methods.Should().HaveCount(2); + description.InterfaceType.ShouldBe(type); + description.Id.ShouldNotBe(0); + description.V1Id.ShouldBe(0); + description.Methods.Length.ShouldBe(2); } [Fact] @@ -50,10 +48,9 @@ namespace Dapr.Actors.Description Action action = () => ActorInterfaceDescription.Create(type); // Assert - action.Should().ThrowExactly() - .WithMessage("The type 'System.Object' is not an Actor interface as it is not an interface.*") - .And.ParamName.Should().Be("actorInterfaceType"); - + var exception = Should.Throw(action); + exception.Message.ShouldMatch(@"The type 'System.Object' is not an Actor interface as it is not an interface.*"); + exception.ParamName.ShouldBe("actorInterfaceType"); } [Fact] @@ -66,9 +63,9 @@ namespace Dapr.Actors.Description Action action = () => ActorInterfaceDescription.Create(type); // Assert - action.Should().ThrowExactly() - .WithMessage("The type 'System.ICloneable' is not an actor interface as it does not derive from the interface 'Dapr.Actors.IActor'.*") - .And.ParamName.Should().Be("actorInterfaceType"); + var exception = Should.Throw(action); + exception.Message.ShouldMatch(@"The type 'System.ICloneable' is not an actor interface as it does not derive from the interface 'Dapr.Actors.IActor'.*"); + exception.ParamName.ShouldBe("actorInterfaceType"); } [Fact] @@ -81,9 +78,9 @@ namespace Dapr.Actors.Description Action action = () => ActorInterfaceDescription.Create(type); // Assert - action.Should().ThrowExactly() - .WithMessage("The type '*+IClonableActor' is not an actor interface as it derive from a non actor interface 'System.ICloneable'. All actor interfaces must derive from 'Dapr.Actors.IActor'.*") - .And.ParamName.Should().Be("actorInterfaceType"); + var exception = Should.Throw(action); + exception.Message.ShouldMatch(@"The type '.*\+IClonableActor' is not an actor interface as it derive from a non actor interface 'System.ICloneable'. All actor interfaces must derive from 'Dapr.Actors.IActor'.*"); + exception.ParamName.ShouldBe("actorInterfaceType"); } [Fact] @@ -96,13 +93,12 @@ namespace Dapr.Actors.Description var description = ActorInterfaceDescription.CreateUsingCRCId(type); // Assert - description.Should().NotBeNull(); + description.ShouldNotBeNull(); - using var _ = new AssertionScope(); - description.InterfaceType.Should().Be(type); - description.Id.Should().Be(-934188464); - description.V1Id.Should().NotBe(0); - description.Methods.Should().HaveCount(2); + description.InterfaceType.ShouldBe(type); + description.Id.ShouldBe(-934188464); + description.V1Id.ShouldNotBe(0); + description.Methods.Length.ShouldBe(2); } [Fact] @@ -115,9 +111,9 @@ namespace Dapr.Actors.Description Action action = () => ActorInterfaceDescription.CreateUsingCRCId(type); // Assert - action.Should().ThrowExactly() - .WithMessage("The type 'System.Object' is not an Actor interface as it is not an interface.*") - .And.ParamName.Should().Be("actorInterfaceType"); + var exception = Should.Throw(action); + exception.Message.ShouldMatch(@"The type 'System.Object' is not an Actor interface as it is not an interface.*"); + exception.ParamName.ShouldBe("actorInterfaceType"); } [Fact] @@ -130,9 +126,9 @@ namespace Dapr.Actors.Description Action action = () => ActorInterfaceDescription.CreateUsingCRCId(type); // Assert - action.Should().ThrowExactly() - .WithMessage("The type 'System.ICloneable' is not an actor interface as it does not derive from the interface 'Dapr.Actors.IActor'.*") - .And.ParamName.Should().Be("actorInterfaceType"); + var exception = Should.Throw(action); + exception.Message.ShouldMatch(@"The type 'System.ICloneable' is not an actor interface as it does not derive from the interface 'Dapr.Actors.IActor'.*"); + exception.ParamName.ShouldBe("actorInterfaceType"); } [Fact] @@ -145,9 +141,9 @@ namespace Dapr.Actors.Description Action action = () => ActorInterfaceDescription.CreateUsingCRCId(type); // Assert - action.Should().ThrowExactly() - .WithMessage("The type '*+IClonableActor' is not an actor interface as it derive from a non actor interface 'System.ICloneable'. All actor interfaces must derive from 'Dapr.Actors.IActor'.*") - .And.ParamName.Should().Be("actorInterfaceType"); + var exception = Should.Throw(action); + exception.Message.ShouldMatch(@"The type '.*\+IClonableActor' is not an actor interface as it derive from a non actor interface 'System.ICloneable'. All actor interfaces must derive from 'Dapr.Actors.IActor'.*"); + exception.ParamName.ShouldBe("actorInterfaceType"); } internal interface IClonableActor : ICloneable, IActor diff --git a/test/Dapr.Actors.Test/Description/InterfaceDescriptionTests.cs b/test/Dapr.Actors.Test/Description/InterfaceDescriptionTests.cs index dbd44d55..83f4de01 100644 --- a/test/Dapr.Actors.Test/Description/InterfaceDescriptionTests.cs +++ b/test/Dapr.Actors.Test/Description/InterfaceDescriptionTests.cs @@ -1,20 +1,20 @@ -// ------------------------------------------------------------------------ -// 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. -// ------------------------------------------------------------------------ - -using System; +// ------------------------------------------------------------------------ +// 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. +// ------------------------------------------------------------------------ + +using System; +using System.Linq; using System.Threading.Tasks; -using FluentAssertions; -using FluentAssertions.Execution; +using Shouldly; using Xunit; namespace Dapr.Actors.Description @@ -31,13 +31,12 @@ namespace Dapr.Actors.Description TestDescription description = new(type); // Assert - description.Should().NotBeNull(); + description.ShouldNotBeNull(); - using var _ = new AssertionScope(); - description.InterfaceType.Should().Be(type); - description.Id.Should().NotBe(0); - description.V1Id.Should().Be(0); - description.Methods.Should().BeEmpty(); + description.InterfaceType.ShouldBe(type); + description.Id.ShouldNotBe(0); + description.V1Id.ShouldBe(0); + description.Methods.ShouldBeEmpty(); } [Fact] @@ -50,9 +49,8 @@ namespace Dapr.Actors.Description TestDescription description = new(type, useCRCIdGeneration: true); // Assert - using var _ = new AssertionScope(); - description.Id.Should().Be(-934188464); - description.V1Id.Should().NotBe(0); + description.Id.ShouldBe(-934188464); + description.V1Id.ShouldNotBe(0); } [Fact] @@ -65,9 +63,9 @@ namespace Dapr.Actors.Description Action action = () => { TestDescription _ = new(type); }; // Assert - action.Should().ThrowExactly() - .WithMessage("The actor interface '*+IGenericActor`1' is using generics. Generic interfaces cannot be remoted.*") - .And.ParamName.Should().Be("actorInterfaceType"); + var exception = Should.Throw(action); + exception.Message.ShouldMatch(@"The actor interface '.*\+IGenericActor`1' is using generics. Generic interfaces cannot be remoted.*"); + exception.ParamName.ShouldBe("actorInterfaceType"); } [Fact] @@ -80,9 +78,9 @@ namespace Dapr.Actors.Description Action action = () => { TestDescription _ = new(type); }; // Assert - action.Should().ThrowExactly() - .WithMessage("The actor interface '*+IGenericActor`1[*IActor*]' is using generics. Generic interfaces cannot be remoted.*") - .And.ParamName.Should().Be("actorInterfaceType"); + var exception = Should.Throw(action); + exception.Message.ShouldMatch(@"The actor interface '.*\+IGenericActor`1\[.*IActor.*\]' is using generics. Generic interfaces cannot be remoted.*"); + exception.ParamName.ShouldBe("actorInterfaceType"); } [Fact] @@ -95,12 +93,9 @@ namespace Dapr.Actors.Description TestDescription description = new(type); // Assert - using var _ = new AssertionScope(); - description.Methods.Should().NotContainNulls(); - description.Methods.Should().AllBeOfType(); - description.Methods.Should().BeEquivalentTo( - new { Name = "GetInt" } - ); + description.Methods.ShouldNotBeNull(); + description.Methods.ShouldBeOfType(); + description.Methods.Select(m => new {m.Name}).ShouldBe(new[] {new {Name = "GetInt"}}); } [Fact] @@ -113,12 +108,9 @@ namespace Dapr.Actors.Description TestDescription description = new(type, methodReturnCheck: MethodReturnCheck.EnsureReturnsVoid); // Assert - using var _ = new AssertionScope(); - description.Methods.Should().NotContainNulls(); - description.Methods.Should().AllBeOfType(); - description.Methods.Should().BeEquivalentTo( - new { Name = "GetString" }, - new { Name = "MethodWithArguments" }); + description.Methods.ShouldNotBeNull(); + description.Methods.ShouldBeOfType(); + description.Methods.Select(m => new {m.Name}).ShouldBe(new[] {new {Name = "GetString"}, new {Name="MethodWithArguments"}}); } [Fact] @@ -128,12 +120,15 @@ namespace Dapr.Actors.Description Type type = typeof(IVoidActor); // Act - Action action = () => { TestDescription _ = new(type); }; + Action action = () => + { + TestDescription _ = new(type); + }; // Assert - action.Should().ThrowExactly() - .WithMessage("Method 'GetString' of actor interface '*+IVoidActor' does not return Task or Task<>. The actor interface methods must be async and must return either Task or Task<>.*") - .And.ParamName.Should().Be("actorInterfaceType"); + var exception = Should.Throw(action); + exception.Message.ShouldMatch(@"Method 'GetString' of actor interface '.*\+IVoidActor' does not return Task or Task<>. The actor interface methods must be async and must return either Task or Task<>.*"); + exception.ParamName.ShouldBe("actorInterfaceType"); } [Fact] @@ -146,9 +141,9 @@ namespace Dapr.Actors.Description Action action = () => { TestDescription _ = new(type, methodReturnCheck: MethodReturnCheck.EnsureReturnsVoid); }; // Assert - action.Should().ThrowExactly() - .WithMessage("Method 'GetString' of actor interface '*+IMethodActor' returns '*.Task`1[*System.String*]'*") - .And.ParamName.Should().Be("actorInterfaceType"); + var exception = Should.Throw(action); + exception.Message.ShouldMatch(@"Method 'GetString' of actor interface '.*\+IMethodActor' returns '.*\.Task`1\[.*System.String.*\]'.*"); + exception.ParamName.ShouldBe("actorInterfaceType"); } [Fact] @@ -161,9 +156,9 @@ namespace Dapr.Actors.Description Action action = () => { TestDescription _ = new(type); }; // Assert - action.Should().ThrowExactly() - .WithMessage("Method 'GetString' of actor interface '*+IOverloadedMethodActor' is overloaded. The actor interface methods cannot be overloaded.*") - .And.ParamName.Should().Be("actorInterfaceType"); + var exception = Should.Throw(action); + exception.Message.ShouldMatch(@"Method 'GetString' of actor interface '.*\+IOverloadedMethodActor' is overloaded. The actor interface methods cannot be overloaded.*"); + exception.ParamName.ShouldBe("actorInterfaceType"); } [Fact] @@ -176,9 +171,9 @@ namespace Dapr.Actors.Description Action action = () => { TestDescription _ = new(type); }; // Assert - action.Should().ThrowExactly() - .WithMessage("Method 'Get' of actor interface '*+IGenericMethodActor' is using generics. The actor interface methods cannot use generics.*") - .And.ParamName.Should().Be("actorInterfaceType"); + var exception = Should.Throw(action); + exception.Message.ShouldMatch(@"Method 'Get' of actor interface '.*\+IGenericMethodActor' is using generics. The actor interface methods cannot use generics.*"); + exception.ParamName.ShouldBe("actorInterfaceType"); } [Fact] @@ -191,9 +186,9 @@ namespace Dapr.Actors.Description Action action = () => { TestDescription _ = new(type); }; // Assert - action.Should().ThrowExactly() - .WithMessage("Method 'MethodWithVarArgs' of actor interface '*+IVariableActor' is using a variable argument list. The actor interface methods cannot have a variable argument list.*") - .And.ParamName.Should().Be("actorInterfaceType"); + var exception = Should.Throw(action); + exception.Message.ShouldMatch(@"Method 'MethodWithVarArgs' of actor interface '.*\+IVariableActor' is using a variable argument list. The actor interface methods cannot have a variable argument list.*"); + exception.ParamName.ShouldBe("actorInterfaceType"); } internal interface ITestActor : IActor diff --git a/test/Dapr.Actors.Test/Description/MethodArgumentDescriptionTests.cs b/test/Dapr.Actors.Test/Description/MethodArgumentDescriptionTests.cs index 3a6f211c..0841298a 100644 --- a/test/Dapr.Actors.Test/Description/MethodArgumentDescriptionTests.cs +++ b/test/Dapr.Actors.Test/Description/MethodArgumentDescriptionTests.cs @@ -1,21 +1,20 @@ -// ------------------------------------------------------------------------ -// 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. -// ------------------------------------------------------------------------ - -using System; +// ------------------------------------------------------------------------ +// 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. +// ------------------------------------------------------------------------ + +using System; using System.Reflection; using System.Threading.Tasks; -using FluentAssertions; -using FluentAssertions.Execution; +using Shouldly; using Xunit; namespace Dapr.Actors.Description @@ -33,11 +32,10 @@ namespace Dapr.Actors.Description var description = MethodArgumentDescription.Create("actor", methodInfo, parameterInfo); // Assert - description.Should().NotBeNull(); + description.ShouldNotBeNull(); - using var _ = new AssertionScope(); - description.Name.Should().Be("number"); - description.ArgumentType.Should().Be(); + description.Name.ShouldBe("number"); + description.ArgumentType.ShouldBe(typeof(int)); } [Fact] @@ -52,9 +50,9 @@ namespace Dapr.Actors.Description Action action = () => MethodArgumentDescription.Create("actor", methodInfo, parameterInfo); // Assert - action.Should().ThrowExactly() - .WithMessage("Method 'MethodWithParams' of actor interface '*ITestActor' has variable length parameter 'values'. The actor interface methods must not have variable length parameters.*") - .And.ParamName.Should().Be("actorInterfaceType"); + var exception = Should.Throw(action); + exception.Message.ShouldMatch(@"Method 'MethodWithParams' of actor interface '.*\+ITestActor' has variable length parameter 'values'. The actor interface methods must not have variable length parameters.*"); + exception.ParamName.ShouldBe("actorInterfaceType"); } [Fact] @@ -69,9 +67,9 @@ namespace Dapr.Actors.Description Action action = () => MethodArgumentDescription.Create("actor", methodInfo, parameterInfo); // Assert - action.Should().ThrowExactly() - .WithMessage("Method 'MethodWithIn' of actor interface '*ITestActor' has out/ref/optional parameter 'value'. The actor interface methods must not have out, ref or optional parameters.*") - .And.ParamName.Should().Be("actorInterfaceType"); + var exception = Should.Throw(action); + exception.Message.ShouldMatch(@"Method 'MethodWithIn' of actor interface '.*\+ITestActor' has out/ref/optional parameter 'value'. The actor interface methods must not have out, ref or optional parameters.*"); + exception.ParamName.ShouldBe("actorInterfaceType"); } [Fact] @@ -86,9 +84,9 @@ namespace Dapr.Actors.Description Action action = () => MethodArgumentDescription.Create("actor", methodInfo, parameterInfo); // Assert - action.Should().ThrowExactly() - .WithMessage("Method 'MethodWithOut' of actor interface '*ITestActor' has out/ref/optional parameter 'value'. The actor interface methods must not have out, ref or optional parameters.*") - .And.ParamName.Should().Be("actorInterfaceType"); + var exception = Should.Throw(action); + exception.Message.ShouldMatch(@"Method 'MethodWithOut' of actor interface '.*\+ITestActor' has out/ref/optional parameter 'value'. The actor interface methods must not have out, ref or optional parameters.*"); + exception.ParamName.ShouldBe("actorInterfaceType"); } [Fact] @@ -103,9 +101,9 @@ namespace Dapr.Actors.Description Action action = () => MethodArgumentDescription.Create("actor", methodInfo, parameterInfo); // Assert - action.Should().ThrowExactly() - .WithMessage("Method 'MethodWithOptional' of actor interface '*ITestActor' has out/ref/optional parameter 'value'. The actor interface methods must not have out, ref or optional parameters.*") - .And.ParamName.Should().Be("actorInterfaceType"); + var exception = Should.Throw(action); + exception.Message.ShouldMatch(@"Method 'MethodWithOptional' of actor interface '.*\+ITestActor' has out/ref/optional parameter 'value'. The actor interface methods must not have out, ref or optional parameters.*"); + exception.ParamName.ShouldBe("actorInterfaceType"); } internal interface ITestActor : IActor diff --git a/test/Dapr.Actors.Test/Description/MethodDescriptionTests.cs b/test/Dapr.Actors.Test/Description/MethodDescriptionTests.cs index 8f11595e..c8162f5f 100644 --- a/test/Dapr.Actors.Test/Description/MethodDescriptionTests.cs +++ b/test/Dapr.Actors.Test/Description/MethodDescriptionTests.cs @@ -1,22 +1,22 @@ -// ------------------------------------------------------------------------ -// 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. -// ------------------------------------------------------------------------ - +// ------------------------------------------------------------------------ +// 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. +// ------------------------------------------------------------------------ + using System; +using System.Linq; using System.Reflection; using System.Threading; using System.Threading.Tasks; -using FluentAssertions; -using FluentAssertions.Execution; +using Shouldly; using Xunit; namespace Dapr.Actors.Description @@ -33,15 +33,14 @@ namespace Dapr.Actors.Description var description = MethodDescription.Create("actor", methodInfo, false); // Assert - description.Should().NotBeNull(); + description.ShouldNotBeNull(); - using var _ = new AssertionScope(); - description.MethodInfo.Should().BeSameAs(methodInfo); - description.Name.Should().Be("GetString"); - description.ReturnType.Should().Be>(); - description.Id.Should().NotBe(0); - description.Arguments.Should().BeEmpty(); - description.HasCancellationToken.Should().BeFalse(); + description.MethodInfo.ShouldBeSameAs(methodInfo); + description.Name.ShouldBe("GetString"); + description.ReturnType.ShouldBe(typeof(Task)); + description.Id.ShouldNotBe(0); + description.Arguments.ShouldBeEmpty(); + description.HasCancellationToken.ShouldBeFalse(); } [Fact] @@ -54,7 +53,7 @@ namespace Dapr.Actors.Description var description = MethodDescription.Create("actor", methodInfo, true); // Assert - description.Id.Should().Be(70257263); + description.Id.ShouldBe(70257263); } [Fact] @@ -67,13 +66,9 @@ namespace Dapr.Actors.Description var description = MethodDescription.Create("actor", methodInfo, false); // Assert - using var _ = new AssertionScope(); - description.Arguments.Should().NotContainNulls(); - description.Arguments.Should().AllBeOfType(); - description.Arguments.Should().BeEquivalentTo( - new { Name = "number" }, - new { Name = "choice" }, - new { Name = "information" }); + description.Arguments.ShouldNotBeNull(); + description.Arguments.ShouldBeOfType(); + description.Arguments.Select(m => new {m.Name}).ShouldBe(new[] {new {Name = "number"}, new {Name = "choice"}, new {Name = "information"}}); } [Fact] @@ -86,7 +81,7 @@ namespace Dapr.Actors.Description var description = MethodDescription.Create("actor", methodInfo, false); // Assert - description.HasCancellationToken.Should().BeTrue(); + description.HasCancellationToken.ShouldBeTrue(); } [Fact] @@ -100,9 +95,9 @@ namespace Dapr.Actors.Description Action action = () => MethodDescription.Create("actor", methodInfo, false); // Assert - action.Should().ThrowExactly() - .WithMessage("Method 'MethodWithTokenNotLast' of actor interface '*+ITestActor' has a '*.CancellationToken' parameter that is not the last parameter. If an actor method accepts a '*.CancellationToken' parameter, it must be the last parameter.*") - .And.ParamName.Should().Be("actorInterfaceType"); + var exception = Should.Throw(action); + exception.Message.ShouldMatch(@"Method 'MethodWithTokenNotLast' of actor interface '.*\+ITestActor' has a '.*\.CancellationToken' parameter that is not the last parameter. If an actor method accepts a '.*\.CancellationToken' parameter, it must be the last parameter\..*"); + exception.ParamName.ShouldBe("actorInterfaceType"); } [Fact] @@ -116,9 +111,9 @@ namespace Dapr.Actors.Description Action action = () => MethodDescription.Create("actor", methodInfo, false); // Assert - action.Should().ThrowExactly() - .WithMessage("Method 'MethodWithMultipleTokens' of actor interface '*+ITestActor' has a '*.CancellationToken' parameter that is not the last parameter. If an actor method accepts a '*.CancellationToken' parameter, it must be the last parameter.*") - .And.ParamName.Should().Be("actorInterfaceType"); + var exception = Should.Throw(action); + exception.Message.ShouldMatch(@"Method 'MethodWithMultipleTokens' of actor interface '.*\+ITestActor' has a '.*\.CancellationToken' parameter that is not the last parameter. If an actor method accepts a '.*\.CancellationToken' parameter, it must be the last parameter.*"); + exception.ParamName.ShouldBe("actorInterfaceType"); } internal interface ITestActor : IActor diff --git a/test/Dapr.Actors.Test/Extensions/DurationExtensionsTests.cs b/test/Dapr.Actors.Test/Extensions/DurationExtensionsTests.cs new file mode 100644 index 00000000..d7017cd6 --- /dev/null +++ b/test/Dapr.Actors.Test/Extensions/DurationExtensionsTests.cs @@ -0,0 +1,90 @@ +// ------------------------------------------------------------------------ +// Copyright 2025 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. +// ------------------------------------------------------------------------ + +using System; +using Xunit; + +namespace Dapr.Actors.Extensions; + +public sealed class DurationExtensionsTests +{ + [Theory] + [InlineData("@yearly", 364, 0, 0, 0, 0)] + [InlineData("@monthly", 28, 0, 0, 0, 0 )] + [InlineData("@weekly", 7, 0, 0, 0, 0 )] + [InlineData("@daily", 1, 0, 0, 0, 0)] + [InlineData("@midnight", 0, 0, 0, 0, 0 )] + [InlineData("@hourly", 0, 1, 0, 0, 0)] + [InlineData("@every 1h", 0, 1, 0, 0, 0)] + [InlineData("@every 30m", 0, 0, 30, 0, 0)] + [InlineData("@every 45s", 0, 0, 0, 45, 0)] + [InlineData("@every 1.5h", 0, 1, 30, 0, 0)] + [InlineData("@every 1h30m", 0, 1, 30, 0, 0)] + [InlineData("@every 1h30m45s", 0, 1, 30, 45, 0)] + [InlineData("@every 1h30m45.3s", 0, 1, 30, 45, 300)] + [InlineData("@every 100ms", 0, 0, 0, 0, 100)] + [InlineData("@every 1s500ms", 0, 0, 0, 1, 500)] + [InlineData("@every 1m1s", 0, 0, 1, 1, 0)] + [InlineData("@every 1.1m", 0, 0, 1, 6, 0)] + [InlineData("@every 1.5h30m45s100ms", 0, 2, 0, 45, 100)] + public void ValidatePrefixedPeriodParsing(string input, int expectedDays, int expectedHours, int expectedMinutes, int expectedSeconds, int expectedMilliseconds) + { + var result = input.FromPrefixedPeriod(); + + if (input is "@yearly" or "@monthly") + { + Assert.True(result.Days >= expectedDays); + return; + } + + Assert.Equal(expectedDays, result.Days); + Assert.Equal(expectedHours, result.Hours); + Assert.Equal(expectedMinutes, result.Minutes); + Assert.Equal(expectedSeconds, result.Seconds); + Assert.Equal(expectedMilliseconds, result.Milliseconds); + } + + [Theory] + [InlineData("@yearly", true)] + [InlineData("@monthly", true)] + [InlineData("@weekly", true)] + [InlineData("@daily", true)] + [InlineData("@midnight", true)] + [InlineData("@hourly", true)] + [InlineData("@every 1h", true)] + [InlineData("@every 30m", true)] + [InlineData("@every 45s", true)] + [InlineData("@every 1.5h", true)] + [InlineData("@every 1h30m", true)] + [InlineData("@every 1h30m45s", true)] + [InlineData("@every 1h30m45.3s", true)] + [InlineData("@every 100ms", true)] + [InlineData("@every 1s500ms", true)] + [InlineData("@every 1m1s", true)] + [InlineData("@every 1.1m", true)] + [InlineData("@every 1.5h30m45s100ms", true)] + public void TestIsDurationExpression(string input, bool expectedResult) + { + var actualResult = input.IsDurationExpression(); + Assert.Equal(expectedResult, actualResult); + } + + [Fact] + public void ValidateExceptionForUnknownExpression() + { + Assert.Throws(() => + { + var result = "every 100s".FromPrefixedPeriod(); + }); + } +} diff --git a/test/Dapr.Actors.Test/Extensions/StringExtensionsTests.cs b/test/Dapr.Actors.Test/Extensions/StringExtensionsTests.cs new file mode 100644 index 00000000..89cfea8a --- /dev/null +++ b/test/Dapr.Actors.Test/Extensions/StringExtensionsTests.cs @@ -0,0 +1,41 @@ +// ------------------------------------------------------------------------ +// Copyright 2025 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. +// ------------------------------------------------------------------------ + +using System; +using System.Collections.Generic; +using Xunit; + +namespace Dapr.Actors.Extensions; + +public sealed class StringExtensionsTests +{ + [Fact] + public void ValidateMatchesValue() + { + var matchingValues = new List { "apples", "bananas", "cherries", }; + const string value = "I have four cherries"; + + var result = value.EndsWithAny(matchingValues, StringComparison.InvariantCulture); + Assert.True(result); + } + + [Fact] + public void ValidateDoesNotMatchValue() + { + var matchingValues = new List { "apples", "bananas", "cherries", }; + const string value = "I have four grapes"; + + var result = value.EndsWithAny(matchingValues, StringComparison.InvariantCulture); + Assert.False(result); + } +} diff --git a/test/Dapr.Actors.Test/ITestActor.cs b/test/Dapr.Actors.Test/ITestActor.cs index 4fa611e7..847342c1 100644 --- a/test/Dapr.Actors.Test/ITestActor.cs +++ b/test/Dapr.Actors.Test/ITestActor.cs @@ -11,118 +11,117 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Test +namespace Dapr.Actors.Test; + +using System; +using System.Threading; +using System.Threading.Tasks; +using Dapr.Actors.Runtime; + +/// +/// Interface for test actor. +/// +public interface ITestActor : IActor { - using System; - using System.Threading; - using System.Threading.Tasks; - using Dapr.Actors.Runtime; + /// + /// GetCount method for TestActor. + /// + /// Cancellation token to cancel the operation. + /// The current count as stored in actor. + Task GetCountAsync(CancellationToken cancellationToken); /// - /// Interface for test actor. + /// SetCount method for test actor. /// - public interface ITestActor : IActor - { - /// - /// GetCount method for TestActor. - /// - /// Cancellation token to cancel the operation. - /// The current count as stored in actor. - Task GetCountAsync(CancellationToken cancellationToken); + /// Count to set for the actor. + /// Cancellation token to cancel the operation. + /// Task. + Task SetCountAsync(int count, CancellationToken cancellationToken); +} - /// - /// SetCount method for test actor. - /// - /// Count to set for the actor. - /// Cancellation token to cancel the operation. - /// Task. - Task SetCountAsync(int count, CancellationToken cancellationToken); +/// +/// Test Actor Class. +/// +public class TestActor : Actor, ITestActor +{ + public TestActor(ActorHost host, IActorStateManager stateManager = null) + : base(host) + { + if (stateManager != null) + { + this.StateManager = stateManager; + } } - /// - /// Test Actor Class. - /// - public class TestActor : Actor, ITestActor + /// + public Task GetCountAsync(CancellationToken cancellationToken) { - public TestActor(ActorHost host, IActorStateManager stateManager = null) - : base(host) - { - if (stateManager != null) - { - this.StateManager = stateManager; - } - } + return Task.FromResult(5); + } - /// - public Task GetCountAsync(CancellationToken cancellationToken) - { - return Task.FromResult(5); - } + /// + public Task SetCountAsync(int count, CancellationToken cancellationToken) + { + return Task.CompletedTask; + } - /// - public Task SetCountAsync(int count, CancellationToken cancellationToken) - { - return Task.CompletedTask; - } + public Task SaveTestState() + { + return this.SaveStateAsync(); + } - public Task SaveTestState() - { - return this.SaveStateAsync(); - } + public Task ResetTestStateAsync() + { + return this.ResetStateAsync(); + } - public Task ResetTestStateAsync() - { - return this.ResetStateAsync(); - } + public void TimerCallbackNonTaskReturnType() + { + } - public void TimerCallbackNonTaskReturnType() - { - } + public Task TimerCallbackTwoArguments(int i, int j) + { + Console.WriteLine(i + j); + return default; + } - public Task TimerCallbackTwoArguments(int i, int j) - { - Console.WriteLine(i + j); - return default; - } + private Task TimerCallbackPrivate() + { + return default; + } - private Task TimerCallbackPrivate() - { - return default; - } + protected Task TimerCallbackProtected() + { + return default; + } - protected Task TimerCallbackProtected() - { - return default; - } + internal Task TimerCallbackInternal() + { + return default; + } - internal Task TimerCallbackInternal() - { - return default; - } + public Task TimerCallbackPublicWithNoArguments() + { + return default; + } - public Task TimerCallbackPublicWithNoArguments() - { - return default; - } + public Task TimerCallbackPublicWithOneArgument(int i) + { + return default; + } - public Task TimerCallbackPublicWithOneArgument(int i) - { - return default; - } + public Task TimerCallbackOverloaded() + { + return default; + } - public Task TimerCallbackOverloaded() - { - return default; - } + public Task TimerCallbackOverloaded(int i) + { + return default; + } - public Task TimerCallbackOverloaded(int i) - { - return default; - } - - public static Task TimerCallbackStatic() - { - return default; - } + public static Task TimerCallbackStatic() + { + return default; } } diff --git a/test/Dapr.Actors.Test/Runtime/ActorRuntimeOptionsTests.cs b/test/Dapr.Actors.Test/Runtime/ActorRuntimeOptionsTests.cs index b68eda5c..6701535f 100644 --- a/test/Dapr.Actors.Test/Runtime/ActorRuntimeOptionsTests.cs +++ b/test/Dapr.Actors.Test/Runtime/ActorRuntimeOptionsTests.cs @@ -17,7 +17,7 @@ namespace Dapr.Actors.Test.Runtime using Moq; using Xunit; using System; - using FluentAssertions; + using Shouldly; public sealed class ActorRuntimeOptionsTests { @@ -59,7 +59,7 @@ namespace Dapr.Actors.Test.Runtime var options = new ActorRuntimeOptions(); Action action = () => options.ActorIdleTimeout = TimeSpan.FromSeconds(-1); - action.Should().Throw(); + action.ShouldThrow(); } @@ -78,7 +78,7 @@ namespace Dapr.Actors.Test.Runtime var options = new ActorRuntimeOptions(); Action action = () => options.ActorScanInterval = TimeSpan.FromSeconds(-1); - action.Should().Throw(); + action.ShouldThrow(); } [Fact] @@ -96,7 +96,7 @@ namespace Dapr.Actors.Test.Runtime var options = new ActorRuntimeOptions(); Action action = () => options.DrainOngoingCallTimeout = TimeSpan.FromSeconds(-1); - action.Should().Throw(); + action.ShouldThrow(); } [Fact] @@ -115,7 +115,7 @@ namespace Dapr.Actors.Test.Runtime var options = new ActorRuntimeOptions(); Action action = () => options.JsonSerializerOptions = null; - action.Should().Throw(); + action.ShouldThrow(); } [Fact] @@ -124,7 +124,7 @@ namespace Dapr.Actors.Test.Runtime var options = new ActorRuntimeOptions(); Action action = () => options.RemindersStoragePartitions = -1; - action.Should().Throw(); + action.ShouldThrow(); } [Fact] diff --git a/test/Dapr.Actors.Test/Runtime/ActorTests.cs b/test/Dapr.Actors.Test/Runtime/ActorTests.cs index f88b4e03..d08f42a0 100644 --- a/test/Dapr.Actors.Test/Runtime/ActorTests.cs +++ b/test/Dapr.Actors.Test/Runtime/ActorTests.cs @@ -11,119 +11,112 @@ // limitations under the License. // ------------------------------------------------------------------------ -namespace Dapr.Actors.Test.Runtime +namespace Dapr.Actors.Test.Runtime; + +using System; +using System.Threading; +using System.Threading.Tasks; +using Dapr.Actors.Runtime; +using Shouldly; +using Moq; +using Xunit; + +public sealed class ActorTests { - using System; - using System.Threading; - using System.Threading.Tasks; - using Dapr.Actors.Runtime; - using FluentAssertions; - using Moq; - using Xunit; - - public sealed class ActorTests + [Fact] + public void TestNewActorWithMockStateManager() { - [Fact] - public void TestNewActorWithMockStateManager() - { - var mockStateManager = new Mock(); - var testDemoActor = this.CreateTestDemoActor(mockStateManager.Object); - testDemoActor.Host.Should().NotBeNull(); - testDemoActor.Id.Should().NotBeNull(); - } - - [Fact] - public async Task TestSaveState() - { - var mockStateManager = new Mock(); - mockStateManager.Setup(manager => manager.SaveStateAsync(It.IsAny())); - var testDemoActor = this.CreateTestDemoActor(mockStateManager.Object); - await testDemoActor.SaveTestState(); - mockStateManager.Verify(manager => manager.SaveStateAsync(It.IsAny()), Times.Once); - } - - [Fact] - public async Task TestResetStateAsync() - { - var mockStateManager = new Mock(); - mockStateManager.Setup(manager => manager.ClearCacheAsync(It.IsAny())); - var testDemoActor = this.CreateTestDemoActor(mockStateManager.Object); - await testDemoActor.ResetTestStateAsync(); - mockStateManager.Verify(manager => manager.ClearCacheAsync(It.IsAny()), Times.Once); - } - - [Theory] - [InlineData("NonExistentMethod", "Timer callback method: NonExistentMethod does not exist in the Actor class: TestActor")] - [InlineData("TimerCallbackTwoArguments", "Timer callback can accept only zero or one parameters")] - [InlineData("TimerCallbackNonTaskReturnType", "Timer callback can only return type Task")] - [InlineData("TimerCallbackOverloaded", "Timer callback method: TimerCallbackOverloaded cannot be overloaded.")] - public void ValidateTimerCallback_CallbackMethodDoesNotMeetRequirements(string callback, string expectedErrorMessage) - { - var mockStateManager = new Mock(); - mockStateManager.Setup(manager => manager.ClearCacheAsync(It.IsAny())); - var testDemoActor = this.CreateTestDemoActor(mockStateManager.Object); - - ; - FluentActions.Invoking(() => - testDemoActor.ValidateTimerCallback(testDemoActor.Host, callback)) - .Should().Throw() - .WithMessage(expectedErrorMessage); - } - - [Theory] - [InlineData("TimerCallbackPrivate")] - [InlineData("TimerCallbackProtected")] - [InlineData("TimerCallbackInternal")] - [InlineData("TimerCallbackPublicWithNoArguments")] - [InlineData("TimerCallbackPublicWithOneArgument")] - [InlineData("TimerCallbackStatic")] - public void ValidateTimerCallback_CallbackMethodMeetsRequirements(string callback) - { - var mockStateManager = new Mock(); - mockStateManager.Setup(manager => manager.ClearCacheAsync(It.IsAny())); - var testDemoActor = this.CreateTestDemoActor(mockStateManager.Object); - - ; - FluentActions.Invoking(() => - testDemoActor.ValidateTimerCallback(testDemoActor.Host, callback)) - .Should().NotThrow(); - } - - [Theory] - [InlineData("TimerCallbackPrivate")] - [InlineData("TimerCallbackPublicWithOneArgument")] - [InlineData("TimerCallbackStatic")] - public void GetMethodInfoUsingReflection_MethodsMatchingBindingFlags(string callback) - { - var mockStateManager = new Mock(); - mockStateManager.Setup(manager => manager.ClearCacheAsync(It.IsAny())); - var testDemoActor = this.CreateTestDemoActor(mockStateManager.Object); - var methodInfo = testDemoActor.GetMethodInfoUsingReflection(testDemoActor.Host.ActorTypeInfo.ImplementationType, callback); - Assert.NotNull(methodInfo); - } - - [Theory] - [InlineData("TestActor")] // Constructor - public void GetMethodInfoUsingReflection_MethodsNotMatchingBindingFlags(string callback) - { - var mockStateManager = new Mock(); - mockStateManager.Setup(manager => manager.ClearCacheAsync(It.IsAny())); - var testDemoActor = this.CreateTestDemoActor(mockStateManager.Object); - var methodInfo = testDemoActor.GetMethodInfoUsingReflection(testDemoActor.Host.ActorTypeInfo.ImplementationType, callback); - Assert.Null(methodInfo); - } - - /// - /// On my test code I want to pass the mock statemanager all the time. - /// - /// Mock StateManager. - /// TestActor. - private TestActor CreateTestDemoActor(IActorStateManager actorStateManager) - { - var host = ActorHost.CreateForTest(); - var testActor = new TestActor(host, actorStateManager); - return testActor; - } - + var mockStateManager = new Mock(); + var testDemoActor = this.CreateTestDemoActor(mockStateManager.Object); + testDemoActor.Host.ShouldNotBeNull(); + testDemoActor.Id.ShouldNotBeNull(); } + + [Fact] + public async Task TestSaveState() + { + var mockStateManager = new Mock(); + mockStateManager.Setup(manager => manager.SaveStateAsync(It.IsAny())); + var testDemoActor = this.CreateTestDemoActor(mockStateManager.Object); + await testDemoActor.SaveTestState(); + mockStateManager.Verify(manager => manager.SaveStateAsync(It.IsAny()), Times.Once); + } + + [Fact] + public async Task TestResetStateAsync() + { + var mockStateManager = new Mock(); + mockStateManager.Setup(manager => manager.ClearCacheAsync(It.IsAny())); + var testDemoActor = this.CreateTestDemoActor(mockStateManager.Object); + await testDemoActor.ResetTestStateAsync(); + mockStateManager.Verify(manager => manager.ClearCacheAsync(It.IsAny()), Times.Once); + } + + [Theory] + [InlineData("NonExistentMethod", "Timer callback method: NonExistentMethod does not exist in the Actor class: TestActor")] + [InlineData("TimerCallbackTwoArguments", "Timer callback can accept only zero or one parameters")] + [InlineData("TimerCallbackNonTaskReturnType", "Timer callback can only return type Task")] + [InlineData("TimerCallbackOverloaded", "Timer callback method: TimerCallbackOverloaded cannot be overloaded.")] + public void ValidateTimerCallback_CallbackMethodDoesNotMeetRequirements(string callback, string expectedErrorMessage) + { + var mockStateManager = new Mock(); + mockStateManager.Setup(manager => manager.ClearCacheAsync(It.IsAny())); + var testDemoActor = this.CreateTestDemoActor(mockStateManager.Object); + + Should.Throw(() => testDemoActor.ValidateTimerCallback(testDemoActor.Host, callback)) + .Message.ShouldBe(expectedErrorMessage); + } + + [Theory] + [InlineData("TimerCallbackPrivate")] + [InlineData("TimerCallbackProtected")] + [InlineData("TimerCallbackInternal")] + [InlineData("TimerCallbackPublicWithNoArguments")] + [InlineData("TimerCallbackPublicWithOneArgument")] + [InlineData("TimerCallbackStatic")] + public void ValidateTimerCallback_CallbackMethodMeetsRequirements(string callback) + { + var mockStateManager = new Mock(); + mockStateManager.Setup(manager => manager.ClearCacheAsync(It.IsAny())); + var testDemoActor = this.CreateTestDemoActor(mockStateManager.Object); + + Should.NotThrow(() => testDemoActor.ValidateTimerCallback(testDemoActor.Host, callback)); + } + + [Theory] + [InlineData("TimerCallbackPrivate")] + [InlineData("TimerCallbackPublicWithOneArgument")] + [InlineData("TimerCallbackStatic")] + public void GetMethodInfoUsingReflection_MethodsMatchingBindingFlags(string callback) + { + var mockStateManager = new Mock(); + mockStateManager.Setup(manager => manager.ClearCacheAsync(It.IsAny())); + var testDemoActor = this.CreateTestDemoActor(mockStateManager.Object); + var methodInfo = testDemoActor.GetMethodInfoUsingReflection(testDemoActor.Host.ActorTypeInfo.ImplementationType, callback); + Assert.NotNull(methodInfo); + } + + [Theory] + [InlineData("TestActor")] // Constructor + public void GetMethodInfoUsingReflection_MethodsNotMatchingBindingFlags(string callback) + { + var mockStateManager = new Mock(); + mockStateManager.Setup(manager => manager.ClearCacheAsync(It.IsAny())); + var testDemoActor = this.CreateTestDemoActor(mockStateManager.Object); + var methodInfo = testDemoActor.GetMethodInfoUsingReflection(testDemoActor.Host.ActorTypeInfo.ImplementationType, callback); + Assert.Null(methodInfo); + } + + /// + /// On my test code I want to pass the mock statemanager all the time. + /// + /// Mock StateManager. + /// TestActor. + private TestActor CreateTestDemoActor(IActorStateManager actorStateManager) + { + var host = ActorHost.CreateForTest(); + var testActor = new TestActor(host, actorStateManager); + return testActor; + } + } diff --git a/test/Dapr.Actors.Test/Runtime/DefaultActorTimerManagerTests.cs b/test/Dapr.Actors.Test/Runtime/DefaultActorTimerManagerTests.cs index 352b431d..e547629a 100644 --- a/test/Dapr.Actors.Test/Runtime/DefaultActorTimerManagerTests.cs +++ b/test/Dapr.Actors.Test/Runtime/DefaultActorTimerManagerTests.cs @@ -1,9 +1,24 @@ -// ------------------------------------------------------------ +// ------------------------------------------------------------------------ +// Copyright 2025 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. +// ------------------------------------------------------------------------ + +// ------------------------------------------------------------ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. // ------------------------------------------------------------ using System; +using System.Net; +using System.Net.Http; using System.Text.Json; using System.Threading; using System.Threading.Tasks; @@ -11,78 +26,156 @@ using Moq; using Xunit; using JsonSerializer = System.Text.Json.JsonSerializer; -namespace Dapr.Actors.Runtime +namespace Dapr.Actors.Runtime; + +public sealed class DefaultActorTimerManagerTests { - public sealed class DefaultActorTimerManagerTests + /// + /// When register reminder is called, interactor is called with correct data. + /// + /// + [Fact] + public async Task RegisterReminderAsync_CallsInteractor_WithCorrectData() { - /// - /// When register reminder is called, interactor is called with correct data. - /// - /// - [Fact] - public async Task RegisterReminderAsync_CallsInteractor_WithCorrectData() + var actorId = "123"; + var actorType = "abc"; + var interactor = new Mock(); + var defaultActorTimerManager = new DefaultActorTimerManager(interactor.Object); + var actorReminder = new ActorReminder(actorType, new ActorId(actorId), "remindername", Array.Empty(), TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1)); + var actualData = string.Empty; + + interactor + .Setup(d => d.RegisterReminderAsync(actorType, actorId, "remindername", It.Is(data => !string.IsNullOrEmpty(data)), It.IsAny())) + .Callback((innerType, innerId, reminderName, data, token) => { + actualData = data; + }) + .Returns(Task.CompletedTask); + + await defaultActorTimerManager.RegisterReminderAsync(actorReminder); + + JsonElement json = JsonSerializer.Deserialize(actualData); + + var isPeriodSet = json.TryGetProperty("period", out var period); + var isdDueTimeSet = json.TryGetProperty("dueTime", out var dueTime); + + Assert.True(isPeriodSet); + Assert.True(isdDueTimeSet); + + Assert.Equal("0h1m0s0ms", period.GetString()); + Assert.Equal("0h1m0s0ms", dueTime.GetString()); + } + + /// + /// When register reminder is called with repetition, interactor is called with correct data. + /// + /// + [Fact] + public async Task RegisterReminderAsync_WithRepetition_CallsInteractor_WithCorrectData() + { + const string actorId = "123"; + const string actorType = "abc"; + var interactor = new Mock(); + var defaultActorTimerManager = new DefaultActorTimerManager(interactor.Object); + var actorReminder = new ActorReminder(actorType, new ActorId(actorId), "remindername", new byte[] { }, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1), 10); + var actualData = string.Empty; + + interactor + .Setup(d => d.RegisterReminderAsync(actorType, actorId, "remindername", It.Is(data => !string.IsNullOrEmpty(data)), It.IsAny())) + .Callback((innerType, innerActorId, reminderName, data, token) => { + actualData = data; + }) + .Returns(Task.CompletedTask); + + await defaultActorTimerManager.RegisterReminderAsync(actorReminder); + + JsonElement json = JsonSerializer.Deserialize(actualData); + + var isPeriodSet = json.TryGetProperty("period", out var period); + var isdDueTimeSet = json.TryGetProperty("dueTime", out var dueTime); + + Assert.True(isPeriodSet); + Assert.True(isdDueTimeSet); + + Assert.Equal("R10/PT1M", period.GetString()); + Assert.Equal("0h1m0s0ms", dueTime.GetString()); + } + + /// + /// Get the GetReminder method is called without a registered reminder, it should return null. + /// + [Fact] + public async Task GetReminderAsync_ReturnsNullWhenUnavailable() + { + const string actorId = "123"; + const string actorType = "abc"; + const string reminderName = "reminderName"; + var interactor = new Mock(); + interactor + .Setup(d => d.GetReminderAsync(It.IsAny(), It.IsAny(), It.IsAny(), + It.IsAny())) + .ReturnsAsync(new HttpResponseMessage(HttpStatusCode.InternalServerError)); + var defaultActorTimerManager = new DefaultActorTimerManager(interactor.Object); + + var reminderResult = await defaultActorTimerManager.GetReminderAsync(new ActorReminderToken(actorType, new ActorId(actorId), reminderName)); + Assert.Null(reminderResult); + } + + [Fact] + public async Task GetReminderAsync_ReturnsNullWhenDeserialziationFails() + { + const string actorId = "123"; + const string actorType = "abc"; + const string reminderName = "reminderName"; + var interactor = new Mock(); + var response = new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent("{}") }; + + interactor + .Setup(d => d.GetReminderAsync(It.IsAny(), It.IsAny(), It.IsAny(), + It.IsAny())) + .ReturnsAsync(response); + var defaultActorTimerManager = new DefaultActorTimerManager(interactor.Object); + + var reminderResult = await defaultActorTimerManager.GetReminderAsync(new ActorReminderToken(actorType, new ActorId(actorId), reminderName)); + Assert.Null(reminderResult); + } + + [Fact] + public async Task GetReminderAsync_ReturnsResultWhenAvailable() + { + const string actorId = "123"; + const string actorType = "abc"; + const string reminderName = "reminderName"; + var interactor = new Mock(); + + //Create the reminder we'll return + var state = Array.Empty(); + var dueTime = TimeSpan.FromMinutes(1); + var period = TimeSpan.FromMinutes(1); + var actorReminder = new ActorReminder(actorType, new ActorId(actorId), "remindername", state, dueTime, period, 10); + + //Serialize and create the response value + var actorReminderInfo = new ReminderInfo(actorReminder.State, actorReminder.DueTime, actorReminder.Period, + actorReminder.Repetitions, actorReminder.Ttl); + var serializedActorReminderInfo = await actorReminderInfo.SerializeAsync(); + var reminderResponse = new HttpResponseMessage(HttpStatusCode.OK) { - var actorId = "123"; - var actorType = "abc"; - var interactor = new Mock(); - var defaultActorTimerManager = new DefaultActorTimerManager(interactor.Object); - var actorReminder = new ActorReminder(actorType, new ActorId(actorId), "remindername", new byte[] { }, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1)); - var actualData = string.Empty; + Content = new StringContent(serializedActorReminderInfo) + }; - interactor - .Setup(d => d.RegisterReminderAsync(actorType, actorId, "remindername", It.Is(data => !string.IsNullOrEmpty(data)), It.IsAny())) - .Callback((actorType, actorID, reminderName, data, token) => { - actualData = data; - }) - .Returns(Task.CompletedTask); - - await defaultActorTimerManager.RegisterReminderAsync(actorReminder); - - JsonElement json = JsonSerializer.Deserialize(actualData); - - var isPeriodSet = json.TryGetProperty("period", out var period); - var isdDueTimeSet = json.TryGetProperty("dueTime", out var dueTime); + //Register the response + interactor + .Setup(d => d.GetReminderAsync(actorType, actorId, reminderName, It.IsAny())) + .Callback((type, id, name, token) => { + }) + .Returns(Task.FromResult(reminderResponse)); + var defaultActorTimerManager = new DefaultActorTimerManager(interactor.Object); - Assert.True(isPeriodSet); - Assert.True(isdDueTimeSet); + var reminderResult = await defaultActorTimerManager.GetReminderAsync(new ActorReminderToken(actorType, new ActorId(actorId), reminderName)); + Assert.NotNull(reminderResult); - Assert.Equal("0h1m0s0ms", period.GetString()); - Assert.Equal("0h1m0s0ms", dueTime.GetString()); - } - - /// - /// When register reminder is called with repetition, interactor is called with correct data. - /// - /// - [Fact] - public async Task RegisterReminderAsync_WithRepetition_CallsInteractor_WithCorrectData() - { - var actorId = "123"; - var actorType = "abc"; - var interactor = new Mock(); - var defaultActorTimerManager = new DefaultActorTimerManager(interactor.Object); - var actorReminder = new ActorReminder(actorType, new ActorId(actorId), "remindername", new byte[] { }, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1), 10); - var actualData = string.Empty; - - interactor - .Setup(d => d.RegisterReminderAsync(actorType, actorId, "remindername", It.Is(data => !string.IsNullOrEmpty(data)), It.IsAny())) - .Callback((actorType, actorID, reminderName, data, token) => { - actualData = data; - }) - .Returns(Task.CompletedTask); - - await defaultActorTimerManager.RegisterReminderAsync(actorReminder); - - JsonElement json = JsonSerializer.Deserialize(actualData); - - var isPeriodSet = json.TryGetProperty("period", out var period); - var isdDueTimeSet = json.TryGetProperty("dueTime", out var dueTime); - - Assert.True(isPeriodSet); - Assert.True(isdDueTimeSet); - - Assert.Equal("R10/PT1M", period.GetString()); - Assert.Equal("0h1m0s0ms", dueTime.GetString()); - } + Assert.Equal(dueTime, reminderResult.DueTime); + Assert.Equal(state, reminderResult.State); + Assert.Equal(period, reminderResult.Period); + Assert.Equal(reminderName, reminderResult.Name); } } diff --git a/test/Dapr.Actors.Test/Serialization/ActorIdDataContractSerializationTest.cs b/test/Dapr.Actors.Test/Serialization/ActorIdDataContractSerializationTest.cs index dfaccbc1..7ac29505 100644 --- a/test/Dapr.Actors.Test/Serialization/ActorIdDataContractSerializationTest.cs +++ b/test/Dapr.Actors.Test/Serialization/ActorIdDataContractSerializationTest.cs @@ -14,69 +14,68 @@ using System.IO; using System.Runtime.Serialization; using System.Xml; -using FluentAssertions; +using Shouldly; using Xunit; -namespace Dapr.Actors.Serialization +namespace Dapr.Actors.Serialization; + +public class ActorReferenceDataContractSerializationTest { - public class ActorReferenceDataContractSerializationTest + [Fact] + public void CanSerializeAndDeserializeActorId() { - [Fact] - public void CanSerializeAndDeserializeActorId() + var id = ActorId.CreateRandom(); + DataContractSerializer serializer = new DataContractSerializer(id.GetType()); + MemoryStream ms = new MemoryStream(); + serializer.WriteObject(ms, id); + ms.Position = 0; + + XmlDocument xmlDoc = new XmlDocument(); + xmlDoc.Load(ms); + xmlDoc.DocumentElement.Name.ShouldBe("ActorId"); + xmlDoc.DocumentElement.InnerText.ShouldBe(id.ToString()); + ms.Position = 0; + + var deserializedActorId = serializer.ReadObject(ms) as ActorId; + deserializedActorId.ShouldBe(id); + } + + [Fact] + public void CanSerializeAndDeserializeNullActorId() + { + ActorId id = null; + DataContractSerializer serializer = new DataContractSerializer(typeof(ActorId)); + MemoryStream ms = new MemoryStream(); + serializer.WriteObject(ms, id); + ms.Position = 0; + + XmlDocument xmlDoc = new XmlDocument(); + xmlDoc.Load(ms); + xmlDoc.DocumentElement.Name.ShouldBe("ActorId"); + xmlDoc.DocumentElement.InnerText.ShouldBe(string.Empty); + + ms.Position = 0; + var deserializedActorId = serializer.ReadObject(ms) as ActorId; + deserializedActorId.ShouldBe(id); + } + + [Fact] + public void CanRoundTripActorReference() + { + var actorId = new ActorId("abc"); + var actorReference = new ActorReference() { - var id = ActorId.CreateRandom(); - DataContractSerializer serializer = new DataContractSerializer(id.GetType()); - MemoryStream ms = new MemoryStream(); - serializer.WriteObject(ms, id); - ms.Position = 0; + ActorId = actorId, + ActorType = "TestActor" + }; - XmlDocument xmlDoc = new XmlDocument(); - xmlDoc.Load(ms); - xmlDoc.DocumentElement.Name.Should().Be("ActorId"); - xmlDoc.DocumentElement.InnerText.Should().Be(id.ToString()); - ms.Position = 0; + DataContractSerializer serializer = new DataContractSerializer(actorReference.GetType()); + MemoryStream ms = new MemoryStream(); + serializer.WriteObject(ms, actorReference); + ms.Position = 0; - var deserializedActorId = serializer.ReadObject(ms) as ActorId; - deserializedActorId.Should().Be(id); - } - - [Fact] - public void CanSerializeAndDeserializeNullActorId() - { - ActorId id = null; - DataContractSerializer serializer = new DataContractSerializer(typeof(ActorId)); - MemoryStream ms = new MemoryStream(); - serializer.WriteObject(ms, id); - ms.Position = 0; - - XmlDocument xmlDoc = new XmlDocument(); - xmlDoc.Load(ms); - xmlDoc.DocumentElement.Name.Should().Be("ActorId"); - xmlDoc.DocumentElement.InnerText.Should().Be(string.Empty); - - ms.Position = 0; - var deserializedActorId = serializer.ReadObject(ms) as ActorId; - deserializedActorId.Should().Be(id); - } - - [Fact] - public void CanRoundTripActorReference() - { - var actorId = new ActorId("abc"); - var actorReference = new ActorReference() - { - ActorId = actorId, - ActorType = "TestActor" - }; - - DataContractSerializer serializer = new DataContractSerializer(actorReference.GetType()); - MemoryStream ms = new MemoryStream(); - serializer.WriteObject(ms, actorReference); - ms.Position = 0; - - var deserializedActorRef = serializer.ReadObject(ms) as ActorReference; - deserializedActorRef.ActorId.Should().Be(actorId); - deserializedActorRef.ActorType.Should().Be("TestActor"); - } + var deserializedActorRef = serializer.ReadObject(ms) as ActorReference; + deserializedActorRef.ActorId.ShouldBe(actorId); + deserializedActorRef.ActorType.ShouldBe("TestActor"); } } diff --git a/test/Dapr.Actors.Test/Serialization/ActorIdJsonConverterTest.cs b/test/Dapr.Actors.Test/Serialization/ActorIdJsonConverterTest.cs index 9c4f2219..06615a1c 100644 --- a/test/Dapr.Actors.Test/Serialization/ActorIdJsonConverterTest.cs +++ b/test/Dapr.Actors.Test/Serialization/ActorIdJsonConverterTest.cs @@ -15,101 +15,100 @@ using System.Text.Json; using System.Text.Json.Serialization; using Xunit; -namespace Dapr.Actors.Serialization +namespace Dapr.Actors.Serialization; + +public class ActorIdJsonConverterTest { - public class ActorIdJsonConverterTest + [Fact] + public void CanSerializeActorId() { - [Fact] - public void CanSerializeActorId() - { - var id = ActorId.CreateRandom(); - var document = new { actor = id, }; + var id = ActorId.CreateRandom(); + var document = new { actor = id, }; - // We use strings for ActorId - the result should be the same as passing the Id directly. - var expected = JsonSerializer.Serialize(new { actor = id.GetId(), }); + // We use strings for ActorId - the result should be the same as passing the Id directly. + var expected = JsonSerializer.Serialize(new { actor = id.GetId(), }); - var serialized = JsonSerializer.Serialize(document); + var serialized = JsonSerializer.Serialize(document); - Assert.Equal(expected, serialized); - } + Assert.Equal(expected, serialized); + } - [Fact] - public void CanSerializeNullActorId() - { - var document = new { actor = (ActorId)null, }; + [Fact] + public void CanSerializeNullActorId() + { + var document = new { actor = (ActorId)null, }; - var expected = JsonSerializer.Serialize(new { actor = (string)null, }); + var expected = JsonSerializer.Serialize(new { actor = (string)null, }); - var serialized = JsonSerializer.Serialize(document); + var serialized = JsonSerializer.Serialize(document); - Assert.Equal(expected, serialized); - } + Assert.Equal(expected, serialized); + } - [Fact] - public void CanDeserializeActorId() - { - var id = ActorId.CreateRandom().GetId(); - var document = $@" + [Fact] + public void CanDeserializeActorId() + { + var id = ActorId.CreateRandom().GetId(); + var document = $@" {{ ""actor"": ""{id}"" }}"; - var deserialized = JsonSerializer.Deserialize(document); + var deserialized = JsonSerializer.Deserialize(document); - Assert.Equal(id, deserialized.Actor.GetId()); - } + Assert.Equal(id, deserialized.Actor.GetId()); + } - [Fact] - public void CanDeserializeNullActorId() - { - const string document = @" + [Fact] + public void CanDeserializeNullActorId() + { + const string document = @" { ""actor"": null }"; - var deserialized = JsonSerializer.Deserialize(document); + var deserialized = JsonSerializer.Deserialize(document); - Assert.Null(deserialized.Actor); - } + Assert.Null(deserialized.Actor); + } - [Theory] - [InlineData("{ \"actor\": ")] - [InlineData("{ \"actor\": \"hi")] - [InlineData("{ \"actor\": }")] - [InlineData("{ \"actor\": 3 }")] - [InlineData("{ \"actor\": \"\"}")] - [InlineData("{ \"actor\": \" \"}")] - public void CanReportErrorsFromInvalidData(string document) + [Theory] + [InlineData("{ \"actor\": ")] + [InlineData("{ \"actor\": \"hi")] + [InlineData("{ \"actor\": }")] + [InlineData("{ \"actor\": 3 }")] + [InlineData("{ \"actor\": \"\"}")] + [InlineData("{ \"actor\": \" \"}")] + public void CanReportErrorsFromInvalidData(string document) + { + // The error messages are provided by the serializer, don't test them here + // that would be fragile. + Assert.Throws(() => { - // The error messages are provided by the serializer, don't test them here - // that would be fragile. - Assert.Throws(() => - { - JsonSerializer.Deserialize(document); - }); - } + JsonSerializer.Deserialize(document); + }); + } - // Regression test for #444 - [Fact] - public void CanRoundTripActorReference() + // Regression test for #444 + [Fact] + public void CanRoundTripActorReference() + { + var reference = new ActorReference() { - var reference = new ActorReference() - { - ActorId = ActorId.CreateRandom(), - ActorType = "TestActor", - }; + ActorId = ActorId.CreateRandom(), + ActorType = "TestActor", + }; - var serialized = JsonSerializer.Serialize(reference); - var deserialized = JsonSerializer.Deserialize(serialized); + var serialized = JsonSerializer.Serialize(reference); + var deserialized = JsonSerializer.Deserialize(serialized); - Assert.Equal(reference.ActorId.GetId(), deserialized.ActorId.GetId()); - Assert.Equal(reference.ActorType, deserialized.ActorType); - } + Assert.Equal(reference.ActorId.GetId(), deserialized.ActorId.GetId()); + Assert.Equal(reference.ActorType, deserialized.ActorType); + } - private class ActorHolder - { - [JsonPropertyName("actor")] - public ActorId Actor { get; set; } - } + private class ActorHolder + { + [JsonPropertyName("actor")] + public ActorId Actor { get; set; } } } diff --git a/test/Dapr.Actors.Test/Serialization/TimerInfoJsonConverterTest.cs b/test/Dapr.Actors.Test/Serialization/TimerInfoJsonConverterTest.cs index 264fcb8a..4cf4c58a 100644 --- a/test/Dapr.Actors.Test/Serialization/TimerInfoJsonConverterTest.cs +++ b/test/Dapr.Actors.Test/Serialization/TimerInfoJsonConverterTest.cs @@ -16,30 +16,29 @@ using System.Text.Json; using Xunit; #pragma warning disable 0618 -namespace Dapr.Actors.Runtime +namespace Dapr.Actors.Runtime; + +public class TimerInfoJsonConverterTest { - public class TimerInfoJsonConverterTest + [Theory] + [InlineData("test", new byte[] {1, 2, 3}, 2, 4)] + [InlineData(null, new byte[] {1}, 2, 4)] + [InlineData("test", null, 2, 4)] + [InlineData("test", new byte[] {1}, 0, 4)] + [InlineData("test", new byte[] {1}, 2, 0)] + public void CanSerializeAndDeserializeTimerInfo(string callback, byte[] state, int dueTime, int period) { - [Theory] - [InlineData("test", new byte[] {1, 2, 3}, 2, 4)] - [InlineData(null, new byte[] {1}, 2, 4)] - [InlineData("test", null, 2, 4)] - [InlineData("test", new byte[] {1}, 0, 4)] - [InlineData("test", new byte[] {1}, 2, 0)] - public void CanSerializeAndDeserializeTimerInfo(string callback, byte[] state, int dueTime, int period) - { - var timerInfo = new TimerInfo(callback, state, TimeSpan.FromSeconds(dueTime), TimeSpan.FromSeconds(period)); + var timerInfo = new TimerInfo(callback, state, TimeSpan.FromSeconds(dueTime), TimeSpan.FromSeconds(period)); - // We use strings for ActorId - the result should be the same as passing the Id directly. - var serializedTimerInfo = JsonSerializer.Serialize(timerInfo); + // We use strings for ActorId - the result should be the same as passing the Id directly. + var serializedTimerInfo = JsonSerializer.Serialize(timerInfo); - var deserializedTimerInfo = JsonSerializer.Deserialize(serializedTimerInfo); + var deserializedTimerInfo = JsonSerializer.Deserialize(serializedTimerInfo); - Assert.Equal(timerInfo.Callback, deserializedTimerInfo.Callback); - Assert.Equal(timerInfo.Data, deserializedTimerInfo.Data); - Assert.Equal(timerInfo.DueTime, deserializedTimerInfo.DueTime); - Assert.Equal(timerInfo.Period, deserializedTimerInfo.Period); - } + Assert.Equal(timerInfo.Callback, deserializedTimerInfo.Callback); + Assert.Equal(timerInfo.Data, deserializedTimerInfo.Data); + Assert.Equal(timerInfo.DueTime, deserializedTimerInfo.DueTime); + Assert.Equal(timerInfo.Period, deserializedTimerInfo.Period); } } #pragma warning restore 0618 diff --git a/test/Dapr.Actors.Test/TestDaprInteractor.cs b/test/Dapr.Actors.Test/TestDaprInteractor.cs index 11f88e68..0ae6b762 100644 --- a/test/Dapr.Actors.Test/TestDaprInteractor.cs +++ b/test/Dapr.Actors.Test/TestDaprInteractor.cs @@ -1,159 +1,159 @@ using System.IO; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; using Dapr.Actors.Communication; -namespace Dapr.Actors +namespace Dapr.Actors; + +/// +/// A Wrapper class for IDaprInteractor which is mainly created for testing. +/// +public class TestDaprInteractor : IDaprInteractor { + private TestDaprInteractor _testDaprInteractor; + /// - /// A Wrapper class for IDaprInteractor which is mainly created for testing. + /// The TestDaprInteractor constructor. /// - public class TestDaprInteractor : IDaprInteractor + /// + public TestDaprInteractor(TestDaprInteractor testDaprInteractor) { - private TestDaprInteractor _testDaprInteractor; + _testDaprInteractor = testDaprInteractor; + } - /// - /// The TestDaprInteractor constructor. - /// - /// - public TestDaprInteractor(TestDaprInteractor testDaprInteractor) - { - _testDaprInteractor = testDaprInteractor; - } - - /// - /// The TestDaprInteractor constructor. - /// - public TestDaprInteractor() - { + /// + /// The TestDaprInteractor constructor. + /// + public TestDaprInteractor() + { - } + } - /// - /// Register a reminder. - /// - /// Type of actor. - /// ActorId. - /// Name of reminder to register. - /// JSON reminder data as per the Dapr spec. - /// Cancels the operation. - /// A representing the result of the asynchronous operation. - public virtual async Task RegisterReminderAsync(string actorType, string actorId, string reminderName, string data, - CancellationToken cancellationToken = default) - { - await _testDaprInteractor.RegisterReminderAsync(actorType, actorId, reminderName, data); - } + /// + /// Register a reminder. + /// + /// Type of actor. + /// ActorId. + /// Name of reminder to register. + /// JSON reminder data as per the Dapr spec. + /// Cancels the operation. + /// A representing the result of the asynchronous operation. + public virtual async Task RegisterReminderAsync(string actorType, string actorId, string reminderName, string data, + CancellationToken cancellationToken = default) + { + await _testDaprInteractor.RegisterReminderAsync(actorType, actorId, reminderName, data); + } - /// - /// Invokes an Actor method on Dapr without remoting. - /// - /// Type of actor. - /// ActorId. - /// Method name to invoke. - /// Serialized body. - /// Cancels the operation. - /// A task that represents the asynchronous operation. - public Task InvokeActorMethodWithoutRemotingAsync(string actorType, string actorId, string methodName, - string jsonPayload, CancellationToken cancellationToken = default) - { - throw new System.NotImplementedException(); - } + /// + /// Invokes an Actor method on Dapr without remoting. + /// + /// Type of actor. + /// ActorId. + /// Method name to invoke. + /// Serialized body. + /// Cancels the operation. + /// A task that represents the asynchronous operation. + public Task InvokeActorMethodWithoutRemotingAsync(string actorType, string actorId, string methodName, + string jsonPayload, CancellationToken cancellationToken = default) + { + throw new System.NotImplementedException(); + } - /// - /// Saves state batch to Dapr. - /// - /// Type of actor. - /// ActorId. - /// JSON data with state changes as per the Dapr spec for transaction state update. - /// Cancels the operation. - /// A task that represents the asynchronous operation. - public virtual async Task SaveStateTransactionallyAsync(string actorType, string actorId, string data, - CancellationToken cancellationToken = default) - { - await _testDaprInteractor.SaveStateTransactionallyAsync(actorType, actorId, data); - } + /// + /// Saves state batch to Dapr. + /// + /// Type of actor. + /// ActorId. + /// JSON data with state changes as per the Dapr spec for transaction state update. + /// Cancels the operation. + /// A task that represents the asynchronous operation. + public virtual async Task SaveStateTransactionallyAsync(string actorType, string actorId, string data, + CancellationToken cancellationToken = default) + { + await _testDaprInteractor.SaveStateTransactionallyAsync(actorType, actorId, data); + } - /// - /// Saves a state to Dapr. - /// - /// Type of actor. - /// ActorId. - /// Name of key to get value for. - /// Cancels the operation. - /// A task that represents the asynchronous operation. - public virtual async Task> GetStateAsync(string actorType, string actorId, string keyName, CancellationToken cancellationToken = default) - { - return await _testDaprInteractor.GetStateAsync(actorType, actorId, keyName); - } + /// + /// Saves a state to Dapr. + /// + /// Type of actor. + /// ActorId. + /// Name of key to get value for. + /// Cancels the operation. + /// A task that represents the asynchronous operation. + public virtual async Task> GetStateAsync(string actorType, string actorId, string keyName, CancellationToken cancellationToken = default) + { + return await _testDaprInteractor.GetStateAsync(actorType, actorId, keyName); + } - /// - /// Invokes Actor method. - /// - /// Serializers manager for remoting calls. - /// Actor Request Message. - /// Cancels the operation. - /// A representing the result of the asynchronous operation. - Task IDaprInteractor.InvokeActorMethodWithRemotingAsync(ActorMessageSerializersManager serializersManager, - IActorRequestMessage remotingRequestRequestMessage, CancellationToken cancellationToken) - { - throw new System.NotImplementedException(); - } + /// + /// Invokes Actor method. + /// + /// Serializers manager for remoting calls. + /// Actor Request Message. + /// Cancels the operation. + /// A representing the result of the asynchronous operation. + Task IDaprInteractor.InvokeActorMethodWithRemotingAsync(ActorMessageSerializersManager serializersManager, + IActorRequestMessage remotingRequestRequestMessage, CancellationToken cancellationToken) + { + throw new System.NotImplementedException(); + } - /// - /// Gets a reminder. - /// - /// Type of actor. - /// ActorId. - /// Name of reminder to unregister. - /// Cancels the operation. - /// A representing the result of the asynchronous operation. - public Task GetReminderAsync(string actorType, string actorId, string reminderName, - CancellationToken cancellationToken = default) - { - throw new System.NotImplementedException(); - } + /// + /// Gets a reminder. + /// + /// Type of actor. + /// ActorId. + /// Name of reminder to unregister. + /// Cancels the operation. + /// A representing the result of the asynchronous operation. + public virtual async Task GetReminderAsync(string actorType, string actorId, string reminderName, + CancellationToken cancellationToken = default) + { + return await _testDaprInteractor.GetReminderAsync(actorType, actorId, reminderName); + } - /// - /// Unregisters a reminder. - /// - /// Type of actor. - /// ActorId. - /// Name of reminder to unregister. - /// Cancels the operation. - /// A representing the result of the asynchronous operation. - public Task UnregisterReminderAsync(string actorType, string actorId, string reminderName, - CancellationToken cancellationToken = default) - { - throw new System.NotImplementedException(); - } + /// + /// Unregisters a reminder. + /// + /// Type of actor. + /// ActorId. + /// Name of reminder to unregister. + /// Cancels the operation. + /// A representing the result of the asynchronous operation. + public Task UnregisterReminderAsync(string actorType, string actorId, string reminderName, + CancellationToken cancellationToken = default) + { + throw new System.NotImplementedException(); + } - /// - /// Registers a timer. - /// - /// Type of actor. - /// ActorId. - /// Name of timer to register. - /// JSON reminder data as per the Dapr spec. - /// Cancels the operation. - /// A representing the result of the asynchronous operation. - public Task RegisterTimerAsync(string actorType, string actorId, string timerName, string data, - CancellationToken cancellationToken = default) - { - throw new System.NotImplementedException(); - } + /// + /// Registers a timer. + /// + /// Type of actor. + /// ActorId. + /// Name of timer to register. + /// JSON reminder data as per the Dapr spec. + /// Cancels the operation. + /// A representing the result of the asynchronous operation. + public Task RegisterTimerAsync(string actorType, string actorId, string timerName, string data, + CancellationToken cancellationToken = default) + { + throw new System.NotImplementedException(); + } - /// - /// Unregisters a timer. - /// - /// Type of actor. - /// ActorId. - /// Name of timer to register. - /// Cancels the operation. - /// A representing the result of the asynchronous operation. - public Task UnregisterTimerAsync(string actorType, string actorId, string timerName, - CancellationToken cancellationToken = default) - { - throw new System.NotImplementedException(); - } + /// + /// Unregisters a timer. + /// + /// Type of actor. + /// ActorId. + /// Name of timer to register. + /// Cancels the operation. + /// A representing the result of the asynchronous operation. + public Task UnregisterTimerAsync(string actorType, string actorId, string timerName, + CancellationToken cancellationToken = default) + { + throw new System.NotImplementedException(); } } diff --git a/test/Dapr.AspNetCore.IntegrationTest/AuthenticationTest.cs b/test/Dapr.AspNetCore.IntegrationTest/AuthenticationTest.cs index 2712779e..3684e709 100644 --- a/test/Dapr.AspNetCore.IntegrationTest/AuthenticationTest.cs +++ b/test/Dapr.AspNetCore.IntegrationTest/AuthenticationTest.cs @@ -16,7 +16,7 @@ using System.Net.Http; using System.Text; using System.Threading.Tasks; using Dapr.AspNetCore.IntegrationTest.App; -using FluentAssertions; +using Shouldly; using Newtonsoft.Json; using Xunit; @@ -43,7 +43,7 @@ namespace Dapr.AspNetCore.IntegrationTest response.EnsureSuccessStatusCode(); var responseContent = await response.Content.ReadAsStringAsync(); var responseUserInfo = JsonConvert.DeserializeObject(responseContent); - responseUserInfo.Name.Should().Be(userInfo.Name); + responseUserInfo.Name.ShouldBe(userInfo.Name); } } @@ -64,7 +64,7 @@ namespace Dapr.AspNetCore.IntegrationTest request.Headers.Add("Dapr-Api-Token", "asdfgh"); var response = await httpClient.SendAsync(request); - response.StatusCode.Should().Be(HttpStatusCode.Unauthorized); + response.StatusCode.ShouldBe(HttpStatusCode.Unauthorized); } } } diff --git a/test/Dapr.AspNetCore.IntegrationTest/CloudEventsIntegrationTest.cs b/test/Dapr.AspNetCore.IntegrationTest/CloudEventsIntegrationTest.cs index 9b0b5d3a..928286fb 100644 --- a/test/Dapr.AspNetCore.IntegrationTest/CloudEventsIntegrationTest.cs +++ b/test/Dapr.AspNetCore.IntegrationTest/CloudEventsIntegrationTest.cs @@ -19,7 +19,7 @@ namespace Dapr.AspNetCore.IntegrationTest using System.Text.Json; using System.Threading.Tasks; using Dapr.AspNetCore.IntegrationTest.App; - using FluentAssertions; + using Shouldly; using Xunit; public class CloudEventsIntegrationTest @@ -48,6 +48,7 @@ namespace Dapr.AspNetCore.IntegrationTest } } + [Fact] public async Task CanSendStructuredCloudEvent() { @@ -74,7 +75,7 @@ namespace Dapr.AspNetCore.IntegrationTest response.EnsureSuccessStatusCode(); var userInfo = await JsonSerializer.DeserializeAsync(await response.Content.ReadAsStreamAsync(), this.options); - userInfo.Name.Should().Be("jimmy"); + userInfo.Name.ShouldBe("jimmy"); } } @@ -105,7 +106,7 @@ namespace Dapr.AspNetCore.IntegrationTest response.EnsureSuccessStatusCode(); var userInfo = await JsonSerializer.DeserializeAsync(await response.Content.ReadAsStreamAsync(), this.options); - userInfo.Name.Should().Be("jimmy"); + userInfo.Name.ShouldBe("jimmy"); } } @@ -133,7 +134,7 @@ namespace Dapr.AspNetCore.IntegrationTest response.EnsureSuccessStatusCode(); var user = await response.Content.ReadAsStringAsync(); - user.Should().Be("jimmy \"the cool guy\" smith"); + user.ShouldBe("jimmy \"the cool guy\" smith"); } } @@ -163,7 +164,7 @@ namespace Dapr.AspNetCore.IntegrationTest response.EnsureSuccessStatusCode(); var userInfo = await JsonSerializer.DeserializeAsync(await response.Content.ReadAsStreamAsync(), this.options); - userInfo.Name.Should().Be("jimmy"); + userInfo.Name.ShouldBe("jimmy"); } } } diff --git a/test/Dapr.AspNetCore.IntegrationTest/ControllerIntegrationTest.cs b/test/Dapr.AspNetCore.IntegrationTest/ControllerIntegrationTest.cs index 7735ec4f..d3eb87cd 100644 --- a/test/Dapr.AspNetCore.IntegrationTest/ControllerIntegrationTest.cs +++ b/test/Dapr.AspNetCore.IntegrationTest/ControllerIntegrationTest.cs @@ -16,7 +16,7 @@ namespace Dapr.AspNetCore.IntegrationTest using System.Net.Http; using System.Threading.Tasks; using Dapr.AspNetCore.IntegrationTest.App; - using FluentAssertions; + using Shouldly; using Newtonsoft.Json; using Xunit; @@ -37,7 +37,7 @@ namespace Dapr.AspNetCore.IntegrationTest response.EnsureSuccessStatusCode(); var widget = await daprClient.GetStateAsync("testStore", "test"); - widget.Count.Should().Be(18); + widget.Count.ShouldBe(18); } } @@ -56,8 +56,8 @@ namespace Dapr.AspNetCore.IntegrationTest response.EnsureSuccessStatusCode(); var responseContent = await response.Content.ReadAsStringAsync(); var responseWidget = JsonConvert.DeserializeObject(responseContent); - responseWidget.Size.Should().Be(widget.Size); - responseWidget.Count.Should().Be(widget.Count); + responseWidget.Size.ShouldBe(widget.Size); + responseWidget.Count.ShouldBe(widget.Count); } } @@ -92,7 +92,7 @@ namespace Dapr.AspNetCore.IntegrationTest response.EnsureSuccessStatusCode(); var widget = await daprClient.GetStateAsync("testStore", "test"); - widget.Count.Should().Be(18); + widget.Count.ShouldBe(18); } } @@ -111,7 +111,7 @@ namespace Dapr.AspNetCore.IntegrationTest response.EnsureSuccessStatusCode(); var widget = await daprClient.GetStateAsync("testStore", "test"); - widget.Count.Should().Be(18); + widget.Count.ShouldBe(18); } } @@ -130,8 +130,8 @@ namespace Dapr.AspNetCore.IntegrationTest response.EnsureSuccessStatusCode(); var responseContent = await response.Content.ReadAsStringAsync(); var responseWidget = JsonConvert.DeserializeObject(responseContent); - responseWidget.Size.Should().Be(widget.Size); - responseWidget.Count.Should().Be(widget.Count); + responseWidget.Size.ShouldBe(widget.Size); + responseWidget.Count.ShouldBe(widget.Count); } } diff --git a/test/Dapr.AspNetCore.IntegrationTest/Dapr.AspNetCore.IntegrationTest.csproj b/test/Dapr.AspNetCore.IntegrationTest/Dapr.AspNetCore.IntegrationTest.csproj index d7dd6d52..5772612b 100644 --- a/test/Dapr.AspNetCore.IntegrationTest/Dapr.AspNetCore.IntegrationTest.csproj +++ b/test/Dapr.AspNetCore.IntegrationTest/Dapr.AspNetCore.IntegrationTest.csproj @@ -5,7 +5,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all - + diff --git a/test/Dapr.AspNetCore.IntegrationTest/RoutingIntegrationTest.cs b/test/Dapr.AspNetCore.IntegrationTest/RoutingIntegrationTest.cs index c3129df9..89130c3b 100644 --- a/test/Dapr.AspNetCore.IntegrationTest/RoutingIntegrationTest.cs +++ b/test/Dapr.AspNetCore.IntegrationTest/RoutingIntegrationTest.cs @@ -16,7 +16,7 @@ namespace Dapr.AspNetCore.IntegrationTest using System.Net.Http; using System.Threading.Tasks; using Dapr.AspNetCore.IntegrationTest.App; - using FluentAssertions; + using Shouldly; using Xunit; public class RoutingIntegrationTest @@ -36,7 +36,7 @@ namespace Dapr.AspNetCore.IntegrationTest response.EnsureSuccessStatusCode(); var widget = await daprClient.GetStateAsync("testStore", "test"); - widget.Count.Should().Be(18); + widget.Count.ShouldBe(18); } } } diff --git a/test/Dapr.AspNetCore.IntegrationTest/SubscribeEndpointTest.cs b/test/Dapr.AspNetCore.IntegrationTest/SubscribeEndpointTest.cs index 4cd0d4f1..7354f6aa 100644 --- a/test/Dapr.AspNetCore.IntegrationTest/SubscribeEndpointTest.cs +++ b/test/Dapr.AspNetCore.IntegrationTest/SubscribeEndpointTest.cs @@ -19,7 +19,7 @@ namespace Dapr.AspNetCore.IntegrationTest using System.Net.Http; using System.Text.Json; using System.Threading.Tasks; - using FluentAssertions; + using Shouldly; using Xunit; public class SubscribeEndpointTest @@ -39,8 +39,8 @@ namespace Dapr.AspNetCore.IntegrationTest { var json = await JsonSerializer.DeserializeAsync(stream); - json.ValueKind.Should().Be(JsonValueKind.Array); - json.GetArrayLength().Should().Be(18); + json.ValueKind.ShouldBe(JsonValueKind.Array); + json.GetArrayLength().ShouldBe(18); var subscriptions = new List<(string PubsubName, string Topic, string Route, string rawPayload, string match, string metadata, string DeadLetterTopic, string bulkSubscribeMetadata)>(); @@ -109,34 +109,34 @@ namespace Dapr.AspNetCore.IntegrationTest } } - subscriptions.Should().Contain(("testpubsub", "A", "topic-a", string.Empty, string.Empty, string.Empty, string.Empty, String.Empty)); - subscriptions.Should().Contain(("testpubsub", "A.1", "topic-a", string.Empty, string.Empty, string.Empty, string.Empty, String.Empty)); - subscriptions.Should().Contain(("pubsub", "B", "B", string.Empty, string.Empty, string.Empty, string.Empty, String.Empty)); - subscriptions.Should().Contain(("custom-pubsub", "custom-C", "C", string.Empty, string.Empty, string.Empty, string.Empty, String.Empty)); - subscriptions.Should().Contain(("pubsub", "register-user", "register-user", string.Empty, string.Empty, string.Empty, string.Empty, String.Empty)); - subscriptions.Should().Contain(("pubsub", "register-user-plaintext", "register-user-plaintext", string.Empty, string.Empty, string.Empty, string.Empty, String.Empty)); - subscriptions.Should().Contain(("pubsub", "D", "D", "true", string.Empty, string.Empty, string.Empty, String.Empty)); - subscriptions.Should().Contain(("pubsub", "E", "E", string.Empty, string.Empty, string.Empty, string.Empty, String.Empty)); - subscriptions.Should().Contain(("pubsub", "E", "E-Critical", string.Empty, "event.type == \"critical\"", string.Empty, string.Empty, String.Empty)); - subscriptions.Should().Contain(("pubsub", "E", "E-Important", string.Empty, "event.type == \"important\"", string.Empty, string.Empty, String.Empty)); - subscriptions.Should().Contain(("pubsub", "F", "multiTopicAttr", string.Empty, string.Empty, string.Empty, string.Empty, + subscriptions.ShouldContain(("testpubsub", "A", "topic-a", string.Empty, string.Empty, string.Empty, string.Empty, String.Empty)); + subscriptions.ShouldContain(("testpubsub", "A.1", "topic-a", string.Empty, string.Empty, string.Empty, string.Empty, String.Empty)); + subscriptions.ShouldContain(("pubsub", "B", "B", string.Empty, string.Empty, string.Empty, string.Empty, String.Empty)); + subscriptions.ShouldContain(("custom-pubsub", "custom-C", "C", string.Empty, string.Empty, string.Empty, string.Empty, String.Empty)); + subscriptions.ShouldContain(("pubsub", "register-user", "register-user", string.Empty, string.Empty, string.Empty, string.Empty, String.Empty)); + subscriptions.ShouldContain(("pubsub", "register-user-plaintext", "register-user-plaintext", string.Empty, string.Empty, string.Empty, string.Empty, String.Empty)); + subscriptions.ShouldContain(("pubsub", "D", "D", "true", string.Empty, string.Empty, string.Empty, String.Empty)); + subscriptions.ShouldContain(("pubsub", "E", "E", string.Empty, string.Empty, string.Empty, string.Empty, String.Empty)); + subscriptions.ShouldContain(("pubsub", "E", "E-Critical", string.Empty, "event.type == \"critical\"", string.Empty, string.Empty, String.Empty)); + subscriptions.ShouldContain(("pubsub", "E", "E-Important", string.Empty, "event.type == \"important\"", string.Empty, string.Empty, String.Empty)); + subscriptions.ShouldContain(("pubsub", "F", "multiTopicAttr", string.Empty, string.Empty, string.Empty, string.Empty, "{\"enabled\":true,\"maxMessagesCount\":100,\"maxAwaitDurationMs\":1000}")); - subscriptions.Should().Contain(("pubsub", "F.1", "multiTopicAttr", "true", string.Empty, string.Empty, string.Empty, String.Empty)); - subscriptions.Should().Contain(("pubsub", "G", "G", string.Empty, string.Empty, string.Empty, "deadLetterTopicName", + subscriptions.ShouldContain(("pubsub", "F.1", "multiTopicAttr", "true", string.Empty, string.Empty, string.Empty, String.Empty)); + subscriptions.ShouldContain(("pubsub", "G", "G", string.Empty, string.Empty, string.Empty, "deadLetterTopicName", "{\"enabled\":true,\"maxMessagesCount\":300,\"maxAwaitDurationMs\":1000}")); - subscriptions.Should().Contain(("pubsub", "splitTopicBuilder", "splitTopics", string.Empty, string.Empty, string.Empty, string.Empty, String.Empty)); - subscriptions.Should().Contain(("pubsub", "splitTopicAttr", "splitTopics", "true", string.Empty, string.Empty, string.Empty, String.Empty)); - subscriptions.Should().Contain(("pubsub", "metadata", "multiMetadataTopicAttr", string.Empty, string.Empty, "n1=v1;n2=v2,v3", string.Empty, String.Empty)); - subscriptions.Should().Contain(("pubsub", "metadata.1", "multiMetadataTopicAttr", "true", string.Empty, "n1=v1", string.Empty, + subscriptions.ShouldContain(("pubsub", "splitTopicBuilder", "splitTopics", string.Empty, string.Empty, string.Empty, string.Empty, String.Empty)); + subscriptions.ShouldContain(("pubsub", "splitTopicAttr", "splitTopics", "true", string.Empty, string.Empty, string.Empty, String.Empty)); + subscriptions.ShouldContain(("pubsub", "metadata", "multiMetadataTopicAttr", string.Empty, string.Empty, "n1=v1;n2=v2,v3", string.Empty, String.Empty)); + subscriptions.ShouldContain(("pubsub", "metadata.1", "multiMetadataTopicAttr", "true", string.Empty, "n1=v1", string.Empty, "{\"enabled\":true,\"maxMessagesCount\":500,\"maxAwaitDurationMs\":2000}")); - subscriptions.Should().Contain(("pubsub", "splitMetadataTopicBuilder", "splitMetadataTopics", string.Empty, string.Empty, "n1=v1;n2=v1", string.Empty, String.Empty)); - subscriptions.Should().Contain(("pubsub", "metadataseparatorbyemptytring", "topicmetadataseparatorattrbyemptytring", string.Empty, string.Empty, "n1=v1,", string.Empty, String.Empty)); + subscriptions.ShouldContain(("pubsub", "splitMetadataTopicBuilder", "splitMetadataTopics", string.Empty, string.Empty, "n1=v1;n2=v1", string.Empty, String.Empty)); + subscriptions.ShouldContain(("pubsub", "metadataseparatorbyemptytring", "topicmetadataseparatorattrbyemptytring", string.Empty, string.Empty, "n1=v1,", string.Empty, String.Empty)); // Test priority route sorting var eTopic = subscriptions.FindAll(e => e.Topic == "E"); - eTopic.Count.Should().Be(3); - eTopic[0].Route.Should().Be("E-Critical"); - eTopic[1].Route.Should().Be("E-Important"); - eTopic[2].Route.Should().Be("E"); + eTopic.Count.ShouldBe(3); + eTopic[0].Route.ShouldBe("E-Critical"); + eTopic[1].Route.ShouldBe("E-Important"); + eTopic[2].Route.ShouldBe("E"); } } } diff --git a/test/Dapr.AspNetCore.Test/CloudEventsMiddlewareTest.cs b/test/Dapr.AspNetCore.Test/CloudEventsMiddlewareTest.cs index 2f9fab93..ae8db689 100644 --- a/test/Dapr.AspNetCore.Test/CloudEventsMiddlewareTest.cs +++ b/test/Dapr.AspNetCore.Test/CloudEventsMiddlewareTest.cs @@ -20,7 +20,7 @@ namespace Dapr.AspNetCore.Test using System.Text; using System.Text.Json; using System.Threading.Tasks; - using FluentAssertions; + using Shouldly; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Xunit; @@ -44,8 +44,8 @@ namespace Dapr.AspNetCore.Test // Do verification in the scope of the middleware app.Run(httpContext => { - httpContext.Request.ContentType.Should().Be(contentType); - ReadBody(httpContext.Request.Body).Should().Be("Hello, world!"); + httpContext.Request.ContentType.ShouldBe(contentType); + ReadBody(httpContext.Request.Body).ShouldBe("Hello, world!"); return Task.CompletedTask; }); @@ -77,8 +77,8 @@ namespace Dapr.AspNetCore.Test // Do verification in the scope of the middleware app.Run(httpContext => { - httpContext.Request.ContentType.Should().Be(dataContentType ?? "application/json"); - ReadBody(httpContext.Request.Body).Should().Be("{\"name\":\"jimmy\"}"); + httpContext.Request.ContentType.ShouldBe(dataContentType ?? "application/json"); + ReadBody(httpContext.Request.Body).ShouldBe("{\"name\":\"jimmy\"}"); return Task.CompletedTask; }); @@ -117,8 +117,8 @@ namespace Dapr.AspNetCore.Test // Do verification in the scope of the middleware app.Run(httpContext => { - httpContext.Request.ContentType.Should().Be(dataContentType ?? "application/json"); - ReadBody(httpContext.Request.Body).Should().Be("{\"name\":\"jimmy\"}"); + httpContext.Request.ContentType.ShouldBe(dataContentType ?? "application/json"); + ReadBody(httpContext.Request.Body).ShouldBe("{\"name\":\"jimmy\"}"); return Task.CompletedTask; }); @@ -160,11 +160,13 @@ namespace Dapr.AspNetCore.Test // Do verification in the scope of the middleware app.Run(httpContext => { - httpContext.Request.ContentType.Should().Be(dataContentType ?? "application/json"); - ReadBody(httpContext.Request.Body).Should().Be("{\"name\":\"jimmy\"}"); + httpContext.Request.ContentType.ShouldBe(dataContentType ?? "application/json"); + ReadBody(httpContext.Request.Body).ShouldBe("{\"name\":\"jimmy\"}"); - httpContext.Request.Headers.Should().ContainKey("Cloudevent.type").WhichValue.Should().BeEquivalentTo("Test.Type"); - httpContext.Request.Headers.Should().ContainKey("Cloudevent.subject").WhichValue.Should().BeEquivalentTo("Test.Subject"); + httpContext.Request.Headers.ShouldContainKey("Cloudevent.type"); + httpContext.Request.Headers["Cloudevent.type"].ToString().ShouldBe("Test.Type"); + httpContext.Request.Headers.ShouldContainKey("Cloudevent.subject"); + httpContext.Request.Headers["Cloudevent.subject"].ToString().ShouldBe("Test.Subject"); return Task.CompletedTask; }); @@ -207,11 +209,12 @@ namespace Dapr.AspNetCore.Test // Do verification in the scope of the middleware app.Run(httpContext => { - httpContext.Request.ContentType.Should().Be(dataContentType ?? "application/json"); - ReadBody(httpContext.Request.Body).Should().Be("{\"name\":\"jimmy\"}"); + httpContext.Request.ContentType.ShouldBe(dataContentType ?? "application/json"); + ReadBody(httpContext.Request.Body).ShouldBe("{\"name\":\"jimmy\"}"); - httpContext.Request.Headers.Should().ContainKey("Cloudevent.type").WhichValue.Should().BeEquivalentTo("Test.Type"); - httpContext.Request.Headers.Should().NotContainKey("Cloudevent.subject"); + httpContext.Request.Headers.ShouldContainKey("Cloudevent.type"); + httpContext.Request.Headers["Cloudevent.type"].ToString().ShouldBe("Test.Type"); + httpContext.Request.Headers.ShouldNotContainKey("Cloudevent.subject"); return Task.CompletedTask; }); @@ -254,11 +257,12 @@ namespace Dapr.AspNetCore.Test // Do verification in the scope of the middleware app.Run(httpContext => { - httpContext.Request.ContentType.Should().Be(dataContentType ?? "application/json"); - ReadBody(httpContext.Request.Body).Should().Be("{\"name\":\"jimmy\"}"); + httpContext.Request.ContentType.ShouldBe(dataContentType ?? "application/json"); + ReadBody(httpContext.Request.Body).ShouldBe("{\"name\":\"jimmy\"}"); - httpContext.Request.Headers.Should().NotContainKey("Cloudevent.type"); - httpContext.Request.Headers.Should().ContainKey("Cloudevent.subject").WhichValue.Should().BeEquivalentTo("Test.Subject"); + httpContext.Request.Headers.ShouldNotContainKey("Cloudevent.type"); + httpContext.Request.Headers.ShouldContainKey("Cloudevent.subject"); + httpContext.Request.Headers["Cloudevent.subject"].ToString().ShouldBe("Test.Subject"); return Task.CompletedTask; }); @@ -297,8 +301,8 @@ namespace Dapr.AspNetCore.Test // Do verification in the scope of the middleware app.Run(httpContext => { - httpContext.Request.ContentType.Should().Be("text/plain"); - ReadBody(httpContext.Request.Body).Should().Be(expected); + httpContext.Request.ContentType.ShouldBe("text/plain"); + ReadBody(httpContext.Request.Body).ShouldBe(expected); return Task.CompletedTask; }); @@ -331,8 +335,8 @@ namespace Dapr.AspNetCore.Test // Do verification in the scope of the middleware app.Run(httpContext => { - httpContext.Request.ContentType.Should().Be("text/plain"); - ReadBody(httpContext.Request.Body).Should().Be(expected); + httpContext.Request.ContentType.ShouldBe("text/plain"); + ReadBody(httpContext.Request.Body).ShouldBe(expected); return Task.CompletedTask; }); @@ -366,8 +370,8 @@ namespace Dapr.AspNetCore.Test // Do verification in the scope of the middleware app.Run(httpContext => { - httpContext.Request.ContentType.Should().Be("application/person+json"); - ReadBody(httpContext.Request.Body).Should().Be("{\"name\":\"jimmy\"}"); + httpContext.Request.ContentType.ShouldBe("application/person+json"); + ReadBody(httpContext.Request.Body).ShouldBe("{\"name\":\"jimmy\"}"); return Task.CompletedTask; }); @@ -396,14 +400,14 @@ namespace Dapr.AspNetCore.Test // Do verification in the scope of the middleware app.Run(httpContext => { - httpContext.Request.ContentType.Should().Be(dataContentType); + httpContext.Request.ContentType.ShouldBe(dataContentType); var bytes = new byte[httpContext.Request.Body.Length]; #if NET9_0 httpContext.Request.Body.ReadExactly(bytes, 0, bytes.Length); #else httpContext.Request.Body.Read(bytes, 0, bytes.Length); #endif - bytes.Should().Equal(data); + bytes.ShouldBe(data); return Task.CompletedTask; }); @@ -432,9 +436,9 @@ namespace Dapr.AspNetCore.Test // Do verification in the scope of the middleware app.Run(httpContext => { - httpContext.Request.ContentType.Should().Be("application/json"); + httpContext.Request.ContentType.ShouldBe("application/json"); var body = ReadBody(httpContext.Request.Body); - body.Should().Equals(data); + body.ShouldBe(data); return Task.CompletedTask; }); @@ -447,7 +451,7 @@ namespace Dapr.AspNetCore.Test MakeBody($"{{ \"datacontenttype\": \"{dataContentType}\", \"data_base64\": \"{base64Str}\", \"data\": {data} }}"); await pipeline.Invoke(context); - context.Response.StatusCode.Should().Be((int)HttpStatusCode.BadRequest); + context.Response.StatusCode.ShouldBe((int)HttpStatusCode.BadRequest); } private static Stream MakeBody(string text, Encoding encoding = null) diff --git a/test/Dapr.AspNetCore.Test/Dapr.AspNetCore.Test.csproj b/test/Dapr.AspNetCore.Test/Dapr.AspNetCore.Test.csproj index 9135e63d..b25fcec6 100644 --- a/test/Dapr.AspNetCore.Test/Dapr.AspNetCore.Test.csproj +++ b/test/Dapr.AspNetCore.Test/Dapr.AspNetCore.Test.csproj @@ -5,8 +5,8 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all - + all diff --git a/test/Dapr.AspNetCore.Test/StateEntryApplicationModelProviderTest.cs b/test/Dapr.AspNetCore.Test/StateEntryApplicationModelProviderTest.cs index 06572caa..243b60bb 100644 --- a/test/Dapr.AspNetCore.Test/StateEntryApplicationModelProviderTest.cs +++ b/test/Dapr.AspNetCore.Test/StateEntryApplicationModelProviderTest.cs @@ -18,7 +18,7 @@ namespace Dapr.AspNetCore.Test using System.Linq; using System.Reflection; using Dapr.AspNetCore.Resources; - using FluentAssertions; + using Shouldly; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ApplicationModels; using Microsoft.AspNetCore.Mvc.ModelBinding; @@ -34,9 +34,7 @@ namespace Dapr.AspNetCore.Test Action action = () => provider.OnProvidersExecuted(context); - action - .Should() - .NotThrow(); + action.ShouldNotThrow(); } [Fact] @@ -48,8 +46,7 @@ namespace Dapr.AspNetCore.Test Action action = () => provider.OnProvidersExecuted(context); action - .Should() - .Throw(SR.ErrorStateStoreNameNotProvidedForStateEntry); + .ShouldThrow(SR.ErrorStateStoreNameNotProvidedForStateEntry); } private ApplicationModelProviderContext CreateContext(string methodName) diff --git a/test/Dapr.AspNetCore.Test/StateEntryModelBinderTest.cs b/test/Dapr.AspNetCore.Test/StateEntryModelBinderTest.cs index e2d77439..8cdcd32d 100644 --- a/test/Dapr.AspNetCore.Test/StateEntryModelBinderTest.cs +++ b/test/Dapr.AspNetCore.Test/StateEntryModelBinderTest.cs @@ -18,7 +18,7 @@ namespace Dapr.AspNetCore.Test using System.Threading.Tasks; using Dapr.Client; using Dapr.Client.Autogen.Grpc.v1; - using FluentAssertions; + using Shouldly; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ModelBinding; @@ -38,9 +38,9 @@ namespace Dapr.AspNetCore.Test await binder.BindModelAsync(context); - context.Result.IsModelSet.Should().BeFalse(); - context.ModelState.ErrorCount.Should().Be(1); - context.ModelState["testParameter"].Errors.Count.Should().Be(1); + context.Result.IsModelSet.ShouldBeFalse(); + context.ModelState.ErrorCount.ShouldBe(1); + context.ModelState["testParameter"].Errors.Count.ShouldBe(1); // No request to state store, validated by disposing client } @@ -66,12 +66,12 @@ namespace Dapr.AspNetCore.Test await SendResponseWithState(state, request); // Get response and validate - context.Result.IsModelSet.Should().BeTrue(); - context.Result.Model.As().Size.Should().Be("small"); - context.Result.Model.As().Color.Should().Be("yellow"); + context.Result.IsModelSet.ShouldBeTrue(); + ((Widget)context.Result.Model).Size.ShouldBe("small"); + ((Widget)context.Result.Model).Color.ShouldBe("yellow"); - context.ValidationState.Count.Should().Be(1); - context.ValidationState[context.Result.Model].SuppressValidation.Should().BeTrue(); + context.ValidationState.Count.ShouldBe(1); + context.ValidationState[context.Result.Model].SuppressValidation.ShouldBeTrue(); } [Fact] @@ -95,13 +95,13 @@ namespace Dapr.AspNetCore.Test await SendResponseWithState(state, request); // Get response and validate - context.Result.IsModelSet.Should().BeTrue(); - context.Result.Model.As>().Key.Should().Be("test"); - context.Result.Model.As>().Value.Size.Should().Be("small"); - context.Result.Model.As>().Value.Color.Should().Be("yellow"); + context.Result.IsModelSet.ShouldBeTrue(); + ((StateEntry)context.Result.Model).Key.ShouldBe("test"); + ((StateEntry)context.Result.Model).Value.Size.ShouldBe("small"); + ((StateEntry)context.Result.Model).Value.Color.ShouldBe("yellow"); - context.ValidationState.Count.Should().Be(1); - context.ValidationState[context.Result.Model].SuppressValidation.Should().BeTrue(); + context.ValidationState.Count.ShouldBe(1); + context.ValidationState[context.Result.Model].SuppressValidation.ShouldBeTrue(); } [Fact] @@ -122,9 +122,9 @@ namespace Dapr.AspNetCore.Test await SendResponseWithState(null, request); - context.ModelState.IsValid.Should().BeTrue(); - context.Result.IsModelSet.Should().BeFalse(); - context.Result.Should().Be(ModelBindingResult.Failed()); + context.ModelState.IsValid.ShouldBeTrue(); + context.Result.IsModelSet.ShouldBeFalse(); + context.Result.ShouldBe(ModelBindingResult.Failed()); } [Fact] @@ -145,9 +145,9 @@ namespace Dapr.AspNetCore.Test await SendResponseWithState(null, request); - context.ModelState.IsValid.Should().BeTrue(); - context.Result.IsModelSet.Should().BeTrue(); - ((StateEntry)context.Result.Model).Value.Should().BeNull(); + context.ModelState.IsValid.ShouldBeTrue(); + context.Result.IsModelSet.ShouldBeTrue(); + ((StateEntry)context.Result.Model).Value.ShouldBeNull(); } private static ModelBindingContext CreateContext(IServiceProvider services) diff --git a/test/Dapr.Client.Test/BulkPublishEventApiTest.cs b/test/Dapr.Client.Test/BulkPublishEventApiTest.cs index 74d617a2..14b946d6 100644 --- a/test/Dapr.Client.Test/BulkPublishEventApiTest.cs +++ b/test/Dapr.Client.Test/BulkPublishEventApiTest.cs @@ -19,7 +19,7 @@ namespace Dapr.Client.Test using System.Threading; using System.Threading.Tasks; using Autogenerated = Dapr.Client.Autogen.Grpc.v1; - using FluentAssertions; + using Shouldly; using Grpc.Core; using Moq; using Xunit; @@ -43,26 +43,24 @@ namespace Dapr.Client.Test var envelope = await request.GetRequestEnvelopeAsync(); - envelope.Entries.Count.Should().Be(2); - envelope.PubsubName.Should().Be(TestPubsubName); - envelope.Topic.Should().Be(TestTopicName); - envelope.Metadata.Count.Should().Be(0); + envelope.Entries.Count.ShouldBe(2); + envelope.PubsubName.ShouldBe(TestPubsubName); + envelope.Topic.ShouldBe(TestTopicName); + envelope.Metadata.Count.ShouldBe(0); var firstEntry = envelope.Entries[0]; - firstEntry.EntryId.Should().Be("0"); - firstEntry.ContentType.Should().Be(TestContentType); - firstEntry.Event.ToStringUtf8().Should() - .Be(JsonSerializer.Serialize(bulkPublishData[0], client.InnerClient.JsonSerializerOptions)); - firstEntry.Metadata.Should().BeEmpty(); + firstEntry.EntryId.ShouldBe("0"); + firstEntry.ContentType.ShouldBe(TestContentType); + firstEntry.Event.ToStringUtf8().ShouldBe(JsonSerializer.Serialize(bulkPublishData[0], client.InnerClient.JsonSerializerOptions)); + firstEntry.Metadata.ShouldBeEmpty(); var secondEntry = envelope.Entries[1]; - secondEntry.EntryId.Should().Be("1"); - secondEntry.ContentType.Should().Be(TestContentType); - secondEntry.Event.ToStringUtf8().Should() - .Be(JsonSerializer.Serialize(bulkPublishData[1], client.InnerClient.JsonSerializerOptions)); - secondEntry.Metadata.Should().BeEmpty(); + secondEntry.EntryId.ShouldBe("1"); + secondEntry.ContentType.ShouldBe(TestContentType); + secondEntry.Event.ToStringUtf8().ShouldBe(JsonSerializer.Serialize(bulkPublishData[1], client.InnerClient.JsonSerializerOptions)); + secondEntry.Metadata.ShouldBeEmpty(); // Create Response & Respond var response = new Autogenerated.BulkPublishResponse @@ -72,7 +70,7 @@ namespace Dapr.Client.Test var bulkPublishResponse = await request.CompleteWithMessageAsync(response); // Get response and validate - bulkPublishResponse.FailedEntries.Count.Should().Be(0); + bulkPublishResponse.FailedEntries.Count.ShouldBe(0); } [Fact] @@ -90,31 +88,29 @@ namespace Dapr.Client.Test var envelope = await request.GetRequestEnvelopeAsync(); - envelope.Entries.Count.Should().Be(2); - envelope.PubsubName.Should().Be(TestPubsubName); - envelope.Topic.Should().Be(TestTopicName); + envelope.Entries.Count.ShouldBe(2); + envelope.PubsubName.ShouldBe(TestPubsubName); + envelope.Topic.ShouldBe(TestTopicName); - envelope.Metadata.Count.Should().Be(2); - envelope.Metadata.Keys.Contains("key1").Should().BeTrue(); - envelope.Metadata.Keys.Contains("key2").Should().BeTrue(); - envelope.Metadata["key1"].Should().Be("value1"); - envelope.Metadata["key2"].Should().Be("value2"); + envelope.Metadata.Count.ShouldBe(2); + envelope.Metadata.Keys.Contains("key1").ShouldBeTrue(); + envelope.Metadata.Keys.Contains("key2").ShouldBeTrue(); + envelope.Metadata["key1"].ShouldBe("value1"); + envelope.Metadata["key2"].ShouldBe("value2"); var firstEntry = envelope.Entries[0]; - firstEntry.EntryId.Should().Be("0"); - firstEntry.ContentType.Should().Be(TestContentType); - firstEntry.Event.ToStringUtf8().Should() - .Be(JsonSerializer.Serialize(bulkPublishData[0], client.InnerClient.JsonSerializerOptions)); - firstEntry.Metadata.Should().BeEmpty(); + firstEntry.EntryId.ShouldBe("0"); + firstEntry.ContentType.ShouldBe(TestContentType); + firstEntry.Event.ToStringUtf8().ShouldBe(JsonSerializer.Serialize(bulkPublishData[0], client.InnerClient.JsonSerializerOptions)); + firstEntry.Metadata.ShouldBeEmpty(); var secondEntry = envelope.Entries[1]; - secondEntry.EntryId.Should().Be("1"); - secondEntry.ContentType.Should().Be(TestContentType); - secondEntry.Event.ToStringUtf8().Should() - .Be(JsonSerializer.Serialize(bulkPublishData[1], client.InnerClient.JsonSerializerOptions)); - secondEntry.Metadata.Should().BeEmpty(); + secondEntry.EntryId.ShouldBe("1"); + secondEntry.ContentType.ShouldBe(TestContentType); + secondEntry.Event.ToStringUtf8().ShouldBe(JsonSerializer.Serialize(bulkPublishData[1], client.InnerClient.JsonSerializerOptions)); + secondEntry.Metadata.ShouldBeEmpty(); // Create Response & Respond var response = new Autogenerated.BulkPublishResponse @@ -124,7 +120,7 @@ namespace Dapr.Client.Test var bulkPublishResponse = await request.CompleteWithMessageAsync(response); // Get response and validate - bulkPublishResponse.FailedEntries.Count.Should().Be(0); + bulkPublishResponse.FailedEntries.Count.ShouldBe(0); } [Fact] @@ -139,26 +135,24 @@ namespace Dapr.Client.Test request.Dismiss(); var envelope = await request.GetRequestEnvelopeAsync(); - envelope.Entries.Count.Should().Be(2); - envelope.PubsubName.Should().Be(TestPubsubName); - envelope.Topic.Should().Be(TestTopicName); - envelope.Metadata.Count.Should().Be(0); + envelope.Entries.Count.ShouldBe(2); + envelope.PubsubName.ShouldBe(TestPubsubName); + envelope.Topic.ShouldBe(TestTopicName); + envelope.Metadata.Count.ShouldBe(0); var firstEntry = envelope.Entries[0]; - firstEntry.EntryId.Should().Be("0"); - firstEntry.ContentType.Should().Be(TestContentType); - firstEntry.Event.ToStringUtf8().Should() - .Be(JsonSerializer.Serialize(bulkPublishData[0], client.InnerClient.JsonSerializerOptions)); - firstEntry.Metadata.Should().BeEmpty(); + firstEntry.EntryId.ShouldBe("0"); + firstEntry.ContentType.ShouldBe(TestContentType); + firstEntry.Event.ToStringUtf8().ShouldBe(JsonSerializer.Serialize(bulkPublishData[0], client.InnerClient.JsonSerializerOptions)); + firstEntry.Metadata.ShouldBeEmpty(); var secondEntry = envelope.Entries[1]; - secondEntry.EntryId.Should().Be("1"); - secondEntry.ContentType.Should().Be(TestContentType); - secondEntry.Event.ToStringUtf8().Should() - .Be(JsonSerializer.Serialize(bulkPublishData[1], client.InnerClient.JsonSerializerOptions)); - secondEntry.Metadata.Should().BeEmpty(); + secondEntry.EntryId.ShouldBe("1"); + secondEntry.ContentType.ShouldBe(TestContentType); + secondEntry.Event.ToStringUtf8().ShouldBe(JsonSerializer.Serialize(bulkPublishData[1], client.InnerClient.JsonSerializerOptions)); + secondEntry.Metadata.ShouldBeEmpty(); // Create Response & Respond var response = new Autogenerated.BulkPublishResponse @@ -168,7 +162,7 @@ namespace Dapr.Client.Test var bulkPublishResponse = await request.CompleteWithMessageAsync(response); // Get response and validate - bulkPublishResponse.FailedEntries.Count.Should().Be(0); + bulkPublishResponse.FailedEntries.Count.ShouldBe(0); } [Fact] @@ -185,31 +179,29 @@ namespace Dapr.Client.Test request.Dismiss(); var envelope = await request.GetRequestEnvelopeAsync(); - envelope.Entries.Count.Should().Be(2); - envelope.PubsubName.Should().Be(TestPubsubName); - envelope.Topic.Should().Be(TestTopicName); + envelope.Entries.Count.ShouldBe(2); + envelope.PubsubName.ShouldBe(TestPubsubName); + envelope.Topic.ShouldBe(TestTopicName); - envelope.Metadata.Count.Should().Be(2); - envelope.Metadata.Keys.Contains("key1").Should().BeTrue(); - envelope.Metadata.Keys.Contains("key2").Should().BeTrue(); - envelope.Metadata["key1"].Should().Be("value1"); - envelope.Metadata["key2"].Should().Be("value2"); + envelope.Metadata.Count.ShouldBe(2); + envelope.Metadata.Keys.Contains("key1").ShouldBeTrue(); + envelope.Metadata.Keys.Contains("key2").ShouldBeTrue(); + envelope.Metadata["key1"].ShouldBe("value1"); + envelope.Metadata["key2"].ShouldBe("value2"); var firstEntry = envelope.Entries[0]; - firstEntry.EntryId.Should().Be("0"); - firstEntry.ContentType.Should().Be(TestContentType); - firstEntry.Event.ToStringUtf8().Should() - .Be(JsonSerializer.Serialize(bulkPublishData[0], client.InnerClient.JsonSerializerOptions)); - firstEntry.Metadata.Should().BeEmpty(); + firstEntry.EntryId.ShouldBe("0"); + firstEntry.ContentType.ShouldBe(TestContentType); + firstEntry.Event.ToStringUtf8().ShouldBe(JsonSerializer.Serialize(bulkPublishData[0], client.InnerClient.JsonSerializerOptions)); + firstEntry.Metadata.ShouldBeEmpty(); var secondEntry = envelope.Entries[1]; - secondEntry.EntryId.Should().Be("1"); - secondEntry.ContentType.Should().Be(TestContentType); - secondEntry.Event.ToStringUtf8().Should() - .Be(JsonSerializer.Serialize(bulkPublishData[1], client.InnerClient.JsonSerializerOptions)); - secondEntry.Metadata.Should().BeEmpty(); + secondEntry.EntryId.ShouldBe("1"); + secondEntry.ContentType.ShouldBe(TestContentType); + secondEntry.Event.ToStringUtf8().ShouldBe(JsonSerializer.Serialize(bulkPublishData[1], client.InnerClient.JsonSerializerOptions)); + secondEntry.Metadata.ShouldBeEmpty(); // Create Response & Respond var response = new Autogenerated.BulkPublishResponse @@ -219,7 +211,7 @@ namespace Dapr.Client.Test var bulkPublishResponse = await request.CompleteWithMessageAsync(response); // Get response and validate - bulkPublishResponse.FailedEntries.Count.Should().Be(0); + bulkPublishResponse.FailedEntries.Count.ShouldBe(0); } [Fact] @@ -236,18 +228,17 @@ namespace Dapr.Client.Test request.Dismiss(); var envelope = await request.GetRequestEnvelopeAsync(); - envelope.Entries.Count.Should().Be(1); - envelope.PubsubName.Should().Be(TestPubsubName); - envelope.Topic.Should().Be(TestTopicName); - envelope.Metadata.Count.Should().Be(0); + envelope.Entries.Count.ShouldBe(1); + envelope.PubsubName.ShouldBe(TestPubsubName); + envelope.Topic.ShouldBe(TestTopicName); + envelope.Metadata.Count.ShouldBe(0); var firstEntry = envelope.Entries[0]; - firstEntry.EntryId.Should().Be("0"); - firstEntry.ContentType.Should().Be(TestContentType); - firstEntry.Event.ToStringUtf8().Should() - .Be(JsonSerializer.Serialize(bulkPublishDataObject, client.InnerClient.JsonSerializerOptions)); - firstEntry.Metadata.Should().BeEmpty(); + firstEntry.EntryId.ShouldBe("0"); + firstEntry.ContentType.ShouldBe(TestContentType); + firstEntry.Event.ToStringUtf8().ShouldBe(JsonSerializer.Serialize(bulkPublishDataObject, client.InnerClient.JsonSerializerOptions)); + firstEntry.Metadata.ShouldBeEmpty(); // Create Response & Respond var response = new Autogenerated.BulkPublishResponse @@ -257,7 +248,7 @@ namespace Dapr.Client.Test var bulkPublishResponse = await request.CompleteWithMessageAsync(response); // Get response and validate - bulkPublishResponse.FailedEntries.Count.Should().Be(0); + bulkPublishResponse.FailedEntries.Count.ShouldBe(0); } [Fact] @@ -333,11 +324,11 @@ namespace Dapr.Client.Test var bulkPublishResponse = await request.CompleteWithMessageAsync(response); // Get response and validate - bulkPublishResponse.FailedEntries[0].Entry.EntryId.Should().Be("0"); - bulkPublishResponse.FailedEntries[0].ErrorMessage.Should().Be("Failed to publish"); + bulkPublishResponse.FailedEntries[0].Entry.EntryId.ShouldBe("0"); + bulkPublishResponse.FailedEntries[0].ErrorMessage.ShouldBe("Failed to publish"); - bulkPublishResponse.FailedEntries[1].Entry.EntryId.Should().Be("1"); - bulkPublishResponse.FailedEntries[1].ErrorMessage.Should().Be("Failed to publish"); + bulkPublishResponse.FailedEntries[1].Entry.EntryId.ShouldBe("1"); + bulkPublishResponse.FailedEntries[1].ErrorMessage.ShouldBe("Failed to publish"); } private class Widget diff --git a/test/Dapr.Client.Test/ConfigurationApiTest.cs b/test/Dapr.Client.Test/ConfigurationApiTest.cs index 09502493..495026fc 100644 --- a/test/Dapr.Client.Test/ConfigurationApiTest.cs +++ b/test/Dapr.Client.Test/ConfigurationApiTest.cs @@ -15,7 +15,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using Autogenerated = Dapr.Client.Autogen.Grpc.v1; using Xunit; -using FluentAssertions; +using Shouldly; namespace Dapr.Client.Test { @@ -37,13 +37,13 @@ namespace Dapr.Client.Test // Get Request and validate var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.Should().Be("testStore"); - envelope.Keys.Should().Contain("test_key"); - envelope.Metadata.Count.Should().Be(2); - envelope.Metadata.Keys.Contains("key1").Should().BeTrue(); - envelope.Metadata.Keys.Contains("key2").Should().BeTrue(); - envelope.Metadata["key1"].Should().Be("value1"); - envelope.Metadata["key2"].Should().Be("value2"); + envelope.StoreName.ShouldBe("testStore"); + envelope.Keys.ShouldContain("test_key"); + envelope.Metadata.Count.ShouldBe(2); + envelope.Metadata.Keys.Contains("key1").ShouldBeTrue(); + envelope.Metadata.Keys.Contains("key2").ShouldBeTrue(); + envelope.Metadata["key1"].ShouldBe("value1"); + envelope.Metadata["key2"].ShouldBe("value2"); // Get response and validate var invokeResponse = new Autogenerated.GetConfigurationResponse(); @@ -55,8 +55,8 @@ namespace Dapr.Client.Test var domainResponse = await request.CompleteWithMessageAsync(invokeResponse); var configItem = domainResponse.Items["testKey"]; - configItem.Value.Should().Be("testValue"); - configItem.Version.Should().Be("v1"); + configItem.Value.ShouldBe("testValue"); + configItem.Version.ShouldBe("v1"); } [Fact] @@ -75,13 +75,13 @@ namespace Dapr.Client.Test // Get Request and validate var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.Should().Be("testStore"); - envelope.Keys.Should().BeEmpty(); - envelope.Metadata.Count.Should().Be(2); - envelope.Metadata.Keys.Contains("key1").Should().BeTrue(); - envelope.Metadata.Keys.Contains("key2").Should().BeTrue(); - envelope.Metadata["key1"].Should().Be("value1"); - envelope.Metadata["key2"].Should().Be("value2"); + envelope.StoreName.ShouldBe("testStore"); + envelope.Keys.ShouldBeEmpty(); + envelope.Metadata.Count.ShouldBe(2); + envelope.Metadata.Keys.Contains("key1").ShouldBeTrue(); + envelope.Metadata.Keys.Contains("key2").ShouldBeTrue(); + envelope.Metadata["key1"].ShouldBe("value1"); + envelope.Metadata["key2"].ShouldBe("value2"); // Get response and validate var invokeResponse = new Autogenerated.GetConfigurationResponse(); @@ -93,8 +93,8 @@ namespace Dapr.Client.Test var domainResponse = await request.CompleteWithMessageAsync(invokeResponse); var configItem = domainResponse.Items["testKey"]; - configItem.Value.Should().Be("testValue"); - configItem.Version.Should().Be("v1"); + configItem.Value.ShouldBe("testValue"); + configItem.Version.ShouldBe("v1"); } [Fact] @@ -108,9 +108,9 @@ namespace Dapr.Client.Test // Get Request and validate var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.Should().Be("testStore"); - envelope.Keys.Should().Contain("test_key"); - envelope.Metadata.Count.Should().Be(0); + envelope.StoreName.ShouldBe("testStore"); + envelope.Keys.ShouldContain("test_key"); + envelope.Metadata.Count.ShouldBe(0); // Get response and validate var invokeResponse = new Autogenerated.GetConfigurationResponse(); @@ -122,8 +122,8 @@ namespace Dapr.Client.Test var domainResponse = await request.CompleteWithMessageAsync(invokeResponse); var configItem = domainResponse.Items["testKey"]; - configItem.Value.Should().Be("testValue"); - configItem.Version.Should().Be("v1"); + configItem.Value.ShouldBe("testValue"); + configItem.Version.ShouldBe("v1"); } [Fact] @@ -136,8 +136,8 @@ namespace Dapr.Client.Test }); var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.Should().Be("testStore"); - envelope.Id.Should().Be("testId"); + envelope.StoreName.ShouldBe("testStore"); + envelope.Id.ShouldBe("testId"); request.Dismiss(); } } diff --git a/test/Dapr.Client.Test/Dapr.Client.Test.csproj b/test/Dapr.Client.Test/Dapr.Client.Test.csproj index f0bea601..17105cd1 100644 --- a/test/Dapr.Client.Test/Dapr.Client.Test.csproj +++ b/test/Dapr.Client.Test/Dapr.Client.Test.csproj @@ -5,7 +5,6 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all - @@ -14,6 +13,7 @@ + all diff --git a/test/Dapr.Client.Test/DaprApiTokenTest.cs b/test/Dapr.Client.Test/DaprApiTokenTest.cs index cf9f422b..e96d29d0 100644 --- a/test/Dapr.Client.Test/DaprApiTokenTest.cs +++ b/test/Dapr.Client.Test/DaprApiTokenTest.cs @@ -15,7 +15,7 @@ namespace Dapr.Client.Test { using System.Linq; using System.Threading.Tasks; - using FluentAssertions; + using Shouldly; using Xunit; using Autogenerated = Dapr.Client.Autogen.Grpc.v1; @@ -38,8 +38,8 @@ namespace Dapr.Client.Test await request.GetRequestEnvelopeAsync(); request.Request.Headers.TryGetValues("dapr-api-token", out var headerValues); - headerValues.Count().Should().Be(1); - headerValues.First().Should().Be("test_token"); + headerValues.Count().ShouldBe(1); + headerValues.First().ShouldBe("test_token"); } [Fact] @@ -59,7 +59,7 @@ namespace Dapr.Client.Test await request.GetRequestEnvelopeAsync(); request.Request.Headers.TryGetValues("dapr-api-token", out var headerValues); - headerValues.Should().BeNull(); + headerValues.ShouldBeNull(); } } } diff --git a/test/Dapr.Client.Test/DaprClientTest.InvokeMethodGrpcAsync.cs b/test/Dapr.Client.Test/DaprClientTest.InvokeMethodGrpcAsync.cs index 6ccbbe4c..3ef0aed6 100644 --- a/test/Dapr.Client.Test/DaprClientTest.InvokeMethodGrpcAsync.cs +++ b/test/Dapr.Client.Test/DaprClientTest.InvokeMethodGrpcAsync.cs @@ -16,7 +16,7 @@ using System.Threading; using System.Threading.Tasks; using Dapr.Client.Autogen.Grpc.v1; using Dapr.Client.Autogen.Test.Grpc.v1; -using FluentAssertions; +using Shouldly; using Google.Protobuf.WellKnownTypes; using Grpc.Core; using Grpc.Net.Client; @@ -62,9 +62,9 @@ namespace Dapr.Client.Test // Get Request and validate var envelope = await request.GetRequestEnvelopeAsync(); - envelope.Id.Should().Be("test"); - envelope.Message.Method.Should().Be("test"); - envelope.Message.ContentType.Should().Be(Constants.ContentTypeApplicationGrpc); + envelope.Id.ShouldBe("test"); + envelope.Message.Method.ShouldBe("test"); + envelope.Message.ContentType.ShouldBe(Constants.ContentTypeApplicationGrpc); // Create Response & Respond var data = new Response() { Name = "Look, I was invoked!" }; @@ -75,7 +75,7 @@ namespace Dapr.Client.Test // Validate Response var invokedResponse = await request.CompleteWithMessageAsync(response); - invokedResponse.Name.Should().Be("Look, I was invoked!"); + invokedResponse.Name.ShouldBe("Look, I was invoked!"); } [Fact] @@ -126,9 +126,9 @@ namespace Dapr.Client.Test // Get Request and validate var envelope = await request.GetRequestEnvelopeAsync(); - envelope.Id.Should().Be("test"); - envelope.Message.Method.Should().Be("test"); - envelope.Message.ContentType.Should().Be(string.Empty); + envelope.Id.ShouldBe("test"); + envelope.Message.Method.ShouldBe("test"); + envelope.Message.ContentType.ShouldBe(string.Empty); // Create Response & Respond var data = new Response() { Name = "Look, I was invoked!" }; @@ -139,7 +139,7 @@ namespace Dapr.Client.Test // Validate Response var invokedResponse = await request.CompleteWithMessageAsync(response); - invokedResponse.Name.Should().Be("Look, I was invoked!"); + invokedResponse.Name.ShouldBe("Look, I was invoked!"); } [Fact] @@ -176,7 +176,7 @@ namespace Dapr.Client.Test } [Fact] - public void InvokeMethodGrpcAsync_CanInvokeMethodWithNoReturnTypeAndData() + public async Task InvokeMethodGrpcAsync_CanInvokeMethodWithNoReturnTypeAndData() { var request = new Request() { RequestParameter = "Hello " }; var client = new MockClient(); @@ -195,7 +195,7 @@ namespace Dapr.Client.Test .Setup(m => m.InvokeServiceAsync(It.IsAny(), It.IsAny())) .Returns(response); - FluentActions.Awaiting(async () => await client.DaprClient.InvokeMethodGrpcAsync("test", "test", request)).Should().NotThrowAsync(); + await Should.NotThrowAsync(async () => await client.DaprClient.InvokeMethodGrpcAsync("test", "test", request)); } [Fact] @@ -250,9 +250,9 @@ namespace Dapr.Client.Test // Get Request and validate var envelope = await request.GetRequestEnvelopeAsync(); - envelope.Id.Should().Be("test"); - envelope.Message.Method.Should().Be("test"); - envelope.Message.ContentType.Should().Be(Constants.ContentTypeApplicationGrpc); + envelope.Id.ShouldBe("test"); + envelope.Message.Method.ShouldBe("test"); + envelope.Message.ContentType.ShouldBe(Constants.ContentTypeApplicationGrpc); var actual = envelope.Message.Data.Unpack(); Assert.Equal(invokeRequest.RequestParameter, actual.RequestParameter); @@ -275,9 +275,9 @@ namespace Dapr.Client.Test // Get Request and validate var envelope = await request.GetRequestEnvelopeAsync(); - envelope.Id.Should().Be("test"); - envelope.Message.Method.Should().Be("test"); - envelope.Message.ContentType.Should().Be(Constants.ContentTypeApplicationGrpc); + envelope.Id.ShouldBe("test"); + envelope.Message.Method.ShouldBe("test"); + envelope.Message.ContentType.ShouldBe(Constants.ContentTypeApplicationGrpc); var actual = envelope.Message.Data.Unpack(); Assert.Equal(invokeRequest.RequestParameter, actual.RequestParameter); @@ -291,7 +291,7 @@ namespace Dapr.Client.Test // Validate Response var invokedResponse = await request.CompleteWithMessageAsync(response); - invokedResponse.Name.Should().Be(invokeResponse.Name); + invokedResponse.Name.ShouldBe(invokeResponse.Name); } [Fact] @@ -308,7 +308,7 @@ namespace Dapr.Client.Test var response = await daprClient.InvokeMethodGrpcAsync("test", "SayHello", request); - response.Name.Should().Be("Hello Look, I was invoked!"); + response.Name.ShouldBe("Hello Look, I was invoked!"); } [Fact] @@ -328,10 +328,10 @@ namespace Dapr.Client.Test var response = await daprClient.InvokeMethodGrpcAsync("test", "TestRun", testRun); - response.Tests.Count.Should().Be(3); - response.Tests[0].Name.Should().Be("test1"); - response.Tests[1].Name.Should().Be("test2"); - response.Tests[2].Name.Should().Be("test3"); + response.Tests.Count.ShouldBe(3); + response.Tests[0].Name.ShouldBe("test1"); + response.Tests[1].Name.ShouldBe("test2"); + response.Tests[2].Name.ShouldBe("test3"); } [Fact] @@ -348,7 +348,7 @@ namespace Dapr.Client.Test var response = await daprClient.InvokeMethodGrpcAsync("test", "not-existing", request); - response.Name.Should().Be("unexpected"); + response.Name.ShouldBe("unexpected"); } @@ -401,10 +401,10 @@ namespace Dapr.Client.Test // Validate Response var metadata = await request.CompleteWithMessageAsync(response); - metadata.Id.Should().Be("testId"); - metadata.Extended.Should().Contain(new System.Collections.Generic.KeyValuePair("e1", "v1")); - metadata.Actors.Should().Contain(actors => actors.Count == 1 && actors.Type == "testType"); - metadata.Components.Should().Contain(components => components.Name == "testName" && components.Type == "testType" && components.Version == "V1" && components.Capabilities.Length == 0); + metadata.Id.ShouldBe("testId"); + metadata.Extended.ShouldContain(new System.Collections.Generic.KeyValuePair("e1", "v1")); + metadata.Actors.ShouldContain(actors => actors.Count == 1 && actors.Type == "testType"); + metadata.Components.ShouldContain(components => components.Name == "testName" && components.Type == "testType" && components.Version == "V1" && components.Capabilities.Length == 0); } [Fact] @@ -445,8 +445,8 @@ namespace Dapr.Client.Test // Get Request and validate var envelope = await request.GetRequestEnvelopeAsync(); - envelope.Key.Should().Be("test"); - envelope.Value.Should().Be("testv"); + envelope.Key.ShouldBe("test"); + envelope.Value.ShouldBe("testv"); await request.CompleteWithMessageAsync(new Empty()); diff --git a/test/Dapr.Client.Test/DistributedLockApiTest.cs b/test/Dapr.Client.Test/DistributedLockApiTest.cs index a030586c..1935a543 100644 --- a/test/Dapr.Client.Test/DistributedLockApiTest.cs +++ b/test/Dapr.Client.Test/DistributedLockApiTest.cs @@ -14,7 +14,7 @@ using System.Threading.Tasks; using Autogenerated = Dapr.Client.Autogen.Grpc.v1; using Xunit; -using FluentAssertions; +using Shouldly; using System; namespace Dapr.Client.Test @@ -37,10 +37,10 @@ namespace Dapr.Client.Test // Get Request and validate var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.Should().Be("redis"); - envelope.ResourceId.Should().Be("resourceId"); - envelope.LockOwner.Should().Be("owner1"); - envelope.ExpiryInSeconds.Should().Be(1000); + envelope.StoreName.ShouldBe("redis"); + envelope.ResourceId.ShouldBe("resourceId"); + envelope.LockOwner.ShouldBe("owner1"); + envelope.ExpiryInSeconds.ShouldBe(1000); // Get response and validate var invokeResponse = new Autogenerated.TryLockResponse{ @@ -48,7 +48,7 @@ namespace Dapr.Client.Test }; var domainResponse = await request.CompleteWithMessageAsync(invokeResponse); - domainResponse.Success.Should().Be(true); + domainResponse.Success.ShouldBe(true); } [Fact] @@ -80,9 +80,9 @@ namespace Dapr.Client.Test // Get Request and validate var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.Should().Be("redis"); - envelope.ResourceId.Should().Be("resourceId"); - envelope.LockOwner.Should().Be("owner1"); + envelope.StoreName.ShouldBe("redis"); + envelope.ResourceId.ShouldBe("resourceId"); + envelope.LockOwner.ShouldBe("owner1"); // Get response and validate var invokeResponse = new Autogenerated.UnlockResponse{ @@ -90,7 +90,7 @@ namespace Dapr.Client.Test }; var domainResponse = await request.CompleteWithMessageAsync(invokeResponse); - domainResponse.status.Should().Be(LockStatus.LockDoesNotExist); + domainResponse.status.ShouldBe(LockStatus.LockDoesNotExist); } } -} \ No newline at end of file +} diff --git a/test/Dapr.Client.Test/InvokeBindingApiTest.cs b/test/Dapr.Client.Test/InvokeBindingApiTest.cs index c2ada7a1..c980e889 100644 --- a/test/Dapr.Client.Test/InvokeBindingApiTest.cs +++ b/test/Dapr.Client.Test/InvokeBindingApiTest.cs @@ -20,7 +20,7 @@ namespace Dapr.Client.Test using System.Threading; using System.Threading.Tasks; using Dapr.Client.Autogen.Grpc.v1; - using FluentAssertions; + using Shouldly; using Google.Protobuf; using Grpc.Core; using Moq; @@ -43,11 +43,11 @@ namespace Dapr.Client.Test // Get Request and validate var envelope = await request.GetRequestEnvelopeAsync(); - envelope.Name.Should().Be("test"); - envelope.Metadata.Count.Should().Be(0); + envelope.Name.ShouldBe("test"); + envelope.Metadata.Count.ShouldBe(0); var json = envelope.Data.ToStringUtf8(); var typeFromRequest = JsonSerializer.Deserialize(json, client.InnerClient.JsonSerializerOptions); - typeFromRequest.RequestParameter.Should().Be("Hello "); + typeFromRequest.RequestParameter.ShouldBe("Hello "); } [Fact] @@ -70,15 +70,15 @@ namespace Dapr.Client.Test // Get Request and validate var envelope = await request.GetRequestEnvelopeAsync(); - envelope.Name.Should().Be("test"); - envelope.Metadata.Count.Should().Be(2); - envelope.Metadata.Keys.Contains("key1").Should().BeTrue(); - envelope.Metadata.Keys.Contains("key2").Should().BeTrue(); - envelope.Metadata["key1"].Should().Be("value1"); - envelope.Metadata["key2"].Should().Be("value2"); + envelope.Name.ShouldBe("test"); + envelope.Metadata.Count.ShouldBe(2); + envelope.Metadata.Keys.Contains("key1").ShouldBeTrue(); + envelope.Metadata.Keys.Contains("key2").ShouldBeTrue(); + envelope.Metadata["key1"].ShouldBe("value1"); + envelope.Metadata["key2"].ShouldBe("value2"); var json = envelope.Data.ToStringUtf8(); var typeFromRequest = JsonSerializer.Deserialize(json, client.InnerClient.JsonSerializerOptions); - typeFromRequest.RequestParameter.Should().Be("Hello "); + typeFromRequest.RequestParameter.ShouldBe("Hello "); } [Fact] @@ -95,8 +95,8 @@ namespace Dapr.Client.Test // Get Request and validate var envelope = await request.GetRequestEnvelopeAsync(); - envelope.Name.Should().Be("test"); - envelope.Metadata.Count.Should().Be(0); + envelope.Name.ShouldBe("test"); + envelope.Metadata.Count.ShouldBe(0); var json = envelope.Data.ToStringUtf8(); Assert.Equal("null", json); } @@ -133,16 +133,16 @@ namespace Dapr.Client.Test var response = await request.CompleteWithMessageAsync(gRpcResponse); var envelope = await request.GetRequestEnvelopeAsync(); - envelope.Name.Should().Be("test"); - envelope.Metadata.Count.Should().Be(2); - envelope.Metadata.Keys.Contains("key1").Should().BeTrue(); - envelope.Metadata.Keys.Contains("key2").Should().BeTrue(); - envelope.Metadata["key1"].Should().Be("value1"); - envelope.Metadata["key2"].Should().Be("value2"); + envelope.Name.ShouldBe("test"); + envelope.Metadata.Count.ShouldBe(2); + envelope.Metadata.Keys.Contains("key1").ShouldBeTrue(); + envelope.Metadata.Keys.Contains("key2").ShouldBeTrue(); + envelope.Metadata["key1"].ShouldBe("value1"); + envelope.Metadata["key2"].ShouldBe("value2"); var json = envelope.Data.ToStringUtf8(); var typeFromRequest = JsonSerializer.Deserialize(json, client.InnerClient.JsonSerializerOptions); - typeFromRequest.RequestParameter.Should().Be("Hello "); + typeFromRequest.RequestParameter.ShouldBe("Hello "); Assert.Same(bindingRequest, response.Request); Assert.Equal("red", JsonSerializer.Deserialize(response.Data.Span, client.InnerClient.JsonSerializerOptions).Color); @@ -239,15 +239,15 @@ namespace Dapr.Client.Test var response = await req.CompleteWithMessageAsync(resp); var envelope = await req.GetRequestEnvelopeAsync(); - envelope.Name.Should().Be("binding"); - envelope.Operation.Should().Be("operation"); - envelope.Metadata.Count.Should().Be(1); - envelope.Metadata.Keys.Contains("key1").Should().BeTrue(); - envelope.Metadata["key1"].Should().Be("value1"); + envelope.Name.ShouldBe("binding"); + envelope.Operation.ShouldBe("operation"); + envelope.Metadata.Count.ShouldBe(1); + envelope.Metadata.Keys.Contains("key1").ShouldBeTrue(); + envelope.Metadata["key1"].ShouldBe("value1"); var json = envelope.Data.ToStringUtf8(); var typeFromRequest = JsonSerializer.Deserialize(json, client.InnerClient.JsonSerializerOptions); - typeFromRequest.RequestParameter.Should().Be("Test"); + typeFromRequest.RequestParameter.ShouldBe("Test"); Assert.Equal("red", response.Color); } @@ -269,8 +269,8 @@ namespace Dapr.Client.Test var response = await req.CompleteWithMessageAsync(resp); var envelope = await req.GetRequestEnvelopeAsync(); - envelope.Name.Should().Be("binding"); - envelope.Operation.Should().Be("operation"); + envelope.Name.ShouldBe("binding"); + envelope.Operation.ShouldBe("operation"); Assert.Equal("red", response.Color); } diff --git a/test/Dapr.Client.Test/PublishEventApiTest.cs b/test/Dapr.Client.Test/PublishEventApiTest.cs index 6ba774e5..0dac474a 100644 --- a/test/Dapr.Client.Test/PublishEventApiTest.cs +++ b/test/Dapr.Client.Test/PublishEventApiTest.cs @@ -24,7 +24,7 @@ namespace Dapr.Client.Test using System.Threading; using System.Threading.Tasks; using Dapr.Client.Autogen.Grpc.v1; - using FluentAssertions; + using Shouldly; using Grpc.Core; using Moq; using Xunit; @@ -49,11 +49,11 @@ namespace Dapr.Client.Test var envelope = await request.GetRequestEnvelopeAsync(); var jsonFromRequest = envelope.Data.ToStringUtf8(); - envelope.DataContentType.Should().Be("application/json"); - envelope.PubsubName.Should().Be(TestPubsubName); - envelope.Topic.Should().Be("test"); - jsonFromRequest.Should().Be(JsonSerializer.Serialize(publishData, client.InnerClient.JsonSerializerOptions)); - envelope.Metadata.Count.Should().Be(0); + envelope.DataContentType.ShouldBe("application/json"); + envelope.PubsubName.ShouldBe(TestPubsubName); + envelope.Topic.ShouldBe("test"); + jsonFromRequest.ShouldBe(JsonSerializer.Serialize(publishData, client.InnerClient.JsonSerializerOptions)); + envelope.Metadata.Count.ShouldBe(0); } [Fact] @@ -75,9 +75,8 @@ namespace Dapr.Client.Test var client = new TestClient(clientBuilder.Build(), handler); //Ensure that the JsonStringEnumConverter is registered - client.InnerClient.JsonSerializerOptions.Converters.Count.Should().Be(1); - client.InnerClient.JsonSerializerOptions.Converters.First().GetType().Name.Should() - .Match(nameof(JsonStringEnumConverter)); + client.InnerClient.JsonSerializerOptions.Converters.Count.ShouldBe(1); + client.InnerClient.JsonSerializerOptions.Converters.First().GetType().Name.ShouldMatch(nameof(JsonStringEnumConverter)); var publishData = new Widget {Size = "Large", Color = WidgetColor.Red}; var request = await client.CaptureGrpcRequestAsync(async daprClient => @@ -89,9 +88,8 @@ namespace Dapr.Client.Test var envelope = await request.GetRequestEnvelopeAsync(); var jsonFromRequest = envelope.Data.ToStringUtf8(); - jsonFromRequest.Should() - .Be(JsonSerializer.Serialize(publishData, client.InnerClient.JsonSerializerOptions)); - jsonFromRequest.Should().Match("{\"Size\":\"Large\",\"Color\":\"Red\"}"); + jsonFromRequest.ShouldBe(JsonSerializer.Serialize(publishData, client.InnerClient.JsonSerializerOptions)); + jsonFromRequest.ShouldMatch("{\"Size\":\"Large\",\"Color\":\"Red\"}"); } [Fact] @@ -116,16 +114,16 @@ namespace Dapr.Client.Test var envelope = await request.GetRequestEnvelopeAsync(); var jsonFromRequest = envelope.Data.ToStringUtf8(); - envelope.DataContentType.Should().Be("application/json"); - envelope.PubsubName.Should().Be(TestPubsubName); - envelope.Topic.Should().Be("test"); - jsonFromRequest.Should().Be(JsonSerializer.Serialize(publishData, client.InnerClient.JsonSerializerOptions)); + envelope.DataContentType.ShouldBe("application/json"); + envelope.PubsubName.ShouldBe(TestPubsubName); + envelope.Topic.ShouldBe("test"); + jsonFromRequest.ShouldBe(JsonSerializer.Serialize(publishData, client.InnerClient.JsonSerializerOptions)); - envelope.Metadata.Count.Should().Be(2); - envelope.Metadata.Keys.Contains("key1").Should().BeTrue(); - envelope.Metadata.Keys.Contains("key2").Should().BeTrue(); - envelope.Metadata["key1"].Should().Be("value1"); - envelope.Metadata["key2"].Should().Be("value2"); + envelope.Metadata.Count.ShouldBe(2); + envelope.Metadata.Keys.Contains("key1").ShouldBeTrue(); + envelope.Metadata.Keys.Contains("key2").ShouldBeTrue(); + envelope.Metadata["key1"].ShouldBe("value1"); + envelope.Metadata["key2"].ShouldBe("value2"); } [Fact] @@ -142,10 +140,10 @@ namespace Dapr.Client.Test var envelope = await request.GetRequestEnvelopeAsync(); - envelope.PubsubName.Should().Be(TestPubsubName); - envelope.Topic.Should().Be("test"); - envelope.Data.Length.Should().Be(0); - envelope.Metadata.Count.Should().Be(0); + envelope.PubsubName.ShouldBe(TestPubsubName); + envelope.Topic.ShouldBe("test"); + envelope.Data.Length.ShouldBe(0); + envelope.Metadata.Count.ShouldBe(0); } [Fact] @@ -167,15 +165,15 @@ namespace Dapr.Client.Test request.Dismiss(); var envelope = await request.GetRequestEnvelopeAsync(); - envelope.PubsubName.Should().Be(TestPubsubName); - envelope.Topic.Should().Be("test"); - envelope.Data.Length.Should().Be(0); + envelope.PubsubName.ShouldBe(TestPubsubName); + envelope.Topic.ShouldBe("test"); + envelope.Data.Length.ShouldBe(0); - envelope.Metadata.Count.Should().Be(2); - envelope.Metadata.Keys.Contains("key1").Should().BeTrue(); - envelope.Metadata.Keys.Contains("key2").Should().BeTrue(); - envelope.Metadata["key1"].Should().Be("value1"); - envelope.Metadata["key2"].Should().Be("value2"); + envelope.Metadata.Count.ShouldBe(2); + envelope.Metadata.Keys.Contains("key1").ShouldBeTrue(); + envelope.Metadata.Keys.Contains("key2").ShouldBeTrue(); + envelope.Metadata["key1"].ShouldBe("value1"); + envelope.Metadata["key2"].ShouldBe("value2"); } [Fact] @@ -200,11 +198,11 @@ namespace Dapr.Client.Test var envelope = await request.GetRequestEnvelopeAsync(); var jsonFromRequest = envelope.Data.ToStringUtf8(); - envelope.DataContentType.Should().Be("application/cloudevents+json"); - envelope.PubsubName.Should().Be(TestPubsubName); - envelope.Topic.Should().Be("test"); - jsonFromRequest.Should().Be(JsonSerializer.Serialize(cloudEvent, client.InnerClient.JsonSerializerOptions)); - envelope.Metadata.Count.Should().Be(0); + envelope.DataContentType.ShouldBe("application/cloudevents+json"); + envelope.PubsubName.ShouldBe(TestPubsubName); + envelope.Topic.ShouldBe("test"); + jsonFromRequest.ShouldBe(JsonSerializer.Serialize(cloudEvent, client.InnerClient.JsonSerializerOptions)); + envelope.Metadata.Count.ShouldBe(0); } [Fact] @@ -228,11 +226,11 @@ namespace Dapr.Client.Test var envelope = await request.GetRequestEnvelopeAsync(); var jsonFromRequest = envelope.Data.ToStringUtf8(); - envelope.DataContentType.Should().Be("application/cloudevents+json"); - envelope.PubsubName.Should().Be(TestPubsubName); - envelope.Topic.Should().Be("test"); - jsonFromRequest.Should().Be(JsonSerializer.Serialize(cloudEvent, client.InnerClient.JsonSerializerOptions)); - envelope.Metadata.Count.Should().Be(0); + envelope.DataContentType.ShouldBe("application/cloudevents+json"); + envelope.PubsubName.ShouldBe(TestPubsubName); + envelope.Topic.ShouldBe("test"); + jsonFromRequest.ShouldBe(JsonSerializer.Serialize(cloudEvent, client.InnerClient.JsonSerializerOptions)); + envelope.Metadata.Count.ShouldBe(0); } [Fact] @@ -287,13 +285,13 @@ namespace Dapr.Client.Test var envelope = await request.GetRequestEnvelopeAsync(); var jsonFromRequest = envelope.Data.ToStringUtf8(); - envelope.DataContentType.Should().Be("application/json"); - envelope.PubsubName.Should().Be(TestPubsubName); - envelope.Topic.Should().Be("test"); - jsonFromRequest.Should().Be(JsonSerializer.Serialize(publishData)); + envelope.DataContentType.ShouldBe("application/json"); + envelope.PubsubName.ShouldBe(TestPubsubName); + envelope.Topic.ShouldBe("test"); + jsonFromRequest.ShouldBe(JsonSerializer.Serialize(publishData)); // The default serializer forces camel case, so this should be different from our serialization above. - jsonFromRequest.Should().NotBe(JsonSerializer.Serialize(publishBytes, client.InnerClient.JsonSerializerOptions)); - envelope.Metadata.Count.Should().Be(0); + jsonFromRequest.ShouldNotBe(JsonSerializer.Serialize(publishBytes, client.InnerClient.JsonSerializerOptions)); + envelope.Metadata.Count.ShouldBe(0); } private class PublishData diff --git a/test/Dapr.Client.Test/SecretApiTest.cs b/test/Dapr.Client.Test/SecretApiTest.cs index 26048e2a..4f7d8aa5 100644 --- a/test/Dapr.Client.Test/SecretApiTest.cs +++ b/test/Dapr.Client.Test/SecretApiTest.cs @@ -17,7 +17,7 @@ namespace Dapr.Client.Test using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; - using FluentAssertions; + using Shouldly; using Grpc.Core; using Moq; using Xunit; @@ -44,13 +44,13 @@ namespace Dapr.Client.Test // Get Request and validate var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.Should().Be("testStore"); - envelope.Key.Should().Be("test_key"); - envelope.Metadata.Count.Should().Be(2); - envelope.Metadata.Keys.Contains("key1").Should().BeTrue(); - envelope.Metadata.Keys.Contains("key2").Should().BeTrue(); - envelope.Metadata["key1"].Should().Be("value1"); - envelope.Metadata["key2"].Should().Be("value2"); + envelope.StoreName.ShouldBe("testStore"); + envelope.Key.ShouldBe("test_key"); + envelope.Metadata.Count.ShouldBe(2); + envelope.Metadata.Keys.Contains("key1").ShouldBeTrue(); + envelope.Metadata.Keys.Contains("key2").ShouldBeTrue(); + envelope.Metadata["key1"].ShouldBe("value1"); + envelope.Metadata["key2"].ShouldBe("value2"); } [Fact] @@ -72,13 +72,13 @@ namespace Dapr.Client.Test // Get Request and validate var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.Should().Be("testStore"); - envelope.Key.Should().Be("test_key"); - envelope.Metadata.Count.Should().Be(2); - envelope.Metadata.Keys.Contains("key1").Should().BeTrue(); - envelope.Metadata.Keys.Contains("key2").Should().BeTrue(); - envelope.Metadata["key1"].Should().Be("value1"); - envelope.Metadata["key2"].Should().Be("value2"); + envelope.StoreName.ShouldBe("testStore"); + envelope.Key.ShouldBe("test_key"); + envelope.Metadata.Count.ShouldBe(2); + envelope.Metadata.Keys.Contains("key1").ShouldBeTrue(); + envelope.Metadata.Keys.Contains("key2").ShouldBeTrue(); + envelope.Metadata["key1"].ShouldBe("value1"); + envelope.Metadata["key2"].ShouldBe("value2"); // Create Response & Respond var secrets = new Dictionary @@ -88,9 +88,9 @@ namespace Dapr.Client.Test var secretsResponse = await SendResponseWithSecrets(secrets, request); // Get response and validate - secretsResponse.Count.Should().Be(1); - secretsResponse.ContainsKey("redis_secret").Should().BeTrue(); - secretsResponse["redis_secret"].Should().Be("Guess_Redis"); + secretsResponse.Count.ShouldBe(1); + secretsResponse.ContainsKey("redis_secret").ShouldBeTrue(); + secretsResponse["redis_secret"].ShouldBe("Guess_Redis"); } [Fact] @@ -107,16 +107,16 @@ namespace Dapr.Client.Test //Get Request and validate var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.Should().Be("testStore"); - envelope.Key.Should().Be("us-west-1/org/xpto/secretabc"); + envelope.StoreName.ShouldBe("testStore"); + envelope.Key.ShouldBe("us-west-1/org/xpto/secretabc"); var secrets = new Dictionary { { "us-west-1/org/xpto/secretabc", "abc123" } }; var secretsResponse = await SendResponseWithSecrets(secrets, request); //Get response and validate - secretsResponse.Count.Should().Be(1); - secretsResponse.ContainsKey("us-west-1/org/xpto/secretabc").Should().BeTrue(); - secretsResponse["us-west-1/org/xpto/secretabc"].Should().Be("abc123"); + secretsResponse.Count.ShouldBe(1); + secretsResponse.ContainsKey("us-west-1/org/xpto/secretabc").ShouldBeTrue(); + secretsResponse["us-west-1/org/xpto/secretabc"].ShouldBe("abc123"); } [Fact] @@ -138,13 +138,13 @@ namespace Dapr.Client.Test // Get Request and validate var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.Should().Be("testStore"); - envelope.Key.Should().Be("test_key"); - envelope.Metadata.Count.Should().Be(2); - envelope.Metadata.Keys.Contains("key1").Should().BeTrue(); - envelope.Metadata.Keys.Contains("key2").Should().BeTrue(); - envelope.Metadata["key1"].Should().Be("value1"); - envelope.Metadata["key2"].Should().Be("value2"); + envelope.StoreName.ShouldBe("testStore"); + envelope.Key.ShouldBe("test_key"); + envelope.Metadata.Count.ShouldBe(2); + envelope.Metadata.Keys.Contains("key1").ShouldBeTrue(); + envelope.Metadata.Keys.Contains("key2").ShouldBeTrue(); + envelope.Metadata["key1"].ShouldBe("value1"); + envelope.Metadata["key2"].ShouldBe("value2"); // Create Response & Respond var secrets = new Dictionary @@ -155,11 +155,11 @@ namespace Dapr.Client.Test var secretsResponse = await SendResponseWithSecrets(secrets, request); // Get response and validate - secretsResponse.Count.Should().Be(2); - secretsResponse.ContainsKey("redis_secret").Should().BeTrue(); - secretsResponse["redis_secret"].Should().Be("Guess_Redis"); - secretsResponse.ContainsKey("kafka_secret").Should().BeTrue(); - secretsResponse["kafka_secret"].Should().Be("Guess_Kafka"); + secretsResponse.Count.ShouldBe(2); + secretsResponse.ContainsKey("redis_secret").ShouldBeTrue(); + secretsResponse["redis_secret"].ShouldBe("Guess_Redis"); + secretsResponse.ContainsKey("kafka_secret").ShouldBeTrue(); + secretsResponse["kafka_secret"].ShouldBe("Guess_Kafka"); } [Fact] @@ -220,12 +220,12 @@ namespace Dapr.Client.Test // Get Request and validate var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.Should().Be("testStore"); - envelope.Metadata.Count.Should().Be(2); - envelope.Metadata.Keys.Contains("key1").Should().BeTrue(); - envelope.Metadata.Keys.Contains("key2").Should().BeTrue(); - envelope.Metadata["key1"].Should().Be("value1"); - envelope.Metadata["key2"].Should().Be("value2"); + envelope.StoreName.ShouldBe("testStore"); + envelope.Metadata.Count.ShouldBe(2); + envelope.Metadata.Keys.Contains("key1").ShouldBeTrue(); + envelope.Metadata.Keys.Contains("key2").ShouldBeTrue(); + envelope.Metadata["key1"].ShouldBe("value1"); + envelope.Metadata["key2"].ShouldBe("value2"); } [Fact] @@ -244,12 +244,12 @@ namespace Dapr.Client.Test // Get Request and validate var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.Should().Be("testStore"); - envelope.Metadata.Count.Should().Be(2); - envelope.Metadata.Keys.Contains("key1").Should().BeTrue(); - envelope.Metadata.Keys.Contains("key2").Should().BeTrue(); - envelope.Metadata["key1"].Should().Be("value1"); - envelope.Metadata["key2"].Should().Be("value2"); + envelope.StoreName.ShouldBe("testStore"); + envelope.Metadata.Count.ShouldBe(2); + envelope.Metadata.Keys.Contains("key1").ShouldBeTrue(); + envelope.Metadata.Keys.Contains("key2").ShouldBeTrue(); + envelope.Metadata["key1"].ShouldBe("value1"); + envelope.Metadata["key2"].ShouldBe("value2"); // Create Response & Respond var secrets = new Dictionary(); @@ -257,9 +257,9 @@ namespace Dapr.Client.Test var secretsResponse = await SendBulkResponseWithSecrets(secrets, request); // Get response and validate - secretsResponse.Count.Should().Be(1); - secretsResponse.ContainsKey("redis_secret").Should().BeTrue(); - secretsResponse["redis_secret"]["redis_secret"].Should().Be("Guess_Redis"); + secretsResponse.Count.ShouldBe(1); + secretsResponse.ContainsKey("redis_secret").ShouldBeTrue(); + secretsResponse["redis_secret"]["redis_secret"].ShouldBe("Guess_Redis"); } [Fact] @@ -279,12 +279,12 @@ namespace Dapr.Client.Test // Get Request and validate var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.Should().Be("testStore"); - envelope.Metadata.Count.Should().Be(2); - envelope.Metadata.Keys.Contains("key1").Should().BeTrue(); - envelope.Metadata.Keys.Contains("key2").Should().BeTrue(); - envelope.Metadata["key1"].Should().Be("value1"); - envelope.Metadata["key2"].Should().Be("value2"); + envelope.StoreName.ShouldBe("testStore"); + envelope.Metadata.Count.ShouldBe(2); + envelope.Metadata.Keys.Contains("key1").ShouldBeTrue(); + envelope.Metadata.Keys.Contains("key2").ShouldBeTrue(); + envelope.Metadata["key1"].ShouldBe("value1"); + envelope.Metadata["key2"].ShouldBe("value2"); // Create Response & Respond var secrets = new Dictionary(); @@ -293,11 +293,11 @@ namespace Dapr.Client.Test var secretsResponse = await SendBulkResponseWithSecrets(secrets, request); // Get response and validate - secretsResponse.Count.Should().Be(2); - secretsResponse.ContainsKey("redis_secret").Should().BeTrue(); - secretsResponse["redis_secret"]["redis_secret"].Should().Be("Guess_Redis"); - secretsResponse.ContainsKey("kafka_secret").Should().BeTrue(); - secretsResponse["kafka_secret"]["kafka_secret"].Should().Be("Guess_Kafka"); + secretsResponse.Count.ShouldBe(2); + secretsResponse.ContainsKey("redis_secret").ShouldBeTrue(); + secretsResponse["redis_secret"]["redis_secret"].ShouldBe("Guess_Redis"); + secretsResponse.ContainsKey("kafka_secret").ShouldBeTrue(); + secretsResponse["kafka_secret"]["kafka_secret"].ShouldBe("Guess_Kafka"); } [Fact] diff --git a/test/Dapr.Client.Test/StateApiTest.cs b/test/Dapr.Client.Test/StateApiTest.cs index 12fd0e3d..43c7f408 100644 --- a/test/Dapr.Client.Test/StateApiTest.cs +++ b/test/Dapr.Client.Test/StateApiTest.cs @@ -18,12 +18,12 @@ using System.Net; using System.Text.Json; using System.Threading.Tasks; using Autogenerated = Dapr.Client.Autogen.Grpc.v1; -using FluentAssertions; using Google.Protobuf; using Grpc.Core; using Moq; using StateConsistency = Dapr.Client.Autogen.Grpc.v1.StateOptions.Types.StateConsistency; using StateConcurrency = Dapr.Client.Autogen.Grpc.v1.StateOptions.Types.StateConcurrency; +using Shouldly; using Xunit; using System.Threading; using System.Net.Http; @@ -48,8 +48,8 @@ namespace Dapr.Client.Test var state = await request.CompleteWithMessageAsync(envelope); // Get response and validate - state.Size.Should().Be("small"); - state.Color.Should().Be("yellow"); + state.Size.ShouldBe("small"); + state.Color.ShouldBe("yellow"); } [Fact] @@ -66,7 +66,7 @@ namespace Dapr.Client.Test var state = await request.CompleteWithMessageAsync(envelope); // Get response and validate - state.Should().HaveCount(1); + state.Count.ShouldBe(1); } [Fact] @@ -85,9 +85,9 @@ namespace Dapr.Client.Test var state = await request.CompleteWithMessageAsync(envelope); // Get response and validate - state.Should().HaveCount(1); - state[0].Value.Size.Should().Match(size); - state[0].Value.Color.Should().Match(color); + state.Count.ShouldBe(1); + state[0].Value.Size.ShouldMatch(size); + state[0].Value.Color.ShouldMatch(color); } [Fact] @@ -122,8 +122,8 @@ namespace Dapr.Client.Test // Create Response & Validate var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.Should().Be("testStore"); - envelope.Metadata.Should().BeEquivalentTo(metadata); + envelope.StoreName.ShouldBe("testStore"); + envelope.Metadata.ShouldBe(metadata); } [Fact] @@ -139,9 +139,9 @@ namespace Dapr.Client.Test var (state, etag) = await request.CompleteWithMessageAsync(envelope); // Get response and validate - state.Size.Should().Be("small"); - state.Color.Should().Be("yellow"); - etag.Should().Be("Test_Etag"); + state.Size.ShouldBe("small"); + state.Color.ShouldBe("yellow"); + etag.ShouldBe("Test_Etag"); } [Fact] @@ -191,7 +191,7 @@ namespace Dapr.Client.Test var state = await request.CompleteWithMessageAsync(envelope); // Get response and validate - state.Should().BeNull(); + state.ShouldBeNull(); } [Theory] @@ -205,15 +205,15 @@ namespace Dapr.Client.Test // Get Request & Validate var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.Should().Be("testStore"); - envelope.Key.Should().Be("test"); - envelope.Consistency.Should().Be(expectedConsistencyMode); + envelope.StoreName.ShouldBe("testStore"); + envelope.Key.ShouldBe("test"); + envelope.Consistency.ShouldBe(expectedConsistencyMode); // Create Response & Respond var state = await request.CompleteWithMessageAsync(MakeGetStateResponse(null)); // Get response and validate - state.Should().BeNull(); + state.ShouldBeNull(); } [Fact] @@ -229,15 +229,15 @@ namespace Dapr.Client.Test // Get Request & Validate var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.Should().Be("testStore"); - envelope.Key.Should().Be("test"); - envelope.Metadata.Should().BeEquivalentTo(metadata); + envelope.StoreName.ShouldBe("testStore"); + envelope.Key.ShouldBe("test"); + envelope.Metadata.ShouldBe(metadata); // Create Response & Respond var state = await request.CompleteWithMessageAsync(MakeGetStateResponse(null)); // Get response and validate - state.Should().BeNull(); + state.ShouldBeNull(); } [Fact] @@ -291,15 +291,15 @@ namespace Dapr.Client.Test // Get Request and validate var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.Should().Be("testStore"); - envelope.States.Count.Should().Be(1); + envelope.StoreName.ShouldBe("testStore"); + envelope.States.Count.ShouldBe(1); var state = envelope.States[0]; - state.Key.Should().Be("test"); + state.Key.ShouldBe("test"); var stateJson = state.Value.ToStringUtf8(); var stateFromRequest = JsonSerializer.Deserialize(stateJson, client.InnerClient.JsonSerializerOptions); - stateFromRequest.Size.Should().Be(widget.Size); - stateFromRequest.Color.Should().Be(widget.Color); + stateFromRequest.Size.ShouldBe(widget.Size); + stateFromRequest.Color.ShouldBe(widget.Color); } [Fact] @@ -329,20 +329,20 @@ namespace Dapr.Client.Test // Create Response & Validate var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.Should().Be("testStore"); - envelope.States.Count.Should().Be(3); + envelope.StoreName.ShouldBe("testStore"); + envelope.States.Count.ShouldBe(3); - envelope.States[0].Key.Should().Be("testKey1"); - envelope.States[0].Value.Should().Equal(ByteString.CopyFromUtf8(JsonSerializer.Serialize("testValue1"))); - envelope.States[0].Metadata.Should().ContainKey("partitionKey1"); + envelope.States[0].Key.ShouldBe("testKey1"); + envelope.States[0].Value.ShouldBe(ByteString.CopyFromUtf8(JsonSerializer.Serialize("testValue1"))); + envelope.States[0].Metadata.ShouldContainKey("partitionKey1"); - envelope.States[1].Key.Should().Be("testKey2"); - envelope.States[1].Value.Should().Equal(ByteString.CopyFromUtf8(JsonSerializer.Serialize("testValue2"))); - envelope.States[1].Metadata.Should().ContainKey("partitionKey2"); + envelope.States[1].Key.ShouldBe("testKey2"); + envelope.States[1].Value.ShouldBe(ByteString.CopyFromUtf8(JsonSerializer.Serialize("testValue2"))); + envelope.States[1].Metadata.ShouldContainKey("partitionKey2"); - envelope.States[2].Key.Should().Be("testKey3"); - envelope.States[2].Value.Should().Equal(ByteString.CopyFromUtf8(JsonSerializer.Serialize("testValue3"))); - envelope.States[2].Metadata.Should().ContainKey("partitionKey3"); + envelope.States[2].Key.ShouldBe("testKey3"); + envelope.States[2].Value.ShouldBe(ByteString.CopyFromUtf8(JsonSerializer.Serialize("testValue3"))); + envelope.States[2].Metadata.ShouldContainKey("partitionKey3"); } [Fact] @@ -374,11 +374,11 @@ namespace Dapr.Client.Test // Get Request and validate var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.Should().Be("testStore"); - envelope.States.Count.Should().Be(1); + envelope.StoreName.ShouldBe("testStore"); + envelope.States.Count.ShouldBe(1); var state = envelope.States[0]; - state.Key.Should().Be("test"); - state.Value.Should().Equal(ByteString.Empty); + state.Key.ShouldBe("test"); + state.Value.ShouldBe(ByteString.Empty); } [Fact] @@ -453,34 +453,34 @@ namespace Dapr.Client.Test // Get Request and validate var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.Should().Be("testStore"); - envelope.Operations.Count.Should().Be(3); + envelope.StoreName.ShouldBe("testStore"); + envelope.Operations.Count.ShouldBe(3); var req1 = envelope.Operations[0]; - req1.Request.Key.Should().Be("stateKey1"); - req1.OperationType.Should().Be(StateOperationType.Upsert.ToString().ToLower()); + req1.Request.Key.ShouldBe("stateKey1"); + req1.OperationType.ShouldBe(StateOperationType.Upsert.ToString().ToLower()); var valueJson1 = req1.Request.Value.ToStringUtf8(); var value1 = JsonSerializer.Deserialize(valueJson1, client.InnerClient.JsonSerializerOptions); - value1.Size.Should().Be(stateValue1.Size); - value1.Color.Should().Be(stateValue1.Color); - req1.Request.Etag.Value.Should().Be("testEtag"); - req1.Request.Metadata.Count.Should().Be(1); - req1.Request.Metadata["a"].Should().Be("b"); - req1.Request.Options.Concurrency.Should().Be(StateConcurrency.ConcurrencyLastWrite); + value1.Size.ShouldBe(stateValue1.Size); + value1.Color.ShouldBe(stateValue1.Color); + req1.Request.Etag.Value.ShouldBe("testEtag"); + req1.Request.Metadata.Count.ShouldBe(1); + req1.Request.Metadata["a"].ShouldBe("b"); + req1.Request.Options.Concurrency.ShouldBe(StateConcurrency.ConcurrencyLastWrite); var req2 = envelope.Operations[1]; - req2.Request.Key.Should().Be("stateKey2"); - req2.OperationType.Should().Be(StateOperationType.Delete.ToString().ToLower()); + req2.Request.Key.ShouldBe("stateKey2"); + req2.OperationType.ShouldBe(StateOperationType.Delete.ToString().ToLower()); var valueJson2 = req2.Request.Value.ToStringUtf8(); var value2 = JsonSerializer.Deserialize(valueJson2, client.InnerClient.JsonSerializerOptions); - value2.Should().Be(100); + value2.ShouldBe(100); var req3 = envelope.Operations[2]; - req3.Request.Key.Should().Be("stateKey3"); - req3.OperationType.Should().Be(StateOperationType.Upsert.ToString().ToLower()); + req3.Request.Key.ShouldBe("stateKey3"); + req3.OperationType.ShouldBe(StateOperationType.Upsert.ToString().ToLower()); var valueJson3 = req3.Request.Value.ToStringUtf8(); var value3 = JsonSerializer.Deserialize(valueJson3, client.InnerClient.JsonSerializerOptions); - value3.Should().Be("teststring"); + value3.ShouldBe("teststring"); } [Fact] @@ -541,8 +541,8 @@ namespace Dapr.Client.Test request.Dismiss(); var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.Should().Be("testStore"); - envelope.Key.Should().Be("test"); + envelope.StoreName.ShouldBe("testStore"); + envelope.Key.ShouldBe("test"); } [Fact] @@ -590,8 +590,8 @@ namespace Dapr.Client.Test var state = await request.CompleteWithMessageAsync(envelope); // Get response and validate - state.Value.Size.Should().Be("small"); - state.Value.Color.Should().Be("yellow"); + state.Value.Size.ShouldBe("small"); + state.Value.Color.ShouldBe("yellow"); } [Fact] @@ -605,8 +605,8 @@ namespace Dapr.Client.Test var envelope = MakeGetStateResponse(null); var state = await request.CompleteWithMessageAsync(envelope); - state.Key.Should().Be("test"); - state.Value.Should().BeNull(); + state.Key.ShouldBe("test"); + state.Value.ShouldBeNull(); } [Fact] @@ -620,9 +620,9 @@ namespace Dapr.Client.Test var data = new Widget() { Size = "small", Color = "yellow", }; var state = await request.CompleteWithMessageAsync(MakeGetStateResponse(data)); - state.Key.Should().Be("test"); - state.Value.Size.Should().Be("small"); - state.Value.Color.Should().Be("yellow"); + state.Key.ShouldBe("test"); + state.Value.Size.ShouldBe("small"); + state.Value.Color.ShouldBe("yellow"); // Modify the state and save it state.Value.Color = "green"; @@ -637,15 +637,15 @@ namespace Dapr.Client.Test // Get Request and validate var envelope = await request2.GetRequestEnvelopeAsync(); - envelope.StoreName.Should().Be("testStore"); - envelope.States.Count.Should().Be(1); + envelope.StoreName.ShouldBe("testStore"); + envelope.States.Count.ShouldBe(1); var requestState = envelope.States[0]; - requestState.Key.Should().Be("test"); + requestState.Key.ShouldBe("test"); var stateJson = requestState.Value.ToStringUtf8(); var stateFromRequest = JsonSerializer.Deserialize(stateJson, client.InnerClient.JsonSerializerOptions); - stateFromRequest.Size.Should().Be("small"); - stateFromRequest.Color.Should().Be("green"); + stateFromRequest.Size.ShouldBe("small"); + stateFromRequest.Color.ShouldBe("green"); } [Fact] @@ -659,9 +659,9 @@ namespace Dapr.Client.Test var data = new Widget() { Size = "small", Color = "yellow", }; var state = await request.CompleteWithMessageAsync(MakeGetStateResponse(data)); - state.Key.Should().Be("test"); - state.Value.Size.Should().Be("small"); - state.Value.Color.Should().Be("yellow"); + state.Key.ShouldBe("test"); + state.Value.Size.ShouldBe("small"); + state.Value.Color.ShouldBe("yellow"); state.Value.Color = "green"; var request2 = await client.CaptureGrpcRequestAsync(async daprClient => @@ -673,8 +673,8 @@ namespace Dapr.Client.Test // Get Request and validate var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.Should().Be("testStore"); - envelope.Key.Should().Be("test"); + envelope.StoreName.ShouldBe("testStore"); + envelope.Key.ShouldBe("test"); } @@ -713,22 +713,22 @@ namespace Dapr.Client.Test // Get Request and validate var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.Should().Be("testStore"); - envelope.States.Count.Should().Be(1); + envelope.StoreName.ShouldBe("testStore"); + envelope.States.Count.ShouldBe(1); var state = envelope.States[0]; - state.Key.Should().Be("test"); - state.Metadata.Count.Should().Be(2); - state.Metadata.Keys.Contains("key1").Should().BeTrue(); - state.Metadata.Keys.Contains("key2").Should().BeTrue(); - state.Metadata["key1"].Should().Be("value1"); - state.Metadata["key2"].Should().Be("value2"); - state.Options.Concurrency.Should().Be(expectedConcurrency); - state.Options.Consistency.Should().Be(expectedConsistency); + state.Key.ShouldBe("test"); + state.Metadata.Count.ShouldBe(2); + state.Metadata.Keys.Contains("key1").ShouldBeTrue(); + state.Metadata.Keys.Contains("key2").ShouldBeTrue(); + state.Metadata["key1"].ShouldBe("value1"); + state.Metadata["key2"].ShouldBe("value2"); + state.Options.Concurrency.ShouldBe(expectedConcurrency); + state.Options.Consistency.ShouldBe(expectedConsistency); var stateJson = state.Value.ToStringUtf8(); var stateFromRequest = JsonSerializer.Deserialize(stateJson, client.InnerClient.JsonSerializerOptions); - stateFromRequest.Size.Should().Be(widget.Size); - stateFromRequest.Color.Should().Be(widget.Color); + stateFromRequest.Size.ShouldBe(widget.Size); + stateFromRequest.Color.ShouldBe(widget.Color); } [Theory] @@ -762,22 +762,22 @@ namespace Dapr.Client.Test // Get Request and validate var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.Should().Be("testStore"); - envelope.States.Count.Should().Be(1); + envelope.StoreName.ShouldBe("testStore"); + envelope.States.Count.ShouldBe(1); var state = envelope.States[0]; - state.Etag.Value.Should().Be("Test_Etag"); - state.Metadata.Count.Should().Be(2); - state.Metadata.Keys.Contains("key1").Should().BeTrue(); - state.Metadata.Keys.Contains("key2").Should().BeTrue(); - state.Metadata["key1"].Should().Be("value1"); - state.Metadata["key2"].Should().Be("value2"); - state.Options.Concurrency.Should().Be(expectedConcurrency); - state.Options.Consistency.Should().Be(expectedConsistency); + state.Etag.Value.ShouldBe("Test_Etag"); + state.Metadata.Count.ShouldBe(2); + state.Metadata.Keys.Contains("key1").ShouldBeTrue(); + state.Metadata.Keys.Contains("key2").ShouldBeTrue(); + state.Metadata["key1"].ShouldBe("value1"); + state.Metadata["key2"].ShouldBe("value2"); + state.Options.Concurrency.ShouldBe(expectedConcurrency); + state.Options.Consistency.ShouldBe(expectedConsistency); var stateJson = state.Value.ToStringUtf8(); var stateFromRequest = JsonSerializer.Deserialize(stateJson, client.InnerClient.JsonSerializerOptions); - stateFromRequest.Size.Should().Be(widget.Size); - stateFromRequest.Color.Should().Be(widget.Color); + stateFromRequest.Size.ShouldBe(widget.Size); + stateFromRequest.Color.ShouldBe(widget.Color); } [Fact] @@ -827,8 +827,7 @@ namespace Dapr.Client.Test await client.CallStateApi() .Build(); - await FluentActions.Awaiting(async () => await client.DaprClient.TrySaveStateAsync("test", "test", "testValue", null)) - .Should().ThrowAsync(); + await Should.ThrowAsync(async () => await client.DaprClient.TrySaveStateAsync("test", "test", "testValue", null)); } [Fact] @@ -877,8 +876,7 @@ namespace Dapr.Client.Test await client.CallStateApi() .Build(); - await FluentActions.Awaiting(async () => await client.DaprClient.TryDeleteStateAsync("test", "test", null)) - .Should().ThrowAsync(); + await Should.ThrowAsync(async () => await client.DaprClient.TryDeleteStateAsync("test", "test", null)); } [Fact] @@ -943,10 +941,10 @@ namespace Dapr.Client.Test // Get Request and validate var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.Should().Be("testStore"); - envelope.Key.Should().Be("test"); - envelope.Options.Concurrency.Should().Be(expectedConcurrency); - envelope.Options.Consistency.Should().Be(expectedConsistency); + envelope.StoreName.ShouldBe("testStore"); + envelope.Key.ShouldBe("test"); + envelope.Options.Concurrency.ShouldBe(expectedConcurrency); + envelope.Options.Consistency.ShouldBe(expectedConsistency); } @@ -975,11 +973,11 @@ namespace Dapr.Client.Test // Get Request and validate var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.Should().Be("testStore"); - envelope.Key.Should().Be("test"); - envelope.Etag.Value.Should().Be("Test_Etag"); - envelope.Options.Concurrency.Should().Be(expectedConcurrency); - envelope.Options.Consistency.Should().Be(expectedConsistency); + envelope.StoreName.ShouldBe("testStore"); + envelope.Key.ShouldBe("test"); + envelope.Etag.Value.ShouldBe("Test_Etag"); + envelope.Options.Concurrency.ShouldBe(expectedConcurrency); + envelope.Options.Consistency.ShouldBe(expectedConsistency); } [Fact] @@ -1003,10 +1001,10 @@ namespace Dapr.Client.Test // Create Response & Validate var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.Should().Be("testStore"); - envelope.States.Count.Should().Be(1); - envelope.States[0].Key.Should().Be(key); - envelope.States[0].Metadata.Should().ContainKey("partitionKey"); + envelope.StoreName.ShouldBe("testStore"); + envelope.States.Count.ShouldBe(1); + envelope.States[0].Key.ShouldBe(key); + envelope.States[0].Metadata.ShouldContainKey("partitionKey"); } [Fact] @@ -1019,9 +1017,9 @@ namespace Dapr.Client.Test // Validate request. var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.Should().Be("testStore"); - envelope.Query.Should().Be(queryJson); - envelope.Metadata.Should().BeEmpty(); + envelope.StoreName.ShouldBe("testStore"); + envelope.Query.ShouldBe(queryJson); + envelope.Metadata.ShouldBeEmpty(); // Validate response. var testData = new Widget() { Color = "Green", Size = "Small" }; @@ -1029,11 +1027,11 @@ namespace Dapr.Client.Test wireResponse.Results.Add(MakeQueryStateItem("test", testData, "an etag")); var response = await request.CompleteWithMessageAsync(wireResponse); - response.Results.Count.Should().Be(1); - response.Results[0].Key.Should().Be("test"); - response.Results[0].Data.Should().Be(testData); - response.Results[0].ETag.Should().Be("an etag"); - response.Results[0].Error.Should().BeNullOrEmpty(); + response.Results.Count.ShouldBe(1); + response.Results[0].Key.ShouldBe("test"); + response.Results[0].Data.ShouldBe(testData); + response.Results[0].ETag.ShouldBe("an etag"); + response.Results[0].Error.ShouldBeNullOrEmpty(); } [Fact] @@ -1046,9 +1044,9 @@ namespace Dapr.Client.Test // Validate request. var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.Should().Be("testStore"); - envelope.Query.Should().Be(queryJson); - envelope.Metadata.Should().BeEmpty(); + envelope.StoreName.ShouldBe("testStore"); + envelope.Query.ShouldBe(queryJson); + envelope.Metadata.ShouldBeEmpty(); // Validate response, we expect to only get the first object as the 2nd will present an error. var testData1 = new Widget() { Color = "Green", Size = "Small" }; @@ -1061,21 +1059,21 @@ namespace Dapr.Client.Test wireResponse.Results.Add(MakeQueryStateItem("test3", testData3)); var ex = await Assert.ThrowsAsync>(() => request.CompleteWithMessageAsync(wireResponse)); - ex.Message.Should().Be("Encountered an error while processing state query results."); + ex.Message.ShouldBe("Encountered an error while processing state query results."); var response = ex.Response; - response.Results.Count.Should().Be(2); - response.Results[0].Key.Should().Be("test1"); - response.Results[0].Data.Should().Be(testData1); - response.Results[0].ETag.Should().BeNullOrEmpty(); - response.Results[0].Error.Should().BeNullOrEmpty(); - response.Results[1].Key.Should().Be("test3"); - response.Results[1].Data.Should().Be(testData3); - response.Results[1].ETag.Should().BeNullOrEmpty(); - response.Results[1].Error.Should().BeNullOrEmpty(); + response.Results.Count.ShouldBe(2); + response.Results[0].Key.ShouldBe("test1"); + response.Results[0].Data.ShouldBe(testData1); + response.Results[0].ETag.ShouldBeNullOrEmpty(); + response.Results[0].Error.ShouldBeNullOrEmpty(); + response.Results[1].Key.ShouldBe("test3"); + response.Results[1].Data.ShouldBe(testData3); + response.Results[1].ETag.ShouldBeNullOrEmpty(); + response.Results[1].Error.ShouldBeNullOrEmpty(); var failedKeys = ex.FailedKeys; - failedKeys.Count.Should().Be(1); - failedKeys[0].Should().Be("test2"); + failedKeys.Count.ShouldBe(1); + failedKeys[0].ShouldBe("test2"); } private Autogenerated.GetStateResponse MakeGetStateResponse(T state, string etag = null) @@ -1157,20 +1155,20 @@ namespace Dapr.Client.Test // Get Request and validate var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.Should().Be("testStore"); - envelope.States.Count.Should().Be(1); + envelope.StoreName.ShouldBe("testStore"); + envelope.States.Count.ShouldBe(1); var state = envelope.States[0]; - state.Key.Should().Be("test"); - state.Metadata.Count.Should().Be(2); - state.Metadata.Keys.Contains("key1").Should().BeTrue(); - state.Metadata.Keys.Contains("key2").Should().BeTrue(); - state.Metadata["key1"].Should().Be("value1"); - state.Metadata["key2"].Should().Be("value2"); - state.Options.Concurrency.Should().Be(expectedConcurrency); - state.Options.Consistency.Should().Be(expectedConsistency); + state.Key.ShouldBe("test"); + state.Metadata.Count.ShouldBe(2); + state.Metadata.Keys.Contains("key1").ShouldBeTrue(); + state.Metadata.Keys.Contains("key2").ShouldBeTrue(); + state.Metadata["key1"].ShouldBe("value1"); + state.Metadata["key2"].ShouldBe("value2"); + state.Options.Concurrency.ShouldBe(expectedConcurrency); + state.Options.Consistency.ShouldBe(expectedConsistency); var stateBinaryData = state.Value.ToStringUtf8(); - stateBinaryData.Should().Be(data); + stateBinaryData.ShouldBe(data); } [Fact] @@ -1189,13 +1187,13 @@ namespace Dapr.Client.Test // Get Request and validate var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.Should().Be("testStore"); - envelope.States.Count.Should().Be(1); + envelope.StoreName.ShouldBe("testStore"); + envelope.States.Count.ShouldBe(1); var state = envelope.States[0]; - state.Key.Should().Be("test"); + state.Key.ShouldBe("test"); var stateBinaryData = state.Value.ToStringUtf8(); - stateBinaryData.Should().Be(data); + stateBinaryData.ShouldBe(data); } [Fact] @@ -1213,11 +1211,11 @@ namespace Dapr.Client.Test // Get Request and validate var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.Should().Be("testStore"); - envelope.States.Count.Should().Be(1); + envelope.StoreName.ShouldBe("testStore"); + envelope.States.Count.ShouldBe(1); var state = envelope.States[0]; - state.Key.Should().Be("test"); - state.Value.Should().Equal(ByteString.Empty); + state.Key.ShouldBe("test"); + state.Value.ShouldBe(ByteString.Empty); } [Fact] @@ -1265,20 +1263,20 @@ namespace Dapr.Client.Test // Get Request and validate var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.Should().Be("testStore"); - envelope.States.Count.Should().Be(1); + envelope.StoreName.ShouldBe("testStore"); + envelope.States.Count.ShouldBe(1); var state = envelope.States[0]; - state.Etag.Value.Should().Be("Test_Etag"); - state.Metadata.Count.Should().Be(2); - state.Metadata.Keys.Contains("key1").Should().BeTrue(); - state.Metadata.Keys.Contains("key2").Should().BeTrue(); - state.Metadata["key1"].Should().Be("value1"); - state.Metadata["key2"].Should().Be("value2"); - state.Options.Concurrency.Should().Be(expectedConcurrency); - state.Options.Consistency.Should().Be(expectedConsistency); + state.Etag.Value.ShouldBe("Test_Etag"); + state.Metadata.Count.ShouldBe(2); + state.Metadata.Keys.Contains("key1").ShouldBeTrue(); + state.Metadata.Keys.Contains("key2").ShouldBeTrue(); + state.Metadata["key1"].ShouldBe("value1"); + state.Metadata["key2"].ShouldBe("value2"); + state.Options.Concurrency.ShouldBe(expectedConcurrency); + state.Options.Consistency.ShouldBe(expectedConsistency); var stateBinaryData = state.Value.ToStringUtf8(); - stateBinaryData.Should().Be(data); + stateBinaryData.ShouldBe(data); } [Fact] @@ -1332,8 +1330,7 @@ namespace Dapr.Client.Test var response = client.CallStateApi() .Build(); - await FluentActions.Awaiting(async () => await client.DaprClient.TrySaveByteStateAsync("test", "test", stateBytes.AsMemory(), null)) - .Should().ThrowAsync(); + await Should.ThrowAsync(async () => await client.DaprClient.TrySaveByteStateAsync("test", "test", stateBytes.AsMemory(), null)); } [Fact] @@ -1365,7 +1362,7 @@ namespace Dapr.Client.Test var state = await request.CompleteWithMessageAsync(envelope); // Get response and validate - state.ToArray().Should().BeNullOrEmpty(); + state.ToArray().ShouldBeEmpty(); } [Theory] @@ -1379,15 +1376,15 @@ namespace Dapr.Client.Test // Get Request & Validate var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.Should().Be("testStore"); - envelope.Key.Should().Be("test"); - envelope.Consistency.Should().Be(expectedConsistencyMode); + envelope.StoreName.ShouldBe("testStore"); + envelope.Key.ShouldBe("test"); + envelope.Consistency.ShouldBe(expectedConsistencyMode); var binaryData = Encoding.ASCII.GetBytes("test data"); // Create Response & Respond var state = await request.CompleteWithMessageAsync(MakeGetByteStateResponse(binaryData.AsMemory())); var stateStr = ByteString.CopyFrom(state.Span).ToByteArray(); // Get response and validate - stateStr.Should().BeEquivalentTo(binaryData); + stateStr.ShouldBeEquivalentTo(binaryData); } [Fact] @@ -1400,18 +1397,17 @@ namespace Dapr.Client.Test { "partitionKey", "mypartition" } }; var request = await client.CaptureGrpcRequestAsync(async daprClient => await daprClient.GetByteStateAndETagAsync("testStore", "test", metadata: metadata)); - // Get Request & Validate var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.Should().Be("testStore"); - envelope.Key.Should().Be("test"); - envelope.Metadata.Should().BeEquivalentTo(metadata); + envelope.StoreName.ShouldBe("testStore"); + envelope.Key.ShouldBe("test"); + envelope.Metadata.ShouldBe(metadata); var binaryData = Encoding.ASCII.GetBytes("test data"); // Create Response & Respond var (state, etag) = await request.CompleteWithMessageAsync((MakeGetByteStateResponse(binaryData.AsMemory()))); var stateStr = ByteString.CopyFrom(state.Span).ToByteArray(); // Get response and validate - stateStr.Should().BeEquivalentTo(binaryData); + stateStr.ShouldBe(binaryData); } [Fact] @@ -1442,8 +1438,8 @@ namespace Dapr.Client.Test var (state, etag) = await request.CompleteWithMessageAsync(envelope); var stateStr = ByteString.CopyFrom(state.Span).ToByteArray(); // Get response and validate - stateStr.Should().BeEquivalentTo(binaryData); - etag.Should().Be("Test_Etag"); + stateStr.ShouldBeEquivalentTo(binaryData); + etag.ShouldBe("Test_Etag"); } [Fact] diff --git a/test/Dapr.Client.Test/TryLockResponseTest.cs b/test/Dapr.Client.Test/TryLockResponseTest.cs index 3420dc23..f9b6f524 100644 --- a/test/Dapr.Client.Test/TryLockResponseTest.cs +++ b/test/Dapr.Client.Test/TryLockResponseTest.cs @@ -14,7 +14,7 @@ using System.Threading.Tasks; using Autogenerated = Dapr.Client.Autogen.Grpc.v1; using Xunit; -using FluentAssertions; +using Shouldly; using System; namespace Dapr.Client.Test @@ -37,10 +37,10 @@ namespace Dapr.Client.Test // Get Request and validate var envelope = await request.GetRequestEnvelopeAsync(); - envelope.StoreName.Should().Be("redis"); - envelope.ResourceId.Should().Be("resourceId"); - envelope.LockOwner.Should().Be("owner1"); - envelope.ExpiryInSeconds.Should().Be(1000); + envelope.StoreName.ShouldBe("redis"); + envelope.ResourceId.ShouldBe("resourceId"); + envelope.LockOwner.ShouldBe("owner1"); + envelope.ExpiryInSeconds.ShouldBe(1000); // Get response and validate var invokeResponse = new Autogenerated.TryLockResponse{ @@ -56,16 +56,16 @@ namespace Dapr.Client.Test return await daprClient.Unlock(storeName, resourceId, lockOwner); }); var unlockEnvelope = await unlockRequest.GetRequestEnvelopeAsync(); - unlockEnvelope.StoreName.Should().Be("redis"); - unlockEnvelope.ResourceId.Should().Be("resourceId"); - unlockEnvelope.LockOwner.Should().Be("owner1"); + unlockEnvelope.StoreName.ShouldBe("redis"); + unlockEnvelope.ResourceId.ShouldBe("resourceId"); + unlockEnvelope.LockOwner.ShouldBe("owner1"); var invokeUnlockResponse = new Autogenerated.UnlockResponse{ Status = Autogenerated.UnlockResponse.Types.Status.LockDoesNotExist }; var domainUnlockResponse = await unlockRequest.CompleteWithMessageAsync(invokeUnlockResponse); - domainUnlockResponse.status.Should().Be(LockStatus.LockDoesNotExist); + domainUnlockResponse.status.ShouldBe(LockStatus.LockDoesNotExist); } } -} \ No newline at end of file +} diff --git a/test/Dapr.Client.Test/TypeConvertersTest.cs b/test/Dapr.Client.Test/TypeConvertersTest.cs index 1e6b57ea..ff73de4c 100644 --- a/test/Dapr.Client.Test/TypeConvertersTest.cs +++ b/test/Dapr.Client.Test/TypeConvertersTest.cs @@ -14,7 +14,7 @@ namespace Dapr.Client.Test { using System.Text.Json; - using FluentAssertions; + using Shouldly; using Xunit; public class TypeConvertersTest @@ -32,9 +32,9 @@ namespace Dapr.Client.Test var any = TypeConverters.ToJsonAny(response, options); var type = TypeConverters.FromJsonAny(any, options); - type.Should().BeEquivalentTo(response); - any.TypeUrl.Should().Be(string.Empty); - type.Name.Should().Be("test"); + type.ShouldBeEquivalentTo(response); + any.TypeUrl.ShouldBe(string.Empty); + type.Name.ShouldBe("test"); } private class Response diff --git a/test/Dapr.Common.Test/Dapr.Common.Test.csproj b/test/Dapr.Common.Test/Dapr.Common.Test.csproj index 6e34c3a7..22120719 100644 --- a/test/Dapr.Common.Test/Dapr.Common.Test.csproj +++ b/test/Dapr.Common.Test/Dapr.Common.Test.csproj @@ -5,7 +5,6 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all - diff --git a/test/Dapr.E2E.Test/Actors/E2ETests.CustomSerializerTests.cs b/test/Dapr.E2E.Test/Actors/E2ETests.CustomSerializerTests.cs index 5a20fff3..26f01fb5 100644 --- a/test/Dapr.E2E.Test/Actors/E2ETests.CustomSerializerTests.cs +++ b/test/Dapr.E2E.Test/Actors/E2ETests.CustomSerializerTests.cs @@ -97,7 +97,7 @@ namespace Dapr.E2E.Test /// . (it's defined in the base of it.) /// That why was created, /// so there are now more then one method. - /// + /// [Fact] public async Task ActorCanSupportCustomSerializerAndCallMoreThenOneDefinedMethod() { diff --git a/test/Dapr.E2E.Test/Actors/E2ETests.WeaklyTypedTests.cs b/test/Dapr.E2E.Test/Actors/E2ETests.WeaklyTypedTests.cs index b250d731..5518f32b 100644 --- a/test/Dapr.E2E.Test/Actors/E2ETests.WeaklyTypedTests.cs +++ b/test/Dapr.E2E.Test/Actors/E2ETests.WeaklyTypedTests.cs @@ -17,7 +17,7 @@ namespace Dapr.E2E.Test using System.Threading.Tasks; using Dapr.Actors; using Dapr.E2E.Test.Actors.WeaklyTypedTesting; - using FluentAssertions; + using Shouldly; using Xunit; public partial class E2ETests : IAsyncLifetime @@ -34,7 +34,7 @@ namespace Dapr.E2E.Test var result = await proxy.InvokeMethodAsync(nameof(IWeaklyTypedTestingActor.GetPolymorphicResponse)); - result.Should().BeOfType().Which.DerivedProperty.Should().NotBeNullOrWhiteSpace(); + result.ShouldBeOfType().DerivedProperty.ShouldNotBeNullOrWhiteSpace(); } #else [Fact] @@ -48,7 +48,7 @@ namespace Dapr.E2E.Test var result = await proxy.InvokeMethodAsync(nameof(IWeaklyTypedTestingActor.GetPolymorphicResponse)); - result.Should().BeOfType().Which.DerivedProperty.Should().NotBeNullOrWhiteSpace(); + result.ShouldBeOfType().DerivedProperty.ShouldNotBeNullOrWhiteSpace(); } #endif [Fact] @@ -62,7 +62,7 @@ namespace Dapr.E2E.Test var result = await proxy.InvokeMethodAsync(nameof(IWeaklyTypedTestingActor.GetNullResponse)); - result.Should().BeNull(); + result.ShouldBeNull(); } } } diff --git a/test/Dapr.E2E.Test/Dapr.E2E.Test.csproj b/test/Dapr.E2E.Test/Dapr.E2E.Test.csproj index fc92396a..50379223 100644 --- a/test/Dapr.E2E.Test/Dapr.E2E.Test.csproj +++ b/test/Dapr.E2E.Test/Dapr.E2E.Test.csproj @@ -1,10 +1,10 @@  - + all diff --git a/test/Dapr.E2E.Test/DaprTestApp.cs b/test/Dapr.E2E.Test/DaprTestApp.cs index 2330785d..8776a43d 100644 --- a/test/Dapr.E2E.Test/DaprTestApp.cs +++ b/test/Dapr.E2E.Test/DaprTestApp.cs @@ -45,7 +45,7 @@ namespace Dapr.E2E.Test { var (appPort, httpPort, grpcPort, metricsPort) = GetFreePorts(); - var componentsPath = Combine(".", "..", "..", "..", "..", "..", "test", "Dapr.E2E.Test", "components"); + var resourcesPath = Combine(".", "..", "..", "..", "..", "..", "test", "Dapr.E2E.Test", "components"); var configPath = Combine(".", "..", "..", "..", "..", "..", "test", "Dapr.E2E.Test", "configuration", "featureconfig.yaml"); var arguments = new List() { @@ -55,11 +55,10 @@ namespace Dapr.E2E.Test "--dapr-http-port", httpPort.ToString(CultureInfo.InvariantCulture), "--dapr-grpc-port", grpcPort.ToString(CultureInfo.InvariantCulture), "--metrics-port", metricsPort.ToString(CultureInfo.InvariantCulture), - "--components-path", componentsPath, + "--resources-path", resourcesPath, "--config", configPath, "--log-level", "debug", - "--dapr-http-max-request-size", "32", - + "--max-body-size", "8Mi" }; if (configuration.UseAppPort) diff --git a/test/Dapr.Extensions.Configuration.Test/Dapr.Extensions.Configuration.Test.csproj b/test/Dapr.Extensions.Configuration.Test/Dapr.Extensions.Configuration.Test.csproj index ef6cfbce..3900bd7e 100644 --- a/test/Dapr.Extensions.Configuration.Test/Dapr.Extensions.Configuration.Test.csproj +++ b/test/Dapr.Extensions.Configuration.Test/Dapr.Extensions.Configuration.Test.csproj @@ -5,9 +5,9 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all - + all diff --git a/test/Dapr.Extensions.Configuration.Test/DaprSecretStoreConfigurationProviderTest.cs b/test/Dapr.Extensions.Configuration.Test/DaprSecretStoreConfigurationProviderTest.cs index fd323f98..d92fc46b 100644 --- a/test/Dapr.Extensions.Configuration.Test/DaprSecretStoreConfigurationProviderTest.cs +++ b/test/Dapr.Extensions.Configuration.Test/DaprSecretStoreConfigurationProviderTest.cs @@ -16,11 +16,10 @@ using System.Collections.Generic; using System.Net; using System.Threading.Tasks; using Dapr.Client; -using Dapr; -using FluentAssertions; using Grpc.Net.Client; using Microsoft.Extensions.Configuration; using Moq; +using Shouldly; using Xunit; using Autogenerated = Dapr.Client.Autogen.Grpc.v1; @@ -163,7 +162,7 @@ namespace Dapr.Extensions.Configuration.Test .AddDaprSecretStore(storeName, secretDescriptors, daprClient.Object) .Build(); - config["secretName"].Should().Be("secret"); + config["secretName"].ShouldBe("secret"); } [Fact] @@ -195,8 +194,8 @@ namespace Dapr.Extensions.Configuration.Test .AddDaprSecretStore(storeName, secretDescriptors, daprClient.Object) .Build(); - config[firstSecretKey].Should().Be(firstSecretValue); - config[secondSecretKey].Should().Be(secondSecretValue); + config[firstSecretKey].ShouldBe(firstSecretValue); + config[secondSecretKey].ShouldBe(secondSecretValue); } [Fact] @@ -224,8 +223,8 @@ namespace Dapr.Extensions.Configuration.Test .AddDaprSecretStore(storeName, secretDescriptors, daprClient.Object) .Build(); - config[firstSecretKey].Should().Be(firstSecretValue); - config[secondSecretKey].Should().Be(secondSecretValue); + config[firstSecretKey].ShouldBe(firstSecretValue); + config[secondSecretKey].ShouldBe(secondSecretValue); } [Fact] @@ -252,7 +251,7 @@ namespace Dapr.Extensions.Configuration.Test .AddDaprSecretStore(storeName, secretDescriptors, daprClient.Object) .Build(); - config[secretName].Should().Be(secretValue); + config[secretName].ShouldBe(secretValue); } [Fact] @@ -282,7 +281,7 @@ namespace Dapr.Extensions.Configuration.Test .AddDaprSecretStore(storeName, secretDescriptors, daprClient) .Build(); - config[secretName].Should().BeNull(); + config[secretName].ShouldBeNull(); } [Fact] @@ -415,7 +414,7 @@ namespace Dapr.Extensions.Configuration.Test .AddDaprSecretStore("store", daprClient) .Build(); - config["secretName"].Should().Be("secret"); + config["secretName"].ShouldBe("secret"); } [Fact] @@ -442,8 +441,8 @@ namespace Dapr.Extensions.Configuration.Test .AddDaprSecretStore("store", daprClient) .Build(); - config["first_secret"].Should().Be("secret1"); - config["second_secret"].Should().Be("secret2"); + config["first_secret"].ShouldBe("secret1"); + config["second_secret"].ShouldBe("secret2"); } [Fact] @@ -468,7 +467,7 @@ namespace Dapr.Extensions.Configuration.Test .AddDaprSecretStore("store", new DaprSecretDescriptor[] { new DaprSecretDescriptor("secretName__value") }, daprClient) .Build(); - config["secretName:value"].Should().Be("secret"); + config["secretName:value"].ShouldBe("secret"); } [Fact] @@ -494,7 +493,7 @@ namespace Dapr.Extensions.Configuration.Test .AddDaprSecretStore("store", daprClient) .Build(); - config["first_secret:value"].Should().Be("secret1"); + config["first_secret:value"].ShouldBe("secret1"); } [Fact] @@ -525,7 +524,7 @@ namespace Dapr.Extensions.Configuration.Test }) .Build(); - config["secretName__value"].Should().Be("secret"); + config["secretName__value"].ShouldBe("secret"); } [Fact] @@ -556,7 +555,7 @@ namespace Dapr.Extensions.Configuration.Test }) .Build(); - config["first_secret__value"].Should().Be("secret1"); + config["first_secret__value"].ShouldBe("secret1"); } [Fact] @@ -590,7 +589,7 @@ namespace Dapr.Extensions.Configuration.Test }) .Build(); - config["secretName:value"].Should().Be("secret"); + config["secretName:value"].ShouldBe("secret"); } [Fact] @@ -624,7 +623,7 @@ namespace Dapr.Extensions.Configuration.Test }) .Build(); - config["secretName--value"].Should().Be("secret"); + config["secretName--value"].ShouldBe("secret"); } [Fact] @@ -658,7 +657,7 @@ namespace Dapr.Extensions.Configuration.Test }) .Build(); - config["secretName--value"].Should().Be("secret"); + config["secretName--value"].ShouldBe("secret"); } [Fact] @@ -714,8 +713,8 @@ namespace Dapr.Extensions.Configuration.Test }) .Build(); - config["secretName:value"].Should().Be("secret"); - config["otherSecretName:value"].Should().Be("secret"); + config["secretName:value"].ShouldBe("secret"); + config["otherSecretName:value"].ShouldBe("secret"); } [Fact] @@ -743,7 +742,7 @@ namespace Dapr.Extensions.Configuration.Test .AddDaprSecretStore("store", daprClient, new[] { "--" }) .Build(); - config["first_secret:value"].Should().Be("secret1"); + config["first_secret:value"].ShouldBe("secret1"); } [Fact] @@ -772,8 +771,8 @@ namespace Dapr.Extensions.Configuration.Test .AddDaprSecretStore("store", daprClient, new[] { "--", "≡" }) .Build(); - config["first_secret:value"].Should().Be("secret1"); - config["second_secret:value"].Should().Be("secret2"); + config["first_secret:value"].ShouldBe("secret1"); + config["second_secret:value"].ShouldBe("secret2"); } [Fact] @@ -803,9 +802,9 @@ namespace Dapr.Extensions.Configuration.Test .AddDaprSecretStore("store", daprClient, new[] { "--", "≡" }) .Build(); - config["first_secret:value"].Should().Be("secret1"); - config["second_secret:value"].Should().Be("secret2"); - config["third_secret:value"].Should().Be("secret3"); + config["first_secret:value"].ShouldBe("secret1"); + config["second_secret:value"].ShouldBe("secret2"); + config["third_secret:value"].ShouldBe("secret3"); } [Fact] @@ -840,7 +839,7 @@ namespace Dapr.Extensions.Configuration.Test }) .Build(); - config["secretName--value"].Should().Be("secret"); + config["secretName--value"].ShouldBe("secret"); } [Fact] @@ -874,7 +873,7 @@ namespace Dapr.Extensions.Configuration.Test }) .Build(); - config["first_secret--value"].Should().Be("secret1"); + config["first_secret--value"].ShouldBe("secret1"); } [Fact] diff --git a/test/Dapr.Jobs.Test/Extensions/EndpointRouteBuilderExtensionsTests.cs b/test/Dapr.Jobs.Test/Extensions/EndpointRouteBuilderExtensionsTests.cs index fa4d094e..4220db7b 100644 --- a/test/Dapr.Jobs.Test/Extensions/EndpointRouteBuilderExtensionsTests.cs +++ b/test/Dapr.Jobs.Test/Extensions/EndpointRouteBuilderExtensionsTests.cs @@ -14,6 +14,9 @@ #nullable enable using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; using System.Net.Http; using System.Text; using System.Text.Json; @@ -25,8 +28,11 @@ using Dapr.Jobs.Models.Responses; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Moq; using Xunit; namespace Dapr.Jobs.Test.Extensions; @@ -40,15 +46,7 @@ public class EndpointRouteBuilderExtensionsTest var client = server.CreateClient(); var serializedPayload = JsonSerializer.Serialize(new SamplePayload("Dapr", 789)); - var serializedPayloadBytes = Encoding.UTF8.GetBytes(serializedPayload); - var jobDetails = new DaprJobDetails(new DaprJobSchedule("0 0 * * *")) - { - RepeatCount = 5, - DueTime = DateTimeOffset.UtcNow, - Ttl = DateTimeOffset.UtcNow.AddHours(1), - Payload = serializedPayloadBytes - }; - var content = new StringContent(JsonSerializer.Serialize(jobDetails), Encoding.UTF8, "application/json"); + var content = new StringContent(serializedPayload, Encoding.UTF8, "application/json"); const string jobName = "testJob"; var response = await client.PostAsync($"/job/{jobName}", content); @@ -68,15 +66,7 @@ public class EndpointRouteBuilderExtensionsTest var client = server.CreateClient(); var serializedPayload = JsonSerializer.Serialize(new SamplePayload("Dapr", 789)); - var serializedPayloadBytes = Encoding.UTF8.GetBytes(serializedPayload); - var jobDetails = new DaprJobDetails(new DaprJobSchedule("0 0 * * *")) - { - RepeatCount = 5, - DueTime = DateTimeOffset.UtcNow, - Ttl = DateTimeOffset.UtcNow.AddHours(1), - Payload = serializedPayloadBytes - }; - var content = new StringContent(JsonSerializer.Serialize(jobDetails), Encoding.UTF8, "application/json"); + var content = new StringContent(serializedPayload, Encoding.UTF8, "application/json"); const string jobName = "testJob"; var response = await client.PostAsync($"/job/{jobName}", content); @@ -88,32 +78,117 @@ public class EndpointRouteBuilderExtensionsTest Assert.Equal(jobName, validator.JobName); Assert.Equal(serializedPayload, validator.SerializedPayload); } - [Fact] - public async Task MapDaprScheduledJobHandler_InvalidPayload() + public async Task MapDaprScheduledJobHandler_HandlesTimeoutCorrectly() { // Arrange - var server = CreateTestServer(); - var client = server.CreateClient(); + var timeout = TimeSpan.FromSeconds(5); + const string testJobName = "testJob"; + var testJobPayload = Encoding.UTF8.GetBytes("testPayload"); - var content = new StringContent("", Encoding.UTF8, "application/json"); + var builder = new WebHostBuilder() + .ConfigureServices(services => + { + services.AddLogging(); + services.AddRouting(); + }) + .Configure(app => + { + app.UseRouting(); + app.UseEndpoints(endpoints => + { + endpoints.MapDaprScheduledJobHandler(async ( + string jobName, + ReadOnlyMemory jobPayload, + ILogger? logger, + CancellationToken cancellationToken) => + { + logger?.LogInformation("Received trigger invocation for job '{jobName}'", jobName); - // Act - const string jobName = "testJob"; - var response = await client.PostAsync($"/job/{jobName}", content); + var deserializedPayload = Encoding.UTF8.GetString(jobPayload.Span); + logger?.LogInformation( + "Received invocation for the job '{jobName}' with payload '{deserializedPayload}'", + jobName, deserializedPayload); + await Task.Delay(TimeSpan.FromSeconds(1), + cancellationToken); //Less than the timeout, so this should work without throwing - var validator = server.Services.GetRequiredService(); - Assert.Equal(jobName, validator.JobName); - Assert.Null(validator.SerializedPayload); + return Task.CompletedTask; + }, timeout); + }); + }); + + var testServer = new TestServer(builder); + var client = testServer.CreateClient(); + + var requestContent = new ByteArrayContent(testJobPayload); + var request = new HttpRequestMessage(HttpMethod.Post, $"/job/{testJobName}") + { + Content = requestContent + }; + + // Act & Assert + var response = await client.SendAsync(request); + Assert.True(response.IsSuccessStatusCode); } + + [Fact] + public async Task MapDaprScheduledJobHandler_AppliesTimeoutCorrectly() + { + // Arrange + var timeout = TimeSpan.FromSeconds(1); + const string testJobName = "testJob"; + var testJobPayload = Encoding.UTF8.GetBytes("testPayload"); + var builder = new WebHostBuilder() + .ConfigureServices(services => + { + services.AddLogging(); + services.AddRouting(); + }) + .Configure(app => + { + app.UseRouting(); + app.UseEndpoints(endpoints => + { + endpoints.MapDaprScheduledJobHandler(async ( + string jobName, + ReadOnlyMemory jobPayload, + ILogger? logger, + CancellationToken cancellationToken) => + { + logger?.LogInformation("Received trigger invocation for job '{jobName}'", jobName); + + var deserializedPayload = Encoding.UTF8.GetString(jobPayload.Span); + logger?.LogInformation( + "Received invocation for the job '{jobName}' with payload '{deserializedPayload}'", + jobName, deserializedPayload); + await Task.Delay(timeout.Add(TimeSpan.FromSeconds(3)), + cancellationToken); //Intentionally delay longer than the timeout allows + + return Task.CompletedTask; + }, timeout); + }); + }); + + var testServer = new TestServer(builder); + var client = testServer.CreateClient(); + + var requestContent = new ByteArrayContent(testJobPayload); + var request = new HttpRequestMessage(HttpMethod.Post, $"/job/{testJobName}") + { + Content = requestContent + }; + + // Act & Assert + await Assert.ThrowsAsync(async () => await client.SendAsync(request)); + } + private sealed record SamplePayload(string Name, int Count); public sealed class Validator { public string? JobName { get; set; } - public string? SerializedPayload { get; set; } } @@ -130,15 +205,10 @@ public class EndpointRouteBuilderExtensionsTest app.UseRouting(); app.UseEndpoints(endpoints => { - endpoints.MapDaprScheduledJobHandler(async (string? jobName, DaprJobDetails? jobDetails, Validator validator, CancellationToken cancellationToken) => + endpoints.MapDaprScheduledJobHandler(async (string jobName, ReadOnlyMemory jobPayload, Validator validator, CancellationToken cancellationToken) => { - if (jobName is not null) - validator.JobName = jobName; - if (jobDetails?.Payload is not null) - { - var payloadString = Encoding.UTF8.GetString(jobDetails.Payload); - validator.SerializedPayload = payloadString; - } + validator.JobName = jobName; + validator.SerializedPayload = Encoding.UTF8.GetString(jobPayload.Span); await Task.CompletedTask; }); }); @@ -160,15 +230,12 @@ public class EndpointRouteBuilderExtensionsTest app.UseRouting(); app.UseEndpoints(endpoints => { - endpoints.MapDaprScheduledJobHandler(async (string? jobName, Validator validator, DaprJobDetails? jobDetails) => + endpoints.MapDaprScheduledJobHandler(async (string jobName, Validator validator, ReadOnlyMemory payload) => { - if (jobName is not null) - validator.JobName = jobName; - if (jobDetails?.Payload is not null) - { - var payloadString = Encoding.UTF8.GetString(jobDetails.Payload); - validator.SerializedPayload = payloadString; - } + validator.JobName = jobName; + + var payloadString = Encoding.UTF8.GetString(payload.Span); + validator.SerializedPayload = payloadString; await Task.CompletedTask; }); }); diff --git a/test/Dapr.Jobs.Test/Models/DaprJobScheduleTests.cs b/test/Dapr.Jobs.Test/Models/DaprJobScheduleTests.cs index 17eb4836..da0afb43 100644 --- a/test/Dapr.Jobs.Test/Models/DaprJobScheduleTests.cs +++ b/test/Dapr.Jobs.Test/Models/DaprJobScheduleTests.cs @@ -23,7 +23,16 @@ public sealed class DaprJobScheduleTests public void FromDuration_Validate() { var schedule = DaprJobSchedule.FromDuration(new TimeSpan(12, 8, 16)); - Assert.Equal("12h8m16s", schedule.ExpressionValue); + Assert.Equal("@every 12h8m16s", schedule.ExpressionValue); + } + + [Fact] + public void FromExpression_Duration() + { + var every5Seconds = new TimeSpan(0, 0, 0, 5); + var schedule = DaprJobSchedule.FromDuration(every5Seconds); + + Assert.Equal("@every 5s", schedule.ExpressionValue); } [Fact] @@ -108,7 +117,7 @@ public sealed class DaprJobScheduleTests Assert.True(schedule.IsDurationExpression); Assert.False(schedule.IsPointInTimeExpression); Assert.False(schedule.IsCronExpression); - Assert.False(schedule.IsPrefixedPeriodExpression); + Assert.True(schedule.IsPrefixedPeriodExpression); //A duration expression _is_ a prefixed period with @every } [Fact] diff --git a/test/Dapr.Messaging.Test/Extensions/PublishSubscribeServiceCollectionExtensionsTests.cs b/test/Dapr.Messaging.Test/Extensions/PublishSubscribeServiceCollectionExtensionsTests.cs index d8e218d5..e51aa837 100644 --- a/test/Dapr.Messaging.Test/Extensions/PublishSubscribeServiceCollectionExtensionsTests.cs +++ b/test/Dapr.Messaging.Test/Extensions/PublishSubscribeServiceCollectionExtensionsTests.cs @@ -15,7 +15,6 @@ using Dapr.Messaging.PublishSubscribe; using Dapr.Messaging.PublishSubscribe.Extensions; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using Moq; namespace Dapr.Messaging.Test.Extensions;