Merge branch 'master' into actor-source-gen

This commit is contained in:
Whit Waldo 2025-03-16 20:30:58 -05:00 committed by GitHub
commit 6d8f83a2e6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
95 changed files with 2932 additions and 2405 deletions

View File

@ -48,8 +48,8 @@ jobs:
GOOS: linux GOOS: linux
GOARCH: amd64 GOARCH: amd64
GOPROXY: https://proxy.golang.org GOPROXY: https://proxy.golang.org
DAPR_CLI_VER: 1.14.0 DAPR_CLI_VER: 1.15.0
DAPR_RUNTIME_VER: 1.14.0 DAPR_RUNTIME_VER: 1.15.3
DAPR_INSTALL_URL: https://raw.githubusercontent.com/dapr/cli/release-1.14/install/install.sh DAPR_INSTALL_URL: https://raw.githubusercontent.com/dapr/cli/release-1.14/install/install.sh
DAPR_CLI_REF: '' DAPR_CLI_REF: ''
steps: steps:

View File

@ -7,7 +7,6 @@
<PackageVersion Include="BenchmarkDotNet" Version="0.14.0" /> <PackageVersion Include="BenchmarkDotNet" Version="0.14.0" />
<PackageVersion Include="coverlet.collector" Version="6.0.2" /> <PackageVersion Include="coverlet.collector" Version="6.0.2" />
<PackageVersion Include="coverlet.msbuild" Version="6.0.2" /> <PackageVersion Include="coverlet.msbuild" Version="6.0.2" />
<PackageVersion Include="FluentAssertions" Version="5.9.0" />
<PackageVersion Include="GitHubActionsTestLogger" Version="1.1.2" /> <PackageVersion Include="GitHubActionsTestLogger" Version="1.1.2" />
<PackageVersion Include="Google.Api.CommonProtos" Version="2.2.0" /> <PackageVersion Include="Google.Api.CommonProtos" Version="2.2.0" />
<PackageVersion Include="Google.Protobuf" Version="3.28.2" /> <PackageVersion Include="Google.Protobuf" Version="3.28.2" />
@ -24,8 +23,8 @@
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.SourceGenerators.Testing" Version="1.1.2" /> <PackageVersion Include="Microsoft.CodeAnalysis.CSharp.SourceGenerators.Testing" Version="1.1.2" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.SourceGenerators.Testing.XUnit" Version="1.1.2" /> <PackageVersion Include="Microsoft.CodeAnalysis.CSharp.SourceGenerators.Testing.XUnit" Version="1.1.2" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.8.0" /> <PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.8.0" />
<PackageVersion Include="Microsoft.DurableTask.Client.Grpc" Version="1.3.0" /> <PackageVersion Include="Microsoft.DurableTask.Client.Grpc" Version="1.5.0" />
<PackageVersion Include="Microsoft.DurableTask.Worker.Grpc" Version="1.3.0" /> <PackageVersion Include="Microsoft.DurableTask.Worker.Grpc" Version="1.5.0" />
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="6.0.1" /> <PackageVersion Include="Microsoft.Extensions.Configuration" Version="6.0.1" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0" /> <PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" /> <PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" />
@ -43,6 +42,7 @@
<PackageVersion Include="Serilog.AspNetCore" Version="6.1.0" /> <PackageVersion Include="Serilog.AspNetCore" Version="6.1.0" />
<PackageVersion Include="Serilog.Sinks.Console" Version="4.1.0" /> <PackageVersion Include="Serilog.Sinks.Console" Version="4.1.0" />
<PackageVersion Include="Serilog.Sinks.File" Version="5.0.0" /> <PackageVersion Include="Serilog.Sinks.File" Version="5.0.0" />
<PackageVersion Include="Shouldly" Version="4.2.1" />
<PackageVersion Include="System.Formats.Asn1" Version="6.0.1" /> <PackageVersion Include="System.Formats.Asn1" Version="6.0.1" />
<PackageVersion Include="System.Text.Json" Version="6.0.10" /> <PackageVersion Include="System.Text.Json" Version="6.0.10" />
<PackageVersion Include="xunit" Version="2.9.2" /> <PackageVersion Include="xunit" Version="2.9.2" />

View File

@ -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 APIs
### Sidecar Health ### 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. 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.

View File

@ -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 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 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. [best practices documentation]({{< ref dotnet-jobsclient-usage.md >}}) for additional guidance.

View File

@ -63,8 +63,8 @@ the dependency injection registration in `Program.cs`, add the following line:
```cs ```cs
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
//Add anywhere between these two //Add anywhere between these two lines
builder.Services.AddDaprJobsClient(); //That's it builder.Services.AddDaprJobsClient();
var app = builder.Build(); 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. 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, 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<byte>` 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: 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(); var app = builder.Build();
//Add our endpoint registration //Add our endpoint registration
app.MapDaprScheduledJob("myJob", (IServiceProvider serviceProvider, string? jobName, JobDetails? jobDetails) => { app.MapDaprScheduledJob("myJob", (IServiceProvider serviceProvider, string jobName, ReadOnlyMemory<byte> jobPayload) => {
var logger = serviceProvider.GetService<ILogger>(); var logger = serviceProvider.GetService<ILogger>();
logger?.LogInformation("Received trigger invocation for '{jobName}'", "myJob"); logger?.LogInformation("Received trigger invocation for '{jobName}'", "myJob");
@ -237,13 +238,34 @@ builder.Services.AddDaprJobsClient();
var app = builder.Build(); var app = builder.Build();
//Add our endpoint registration //Add our endpoint registration
app.MapDaprScheduledJob("myJob", (string? jobName, JobDetails? jobDetails) => { app.MapDaprScheduledJob("myJob", (string jobName, ReadOnlyMemory<byte> jobPayload) => {
//Do something... //Do something...
}); });
app.Run(); 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<byte> jobPayload) => {
//Do something...
}, TimeSpan.FromSeconds(15)); //Assigns a maximum timeout of 15 seconds for handling the invocation request
app.Run();
```
## Register the job ## 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. 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.

View File

@ -165,31 +165,18 @@ var oneWeekFromNow = now.AddDays(7);
await daprJobsClient.ScheduleOneTimeJobWithPayloadAsync("myOtherJob", oneWeekFromNow, "This is a test!"); await daprJobsClient.ScheduleOneTimeJobWithPayloadAsync("myOtherJob", oneWeekFromNow, "This is a test!");
``` ```
The `JobDetails` type returns the data as a `ReadOnlyMemory<byte>?` 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<byte>` that is populated with the bytes originally provided during the job registration.
Because the payload is stored as a `ReadOnlyMemory<byte>`, 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 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 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. 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: To deserialize the bytes to a string, the following helper method can be used:
```cs ```cs
if (jobDetails.Payload is not null) var payloadAsString = Encoding.UTF8.GetString(jobPayload.Span); //If successful, returns a string with the value
{
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<Doodad>();
}
``` ```
## Error handling ## Error handling

View File

@ -20,7 +20,7 @@ To load secrets into configuration call the _AddDaprSecretStore_ extension metho
Use Dapr to run the application: Use Dapr to run the application:
```shell ```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 ### 2. Test the application

View File

@ -147,7 +147,7 @@ cd examples/Client/ConfigurationApi
To run the `ConfigurationExample`, execute the following command: To run the `ConfigurationExample`, execute the following command:
```bash ```bash
dapr run --app-id configexample --components-path ./Components -- dotnet run dapr run --app-id configexample --resources-path ./Components -- dotnet run
``` ```
### Get Configuration ### Get Configuration

View File

@ -1,25 +0,0 @@
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: azurekeyvault
spec:
type: crypto.azure.keyvault
metadata:
- name: vaultName
value: "<changeMe>"
- 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

View File

@ -1,7 +0,0 @@
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: envvar-secret-store
spec:
type: secretstores.local.env
version: v1

View File

@ -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

View File

@ -17,16 +17,13 @@ using Dapr.Client;
namespace Cryptography.Examples 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 string DisplayName => "Use Cryptography to encrypt and decrypt a file";
public override async Task RunAsync(CancellationToken cancellationToken) public override async Task RunAsync(CancellationToken cancellationToken)
{ {
using var client = new DaprClientBuilder().Build(); 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 // The name of the file we're using as an example
const string fileName = "file.txt"; const string fileName = "file.txt";
@ -35,7 +32,6 @@ namespace Cryptography.Examples
{ {
Console.WriteLine(line); Console.WriteLine(line);
} }
Console.WriteLine();
//Encrypt from a file stream and buffer the resulting bytes to an in-memory buffer //Encrypt from a file stream and buffer the resulting bytes to an in-memory buffer
await using var encryptFs = new FileStream(fileName, FileMode.Open); await using var encryptFs = new FileStream(fileName, FileMode.Open);
@ -48,8 +44,8 @@ namespace Cryptography.Examples
bufferedEncryptedBytes.Write(bytes.Span); bufferedEncryptedBytes.Write(bytes.Span);
} }
Console.WriteLine($"Encrypted bytes: {Convert.ToBase64String(bufferedEncryptedBytes.GetSpan())}"); Console.WriteLine("Encrypted bytes:");
Console.WriteLine(); Console.WriteLine(Convert.ToBase64String(bufferedEncryptedBytes.WrittenMemory.ToArray()));
//We'll write to a temporary file via a FileStream //We'll write to a temporary file via a FileStream
var tempDecryptedFile = Path.GetTempFileName(); var tempDecryptedFile = Path.GetTempFileName();
@ -67,7 +63,7 @@ namespace Cryptography.Examples
//Let's confirm the value as written to the file //Let's confirm the value as written to the file
var decryptedValue = await File.ReadAllTextAsync(tempDecryptedFile, cancellationToken); var decryptedValue = await File.ReadAllTextAsync(tempDecryptedFile, cancellationToken);
Console.WriteLine($"Decrypted value: "); Console.WriteLine("Decrypted value: ");
Console.WriteLine(decryptedValue); Console.WriteLine(decryptedValue);
//And some cleanup to delete our temp file //And some cleanup to delete our temp file

View File

@ -17,7 +17,7 @@ using Dapr.Client;
namespace Cryptography.Examples 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 string DisplayName => "Using Cryptography to encrypt and decrypt a string";
@ -25,10 +25,6 @@ namespace Cryptography.Examples
{ {
using var client = new DaprClientBuilder().Build(); 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"; const string plaintextStr = "This is the value we're going to encrypt today";
Console.WriteLine($"Original string value: '{plaintextStr}'"); Console.WriteLine($"Original string value: '{plaintextStr}'");
@ -40,7 +36,7 @@ namespace Cryptography.Examples
Console.WriteLine($"Encrypted bytes: '{Convert.ToBase64String(encryptedBytesResult.Span)}'"); Console.WriteLine($"Encrypted bytes: '{Convert.ToBase64String(encryptedBytesResult.Span)}'");
//Decrypt the string //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())}'"); Console.WriteLine($"Decrypted string: '{Encoding.UTF8.GetString(decryptedBytes.ToArray())}'");
} }
} }

View File

@ -17,10 +17,13 @@ namespace Cryptography
{ {
class Program 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[] private static readonly Example[] Examples = new Example[]
{ {
new EncryptDecryptStringExample(), new EncryptDecryptStringExample(ComponentName, KeyName),
new EncryptDecryptFileStreamExample() new EncryptDecryptFileStreamExample(ComponentName, KeyName)
}; };
static async Task<int> Main(string[] args) static async Task<int> Main(string[] args)
@ -34,7 +37,7 @@ namespace Cryptography
return 0; 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++) for (var i = 0; i < Examples.Length; i++)
{ {
Console.WriteLine($"{i}: {Examples[i].DisplayName}"); Console.WriteLine($"{i}: {Examples[i].DisplayName}");

View File

@ -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 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. 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 ## Running the example
To run the sample locally, run this command in the DaprClient directory: To run the sample locally, run this command in the DaprClient directory:

View File

@ -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-----

View File

@ -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: In order to run the application that generates data for the workers to process, simply run the following command:
```bash ```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`. 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: 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 ```bash
dapr run --components-path ./Components --app-id worker --app-port 5000 -- dotnet run dapr run --resources-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 5001 -- dotnet run
``` ```
After running the applications, they will attempt to process files. You should see output such as: After running the applications, they will attempt to process files. You should see output such as:

View File

@ -16,43 +16,36 @@ using System.Text;
using Dapr.Jobs; using Dapr.Jobs;
using Dapr.Jobs.Extensions; using Dapr.Jobs.Extensions;
using Dapr.Jobs.Models; using Dapr.Jobs.Models;
using Dapr.Jobs.Models.Responses;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDaprJobsClient(); builder.Services.AddDaprJobsClient();
builder.Logging.ClearProviders();
builder.Logging.AddConsole();
var app = builder.Build(); var app = builder.Build();
//Set a handler to deal with incoming jobs //Set a handler to deal with incoming jobs
var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(5)); app.MapDaprScheduledJobHandler(async (string jobName, ReadOnlyMemory<byte> jobPayload, ILogger? logger, CancellationToken cancellationToken) =>
app.MapDaprScheduledJobHandler((string? jobName, DaprJobDetails? jobDetails, ILogger? logger, CancellationToken cancellationToken) =>
{ {
logger?.LogInformation("Received trigger invocation for job '{jobName}'", jobName); logger?.LogInformation("Received trigger invocation for job '{jobName}'", jobName);
if (jobDetails?.Payload is not null)
{ var deserializedPayload = Encoding.UTF8.GetString(jobPayload.Span);
var deserializedPayload = Encoding.UTF8.GetString(jobDetails.Payload);
logger?.LogInformation("Received invocation for the job '{jobName}' with payload '{deserializedPayload}'", logger?.LogInformation("Received invocation for the job '{jobName}' with payload '{deserializedPayload}'",
jobName, deserializedPayload); jobName, deserializedPayload);
//Do something that needs the cancellation token await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
}
else
{
logger?.LogWarning("Failed to deserialize payload for job '{jobName}'", jobName);
}
return Task.CompletedTask; return Task.CompletedTask;
}, cancellationTokenSource.Token); }, TimeSpan.FromSeconds(5));
app.Run(); using var scope = app.Services.CreateScope();
var logger = scope.ServiceProvider.GetRequiredService<ILogger<Program>>();
await using var scope = app.Services.CreateAsyncScope();
var logger = scope.ServiceProvider.GetRequiredService<ILogger>();
var daprJobsClient = scope.ServiceProvider.GetRequiredService<DaprJobsClient>(); var daprJobsClient = scope.ServiceProvider.GetRequiredService<DaprJobsClient>();
logger.LogInformation("Scheduling one-time job 'myJob' to execute 10 seconds from now"); logger.LogInformation("Scheduling one-time job 'myJob' to execute 10 seconds from now");
await daprJobsClient.ScheduleJobAsync("myJob", DaprJobSchedule.FromDateTime(DateTime.UtcNow.AddSeconds(10)), await daprJobsClient.ScheduleJobAsync("myJob", DaprJobSchedule.FromDuration(TimeSpan.FromSeconds(2)),
Encoding.UTF8.GetBytes("This is a test")); Encoding.UTF8.GetBytes("This is a test"), repeats: 10);
logger.LogInformation("Scheduled one-time job 'myJob'"); logger.LogInformation("Scheduled one-time job 'myJob'");
app.Run();
#pragma warning restore CS0618 // Type or member is obsolete #pragma warning restore CS0618 // Type or member is obsolete

View File

@ -76,8 +76,12 @@ public sealed class DaprConversationClient : DaprAIClient
}; };
if (options is not null) if (options is not null)
{
if (options.ConversationId is not null)
{ {
request.ContextID = options.ConversationId; request.ContextID = options.ConversationId;
}
request.ScrubPII = options.ScrubPII; request.ScrubPII = options.ScrubPII;
foreach (var (key, value) in options.Metadata) foreach (var (key, value) in options.Metadata)
@ -96,7 +100,7 @@ public sealed class DaprConversationClient : DaprAIClient
request.Inputs.Add(new P.ConversationInput request.Inputs.Add(new P.ConversationInput
{ {
ScrubPII = input.ScrubPII, ScrubPII = input.ScrubPII,
Message = input.Message, Content = input.Content,
Role = input.Role.GetValueFromEnumMember() Role = input.Role.GetValueFromEnumMember()
}); });
} }

View File

@ -16,7 +16,7 @@ namespace Dapr.AI.Conversation;
/// <summary> /// <summary>
/// Represents an input for the Dapr Conversational API. /// Represents an input for the Dapr Conversational API.
/// </summary> /// </summary>
/// <param name="Message">The message to send to the LLM.</param> /// <param name="Content">The content to send to the LLM.</param>
/// <param name="Role">The role indicating the entity providing the message.</param> /// <param name="Role">The role indicating the entity providing the message.</param>
/// <param name="ScrubPII">If true, scrubs the data that goes into the LLM.</param> /// <param name="ScrubPII">If true, scrubs the data that goes into the LLM.</param>
public sealed record DaprConversationInput(string Message, DaprConversationRole Role, bool ScrubPII = false); public sealed record DaprConversationInput(string Content, DaprConversationRole Role, bool ScrubPII = false);

View File

@ -265,7 +265,7 @@ namespace Dapr.Actors
return this.SendAsync(RequestFunc, relativeUrl, cancellationToken); return this.SendAsync(RequestFunc, relativeUrl, cancellationToken);
} }
public async Task<Stream> GetReminderAsync(string actorType, string actorId, string reminderName, CancellationToken cancellationToken = default) public async Task<HttpResponseMessage> GetReminderAsync(string actorType, string actorId, string reminderName, CancellationToken cancellationToken = default)
{ {
var relativeUrl = string.Format(CultureInfo.InvariantCulture, Constants.ActorReminderRelativeUrlFormat, actorType, actorId, reminderName); var relativeUrl = string.Format(CultureInfo.InvariantCulture, Constants.ActorReminderRelativeUrlFormat, actorType, actorId, reminderName);
@ -278,8 +278,7 @@ namespace Dapr.Actors
return request; return request;
} }
var response = await this.SendAsync(RequestFunc, relativeUrl, cancellationToken); return await this.SendAsync(RequestFunc, relativeUrl, cancellationToken);
return await response.Content.ReadAsStreamAsync();
} }
public Task UnregisterReminderAsync(string actorType, string actorId, string reminderName, CancellationToken cancellationToken = default) public Task UnregisterReminderAsync(string actorType, string actorId, string reminderName, CancellationToken cancellationToken = default)

View File

@ -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
{
/// <summary>
/// Used to parse the duration string accompanying an @every expression.
/// </summary>
private static readonly Regex durationRegex = new(@"(?<value>\d+(\.\d+)?)(?<unit>ns|us|µs|ms|s|m|h)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
/// <summary>
/// A regular expression used to evaluate whether a given prefix period embodies an @every statement.
/// </summary>
private static readonly Regex isEveryExpression = new(@"^@every (\d+(\.\d+)?(ns|us|µs|ms|s|m|h))+$");
/// <summary>
/// The various acceptable duration values for a period expression.
/// </summary>
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";
/// <summary>
/// Indicates that the schedule represents a prefixed period expression.
/// </summary>
/// <param name="expression"></param>
/// <returns></returns>
public static bool IsDurationExpression(this string expression) => expression.StartsWith('@') &&
(isEveryExpression.IsMatch(expression) ||
expression.EndsWithAny(acceptablePeriodValues, StringComparison.InvariantCulture));
/// <summary>
/// Creates a TimeSpan value from the prefixed period value.
/// </summary>
/// <param name="period">The prefixed period value to parse.</param>
/// <returns>A TimeSpan value matching the provided period.</returns>
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}");
}
}

View File

@ -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
{
/// <summary>
/// Extension method that validates a string against a list of possible matches.
/// </summary>
/// <param name="value">The string value to evaluate.</param>
/// <param name="possibleValues">The possible values to look for a match within.</param>
/// <param name="comparisonType">The type of string comparison to perform.</param>
/// <returns>True if the value ends with any of the possible values; otherwise false.</returns>
public static bool EndsWithAny(this string value, IReadOnlyList<string> possibleValues, StringComparison comparisonType )
=> possibleValues.Any(val => value.EndsWith(val, comparisonType));
}

View File

@ -11,6 +11,8 @@
// limitations under the License. // limitations under the License.
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
using System.Net.Http;
namespace Dapr.Actors namespace Dapr.Actors
{ {
using System.IO; using System.IO;
@ -81,8 +83,8 @@ namespace Dapr.Actors
/// <param name="actorId">ActorId.</param> /// <param name="actorId">ActorId.</param>
/// <param name="reminderName">Name of reminder to unregister.</param> /// <param name="reminderName">Name of reminder to unregister.</param>
/// <param name="cancellationToken">Cancels the operation.</param> /// <param name="cancellationToken">Cancels the operation.</param>
/// <returns>A <see cref="Task"/> representing the result of the asynchronous operation.</returns> /// <returns>A <see cref="Task"/> containing the response of the asynchronous HTTP operation.</returns>
Task<Stream> GetReminderAsync(string actorType, string actorId, string reminderName, CancellationToken cancellationToken = default); Task<HttpResponseMessage> GetReminderAsync(string actorType, string actorId, string reminderName, CancellationToken cancellationToken = default);
/// <summary> /// <summary>
/// Unregisters a reminder. /// Unregisters a reminder.

View File

@ -11,15 +11,17 @@
// limitations under the License. // limitations under the License.
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
namespace Dapr.Actors.Runtime using Dapr.Actors.Extensions;
{
namespace Dapr.Actors.Runtime;
using System; using System;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
internal class ConverterUtils internal static class ConverterUtils
{ {
private static Regex regex = new Regex("^(R(?<repetition>\\d+)/)?P((?<year>\\d+)Y)?((?<month>\\d+)M)?((?<week>\\d+)W)?((?<day>\\d+)D)?(T((?<hour>\\d+)H)?((?<minute>\\d+)M)?((?<second>\\d+)S)?)?$", RegexOptions.Compiled); private static Regex regex = new("^(R(?<repetition>\\d+)/)?P((?<year>\\d+)Y)?((?<month>\\d+)M)?((?<week>\\d+)W)?((?<day>\\d+)D)?(T((?<hour>\\d+)H)?((?<minute>\\d+)M)?((?<second>\\d+)S)?)?$", RegexOptions.Compiled);
public static TimeSpan ConvertTimeSpanFromDaprFormat(string valueString) public static TimeSpan ConvertTimeSpanFromDaprFormat(string valueString)
{ {
if (string.IsNullOrEmpty(valueString)) if (string.IsNullOrEmpty(valueString))
@ -28,6 +30,11 @@ namespace Dapr.Actors.Runtime
return never; return never;
} }
if (valueString.IsDurationExpression())
{
return valueString.FromPrefixedPeriod();
}
// TimeSpan is a string. Format returned by Dapr is: 1h4m5s4ms4us4ns // TimeSpan is a string. Format returned by Dapr is: 1h4m5s4ms4us4ns
// acceptable values are: m, s, ms, us(micro), ns // acceptable values are: m, s, ms, us(micro), ns
var spanOfValue = valueString.AsSpan(); var spanOfValue = valueString.AsSpan();
@ -63,6 +70,9 @@ namespace Dapr.Actors.Runtime
{ {
// write in format expected by Dapr, it only accepts h, m, s, ms, us(micro), ns // write in format expected by Dapr, it only accepts h, m, s, ms, us(micro), ns
var stringValue = string.Empty; var stringValue = string.Empty;
if (value is null)
return stringValue;
if (value.Value >= TimeSpan.Zero) if (value.Value >= TimeSpan.Zero)
{ {
var hours = (value.Value.Days * 24) + value.Value.Hours; var hours = (value.Value.Days * 24) + value.Value.Hours;
@ -86,28 +96,28 @@ namespace Dapr.Actors.Runtime
throw new ArgumentException("The TimeSpan value, combined with repetition cannot be in milliseconds.", nameof(value)); throw new ArgumentException("The TimeSpan value, combined with repetition cannot be in milliseconds.", nameof(value));
} }
builder.AppendFormat("R{0}/P", repetitions); builder.Append($"R{repetitions}/P");
if(value.Days > 0) if(value.Days > 0)
{ {
builder.AppendFormat("{0}D", value.Days); builder.Append($"{value.Days}D");
} }
builder.Append("T"); builder.Append("T");
if(value.Hours > 0) if(value.Hours > 0)
{ {
builder.AppendFormat("{0}H", value.Hours); builder.Append($"{value.Hours}H");
} }
if(value.Minutes > 0) if(value.Minutes > 0)
{ {
builder.AppendFormat("{0}M", value.Minutes); builder.Append($"{value.Minutes}M");
} }
if(value.Seconds > 0) if(value.Seconds > 0)
{ {
builder.AppendFormat("{0}S", value.Seconds); builder.Append($"{value.Seconds}S");
} }
return builder.ToString(); return builder.ToString();
} }
@ -125,6 +135,7 @@ namespace Dapr.Actors.Runtime
var repetition = matches.Groups["repetition"].Success ? int.Parse(matches.Groups["repetition"].Value) : (int?)null; var repetition = matches.Groups["repetition"].Success ? int.Parse(matches.Groups["repetition"].Value) : (int?)null;
var days = 0; var days = 0;
var year = matches.Groups["year"].Success ? int.Parse(matches.Groups["year"].Value) : 0; var year = matches.Groups["year"].Success ? int.Parse(matches.Groups["year"].Value) : 0;
days = year * 365; days = year * 365;
@ -145,4 +156,3 @@ namespace Dapr.Actors.Runtime
return (new TimeSpan(days, hour, minute, second), repetition); return (new TimeSpan(days, hour, minute, second), repetition);
} }
} }
}

View File

@ -15,6 +15,7 @@ using System;
using System.Text.Json; using System.Text.Json;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.IO; using System.IO;
using Grpc.Core;
namespace Dapr.Actors.Runtime namespace Dapr.Actors.Runtime
{ {
@ -45,9 +46,14 @@ namespace Dapr.Actors.Runtime
throw new ArgumentNullException(nameof(token)); throw new ArgumentNullException(nameof(token));
} }
var responseStream = await this.interactor.GetReminderAsync(token.ActorType, token.ActorId.ToString(), token.Name); var response = await this.interactor.GetReminderAsync(token.ActorType, token.ActorId.ToString(), token.Name);
var reminder = await DeserializeReminderAsync(responseStream, token); if ((int)response.StatusCode == 500)
return reminder; {
return null;
}
var responseStream = await response.Content.ReadAsStreamAsync();
return await DeserializeReminderAsync(responseStream, token);
} }
public override async Task UnregisterReminderAsync(ActorReminderToken reminder) 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); await this.interactor.UnregisterTimerAsync(timer.ActorType, timer.ActorId.ToString(), timer.Name);
} }
private async ValueTask<string> SerializeReminderAsync(ActorReminder reminder) private static async ValueTask<string> SerializeReminderAsync(ActorReminder reminder)
{ {
var info = new ReminderInfo(reminder.State, reminder.DueTime, reminder.Period, reminder.Repetitions, var info = new ReminderInfo(reminder.State, reminder.DueTime, reminder.Period, reminder.Repetitions,
reminder.Ttl); reminder.Ttl);
return await info.SerializeAsync(); return await info.SerializeAsync();
} }
private async ValueTask<ActorReminder> DeserializeReminderAsync(Stream stream, ActorReminderToken token) private static async ValueTask<ActorReminder> DeserializeReminderAsync(Stream stream, ActorReminderToken token)
{ {
if (stream == null) if (stream == null)
{ {
throw new ArgumentNullException(nameof(stream)); throw new ArgumentNullException(nameof(stream));
} }
var info = await ReminderInfo.DeserializeAsync(stream); var info = await ReminderInfo.DeserializeAsync(stream);
if (info == null) if (info == null)
{ {
return null; return null;
} }
var reminder = new ActorReminder(token.ActorType, token.ActorId, token.Name, info.Data, info.DueTime, var reminder = new ActorReminder(token.ActorType, token.ActorId, token.Name, info.Data, info.DueTime,
info.Period); info.Period);
return reminder; return reminder;

View File

@ -11,8 +11,10 @@
// limitations under the License. // limitations under the License.
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
namespace Dapr.Actors.Runtime #nullable enable
{
namespace Dapr.Actors.Runtime;
using System; using System;
using System.IO; using System.IO;
using System.Text; using System.Text;
@ -46,7 +48,7 @@ namespace Dapr.Actors.Runtime
public int? Repetitions { get; private set; } public int? Repetitions { get; private set; }
internal static async Task<ReminderInfo> DeserializeAsync(Stream stream) internal static async Task<ReminderInfo?> DeserializeAsync(Stream stream)
{ {
var json = await JsonSerializer.DeserializeAsync<JsonElement>(stream); var json = await JsonSerializer.DeserializeAsync<JsonElement>(stream);
if(json.ValueKind == JsonValueKind.Null) if(json.ValueKind == JsonValueKind.Null)
@ -54,41 +56,51 @@ namespace Dapr.Actors.Runtime
return null; return null;
} }
var dueTime = default(TimeSpan); var setAnyProperties = false; //Used to determine if anything was actually deserialized
var period = default(TimeSpan); var dueTime = TimeSpan.Zero;
var data = default(byte[]); var period = TimeSpan.Zero;
var data = Array.Empty<byte>();
int? repetition = null; int? repetition = null;
TimeSpan? ttl = null; TimeSpan? ttl = null;
if (json.TryGetProperty("dueTime", out var dueTimeProperty)) if (json.TryGetProperty("dueTime", out var dueTimeProperty))
{ {
setAnyProperties = true;
var dueTimeString = dueTimeProperty.GetString(); var dueTimeString = dueTimeProperty.GetString();
dueTime = ConverterUtils.ConvertTimeSpanFromDaprFormat(dueTimeString); dueTime = ConverterUtils.ConvertTimeSpanFromDaprFormat(dueTimeString);
} }
if (json.TryGetProperty("period", out var periodProperty)) if (json.TryGetProperty("period", out var periodProperty))
{ {
setAnyProperties = true;
var periodString = periodProperty.GetString(); var periodString = periodProperty.GetString();
(period, repetition) = ConverterUtils.ConvertTimeSpanValueFromISO8601Format(periodString); (period, repetition) = ConverterUtils.ConvertTimeSpanValueFromISO8601Format(periodString);
} }
if (json.TryGetProperty("data", out var dataProperty) && dataProperty.ValueKind != JsonValueKind.Null) if (json.TryGetProperty("data", out var dataProperty) && dataProperty.ValueKind != JsonValueKind.Null)
{ {
setAnyProperties = true;
data = dataProperty.GetBytesFromBase64(); data = dataProperty.GetBytesFromBase64();
} }
if (json.TryGetProperty("ttl", out var ttlProperty)) if (json.TryGetProperty("ttl", out var ttlProperty))
{ {
setAnyProperties = true;
var ttlString = ttlProperty.GetString(); var ttlString = ttlProperty.GetString();
ttl = ConverterUtils.ConvertTimeSpanFromDaprFormat(ttlString); 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); return new ReminderInfo(data, dueTime, period, repetition, ttl);
} }
internal async ValueTask<string> SerializeAsync() internal async ValueTask<string> SerializeAsync()
{ {
using var stream = new MemoryStream(); using var stream = new MemoryStream();
using Utf8JsonWriter writer = new Utf8JsonWriter(stream); await using Utf8JsonWriter writer = new Utf8JsonWriter(stream);
writer.WriteStartObject(); writer.WriteStartObject();
writer.WriteString("dueTime", ConverterUtils.ConvertTimeSpanValueInDaprFormat(this.DueTime)); writer.WriteString("dueTime", ConverterUtils.ConvertTimeSpanValueInDaprFormat(this.DueTime));
@ -106,4 +118,3 @@ namespace Dapr.Actors.Runtime
return Encoding.UTF8.GetString(stream.ToArray()); return Encoding.UTF8.GetString(stream.ToArray());
} }
} }
}

View File

@ -1670,14 +1670,12 @@ internal class DaprClientGrpc : DaprClient
ReadOnlyMemory<byte> plaintextBytes, string keyName, EncryptionOptions encryptionOptions, ReadOnlyMemory<byte> plaintextBytes, string keyName, EncryptionOptions encryptionOptions,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
if (MemoryMarshal.TryGetArray(plaintextBytes, out var plaintextSegment) && plaintextSegment.Array != null) using var memoryStream = plaintextBytes.CreateMemoryStream(true);
{
var encryptionResult = await EncryptAsync(vaultResourceName, new MemoryStream(plaintextSegment.Array), var encryptionResult =
keyName, encryptionOptions, await EncryptAsync(vaultResourceName, memoryStream, keyName, encryptionOptions, cancellationToken);
cancellationToken);
var bufferedResult = new ArrayBufferWriter<byte>(); var bufferedResult = new ArrayBufferWriter<byte>();
await foreach (var item in encryptionResult.WithCancellation(cancellationToken)) await foreach (var item in encryptionResult.WithCancellation(cancellationToken))
{ {
bufferedResult.Write(item.Span); bufferedResult.Write(item.Span);
@ -1686,10 +1684,6 @@ internal class DaprClientGrpc : DaprClient
return bufferedResult.WrittenMemory; return bufferedResult.WrittenMemory;
} }
throw new ArgumentException("The input instance doesn't have a valid underlying data store.",
nameof(plaintextBytes));
}
/// <inheritdoc /> /// <inheritdoc />
[Obsolete( [Obsolete(
"The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")] "The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
@ -1895,10 +1889,10 @@ internal class DaprClientGrpc : DaprClient
ReadOnlyMemory<byte> ciphertextBytes, string keyName, DecryptionOptions decryptionOptions, ReadOnlyMemory<byte> ciphertextBytes, string keyName, DecryptionOptions decryptionOptions,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
if (MemoryMarshal.TryGetArray(ciphertextBytes, out var ciphertextSegment) && ciphertextSegment.Array != null) using var memoryStream = ciphertextBytes.CreateMemoryStream(true);
{
var decryptionResult = await DecryptAsync(vaultResourceName, new MemoryStream(ciphertextSegment.Array), var decryptionResult =
keyName, decryptionOptions, cancellationToken); await DecryptAsync(vaultResourceName, memoryStream, keyName, decryptionOptions, cancellationToken);
var bufferedResult = new ArrayBufferWriter<byte>(); var bufferedResult = new ArrayBufferWriter<byte>();
await foreach (var item in decryptionResult.WithCancellation(cancellationToken)) await foreach (var item in decryptionResult.WithCancellation(cancellationToken))
@ -1909,10 +1903,6 @@ internal class DaprClientGrpc : DaprClient
return bufferedResult.WrittenMemory; return bufferedResult.WrittenMemory;
} }
throw new ArgumentException("The input instance doesn't have a valid underlying data store",
nameof(ciphertextBytes));
}
/// <inheritdoc /> /// <inheritdoc />
[Obsolete( [Obsolete(
"The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")] "The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]

View File

@ -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<byte> memory, bool isReadOnly)
{
if (memory.IsEmpty)
{
return new MemoryStream(Array.Empty<byte>(), !isReadOnly);
}
if (MemoryMarshal.TryGetArray(memory, out ArraySegment<byte> segment))
{
return new MemoryStream(segment.Array!, segment.Offset, segment.Count, !isReadOnly);
}
throw new ArgumentException(nameof(memory), "Unable to create MemoryStream from provided memory value");
}
}

View File

@ -77,7 +77,17 @@ internal sealed class DaprJobsGrpcClient : DaprJobsClient
ArgumentNullException.ThrowIfNull(jobName, nameof(jobName)); ArgumentNullException.ThrowIfNull(jobName, nameof(jobName));
ArgumentNullException.ThrowIfNull(schedule, nameof(schedule)); 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) if (startingFrom is not null)
{ {
@ -153,11 +163,15 @@ internal sealed class DaprJobsGrpcClient : DaprJobsClient
var envelope = new Autogenerated.GetJobRequest { Name = jobName }; var envelope = new Autogenerated.GetJobRequest { Name = jobName };
var grpcCallOptions = DaprClientUtilities.ConfigureGrpcCallOptions(typeof(DaprJobsClient).Assembly, this.DaprApiToken, cancellationToken); var grpcCallOptions = DaprClientUtilities.ConfigureGrpcCallOptions(typeof(DaprJobsClient).Assembly, this.DaprApiToken, cancellationToken);
var response = await Client.GetJobAlpha1Async(envelope, grpcCallOptions); 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, DueTime = !string.IsNullOrWhiteSpace(response.Job.DueTime) ? DateTime.Parse(response.Job.DueTime) : null,
Ttl = response.Job.Ttl is not null ? DateTime.Parse(response.Job.Ttl) : null, Ttl = !string.IsNullOrWhiteSpace(response.Job.Ttl) ? DateTime.Parse(response.Job.Ttl) : null,
RepeatCount = response.Job.Repeats == default ? null : (int?)response.Job.Repeats, RepeatCount = (int?)response.Job.Repeats,
Payload = response.Job.Data.ToByteArray() Payload = response.Job.Data.ToByteArray()
}; };
} }

View File

@ -11,8 +11,6 @@
// limitations under the License. // limitations under the License.
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
using System.Text.Json;
using Dapr.Jobs.Models.Responses;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.Routing;
@ -29,41 +27,41 @@ public static class EndpointRouteBuilderExtensions
/// </summary> /// </summary>
/// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param> /// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param>
/// <param name="action">The asynchronous action provided by the developer that handles any inbound requests. The first two /// <param name="action">The asynchronous action provided by the developer that handles any inbound requests. The first two
/// parameters must be a nullable <see cref="string"/> for the jobName and a nullable <see cref="DaprJobDetails"/> with the /// parameters must be a <see cref="string"/> for the jobName and the originally registered ReadOnlyMemory&lt;byte&gt; with the
/// payload details, but otherwise can be populated with additional services to be injected into the delegate.</param> /// payload value, but otherwise can be populated with additional services to be injected into the delegate.</param>
/// <param name="cancellationToken">Cancellation token that will be passed in as the last parameter to the delegate action.</param> /// <param name="timeout">Optional timeout to apply to a per-request cancellation token.</param>
public static IEndpointRouteBuilder MapDaprScheduledJobHandler(this IEndpointRouteBuilder endpoints, public static IEndpointRouteBuilder MapDaprScheduledJobHandler(this IEndpointRouteBuilder endpoints,
Delegate action, CancellationToken cancellationToken = default) Delegate action, TimeSpan? timeout = null)
{ {
ArgumentNullException.ThrowIfNull(endpoints, nameof(endpoints)); ArgumentNullException.ThrowIfNull(endpoints, nameof(endpoints));
ArgumentNullException.ThrowIfNull(action, nameof(action)); ArgumentNullException.ThrowIfNull(action, nameof(action));
endpoints.MapPost("/job/{jobName}", async context => endpoints.MapPost("/job/{jobName}", async context =>
{ {
var jobName = (string?)context.Request.RouteValues["jobName"]; //Retrieve the name of the job from the request path
DaprJobDetails? jobPayload = null; var jobName = string.Empty;
if (context.Request.RouteValues.TryGetValue("jobName", out var capturedJobName))
{
jobName = (string)capturedJobName!;
}
//Retrieve the job payload from the request body
ReadOnlyMemory<byte> payload = new();
if (context.Request.ContentLength is > 0) if (context.Request.ContentLength is > 0)
{ {
using var reader = new StreamReader(context.Request.Body); using var streamContent = new StreamContent(context.Request.Body);
var body = await reader.ReadToEndAsync(); payload = await streamContent.ReadAsByteArrayAsync(CancellationToken.None);
try
{
var deserializedJobPayload = JsonSerializer.Deserialize<DeserializableDaprJobDetails>(body);
jobPayload = deserializedJobPayload?.ToType() ?? null;
}
catch (JsonException)
{
jobPayload = null;
}
} }
var parameters = new Dictionary<Type, object?> using var cts = timeout.HasValue
? new CancellationTokenSource(timeout.Value)
: new CancellationTokenSource();
var parameters = new Dictionary<Type, object>
{ {
{ typeof(string), jobName }, { typeof(string), jobName },
{ typeof(DaprJobDetails), jobPayload }, { typeof(ReadOnlyMemory<byte>), payload },
{ typeof(CancellationToken), CancellationToken.None } { typeof(CancellationToken), cts.Token }
}; };
var actionParameters = action.Method.GetParameters(); var actionParameters = action.Method.GetParameters();

View File

@ -67,7 +67,7 @@ internal static class TimeSpanExtensions
/// <returns>True if the string represents a parseable interval duration; false if not.</returns> /// <returns>True if the string represents a parseable interval duration; false if not.</returns>
public static bool IsDurationString(this string interval) 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 || return hourRegex.Match(interval).Success ||
minuteRegex.Match(interval).Success || minuteRegex.Match(interval).Success ||
secondRegex.Match(interval).Success || secondRegex.Match(interval).Success ||

View File

@ -65,10 +65,7 @@ public sealed class DaprJobSchedule
/// </summary> /// </summary>
/// <param name="scheduledTime">The date and time when the job should be triggered.</param> /// <param name="scheduledTime">The date and time when the job should be triggered.</param>
/// <returns></returns> /// <returns></returns>
public static DaprJobSchedule FromDateTime(DateTimeOffset scheduledTime) public static DaprJobSchedule FromDateTime(DateTimeOffset scheduledTime) => new(scheduledTime.ToString("O"));
{
return new DaprJobSchedule(scheduledTime.ToString("O"));
}
/// <summary> /// <summary>
/// Specifies a schedule using a Cron-like expression or '@' prefixed period strings. /// 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 <see cref="TimeSpan"/>. /// Specifies a schedule using a duration interval articulated via a <see cref="TimeSpan"/>.
/// </summary> /// </summary>
/// <param name="duration">The duration interval.</param> /// <param name="duration">The duration interval.</param>
public static DaprJobSchedule FromDuration(TimeSpan duration) public static DaprJobSchedule FromDuration(TimeSpan duration) => new($"@every {duration.ToDurationString()}");
{
return new DaprJobSchedule(duration.ToDurationString());
}
/// <summary> /// <summary>
/// Specifies a schedule in which the job is triggered to run once a year. /// Specifies a schedule in which the job is triggered to run once a year.

View File

@ -11,9 +11,6 @@
// limitations under the License. // limitations under the License.
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
using System.Text.Json.Serialization;
using Dapr.Jobs.JsonConverters;
namespace Dapr.Jobs.Models.Responses; namespace Dapr.Jobs.Models.Responses;
/// <summary> /// <summary>
@ -46,52 +43,3 @@ public sealed record DaprJobDetails(DaprJobSchedule Schedule)
/// </summary> /// </summary>
public byte[]? Payload { get; init; } = null; public byte[]? Payload { get; init; } = null;
} }
/// <summary>
/// A deserializable version of the <see cref="DaprJobDetails"/>.
/// </summary>
internal sealed record DeserializableDaprJobDetails
{
/// <summary>
/// Represents the schedule that triggers the job.
/// </summary>
public string? Schedule { get; init; }
/// <summary>
/// Allows for jobs with fixed repeat counts.
/// </summary>
public int? RepeatCount { get; init; } = null;
/// <summary>
/// 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.
/// </summary>
[JsonConverter(typeof(Iso8601DateTimeJsonConverter))]
public DateTimeOffset? DueTime { get; init; } = null;
/// <summary>
/// A point-in-time value representing with the job should expire.
/// </summary>
/// <remarks>
/// This must be greater than <see cref="DueTime"/> if both are set.
/// </remarks>
[JsonConverter(typeof(Iso8601DateTimeJsonConverter))]
public DateTimeOffset? Ttl { get; init; } = null;
/// <summary>
/// Stores the main payload of the job which is passed to the trigger function.
/// </summary>
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
};
}
}

View File

@ -1225,7 +1225,6 @@ message Job {
// Systemd timer style cron accepts 6 fields: // Systemd timer style cron accepts 6 fields:
// seconds | minutes | hours | day of month | month | day of week // 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-59 | 0-59 | 0-23 | 1-31 | 1-12/jan-dec | 0-6/sun-sat
// //
// "0 30 * * * *" - every hour on the half hour // "0 30 * * * *" - every hour on the half hour
// "0 15 3 * * *" - every day at 03:15 // "0 15 3 * * *" - every day at 03:15
@ -1320,8 +1319,8 @@ message ConversationRequest {
} }
message ConversationInput { message ConversationInput {
// The message to send to the llm // The content to send to the llm
string message = 1; string content = 1;
// The role to set for the message // The role to set for the message
optional string role = 2; optional string role = 2;

View File

@ -7,7 +7,6 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="FluentAssertions"/>
<PackageReference Include="Microsoft.Extensions.Configuration" /> <PackageReference Include="Microsoft.Extensions.Configuration" />
<PackageReference Include="Microsoft.NET.Test.Sdk" /> <PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="xunit" /> <PackageReference Include="xunit" />

View File

@ -4,7 +4,6 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="FluentAssertions" />
<PackageReference Include="Microsoft.NET.Test.Sdk" /> <PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" /> <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" />
<PackageReference Include="xunit" /> <PackageReference Include="xunit" />

View File

@ -9,7 +9,6 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="FluentAssertions" />
<PackageReference Include="Microsoft.AspNetCore.TestHost" /> <PackageReference Include="Microsoft.AspNetCore.TestHost" />
<PackageReference Include="Microsoft.NET.Test.Sdk" /> <PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="Moq" /> <PackageReference Include="Moq" />

View File

@ -11,8 +11,8 @@
// limitations under the License. // limitations under the License.
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
namespace Dapr.Actors.Test namespace Dapr.Actors.Test;
{
using System.Threading.Tasks; using System.Threading.Tasks;
using Dapr.Actors.Builder; using Dapr.Actors.Builder;
using Dapr.Actors.Communication; using Dapr.Actors.Communication;
@ -51,4 +51,3 @@ namespace Dapr.Actors.Test
Assert.Equal(5, (int)bodyValue.retVal); Assert.Equal(5, (int)bodyValue.retVal);
} }
} }
}

View File

@ -11,8 +11,8 @@
// limitations under the License. // limitations under the License.
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
namespace Dapr.Actors.Test namespace Dapr.Actors.Test;
{
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Xunit; using Xunit;
@ -201,4 +201,3 @@ namespace Dapr.Actors.Test
Assert.Equal(expectedValue, id1.CompareTo(id2)); Assert.Equal(expectedValue, id1.CompareTo(id2));
} }
} }
}

View File

@ -11,11 +11,11 @@
// limitations under the License. // limitations under the License.
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
namespace Dapr.Actors.Test namespace Dapr.Actors.Test;
{
using System; using System;
using System.IO; using System.IO;
using FluentAssertions; using Shouldly;
using Xunit; using Xunit;
/// <summary> /// <summary>
@ -43,14 +43,14 @@ namespace Dapr.Actors.Test
var isDeserialzied = ActorInvokeException.ToException( var isDeserialzied = ActorInvokeException.ToException(
new MemoryStream(serializedException), new MemoryStream(serializedException),
out var remoteMethodException); out var remoteMethodException);
isDeserialzied.Should().BeTrue(); isDeserialzied.ShouldBeTrue();
var ex = this.ThrowRemoteException(message, remoteMethodException); var ex = this.ThrowRemoteException(message, remoteMethodException);
ex.Should().BeOfType<ActorMethodInvocationException>(); ex.ShouldBeOfType<ActorMethodInvocationException>();
ex.InnerException.Should().BeOfType<ActorInvokeException>(); ex.InnerException.ShouldBeOfType<ActorInvokeException>();
((ActorInvokeException)ex.InnerException).ActualExceptionType.Should().Be("System.InvalidOperationException"); ((ActorInvokeException)ex.InnerException).ActualExceptionType.ShouldBe("System.InvalidOperationException");
ex.InnerException.InnerException.Should().BeNull(); ex.InnerException.InnerException.ShouldBeNull();
ex.Message.Should().Be(message); ex.Message.ShouldBe(message);
ex.InnerException.Message.Should().Be(message); ex.InnerException.Message.ShouldBe(message);
} }
private Exception ThrowRemoteException(string message, Exception exception) private Exception ThrowRemoteException(string message, Exception exception)
@ -58,4 +58,3 @@ namespace Dapr.Actors.Test
return new ActorMethodInvocationException(message, exception, false); return new ActorMethodInvocationException(message, exception, false);
} }
} }
}

View File

@ -11,10 +11,10 @@
// limitations under the License. // limitations under the License.
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
namespace Dapr.Actors.Client namespace Dapr.Actors.Client;
{
using System; using System;
using FluentAssertions; using Shouldly;
using Xunit; using Xunit;
/// <summary> /// <summary>
@ -35,7 +35,6 @@ namespace Dapr.Actors.Client
var options = new ActorProxyOptions(); var options = new ActorProxyOptions();
Action action = () => options.JsonSerializerOptions = null; Action action = () => options.JsonSerializerOptions = null;
action.Should().Throw<ArgumentNullException>(); action.ShouldThrow<ArgumentNullException>();
}
} }
} }

View File

@ -11,12 +11,12 @@
// limitations under the License. // limitations under the License.
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
namespace Dapr.Actors.Client namespace Dapr.Actors.Client;
{
using System; using System;
using System.Text.Json; using System.Text.Json;
using Dapr.Actors.Test; using Dapr.Actors.Test;
using FluentAssertions; using Shouldly;
using Xunit; using Xunit;
/// <summary> /// <summary>
@ -48,7 +48,7 @@ namespace Dapr.Actors.Client
{ {
var actorId = new ActorId("abc"); var actorId = new ActorId("abc");
Action action = () => ActorProxy.Create(actorId, typeof(ActorId), "TestActor"); Action action = () => ActorProxy.Create(actorId, typeof(ActorId), "TestActor");
action.Should().Throw<ArgumentException>(); action.ShouldThrow<ArgumentException>();
} }
[Fact] [Fact]
@ -117,7 +117,6 @@ namespace Dapr.Actors.Client
var factory = new ActorProxyFactory(); var factory = new ActorProxyFactory();
Action action = () => factory.DefaultOptions = null; Action action = () => factory.DefaultOptions = null;
action.Should().Throw<ArgumentNullException>(); action.ShouldThrow<ArgumentNullException>();
}
} }
} }

View File

@ -1,12 +1,25 @@
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 System.Threading.Tasks;
using Dapr.Actors.Client; using Dapr.Actors.Client;
using Dapr.Actors.Runtime; using Dapr.Actors.Runtime;
using Dapr.Actors.Test; using Dapr.Actors.Test;
using Xunit; using Xunit;
namespace Dapr.Actors namespace Dapr.Actors;
{
public class ActorReferenceTests public class ActorReferenceTests
{ {
[Fact] [Fact]
@ -90,4 +103,3 @@ namespace Dapr.Actors
return Task.FromResult(ActorReference.Get(this)); return Task.FromResult(ActorReference.Get(this));
} }
} }
}

View File

@ -69,7 +69,7 @@ namespace Dapr.Actors
} }
[Fact] [Fact]
public async Task CanTestStartingAndStoppinReminder() public async Task CanTestStartingAndStoppingReminder()
{ {
var reminders = new List<ActorReminder>(); var reminders = new List<ActorReminder>();
IActorReminder getReminder = null; IActorReminder getReminder = null;
@ -120,6 +120,22 @@ namespace Dapr.Actors
Assert.Empty(reminders); Assert.Empty(reminders);
} }
[Fact]
public async Task ReminderReturnsNullIfNotAvailable()
{
var timerManager = new Mock<ActorTimerManager>(MockBehavior.Strict);
timerManager
.Setup(tm => tm.GetReminderAsync(It.IsAny<ActorReminderToken>()))
.Returns(() => Task.FromResult<IActorReminder>(null));
var host = ActorHost.CreateForTest<CoolTestActor>(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 public interface ICoolTestActor : IActor
{ {
} }

View File

@ -15,7 +15,7 @@ using System;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Dapr.Actors.Client; using Dapr.Actors.Client;
using FluentAssertions; using Shouldly;
using Xunit; using Xunit;
namespace Dapr.Actors.Test namespace Dapr.Actors.Test
@ -43,7 +43,7 @@ namespace Dapr.Actors.Test
request.Dismiss(); request.Dismiss();
var headerValues = request.Request.Headers.GetValues("dapr-api-token"); 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")] [Fact(Skip = "https://github.com/dapr/dotnet-sdk/issues/596")]
@ -89,7 +89,7 @@ namespace Dapr.Actors.Test
request.Dismiss(); request.Dismiss();
var headerValues = request.Request.Headers.GetValues("dapr-api-token"); var headerValues = request.Request.Headers.GetValues("dapr-api-token");
headerValues.Should().Contain("test_token"); headerValues.ShouldContain("test_token");
} }
[Fact] [Fact]

View File

@ -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);
}
}

View File

@ -9,10 +9,10 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="FluentAssertions" />
<PackageReference Include="Microsoft.Extensions.Logging" /> <PackageReference Include="Microsoft.Extensions.Logging" />
<PackageReference Include="Microsoft.NET.Test.Sdk" /> <PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="Moq" /> <PackageReference Include="Moq" />
<PackageReference Include="Shouldly"/>
<PackageReference Include="xunit" /> <PackageReference Include="xunit" />
<PackageReference Include="xunit.runner.visualstudio"> <PackageReference Include="xunit.runner.visualstudio">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>

View File

@ -21,7 +21,7 @@ namespace Dapr.Actors.Test
using System.Security.Authentication; using System.Security.Authentication;
using System.Text.Json; using System.Text.Json;
using System.Threading.Tasks; using System.Threading.Tasks;
using FluentAssertions; using Shouldly;
using Xunit; using Xunit;
/// <summary> /// <summary>
@ -34,9 +34,9 @@ namespace Dapr.Actors.Test
{ {
await using var client = TestClient.CreateForDaprHttpInterator(); await using var client = TestClient.CreateForDaprHttpInterator();
var actorType = "ActorType_Test"; const string actorType = "ActorType_Test";
var actorId = "ActorId_Test"; const string actorId = "ActorId_Test";
var keyName = "StateKey_Test"; const string keyName = "StateKey_Test";
var request = await client.CaptureHttpRequestAsync(async httpInteractor => var request = await client.CaptureHttpRequestAsync(async httpInteractor =>
{ {
@ -48,8 +48,8 @@ namespace Dapr.Actors.Test
var actualPath = request.Request.RequestUri.LocalPath.TrimStart('/'); var actualPath = request.Request.RequestUri.LocalPath.TrimStart('/');
var expectedPath = string.Format(CultureInfo.InvariantCulture, Constants.ActorStateKeyRelativeUrlFormat, actorType, actorId, keyName); var expectedPath = string.Format(CultureInfo.InvariantCulture, Constants.ActorStateKeyRelativeUrlFormat, actorType, actorId, keyName);
actualPath.Should().Be(expectedPath); actualPath.ShouldBe(expectedPath);
request.Request.Method.Should().Be(HttpMethod.Get); request.Request.Method.ShouldBe(HttpMethod.Get);
} }
[Fact] [Fact]
@ -57,9 +57,9 @@ namespace Dapr.Actors.Test
{ {
await using var client = TestClient.CreateForDaprHttpInterator(); await using var client = TestClient.CreateForDaprHttpInterator();
var actorType = "ActorType_Test"; const string actorType = "ActorType_Test";
var actorId = "ActorId_Test"; const string actorId = "ActorId_Test";
var data = "StateData"; const string data = "StateData";
var request = await client.CaptureHttpRequestAsync(async httpInteractor => var request = await client.CaptureHttpRequestAsync(async httpInteractor =>
{ {
@ -71,8 +71,8 @@ namespace Dapr.Actors.Test
var actualPath = request.Request.RequestUri.LocalPath.TrimStart('/'); var actualPath = request.Request.RequestUri.LocalPath.TrimStart('/');
var expectedPath = string.Format(CultureInfo.InvariantCulture, Constants.ActorStateRelativeUrlFormat, actorType, actorId); var expectedPath = string.Format(CultureInfo.InvariantCulture, Constants.ActorStateRelativeUrlFormat, actorType, actorId);
actualPath.Should().Be(expectedPath); actualPath.ShouldBe(expectedPath);
request.Request.Method.Should().Be(HttpMethod.Put); request.Request.Method.ShouldBe(HttpMethod.Put);
} }
[Fact] [Fact]
@ -80,10 +80,10 @@ namespace Dapr.Actors.Test
{ {
await using var client = TestClient.CreateForDaprHttpInterator(); await using var client = TestClient.CreateForDaprHttpInterator();
var actorType = "ActorType_Test"; const string actorType = "ActorType_Test";
var actorId = "ActorId_Test"; const string actorId = "ActorId_Test";
var methodName = "MethodName"; const string methodName = "MethodName";
var payload = "JsonData"; const string payload = "JsonData";
var request = await client.CaptureHttpRequestAsync(async httpInteractor => var request = await client.CaptureHttpRequestAsync(async httpInteractor =>
{ {
@ -95,8 +95,8 @@ namespace Dapr.Actors.Test
var actualPath = request.Request.RequestUri.LocalPath.TrimStart('/'); var actualPath = request.Request.RequestUri.LocalPath.TrimStart('/');
var expectedPath = string.Format(CultureInfo.InvariantCulture, Constants.ActorMethodRelativeUrlFormat, actorType, actorId, methodName); var expectedPath = string.Format(CultureInfo.InvariantCulture, Constants.ActorMethodRelativeUrlFormat, actorType, actorId, methodName);
actualPath.Should().Be(expectedPath); actualPath.ShouldBe(expectedPath);
request.Request.Method.Should().Be(HttpMethod.Put); request.Request.Method.ShouldBe(HttpMethod.Put);
} }
[Fact] [Fact]
@ -104,10 +104,10 @@ namespace Dapr.Actors.Test
{ {
await using var client = TestClient.CreateForDaprHttpInterator(); await using var client = TestClient.CreateForDaprHttpInterator();
var actorType = "ActorType_Test"; const string actorType = "ActorType_Test";
var actorId = "ActorId_Test"; const string actorId = "ActorId_Test";
var reminderName = "ReminderName"; const string reminderName = "ReminderName";
var payload = "JsonData"; const string payload = "JsonData";
var request = await client.CaptureHttpRequestAsync(async httpInteractor => var request = await client.CaptureHttpRequestAsync(async httpInteractor =>
{ {
@ -119,8 +119,8 @@ namespace Dapr.Actors.Test
var actualPath = request.Request.RequestUri.LocalPath.TrimStart('/'); var actualPath = request.Request.RequestUri.LocalPath.TrimStart('/');
var expectedPath = string.Format(CultureInfo.InvariantCulture, Constants.ActorReminderRelativeUrlFormat, actorType, actorId, reminderName); var expectedPath = string.Format(CultureInfo.InvariantCulture, Constants.ActorReminderRelativeUrlFormat, actorType, actorId, reminderName);
actualPath.Should().Be(expectedPath); actualPath.ShouldBe(expectedPath);
request.Request.Method.Should().Be(HttpMethod.Put); request.Request.Method.ShouldBe(HttpMethod.Put);
} }
[Fact] [Fact]
@ -128,9 +128,9 @@ namespace Dapr.Actors.Test
{ {
await using var client = TestClient.CreateForDaprHttpInterator(); await using var client = TestClient.CreateForDaprHttpInterator();
var actorType = "ActorType_Test"; const string actorType = "ActorType_Test";
var actorId = "ActorId_Test"; const string actorId = "ActorId_Test";
var reminderName = "ReminderName"; const string reminderName = "ReminderName";
var request = await client.CaptureHttpRequestAsync(async httpInteractor => var request = await client.CaptureHttpRequestAsync(async httpInteractor =>
{ {
@ -142,8 +142,32 @@ namespace Dapr.Actors.Test
var actualPath = request.Request.RequestUri.LocalPath.TrimStart('/'); var actualPath = request.Request.RequestUri.LocalPath.TrimStart('/');
var expectedPath = string.Format(CultureInfo.InvariantCulture, Constants.ActorReminderRelativeUrlFormat, actorType, actorId, reminderName); var expectedPath = string.Format(CultureInfo.InvariantCulture, Constants.ActorReminderRelativeUrlFormat, actorType, actorId, reminderName);
actualPath.Should().Be(expectedPath); actualPath.ShouldBe(expectedPath);
request.Request.Method.Should().Be(HttpMethod.Delete); 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] [Fact]
@ -151,10 +175,10 @@ namespace Dapr.Actors.Test
{ {
await using var client = TestClient.CreateForDaprHttpInterator(); await using var client = TestClient.CreateForDaprHttpInterator();
var actorType = "ActorType_Test"; const string actorType = "ActorType_Test";
var actorId = "ActorId_Test"; const string actorId = "ActorId_Test";
var timerName = "TimerName"; const string timerName = "TimerName";
var payload = "JsonData"; const string payload = "JsonData";
var request = await client.CaptureHttpRequestAsync(async httpInteractor => var request = await client.CaptureHttpRequestAsync(async httpInteractor =>
{ {
@ -166,8 +190,8 @@ namespace Dapr.Actors.Test
var actualPath = request.Request.RequestUri.LocalPath.TrimStart('/'); var actualPath = request.Request.RequestUri.LocalPath.TrimStart('/');
var expectedPath = string.Format(CultureInfo.InvariantCulture, Constants.ActorTimerRelativeUrlFormat, actorType, actorId, timerName); var expectedPath = string.Format(CultureInfo.InvariantCulture, Constants.ActorTimerRelativeUrlFormat, actorType, actorId, timerName);
actualPath.Should().Be(expectedPath); actualPath.ShouldBe(expectedPath);
request.Request.Method.Should().Be(HttpMethod.Put); request.Request.Method.ShouldBe(HttpMethod.Put);
} }
[Fact] [Fact]
@ -189,8 +213,8 @@ namespace Dapr.Actors.Test
var actualPath = request.Request.RequestUri.LocalPath.TrimStart('/'); var actualPath = request.Request.RequestUri.LocalPath.TrimStart('/');
var expectedPath = string.Format(CultureInfo.InvariantCulture, Constants.ActorTimerRelativeUrlFormat, actorType, actorId, timerName); var expectedPath = string.Format(CultureInfo.InvariantCulture, Constants.ActorTimerRelativeUrlFormat, actorType, actorId, timerName);
actualPath.Should().Be(expectedPath); actualPath.ShouldBe(expectedPath);
request.Request.Method.Should().Be(HttpMethod.Delete); request.Request.Method.ShouldBe(HttpMethod.Delete);
} }
[Fact] [Fact]
@ -198,9 +222,9 @@ namespace Dapr.Actors.Test
{ {
await using var client = TestClient.CreateForDaprHttpInterator(apiToken: "test_token"); await using var client = TestClient.CreateForDaprHttpInterator(apiToken: "test_token");
var actorType = "ActorType_Test"; const string actorType = "ActorType_Test";
var actorId = "ActorId_Test"; const string actorId = "ActorId_Test";
var timerName = "TimerName"; const string timerName = "TimerName";
var request = await client.CaptureHttpRequestAsync(async httpInteractor => var request = await client.CaptureHttpRequestAsync(async httpInteractor =>
{ {
@ -210,8 +234,8 @@ namespace Dapr.Actors.Test
request.Dismiss(); request.Dismiss();
request.Request.Headers.TryGetValues("dapr-api-token", out var headerValues); request.Request.Headers.TryGetValues("dapr-api-token", out var headerValues);
headerValues.Count().Should().Be(1); headerValues.Count().ShouldBe(1);
headerValues.First().Should().Be("test_token"); headerValues.First().ShouldBe("test_token");
} }
[Fact] [Fact]
@ -219,9 +243,9 @@ namespace Dapr.Actors.Test
{ {
await using var client = TestClient.CreateForDaprHttpInterator(); await using var client = TestClient.CreateForDaprHttpInterator();
var actorType = "ActorType_Test"; const string actorType = "ActorType_Test";
var actorId = "ActorId_Test"; const string actorId = "ActorId_Test";
var timerName = "TimerName"; const string timerName = "TimerName";
var request = await client.CaptureHttpRequestAsync(async httpInteractor => var request = await client.CaptureHttpRequestAsync(async httpInteractor =>
{ {
@ -231,7 +255,7 @@ namespace Dapr.Actors.Test
request.Dismiss(); request.Dismiss();
request.Request.Headers.TryGetValues("dapr-api-token", out var headerValues); request.Request.Headers.TryGetValues("dapr-api-token", out var headerValues);
headerValues.Should().BeNull(); headerValues.ShouldBeNull();
} }
[Fact] [Fact]
@ -239,9 +263,9 @@ namespace Dapr.Actors.Test
{ {
await using var client = TestClient.CreateForDaprHttpInterator(); await using var client = TestClient.CreateForDaprHttpInterator();
var actorType = "ActorType_Test"; const string actorType = "ActorType_Test";
var actorId = "ActorId_Test"; const string actorId = "ActorId_Test";
var timerName = "TimerName"; const string timerName = "TimerName";
var request = await client.CaptureHttpRequestAsync(async httpInteractor => var request = await client.CaptureHttpRequestAsync(async httpInteractor =>
{ {
@ -272,9 +296,9 @@ namespace Dapr.Actors.Test
{ {
await using var client = TestClient.CreateForDaprHttpInterator(); await using var client = TestClient.CreateForDaprHttpInterator();
var actorType = "ActorType_Test"; const string actorType = "ActorType_Test";
var actorId = "ActorId_Test"; const string actorId = "ActorId_Test";
var timerName = "TimerName"; const string timerName = "TimerName";
var request = await client.CaptureHttpRequestAsync(async httpInteractor => var request = await client.CaptureHttpRequestAsync(async httpInteractor =>
{ {
@ -294,9 +318,9 @@ namespace Dapr.Actors.Test
{ {
await using var client = TestClient.CreateForDaprHttpInterator(); await using var client = TestClient.CreateForDaprHttpInterator();
var actorType = "ActorType_Test"; const string actorType = "ActorType_Test";
var actorId = "ActorId_Test"; const string actorId = "ActorId_Test";
var timerName = "TimerName"; const string timerName = "TimerName";
var request = await client.CaptureHttpRequestAsync(async httpInteractor => var request = await client.CaptureHttpRequestAsync(async httpInteractor =>
{ {
@ -316,10 +340,10 @@ namespace Dapr.Actors.Test
{ {
await using var client = TestClient.CreateForDaprHttpInterator(); await using var client = TestClient.CreateForDaprHttpInterator();
var actorType = "ActorType_Test"; const string actorType = "ActorType_Test";
var actorId = "ActorId_Test"; const string actorId = "ActorId_Test";
var methodName = "MethodName"; const string methodName = "MethodName";
var payload = "JsonData"; const string payload = "JsonData";
ActorReentrancyContextAccessor.ReentrancyContext = "1"; ActorReentrancyContextAccessor.ReentrancyContext = "1";
var request = await client.CaptureHttpRequestAsync(async httpInteractor => var request = await client.CaptureHttpRequestAsync(async httpInteractor =>
@ -337,10 +361,10 @@ namespace Dapr.Actors.Test
{ {
await using var client = TestClient.CreateForDaprHttpInterator(); await using var client = TestClient.CreateForDaprHttpInterator();
var actorType = "ActorType_Test"; const string actorType = "ActorType_Test";
var actorId = "ActorId_Test"; const string actorId = "ActorId_Test";
var methodName = "MethodName"; const string methodName = "MethodName";
var payload = "JsonData"; const string payload = "JsonData";
var request = await client.CaptureHttpRequestAsync(async httpInteractor => var request = await client.CaptureHttpRequestAsync(async httpInteractor =>
{ {
@ -356,9 +380,9 @@ namespace Dapr.Actors.Test
{ {
await using var client = TestClient.CreateForDaprHttpInterator(); await using var client = TestClient.CreateForDaprHttpInterator();
var actorType = "ActorType_Test"; const string actorType = "ActorType_Test";
var actorId = "ActorId_Test"; const string actorId = "ActorId_Test";
var keyName = "StateKey_Test"; const string keyName = "StateKey_Test";
var request = await client.CaptureHttpRequestAsync(async httpInteractor => var request = await client.CaptureHttpRequestAsync(async httpInteractor =>
{ {
@ -385,9 +409,9 @@ namespace Dapr.Actors.Test
{ {
await using var client = TestClient.CreateForDaprHttpInterator(); await using var client = TestClient.CreateForDaprHttpInterator();
var actorType = "ActorType_Test"; const string actorType = "ActorType_Test";
var actorId = "ActorId_Test"; const string actorId = "ActorId_Test";
var keyName = "StateKey_Test"; const string keyName = "StateKey_Test";
var request = await client.CaptureHttpRequestAsync(async httpInteractor => var request = await client.CaptureHttpRequestAsync(async httpInteractor =>
{ {

View File

@ -11,10 +11,9 @@
// limitations under the License. // limitations under the License.
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using FluentAssertions; using Shouldly;
using FluentAssertions.Execution;
using Xunit; using Xunit;
namespace Dapr.Actors.Description namespace Dapr.Actors.Description
@ -31,13 +30,12 @@ namespace Dapr.Actors.Description
var description = ActorInterfaceDescription.Create(type); var description = ActorInterfaceDescription.Create(type);
// Assert // Assert
description.Should().NotBeNull(); description.ShouldNotBeNull();
using var _ = new AssertionScope(); description.InterfaceType.ShouldBe(type);
description.InterfaceType.Should().Be(type); description.Id.ShouldNotBe(0);
description.Id.Should().NotBe(0); description.V1Id.ShouldBe(0);
description.V1Id.Should().Be(0); description.Methods.Length.ShouldBe(2);
description.Methods.Should().HaveCount(2);
} }
[Fact] [Fact]
@ -50,10 +48,9 @@ namespace Dapr.Actors.Description
Action action = () => ActorInterfaceDescription.Create(type); Action action = () => ActorInterfaceDescription.Create(type);
// Assert // Assert
action.Should().ThrowExactly<ArgumentException>() var exception = Should.Throw<ArgumentException>(action);
.WithMessage("The type 'System.Object' is not an Actor interface as it is not an interface.*") exception.Message.ShouldMatch(@"The type 'System.Object' is not an Actor interface as it is not an interface.*");
.And.ParamName.Should().Be("actorInterfaceType"); exception.ParamName.ShouldBe("actorInterfaceType");
} }
[Fact] [Fact]
@ -66,9 +63,9 @@ namespace Dapr.Actors.Description
Action action = () => ActorInterfaceDescription.Create(type); Action action = () => ActorInterfaceDescription.Create(type);
// Assert // Assert
action.Should().ThrowExactly<ArgumentException>() var exception = Should.Throw<ArgumentException>(action);
.WithMessage("The type 'System.ICloneable' is not an actor interface as it does not derive from the interface 'Dapr.Actors.IActor'.*") exception.Message.ShouldMatch(@"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"); exception.ParamName.ShouldBe("actorInterfaceType");
} }
[Fact] [Fact]
@ -81,9 +78,9 @@ namespace Dapr.Actors.Description
Action action = () => ActorInterfaceDescription.Create(type); Action action = () => ActorInterfaceDescription.Create(type);
// Assert // Assert
action.Should().ThrowExactly<ArgumentException>() var exception = Should.Throw<ArgumentException>(action);
.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'.*") 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'.*");
.And.ParamName.Should().Be("actorInterfaceType"); exception.ParamName.ShouldBe("actorInterfaceType");
} }
[Fact] [Fact]
@ -96,13 +93,12 @@ namespace Dapr.Actors.Description
var description = ActorInterfaceDescription.CreateUsingCRCId(type); var description = ActorInterfaceDescription.CreateUsingCRCId(type);
// Assert // Assert
description.Should().NotBeNull(); description.ShouldNotBeNull();
using var _ = new AssertionScope(); description.InterfaceType.ShouldBe(type);
description.InterfaceType.Should().Be(type); description.Id.ShouldBe(-934188464);
description.Id.Should().Be(-934188464); description.V1Id.ShouldNotBe(0);
description.V1Id.Should().NotBe(0); description.Methods.Length.ShouldBe(2);
description.Methods.Should().HaveCount(2);
} }
[Fact] [Fact]
@ -115,9 +111,9 @@ namespace Dapr.Actors.Description
Action action = () => ActorInterfaceDescription.CreateUsingCRCId(type); Action action = () => ActorInterfaceDescription.CreateUsingCRCId(type);
// Assert // Assert
action.Should().ThrowExactly<ArgumentException>() var exception = Should.Throw<ArgumentException>(action);
.WithMessage("The type 'System.Object' is not an Actor interface as it is not an interface.*") exception.Message.ShouldMatch(@"The type 'System.Object' is not an Actor interface as it is not an interface.*");
.And.ParamName.Should().Be("actorInterfaceType"); exception.ParamName.ShouldBe("actorInterfaceType");
} }
[Fact] [Fact]
@ -130,9 +126,9 @@ namespace Dapr.Actors.Description
Action action = () => ActorInterfaceDescription.CreateUsingCRCId(type); Action action = () => ActorInterfaceDescription.CreateUsingCRCId(type);
// Assert // Assert
action.Should().ThrowExactly<ArgumentException>() var exception = Should.Throw<ArgumentException>(action);
.WithMessage("The type 'System.ICloneable' is not an actor interface as it does not derive from the interface 'Dapr.Actors.IActor'.*") exception.Message.ShouldMatch(@"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"); exception.ParamName.ShouldBe("actorInterfaceType");
} }
[Fact] [Fact]
@ -145,9 +141,9 @@ namespace Dapr.Actors.Description
Action action = () => ActorInterfaceDescription.CreateUsingCRCId(type); Action action = () => ActorInterfaceDescription.CreateUsingCRCId(type);
// Assert // Assert
action.Should().ThrowExactly<ArgumentException>() var exception = Should.Throw<ArgumentException>(action);
.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'.*") 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'.*");
.And.ParamName.Should().Be("actorInterfaceType"); exception.ParamName.ShouldBe("actorInterfaceType");
} }
internal interface IClonableActor : ICloneable, IActor internal interface IClonableActor : ICloneable, IActor

View File

@ -11,10 +11,10 @@
// limitations under the License. // limitations under the License.
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
using System; using System;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using FluentAssertions; using Shouldly;
using FluentAssertions.Execution;
using Xunit; using Xunit;
namespace Dapr.Actors.Description namespace Dapr.Actors.Description
@ -31,13 +31,12 @@ namespace Dapr.Actors.Description
TestDescription description = new(type); TestDescription description = new(type);
// Assert // Assert
description.Should().NotBeNull(); description.ShouldNotBeNull();
using var _ = new AssertionScope(); description.InterfaceType.ShouldBe(type);
description.InterfaceType.Should().Be(type); description.Id.ShouldNotBe(0);
description.Id.Should().NotBe(0); description.V1Id.ShouldBe(0);
description.V1Id.Should().Be(0); description.Methods.ShouldBeEmpty();
description.Methods.Should().BeEmpty();
} }
[Fact] [Fact]
@ -50,9 +49,8 @@ namespace Dapr.Actors.Description
TestDescription description = new(type, useCRCIdGeneration: true); TestDescription description = new(type, useCRCIdGeneration: true);
// Assert // Assert
using var _ = new AssertionScope(); description.Id.ShouldBe(-934188464);
description.Id.Should().Be(-934188464); description.V1Id.ShouldNotBe(0);
description.V1Id.Should().NotBe(0);
} }
[Fact] [Fact]
@ -65,9 +63,9 @@ namespace Dapr.Actors.Description
Action action = () => { TestDescription _ = new(type); }; Action action = () => { TestDescription _ = new(type); };
// Assert // Assert
action.Should().ThrowExactly<ArgumentException>() var exception = Should.Throw<ArgumentException>(action);
.WithMessage("The actor interface '*+IGenericActor`1' is using generics. Generic interfaces cannot be remoted.*") exception.Message.ShouldMatch(@"The actor interface '.*\+IGenericActor`1' is using generics. Generic interfaces cannot be remoted.*");
.And.ParamName.Should().Be("actorInterfaceType"); exception.ParamName.ShouldBe("actorInterfaceType");
} }
[Fact] [Fact]
@ -80,9 +78,9 @@ namespace Dapr.Actors.Description
Action action = () => { TestDescription _ = new(type); }; Action action = () => { TestDescription _ = new(type); };
// Assert // Assert
action.Should().ThrowExactly<ArgumentException>() var exception = Should.Throw<ArgumentException>(action);
.WithMessage("The actor interface '*+IGenericActor`1[*IActor*]' is using generics. Generic interfaces cannot be remoted.*") exception.Message.ShouldMatch(@"The actor interface '.*\+IGenericActor`1\[.*IActor.*\]' is using generics. Generic interfaces cannot be remoted.*");
.And.ParamName.Should().Be("actorInterfaceType"); exception.ParamName.ShouldBe("actorInterfaceType");
} }
[Fact] [Fact]
@ -95,12 +93,9 @@ namespace Dapr.Actors.Description
TestDescription description = new(type); TestDescription description = new(type);
// Assert // Assert
using var _ = new AssertionScope(); description.Methods.ShouldNotBeNull();
description.Methods.Should().NotContainNulls(); description.Methods.ShouldBeOfType<MethodDescription[]>();
description.Methods.Should().AllBeOfType<MethodDescription>(); description.Methods.Select(m => new {m.Name}).ShouldBe(new[] {new {Name = "GetInt"}});
description.Methods.Should().BeEquivalentTo(
new { Name = "GetInt" }
);
} }
[Fact] [Fact]
@ -113,12 +108,9 @@ namespace Dapr.Actors.Description
TestDescription description = new(type, methodReturnCheck: MethodReturnCheck.EnsureReturnsVoid); TestDescription description = new(type, methodReturnCheck: MethodReturnCheck.EnsureReturnsVoid);
// Assert // Assert
using var _ = new AssertionScope(); description.Methods.ShouldNotBeNull();
description.Methods.Should().NotContainNulls(); description.Methods.ShouldBeOfType<MethodDescription[]>();
description.Methods.Should().AllBeOfType<MethodDescription>(); description.Methods.Select(m => new {m.Name}).ShouldBe(new[] {new {Name = "GetString"}, new {Name="MethodWithArguments"}});
description.Methods.Should().BeEquivalentTo(
new { Name = "GetString" },
new { Name = "MethodWithArguments" });
} }
[Fact] [Fact]
@ -128,12 +120,15 @@ namespace Dapr.Actors.Description
Type type = typeof(IVoidActor); Type type = typeof(IVoidActor);
// Act // Act
Action action = () => { TestDescription _ = new(type); }; Action action = () =>
{
TestDescription _ = new(type);
};
// Assert // Assert
action.Should().ThrowExactly<ArgumentException>() var exception = Should.Throw<ArgumentException>(action);
.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<>.*") 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<>.*");
.And.ParamName.Should().Be("actorInterfaceType"); exception.ParamName.ShouldBe("actorInterfaceType");
} }
[Fact] [Fact]
@ -146,9 +141,9 @@ namespace Dapr.Actors.Description
Action action = () => { TestDescription _ = new(type, methodReturnCheck: MethodReturnCheck.EnsureReturnsVoid); }; Action action = () => { TestDescription _ = new(type, methodReturnCheck: MethodReturnCheck.EnsureReturnsVoid); };
// Assert // Assert
action.Should().ThrowExactly<ArgumentException>() var exception = Should.Throw<ArgumentException>(action);
.WithMessage("Method 'GetString' of actor interface '*+IMethodActor' returns '*.Task`1[*System.String*]'*") exception.Message.ShouldMatch(@"Method 'GetString' of actor interface '.*\+IMethodActor' returns '.*\.Task`1\[.*System.String.*\]'.*");
.And.ParamName.Should().Be("actorInterfaceType"); exception.ParamName.ShouldBe("actorInterfaceType");
} }
[Fact] [Fact]
@ -161,9 +156,9 @@ namespace Dapr.Actors.Description
Action action = () => { TestDescription _ = new(type); }; Action action = () => { TestDescription _ = new(type); };
// Assert // Assert
action.Should().ThrowExactly<ArgumentException>() var exception = Should.Throw<ArgumentException>(action);
.WithMessage("Method 'GetString' of actor interface '*+IOverloadedMethodActor' is overloaded. The actor interface methods cannot be overloaded.*") exception.Message.ShouldMatch(@"Method 'GetString' of actor interface '.*\+IOverloadedMethodActor' is overloaded. The actor interface methods cannot be overloaded.*");
.And.ParamName.Should().Be("actorInterfaceType"); exception.ParamName.ShouldBe("actorInterfaceType");
} }
[Fact] [Fact]
@ -176,9 +171,9 @@ namespace Dapr.Actors.Description
Action action = () => { TestDescription _ = new(type); }; Action action = () => { TestDescription _ = new(type); };
// Assert // Assert
action.Should().ThrowExactly<ArgumentException>() var exception = Should.Throw<ArgumentException>(action);
.WithMessage("Method 'Get' of actor interface '*+IGenericMethodActor' is using generics. The actor interface methods cannot use generics.*") exception.Message.ShouldMatch(@"Method 'Get' of actor interface '.*\+IGenericMethodActor' is using generics. The actor interface methods cannot use generics.*");
.And.ParamName.Should().Be("actorInterfaceType"); exception.ParamName.ShouldBe("actorInterfaceType");
} }
[Fact] [Fact]
@ -191,9 +186,9 @@ namespace Dapr.Actors.Description
Action action = () => { TestDescription _ = new(type); }; Action action = () => { TestDescription _ = new(type); };
// Assert // Assert
action.Should().ThrowExactly<ArgumentException>() var exception = Should.Throw<ArgumentException>(action);
.WithMessage("Method 'MethodWithVarArgs' of actor interface '*+IVariableActor' is using a variable argument list. The actor interface methods cannot have a variable argument list.*") 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.*");
.And.ParamName.Should().Be("actorInterfaceType"); exception.ParamName.ShouldBe("actorInterfaceType");
} }
internal interface ITestActor : IActor internal interface ITestActor : IActor

View File

@ -11,11 +11,10 @@
// limitations under the License. // limitations under the License.
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
using System; using System;
using System.Reflection; using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
using FluentAssertions; using Shouldly;
using FluentAssertions.Execution;
using Xunit; using Xunit;
namespace Dapr.Actors.Description namespace Dapr.Actors.Description
@ -33,11 +32,10 @@ namespace Dapr.Actors.Description
var description = MethodArgumentDescription.Create("actor", methodInfo, parameterInfo); var description = MethodArgumentDescription.Create("actor", methodInfo, parameterInfo);
// Assert // Assert
description.Should().NotBeNull(); description.ShouldNotBeNull();
using var _ = new AssertionScope(); description.Name.ShouldBe("number");
description.Name.Should().Be("number"); description.ArgumentType.ShouldBe(typeof(int));
description.ArgumentType.Should().Be<int>();
} }
[Fact] [Fact]
@ -52,9 +50,9 @@ namespace Dapr.Actors.Description
Action action = () => MethodArgumentDescription.Create("actor", methodInfo, parameterInfo); Action action = () => MethodArgumentDescription.Create("actor", methodInfo, parameterInfo);
// Assert // Assert
action.Should().ThrowExactly<ArgumentException>() var exception = Should.Throw<ArgumentException>(action);
.WithMessage("Method 'MethodWithParams' of actor interface '*ITestActor' has variable length parameter 'values'. The actor interface methods must not have variable length parameters.*") exception.Message.ShouldMatch(@"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"); exception.ParamName.ShouldBe("actorInterfaceType");
} }
[Fact] [Fact]
@ -69,9 +67,9 @@ namespace Dapr.Actors.Description
Action action = () => MethodArgumentDescription.Create("actor", methodInfo, parameterInfo); Action action = () => MethodArgumentDescription.Create("actor", methodInfo, parameterInfo);
// Assert // Assert
action.Should().ThrowExactly<ArgumentException>() var exception = Should.Throw<ArgumentException>(action);
.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.*") 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.*");
.And.ParamName.Should().Be("actorInterfaceType"); exception.ParamName.ShouldBe("actorInterfaceType");
} }
[Fact] [Fact]
@ -86,9 +84,9 @@ namespace Dapr.Actors.Description
Action action = () => MethodArgumentDescription.Create("actor", methodInfo, parameterInfo); Action action = () => MethodArgumentDescription.Create("actor", methodInfo, parameterInfo);
// Assert // Assert
action.Should().ThrowExactly<ArgumentException>() var exception = Should.Throw<ArgumentException>(action);
.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.*") 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.*");
.And.ParamName.Should().Be("actorInterfaceType"); exception.ParamName.ShouldBe("actorInterfaceType");
} }
[Fact] [Fact]
@ -103,9 +101,9 @@ namespace Dapr.Actors.Description
Action action = () => MethodArgumentDescription.Create("actor", methodInfo, parameterInfo); Action action = () => MethodArgumentDescription.Create("actor", methodInfo, parameterInfo);
// Assert // Assert
action.Should().ThrowExactly<ArgumentException>() var exception = Should.Throw<ArgumentException>(action);
.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.*") 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.*");
.And.ParamName.Should().Be("actorInterfaceType"); exception.ParamName.ShouldBe("actorInterfaceType");
} }
internal interface ITestActor : IActor internal interface ITestActor : IActor

View File

@ -12,11 +12,11 @@
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
using System; using System;
using System.Linq;
using System.Reflection; using System.Reflection;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using FluentAssertions; using Shouldly;
using FluentAssertions.Execution;
using Xunit; using Xunit;
namespace Dapr.Actors.Description namespace Dapr.Actors.Description
@ -33,15 +33,14 @@ namespace Dapr.Actors.Description
var description = MethodDescription.Create("actor", methodInfo, false); var description = MethodDescription.Create("actor", methodInfo, false);
// Assert // Assert
description.Should().NotBeNull(); description.ShouldNotBeNull();
using var _ = new AssertionScope(); description.MethodInfo.ShouldBeSameAs(methodInfo);
description.MethodInfo.Should().BeSameAs(methodInfo); description.Name.ShouldBe("GetString");
description.Name.Should().Be("GetString"); description.ReturnType.ShouldBe(typeof(Task<string>));
description.ReturnType.Should().Be<Task<string>>(); description.Id.ShouldNotBe(0);
description.Id.Should().NotBe(0); description.Arguments.ShouldBeEmpty();
description.Arguments.Should().BeEmpty(); description.HasCancellationToken.ShouldBeFalse();
description.HasCancellationToken.Should().BeFalse();
} }
[Fact] [Fact]
@ -54,7 +53,7 @@ namespace Dapr.Actors.Description
var description = MethodDescription.Create("actor", methodInfo, true); var description = MethodDescription.Create("actor", methodInfo, true);
// Assert // Assert
description.Id.Should().Be(70257263); description.Id.ShouldBe(70257263);
} }
[Fact] [Fact]
@ -67,13 +66,9 @@ namespace Dapr.Actors.Description
var description = MethodDescription.Create("actor", methodInfo, false); var description = MethodDescription.Create("actor", methodInfo, false);
// Assert // Assert
using var _ = new AssertionScope(); description.Arguments.ShouldNotBeNull();
description.Arguments.Should().NotContainNulls(); description.Arguments.ShouldBeOfType<MethodArgumentDescription[]>();
description.Arguments.Should().AllBeOfType<MethodArgumentDescription>(); description.Arguments.Select(m => new {m.Name}).ShouldBe(new[] {new {Name = "number"}, new {Name = "choice"}, new {Name = "information"}});
description.Arguments.Should().BeEquivalentTo(
new { Name = "number" },
new { Name = "choice" },
new { Name = "information" });
} }
[Fact] [Fact]
@ -86,7 +81,7 @@ namespace Dapr.Actors.Description
var description = MethodDescription.Create("actor", methodInfo, false); var description = MethodDescription.Create("actor", methodInfo, false);
// Assert // Assert
description.HasCancellationToken.Should().BeTrue(); description.HasCancellationToken.ShouldBeTrue();
} }
[Fact] [Fact]
@ -100,9 +95,9 @@ namespace Dapr.Actors.Description
Action action = () => MethodDescription.Create("actor", methodInfo, false); Action action = () => MethodDescription.Create("actor", methodInfo, false);
// Assert // Assert
action.Should().ThrowExactly<ArgumentException>() var exception = Should.Throw<ArgumentException>(action);
.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.*") 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\..*");
.And.ParamName.Should().Be("actorInterfaceType"); exception.ParamName.ShouldBe("actorInterfaceType");
} }
[Fact] [Fact]
@ -116,9 +111,9 @@ namespace Dapr.Actors.Description
Action action = () => MethodDescription.Create("actor", methodInfo, false); Action action = () => MethodDescription.Create("actor", methodInfo, false);
// Assert // Assert
action.Should().ThrowExactly<ArgumentException>() var exception = Should.Throw<ArgumentException>(action);
.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.*") 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.*");
.And.ParamName.Should().Be("actorInterfaceType"); exception.ParamName.ShouldBe("actorInterfaceType");
} }
internal interface ITestActor : IActor internal interface ITestActor : IActor

View File

@ -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<ArgumentException>(() =>
{
var result = "every 100s".FromPrefixedPeriod();
});
}
}

View File

@ -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<string> { "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<string> { "apples", "bananas", "cherries", };
const string value = "I have four grapes";
var result = value.EndsWithAny(matchingValues, StringComparison.InvariantCulture);
Assert.False(result);
}
}

View File

@ -11,8 +11,8 @@
// limitations under the License. // limitations under the License.
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
namespace Dapr.Actors.Test namespace Dapr.Actors.Test;
{
using System; using System;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -125,4 +125,3 @@ namespace Dapr.Actors.Test
return default; return default;
} }
} }
}

View File

@ -17,7 +17,7 @@ namespace Dapr.Actors.Test.Runtime
using Moq; using Moq;
using Xunit; using Xunit;
using System; using System;
using FluentAssertions; using Shouldly;
public sealed class ActorRuntimeOptionsTests public sealed class ActorRuntimeOptionsTests
{ {
@ -59,7 +59,7 @@ namespace Dapr.Actors.Test.Runtime
var options = new ActorRuntimeOptions(); var options = new ActorRuntimeOptions();
Action action = () => options.ActorIdleTimeout = TimeSpan.FromSeconds(-1); Action action = () => options.ActorIdleTimeout = TimeSpan.FromSeconds(-1);
action.Should().Throw<ArgumentOutOfRangeException>(); action.ShouldThrow<ArgumentOutOfRangeException>();
} }
@ -78,7 +78,7 @@ namespace Dapr.Actors.Test.Runtime
var options = new ActorRuntimeOptions(); var options = new ActorRuntimeOptions();
Action action = () => options.ActorScanInterval = TimeSpan.FromSeconds(-1); Action action = () => options.ActorScanInterval = TimeSpan.FromSeconds(-1);
action.Should().Throw<ArgumentOutOfRangeException>(); action.ShouldThrow<ArgumentOutOfRangeException>();
} }
[Fact] [Fact]
@ -96,7 +96,7 @@ namespace Dapr.Actors.Test.Runtime
var options = new ActorRuntimeOptions(); var options = new ActorRuntimeOptions();
Action action = () => options.DrainOngoingCallTimeout = TimeSpan.FromSeconds(-1); Action action = () => options.DrainOngoingCallTimeout = TimeSpan.FromSeconds(-1);
action.Should().Throw<ArgumentOutOfRangeException>(); action.ShouldThrow<ArgumentOutOfRangeException>();
} }
[Fact] [Fact]
@ -115,7 +115,7 @@ namespace Dapr.Actors.Test.Runtime
var options = new ActorRuntimeOptions(); var options = new ActorRuntimeOptions();
Action action = () => options.JsonSerializerOptions = null; Action action = () => options.JsonSerializerOptions = null;
action.Should().Throw<ArgumentNullException>(); action.ShouldThrow<ArgumentNullException>();
} }
[Fact] [Fact]
@ -124,7 +124,7 @@ namespace Dapr.Actors.Test.Runtime
var options = new ActorRuntimeOptions(); var options = new ActorRuntimeOptions();
Action action = () => options.RemindersStoragePartitions = -1; Action action = () => options.RemindersStoragePartitions = -1;
action.Should().Throw<ArgumentOutOfRangeException>(); action.ShouldThrow<ArgumentOutOfRangeException>();
} }
[Fact] [Fact]

View File

@ -11,13 +11,13 @@
// limitations under the License. // limitations under the License.
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
namespace Dapr.Actors.Test.Runtime namespace Dapr.Actors.Test.Runtime;
{
using System; using System;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Dapr.Actors.Runtime; using Dapr.Actors.Runtime;
using FluentAssertions; using Shouldly;
using Moq; using Moq;
using Xunit; using Xunit;
@ -28,8 +28,8 @@ namespace Dapr.Actors.Test.Runtime
{ {
var mockStateManager = new Mock<IActorStateManager>(); var mockStateManager = new Mock<IActorStateManager>();
var testDemoActor = this.CreateTestDemoActor(mockStateManager.Object); var testDemoActor = this.CreateTestDemoActor(mockStateManager.Object);
testDemoActor.Host.Should().NotBeNull(); testDemoActor.Host.ShouldNotBeNull();
testDemoActor.Id.Should().NotBeNull(); testDemoActor.Id.ShouldNotBeNull();
} }
[Fact] [Fact]
@ -63,11 +63,8 @@ namespace Dapr.Actors.Test.Runtime
mockStateManager.Setup(manager => manager.ClearCacheAsync(It.IsAny<CancellationToken>())); mockStateManager.Setup(manager => manager.ClearCacheAsync(It.IsAny<CancellationToken>()));
var testDemoActor = this.CreateTestDemoActor(mockStateManager.Object); var testDemoActor = this.CreateTestDemoActor(mockStateManager.Object);
; Should.Throw<ArgumentException>(() => testDemoActor.ValidateTimerCallback(testDemoActor.Host, callback))
FluentActions.Invoking(() => .Message.ShouldBe(expectedErrorMessage);
testDemoActor.ValidateTimerCallback(testDemoActor.Host, callback))
.Should().Throw<ArgumentException>()
.WithMessage(expectedErrorMessage);
} }
[Theory] [Theory]
@ -83,10 +80,7 @@ namespace Dapr.Actors.Test.Runtime
mockStateManager.Setup(manager => manager.ClearCacheAsync(It.IsAny<CancellationToken>())); mockStateManager.Setup(manager => manager.ClearCacheAsync(It.IsAny<CancellationToken>()));
var testDemoActor = this.CreateTestDemoActor(mockStateManager.Object); var testDemoActor = this.CreateTestDemoActor(mockStateManager.Object);
; Should.NotThrow(() => testDemoActor.ValidateTimerCallback(testDemoActor.Host, callback));
FluentActions.Invoking(() =>
testDemoActor.ValidateTimerCallback(testDemoActor.Host, callback))
.Should().NotThrow();
} }
[Theory] [Theory]
@ -126,4 +120,3 @@ namespace Dapr.Actors.Test.Runtime
} }
} }
}

View File

@ -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. // Copyright (c) Microsoft Corporation.
// Licensed under the MIT License. // Licensed under the MIT License.
// ------------------------------------------------------------ // ------------------------------------------------------------
using System; using System;
using System.Net;
using System.Net.Http;
using System.Text.Json; using System.Text.Json;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -11,8 +26,8 @@ using Moq;
using Xunit; using Xunit;
using JsonSerializer = System.Text.Json.JsonSerializer; using JsonSerializer = System.Text.Json.JsonSerializer;
namespace Dapr.Actors.Runtime namespace Dapr.Actors.Runtime;
{
public sealed class DefaultActorTimerManagerTests public sealed class DefaultActorTimerManagerTests
{ {
/// <summary> /// <summary>
@ -26,12 +41,12 @@ namespace Dapr.Actors.Runtime
var actorType = "abc"; var actorType = "abc";
var interactor = new Mock<TestDaprInteractor>(); var interactor = new Mock<TestDaprInteractor>();
var defaultActorTimerManager = new DefaultActorTimerManager(interactor.Object); var defaultActorTimerManager = new DefaultActorTimerManager(interactor.Object);
var actorReminder = new ActorReminder(actorType, new ActorId(actorId), "remindername", new byte[] { }, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1)); var actorReminder = new ActorReminder(actorType, new ActorId(actorId), "remindername", Array.Empty<byte>(), TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1));
var actualData = string.Empty; var actualData = string.Empty;
interactor interactor
.Setup(d => d.RegisterReminderAsync(actorType, actorId, "remindername", It.Is<string>(data => !string.IsNullOrEmpty(data)), It.IsAny<CancellationToken>())) .Setup(d => d.RegisterReminderAsync(actorType, actorId, "remindername", It.Is<string>(data => !string.IsNullOrEmpty(data)), It.IsAny<CancellationToken>()))
.Callback<string, string, string, string, CancellationToken>((actorType, actorID, reminderName, data, token) => { .Callback<string, string, string, string, CancellationToken>((innerType, innerId, reminderName, data, token) => {
actualData = data; actualData = data;
}) })
.Returns(Task.CompletedTask); .Returns(Task.CompletedTask);
@ -57,8 +72,8 @@ namespace Dapr.Actors.Runtime
[Fact] [Fact]
public async Task RegisterReminderAsync_WithRepetition_CallsInteractor_WithCorrectData() public async Task RegisterReminderAsync_WithRepetition_CallsInteractor_WithCorrectData()
{ {
var actorId = "123"; const string actorId = "123";
var actorType = "abc"; const string actorType = "abc";
var interactor = new Mock<TestDaprInteractor>(); var interactor = new Mock<TestDaprInteractor>();
var defaultActorTimerManager = new DefaultActorTimerManager(interactor.Object); 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 actorReminder = new ActorReminder(actorType, new ActorId(actorId), "remindername", new byte[] { }, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1), 10);
@ -66,7 +81,7 @@ namespace Dapr.Actors.Runtime
interactor interactor
.Setup(d => d.RegisterReminderAsync(actorType, actorId, "remindername", It.Is<string>(data => !string.IsNullOrEmpty(data)), It.IsAny<CancellationToken>())) .Setup(d => d.RegisterReminderAsync(actorType, actorId, "remindername", It.Is<string>(data => !string.IsNullOrEmpty(data)), It.IsAny<CancellationToken>()))
.Callback<string, string, string, string, CancellationToken>((actorType, actorID, reminderName, data, token) => { .Callback<string, string, string, string, CancellationToken>((innerType, innerActorId, reminderName, data, token) => {
actualData = data; actualData = data;
}) })
.Returns(Task.CompletedTask); .Returns(Task.CompletedTask);
@ -84,5 +99,83 @@ namespace Dapr.Actors.Runtime
Assert.Equal("R10/PT1M", period.GetString()); Assert.Equal("R10/PT1M", period.GetString());
Assert.Equal("0h1m0s0ms", dueTime.GetString()); Assert.Equal("0h1m0s0ms", dueTime.GetString());
} }
/// <summary>
/// Get the GetReminder method is called without a registered reminder, it should return null.
/// </summary>
[Fact]
public async Task GetReminderAsync_ReturnsNullWhenUnavailable()
{
const string actorId = "123";
const string actorType = "abc";
const string reminderName = "reminderName";
var interactor = new Mock<TestDaprInteractor>();
interactor
.Setup(d => d.GetReminderAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(),
It.IsAny<CancellationToken>()))
.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<TestDaprInteractor>();
var response = new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent("{}") };
interactor
.Setup(d => d.GetReminderAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(),
It.IsAny<CancellationToken>()))
.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<TestDaprInteractor>();
//Create the reminder we'll return
var state = Array.Empty<byte>();
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)
{
Content = new StringContent(serializedActorReminderInfo)
};
//Register the response
interactor
.Setup(d => d.GetReminderAsync(actorType, actorId, reminderName, It.IsAny<CancellationToken>()))
.Callback<string, string, string, CancellationToken>((type, id, name, token) => {
})
.Returns(Task.FromResult(reminderResponse));
var defaultActorTimerManager = new DefaultActorTimerManager(interactor.Object);
var reminderResult = await defaultActorTimerManager.GetReminderAsync(new ActorReminderToken(actorType, new ActorId(actorId), reminderName));
Assert.NotNull(reminderResult);
Assert.Equal(dueTime, reminderResult.DueTime);
Assert.Equal(state, reminderResult.State);
Assert.Equal(period, reminderResult.Period);
Assert.Equal(reminderName, reminderResult.Name);
} }
} }

View File

@ -14,11 +14,11 @@
using System.IO; using System.IO;
using System.Runtime.Serialization; using System.Runtime.Serialization;
using System.Xml; using System.Xml;
using FluentAssertions; using Shouldly;
using Xunit; using Xunit;
namespace Dapr.Actors.Serialization namespace Dapr.Actors.Serialization;
{
public class ActorReferenceDataContractSerializationTest public class ActorReferenceDataContractSerializationTest
{ {
[Fact] [Fact]
@ -32,12 +32,12 @@ namespace Dapr.Actors.Serialization
XmlDocument xmlDoc = new XmlDocument(); XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(ms); xmlDoc.Load(ms);
xmlDoc.DocumentElement.Name.Should().Be("ActorId"); xmlDoc.DocumentElement.Name.ShouldBe("ActorId");
xmlDoc.DocumentElement.InnerText.Should().Be(id.ToString()); xmlDoc.DocumentElement.InnerText.ShouldBe(id.ToString());
ms.Position = 0; ms.Position = 0;
var deserializedActorId = serializer.ReadObject(ms) as ActorId; var deserializedActorId = serializer.ReadObject(ms) as ActorId;
deserializedActorId.Should().Be(id); deserializedActorId.ShouldBe(id);
} }
[Fact] [Fact]
@ -51,12 +51,12 @@ namespace Dapr.Actors.Serialization
XmlDocument xmlDoc = new XmlDocument(); XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(ms); xmlDoc.Load(ms);
xmlDoc.DocumentElement.Name.Should().Be("ActorId"); xmlDoc.DocumentElement.Name.ShouldBe("ActorId");
xmlDoc.DocumentElement.InnerText.Should().Be(string.Empty); xmlDoc.DocumentElement.InnerText.ShouldBe(string.Empty);
ms.Position = 0; ms.Position = 0;
var deserializedActorId = serializer.ReadObject(ms) as ActorId; var deserializedActorId = serializer.ReadObject(ms) as ActorId;
deserializedActorId.Should().Be(id); deserializedActorId.ShouldBe(id);
} }
[Fact] [Fact]
@ -75,8 +75,7 @@ namespace Dapr.Actors.Serialization
ms.Position = 0; ms.Position = 0;
var deserializedActorRef = serializer.ReadObject(ms) as ActorReference; var deserializedActorRef = serializer.ReadObject(ms) as ActorReference;
deserializedActorRef.ActorId.Should().Be(actorId); deserializedActorRef.ActorId.ShouldBe(actorId);
deserializedActorRef.ActorType.Should().Be("TestActor"); deserializedActorRef.ActorType.ShouldBe("TestActor");
}
} }
} }

View File

@ -15,8 +15,8 @@ using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using Xunit; using Xunit;
namespace Dapr.Actors.Serialization namespace Dapr.Actors.Serialization;
{
public class ActorIdJsonConverterTest public class ActorIdJsonConverterTest
{ {
[Fact] [Fact]
@ -112,4 +112,3 @@ namespace Dapr.Actors.Serialization
public ActorId Actor { get; set; } public ActorId Actor { get; set; }
} }
} }
}

View File

@ -16,8 +16,8 @@ using System.Text.Json;
using Xunit; using Xunit;
#pragma warning disable 0618 #pragma warning disable 0618
namespace Dapr.Actors.Runtime namespace Dapr.Actors.Runtime;
{
public class TimerInfoJsonConverterTest public class TimerInfoJsonConverterTest
{ {
[Theory] [Theory]
@ -41,5 +41,4 @@ namespace Dapr.Actors.Runtime
Assert.Equal(timerInfo.Period, deserializedTimerInfo.Period); Assert.Equal(timerInfo.Period, deserializedTimerInfo.Period);
} }
} }
}
#pragma warning restore 0618 #pragma warning restore 0618

View File

@ -1,10 +1,11 @@
using System.IO; using System.IO;
using System.Net.Http;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Dapr.Actors.Communication; using Dapr.Actors.Communication;
namespace Dapr.Actors namespace Dapr.Actors;
{
/// <summary> /// <summary>
/// A Wrapper class for IDaprInteractor which is mainly created for testing. /// A Wrapper class for IDaprInteractor which is mainly created for testing.
/// </summary> /// </summary>
@ -107,10 +108,10 @@ namespace Dapr.Actors
/// <param name="reminderName">Name of reminder to unregister.</param> /// <param name="reminderName">Name of reminder to unregister.</param>
/// <param name="cancellationToken">Cancels the operation.</param> /// <param name="cancellationToken">Cancels the operation.</param>
/// <returns>A <see cref="Task"/> representing the result of the asynchronous operation.</returns> /// <returns>A <see cref="Task"/> representing the result of the asynchronous operation.</returns>
public Task<Stream> GetReminderAsync(string actorType, string actorId, string reminderName, public virtual async Task<HttpResponseMessage> GetReminderAsync(string actorType, string actorId, string reminderName,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
throw new System.NotImplementedException(); return await _testDaprInteractor.GetReminderAsync(actorType, actorId, reminderName);
} }
/// <summary> /// <summary>
@ -156,4 +157,3 @@ namespace Dapr.Actors
throw new System.NotImplementedException(); throw new System.NotImplementedException();
} }
} }
}

View File

@ -16,7 +16,7 @@ using System.Net.Http;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Dapr.AspNetCore.IntegrationTest.App; using Dapr.AspNetCore.IntegrationTest.App;
using FluentAssertions; using Shouldly;
using Newtonsoft.Json; using Newtonsoft.Json;
using Xunit; using Xunit;
@ -43,7 +43,7 @@ namespace Dapr.AspNetCore.IntegrationTest
response.EnsureSuccessStatusCode(); response.EnsureSuccessStatusCode();
var responseContent = await response.Content.ReadAsStringAsync(); var responseContent = await response.Content.ReadAsStringAsync();
var responseUserInfo = JsonConvert.DeserializeObject<UserInfo>(responseContent); var responseUserInfo = JsonConvert.DeserializeObject<UserInfo>(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"); request.Headers.Add("Dapr-Api-Token", "asdfgh");
var response = await httpClient.SendAsync(request); var response = await httpClient.SendAsync(request);
response.StatusCode.Should().Be(HttpStatusCode.Unauthorized); response.StatusCode.ShouldBe(HttpStatusCode.Unauthorized);
} }
} }
} }

View File

@ -19,7 +19,7 @@ namespace Dapr.AspNetCore.IntegrationTest
using System.Text.Json; using System.Text.Json;
using System.Threading.Tasks; using System.Threading.Tasks;
using Dapr.AspNetCore.IntegrationTest.App; using Dapr.AspNetCore.IntegrationTest.App;
using FluentAssertions; using Shouldly;
using Xunit; using Xunit;
public class CloudEventsIntegrationTest public class CloudEventsIntegrationTest
@ -48,6 +48,7 @@ namespace Dapr.AspNetCore.IntegrationTest
} }
} }
[Fact] [Fact]
public async Task CanSendStructuredCloudEvent() public async Task CanSendStructuredCloudEvent()
{ {
@ -74,7 +75,7 @@ namespace Dapr.AspNetCore.IntegrationTest
response.EnsureSuccessStatusCode(); response.EnsureSuccessStatusCode();
var userInfo = await JsonSerializer.DeserializeAsync<UserInfo>(await response.Content.ReadAsStreamAsync(), this.options); var userInfo = await JsonSerializer.DeserializeAsync<UserInfo>(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(); response.EnsureSuccessStatusCode();
var userInfo = await JsonSerializer.DeserializeAsync<UserInfo>(await response.Content.ReadAsStreamAsync(), this.options); var userInfo = await JsonSerializer.DeserializeAsync<UserInfo>(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(); response.EnsureSuccessStatusCode();
var user = await response.Content.ReadAsStringAsync(); 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(); response.EnsureSuccessStatusCode();
var userInfo = await JsonSerializer.DeserializeAsync<UserInfo>(await response.Content.ReadAsStreamAsync(), this.options); var userInfo = await JsonSerializer.DeserializeAsync<UserInfo>(await response.Content.ReadAsStreamAsync(), this.options);
userInfo.Name.Should().Be("jimmy"); userInfo.Name.ShouldBe("jimmy");
} }
} }
} }

View File

@ -16,7 +16,7 @@ namespace Dapr.AspNetCore.IntegrationTest
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using Dapr.AspNetCore.IntegrationTest.App; using Dapr.AspNetCore.IntegrationTest.App;
using FluentAssertions; using Shouldly;
using Newtonsoft.Json; using Newtonsoft.Json;
using Xunit; using Xunit;
@ -37,7 +37,7 @@ namespace Dapr.AspNetCore.IntegrationTest
response.EnsureSuccessStatusCode(); response.EnsureSuccessStatusCode();
var widget = await daprClient.GetStateAsync<Widget>("testStore", "test"); var widget = await daprClient.GetStateAsync<Widget>("testStore", "test");
widget.Count.Should().Be(18); widget.Count.ShouldBe(18);
} }
} }
@ -56,8 +56,8 @@ namespace Dapr.AspNetCore.IntegrationTest
response.EnsureSuccessStatusCode(); response.EnsureSuccessStatusCode();
var responseContent = await response.Content.ReadAsStringAsync(); var responseContent = await response.Content.ReadAsStringAsync();
var responseWidget = JsonConvert.DeserializeObject<Widget>(responseContent); var responseWidget = JsonConvert.DeserializeObject<Widget>(responseContent);
responseWidget.Size.Should().Be(widget.Size); responseWidget.Size.ShouldBe(widget.Size);
responseWidget.Count.Should().Be(widget.Count); responseWidget.Count.ShouldBe(widget.Count);
} }
} }
@ -92,7 +92,7 @@ namespace Dapr.AspNetCore.IntegrationTest
response.EnsureSuccessStatusCode(); response.EnsureSuccessStatusCode();
var widget = await daprClient.GetStateAsync<Widget>("testStore", "test"); var widget = await daprClient.GetStateAsync<Widget>("testStore", "test");
widget.Count.Should().Be(18); widget.Count.ShouldBe(18);
} }
} }
@ -111,7 +111,7 @@ namespace Dapr.AspNetCore.IntegrationTest
response.EnsureSuccessStatusCode(); response.EnsureSuccessStatusCode();
var widget = await daprClient.GetStateAsync<Widget>("testStore", "test"); var widget = await daprClient.GetStateAsync<Widget>("testStore", "test");
widget.Count.Should().Be(18); widget.Count.ShouldBe(18);
} }
} }
@ -130,8 +130,8 @@ namespace Dapr.AspNetCore.IntegrationTest
response.EnsureSuccessStatusCode(); response.EnsureSuccessStatusCode();
var responseContent = await response.Content.ReadAsStringAsync(); var responseContent = await response.Content.ReadAsStringAsync();
var responseWidget = JsonConvert.DeserializeObject<Widget>(responseContent); var responseWidget = JsonConvert.DeserializeObject<Widget>(responseContent);
responseWidget.Size.Should().Be(widget.Size); responseWidget.Size.ShouldBe(widget.Size);
responseWidget.Count.Should().Be(widget.Count); responseWidget.Count.ShouldBe(widget.Count);
} }
} }

View File

@ -5,7 +5,7 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="FluentAssertions" /> <PackageReference Include="Shouldly"/>
<PackageReference Include="Microsoft.NET.Test.Sdk" /> <PackageReference Include="Microsoft.NET.Test.Sdk" />
<!-- <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" />--> <!-- <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" />-->
<PackageReference Include="xunit" /> <PackageReference Include="xunit" />

View File

@ -16,7 +16,7 @@ namespace Dapr.AspNetCore.IntegrationTest
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using Dapr.AspNetCore.IntegrationTest.App; using Dapr.AspNetCore.IntegrationTest.App;
using FluentAssertions; using Shouldly;
using Xunit; using Xunit;
public class RoutingIntegrationTest public class RoutingIntegrationTest
@ -36,7 +36,7 @@ namespace Dapr.AspNetCore.IntegrationTest
response.EnsureSuccessStatusCode(); response.EnsureSuccessStatusCode();
var widget = await daprClient.GetStateAsync<Widget>("testStore", "test"); var widget = await daprClient.GetStateAsync<Widget>("testStore", "test");
widget.Count.Should().Be(18); widget.Count.ShouldBe(18);
} }
} }
} }

View File

@ -19,7 +19,7 @@ namespace Dapr.AspNetCore.IntegrationTest
using System.Net.Http; using System.Net.Http;
using System.Text.Json; using System.Text.Json;
using System.Threading.Tasks; using System.Threading.Tasks;
using FluentAssertions; using Shouldly;
using Xunit; using Xunit;
public class SubscribeEndpointTest public class SubscribeEndpointTest
@ -39,8 +39,8 @@ namespace Dapr.AspNetCore.IntegrationTest
{ {
var json = await JsonSerializer.DeserializeAsync<JsonElement>(stream); var json = await JsonSerializer.DeserializeAsync<JsonElement>(stream);
json.ValueKind.Should().Be(JsonValueKind.Array); json.ValueKind.ShouldBe(JsonValueKind.Array);
json.GetArrayLength().Should().Be(18); json.GetArrayLength().ShouldBe(18);
var subscriptions = new List<(string PubsubName, string Topic, string Route, string rawPayload, var subscriptions = new List<(string PubsubName, string Topic, string Route, string rawPayload,
string match, string metadata, string DeadLetterTopic, string bulkSubscribeMetadata)>(); 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.ShouldContain(("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.ShouldContain(("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.ShouldContain(("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.ShouldContain(("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.ShouldContain(("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.ShouldContain(("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.ShouldContain(("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.ShouldContain(("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.ShouldContain(("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.ShouldContain(("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(("pubsub", "F", "multiTopicAttr", string.Empty, string.Empty, string.Empty, string.Empty,
"{\"enabled\":true,\"maxMessagesCount\":100,\"maxAwaitDurationMs\":1000}")); "{\"enabled\":true,\"maxMessagesCount\":100,\"maxAwaitDurationMs\":1000}"));
subscriptions.Should().Contain(("pubsub", "F.1", "multiTopicAttr", "true", string.Empty, string.Empty, string.Empty, String.Empty)); subscriptions.ShouldContain(("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", "G", "G", string.Empty, string.Empty, string.Empty, "deadLetterTopicName",
"{\"enabled\":true,\"maxMessagesCount\":300,\"maxAwaitDurationMs\":1000}")); "{\"enabled\":true,\"maxMessagesCount\":300,\"maxAwaitDurationMs\":1000}"));
subscriptions.Should().Contain(("pubsub", "splitTopicBuilder", "splitTopics", string.Empty, string.Empty, string.Empty, string.Empty, String.Empty)); subscriptions.ShouldContain(("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.ShouldContain(("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.ShouldContain(("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", "metadata.1", "multiMetadataTopicAttr", "true", string.Empty, "n1=v1", string.Empty,
"{\"enabled\":true,\"maxMessagesCount\":500,\"maxAwaitDurationMs\":2000}")); "{\"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.ShouldContain(("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", "metadataseparatorbyemptytring", "topicmetadataseparatorattrbyemptytring", string.Empty, string.Empty, "n1=v1,", string.Empty, String.Empty));
// Test priority route sorting // Test priority route sorting
var eTopic = subscriptions.FindAll(e => e.Topic == "E"); var eTopic = subscriptions.FindAll(e => e.Topic == "E");
eTopic.Count.Should().Be(3); eTopic.Count.ShouldBe(3);
eTopic[0].Route.Should().Be("E-Critical"); eTopic[0].Route.ShouldBe("E-Critical");
eTopic[1].Route.Should().Be("E-Important"); eTopic[1].Route.ShouldBe("E-Important");
eTopic[2].Route.Should().Be("E"); eTopic[2].Route.ShouldBe("E");
} }
} }
} }

View File

@ -20,7 +20,7 @@ namespace Dapr.AspNetCore.Test
using System.Text; using System.Text;
using System.Text.Json; using System.Text.Json;
using System.Threading.Tasks; using System.Threading.Tasks;
using FluentAssertions; using Shouldly;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Xunit; using Xunit;
@ -44,8 +44,8 @@ namespace Dapr.AspNetCore.Test
// Do verification in the scope of the middleware // Do verification in the scope of the middleware
app.Run(httpContext => app.Run(httpContext =>
{ {
httpContext.Request.ContentType.Should().Be(contentType); httpContext.Request.ContentType.ShouldBe(contentType);
ReadBody(httpContext.Request.Body).Should().Be("Hello, world!"); ReadBody(httpContext.Request.Body).ShouldBe("Hello, world!");
return Task.CompletedTask; return Task.CompletedTask;
}); });
@ -77,8 +77,8 @@ namespace Dapr.AspNetCore.Test
// Do verification in the scope of the middleware // Do verification in the scope of the middleware
app.Run(httpContext => app.Run(httpContext =>
{ {
httpContext.Request.ContentType.Should().Be(dataContentType ?? "application/json"); httpContext.Request.ContentType.ShouldBe(dataContentType ?? "application/json");
ReadBody(httpContext.Request.Body).Should().Be("{\"name\":\"jimmy\"}"); ReadBody(httpContext.Request.Body).ShouldBe("{\"name\":\"jimmy\"}");
return Task.CompletedTask; return Task.CompletedTask;
}); });
@ -117,8 +117,8 @@ namespace Dapr.AspNetCore.Test
// Do verification in the scope of the middleware // Do verification in the scope of the middleware
app.Run(httpContext => app.Run(httpContext =>
{ {
httpContext.Request.ContentType.Should().Be(dataContentType ?? "application/json"); httpContext.Request.ContentType.ShouldBe(dataContentType ?? "application/json");
ReadBody(httpContext.Request.Body).Should().Be("{\"name\":\"jimmy\"}"); ReadBody(httpContext.Request.Body).ShouldBe("{\"name\":\"jimmy\"}");
return Task.CompletedTask; return Task.CompletedTask;
}); });
@ -160,11 +160,13 @@ namespace Dapr.AspNetCore.Test
// Do verification in the scope of the middleware // Do verification in the scope of the middleware
app.Run(httpContext => app.Run(httpContext =>
{ {
httpContext.Request.ContentType.Should().Be(dataContentType ?? "application/json"); httpContext.Request.ContentType.ShouldBe(dataContentType ?? "application/json");
ReadBody(httpContext.Request.Body).Should().Be("{\"name\":\"jimmy\"}"); ReadBody(httpContext.Request.Body).ShouldBe("{\"name\":\"jimmy\"}");
httpContext.Request.Headers.Should().ContainKey("Cloudevent.type").WhichValue.Should().BeEquivalentTo("Test.Type"); httpContext.Request.Headers.ShouldContainKey("Cloudevent.type");
httpContext.Request.Headers.Should().ContainKey("Cloudevent.subject").WhichValue.Should().BeEquivalentTo("Test.Subject"); 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; return Task.CompletedTask;
}); });
@ -207,11 +209,12 @@ namespace Dapr.AspNetCore.Test
// Do verification in the scope of the middleware // Do verification in the scope of the middleware
app.Run(httpContext => app.Run(httpContext =>
{ {
httpContext.Request.ContentType.Should().Be(dataContentType ?? "application/json"); httpContext.Request.ContentType.ShouldBe(dataContentType ?? "application/json");
ReadBody(httpContext.Request.Body).Should().Be("{\"name\":\"jimmy\"}"); ReadBody(httpContext.Request.Body).ShouldBe("{\"name\":\"jimmy\"}");
httpContext.Request.Headers.Should().ContainKey("Cloudevent.type").WhichValue.Should().BeEquivalentTo("Test.Type"); httpContext.Request.Headers.ShouldContainKey("Cloudevent.type");
httpContext.Request.Headers.Should().NotContainKey("Cloudevent.subject"); httpContext.Request.Headers["Cloudevent.type"].ToString().ShouldBe("Test.Type");
httpContext.Request.Headers.ShouldNotContainKey("Cloudevent.subject");
return Task.CompletedTask; return Task.CompletedTask;
}); });
@ -254,11 +257,12 @@ namespace Dapr.AspNetCore.Test
// Do verification in the scope of the middleware // Do verification in the scope of the middleware
app.Run(httpContext => app.Run(httpContext =>
{ {
httpContext.Request.ContentType.Should().Be(dataContentType ?? "application/json"); httpContext.Request.ContentType.ShouldBe(dataContentType ?? "application/json");
ReadBody(httpContext.Request.Body).Should().Be("{\"name\":\"jimmy\"}"); ReadBody(httpContext.Request.Body).ShouldBe("{\"name\":\"jimmy\"}");
httpContext.Request.Headers.Should().NotContainKey("Cloudevent.type"); httpContext.Request.Headers.ShouldNotContainKey("Cloudevent.type");
httpContext.Request.Headers.Should().ContainKey("Cloudevent.subject").WhichValue.Should().BeEquivalentTo("Test.Subject"); httpContext.Request.Headers.ShouldContainKey("Cloudevent.subject");
httpContext.Request.Headers["Cloudevent.subject"].ToString().ShouldBe("Test.Subject");
return Task.CompletedTask; return Task.CompletedTask;
}); });
@ -297,8 +301,8 @@ namespace Dapr.AspNetCore.Test
// Do verification in the scope of the middleware // Do verification in the scope of the middleware
app.Run(httpContext => app.Run(httpContext =>
{ {
httpContext.Request.ContentType.Should().Be("text/plain"); httpContext.Request.ContentType.ShouldBe("text/plain");
ReadBody(httpContext.Request.Body).Should().Be(expected); ReadBody(httpContext.Request.Body).ShouldBe(expected);
return Task.CompletedTask; return Task.CompletedTask;
}); });
@ -331,8 +335,8 @@ namespace Dapr.AspNetCore.Test
// Do verification in the scope of the middleware // Do verification in the scope of the middleware
app.Run(httpContext => app.Run(httpContext =>
{ {
httpContext.Request.ContentType.Should().Be("text/plain"); httpContext.Request.ContentType.ShouldBe("text/plain");
ReadBody(httpContext.Request.Body).Should().Be(expected); ReadBody(httpContext.Request.Body).ShouldBe(expected);
return Task.CompletedTask; return Task.CompletedTask;
}); });
@ -366,8 +370,8 @@ namespace Dapr.AspNetCore.Test
// Do verification in the scope of the middleware // Do verification in the scope of the middleware
app.Run(httpContext => app.Run(httpContext =>
{ {
httpContext.Request.ContentType.Should().Be("application/person+json"); httpContext.Request.ContentType.ShouldBe("application/person+json");
ReadBody(httpContext.Request.Body).Should().Be("{\"name\":\"jimmy\"}"); ReadBody(httpContext.Request.Body).ShouldBe("{\"name\":\"jimmy\"}");
return Task.CompletedTask; return Task.CompletedTask;
}); });
@ -396,14 +400,14 @@ namespace Dapr.AspNetCore.Test
// Do verification in the scope of the middleware // Do verification in the scope of the middleware
app.Run(httpContext => app.Run(httpContext =>
{ {
httpContext.Request.ContentType.Should().Be(dataContentType); httpContext.Request.ContentType.ShouldBe(dataContentType);
var bytes = new byte[httpContext.Request.Body.Length]; var bytes = new byte[httpContext.Request.Body.Length];
#if NET9_0 #if NET9_0
httpContext.Request.Body.ReadExactly(bytes, 0, bytes.Length); httpContext.Request.Body.ReadExactly(bytes, 0, bytes.Length);
#else #else
httpContext.Request.Body.Read(bytes, 0, bytes.Length); httpContext.Request.Body.Read(bytes, 0, bytes.Length);
#endif #endif
bytes.Should().Equal(data); bytes.ShouldBe(data);
return Task.CompletedTask; return Task.CompletedTask;
}); });
@ -432,9 +436,9 @@ namespace Dapr.AspNetCore.Test
// Do verification in the scope of the middleware // Do verification in the scope of the middleware
app.Run(httpContext => app.Run(httpContext =>
{ {
httpContext.Request.ContentType.Should().Be("application/json"); httpContext.Request.ContentType.ShouldBe("application/json");
var body = ReadBody(httpContext.Request.Body); var body = ReadBody(httpContext.Request.Body);
body.Should().Equals(data); body.ShouldBe(data);
return Task.CompletedTask; return Task.CompletedTask;
}); });
@ -447,7 +451,7 @@ namespace Dapr.AspNetCore.Test
MakeBody($"{{ \"datacontenttype\": \"{dataContentType}\", \"data_base64\": \"{base64Str}\", \"data\": {data} }}"); MakeBody($"{{ \"datacontenttype\": \"{dataContentType}\", \"data_base64\": \"{base64Str}\", \"data\": {data} }}");
await pipeline.Invoke(context); 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) private static Stream MakeBody(string text, Encoding encoding = null)

View File

@ -5,8 +5,8 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="FluentAssertions"/>
<PackageReference Include="Microsoft.NET.Test.Sdk" /> <PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="Shouldly"/>
<PackageReference Include="xunit" /> <PackageReference Include="xunit" />
<PackageReference Include="xunit.runner.visualstudio"> <PackageReference Include="xunit.runner.visualstudio">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>

View File

@ -18,7 +18,7 @@ namespace Dapr.AspNetCore.Test
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using Dapr.AspNetCore.Resources; using Dapr.AspNetCore.Resources;
using FluentAssertions; using Shouldly;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ApplicationModels; using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ModelBinding;
@ -34,9 +34,7 @@ namespace Dapr.AspNetCore.Test
Action action = () => provider.OnProvidersExecuted(context); Action action = () => provider.OnProvidersExecuted(context);
action action.ShouldNotThrow();
.Should()
.NotThrow<NullReferenceException>();
} }
[Fact] [Fact]
@ -48,8 +46,7 @@ namespace Dapr.AspNetCore.Test
Action action = () => provider.OnProvidersExecuted(context); Action action = () => provider.OnProvidersExecuted(context);
action action
.Should() .ShouldThrow<InvalidOperationException>(SR.ErrorStateStoreNameNotProvidedForStateEntry);
.Throw<InvalidOperationException>(SR.ErrorStateStoreNameNotProvidedForStateEntry);
} }
private ApplicationModelProviderContext CreateContext(string methodName) private ApplicationModelProviderContext CreateContext(string methodName)

View File

@ -18,7 +18,7 @@ namespace Dapr.AspNetCore.Test
using System.Threading.Tasks; using System.Threading.Tasks;
using Dapr.Client; using Dapr.Client;
using Dapr.Client.Autogen.Grpc.v1; using Dapr.Client.Autogen.Grpc.v1;
using FluentAssertions; using Shouldly;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ModelBinding;
@ -38,9 +38,9 @@ namespace Dapr.AspNetCore.Test
await binder.BindModelAsync(context); await binder.BindModelAsync(context);
context.Result.IsModelSet.Should().BeFalse(); context.Result.IsModelSet.ShouldBeFalse();
context.ModelState.ErrorCount.Should().Be(1); context.ModelState.ErrorCount.ShouldBe(1);
context.ModelState["testParameter"].Errors.Count.Should().Be(1); context.ModelState["testParameter"].Errors.Count.ShouldBe(1);
// No request to state store, validated by disposing client // No request to state store, validated by disposing client
} }
@ -66,12 +66,12 @@ namespace Dapr.AspNetCore.Test
await SendResponseWithState(state, request); await SendResponseWithState(state, request);
// Get response and validate // Get response and validate
context.Result.IsModelSet.Should().BeTrue(); context.Result.IsModelSet.ShouldBeTrue();
context.Result.Model.As<Widget>().Size.Should().Be("small"); ((Widget)context.Result.Model).Size.ShouldBe("small");
context.Result.Model.As<Widget>().Color.Should().Be("yellow"); ((Widget)context.Result.Model).Color.ShouldBe("yellow");
context.ValidationState.Count.Should().Be(1); context.ValidationState.Count.ShouldBe(1);
context.ValidationState[context.Result.Model].SuppressValidation.Should().BeTrue(); context.ValidationState[context.Result.Model].SuppressValidation.ShouldBeTrue();
} }
[Fact] [Fact]
@ -95,13 +95,13 @@ namespace Dapr.AspNetCore.Test
await SendResponseWithState(state, request); await SendResponseWithState(state, request);
// Get response and validate // Get response and validate
context.Result.IsModelSet.Should().BeTrue(); context.Result.IsModelSet.ShouldBeTrue();
context.Result.Model.As<StateEntry<Widget>>().Key.Should().Be("test"); ((StateEntry<Widget>)context.Result.Model).Key.ShouldBe("test");
context.Result.Model.As<StateEntry<Widget>>().Value.Size.Should().Be("small"); ((StateEntry<Widget>)context.Result.Model).Value.Size.ShouldBe("small");
context.Result.Model.As<StateEntry<Widget>>().Value.Color.Should().Be("yellow"); ((StateEntry<Widget>)context.Result.Model).Value.Color.ShouldBe("yellow");
context.ValidationState.Count.Should().Be(1); context.ValidationState.Count.ShouldBe(1);
context.ValidationState[context.Result.Model].SuppressValidation.Should().BeTrue(); context.ValidationState[context.Result.Model].SuppressValidation.ShouldBeTrue();
} }
[Fact] [Fact]
@ -122,9 +122,9 @@ namespace Dapr.AspNetCore.Test
await SendResponseWithState<string>(null, request); await SendResponseWithState<string>(null, request);
context.ModelState.IsValid.Should().BeTrue(); context.ModelState.IsValid.ShouldBeTrue();
context.Result.IsModelSet.Should().BeFalse(); context.Result.IsModelSet.ShouldBeFalse();
context.Result.Should().Be(ModelBindingResult.Failed()); context.Result.ShouldBe(ModelBindingResult.Failed());
} }
[Fact] [Fact]
@ -145,9 +145,9 @@ namespace Dapr.AspNetCore.Test
await SendResponseWithState<string>(null, request); await SendResponseWithState<string>(null, request);
context.ModelState.IsValid.Should().BeTrue(); context.ModelState.IsValid.ShouldBeTrue();
context.Result.IsModelSet.Should().BeTrue(); context.Result.IsModelSet.ShouldBeTrue();
((StateEntry<Widget>)context.Result.Model).Value.Should().BeNull(); ((StateEntry<Widget>)context.Result.Model).Value.ShouldBeNull();
} }
private static ModelBindingContext CreateContext(IServiceProvider services) private static ModelBindingContext CreateContext(IServiceProvider services)

View File

@ -19,7 +19,7 @@ namespace Dapr.Client.Test
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Autogenerated = Dapr.Client.Autogen.Grpc.v1; using Autogenerated = Dapr.Client.Autogen.Grpc.v1;
using FluentAssertions; using Shouldly;
using Grpc.Core; using Grpc.Core;
using Moq; using Moq;
using Xunit; using Xunit;
@ -43,26 +43,24 @@ namespace Dapr.Client.Test
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.BulkPublishRequest>(); var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.BulkPublishRequest>();
envelope.Entries.Count.Should().Be(2); envelope.Entries.Count.ShouldBe(2);
envelope.PubsubName.Should().Be(TestPubsubName); envelope.PubsubName.ShouldBe(TestPubsubName);
envelope.Topic.Should().Be(TestTopicName); envelope.Topic.ShouldBe(TestTopicName);
envelope.Metadata.Count.Should().Be(0); envelope.Metadata.Count.ShouldBe(0);
var firstEntry = envelope.Entries[0]; var firstEntry = envelope.Entries[0];
firstEntry.EntryId.Should().Be("0"); firstEntry.EntryId.ShouldBe("0");
firstEntry.ContentType.Should().Be(TestContentType); firstEntry.ContentType.ShouldBe(TestContentType);
firstEntry.Event.ToStringUtf8().Should() firstEntry.Event.ToStringUtf8().ShouldBe(JsonSerializer.Serialize(bulkPublishData[0], client.InnerClient.JsonSerializerOptions));
.Be(JsonSerializer.Serialize(bulkPublishData[0], client.InnerClient.JsonSerializerOptions)); firstEntry.Metadata.ShouldBeEmpty();
firstEntry.Metadata.Should().BeEmpty();
var secondEntry = envelope.Entries[1]; var secondEntry = envelope.Entries[1];
secondEntry.EntryId.Should().Be("1"); secondEntry.EntryId.ShouldBe("1");
secondEntry.ContentType.Should().Be(TestContentType); secondEntry.ContentType.ShouldBe(TestContentType);
secondEntry.Event.ToStringUtf8().Should() secondEntry.Event.ToStringUtf8().ShouldBe(JsonSerializer.Serialize(bulkPublishData[1], client.InnerClient.JsonSerializerOptions));
.Be(JsonSerializer.Serialize(bulkPublishData[1], client.InnerClient.JsonSerializerOptions)); secondEntry.Metadata.ShouldBeEmpty();
secondEntry.Metadata.Should().BeEmpty();
// Create Response & Respond // Create Response & Respond
var response = new Autogenerated.BulkPublishResponse var response = new Autogenerated.BulkPublishResponse
@ -72,7 +70,7 @@ namespace Dapr.Client.Test
var bulkPublishResponse = await request.CompleteWithMessageAsync(response); var bulkPublishResponse = await request.CompleteWithMessageAsync(response);
// Get response and validate // Get response and validate
bulkPublishResponse.FailedEntries.Count.Should().Be(0); bulkPublishResponse.FailedEntries.Count.ShouldBe(0);
} }
[Fact] [Fact]
@ -90,31 +88,29 @@ namespace Dapr.Client.Test
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.BulkPublishRequest>(); var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.BulkPublishRequest>();
envelope.Entries.Count.Should().Be(2); envelope.Entries.Count.ShouldBe(2);
envelope.PubsubName.Should().Be(TestPubsubName); envelope.PubsubName.ShouldBe(TestPubsubName);
envelope.Topic.Should().Be(TestTopicName); envelope.Topic.ShouldBe(TestTopicName);
envelope.Metadata.Count.Should().Be(2); envelope.Metadata.Count.ShouldBe(2);
envelope.Metadata.Keys.Contains("key1").Should().BeTrue(); envelope.Metadata.Keys.Contains("key1").ShouldBeTrue();
envelope.Metadata.Keys.Contains("key2").Should().BeTrue(); envelope.Metadata.Keys.Contains("key2").ShouldBeTrue();
envelope.Metadata["key1"].Should().Be("value1"); envelope.Metadata["key1"].ShouldBe("value1");
envelope.Metadata["key2"].Should().Be("value2"); envelope.Metadata["key2"].ShouldBe("value2");
var firstEntry = envelope.Entries[0]; var firstEntry = envelope.Entries[0];
firstEntry.EntryId.Should().Be("0"); firstEntry.EntryId.ShouldBe("0");
firstEntry.ContentType.Should().Be(TestContentType); firstEntry.ContentType.ShouldBe(TestContentType);
firstEntry.Event.ToStringUtf8().Should() firstEntry.Event.ToStringUtf8().ShouldBe(JsonSerializer.Serialize(bulkPublishData[0], client.InnerClient.JsonSerializerOptions));
.Be(JsonSerializer.Serialize(bulkPublishData[0], client.InnerClient.JsonSerializerOptions)); firstEntry.Metadata.ShouldBeEmpty();
firstEntry.Metadata.Should().BeEmpty();
var secondEntry = envelope.Entries[1]; var secondEntry = envelope.Entries[1];
secondEntry.EntryId.Should().Be("1"); secondEntry.EntryId.ShouldBe("1");
secondEntry.ContentType.Should().Be(TestContentType); secondEntry.ContentType.ShouldBe(TestContentType);
secondEntry.Event.ToStringUtf8().Should() secondEntry.Event.ToStringUtf8().ShouldBe(JsonSerializer.Serialize(bulkPublishData[1], client.InnerClient.JsonSerializerOptions));
.Be(JsonSerializer.Serialize(bulkPublishData[1], client.InnerClient.JsonSerializerOptions)); secondEntry.Metadata.ShouldBeEmpty();
secondEntry.Metadata.Should().BeEmpty();
// Create Response & Respond // Create Response & Respond
var response = new Autogenerated.BulkPublishResponse var response = new Autogenerated.BulkPublishResponse
@ -124,7 +120,7 @@ namespace Dapr.Client.Test
var bulkPublishResponse = await request.CompleteWithMessageAsync(response); var bulkPublishResponse = await request.CompleteWithMessageAsync(response);
// Get response and validate // Get response and validate
bulkPublishResponse.FailedEntries.Count.Should().Be(0); bulkPublishResponse.FailedEntries.Count.ShouldBe(0);
} }
[Fact] [Fact]
@ -139,26 +135,24 @@ namespace Dapr.Client.Test
request.Dismiss(); request.Dismiss();
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.BulkPublishRequest>(); var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.BulkPublishRequest>();
envelope.Entries.Count.Should().Be(2); envelope.Entries.Count.ShouldBe(2);
envelope.PubsubName.Should().Be(TestPubsubName); envelope.PubsubName.ShouldBe(TestPubsubName);
envelope.Topic.Should().Be(TestTopicName); envelope.Topic.ShouldBe(TestTopicName);
envelope.Metadata.Count.Should().Be(0); envelope.Metadata.Count.ShouldBe(0);
var firstEntry = envelope.Entries[0]; var firstEntry = envelope.Entries[0];
firstEntry.EntryId.Should().Be("0"); firstEntry.EntryId.ShouldBe("0");
firstEntry.ContentType.Should().Be(TestContentType); firstEntry.ContentType.ShouldBe(TestContentType);
firstEntry.Event.ToStringUtf8().Should() firstEntry.Event.ToStringUtf8().ShouldBe(JsonSerializer.Serialize(bulkPublishData[0], client.InnerClient.JsonSerializerOptions));
.Be(JsonSerializer.Serialize(bulkPublishData[0], client.InnerClient.JsonSerializerOptions)); firstEntry.Metadata.ShouldBeEmpty();
firstEntry.Metadata.Should().BeEmpty();
var secondEntry = envelope.Entries[1]; var secondEntry = envelope.Entries[1];
secondEntry.EntryId.Should().Be("1"); secondEntry.EntryId.ShouldBe("1");
secondEntry.ContentType.Should().Be(TestContentType); secondEntry.ContentType.ShouldBe(TestContentType);
secondEntry.Event.ToStringUtf8().Should() secondEntry.Event.ToStringUtf8().ShouldBe(JsonSerializer.Serialize(bulkPublishData[1], client.InnerClient.JsonSerializerOptions));
.Be(JsonSerializer.Serialize(bulkPublishData[1], client.InnerClient.JsonSerializerOptions)); secondEntry.Metadata.ShouldBeEmpty();
secondEntry.Metadata.Should().BeEmpty();
// Create Response & Respond // Create Response & Respond
var response = new Autogenerated.BulkPublishResponse var response = new Autogenerated.BulkPublishResponse
@ -168,7 +162,7 @@ namespace Dapr.Client.Test
var bulkPublishResponse = await request.CompleteWithMessageAsync(response); var bulkPublishResponse = await request.CompleteWithMessageAsync(response);
// Get response and validate // Get response and validate
bulkPublishResponse.FailedEntries.Count.Should().Be(0); bulkPublishResponse.FailedEntries.Count.ShouldBe(0);
} }
[Fact] [Fact]
@ -185,31 +179,29 @@ namespace Dapr.Client.Test
request.Dismiss(); request.Dismiss();
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.BulkPublishRequest>(); var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.BulkPublishRequest>();
envelope.Entries.Count.Should().Be(2); envelope.Entries.Count.ShouldBe(2);
envelope.PubsubName.Should().Be(TestPubsubName); envelope.PubsubName.ShouldBe(TestPubsubName);
envelope.Topic.Should().Be(TestTopicName); envelope.Topic.ShouldBe(TestTopicName);
envelope.Metadata.Count.Should().Be(2); envelope.Metadata.Count.ShouldBe(2);
envelope.Metadata.Keys.Contains("key1").Should().BeTrue(); envelope.Metadata.Keys.Contains("key1").ShouldBeTrue();
envelope.Metadata.Keys.Contains("key2").Should().BeTrue(); envelope.Metadata.Keys.Contains("key2").ShouldBeTrue();
envelope.Metadata["key1"].Should().Be("value1"); envelope.Metadata["key1"].ShouldBe("value1");
envelope.Metadata["key2"].Should().Be("value2"); envelope.Metadata["key2"].ShouldBe("value2");
var firstEntry = envelope.Entries[0]; var firstEntry = envelope.Entries[0];
firstEntry.EntryId.Should().Be("0"); firstEntry.EntryId.ShouldBe("0");
firstEntry.ContentType.Should().Be(TestContentType); firstEntry.ContentType.ShouldBe(TestContentType);
firstEntry.Event.ToStringUtf8().Should() firstEntry.Event.ToStringUtf8().ShouldBe(JsonSerializer.Serialize(bulkPublishData[0], client.InnerClient.JsonSerializerOptions));
.Be(JsonSerializer.Serialize(bulkPublishData[0], client.InnerClient.JsonSerializerOptions)); firstEntry.Metadata.ShouldBeEmpty();
firstEntry.Metadata.Should().BeEmpty();
var secondEntry = envelope.Entries[1]; var secondEntry = envelope.Entries[1];
secondEntry.EntryId.Should().Be("1"); secondEntry.EntryId.ShouldBe("1");
secondEntry.ContentType.Should().Be(TestContentType); secondEntry.ContentType.ShouldBe(TestContentType);
secondEntry.Event.ToStringUtf8().Should() secondEntry.Event.ToStringUtf8().ShouldBe(JsonSerializer.Serialize(bulkPublishData[1], client.InnerClient.JsonSerializerOptions));
.Be(JsonSerializer.Serialize(bulkPublishData[1], client.InnerClient.JsonSerializerOptions)); secondEntry.Metadata.ShouldBeEmpty();
secondEntry.Metadata.Should().BeEmpty();
// Create Response & Respond // Create Response & Respond
var response = new Autogenerated.BulkPublishResponse var response = new Autogenerated.BulkPublishResponse
@ -219,7 +211,7 @@ namespace Dapr.Client.Test
var bulkPublishResponse = await request.CompleteWithMessageAsync(response); var bulkPublishResponse = await request.CompleteWithMessageAsync(response);
// Get response and validate // Get response and validate
bulkPublishResponse.FailedEntries.Count.Should().Be(0); bulkPublishResponse.FailedEntries.Count.ShouldBe(0);
} }
[Fact] [Fact]
@ -236,18 +228,17 @@ namespace Dapr.Client.Test
request.Dismiss(); request.Dismiss();
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.BulkPublishRequest>(); var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.BulkPublishRequest>();
envelope.Entries.Count.Should().Be(1); envelope.Entries.Count.ShouldBe(1);
envelope.PubsubName.Should().Be(TestPubsubName); envelope.PubsubName.ShouldBe(TestPubsubName);
envelope.Topic.Should().Be(TestTopicName); envelope.Topic.ShouldBe(TestTopicName);
envelope.Metadata.Count.Should().Be(0); envelope.Metadata.Count.ShouldBe(0);
var firstEntry = envelope.Entries[0]; var firstEntry = envelope.Entries[0];
firstEntry.EntryId.Should().Be("0"); firstEntry.EntryId.ShouldBe("0");
firstEntry.ContentType.Should().Be(TestContentType); firstEntry.ContentType.ShouldBe(TestContentType);
firstEntry.Event.ToStringUtf8().Should() firstEntry.Event.ToStringUtf8().ShouldBe(JsonSerializer.Serialize(bulkPublishDataObject, client.InnerClient.JsonSerializerOptions));
.Be(JsonSerializer.Serialize(bulkPublishDataObject, client.InnerClient.JsonSerializerOptions)); firstEntry.Metadata.ShouldBeEmpty();
firstEntry.Metadata.Should().BeEmpty();
// Create Response & Respond // Create Response & Respond
var response = new Autogenerated.BulkPublishResponse var response = new Autogenerated.BulkPublishResponse
@ -257,7 +248,7 @@ namespace Dapr.Client.Test
var bulkPublishResponse = await request.CompleteWithMessageAsync(response); var bulkPublishResponse = await request.CompleteWithMessageAsync(response);
// Get response and validate // Get response and validate
bulkPublishResponse.FailedEntries.Count.Should().Be(0); bulkPublishResponse.FailedEntries.Count.ShouldBe(0);
} }
[Fact] [Fact]
@ -333,11 +324,11 @@ namespace Dapr.Client.Test
var bulkPublishResponse = await request.CompleteWithMessageAsync(response); var bulkPublishResponse = await request.CompleteWithMessageAsync(response);
// Get response and validate // Get response and validate
bulkPublishResponse.FailedEntries[0].Entry.EntryId.Should().Be("0"); bulkPublishResponse.FailedEntries[0].Entry.EntryId.ShouldBe("0");
bulkPublishResponse.FailedEntries[0].ErrorMessage.Should().Be("Failed to publish"); bulkPublishResponse.FailedEntries[0].ErrorMessage.ShouldBe("Failed to publish");
bulkPublishResponse.FailedEntries[1].Entry.EntryId.Should().Be("1"); bulkPublishResponse.FailedEntries[1].Entry.EntryId.ShouldBe("1");
bulkPublishResponse.FailedEntries[1].ErrorMessage.Should().Be("Failed to publish"); bulkPublishResponse.FailedEntries[1].ErrorMessage.ShouldBe("Failed to publish");
} }
private class Widget private class Widget

View File

@ -15,7 +15,7 @@ using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Autogenerated = Dapr.Client.Autogen.Grpc.v1; using Autogenerated = Dapr.Client.Autogen.Grpc.v1;
using Xunit; using Xunit;
using FluentAssertions; using Shouldly;
namespace Dapr.Client.Test namespace Dapr.Client.Test
{ {
@ -37,13 +37,13 @@ namespace Dapr.Client.Test
// Get Request and validate // Get Request and validate
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.GetConfigurationRequest>(); var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.GetConfigurationRequest>();
envelope.StoreName.Should().Be("testStore"); envelope.StoreName.ShouldBe("testStore");
envelope.Keys.Should().Contain("test_key"); envelope.Keys.ShouldContain("test_key");
envelope.Metadata.Count.Should().Be(2); envelope.Metadata.Count.ShouldBe(2);
envelope.Metadata.Keys.Contains("key1").Should().BeTrue(); envelope.Metadata.Keys.Contains("key1").ShouldBeTrue();
envelope.Metadata.Keys.Contains("key2").Should().BeTrue(); envelope.Metadata.Keys.Contains("key2").ShouldBeTrue();
envelope.Metadata["key1"].Should().Be("value1"); envelope.Metadata["key1"].ShouldBe("value1");
envelope.Metadata["key2"].Should().Be("value2"); envelope.Metadata["key2"].ShouldBe("value2");
// Get response and validate // Get response and validate
var invokeResponse = new Autogenerated.GetConfigurationResponse(); var invokeResponse = new Autogenerated.GetConfigurationResponse();
@ -55,8 +55,8 @@ namespace Dapr.Client.Test
var domainResponse = await request.CompleteWithMessageAsync(invokeResponse); var domainResponse = await request.CompleteWithMessageAsync(invokeResponse);
var configItem = domainResponse.Items["testKey"]; var configItem = domainResponse.Items["testKey"];
configItem.Value.Should().Be("testValue"); configItem.Value.ShouldBe("testValue");
configItem.Version.Should().Be("v1"); configItem.Version.ShouldBe("v1");
} }
[Fact] [Fact]
@ -75,13 +75,13 @@ namespace Dapr.Client.Test
// Get Request and validate // Get Request and validate
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.GetConfigurationRequest>(); var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.GetConfigurationRequest>();
envelope.StoreName.Should().Be("testStore"); envelope.StoreName.ShouldBe("testStore");
envelope.Keys.Should().BeEmpty(); envelope.Keys.ShouldBeEmpty();
envelope.Metadata.Count.Should().Be(2); envelope.Metadata.Count.ShouldBe(2);
envelope.Metadata.Keys.Contains("key1").Should().BeTrue(); envelope.Metadata.Keys.Contains("key1").ShouldBeTrue();
envelope.Metadata.Keys.Contains("key2").Should().BeTrue(); envelope.Metadata.Keys.Contains("key2").ShouldBeTrue();
envelope.Metadata["key1"].Should().Be("value1"); envelope.Metadata["key1"].ShouldBe("value1");
envelope.Metadata["key2"].Should().Be("value2"); envelope.Metadata["key2"].ShouldBe("value2");
// Get response and validate // Get response and validate
var invokeResponse = new Autogenerated.GetConfigurationResponse(); var invokeResponse = new Autogenerated.GetConfigurationResponse();
@ -93,8 +93,8 @@ namespace Dapr.Client.Test
var domainResponse = await request.CompleteWithMessageAsync(invokeResponse); var domainResponse = await request.CompleteWithMessageAsync(invokeResponse);
var configItem = domainResponse.Items["testKey"]; var configItem = domainResponse.Items["testKey"];
configItem.Value.Should().Be("testValue"); configItem.Value.ShouldBe("testValue");
configItem.Version.Should().Be("v1"); configItem.Version.ShouldBe("v1");
} }
[Fact] [Fact]
@ -108,9 +108,9 @@ namespace Dapr.Client.Test
// Get Request and validate // Get Request and validate
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.GetConfigurationRequest>(); var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.GetConfigurationRequest>();
envelope.StoreName.Should().Be("testStore"); envelope.StoreName.ShouldBe("testStore");
envelope.Keys.Should().Contain("test_key"); envelope.Keys.ShouldContain("test_key");
envelope.Metadata.Count.Should().Be(0); envelope.Metadata.Count.ShouldBe(0);
// Get response and validate // Get response and validate
var invokeResponse = new Autogenerated.GetConfigurationResponse(); var invokeResponse = new Autogenerated.GetConfigurationResponse();
@ -122,8 +122,8 @@ namespace Dapr.Client.Test
var domainResponse = await request.CompleteWithMessageAsync(invokeResponse); var domainResponse = await request.CompleteWithMessageAsync(invokeResponse);
var configItem = domainResponse.Items["testKey"]; var configItem = domainResponse.Items["testKey"];
configItem.Value.Should().Be("testValue"); configItem.Value.ShouldBe("testValue");
configItem.Version.Should().Be("v1"); configItem.Version.ShouldBe("v1");
} }
[Fact] [Fact]
@ -136,8 +136,8 @@ namespace Dapr.Client.Test
}); });
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.UnsubscribeConfigurationRequest>(); var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.UnsubscribeConfigurationRequest>();
envelope.StoreName.Should().Be("testStore"); envelope.StoreName.ShouldBe("testStore");
envelope.Id.Should().Be("testId"); envelope.Id.ShouldBe("testId");
request.Dismiss(); request.Dismiss();
} }
} }

View File

@ -5,7 +5,6 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="FluentAssertions" />
<PackageReference Include="Google.Protobuf" /> <PackageReference Include="Google.Protobuf" />
<PackageReference Include="Grpc.Core.Testing" /> <PackageReference Include="Grpc.Core.Testing" />
<PackageReference Include="Grpc.Net.Client" /> <PackageReference Include="Grpc.Net.Client" />
@ -14,6 +13,7 @@
<PackageReference Include="moq" /> <PackageReference Include="moq" />
<PackageReference Include="protobuf-net.Grpc.AspNetCore" /> <PackageReference Include="protobuf-net.Grpc.AspNetCore" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" /> <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" />
<PackageReference Include="Shouldly"/>
<PackageReference Include="xunit" /> <PackageReference Include="xunit" />
<PackageReference Include="xunit.runner.visualstudio"> <PackageReference Include="xunit.runner.visualstudio">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>

View File

@ -15,7 +15,7 @@ namespace Dapr.Client.Test
{ {
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using FluentAssertions; using Shouldly;
using Xunit; using Xunit;
using Autogenerated = Dapr.Client.Autogen.Grpc.v1; using Autogenerated = Dapr.Client.Autogen.Grpc.v1;
@ -38,8 +38,8 @@ namespace Dapr.Client.Test
await request.GetRequestEnvelopeAsync<Autogenerated.GetSecretRequest>(); await request.GetRequestEnvelopeAsync<Autogenerated.GetSecretRequest>();
request.Request.Headers.TryGetValues("dapr-api-token", out var headerValues); request.Request.Headers.TryGetValues("dapr-api-token", out var headerValues);
headerValues.Count().Should().Be(1); headerValues.Count().ShouldBe(1);
headerValues.First().Should().Be("test_token"); headerValues.First().ShouldBe("test_token");
} }
[Fact] [Fact]
@ -59,7 +59,7 @@ namespace Dapr.Client.Test
await request.GetRequestEnvelopeAsync<Autogenerated.GetSecretRequest>(); await request.GetRequestEnvelopeAsync<Autogenerated.GetSecretRequest>();
request.Request.Headers.TryGetValues("dapr-api-token", out var headerValues); request.Request.Headers.TryGetValues("dapr-api-token", out var headerValues);
headerValues.Should().BeNull(); headerValues.ShouldBeNull();
} }
} }
} }

View File

@ -16,7 +16,7 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Dapr.Client.Autogen.Grpc.v1; using Dapr.Client.Autogen.Grpc.v1;
using Dapr.Client.Autogen.Test.Grpc.v1; using Dapr.Client.Autogen.Test.Grpc.v1;
using FluentAssertions; using Shouldly;
using Google.Protobuf.WellKnownTypes; using Google.Protobuf.WellKnownTypes;
using Grpc.Core; using Grpc.Core;
using Grpc.Net.Client; using Grpc.Net.Client;
@ -62,9 +62,9 @@ namespace Dapr.Client.Test
// Get Request and validate // Get Request and validate
var envelope = await request.GetRequestEnvelopeAsync<InvokeServiceRequest>(); var envelope = await request.GetRequestEnvelopeAsync<InvokeServiceRequest>();
envelope.Id.Should().Be("test"); envelope.Id.ShouldBe("test");
envelope.Message.Method.Should().Be("test"); envelope.Message.Method.ShouldBe("test");
envelope.Message.ContentType.Should().Be(Constants.ContentTypeApplicationGrpc); envelope.Message.ContentType.ShouldBe(Constants.ContentTypeApplicationGrpc);
// Create Response & Respond // Create Response & Respond
var data = new Response() { Name = "Look, I was invoked!" }; var data = new Response() { Name = "Look, I was invoked!" };
@ -75,7 +75,7 @@ namespace Dapr.Client.Test
// Validate Response // Validate Response
var invokedResponse = await request.CompleteWithMessageAsync(response); var invokedResponse = await request.CompleteWithMessageAsync(response);
invokedResponse.Name.Should().Be("Look, I was invoked!"); invokedResponse.Name.ShouldBe("Look, I was invoked!");
} }
[Fact] [Fact]
@ -126,9 +126,9 @@ namespace Dapr.Client.Test
// Get Request and validate // Get Request and validate
var envelope = await request.GetRequestEnvelopeAsync<InvokeServiceRequest>(); var envelope = await request.GetRequestEnvelopeAsync<InvokeServiceRequest>();
envelope.Id.Should().Be("test"); envelope.Id.ShouldBe("test");
envelope.Message.Method.Should().Be("test"); envelope.Message.Method.ShouldBe("test");
envelope.Message.ContentType.Should().Be(string.Empty); envelope.Message.ContentType.ShouldBe(string.Empty);
// Create Response & Respond // Create Response & Respond
var data = new Response() { Name = "Look, I was invoked!" }; var data = new Response() { Name = "Look, I was invoked!" };
@ -139,7 +139,7 @@ namespace Dapr.Client.Test
// Validate Response // Validate Response
var invokedResponse = await request.CompleteWithMessageAsync(response); var invokedResponse = await request.CompleteWithMessageAsync(response);
invokedResponse.Name.Should().Be("Look, I was invoked!"); invokedResponse.Name.ShouldBe("Look, I was invoked!");
} }
[Fact] [Fact]
@ -176,7 +176,7 @@ namespace Dapr.Client.Test
} }
[Fact] [Fact]
public void InvokeMethodGrpcAsync_CanInvokeMethodWithNoReturnTypeAndData() public async Task InvokeMethodGrpcAsync_CanInvokeMethodWithNoReturnTypeAndData()
{ {
var request = new Request() { RequestParameter = "Hello " }; var request = new Request() { RequestParameter = "Hello " };
var client = new MockClient(); var client = new MockClient();
@ -195,7 +195,7 @@ namespace Dapr.Client.Test
.Setup(m => m.InvokeServiceAsync(It.IsAny<Autogen.Grpc.v1.InvokeServiceRequest>(), It.IsAny<CallOptions>())) .Setup(m => m.InvokeServiceAsync(It.IsAny<Autogen.Grpc.v1.InvokeServiceRequest>(), It.IsAny<CallOptions>()))
.Returns(response); .Returns(response);
FluentActions.Awaiting(async () => await client.DaprClient.InvokeMethodGrpcAsync<Request>("test", "test", request)).Should().NotThrowAsync(); await Should.NotThrowAsync(async () => await client.DaprClient.InvokeMethodGrpcAsync<Request>("test", "test", request));
} }
[Fact] [Fact]
@ -250,9 +250,9 @@ namespace Dapr.Client.Test
// Get Request and validate // Get Request and validate
var envelope = await request.GetRequestEnvelopeAsync<InvokeServiceRequest>(); var envelope = await request.GetRequestEnvelopeAsync<InvokeServiceRequest>();
envelope.Id.Should().Be("test"); envelope.Id.ShouldBe("test");
envelope.Message.Method.Should().Be("test"); envelope.Message.Method.ShouldBe("test");
envelope.Message.ContentType.Should().Be(Constants.ContentTypeApplicationGrpc); envelope.Message.ContentType.ShouldBe(Constants.ContentTypeApplicationGrpc);
var actual = envelope.Message.Data.Unpack<Request>(); var actual = envelope.Message.Data.Unpack<Request>();
Assert.Equal(invokeRequest.RequestParameter, actual.RequestParameter); Assert.Equal(invokeRequest.RequestParameter, actual.RequestParameter);
@ -275,9 +275,9 @@ namespace Dapr.Client.Test
// Get Request and validate // Get Request and validate
var envelope = await request.GetRequestEnvelopeAsync<InvokeServiceRequest>(); var envelope = await request.GetRequestEnvelopeAsync<InvokeServiceRequest>();
envelope.Id.Should().Be("test"); envelope.Id.ShouldBe("test");
envelope.Message.Method.Should().Be("test"); envelope.Message.Method.ShouldBe("test");
envelope.Message.ContentType.Should().Be(Constants.ContentTypeApplicationGrpc); envelope.Message.ContentType.ShouldBe(Constants.ContentTypeApplicationGrpc);
var actual = envelope.Message.Data.Unpack<Request>(); var actual = envelope.Message.Data.Unpack<Request>();
Assert.Equal(invokeRequest.RequestParameter, actual.RequestParameter); Assert.Equal(invokeRequest.RequestParameter, actual.RequestParameter);
@ -291,7 +291,7 @@ namespace Dapr.Client.Test
// Validate Response // Validate Response
var invokedResponse = await request.CompleteWithMessageAsync(response); var invokedResponse = await request.CompleteWithMessageAsync(response);
invokedResponse.Name.Should().Be(invokeResponse.Name); invokedResponse.Name.ShouldBe(invokeResponse.Name);
} }
[Fact] [Fact]
@ -308,7 +308,7 @@ namespace Dapr.Client.Test
var response = await daprClient.InvokeMethodGrpcAsync<Request, Response>("test", "SayHello", request); var response = await daprClient.InvokeMethodGrpcAsync<Request, Response>("test", "SayHello", request);
response.Name.Should().Be("Hello Look, I was invoked!"); response.Name.ShouldBe("Hello Look, I was invoked!");
} }
[Fact] [Fact]
@ -328,10 +328,10 @@ namespace Dapr.Client.Test
var response = await daprClient.InvokeMethodGrpcAsync<TestRun, TestRun>("test", "TestRun", testRun); var response = await daprClient.InvokeMethodGrpcAsync<TestRun, TestRun>("test", "TestRun", testRun);
response.Tests.Count.Should().Be(3); response.Tests.Count.ShouldBe(3);
response.Tests[0].Name.Should().Be("test1"); response.Tests[0].Name.ShouldBe("test1");
response.Tests[1].Name.Should().Be("test2"); response.Tests[1].Name.ShouldBe("test2");
response.Tests[2].Name.Should().Be("test3"); response.Tests[2].Name.ShouldBe("test3");
} }
[Fact] [Fact]
@ -348,7 +348,7 @@ namespace Dapr.Client.Test
var response = await daprClient.InvokeMethodGrpcAsync<Request, Response>("test", "not-existing", request); var response = await daprClient.InvokeMethodGrpcAsync<Request, Response>("test", "not-existing", request);
response.Name.Should().Be("unexpected"); response.Name.ShouldBe("unexpected");
} }
@ -401,10 +401,10 @@ namespace Dapr.Client.Test
// Validate Response // Validate Response
var metadata = await request.CompleteWithMessageAsync(response); var metadata = await request.CompleteWithMessageAsync(response);
metadata.Id.Should().Be("testId"); metadata.Id.ShouldBe("testId");
metadata.Extended.Should().Contain(new System.Collections.Generic.KeyValuePair<string, string>("e1", "v1")); metadata.Extended.ShouldContain(new System.Collections.Generic.KeyValuePair<string, string>("e1", "v1"));
metadata.Actors.Should().Contain(actors => actors.Count == 1 && actors.Type == "testType"); metadata.Actors.ShouldContain(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.Components.ShouldContain(components => components.Name == "testName" && components.Type == "testType" && components.Version == "V1" && components.Capabilities.Length == 0);
} }
[Fact] [Fact]
@ -445,8 +445,8 @@ namespace Dapr.Client.Test
// Get Request and validate // Get Request and validate
var envelope = await request.GetRequestEnvelopeAsync<SetMetadataRequest>(); var envelope = await request.GetRequestEnvelopeAsync<SetMetadataRequest>();
envelope.Key.Should().Be("test"); envelope.Key.ShouldBe("test");
envelope.Value.Should().Be("testv"); envelope.Value.ShouldBe("testv");
await request.CompleteWithMessageAsync(new Empty()); await request.CompleteWithMessageAsync(new Empty());

View File

@ -14,7 +14,7 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using Autogenerated = Dapr.Client.Autogen.Grpc.v1; using Autogenerated = Dapr.Client.Autogen.Grpc.v1;
using Xunit; using Xunit;
using FluentAssertions; using Shouldly;
using System; using System;
namespace Dapr.Client.Test namespace Dapr.Client.Test
@ -37,10 +37,10 @@ namespace Dapr.Client.Test
// Get Request and validate // Get Request and validate
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.TryLockRequest>(); var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.TryLockRequest>();
envelope.StoreName.Should().Be("redis"); envelope.StoreName.ShouldBe("redis");
envelope.ResourceId.Should().Be("resourceId"); envelope.ResourceId.ShouldBe("resourceId");
envelope.LockOwner.Should().Be("owner1"); envelope.LockOwner.ShouldBe("owner1");
envelope.ExpiryInSeconds.Should().Be(1000); envelope.ExpiryInSeconds.ShouldBe(1000);
// Get response and validate // Get response and validate
var invokeResponse = new Autogenerated.TryLockResponse{ var invokeResponse = new Autogenerated.TryLockResponse{
@ -48,7 +48,7 @@ namespace Dapr.Client.Test
}; };
var domainResponse = await request.CompleteWithMessageAsync(invokeResponse); var domainResponse = await request.CompleteWithMessageAsync(invokeResponse);
domainResponse.Success.Should().Be(true); domainResponse.Success.ShouldBe(true);
} }
[Fact] [Fact]
@ -80,9 +80,9 @@ namespace Dapr.Client.Test
// Get Request and validate // Get Request and validate
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.UnlockRequest>(); var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.UnlockRequest>();
envelope.StoreName.Should().Be("redis"); envelope.StoreName.ShouldBe("redis");
envelope.ResourceId.Should().Be("resourceId"); envelope.ResourceId.ShouldBe("resourceId");
envelope.LockOwner.Should().Be("owner1"); envelope.LockOwner.ShouldBe("owner1");
// Get response and validate // Get response and validate
var invokeResponse = new Autogenerated.UnlockResponse{ var invokeResponse = new Autogenerated.UnlockResponse{
@ -90,7 +90,7 @@ namespace Dapr.Client.Test
}; };
var domainResponse = await request.CompleteWithMessageAsync(invokeResponse); var domainResponse = await request.CompleteWithMessageAsync(invokeResponse);
domainResponse.status.Should().Be(LockStatus.LockDoesNotExist); domainResponse.status.ShouldBe(LockStatus.LockDoesNotExist);
} }
} }
} }

View File

@ -20,7 +20,7 @@ namespace Dapr.Client.Test
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Dapr.Client.Autogen.Grpc.v1; using Dapr.Client.Autogen.Grpc.v1;
using FluentAssertions; using Shouldly;
using Google.Protobuf; using Google.Protobuf;
using Grpc.Core; using Grpc.Core;
using Moq; using Moq;
@ -43,11 +43,11 @@ namespace Dapr.Client.Test
// Get Request and validate // Get Request and validate
var envelope = await request.GetRequestEnvelopeAsync<InvokeBindingRequest>(); var envelope = await request.GetRequestEnvelopeAsync<InvokeBindingRequest>();
envelope.Name.Should().Be("test"); envelope.Name.ShouldBe("test");
envelope.Metadata.Count.Should().Be(0); envelope.Metadata.Count.ShouldBe(0);
var json = envelope.Data.ToStringUtf8(); var json = envelope.Data.ToStringUtf8();
var typeFromRequest = JsonSerializer.Deserialize<InvokeRequest>(json, client.InnerClient.JsonSerializerOptions); var typeFromRequest = JsonSerializer.Deserialize<InvokeRequest>(json, client.InnerClient.JsonSerializerOptions);
typeFromRequest.RequestParameter.Should().Be("Hello "); typeFromRequest.RequestParameter.ShouldBe("Hello ");
} }
[Fact] [Fact]
@ -70,15 +70,15 @@ namespace Dapr.Client.Test
// Get Request and validate // Get Request and validate
var envelope = await request.GetRequestEnvelopeAsync<InvokeBindingRequest>(); var envelope = await request.GetRequestEnvelopeAsync<InvokeBindingRequest>();
envelope.Name.Should().Be("test"); envelope.Name.ShouldBe("test");
envelope.Metadata.Count.Should().Be(2); envelope.Metadata.Count.ShouldBe(2);
envelope.Metadata.Keys.Contains("key1").Should().BeTrue(); envelope.Metadata.Keys.Contains("key1").ShouldBeTrue();
envelope.Metadata.Keys.Contains("key2").Should().BeTrue(); envelope.Metadata.Keys.Contains("key2").ShouldBeTrue();
envelope.Metadata["key1"].Should().Be("value1"); envelope.Metadata["key1"].ShouldBe("value1");
envelope.Metadata["key2"].Should().Be("value2"); envelope.Metadata["key2"].ShouldBe("value2");
var json = envelope.Data.ToStringUtf8(); var json = envelope.Data.ToStringUtf8();
var typeFromRequest = JsonSerializer.Deserialize<InvokeRequest>(json, client.InnerClient.JsonSerializerOptions); var typeFromRequest = JsonSerializer.Deserialize<InvokeRequest>(json, client.InnerClient.JsonSerializerOptions);
typeFromRequest.RequestParameter.Should().Be("Hello "); typeFromRequest.RequestParameter.ShouldBe("Hello ");
} }
[Fact] [Fact]
@ -95,8 +95,8 @@ namespace Dapr.Client.Test
// Get Request and validate // Get Request and validate
var envelope = await request.GetRequestEnvelopeAsync<InvokeBindingRequest>(); var envelope = await request.GetRequestEnvelopeAsync<InvokeBindingRequest>();
envelope.Name.Should().Be("test"); envelope.Name.ShouldBe("test");
envelope.Metadata.Count.Should().Be(0); envelope.Metadata.Count.ShouldBe(0);
var json = envelope.Data.ToStringUtf8(); var json = envelope.Data.ToStringUtf8();
Assert.Equal("null", json); Assert.Equal("null", json);
} }
@ -133,16 +133,16 @@ namespace Dapr.Client.Test
var response = await request.CompleteWithMessageAsync(gRpcResponse); var response = await request.CompleteWithMessageAsync(gRpcResponse);
var envelope = await request.GetRequestEnvelopeAsync<InvokeBindingRequest>(); var envelope = await request.GetRequestEnvelopeAsync<InvokeBindingRequest>();
envelope.Name.Should().Be("test"); envelope.Name.ShouldBe("test");
envelope.Metadata.Count.Should().Be(2); envelope.Metadata.Count.ShouldBe(2);
envelope.Metadata.Keys.Contains("key1").Should().BeTrue(); envelope.Metadata.Keys.Contains("key1").ShouldBeTrue();
envelope.Metadata.Keys.Contains("key2").Should().BeTrue(); envelope.Metadata.Keys.Contains("key2").ShouldBeTrue();
envelope.Metadata["key1"].Should().Be("value1"); envelope.Metadata["key1"].ShouldBe("value1");
envelope.Metadata["key2"].Should().Be("value2"); envelope.Metadata["key2"].ShouldBe("value2");
var json = envelope.Data.ToStringUtf8(); var json = envelope.Data.ToStringUtf8();
var typeFromRequest = JsonSerializer.Deserialize<InvokeRequest>(json, client.InnerClient.JsonSerializerOptions); var typeFromRequest = JsonSerializer.Deserialize<InvokeRequest>(json, client.InnerClient.JsonSerializerOptions);
typeFromRequest.RequestParameter.Should().Be("Hello "); typeFromRequest.RequestParameter.ShouldBe("Hello ");
Assert.Same(bindingRequest, response.Request); Assert.Same(bindingRequest, response.Request);
Assert.Equal("red", JsonSerializer.Deserialize<Widget>(response.Data.Span, client.InnerClient.JsonSerializerOptions).Color); Assert.Equal("red", JsonSerializer.Deserialize<Widget>(response.Data.Span, client.InnerClient.JsonSerializerOptions).Color);
@ -239,15 +239,15 @@ namespace Dapr.Client.Test
var response = await req.CompleteWithMessageAsync(resp); var response = await req.CompleteWithMessageAsync(resp);
var envelope = await req.GetRequestEnvelopeAsync<InvokeBindingRequest>(); var envelope = await req.GetRequestEnvelopeAsync<InvokeBindingRequest>();
envelope.Name.Should().Be("binding"); envelope.Name.ShouldBe("binding");
envelope.Operation.Should().Be("operation"); envelope.Operation.ShouldBe("operation");
envelope.Metadata.Count.Should().Be(1); envelope.Metadata.Count.ShouldBe(1);
envelope.Metadata.Keys.Contains("key1").Should().BeTrue(); envelope.Metadata.Keys.Contains("key1").ShouldBeTrue();
envelope.Metadata["key1"].Should().Be("value1"); envelope.Metadata["key1"].ShouldBe("value1");
var json = envelope.Data.ToStringUtf8(); var json = envelope.Data.ToStringUtf8();
var typeFromRequest = JsonSerializer.Deserialize<InvokeRequest>(json, client.InnerClient.JsonSerializerOptions); var typeFromRequest = JsonSerializer.Deserialize<InvokeRequest>(json, client.InnerClient.JsonSerializerOptions);
typeFromRequest.RequestParameter.Should().Be("Test"); typeFromRequest.RequestParameter.ShouldBe("Test");
Assert.Equal("red", response.Color); Assert.Equal("red", response.Color);
} }
@ -269,8 +269,8 @@ namespace Dapr.Client.Test
var response = await req.CompleteWithMessageAsync(resp); var response = await req.CompleteWithMessageAsync(resp);
var envelope = await req.GetRequestEnvelopeAsync<InvokeBindingRequest>(); var envelope = await req.GetRequestEnvelopeAsync<InvokeBindingRequest>();
envelope.Name.Should().Be("binding"); envelope.Name.ShouldBe("binding");
envelope.Operation.Should().Be("operation"); envelope.Operation.ShouldBe("operation");
Assert.Equal("red", response.Color); Assert.Equal("red", response.Color);
} }

View File

@ -24,7 +24,7 @@ namespace Dapr.Client.Test
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Dapr.Client.Autogen.Grpc.v1; using Dapr.Client.Autogen.Grpc.v1;
using FluentAssertions; using Shouldly;
using Grpc.Core; using Grpc.Core;
using Moq; using Moq;
using Xunit; using Xunit;
@ -49,11 +49,11 @@ namespace Dapr.Client.Test
var envelope = await request.GetRequestEnvelopeAsync<PublishEventRequest>(); var envelope = await request.GetRequestEnvelopeAsync<PublishEventRequest>();
var jsonFromRequest = envelope.Data.ToStringUtf8(); var jsonFromRequest = envelope.Data.ToStringUtf8();
envelope.DataContentType.Should().Be("application/json"); envelope.DataContentType.ShouldBe("application/json");
envelope.PubsubName.Should().Be(TestPubsubName); envelope.PubsubName.ShouldBe(TestPubsubName);
envelope.Topic.Should().Be("test"); envelope.Topic.ShouldBe("test");
jsonFromRequest.Should().Be(JsonSerializer.Serialize(publishData, client.InnerClient.JsonSerializerOptions)); jsonFromRequest.ShouldBe(JsonSerializer.Serialize(publishData, client.InnerClient.JsonSerializerOptions));
envelope.Metadata.Count.Should().Be(0); envelope.Metadata.Count.ShouldBe(0);
} }
[Fact] [Fact]
@ -75,9 +75,8 @@ namespace Dapr.Client.Test
var client = new TestClient<DaprClient>(clientBuilder.Build(), handler); var client = new TestClient<DaprClient>(clientBuilder.Build(), handler);
//Ensure that the JsonStringEnumConverter is registered //Ensure that the JsonStringEnumConverter is registered
client.InnerClient.JsonSerializerOptions.Converters.Count.Should().Be(1); client.InnerClient.JsonSerializerOptions.Converters.Count.ShouldBe(1);
client.InnerClient.JsonSerializerOptions.Converters.First().GetType().Name.Should() client.InnerClient.JsonSerializerOptions.Converters.First().GetType().Name.ShouldMatch(nameof(JsonStringEnumConverter));
.Match(nameof(JsonStringEnumConverter));
var publishData = new Widget {Size = "Large", Color = WidgetColor.Red}; var publishData = new Widget {Size = "Large", Color = WidgetColor.Red};
var request = await client.CaptureGrpcRequestAsync(async daprClient => var request = await client.CaptureGrpcRequestAsync(async daprClient =>
@ -89,9 +88,8 @@ namespace Dapr.Client.Test
var envelope = await request.GetRequestEnvelopeAsync<PublishEventRequest>(); var envelope = await request.GetRequestEnvelopeAsync<PublishEventRequest>();
var jsonFromRequest = envelope.Data.ToStringUtf8(); var jsonFromRequest = envelope.Data.ToStringUtf8();
jsonFromRequest.Should() jsonFromRequest.ShouldBe(JsonSerializer.Serialize(publishData, client.InnerClient.JsonSerializerOptions));
.Be(JsonSerializer.Serialize(publishData, client.InnerClient.JsonSerializerOptions)); jsonFromRequest.ShouldMatch("{\"Size\":\"Large\",\"Color\":\"Red\"}");
jsonFromRequest.Should().Match("{\"Size\":\"Large\",\"Color\":\"Red\"}");
} }
[Fact] [Fact]
@ -116,16 +114,16 @@ namespace Dapr.Client.Test
var envelope = await request.GetRequestEnvelopeAsync<PublishEventRequest>(); var envelope = await request.GetRequestEnvelopeAsync<PublishEventRequest>();
var jsonFromRequest = envelope.Data.ToStringUtf8(); var jsonFromRequest = envelope.Data.ToStringUtf8();
envelope.DataContentType.Should().Be("application/json"); envelope.DataContentType.ShouldBe("application/json");
envelope.PubsubName.Should().Be(TestPubsubName); envelope.PubsubName.ShouldBe(TestPubsubName);
envelope.Topic.Should().Be("test"); envelope.Topic.ShouldBe("test");
jsonFromRequest.Should().Be(JsonSerializer.Serialize(publishData, client.InnerClient.JsonSerializerOptions)); jsonFromRequest.ShouldBe(JsonSerializer.Serialize(publishData, client.InnerClient.JsonSerializerOptions));
envelope.Metadata.Count.Should().Be(2); envelope.Metadata.Count.ShouldBe(2);
envelope.Metadata.Keys.Contains("key1").Should().BeTrue(); envelope.Metadata.Keys.Contains("key1").ShouldBeTrue();
envelope.Metadata.Keys.Contains("key2").Should().BeTrue(); envelope.Metadata.Keys.Contains("key2").ShouldBeTrue();
envelope.Metadata["key1"].Should().Be("value1"); envelope.Metadata["key1"].ShouldBe("value1");
envelope.Metadata["key2"].Should().Be("value2"); envelope.Metadata["key2"].ShouldBe("value2");
} }
[Fact] [Fact]
@ -142,10 +140,10 @@ namespace Dapr.Client.Test
var envelope = await request.GetRequestEnvelopeAsync<PublishEventRequest>(); var envelope = await request.GetRequestEnvelopeAsync<PublishEventRequest>();
envelope.PubsubName.Should().Be(TestPubsubName); envelope.PubsubName.ShouldBe(TestPubsubName);
envelope.Topic.Should().Be("test"); envelope.Topic.ShouldBe("test");
envelope.Data.Length.Should().Be(0); envelope.Data.Length.ShouldBe(0);
envelope.Metadata.Count.Should().Be(0); envelope.Metadata.Count.ShouldBe(0);
} }
[Fact] [Fact]
@ -167,15 +165,15 @@ namespace Dapr.Client.Test
request.Dismiss(); request.Dismiss();
var envelope = await request.GetRequestEnvelopeAsync<PublishEventRequest>(); var envelope = await request.GetRequestEnvelopeAsync<PublishEventRequest>();
envelope.PubsubName.Should().Be(TestPubsubName); envelope.PubsubName.ShouldBe(TestPubsubName);
envelope.Topic.Should().Be("test"); envelope.Topic.ShouldBe("test");
envelope.Data.Length.Should().Be(0); envelope.Data.Length.ShouldBe(0);
envelope.Metadata.Count.Should().Be(2); envelope.Metadata.Count.ShouldBe(2);
envelope.Metadata.Keys.Contains("key1").Should().BeTrue(); envelope.Metadata.Keys.Contains("key1").ShouldBeTrue();
envelope.Metadata.Keys.Contains("key2").Should().BeTrue(); envelope.Metadata.Keys.Contains("key2").ShouldBeTrue();
envelope.Metadata["key1"].Should().Be("value1"); envelope.Metadata["key1"].ShouldBe("value1");
envelope.Metadata["key2"].Should().Be("value2"); envelope.Metadata["key2"].ShouldBe("value2");
} }
[Fact] [Fact]
@ -200,11 +198,11 @@ namespace Dapr.Client.Test
var envelope = await request.GetRequestEnvelopeAsync<PublishEventRequest>(); var envelope = await request.GetRequestEnvelopeAsync<PublishEventRequest>();
var jsonFromRequest = envelope.Data.ToStringUtf8(); var jsonFromRequest = envelope.Data.ToStringUtf8();
envelope.DataContentType.Should().Be("application/cloudevents+json"); envelope.DataContentType.ShouldBe("application/cloudevents+json");
envelope.PubsubName.Should().Be(TestPubsubName); envelope.PubsubName.ShouldBe(TestPubsubName);
envelope.Topic.Should().Be("test"); envelope.Topic.ShouldBe("test");
jsonFromRequest.Should().Be(JsonSerializer.Serialize(cloudEvent, client.InnerClient.JsonSerializerOptions)); jsonFromRequest.ShouldBe(JsonSerializer.Serialize(cloudEvent, client.InnerClient.JsonSerializerOptions));
envelope.Metadata.Count.Should().Be(0); envelope.Metadata.Count.ShouldBe(0);
} }
[Fact] [Fact]
@ -228,11 +226,11 @@ namespace Dapr.Client.Test
var envelope = await request.GetRequestEnvelopeAsync<PublishEventRequest>(); var envelope = await request.GetRequestEnvelopeAsync<PublishEventRequest>();
var jsonFromRequest = envelope.Data.ToStringUtf8(); var jsonFromRequest = envelope.Data.ToStringUtf8();
envelope.DataContentType.Should().Be("application/cloudevents+json"); envelope.DataContentType.ShouldBe("application/cloudevents+json");
envelope.PubsubName.Should().Be(TestPubsubName); envelope.PubsubName.ShouldBe(TestPubsubName);
envelope.Topic.Should().Be("test"); envelope.Topic.ShouldBe("test");
jsonFromRequest.Should().Be(JsonSerializer.Serialize(cloudEvent, client.InnerClient.JsonSerializerOptions)); jsonFromRequest.ShouldBe(JsonSerializer.Serialize(cloudEvent, client.InnerClient.JsonSerializerOptions));
envelope.Metadata.Count.Should().Be(0); envelope.Metadata.Count.ShouldBe(0);
} }
[Fact] [Fact]
@ -287,13 +285,13 @@ namespace Dapr.Client.Test
var envelope = await request.GetRequestEnvelopeAsync<PublishEventRequest>(); var envelope = await request.GetRequestEnvelopeAsync<PublishEventRequest>();
var jsonFromRequest = envelope.Data.ToStringUtf8(); var jsonFromRequest = envelope.Data.ToStringUtf8();
envelope.DataContentType.Should().Be("application/json"); envelope.DataContentType.ShouldBe("application/json");
envelope.PubsubName.Should().Be(TestPubsubName); envelope.PubsubName.ShouldBe(TestPubsubName);
envelope.Topic.Should().Be("test"); envelope.Topic.ShouldBe("test");
jsonFromRequest.Should().Be(JsonSerializer.Serialize(publishData)); jsonFromRequest.ShouldBe(JsonSerializer.Serialize(publishData));
// The default serializer forces camel case, so this should be different from our serialization above. // The default serializer forces camel case, so this should be different from our serialization above.
jsonFromRequest.Should().NotBe(JsonSerializer.Serialize(publishBytes, client.InnerClient.JsonSerializerOptions)); jsonFromRequest.ShouldNotBe(JsonSerializer.Serialize(publishBytes, client.InnerClient.JsonSerializerOptions));
envelope.Metadata.Count.Should().Be(0); envelope.Metadata.Count.ShouldBe(0);
} }
private class PublishData private class PublishData

View File

@ -17,7 +17,7 @@ namespace Dapr.Client.Test
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using FluentAssertions; using Shouldly;
using Grpc.Core; using Grpc.Core;
using Moq; using Moq;
using Xunit; using Xunit;
@ -44,13 +44,13 @@ namespace Dapr.Client.Test
// Get Request and validate // Get Request and validate
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.GetSecretRequest>(); var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.GetSecretRequest>();
envelope.StoreName.Should().Be("testStore"); envelope.StoreName.ShouldBe("testStore");
envelope.Key.Should().Be("test_key"); envelope.Key.ShouldBe("test_key");
envelope.Metadata.Count.Should().Be(2); envelope.Metadata.Count.ShouldBe(2);
envelope.Metadata.Keys.Contains("key1").Should().BeTrue(); envelope.Metadata.Keys.Contains("key1").ShouldBeTrue();
envelope.Metadata.Keys.Contains("key2").Should().BeTrue(); envelope.Metadata.Keys.Contains("key2").ShouldBeTrue();
envelope.Metadata["key1"].Should().Be("value1"); envelope.Metadata["key1"].ShouldBe("value1");
envelope.Metadata["key2"].Should().Be("value2"); envelope.Metadata["key2"].ShouldBe("value2");
} }
[Fact] [Fact]
@ -72,13 +72,13 @@ namespace Dapr.Client.Test
// Get Request and validate // Get Request and validate
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.GetSecretRequest>(); var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.GetSecretRequest>();
envelope.StoreName.Should().Be("testStore"); envelope.StoreName.ShouldBe("testStore");
envelope.Key.Should().Be("test_key"); envelope.Key.ShouldBe("test_key");
envelope.Metadata.Count.Should().Be(2); envelope.Metadata.Count.ShouldBe(2);
envelope.Metadata.Keys.Contains("key1").Should().BeTrue(); envelope.Metadata.Keys.Contains("key1").ShouldBeTrue();
envelope.Metadata.Keys.Contains("key2").Should().BeTrue(); envelope.Metadata.Keys.Contains("key2").ShouldBeTrue();
envelope.Metadata["key1"].Should().Be("value1"); envelope.Metadata["key1"].ShouldBe("value1");
envelope.Metadata["key2"].Should().Be("value2"); envelope.Metadata["key2"].ShouldBe("value2");
// Create Response & Respond // Create Response & Respond
var secrets = new Dictionary<string, string> var secrets = new Dictionary<string, string>
@ -88,9 +88,9 @@ namespace Dapr.Client.Test
var secretsResponse = await SendResponseWithSecrets(secrets, request); var secretsResponse = await SendResponseWithSecrets(secrets, request);
// Get response and validate // Get response and validate
secretsResponse.Count.Should().Be(1); secretsResponse.Count.ShouldBe(1);
secretsResponse.ContainsKey("redis_secret").Should().BeTrue(); secretsResponse.ContainsKey("redis_secret").ShouldBeTrue();
secretsResponse["redis_secret"].Should().Be("Guess_Redis"); secretsResponse["redis_secret"].ShouldBe("Guess_Redis");
} }
[Fact] [Fact]
@ -107,16 +107,16 @@ namespace Dapr.Client.Test
//Get Request and validate //Get Request and validate
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.GetSecretRequest>(); var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.GetSecretRequest>();
envelope.StoreName.Should().Be("testStore"); envelope.StoreName.ShouldBe("testStore");
envelope.Key.Should().Be("us-west-1/org/xpto/secretabc"); envelope.Key.ShouldBe("us-west-1/org/xpto/secretabc");
var secrets = new Dictionary<string, string> { { "us-west-1/org/xpto/secretabc", "abc123" } }; var secrets = new Dictionary<string, string> { { "us-west-1/org/xpto/secretabc", "abc123" } };
var secretsResponse = await SendResponseWithSecrets(secrets, request); var secretsResponse = await SendResponseWithSecrets(secrets, request);
//Get response and validate //Get response and validate
secretsResponse.Count.Should().Be(1); secretsResponse.Count.ShouldBe(1);
secretsResponse.ContainsKey("us-west-1/org/xpto/secretabc").Should().BeTrue(); secretsResponse.ContainsKey("us-west-1/org/xpto/secretabc").ShouldBeTrue();
secretsResponse["us-west-1/org/xpto/secretabc"].Should().Be("abc123"); secretsResponse["us-west-1/org/xpto/secretabc"].ShouldBe("abc123");
} }
[Fact] [Fact]
@ -138,13 +138,13 @@ namespace Dapr.Client.Test
// Get Request and validate // Get Request and validate
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.GetSecretRequest>(); var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.GetSecretRequest>();
envelope.StoreName.Should().Be("testStore"); envelope.StoreName.ShouldBe("testStore");
envelope.Key.Should().Be("test_key"); envelope.Key.ShouldBe("test_key");
envelope.Metadata.Count.Should().Be(2); envelope.Metadata.Count.ShouldBe(2);
envelope.Metadata.Keys.Contains("key1").Should().BeTrue(); envelope.Metadata.Keys.Contains("key1").ShouldBeTrue();
envelope.Metadata.Keys.Contains("key2").Should().BeTrue(); envelope.Metadata.Keys.Contains("key2").ShouldBeTrue();
envelope.Metadata["key1"].Should().Be("value1"); envelope.Metadata["key1"].ShouldBe("value1");
envelope.Metadata["key2"].Should().Be("value2"); envelope.Metadata["key2"].ShouldBe("value2");
// Create Response & Respond // Create Response & Respond
var secrets = new Dictionary<string, string> var secrets = new Dictionary<string, string>
@ -155,11 +155,11 @@ namespace Dapr.Client.Test
var secretsResponse = await SendResponseWithSecrets(secrets, request); var secretsResponse = await SendResponseWithSecrets(secrets, request);
// Get response and validate // Get response and validate
secretsResponse.Count.Should().Be(2); secretsResponse.Count.ShouldBe(2);
secretsResponse.ContainsKey("redis_secret").Should().BeTrue(); secretsResponse.ContainsKey("redis_secret").ShouldBeTrue();
secretsResponse["redis_secret"].Should().Be("Guess_Redis"); secretsResponse["redis_secret"].ShouldBe("Guess_Redis");
secretsResponse.ContainsKey("kafka_secret").Should().BeTrue(); secretsResponse.ContainsKey("kafka_secret").ShouldBeTrue();
secretsResponse["kafka_secret"].Should().Be("Guess_Kafka"); secretsResponse["kafka_secret"].ShouldBe("Guess_Kafka");
} }
[Fact] [Fact]
@ -220,12 +220,12 @@ namespace Dapr.Client.Test
// Get Request and validate // Get Request and validate
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.GetBulkSecretRequest>(); var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.GetBulkSecretRequest>();
envelope.StoreName.Should().Be("testStore"); envelope.StoreName.ShouldBe("testStore");
envelope.Metadata.Count.Should().Be(2); envelope.Metadata.Count.ShouldBe(2);
envelope.Metadata.Keys.Contains("key1").Should().BeTrue(); envelope.Metadata.Keys.Contains("key1").ShouldBeTrue();
envelope.Metadata.Keys.Contains("key2").Should().BeTrue(); envelope.Metadata.Keys.Contains("key2").ShouldBeTrue();
envelope.Metadata["key1"].Should().Be("value1"); envelope.Metadata["key1"].ShouldBe("value1");
envelope.Metadata["key2"].Should().Be("value2"); envelope.Metadata["key2"].ShouldBe("value2");
} }
[Fact] [Fact]
@ -244,12 +244,12 @@ namespace Dapr.Client.Test
// Get Request and validate // Get Request and validate
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.GetBulkSecretRequest>(); var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.GetBulkSecretRequest>();
envelope.StoreName.Should().Be("testStore"); envelope.StoreName.ShouldBe("testStore");
envelope.Metadata.Count.Should().Be(2); envelope.Metadata.Count.ShouldBe(2);
envelope.Metadata.Keys.Contains("key1").Should().BeTrue(); envelope.Metadata.Keys.Contains("key1").ShouldBeTrue();
envelope.Metadata.Keys.Contains("key2").Should().BeTrue(); envelope.Metadata.Keys.Contains("key2").ShouldBeTrue();
envelope.Metadata["key1"].Should().Be("value1"); envelope.Metadata["key1"].ShouldBe("value1");
envelope.Metadata["key2"].Should().Be("value2"); envelope.Metadata["key2"].ShouldBe("value2");
// Create Response & Respond // Create Response & Respond
var secrets = new Dictionary<string, string>(); var secrets = new Dictionary<string, string>();
@ -257,9 +257,9 @@ namespace Dapr.Client.Test
var secretsResponse = await SendBulkResponseWithSecrets(secrets, request); var secretsResponse = await SendBulkResponseWithSecrets(secrets, request);
// Get response and validate // Get response and validate
secretsResponse.Count.Should().Be(1); secretsResponse.Count.ShouldBe(1);
secretsResponse.ContainsKey("redis_secret").Should().BeTrue(); secretsResponse.ContainsKey("redis_secret").ShouldBeTrue();
secretsResponse["redis_secret"]["redis_secret"].Should().Be("Guess_Redis"); secretsResponse["redis_secret"]["redis_secret"].ShouldBe("Guess_Redis");
} }
[Fact] [Fact]
@ -279,12 +279,12 @@ namespace Dapr.Client.Test
// Get Request and validate // Get Request and validate
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.GetBulkSecretRequest>(); var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.GetBulkSecretRequest>();
envelope.StoreName.Should().Be("testStore"); envelope.StoreName.ShouldBe("testStore");
envelope.Metadata.Count.Should().Be(2); envelope.Metadata.Count.ShouldBe(2);
envelope.Metadata.Keys.Contains("key1").Should().BeTrue(); envelope.Metadata.Keys.Contains("key1").ShouldBeTrue();
envelope.Metadata.Keys.Contains("key2").Should().BeTrue(); envelope.Metadata.Keys.Contains("key2").ShouldBeTrue();
envelope.Metadata["key1"].Should().Be("value1"); envelope.Metadata["key1"].ShouldBe("value1");
envelope.Metadata["key2"].Should().Be("value2"); envelope.Metadata["key2"].ShouldBe("value2");
// Create Response & Respond // Create Response & Respond
var secrets = new Dictionary<string, string>(); var secrets = new Dictionary<string, string>();
@ -293,11 +293,11 @@ namespace Dapr.Client.Test
var secretsResponse = await SendBulkResponseWithSecrets(secrets, request); var secretsResponse = await SendBulkResponseWithSecrets(secrets, request);
// Get response and validate // Get response and validate
secretsResponse.Count.Should().Be(2); secretsResponse.Count.ShouldBe(2);
secretsResponse.ContainsKey("redis_secret").Should().BeTrue(); secretsResponse.ContainsKey("redis_secret").ShouldBeTrue();
secretsResponse["redis_secret"]["redis_secret"].Should().Be("Guess_Redis"); secretsResponse["redis_secret"]["redis_secret"].ShouldBe("Guess_Redis");
secretsResponse.ContainsKey("kafka_secret").Should().BeTrue(); secretsResponse.ContainsKey("kafka_secret").ShouldBeTrue();
secretsResponse["kafka_secret"]["kafka_secret"].Should().Be("Guess_Kafka"); secretsResponse["kafka_secret"]["kafka_secret"].ShouldBe("Guess_Kafka");
} }
[Fact] [Fact]

View File

@ -18,12 +18,12 @@ using System.Net;
using System.Text.Json; using System.Text.Json;
using System.Threading.Tasks; using System.Threading.Tasks;
using Autogenerated = Dapr.Client.Autogen.Grpc.v1; using Autogenerated = Dapr.Client.Autogen.Grpc.v1;
using FluentAssertions;
using Google.Protobuf; using Google.Protobuf;
using Grpc.Core; using Grpc.Core;
using Moq; using Moq;
using StateConsistency = Dapr.Client.Autogen.Grpc.v1.StateOptions.Types.StateConsistency; using StateConsistency = Dapr.Client.Autogen.Grpc.v1.StateOptions.Types.StateConsistency;
using StateConcurrency = Dapr.Client.Autogen.Grpc.v1.StateOptions.Types.StateConcurrency; using StateConcurrency = Dapr.Client.Autogen.Grpc.v1.StateOptions.Types.StateConcurrency;
using Shouldly;
using Xunit; using Xunit;
using System.Threading; using System.Threading;
using System.Net.Http; using System.Net.Http;
@ -48,8 +48,8 @@ namespace Dapr.Client.Test
var state = await request.CompleteWithMessageAsync(envelope); var state = await request.CompleteWithMessageAsync(envelope);
// Get response and validate // Get response and validate
state.Size.Should().Be("small"); state.Size.ShouldBe("small");
state.Color.Should().Be("yellow"); state.Color.ShouldBe("yellow");
} }
[Fact] [Fact]
@ -66,7 +66,7 @@ namespace Dapr.Client.Test
var state = await request.CompleteWithMessageAsync(envelope); var state = await request.CompleteWithMessageAsync(envelope);
// Get response and validate // Get response and validate
state.Should().HaveCount(1); state.Count.ShouldBe(1);
} }
[Fact] [Fact]
@ -85,9 +85,9 @@ namespace Dapr.Client.Test
var state = await request.CompleteWithMessageAsync(envelope); var state = await request.CompleteWithMessageAsync(envelope);
// Get response and validate // Get response and validate
state.Should().HaveCount(1); state.Count.ShouldBe(1);
state[0].Value.Size.Should().Match(size); state[0].Value.Size.ShouldMatch(size);
state[0].Value.Color.Should().Match(color); state[0].Value.Color.ShouldMatch(color);
} }
[Fact] [Fact]
@ -122,8 +122,8 @@ namespace Dapr.Client.Test
// Create Response & Validate // Create Response & Validate
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.GetBulkStateRequest>(); var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.GetBulkStateRequest>();
envelope.StoreName.Should().Be("testStore"); envelope.StoreName.ShouldBe("testStore");
envelope.Metadata.Should().BeEquivalentTo(metadata); envelope.Metadata.ShouldBe(metadata);
} }
[Fact] [Fact]
@ -139,9 +139,9 @@ namespace Dapr.Client.Test
var (state, etag) = await request.CompleteWithMessageAsync(envelope); var (state, etag) = await request.CompleteWithMessageAsync(envelope);
// Get response and validate // Get response and validate
state.Size.Should().Be("small"); state.Size.ShouldBe("small");
state.Color.Should().Be("yellow"); state.Color.ShouldBe("yellow");
etag.Should().Be("Test_Etag"); etag.ShouldBe("Test_Etag");
} }
[Fact] [Fact]
@ -191,7 +191,7 @@ namespace Dapr.Client.Test
var state = await request.CompleteWithMessageAsync(envelope); var state = await request.CompleteWithMessageAsync(envelope);
// Get response and validate // Get response and validate
state.Should().BeNull(); state.ShouldBeNull();
} }
[Theory] [Theory]
@ -205,15 +205,15 @@ namespace Dapr.Client.Test
// Get Request & Validate // Get Request & Validate
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.GetStateRequest>(); var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.GetStateRequest>();
envelope.StoreName.Should().Be("testStore"); envelope.StoreName.ShouldBe("testStore");
envelope.Key.Should().Be("test"); envelope.Key.ShouldBe("test");
envelope.Consistency.Should().Be(expectedConsistencyMode); envelope.Consistency.ShouldBe(expectedConsistencyMode);
// Create Response & Respond // Create Response & Respond
var state = await request.CompleteWithMessageAsync(MakeGetStateResponse<Widget>(null)); var state = await request.CompleteWithMessageAsync(MakeGetStateResponse<Widget>(null));
// Get response and validate // Get response and validate
state.Should().BeNull(); state.ShouldBeNull();
} }
[Fact] [Fact]
@ -229,15 +229,15 @@ namespace Dapr.Client.Test
// Get Request & Validate // Get Request & Validate
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.GetStateRequest>(); var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.GetStateRequest>();
envelope.StoreName.Should().Be("testStore"); envelope.StoreName.ShouldBe("testStore");
envelope.Key.Should().Be("test"); envelope.Key.ShouldBe("test");
envelope.Metadata.Should().BeEquivalentTo(metadata); envelope.Metadata.ShouldBe(metadata);
// Create Response & Respond // Create Response & Respond
var state = await request.CompleteWithMessageAsync(MakeGetStateResponse<Widget>(null)); var state = await request.CompleteWithMessageAsync(MakeGetStateResponse<Widget>(null));
// Get response and validate // Get response and validate
state.Should().BeNull(); state.ShouldBeNull();
} }
[Fact] [Fact]
@ -291,15 +291,15 @@ namespace Dapr.Client.Test
// Get Request and validate // Get Request and validate
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.SaveStateRequest>(); var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.SaveStateRequest>();
envelope.StoreName.Should().Be("testStore"); envelope.StoreName.ShouldBe("testStore");
envelope.States.Count.Should().Be(1); envelope.States.Count.ShouldBe(1);
var state = envelope.States[0]; var state = envelope.States[0];
state.Key.Should().Be("test"); state.Key.ShouldBe("test");
var stateJson = state.Value.ToStringUtf8(); var stateJson = state.Value.ToStringUtf8();
var stateFromRequest = JsonSerializer.Deserialize<Widget>(stateJson, client.InnerClient.JsonSerializerOptions); var stateFromRequest = JsonSerializer.Deserialize<Widget>(stateJson, client.InnerClient.JsonSerializerOptions);
stateFromRequest.Size.Should().Be(widget.Size); stateFromRequest.Size.ShouldBe(widget.Size);
stateFromRequest.Color.Should().Be(widget.Color); stateFromRequest.Color.ShouldBe(widget.Color);
} }
[Fact] [Fact]
@ -329,20 +329,20 @@ namespace Dapr.Client.Test
// Create Response & Validate // Create Response & Validate
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.SaveStateRequest>(); var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.SaveStateRequest>();
envelope.StoreName.Should().Be("testStore"); envelope.StoreName.ShouldBe("testStore");
envelope.States.Count.Should().Be(3); envelope.States.Count.ShouldBe(3);
envelope.States[0].Key.Should().Be("testKey1"); envelope.States[0].Key.ShouldBe("testKey1");
envelope.States[0].Value.Should().Equal(ByteString.CopyFromUtf8(JsonSerializer.Serialize("testValue1"))); envelope.States[0].Value.ShouldBe(ByteString.CopyFromUtf8(JsonSerializer.Serialize("testValue1")));
envelope.States[0].Metadata.Should().ContainKey("partitionKey1"); envelope.States[0].Metadata.ShouldContainKey("partitionKey1");
envelope.States[1].Key.Should().Be("testKey2"); envelope.States[1].Key.ShouldBe("testKey2");
envelope.States[1].Value.Should().Equal(ByteString.CopyFromUtf8(JsonSerializer.Serialize("testValue2"))); envelope.States[1].Value.ShouldBe(ByteString.CopyFromUtf8(JsonSerializer.Serialize("testValue2")));
envelope.States[1].Metadata.Should().ContainKey("partitionKey2"); envelope.States[1].Metadata.ShouldContainKey("partitionKey2");
envelope.States[2].Key.Should().Be("testKey3"); envelope.States[2].Key.ShouldBe("testKey3");
envelope.States[2].Value.Should().Equal(ByteString.CopyFromUtf8(JsonSerializer.Serialize("testValue3"))); envelope.States[2].Value.ShouldBe(ByteString.CopyFromUtf8(JsonSerializer.Serialize("testValue3")));
envelope.States[2].Metadata.Should().ContainKey("partitionKey3"); envelope.States[2].Metadata.ShouldContainKey("partitionKey3");
} }
[Fact] [Fact]
@ -374,11 +374,11 @@ namespace Dapr.Client.Test
// Get Request and validate // Get Request and validate
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.SaveStateRequest>(); var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.SaveStateRequest>();
envelope.StoreName.Should().Be("testStore"); envelope.StoreName.ShouldBe("testStore");
envelope.States.Count.Should().Be(1); envelope.States.Count.ShouldBe(1);
var state = envelope.States[0]; var state = envelope.States[0];
state.Key.Should().Be("test"); state.Key.ShouldBe("test");
state.Value.Should().Equal(ByteString.Empty); state.Value.ShouldBe(ByteString.Empty);
} }
[Fact] [Fact]
@ -453,34 +453,34 @@ namespace Dapr.Client.Test
// Get Request and validate // Get Request and validate
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.ExecuteStateTransactionRequest>(); var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.ExecuteStateTransactionRequest>();
envelope.StoreName.Should().Be("testStore"); envelope.StoreName.ShouldBe("testStore");
envelope.Operations.Count.Should().Be(3); envelope.Operations.Count.ShouldBe(3);
var req1 = envelope.Operations[0]; var req1 = envelope.Operations[0];
req1.Request.Key.Should().Be("stateKey1"); req1.Request.Key.ShouldBe("stateKey1");
req1.OperationType.Should().Be(StateOperationType.Upsert.ToString().ToLower()); req1.OperationType.ShouldBe(StateOperationType.Upsert.ToString().ToLower());
var valueJson1 = req1.Request.Value.ToStringUtf8(); var valueJson1 = req1.Request.Value.ToStringUtf8();
var value1 = JsonSerializer.Deserialize<Widget>(valueJson1, client.InnerClient.JsonSerializerOptions); var value1 = JsonSerializer.Deserialize<Widget>(valueJson1, client.InnerClient.JsonSerializerOptions);
value1.Size.Should().Be(stateValue1.Size); value1.Size.ShouldBe(stateValue1.Size);
value1.Color.Should().Be(stateValue1.Color); value1.Color.ShouldBe(stateValue1.Color);
req1.Request.Etag.Value.Should().Be("testEtag"); req1.Request.Etag.Value.ShouldBe("testEtag");
req1.Request.Metadata.Count.Should().Be(1); req1.Request.Metadata.Count.ShouldBe(1);
req1.Request.Metadata["a"].Should().Be("b"); req1.Request.Metadata["a"].ShouldBe("b");
req1.Request.Options.Concurrency.Should().Be(StateConcurrency.ConcurrencyLastWrite); req1.Request.Options.Concurrency.ShouldBe(StateConcurrency.ConcurrencyLastWrite);
var req2 = envelope.Operations[1]; var req2 = envelope.Operations[1];
req2.Request.Key.Should().Be("stateKey2"); req2.Request.Key.ShouldBe("stateKey2");
req2.OperationType.Should().Be(StateOperationType.Delete.ToString().ToLower()); req2.OperationType.ShouldBe(StateOperationType.Delete.ToString().ToLower());
var valueJson2 = req2.Request.Value.ToStringUtf8(); var valueJson2 = req2.Request.Value.ToStringUtf8();
var value2 = JsonSerializer.Deserialize<int>(valueJson2, client.InnerClient.JsonSerializerOptions); var value2 = JsonSerializer.Deserialize<int>(valueJson2, client.InnerClient.JsonSerializerOptions);
value2.Should().Be(100); value2.ShouldBe(100);
var req3 = envelope.Operations[2]; var req3 = envelope.Operations[2];
req3.Request.Key.Should().Be("stateKey3"); req3.Request.Key.ShouldBe("stateKey3");
req3.OperationType.Should().Be(StateOperationType.Upsert.ToString().ToLower()); req3.OperationType.ShouldBe(StateOperationType.Upsert.ToString().ToLower());
var valueJson3 = req3.Request.Value.ToStringUtf8(); var valueJson3 = req3.Request.Value.ToStringUtf8();
var value3 = JsonSerializer.Deserialize<string>(valueJson3, client.InnerClient.JsonSerializerOptions); var value3 = JsonSerializer.Deserialize<string>(valueJson3, client.InnerClient.JsonSerializerOptions);
value3.Should().Be("teststring"); value3.ShouldBe("teststring");
} }
[Fact] [Fact]
@ -541,8 +541,8 @@ namespace Dapr.Client.Test
request.Dismiss(); request.Dismiss();
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.DeleteStateRequest>(); var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.DeleteStateRequest>();
envelope.StoreName.Should().Be("testStore"); envelope.StoreName.ShouldBe("testStore");
envelope.Key.Should().Be("test"); envelope.Key.ShouldBe("test");
} }
[Fact] [Fact]
@ -590,8 +590,8 @@ namespace Dapr.Client.Test
var state = await request.CompleteWithMessageAsync(envelope); var state = await request.CompleteWithMessageAsync(envelope);
// Get response and validate // Get response and validate
state.Value.Size.Should().Be("small"); state.Value.Size.ShouldBe("small");
state.Value.Color.Should().Be("yellow"); state.Value.Color.ShouldBe("yellow");
} }
[Fact] [Fact]
@ -605,8 +605,8 @@ namespace Dapr.Client.Test
var envelope = MakeGetStateResponse<Widget>(null); var envelope = MakeGetStateResponse<Widget>(null);
var state = await request.CompleteWithMessageAsync(envelope); var state = await request.CompleteWithMessageAsync(envelope);
state.Key.Should().Be("test"); state.Key.ShouldBe("test");
state.Value.Should().BeNull(); state.Value.ShouldBeNull();
} }
[Fact] [Fact]
@ -620,9 +620,9 @@ namespace Dapr.Client.Test
var data = new Widget() { Size = "small", Color = "yellow", }; var data = new Widget() { Size = "small", Color = "yellow", };
var state = await request.CompleteWithMessageAsync(MakeGetStateResponse(data)); var state = await request.CompleteWithMessageAsync(MakeGetStateResponse(data));
state.Key.Should().Be("test"); state.Key.ShouldBe("test");
state.Value.Size.Should().Be("small"); state.Value.Size.ShouldBe("small");
state.Value.Color.Should().Be("yellow"); state.Value.Color.ShouldBe("yellow");
// Modify the state and save it // Modify the state and save it
state.Value.Color = "green"; state.Value.Color = "green";
@ -637,15 +637,15 @@ namespace Dapr.Client.Test
// Get Request and validate // Get Request and validate
var envelope = await request2.GetRequestEnvelopeAsync<Autogenerated.SaveStateRequest>(); var envelope = await request2.GetRequestEnvelopeAsync<Autogenerated.SaveStateRequest>();
envelope.StoreName.Should().Be("testStore"); envelope.StoreName.ShouldBe("testStore");
envelope.States.Count.Should().Be(1); envelope.States.Count.ShouldBe(1);
var requestState = envelope.States[0]; var requestState = envelope.States[0];
requestState.Key.Should().Be("test"); requestState.Key.ShouldBe("test");
var stateJson = requestState.Value.ToStringUtf8(); var stateJson = requestState.Value.ToStringUtf8();
var stateFromRequest = JsonSerializer.Deserialize<Widget>(stateJson, client.InnerClient.JsonSerializerOptions); var stateFromRequest = JsonSerializer.Deserialize<Widget>(stateJson, client.InnerClient.JsonSerializerOptions);
stateFromRequest.Size.Should().Be("small"); stateFromRequest.Size.ShouldBe("small");
stateFromRequest.Color.Should().Be("green"); stateFromRequest.Color.ShouldBe("green");
} }
[Fact] [Fact]
@ -659,9 +659,9 @@ namespace Dapr.Client.Test
var data = new Widget() { Size = "small", Color = "yellow", }; var data = new Widget() { Size = "small", Color = "yellow", };
var state = await request.CompleteWithMessageAsync(MakeGetStateResponse(data)); var state = await request.CompleteWithMessageAsync(MakeGetStateResponse(data));
state.Key.Should().Be("test"); state.Key.ShouldBe("test");
state.Value.Size.Should().Be("small"); state.Value.Size.ShouldBe("small");
state.Value.Color.Should().Be("yellow"); state.Value.Color.ShouldBe("yellow");
state.Value.Color = "green"; state.Value.Color = "green";
var request2 = await client.CaptureGrpcRequestAsync(async daprClient => var request2 = await client.CaptureGrpcRequestAsync(async daprClient =>
@ -673,8 +673,8 @@ namespace Dapr.Client.Test
// Get Request and validate // Get Request and validate
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.DeleteStateRequest>(); var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.DeleteStateRequest>();
envelope.StoreName.Should().Be("testStore"); envelope.StoreName.ShouldBe("testStore");
envelope.Key.Should().Be("test"); envelope.Key.ShouldBe("test");
} }
@ -713,22 +713,22 @@ namespace Dapr.Client.Test
// Get Request and validate // Get Request and validate
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.SaveStateRequest>(); var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.SaveStateRequest>();
envelope.StoreName.Should().Be("testStore"); envelope.StoreName.ShouldBe("testStore");
envelope.States.Count.Should().Be(1); envelope.States.Count.ShouldBe(1);
var state = envelope.States[0]; var state = envelope.States[0];
state.Key.Should().Be("test"); state.Key.ShouldBe("test");
state.Metadata.Count.Should().Be(2); state.Metadata.Count.ShouldBe(2);
state.Metadata.Keys.Contains("key1").Should().BeTrue(); state.Metadata.Keys.Contains("key1").ShouldBeTrue();
state.Metadata.Keys.Contains("key2").Should().BeTrue(); state.Metadata.Keys.Contains("key2").ShouldBeTrue();
state.Metadata["key1"].Should().Be("value1"); state.Metadata["key1"].ShouldBe("value1");
state.Metadata["key2"].Should().Be("value2"); state.Metadata["key2"].ShouldBe("value2");
state.Options.Concurrency.Should().Be(expectedConcurrency); state.Options.Concurrency.ShouldBe(expectedConcurrency);
state.Options.Consistency.Should().Be(expectedConsistency); state.Options.Consistency.ShouldBe(expectedConsistency);
var stateJson = state.Value.ToStringUtf8(); var stateJson = state.Value.ToStringUtf8();
var stateFromRequest = JsonSerializer.Deserialize<Widget>(stateJson, client.InnerClient.JsonSerializerOptions); var stateFromRequest = JsonSerializer.Deserialize<Widget>(stateJson, client.InnerClient.JsonSerializerOptions);
stateFromRequest.Size.Should().Be(widget.Size); stateFromRequest.Size.ShouldBe(widget.Size);
stateFromRequest.Color.Should().Be(widget.Color); stateFromRequest.Color.ShouldBe(widget.Color);
} }
[Theory] [Theory]
@ -762,22 +762,22 @@ namespace Dapr.Client.Test
// Get Request and validate // Get Request and validate
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.SaveStateRequest>(); var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.SaveStateRequest>();
envelope.StoreName.Should().Be("testStore"); envelope.StoreName.ShouldBe("testStore");
envelope.States.Count.Should().Be(1); envelope.States.Count.ShouldBe(1);
var state = envelope.States[0]; var state = envelope.States[0];
state.Etag.Value.Should().Be("Test_Etag"); state.Etag.Value.ShouldBe("Test_Etag");
state.Metadata.Count.Should().Be(2); state.Metadata.Count.ShouldBe(2);
state.Metadata.Keys.Contains("key1").Should().BeTrue(); state.Metadata.Keys.Contains("key1").ShouldBeTrue();
state.Metadata.Keys.Contains("key2").Should().BeTrue(); state.Metadata.Keys.Contains("key2").ShouldBeTrue();
state.Metadata["key1"].Should().Be("value1"); state.Metadata["key1"].ShouldBe("value1");
state.Metadata["key2"].Should().Be("value2"); state.Metadata["key2"].ShouldBe("value2");
state.Options.Concurrency.Should().Be(expectedConcurrency); state.Options.Concurrency.ShouldBe(expectedConcurrency);
state.Options.Consistency.Should().Be(expectedConsistency); state.Options.Consistency.ShouldBe(expectedConsistency);
var stateJson = state.Value.ToStringUtf8(); var stateJson = state.Value.ToStringUtf8();
var stateFromRequest = JsonSerializer.Deserialize<Widget>(stateJson, client.InnerClient.JsonSerializerOptions); var stateFromRequest = JsonSerializer.Deserialize<Widget>(stateJson, client.InnerClient.JsonSerializerOptions);
stateFromRequest.Size.Should().Be(widget.Size); stateFromRequest.Size.ShouldBe(widget.Size);
stateFromRequest.Color.Should().Be(widget.Color); stateFromRequest.Color.ShouldBe(widget.Color);
} }
[Fact] [Fact]
@ -827,8 +827,7 @@ namespace Dapr.Client.Test
await client.CallStateApi<string>() await client.CallStateApi<string>()
.Build(); .Build();
await FluentActions.Awaiting(async () => await client.DaprClient.TrySaveStateAsync("test", "test", "testValue", null)) await Should.ThrowAsync<ArgumentException>(async () => await client.DaprClient.TrySaveStateAsync("test", "test", "testValue", null));
.Should().ThrowAsync<ArgumentException>();
} }
[Fact] [Fact]
@ -877,8 +876,7 @@ namespace Dapr.Client.Test
await client.CallStateApi<string>() await client.CallStateApi<string>()
.Build(); .Build();
await FluentActions.Awaiting(async () => await client.DaprClient.TryDeleteStateAsync("test", "test", null)) await Should.ThrowAsync<ArgumentException>(async () => await client.DaprClient.TryDeleteStateAsync("test", "test", null));
.Should().ThrowAsync<ArgumentException>();
} }
[Fact] [Fact]
@ -943,10 +941,10 @@ namespace Dapr.Client.Test
// Get Request and validate // Get Request and validate
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.DeleteStateRequest>(); var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.DeleteStateRequest>();
envelope.StoreName.Should().Be("testStore"); envelope.StoreName.ShouldBe("testStore");
envelope.Key.Should().Be("test"); envelope.Key.ShouldBe("test");
envelope.Options.Concurrency.Should().Be(expectedConcurrency); envelope.Options.Concurrency.ShouldBe(expectedConcurrency);
envelope.Options.Consistency.Should().Be(expectedConsistency); envelope.Options.Consistency.ShouldBe(expectedConsistency);
} }
@ -975,11 +973,11 @@ namespace Dapr.Client.Test
// Get Request and validate // Get Request and validate
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.DeleteStateRequest>(); var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.DeleteStateRequest>();
envelope.StoreName.Should().Be("testStore"); envelope.StoreName.ShouldBe("testStore");
envelope.Key.Should().Be("test"); envelope.Key.ShouldBe("test");
envelope.Etag.Value.Should().Be("Test_Etag"); envelope.Etag.Value.ShouldBe("Test_Etag");
envelope.Options.Concurrency.Should().Be(expectedConcurrency); envelope.Options.Concurrency.ShouldBe(expectedConcurrency);
envelope.Options.Consistency.Should().Be(expectedConsistency); envelope.Options.Consistency.ShouldBe(expectedConsistency);
} }
[Fact] [Fact]
@ -1003,10 +1001,10 @@ namespace Dapr.Client.Test
// Create Response & Validate // Create Response & Validate
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.DeleteBulkStateRequest>(); var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.DeleteBulkStateRequest>();
envelope.StoreName.Should().Be("testStore"); envelope.StoreName.ShouldBe("testStore");
envelope.States.Count.Should().Be(1); envelope.States.Count.ShouldBe(1);
envelope.States[0].Key.Should().Be(key); envelope.States[0].Key.ShouldBe(key);
envelope.States[0].Metadata.Should().ContainKey("partitionKey"); envelope.States[0].Metadata.ShouldContainKey("partitionKey");
} }
[Fact] [Fact]
@ -1019,9 +1017,9 @@ namespace Dapr.Client.Test
// Validate request. // Validate request.
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.QueryStateRequest>(); var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.QueryStateRequest>();
envelope.StoreName.Should().Be("testStore"); envelope.StoreName.ShouldBe("testStore");
envelope.Query.Should().Be(queryJson); envelope.Query.ShouldBe(queryJson);
envelope.Metadata.Should().BeEmpty(); envelope.Metadata.ShouldBeEmpty();
// Validate response. // Validate response.
var testData = new Widget() { Color = "Green", Size = "Small" }; var testData = new Widget() { Color = "Green", Size = "Small" };
@ -1029,11 +1027,11 @@ namespace Dapr.Client.Test
wireResponse.Results.Add(MakeQueryStateItem("test", testData, "an etag")); wireResponse.Results.Add(MakeQueryStateItem("test", testData, "an etag"));
var response = await request.CompleteWithMessageAsync(wireResponse); var response = await request.CompleteWithMessageAsync(wireResponse);
response.Results.Count.Should().Be(1); response.Results.Count.ShouldBe(1);
response.Results[0].Key.Should().Be("test"); response.Results[0].Key.ShouldBe("test");
response.Results[0].Data.Should().Be(testData); response.Results[0].Data.ShouldBe(testData);
response.Results[0].ETag.Should().Be("an etag"); response.Results[0].ETag.ShouldBe("an etag");
response.Results[0].Error.Should().BeNullOrEmpty(); response.Results[0].Error.ShouldBeNullOrEmpty();
} }
[Fact] [Fact]
@ -1046,9 +1044,9 @@ namespace Dapr.Client.Test
// Validate request. // Validate request.
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.QueryStateRequest>(); var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.QueryStateRequest>();
envelope.StoreName.Should().Be("testStore"); envelope.StoreName.ShouldBe("testStore");
envelope.Query.Should().Be(queryJson); envelope.Query.ShouldBe(queryJson);
envelope.Metadata.Should().BeEmpty(); envelope.Metadata.ShouldBeEmpty();
// Validate response, we expect to only get the first object as the 2nd will present an error. // 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" }; var testData1 = new Widget() { Color = "Green", Size = "Small" };
@ -1061,21 +1059,21 @@ namespace Dapr.Client.Test
wireResponse.Results.Add(MakeQueryStateItem("test3", testData3)); wireResponse.Results.Add(MakeQueryStateItem("test3", testData3));
var ex = await Assert.ThrowsAsync<StateQueryException<Widget>>(() => request.CompleteWithMessageAsync(wireResponse)); var ex = await Assert.ThrowsAsync<StateQueryException<Widget>>(() => 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; var response = ex.Response;
response.Results.Count.Should().Be(2); response.Results.Count.ShouldBe(2);
response.Results[0].Key.Should().Be("test1"); response.Results[0].Key.ShouldBe("test1");
response.Results[0].Data.Should().Be(testData1); response.Results[0].Data.ShouldBe(testData1);
response.Results[0].ETag.Should().BeNullOrEmpty(); response.Results[0].ETag.ShouldBeNullOrEmpty();
response.Results[0].Error.Should().BeNullOrEmpty(); response.Results[0].Error.ShouldBeNullOrEmpty();
response.Results[1].Key.Should().Be("test3"); response.Results[1].Key.ShouldBe("test3");
response.Results[1].Data.Should().Be(testData3); response.Results[1].Data.ShouldBe(testData3);
response.Results[1].ETag.Should().BeNullOrEmpty(); response.Results[1].ETag.ShouldBeNullOrEmpty();
response.Results[1].Error.Should().BeNullOrEmpty(); response.Results[1].Error.ShouldBeNullOrEmpty();
var failedKeys = ex.FailedKeys; var failedKeys = ex.FailedKeys;
failedKeys.Count.Should().Be(1); failedKeys.Count.ShouldBe(1);
failedKeys[0].Should().Be("test2"); failedKeys[0].ShouldBe("test2");
} }
private Autogenerated.GetStateResponse MakeGetStateResponse<T>(T state, string etag = null) private Autogenerated.GetStateResponse MakeGetStateResponse<T>(T state, string etag = null)
@ -1157,20 +1155,20 @@ namespace Dapr.Client.Test
// Get Request and validate // Get Request and validate
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.SaveStateRequest>(); var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.SaveStateRequest>();
envelope.StoreName.Should().Be("testStore"); envelope.StoreName.ShouldBe("testStore");
envelope.States.Count.Should().Be(1); envelope.States.Count.ShouldBe(1);
var state = envelope.States[0]; var state = envelope.States[0];
state.Key.Should().Be("test"); state.Key.ShouldBe("test");
state.Metadata.Count.Should().Be(2); state.Metadata.Count.ShouldBe(2);
state.Metadata.Keys.Contains("key1").Should().BeTrue(); state.Metadata.Keys.Contains("key1").ShouldBeTrue();
state.Metadata.Keys.Contains("key2").Should().BeTrue(); state.Metadata.Keys.Contains("key2").ShouldBeTrue();
state.Metadata["key1"].Should().Be("value1"); state.Metadata["key1"].ShouldBe("value1");
state.Metadata["key2"].Should().Be("value2"); state.Metadata["key2"].ShouldBe("value2");
state.Options.Concurrency.Should().Be(expectedConcurrency); state.Options.Concurrency.ShouldBe(expectedConcurrency);
state.Options.Consistency.Should().Be(expectedConsistency); state.Options.Consistency.ShouldBe(expectedConsistency);
var stateBinaryData = state.Value.ToStringUtf8(); var stateBinaryData = state.Value.ToStringUtf8();
stateBinaryData.Should().Be(data); stateBinaryData.ShouldBe(data);
} }
[Fact] [Fact]
@ -1189,13 +1187,13 @@ namespace Dapr.Client.Test
// Get Request and validate // Get Request and validate
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.SaveStateRequest>(); var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.SaveStateRequest>();
envelope.StoreName.Should().Be("testStore"); envelope.StoreName.ShouldBe("testStore");
envelope.States.Count.Should().Be(1); envelope.States.Count.ShouldBe(1);
var state = envelope.States[0]; var state = envelope.States[0];
state.Key.Should().Be("test"); state.Key.ShouldBe("test");
var stateBinaryData = state.Value.ToStringUtf8(); var stateBinaryData = state.Value.ToStringUtf8();
stateBinaryData.Should().Be(data); stateBinaryData.ShouldBe(data);
} }
[Fact] [Fact]
@ -1213,11 +1211,11 @@ namespace Dapr.Client.Test
// Get Request and validate // Get Request and validate
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.SaveStateRequest>(); var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.SaveStateRequest>();
envelope.StoreName.Should().Be("testStore"); envelope.StoreName.ShouldBe("testStore");
envelope.States.Count.Should().Be(1); envelope.States.Count.ShouldBe(1);
var state = envelope.States[0]; var state = envelope.States[0];
state.Key.Should().Be("test"); state.Key.ShouldBe("test");
state.Value.Should().Equal(ByteString.Empty); state.Value.ShouldBe(ByteString.Empty);
} }
[Fact] [Fact]
@ -1265,20 +1263,20 @@ namespace Dapr.Client.Test
// Get Request and validate // Get Request and validate
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.SaveStateRequest>(); var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.SaveStateRequest>();
envelope.StoreName.Should().Be("testStore"); envelope.StoreName.ShouldBe("testStore");
envelope.States.Count.Should().Be(1); envelope.States.Count.ShouldBe(1);
var state = envelope.States[0]; var state = envelope.States[0];
state.Etag.Value.Should().Be("Test_Etag"); state.Etag.Value.ShouldBe("Test_Etag");
state.Metadata.Count.Should().Be(2); state.Metadata.Count.ShouldBe(2);
state.Metadata.Keys.Contains("key1").Should().BeTrue(); state.Metadata.Keys.Contains("key1").ShouldBeTrue();
state.Metadata.Keys.Contains("key2").Should().BeTrue(); state.Metadata.Keys.Contains("key2").ShouldBeTrue();
state.Metadata["key1"].Should().Be("value1"); state.Metadata["key1"].ShouldBe("value1");
state.Metadata["key2"].Should().Be("value2"); state.Metadata["key2"].ShouldBe("value2");
state.Options.Concurrency.Should().Be(expectedConcurrency); state.Options.Concurrency.ShouldBe(expectedConcurrency);
state.Options.Consistency.Should().Be(expectedConsistency); state.Options.Consistency.ShouldBe(expectedConsistency);
var stateBinaryData = state.Value.ToStringUtf8(); var stateBinaryData = state.Value.ToStringUtf8();
stateBinaryData.Should().Be(data); stateBinaryData.ShouldBe(data);
} }
[Fact] [Fact]
@ -1332,8 +1330,7 @@ namespace Dapr.Client.Test
var response = client.CallStateApi<string>() var response = client.CallStateApi<string>()
.Build(); .Build();
await FluentActions.Awaiting(async () => await client.DaprClient.TrySaveByteStateAsync("test", "test", stateBytes.AsMemory(), null)) await Should.ThrowAsync<ArgumentException>(async () => await client.DaprClient.TrySaveByteStateAsync("test", "test", stateBytes.AsMemory(), null));
.Should().ThrowAsync<ArgumentException>();
} }
[Fact] [Fact]
@ -1365,7 +1362,7 @@ namespace Dapr.Client.Test
var state = await request.CompleteWithMessageAsync(envelope); var state = await request.CompleteWithMessageAsync(envelope);
// Get response and validate // Get response and validate
state.ToArray().Should().BeNullOrEmpty(); state.ToArray().ShouldBeEmpty();
} }
[Theory] [Theory]
@ -1379,15 +1376,15 @@ namespace Dapr.Client.Test
// Get Request & Validate // Get Request & Validate
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.GetStateRequest>(); var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.GetStateRequest>();
envelope.StoreName.Should().Be("testStore"); envelope.StoreName.ShouldBe("testStore");
envelope.Key.Should().Be("test"); envelope.Key.ShouldBe("test");
envelope.Consistency.Should().Be(expectedConsistencyMode); envelope.Consistency.ShouldBe(expectedConsistencyMode);
var binaryData = Encoding.ASCII.GetBytes("test data"); var binaryData = Encoding.ASCII.GetBytes("test data");
// Create Response & Respond // Create Response & Respond
var state = await request.CompleteWithMessageAsync(MakeGetByteStateResponse(binaryData.AsMemory())); var state = await request.CompleteWithMessageAsync(MakeGetByteStateResponse(binaryData.AsMemory()));
var stateStr = ByteString.CopyFrom(state.Span).ToByteArray(); var stateStr = ByteString.CopyFrom(state.Span).ToByteArray();
// Get response and validate // Get response and validate
stateStr.Should().BeEquivalentTo(binaryData); stateStr.ShouldBeEquivalentTo(binaryData);
} }
[Fact] [Fact]
@ -1400,18 +1397,17 @@ namespace Dapr.Client.Test
{ "partitionKey", "mypartition" } { "partitionKey", "mypartition" }
}; };
var request = await client.CaptureGrpcRequestAsync(async daprClient => await daprClient.GetByteStateAndETagAsync("testStore", "test", metadata: metadata)); var request = await client.CaptureGrpcRequestAsync(async daprClient => await daprClient.GetByteStateAndETagAsync("testStore", "test", metadata: metadata));
// Get Request & Validate // Get Request & Validate
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.GetStateRequest>(); var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.GetStateRequest>();
envelope.StoreName.Should().Be("testStore"); envelope.StoreName.ShouldBe("testStore");
envelope.Key.Should().Be("test"); envelope.Key.ShouldBe("test");
envelope.Metadata.Should().BeEquivalentTo(metadata); envelope.Metadata.ShouldBe(metadata);
var binaryData = Encoding.ASCII.GetBytes("test data"); var binaryData = Encoding.ASCII.GetBytes("test data");
// Create Response & Respond // Create Response & Respond
var (state, etag) = await request.CompleteWithMessageAsync((MakeGetByteStateResponse(binaryData.AsMemory()))); var (state, etag) = await request.CompleteWithMessageAsync((MakeGetByteStateResponse(binaryData.AsMemory())));
var stateStr = ByteString.CopyFrom(state.Span).ToByteArray(); var stateStr = ByteString.CopyFrom(state.Span).ToByteArray();
// Get response and validate // Get response and validate
stateStr.Should().BeEquivalentTo(binaryData); stateStr.ShouldBe(binaryData);
} }
[Fact] [Fact]
@ -1442,8 +1438,8 @@ namespace Dapr.Client.Test
var (state, etag) = await request.CompleteWithMessageAsync(envelope); var (state, etag) = await request.CompleteWithMessageAsync(envelope);
var stateStr = ByteString.CopyFrom(state.Span).ToByteArray(); var stateStr = ByteString.CopyFrom(state.Span).ToByteArray();
// Get response and validate // Get response and validate
stateStr.Should().BeEquivalentTo(binaryData); stateStr.ShouldBeEquivalentTo(binaryData);
etag.Should().Be("Test_Etag"); etag.ShouldBe("Test_Etag");
} }
[Fact] [Fact]

View File

@ -14,7 +14,7 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using Autogenerated = Dapr.Client.Autogen.Grpc.v1; using Autogenerated = Dapr.Client.Autogen.Grpc.v1;
using Xunit; using Xunit;
using FluentAssertions; using Shouldly;
using System; using System;
namespace Dapr.Client.Test namespace Dapr.Client.Test
@ -37,10 +37,10 @@ namespace Dapr.Client.Test
// Get Request and validate // Get Request and validate
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.TryLockRequest>(); var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.TryLockRequest>();
envelope.StoreName.Should().Be("redis"); envelope.StoreName.ShouldBe("redis");
envelope.ResourceId.Should().Be("resourceId"); envelope.ResourceId.ShouldBe("resourceId");
envelope.LockOwner.Should().Be("owner1"); envelope.LockOwner.ShouldBe("owner1");
envelope.ExpiryInSeconds.Should().Be(1000); envelope.ExpiryInSeconds.ShouldBe(1000);
// Get response and validate // Get response and validate
var invokeResponse = new Autogenerated.TryLockResponse{ var invokeResponse = new Autogenerated.TryLockResponse{
@ -56,16 +56,16 @@ namespace Dapr.Client.Test
return await daprClient.Unlock(storeName, resourceId, lockOwner); return await daprClient.Unlock(storeName, resourceId, lockOwner);
}); });
var unlockEnvelope = await unlockRequest.GetRequestEnvelopeAsync<Autogenerated.UnlockRequest>(); var unlockEnvelope = await unlockRequest.GetRequestEnvelopeAsync<Autogenerated.UnlockRequest>();
unlockEnvelope.StoreName.Should().Be("redis"); unlockEnvelope.StoreName.ShouldBe("redis");
unlockEnvelope.ResourceId.Should().Be("resourceId"); unlockEnvelope.ResourceId.ShouldBe("resourceId");
unlockEnvelope.LockOwner.Should().Be("owner1"); unlockEnvelope.LockOwner.ShouldBe("owner1");
var invokeUnlockResponse = new Autogenerated.UnlockResponse{ var invokeUnlockResponse = new Autogenerated.UnlockResponse{
Status = Autogenerated.UnlockResponse.Types.Status.LockDoesNotExist Status = Autogenerated.UnlockResponse.Types.Status.LockDoesNotExist
}; };
var domainUnlockResponse = await unlockRequest.CompleteWithMessageAsync(invokeUnlockResponse); var domainUnlockResponse = await unlockRequest.CompleteWithMessageAsync(invokeUnlockResponse);
domainUnlockResponse.status.Should().Be(LockStatus.LockDoesNotExist); domainUnlockResponse.status.ShouldBe(LockStatus.LockDoesNotExist);
} }
} }
} }

View File

@ -14,7 +14,7 @@
namespace Dapr.Client.Test namespace Dapr.Client.Test
{ {
using System.Text.Json; using System.Text.Json;
using FluentAssertions; using Shouldly;
using Xunit; using Xunit;
public class TypeConvertersTest public class TypeConvertersTest
@ -32,9 +32,9 @@ namespace Dapr.Client.Test
var any = TypeConverters.ToJsonAny(response, options); var any = TypeConverters.ToJsonAny(response, options);
var type = TypeConverters.FromJsonAny<Response>(any, options); var type = TypeConverters.FromJsonAny<Response>(any, options);
type.Should().BeEquivalentTo(response); type.ShouldBeEquivalentTo(response);
any.TypeUrl.Should().Be(string.Empty); any.TypeUrl.ShouldBe(string.Empty);
type.Name.Should().Be("test"); type.Name.ShouldBe("test");
} }
private class Response private class Response

View File

@ -5,7 +5,6 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="FluentAssertions"/>
<PackageReference Include="Microsoft.Extensions.Configuration" /> <PackageReference Include="Microsoft.Extensions.Configuration" />
<PackageReference Include="Microsoft.NET.Test.Sdk" /> <PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="xunit" /> <PackageReference Include="xunit" />

View File

@ -97,7 +97,7 @@ namespace Dapr.E2E.Test
/// <see cref="ISerializationActor"/>. (it's defined in the base of it.) /// <see cref="ISerializationActor"/>. (it's defined in the base of it.)
/// That why <see cref="ISerializationActor.AnotherMethod(DateTime)"/> was created, /// That why <see cref="ISerializationActor.AnotherMethod(DateTime)"/> was created,
/// so there are now more then one method. /// so there are now more then one method.
/// </remark> /// </remarks>
[Fact] [Fact]
public async Task ActorCanSupportCustomSerializerAndCallMoreThenOneDefinedMethod() public async Task ActorCanSupportCustomSerializerAndCallMoreThenOneDefinedMethod()
{ {

View File

@ -17,7 +17,7 @@ namespace Dapr.E2E.Test
using System.Threading.Tasks; using System.Threading.Tasks;
using Dapr.Actors; using Dapr.Actors;
using Dapr.E2E.Test.Actors.WeaklyTypedTesting; using Dapr.E2E.Test.Actors.WeaklyTypedTesting;
using FluentAssertions; using Shouldly;
using Xunit; using Xunit;
public partial class E2ETests : IAsyncLifetime public partial class E2ETests : IAsyncLifetime
@ -34,7 +34,7 @@ namespace Dapr.E2E.Test
var result = await proxy.InvokeMethodAsync<ResponseBase>(nameof(IWeaklyTypedTestingActor.GetPolymorphicResponse)); var result = await proxy.InvokeMethodAsync<ResponseBase>(nameof(IWeaklyTypedTestingActor.GetPolymorphicResponse));
result.Should().BeOfType<DerivedResponse>().Which.DerivedProperty.Should().NotBeNullOrWhiteSpace(); result.ShouldBeOfType<DerivedResponse>().DerivedProperty.ShouldNotBeNullOrWhiteSpace();
} }
#else #else
[Fact] [Fact]
@ -48,7 +48,7 @@ namespace Dapr.E2E.Test
var result = await proxy.InvokeMethodAsync<DerivedResponse>(nameof(IWeaklyTypedTestingActor.GetPolymorphicResponse)); var result = await proxy.InvokeMethodAsync<DerivedResponse>(nameof(IWeaklyTypedTestingActor.GetPolymorphicResponse));
result.Should().BeOfType<DerivedResponse>().Which.DerivedProperty.Should().NotBeNullOrWhiteSpace(); result.ShouldBeOfType<DerivedResponse>().DerivedProperty.ShouldNotBeNullOrWhiteSpace();
} }
#endif #endif
[Fact] [Fact]
@ -62,7 +62,7 @@ namespace Dapr.E2E.Test
var result = await proxy.InvokeMethodAsync<ResponseBase>(nameof(IWeaklyTypedTestingActor.GetNullResponse)); var result = await proxy.InvokeMethodAsync<ResponseBase>(nameof(IWeaklyTypedTestingActor.GetNullResponse));
result.Should().BeNull(); result.ShouldBeNull();
} }
} }
} }

View File

@ -1,10 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<ItemGroup> <ItemGroup>
<PackageReference Include="FluentAssertions" />
<PackageReference Include="Google.Protobuf" /> <PackageReference Include="Google.Protobuf" />
<PackageReference Include="Grpc.Net.ClientFactory" /> <PackageReference Include="Grpc.Net.ClientFactory" />
<PackageReference Include="Grpc.Tools" PrivateAssets="All" /> <PackageReference Include="Grpc.Tools" PrivateAssets="All" />
<PackageReference Include="Microsoft.NET.Test.Sdk" /> <PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="Shouldly"/>
<PackageReference Include="xunit" /> <PackageReference Include="xunit" />
<PackageReference Include="xunit.runner.visualstudio"> <PackageReference Include="xunit.runner.visualstudio">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>

View File

@ -45,7 +45,7 @@ namespace Dapr.E2E.Test
{ {
var (appPort, httpPort, grpcPort, metricsPort) = GetFreePorts(); 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 configPath = Combine(".", "..", "..", "..", "..", "..", "test", "Dapr.E2E.Test", "configuration", "featureconfig.yaml");
var arguments = new List<string>() var arguments = new List<string>()
{ {
@ -55,11 +55,10 @@ namespace Dapr.E2E.Test
"--dapr-http-port", httpPort.ToString(CultureInfo.InvariantCulture), "--dapr-http-port", httpPort.ToString(CultureInfo.InvariantCulture),
"--dapr-grpc-port", grpcPort.ToString(CultureInfo.InvariantCulture), "--dapr-grpc-port", grpcPort.ToString(CultureInfo.InvariantCulture),
"--metrics-port", metricsPort.ToString(CultureInfo.InvariantCulture), "--metrics-port", metricsPort.ToString(CultureInfo.InvariantCulture),
"--components-path", componentsPath, "--resources-path", resourcesPath,
"--config", configPath, "--config", configPath,
"--log-level", "debug", "--log-level", "debug",
"--dapr-http-max-request-size", "32", "--max-body-size", "8Mi"
}; };
if (configuration.UseAppPort) if (configuration.UseAppPort)

View File

@ -5,9 +5,9 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="FluentAssertions" />
<PackageReference Include="Microsoft.NET.Test.Sdk" /> <PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="Moq" /> <PackageReference Include="Moq" />
<PackageReference Include="Shouldly" />
<PackageReference Include="xunit"/> <PackageReference Include="xunit"/>
<PackageReference Include="xunit.runner.visualstudio"> <PackageReference Include="xunit.runner.visualstudio">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>

View File

@ -16,11 +16,10 @@ using System.Collections.Generic;
using System.Net; using System.Net;
using System.Threading.Tasks; using System.Threading.Tasks;
using Dapr.Client; using Dapr.Client;
using Dapr;
using FluentAssertions;
using Grpc.Net.Client; using Grpc.Net.Client;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Moq; using Moq;
using Shouldly;
using Xunit; using Xunit;
using Autogenerated = Dapr.Client.Autogen.Grpc.v1; using Autogenerated = Dapr.Client.Autogen.Grpc.v1;
@ -163,7 +162,7 @@ namespace Dapr.Extensions.Configuration.Test
.AddDaprSecretStore(storeName, secretDescriptors, daprClient.Object) .AddDaprSecretStore(storeName, secretDescriptors, daprClient.Object)
.Build(); .Build();
config["secretName"].Should().Be("secret"); config["secretName"].ShouldBe("secret");
} }
[Fact] [Fact]
@ -195,8 +194,8 @@ namespace Dapr.Extensions.Configuration.Test
.AddDaprSecretStore(storeName, secretDescriptors, daprClient.Object) .AddDaprSecretStore(storeName, secretDescriptors, daprClient.Object)
.Build(); .Build();
config[firstSecretKey].Should().Be(firstSecretValue); config[firstSecretKey].ShouldBe(firstSecretValue);
config[secondSecretKey].Should().Be(secondSecretValue); config[secondSecretKey].ShouldBe(secondSecretValue);
} }
[Fact] [Fact]
@ -224,8 +223,8 @@ namespace Dapr.Extensions.Configuration.Test
.AddDaprSecretStore(storeName, secretDescriptors, daprClient.Object) .AddDaprSecretStore(storeName, secretDescriptors, daprClient.Object)
.Build(); .Build();
config[firstSecretKey].Should().Be(firstSecretValue); config[firstSecretKey].ShouldBe(firstSecretValue);
config[secondSecretKey].Should().Be(secondSecretValue); config[secondSecretKey].ShouldBe(secondSecretValue);
} }
[Fact] [Fact]
@ -252,7 +251,7 @@ namespace Dapr.Extensions.Configuration.Test
.AddDaprSecretStore(storeName, secretDescriptors, daprClient.Object) .AddDaprSecretStore(storeName, secretDescriptors, daprClient.Object)
.Build(); .Build();
config[secretName].Should().Be(secretValue); config[secretName].ShouldBe(secretValue);
} }
[Fact] [Fact]
@ -282,7 +281,7 @@ namespace Dapr.Extensions.Configuration.Test
.AddDaprSecretStore(storeName, secretDescriptors, daprClient) .AddDaprSecretStore(storeName, secretDescriptors, daprClient)
.Build(); .Build();
config[secretName].Should().BeNull(); config[secretName].ShouldBeNull();
} }
[Fact] [Fact]
@ -415,7 +414,7 @@ namespace Dapr.Extensions.Configuration.Test
.AddDaprSecretStore("store", daprClient) .AddDaprSecretStore("store", daprClient)
.Build(); .Build();
config["secretName"].Should().Be("secret"); config["secretName"].ShouldBe("secret");
} }
[Fact] [Fact]
@ -442,8 +441,8 @@ namespace Dapr.Extensions.Configuration.Test
.AddDaprSecretStore("store", daprClient) .AddDaprSecretStore("store", daprClient)
.Build(); .Build();
config["first_secret"].Should().Be("secret1"); config["first_secret"].ShouldBe("secret1");
config["second_secret"].Should().Be("secret2"); config["second_secret"].ShouldBe("secret2");
} }
[Fact] [Fact]
@ -468,7 +467,7 @@ namespace Dapr.Extensions.Configuration.Test
.AddDaprSecretStore("store", new DaprSecretDescriptor[] { new DaprSecretDescriptor("secretName__value") }, daprClient) .AddDaprSecretStore("store", new DaprSecretDescriptor[] { new DaprSecretDescriptor("secretName__value") }, daprClient)
.Build(); .Build();
config["secretName:value"].Should().Be("secret"); config["secretName:value"].ShouldBe("secret");
} }
[Fact] [Fact]
@ -494,7 +493,7 @@ namespace Dapr.Extensions.Configuration.Test
.AddDaprSecretStore("store", daprClient) .AddDaprSecretStore("store", daprClient)
.Build(); .Build();
config["first_secret:value"].Should().Be("secret1"); config["first_secret:value"].ShouldBe("secret1");
} }
[Fact] [Fact]
@ -525,7 +524,7 @@ namespace Dapr.Extensions.Configuration.Test
}) })
.Build(); .Build();
config["secretName__value"].Should().Be("secret"); config["secretName__value"].ShouldBe("secret");
} }
[Fact] [Fact]
@ -556,7 +555,7 @@ namespace Dapr.Extensions.Configuration.Test
}) })
.Build(); .Build();
config["first_secret__value"].Should().Be("secret1"); config["first_secret__value"].ShouldBe("secret1");
} }
[Fact] [Fact]
@ -590,7 +589,7 @@ namespace Dapr.Extensions.Configuration.Test
}) })
.Build(); .Build();
config["secretName:value"].Should().Be("secret"); config["secretName:value"].ShouldBe("secret");
} }
[Fact] [Fact]
@ -624,7 +623,7 @@ namespace Dapr.Extensions.Configuration.Test
}) })
.Build(); .Build();
config["secretName--value"].Should().Be("secret"); config["secretName--value"].ShouldBe("secret");
} }
[Fact] [Fact]
@ -658,7 +657,7 @@ namespace Dapr.Extensions.Configuration.Test
}) })
.Build(); .Build();
config["secretName--value"].Should().Be("secret"); config["secretName--value"].ShouldBe("secret");
} }
[Fact] [Fact]
@ -714,8 +713,8 @@ namespace Dapr.Extensions.Configuration.Test
}) })
.Build(); .Build();
config["secretName:value"].Should().Be("secret"); config["secretName:value"].ShouldBe("secret");
config["otherSecretName:value"].Should().Be("secret"); config["otherSecretName:value"].ShouldBe("secret");
} }
[Fact] [Fact]
@ -743,7 +742,7 @@ namespace Dapr.Extensions.Configuration.Test
.AddDaprSecretStore("store", daprClient, new[] { "--" }) .AddDaprSecretStore("store", daprClient, new[] { "--" })
.Build(); .Build();
config["first_secret:value"].Should().Be("secret1"); config["first_secret:value"].ShouldBe("secret1");
} }
[Fact] [Fact]
@ -772,8 +771,8 @@ namespace Dapr.Extensions.Configuration.Test
.AddDaprSecretStore("store", daprClient, new[] { "--", "≡" }) .AddDaprSecretStore("store", daprClient, new[] { "--", "≡" })
.Build(); .Build();
config["first_secret:value"].Should().Be("secret1"); config["first_secret:value"].ShouldBe("secret1");
config["second_secret:value"].Should().Be("secret2"); config["second_secret:value"].ShouldBe("secret2");
} }
[Fact] [Fact]
@ -803,9 +802,9 @@ namespace Dapr.Extensions.Configuration.Test
.AddDaprSecretStore("store", daprClient, new[] { "--", "≡" }) .AddDaprSecretStore("store", daprClient, new[] { "--", "≡" })
.Build(); .Build();
config["first_secret:value"].Should().Be("secret1"); config["first_secret:value"].ShouldBe("secret1");
config["second_secret:value"].Should().Be("secret2"); config["second_secret:value"].ShouldBe("secret2");
config["third_secret:value"].Should().Be("secret3"); config["third_secret:value"].ShouldBe("secret3");
} }
[Fact] [Fact]
@ -840,7 +839,7 @@ namespace Dapr.Extensions.Configuration.Test
}) })
.Build(); .Build();
config["secretName--value"].Should().Be("secret"); config["secretName--value"].ShouldBe("secret");
} }
[Fact] [Fact]
@ -874,7 +873,7 @@ namespace Dapr.Extensions.Configuration.Test
}) })
.Build(); .Build();
config["first_secret--value"].Should().Be("secret1"); config["first_secret--value"].ShouldBe("secret1");
} }
[Fact] [Fact]

View File

@ -14,6 +14,9 @@
#nullable enable #nullable enable
using System; using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Text; using System.Text;
using System.Text.Json; using System.Text.Json;
@ -25,8 +28,11 @@ using Dapr.Jobs.Models.Responses;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.TestHost; using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Moq;
using Xunit; using Xunit;
namespace Dapr.Jobs.Test.Extensions; namespace Dapr.Jobs.Test.Extensions;
@ -40,15 +46,7 @@ public class EndpointRouteBuilderExtensionsTest
var client = server.CreateClient(); var client = server.CreateClient();
var serializedPayload = JsonSerializer.Serialize(new SamplePayload("Dapr", 789)); var serializedPayload = JsonSerializer.Serialize(new SamplePayload("Dapr", 789));
var serializedPayloadBytes = Encoding.UTF8.GetBytes(serializedPayload); var content = new StringContent(serializedPayload, Encoding.UTF8, "application/json");
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");
const string jobName = "testJob"; const string jobName = "testJob";
var response = await client.PostAsync($"/job/{jobName}", content); var response = await client.PostAsync($"/job/{jobName}", content);
@ -68,15 +66,7 @@ public class EndpointRouteBuilderExtensionsTest
var client = server.CreateClient(); var client = server.CreateClient();
var serializedPayload = JsonSerializer.Serialize(new SamplePayload("Dapr", 789)); var serializedPayload = JsonSerializer.Serialize(new SamplePayload("Dapr", 789));
var serializedPayloadBytes = Encoding.UTF8.GetBytes(serializedPayload); var content = new StringContent(serializedPayload, Encoding.UTF8, "application/json");
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");
const string jobName = "testJob"; const string jobName = "testJob";
var response = await client.PostAsync($"/job/{jobName}", content); var response = await client.PostAsync($"/job/{jobName}", content);
@ -89,23 +79,109 @@ public class EndpointRouteBuilderExtensionsTest
Assert.Equal(serializedPayload, validator.SerializedPayload); Assert.Equal(serializedPayload, validator.SerializedPayload);
} }
[Fact] [Fact]
public async Task MapDaprScheduledJobHandler_InvalidPayload() public async Task MapDaprScheduledJobHandler_HandlesTimeoutCorrectly()
{ {
// Arrange // Arrange
var server = CreateTestServer(); var timeout = TimeSpan.FromSeconds(5);
var client = server.CreateClient(); 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<byte> jobPayload,
ILogger? logger,
CancellationToken cancellationToken) =>
{
logger?.LogInformation("Received trigger invocation for job '{jobName}'", jobName);
// Act var deserializedPayload = Encoding.UTF8.GetString(jobPayload.Span);
const string jobName = "testJob"; logger?.LogInformation(
var response = await client.PostAsync($"/job/{jobName}", content); "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<Validator>(); return Task.CompletedTask;
Assert.Equal(jobName, validator.JobName); }, timeout);
Assert.Null(validator.SerializedPayload); });
});
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<byte> 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<TaskCanceledException>(async () => await client.SendAsync(request));
} }
private sealed record SamplePayload(string Name, int Count); private sealed record SamplePayload(string Name, int Count);
@ -113,7 +189,6 @@ public class EndpointRouteBuilderExtensionsTest
public sealed class Validator public sealed class Validator
{ {
public string? JobName { get; set; } public string? JobName { get; set; }
public string? SerializedPayload { get; set; } public string? SerializedPayload { get; set; }
} }
@ -130,15 +205,10 @@ public class EndpointRouteBuilderExtensionsTest
app.UseRouting(); app.UseRouting();
app.UseEndpoints(endpoints => app.UseEndpoints(endpoints =>
{ {
endpoints.MapDaprScheduledJobHandler(async (string? jobName, DaprJobDetails? jobDetails, Validator validator, CancellationToken cancellationToken) => endpoints.MapDaprScheduledJobHandler(async (string jobName, ReadOnlyMemory<byte> jobPayload, Validator validator, CancellationToken cancellationToken) =>
{ {
if (jobName is not null)
validator.JobName = jobName; validator.JobName = jobName;
if (jobDetails?.Payload is not null) validator.SerializedPayload = Encoding.UTF8.GetString(jobPayload.Span);
{
var payloadString = Encoding.UTF8.GetString(jobDetails.Payload);
validator.SerializedPayload = payloadString;
}
await Task.CompletedTask; await Task.CompletedTask;
}); });
}); });
@ -160,15 +230,12 @@ public class EndpointRouteBuilderExtensionsTest
app.UseRouting(); app.UseRouting();
app.UseEndpoints(endpoints => app.UseEndpoints(endpoints =>
{ {
endpoints.MapDaprScheduledJobHandler(async (string? jobName, Validator validator, DaprJobDetails? jobDetails) => endpoints.MapDaprScheduledJobHandler(async (string jobName, Validator validator, ReadOnlyMemory<byte> payload) =>
{ {
if (jobName is not null)
validator.JobName = jobName; validator.JobName = jobName;
if (jobDetails?.Payload is not null)
{ var payloadString = Encoding.UTF8.GetString(payload.Span);
var payloadString = Encoding.UTF8.GetString(jobDetails.Payload);
validator.SerializedPayload = payloadString; validator.SerializedPayload = payloadString;
}
await Task.CompletedTask; await Task.CompletedTask;
}); });
}); });

View File

@ -23,7 +23,16 @@ public sealed class DaprJobScheduleTests
public void FromDuration_Validate() public void FromDuration_Validate()
{ {
var schedule = DaprJobSchedule.FromDuration(new TimeSpan(12, 8, 16)); 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] [Fact]
@ -108,7 +117,7 @@ public sealed class DaprJobScheduleTests
Assert.True(schedule.IsDurationExpression); Assert.True(schedule.IsDurationExpression);
Assert.False(schedule.IsPointInTimeExpression); Assert.False(schedule.IsPointInTimeExpression);
Assert.False(schedule.IsCronExpression); Assert.False(schedule.IsCronExpression);
Assert.False(schedule.IsPrefixedPeriodExpression); Assert.True(schedule.IsPrefixedPeriodExpression); //A duration expression _is_ a prefixed period with @every
} }
[Fact] [Fact]

View File

@ -15,7 +15,6 @@ using Dapr.Messaging.PublishSubscribe;
using Dapr.Messaging.PublishSubscribe.Extensions; using Dapr.Messaging.PublishSubscribe.Extensions;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Moq;
namespace Dapr.Messaging.Test.Extensions; namespace Dapr.Messaging.Test.Extensions;