mirror of https://github.com/dapr/dotnet-sdk.git
Compare commits
22 Commits
v1.15.0-rc
...
master
Author | SHA1 | Date |
---|---|---|
|
6b49bed7f1 | |
|
faeeb8eaca | |
|
0873c5ef6f | |
|
32d06a7136 | |
|
6f07643280 | |
|
55895fa19d | |
|
c14fcea0d4 | |
|
bb47132f98 | |
|
cb065f2089 | |
|
19fd40390b | |
|
94dcdfd5b2 | |
|
c94b61e0d6 | |
|
52f0851780 | |
|
2b288a1135 | |
|
0dc268f501 | |
|
e9ee4d21bf | |
|
89d9d56bd5 | |
|
ab3ef305f2 | |
|
9e0672525e | |
|
676c0d7a7f | |
|
ef54d75f70 | |
|
01b4833474 |
|
@ -48,8 +48,8 @@ jobs:
|
|||
GOOS: linux
|
||||
GOARCH: amd64
|
||||
GOPROXY: https://proxy.golang.org
|
||||
DAPR_CLI_VER: 1.14.0
|
||||
DAPR_RUNTIME_VER: 1.14.0
|
||||
DAPR_CLI_VER: 1.15.0
|
||||
DAPR_RUNTIME_VER: 1.15.3
|
||||
DAPR_INSTALL_URL: https://raw.githubusercontent.com/dapr/cli/release-1.14/install/install.sh
|
||||
DAPR_CLI_REF: ''
|
||||
steps:
|
||||
|
|
|
@ -7,15 +7,14 @@
|
|||
<PackageVersion Include="BenchmarkDotNet" Version="0.14.0" />
|
||||
<PackageVersion Include="coverlet.collector" 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="Google.Api.CommonProtos" Version="2.2.0" />
|
||||
<PackageVersion Include="Google.Protobuf" Version="3.28.2" />
|
||||
<PackageVersion Include="Grpc.AspNetCore" Version="2.66.0" />
|
||||
<PackageVersion Include="Google.Protobuf" Version="3.30.2" />
|
||||
<PackageVersion Include="Grpc.AspNetCore" Version="2.71.0" />
|
||||
<PackageVersion Include="Grpc.Core.Testing" Version="2.46.6" />
|
||||
<PackageVersion Include="Grpc.Net.Client" Version="2.66.0" />
|
||||
<PackageVersion Include="Grpc.Net.ClientFactory" Version="2.66.0" />
|
||||
<PackageVersion Include="Grpc.Tools" Version="2.67.0" />
|
||||
<PackageVersion Include="Grpc.Net.Client" Version="2.71.0" />
|
||||
<PackageVersion Include="Grpc.Net.ClientFactory" Version="2.71.0" />
|
||||
<PackageVersion Include="Grpc.Tools" Version="2.71.0" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="6.0.35" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.TestHost" Version="6.0.35" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
|
||||
|
@ -24,8 +23,8 @@
|
|||
<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.Workspaces" Version="4.8.0" />
|
||||
<PackageVersion Include="Microsoft.DurableTask.Client.Grpc" Version="1.3.0" />
|
||||
<PackageVersion Include="Microsoft.DurableTask.Worker.Grpc" Version="1.3.0" />
|
||||
<PackageVersion Include="Microsoft.DurableTask.Client.Grpc" Version="1.10.0" />
|
||||
<PackageVersion Include="Microsoft.DurableTask.Worker.Grpc" Version="1.10.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="6.0.1" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" />
|
||||
|
@ -43,10 +42,11 @@
|
|||
<PackageVersion Include="Serilog.AspNetCore" Version="6.1.0" />
|
||||
<PackageVersion Include="Serilog.Sinks.Console" Version="4.1.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.Text.Json" Version="6.0.10" />
|
||||
<PackageVersion Include="xunit" Version="2.9.2" />
|
||||
<PackageVersion Include="xunit.extensibility.core" Version="2.9.2" />
|
||||
<PackageVersion Include="xunit.runner.visualstudio" Version="2.8.2" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
|
@ -282,26 +282,6 @@ namespace LockService
|
|||
}
|
||||
```
|
||||
|
||||
### Manage workflow instances (Alpha)
|
||||
|
||||
```csharp
|
||||
var daprClient = new DaprClientBuilder().Build();
|
||||
|
||||
string instanceId = "MyWorkflowInstance1";
|
||||
string workflowComponentName = "dapr"; // alternatively, this could be the name of a workflow component defined in yaml
|
||||
string workflowName = "MyWorkflowDefinition";
|
||||
var input = new { name = "Billy", age = 30 }; // Any JSON-serializable value is OK
|
||||
|
||||
// Start workflow
|
||||
var startResponse = await daprClient.StartWorkflowAsync(instanceId, workflowComponentName, workflowName, input);
|
||||
|
||||
// Terminate workflow
|
||||
await daprClient.TerminateWorkflowAsync(instanceId, workflowComponentName);
|
||||
|
||||
// Get workflow metadata
|
||||
var getResponse = await daprClient.GetWorkflowAsync(instanceId, workflowComponentName, workflowName);
|
||||
```
|
||||
|
||||
## Sidecar APIs
|
||||
### Sidecar Health
|
||||
The .NET SDK provides a way to poll for the sidecar health, as well as a convenience method to wait for the sidecar to be ready.
|
||||
|
|
|
@ -10,4 +10,4 @@ With the Dapr Job package, you can interact with the Dapr Job APIs from a .NET a
|
|||
to run according to a predefined schedule with an optional payload.
|
||||
|
||||
To get started, walk through the [Dapr Jobs]({{< ref dotnet-jobs-howto.md >}}) how-to guide and refer to
|
||||
[best practices documentation]({{< ref dotnet-jobs-usage.md >}}) for additional guidance.
|
||||
[best practices documentation]({{< ref dotnet-jobsclient-usage.md >}}) for additional guidance.
|
||||
|
|
|
@ -63,8 +63,8 @@ the dependency injection registration in `Program.cs`, add the following line:
|
|||
```cs
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
//Add anywhere between these two
|
||||
builder.Services.AddDaprJobsClient(); //That's it
|
||||
//Add anywhere between these two lines
|
||||
builder.Services.AddDaprJobsClient();
|
||||
|
||||
var app = builder.Build();
|
||||
```
|
||||
|
@ -203,7 +203,8 @@ public class MySampleClass
|
|||
It's easy to set up a jobs endpoint if you're at all familiar with [minimal APIs in ASP.NET Core](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis/overview) as the syntax is the same between the two.
|
||||
|
||||
Once dependency injection registration has been completed, configure the application the same way you would to handle mapping an HTTP request via the minimal API functionality in ASP.NET Core. Implemented as an extension method,
|
||||
pass the name of the job it should be responsive to and a delegate. Services can be injected into the delegate's arguments as you wish and you can optionally pass a `JobDetails` to get information about the job that has been triggered (e.g. access its scheduling setup or payload).
|
||||
pass the name of the job it should be responsive to and a delegate. Services can be injected into the delegate's arguments as you wish and the job payload can be accessed from the `ReadOnlyMemory<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:
|
||||
|
||||
|
@ -216,7 +217,7 @@ builder.Services.AddDaprJobsClient();
|
|||
var app = builder.Build();
|
||||
|
||||
//Add our endpoint registration
|
||||
app.MapDaprScheduledJob("myJob", (IServiceProvider serviceProvider, string? jobName, JobDetails? jobDetails) => {
|
||||
app.MapDaprScheduledJob("myJob", (IServiceProvider serviceProvider, string jobName, ReadOnlyMemory<byte> jobPayload) => {
|
||||
var logger = serviceProvider.GetService<ILogger>();
|
||||
logger?.LogInformation("Received trigger invocation for '{jobName}'", "myJob");
|
||||
|
||||
|
@ -237,13 +238,34 @@ builder.Services.AddDaprJobsClient();
|
|||
var app = builder.Build();
|
||||
|
||||
//Add our endpoint registration
|
||||
app.MapDaprScheduledJob("myJob", (string? jobName, JobDetails? jobDetails) => {
|
||||
app.MapDaprScheduledJob("myJob", (string jobName, ReadOnlyMemory<byte> jobPayload) => {
|
||||
//Do something...
|
||||
});
|
||||
|
||||
app.Run();
|
||||
```
|
||||
|
||||
## Support cancellation tokens when processing mapped invocations
|
||||
You may want to ensure that timeouts are handled on job invocations so that they don't indefinitely hang and use system resources. When setting up the job mapping, there's an optional `TimeSpan` parameter that can be
|
||||
provided as the last argument to specify a timeout for the request. Every time the job mapping invocation is triggered, a new `CancellationTokenSource` will be created using this timeout parameter and a `CancellationToken`
|
||||
will be created from it to put an upper bound on the processing of the request. If a timeout isn't provided, this defaults to `CancellationToken.None` and a timeout will not be automatically applied to the mapping.
|
||||
|
||||
```cs
|
||||
//We have this from the example above
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
builder.Services.AddDaprJobsClient();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
//Add our endpoint registration
|
||||
app.MapDaprScheduledJob("myJob", (string jobName, ReadOnlyMemory<byte> jobPayload) => {
|
||||
//Do something...
|
||||
}, TimeSpan.FromSeconds(15)); //Assigns a maximum timeout of 15 seconds for handling the invocation request
|
||||
|
||||
app.Run();
|
||||
```
|
||||
|
||||
## Register the job
|
||||
|
||||
Finally, we have to register the job we want scheduled. Note that from here, all SDK methods have cancellation token support and use a default token if not otherwise set.
|
||||
|
|
|
@ -165,31 +165,18 @@ var oneWeekFromNow = now.AddDays(7);
|
|||
await daprJobsClient.ScheduleOneTimeJobWithPayloadAsync("myOtherJob", oneWeekFromNow, "This is a test!");
|
||||
```
|
||||
|
||||
The `JobDetails` type returns the data as a `ReadOnlyMemory<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
|
||||
type or a string. Both methods assume that the developer encoded the originally scheduled job (perhaps using the
|
||||
helper serialization methods) as these methods will not force the bytes to represent something they're not.
|
||||
|
||||
To deserialize the bytes to a string, the following helper method can be used:
|
||||
```cs
|
||||
if (jobDetails.Payload is not null)
|
||||
{
|
||||
string payloadAsString = jobDetails.Payload.DeserializeToString(); //If successful, returns a string value with the value
|
||||
}
|
||||
```
|
||||
|
||||
To deserialize JSON-encoded UTF-8 bytes to the corresponding type, the following helper method can be used. An
|
||||
overload argument is available that permits the developer to pass in their own `JsonSerializerOptions` to be applied
|
||||
during deserialization.
|
||||
|
||||
```cs
|
||||
public sealed record Doodad (string Name, int Value);
|
||||
|
||||
//...
|
||||
if (jobDetails.Payload is not null)
|
||||
{
|
||||
var deserializedDoodad = jobDetails.Payload.DeserializeFromJsonBytes<Doodad>();
|
||||
}
|
||||
var payloadAsString = Encoding.UTF8.GetString(jobPayload.Span); //If successful, returns a string with the value
|
||||
```
|
||||
|
||||
## Error handling
|
||||
|
|
|
@ -20,7 +20,7 @@ To load secrets into configuration call the _AddDaprSecretStore_ extension metho
|
|||
Use Dapr to run the application:
|
||||
|
||||
```shell
|
||||
dapr run --app-id SecretStoreConfigurationProviderSample --components-path ./components/ -- dotnet run
|
||||
dapr run --app-id SecretStoreConfigurationProviderSample --resources-path ./components/ -- dotnet run
|
||||
```
|
||||
|
||||
### 2. Test the application
|
||||
|
|
|
@ -147,7 +147,7 @@ cd examples/Client/ConfigurationApi
|
|||
To run the `ConfigurationExample`, execute the following command:
|
||||
|
||||
```bash
|
||||
dapr run --app-id configexample --components-path ./Components -- dotnet run
|
||||
dapr run --app-id configexample --resources-path ./Components -- dotnet run
|
||||
```
|
||||
|
||||
### Get Configuration
|
||||
|
|
|
@ -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
|
|
@ -1,7 +0,0 @@
|
|||
apiVersion: dapr.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: envvar-secret-store
|
||||
spec:
|
||||
type: secretstores.local.env
|
||||
version: v1
|
|
@ -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
|
|
@ -17,16 +17,13 @@ using Dapr.Client;
|
|||
|
||||
namespace Cryptography.Examples
|
||||
{
|
||||
internal class EncryptDecryptFileStreamExample : Example
|
||||
internal class EncryptDecryptFileStreamExample(string componentName, string keyName) : Example
|
||||
{
|
||||
public override string DisplayName => "Use Cryptography to encrypt and decrypt a file";
|
||||
public override async Task RunAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
using var client = new DaprClientBuilder().Build();
|
||||
|
||||
const string componentName = "azurekeyvault"; // Change this to match the name of the component containing your vault
|
||||
const string keyName = "myKey";
|
||||
|
||||
// The name of the file we're using as an example
|
||||
const string fileName = "file.txt";
|
||||
|
||||
|
@ -35,21 +32,19 @@ namespace Cryptography.Examples
|
|||
{
|
||||
Console.WriteLine(line);
|
||||
}
|
||||
Console.WriteLine();
|
||||
|
||||
//Encrypt from a file stream and buffer the resulting bytes to an in-memory buffer
|
||||
await using var encryptFs = new FileStream(fileName, FileMode.Open);
|
||||
|
||||
var bufferedEncryptedBytes = new ArrayBufferWriter<byte>();
|
||||
await foreach (var bytes in (await client.EncryptAsync(componentName, encryptFs, keyName,
|
||||
new EncryptionOptions(KeyWrapAlgorithm.Rsa), cancellationToken))
|
||||
.WithCancellation(cancellationToken))
|
||||
await foreach (var bytes in (client.EncryptAsync(componentName, encryptFs, keyName,
|
||||
new EncryptionOptions(KeyWrapAlgorithm.Rsa), cancellationToken)))
|
||||
{
|
||||
bufferedEncryptedBytes.Write(bytes.Span);
|
||||
}
|
||||
|
||||
Console.WriteLine($"Encrypted bytes: {Convert.ToBase64String(bufferedEncryptedBytes.GetSpan())}");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Encrypted bytes:");
|
||||
Console.WriteLine(Convert.ToBase64String(bufferedEncryptedBytes.WrittenMemory.ToArray()));
|
||||
|
||||
//We'll write to a temporary file via a FileStream
|
||||
var tempDecryptedFile = Path.GetTempFileName();
|
||||
|
@ -57,8 +52,8 @@ namespace Cryptography.Examples
|
|||
|
||||
//We'll stream the decrypted bytes from a MemoryStream into the above temporary file
|
||||
await using var encryptedMs = new MemoryStream(bufferedEncryptedBytes.WrittenMemory.ToArray());
|
||||
await foreach (var result in (await client.DecryptAsync(componentName, encryptedMs, keyName,
|
||||
cancellationToken)).WithCancellation(cancellationToken))
|
||||
await foreach (var result in (client.DecryptAsync(componentName, encryptedMs, keyName,
|
||||
cancellationToken)))
|
||||
{
|
||||
decryptFs.Write(result.Span);
|
||||
}
|
||||
|
@ -67,7 +62,7 @@ namespace Cryptography.Examples
|
|||
|
||||
//Let's confirm the value as written to the file
|
||||
var decryptedValue = await File.ReadAllTextAsync(tempDecryptedFile, cancellationToken);
|
||||
Console.WriteLine($"Decrypted value: ");
|
||||
Console.WriteLine("Decrypted value: ");
|
||||
Console.WriteLine(decryptedValue);
|
||||
|
||||
//And some cleanup to delete our temp file
|
||||
|
|
|
@ -17,17 +17,13 @@ using Dapr.Client;
|
|||
|
||||
namespace Cryptography.Examples
|
||||
{
|
||||
internal class EncryptDecryptStringExample : Example
|
||||
internal class EncryptDecryptStringExample(string componentName, string keyName) : Example
|
||||
{
|
||||
public override string DisplayName => "Using Cryptography to encrypt and decrypt a string";
|
||||
|
||||
public override async Task RunAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
using var client = new DaprClientBuilder().Build();
|
||||
|
||||
const string componentName = "azurekeyvault"; //Change this to match the name of the component containing your vault
|
||||
const string keyName = "myKey"; //Change this to match the name of the key in your Vault
|
||||
|
||||
|
||||
const string plaintextStr = "This is the value we're going to encrypt today";
|
||||
Console.WriteLine($"Original string value: '{plaintextStr}'");
|
||||
|
@ -40,7 +36,7 @@ namespace Cryptography.Examples
|
|||
Console.WriteLine($"Encrypted bytes: '{Convert.ToBase64String(encryptedBytesResult.Span)}'");
|
||||
|
||||
//Decrypt the string
|
||||
var decryptedBytes = await client.DecryptAsync(componentName, encryptedBytesResult, keyName, new DecryptionOptions(), cancellationToken);
|
||||
var decryptedBytes = await client.DecryptAsync(componentName, encryptedBytesResult, keyName, cancellationToken);
|
||||
Console.WriteLine($"Decrypted string: '{Encoding.UTF8.GetString(decryptedBytes.ToArray())}'");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,10 +17,13 @@ namespace Cryptography
|
|||
{
|
||||
class Program
|
||||
{
|
||||
private const string ComponentName = "localstorage";
|
||||
private const string KeyName = "rsa-private-key.pem"; //This should match the name of your generated key - this sample expects an RSA symmetrical key.
|
||||
|
||||
private static readonly Example[] Examples = new Example[]
|
||||
{
|
||||
new EncryptDecryptStringExample(),
|
||||
new EncryptDecryptFileStreamExample()
|
||||
new EncryptDecryptStringExample(ComponentName, KeyName),
|
||||
new EncryptDecryptFileStreamExample(ComponentName, KeyName)
|
||||
};
|
||||
|
||||
static async Task<int> Main(string[] args)
|
||||
|
@ -34,7 +37,7 @@ namespace Cryptography
|
|||
return 0;
|
||||
}
|
||||
|
||||
Console.WriteLine("Hello, please choose a sample to run:");
|
||||
Console.WriteLine("Hello, please choose a sample to run by passing your selection's number into the arguments, e.g. 'dotnet run 0':");
|
||||
for (var i = 0; i < Examples.Length; i++)
|
||||
{
|
||||
Console.WriteLine($"{i}: {Examples[i].DisplayName}");
|
||||
|
|
|
@ -50,6 +50,21 @@ button. Ensuring that the "User, group or service principal" option is selected,
|
|||
Add to add this service principal to the list of members for the new role assignment and click Review + Assign twice to assign the role. This will take effect within a few seconds
|
||||
or minutes. This step ensures that while Dapr can authenticate as your service principal, that it also has permission to access and use the key in your Key Vault.
|
||||
|
||||
## Generating the Keys
|
||||
This sample requires a private RSA key to be generated and placed in the `/keys` directory within the project.
|
||||
If you have OpenSSL installed on your machine, you can generate the key by navigating first
|
||||
into the project directory and then running the following command:
|
||||
|
||||
```bash
|
||||
# Generates a private RSA 40960-bit key named 'rsa-private-key.pem'
|
||||
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:4096 -out keys/rsa-private-key.pem
|
||||
```
|
||||
|
||||
> **WARNING: This RSA key is included in this project strictly for demonstration and testing purposes.**
|
||||
> - Do **NOT** use this key in any production environment or for any real-world applications.
|
||||
> - This key is publicly available and should be considered compromised.
|
||||
> - Generating and using your own secure keys is essential for maintaining security in your projects.
|
||||
|
||||
## Running the example
|
||||
|
||||
To run the sample locally, run this command in the DaprClient directory:
|
||||
|
|
|
@ -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-----
|
|
@ -24,7 +24,7 @@ cd examples/Client/DistributedLock
|
|||
In order to run the application that generates data for the workers to process, simply run the following command:
|
||||
|
||||
```bash
|
||||
dapr run --components-path ./Components --app-id generator -- dotnet run
|
||||
dapr run --resources-path ./Components --app-id generator -- dotnet run
|
||||
```
|
||||
|
||||
This application will create a new file to process once every 10 seconds. The files are stored in `DistributedLock/tmp`.
|
||||
|
@ -33,8 +33,8 @@ This application will create a new file to process once every 10 seconds. The fi
|
|||
In order to properly demonstrate locking, this application will be run more than once with the same App ID. However, the applications do need different ports in order to properly receive bindings. Run them with the command below:
|
||||
|
||||
```bash
|
||||
dapr run --components-path ./Components --app-id worker --app-port 5000 -- dotnet run
|
||||
dapr run --components-path ./Components --app-id worker --app-port 5001 -- dotnet run
|
||||
dapr run --resources-path ./Components --app-id worker --app-port 5000 -- dotnet run
|
||||
dapr run --resources-path ./Components --app-id worker --app-port 5001 -- dotnet run
|
||||
```
|
||||
|
||||
After running the applications, they will attempt to process files. You should see output such as:
|
||||
|
|
|
@ -16,43 +16,36 @@ using System.Text;
|
|||
using Dapr.Jobs;
|
||||
using Dapr.Jobs.Extensions;
|
||||
using Dapr.Jobs.Models;
|
||||
using Dapr.Jobs.Models.Responses;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
builder.Services.AddDaprJobsClient();
|
||||
builder.Logging.ClearProviders();
|
||||
builder.Logging.AddConsole();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
//Set a handler to deal with incoming jobs
|
||||
var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(5));
|
||||
app.MapDaprScheduledJobHandler((string? jobName, DaprJobDetails? jobDetails, ILogger? logger, CancellationToken cancellationToken) =>
|
||||
app.MapDaprScheduledJobHandler(async (string jobName, ReadOnlyMemory<byte> jobPayload, ILogger? logger, CancellationToken cancellationToken) =>
|
||||
{
|
||||
logger?.LogInformation("Received trigger invocation for job '{jobName}'", jobName);
|
||||
if (jobDetails?.Payload is not null)
|
||||
{
|
||||
var deserializedPayload = Encoding.UTF8.GetString(jobDetails.Payload);
|
||||
logger?.LogInformation("Received invocation for the job '{jobName}' with payload '{deserializedPayload}'",
|
||||
jobName, deserializedPayload);
|
||||
//Do something that needs the cancellation token
|
||||
}
|
||||
else
|
||||
{
|
||||
logger?.LogWarning("Failed to deserialize payload for job '{jobName}'", jobName);
|
||||
}
|
||||
|
||||
var deserializedPayload = Encoding.UTF8.GetString(jobPayload.Span);
|
||||
logger?.LogInformation("Received invocation for the job '{jobName}' with payload '{deserializedPayload}'",
|
||||
jobName, deserializedPayload);
|
||||
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}, cancellationTokenSource.Token);
|
||||
}, TimeSpan.FromSeconds(5));
|
||||
|
||||
app.Run();
|
||||
|
||||
await using var scope = app.Services.CreateAsyncScope();
|
||||
var logger = scope.ServiceProvider.GetRequiredService<ILogger>();
|
||||
using var scope = app.Services.CreateScope();
|
||||
var logger = scope.ServiceProvider.GetRequiredService<ILogger<Program>>();
|
||||
var daprJobsClient = scope.ServiceProvider.GetRequiredService<DaprJobsClient>();
|
||||
|
||||
logger.LogInformation("Scheduling one-time job 'myJob' to execute 10 seconds from now");
|
||||
await daprJobsClient.ScheduleJobAsync("myJob", DaprJobSchedule.FromDateTime(DateTime.UtcNow.AddSeconds(10)),
|
||||
Encoding.UTF8.GetBytes("This is a test"));
|
||||
await daprJobsClient.ScheduleJobAsync("myJob", DaprJobSchedule.FromDuration(TimeSpan.FromSeconds(2)),
|
||||
Encoding.UTF8.GetBytes("This is a test"), repeats: 10);
|
||||
logger.LogInformation("Scheduled one-time job 'myJob'");
|
||||
|
||||
app.Run();
|
||||
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
|
|
|
@ -77,7 +77,11 @@ public sealed class DaprConversationClient : DaprAIClient
|
|||
|
||||
if (options is not null)
|
||||
{
|
||||
request.ContextID = options.ConversationId;
|
||||
if (options.ConversationId is not null)
|
||||
{
|
||||
request.ContextID = options.ConversationId;
|
||||
}
|
||||
|
||||
request.ScrubPII = options.ScrubPII;
|
||||
|
||||
foreach (var (key, value) in options.Metadata)
|
||||
|
@ -96,7 +100,7 @@ public sealed class DaprConversationClient : DaprAIClient
|
|||
request.Inputs.Add(new P.ConversationInput
|
||||
{
|
||||
ScrubPII = input.ScrubPII,
|
||||
Message = input.Message,
|
||||
Content = input.Content,
|
||||
Role = input.Role.GetValueFromEnumMember()
|
||||
});
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ namespace Dapr.AI.Conversation;
|
|||
/// <summary>
|
||||
/// Represents an input for the Dapr Conversational API.
|
||||
/// </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="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);
|
||||
|
|
|
@ -265,7 +265,7 @@ namespace Dapr.Actors
|
|||
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);
|
||||
|
||||
|
@ -278,8 +278,7 @@ namespace Dapr.Actors
|
|||
return request;
|
||||
}
|
||||
|
||||
var response = await this.SendAsync(RequestFunc, relativeUrl, cancellationToken);
|
||||
return await response.Content.ReadAsStreamAsync();
|
||||
return await this.SendAsync(RequestFunc, relativeUrl, cancellationToken);
|
||||
}
|
||||
|
||||
public Task UnregisterReminderAsync(string actorType, string actorId, string reminderName, CancellationToken cancellationToken = default)
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
// ------------------------------------------------------------------------
|
||||
// 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))
|
||||
{
|
||||
return TimeSpan.FromDays(30);
|
||||
}
|
||||
|
||||
if (period.StartsWith(MidnightPrefixPeriod))
|
||||
{
|
||||
return TimeSpan.Zero;
|
||||
}
|
||||
|
||||
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}");
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
|
@ -11,6 +11,8 @@
|
|||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
using System.Net.Http;
|
||||
|
||||
namespace Dapr.Actors
|
||||
{
|
||||
using System.IO;
|
||||
|
@ -81,8 +83,8 @@ namespace Dapr.Actors
|
|||
/// <param name="actorId">ActorId.</param>
|
||||
/// <param name="reminderName">Name of reminder to unregister.</param>
|
||||
/// <param name="cancellationToken">Cancels the operation.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the result of the asynchronous operation.</returns>
|
||||
Task<Stream> GetReminderAsync(string actorType, string actorId, string reminderName, CancellationToken cancellationToken = default);
|
||||
/// <returns>A <see cref="Task"/> containing the response of the asynchronous HTTP operation.</returns>
|
||||
Task<HttpResponseMessage> GetReminderAsync(string actorType, string actorId, string reminderName, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Unregisters a reminder.
|
||||
|
|
|
@ -223,7 +223,7 @@ namespace Dapr.Actors.Runtime
|
|||
internal async Task FireTimerAsync(ActorId actorId, Stream requestBodyStream, CancellationToken cancellationToken = default)
|
||||
{
|
||||
#pragma warning disable 0618
|
||||
var timerData = await JsonSerializer.DeserializeAsync<TimerInfo>(requestBodyStream);
|
||||
var timerData = await DeserializeAsync(requestBodyStream);
|
||||
#pragma warning restore 0618
|
||||
|
||||
// Create a Func to be invoked by common method.
|
||||
|
@ -243,6 +243,62 @@ namespace Dapr.Actors.Runtime
|
|||
await this.DispatchInternalAsync(actorId, this.timerMethodContext, RequestFunc, cancellationToken);
|
||||
}
|
||||
|
||||
#pragma warning disable 0618
|
||||
internal static async Task<TimerInfo> DeserializeAsync(Stream stream)
|
||||
{
|
||||
var json = await JsonSerializer.DeserializeAsync<JsonElement>(stream);
|
||||
if (json.ValueKind == JsonValueKind.Null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var setAnyProperties = false; // Used to determine if anything was actually deserialized
|
||||
var dueTime = TimeSpan.Zero;
|
||||
var callback = "";
|
||||
var period = TimeSpan.Zero;
|
||||
var data = Array.Empty<byte>();
|
||||
TimeSpan? ttl = null;
|
||||
if (json.TryGetProperty("callback", out var callbackProperty))
|
||||
{
|
||||
setAnyProperties = true;
|
||||
callback = callbackProperty.GetString();
|
||||
}
|
||||
if (json.TryGetProperty("dueTime", out var dueTimeProperty))
|
||||
{
|
||||
setAnyProperties = true;
|
||||
var dueTimeString = dueTimeProperty.GetString();
|
||||
dueTime = ConverterUtils.ConvertTimeSpanFromDaprFormat(dueTimeString);
|
||||
}
|
||||
|
||||
if (json.TryGetProperty("period", out var periodProperty))
|
||||
{
|
||||
setAnyProperties = true;
|
||||
var periodString = periodProperty.GetString();
|
||||
(period, _) = ConverterUtils.ConvertTimeSpanValueFromISO8601Format(periodString);
|
||||
}
|
||||
|
||||
if (json.TryGetProperty("data", out var dataProperty) && dataProperty.ValueKind != JsonValueKind.Null)
|
||||
{
|
||||
setAnyProperties = true;
|
||||
data = dataProperty.GetBytesFromBase64();
|
||||
}
|
||||
|
||||
if (json.TryGetProperty("ttl", out var ttlProperty))
|
||||
{
|
||||
setAnyProperties = true;
|
||||
var ttlString = ttlProperty.GetString();
|
||||
ttl = ConverterUtils.ConvertTimeSpanFromDaprFormat(ttlString);
|
||||
}
|
||||
|
||||
if (!setAnyProperties)
|
||||
{
|
||||
return null; //No properties were ever deserialized, so return null instead of default values
|
||||
}
|
||||
|
||||
return new TimerInfo(callback, data, dueTime, period, ttl);
|
||||
}
|
||||
#pragma warning restore 0618
|
||||
|
||||
internal async Task ActivateActorAsync(ActorId actorId)
|
||||
{
|
||||
// An actor is activated by "Dapr" runtime when a call is to be made for an actor.
|
||||
|
|
|
@ -11,138 +11,148 @@
|
|||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
namespace Dapr.Actors.Runtime
|
||||
using Dapr.Actors.Extensions;
|
||||
|
||||
namespace Dapr.Actors.Runtime;
|
||||
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
internal static class ConverterUtils
|
||||
{
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
internal class ConverterUtils
|
||||
private static Regex regex = new("^(R(?<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)
|
||||
{
|
||||
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);
|
||||
public static TimeSpan ConvertTimeSpanFromDaprFormat(string valueString)
|
||||
if (string.IsNullOrEmpty(valueString))
|
||||
{
|
||||
if (string.IsNullOrEmpty(valueString))
|
||||
{
|
||||
var never = TimeSpan.FromMilliseconds(-1);
|
||||
return never;
|
||||
}
|
||||
|
||||
// TimeSpan is a string. Format returned by Dapr is: 1h4m5s4ms4us4ns
|
||||
// acceptable values are: m, s, ms, us(micro), ns
|
||||
var spanOfValue = valueString.AsSpan();
|
||||
|
||||
// Change the value returned by Dapr runtime, so that it can be parsed with TimeSpan.
|
||||
// Format returned by Dapr runtime: 4h15m50s60ms. It doesnt have days.
|
||||
// Dapr runtime should handle timespans in ISO 8601 format.
|
||||
// Replace ms before m & s. Also append 0 days for parsing correctly with TimeSpan
|
||||
int hIndex = spanOfValue.IndexOf('h');
|
||||
int mIndex = spanOfValue.IndexOf('m');
|
||||
int sIndex = spanOfValue.IndexOf('s');
|
||||
int msIndex = spanOfValue.IndexOf("ms");
|
||||
|
||||
// handle days from hours.
|
||||
var hoursSpan = spanOfValue.Slice(0, hIndex);
|
||||
var hours = int.Parse(hoursSpan);
|
||||
var days = hours / 24;
|
||||
hours %= 24;
|
||||
|
||||
var minutesSpan = spanOfValue[(hIndex + 1)..mIndex];
|
||||
var minutes = int.Parse(minutesSpan);
|
||||
|
||||
var secondsSpan = spanOfValue[(mIndex + 1)..sIndex];
|
||||
var seconds = int.Parse(secondsSpan);
|
||||
|
||||
var millisecondsSpan = spanOfValue[(sIndex + 1)..msIndex];
|
||||
var milliseconds = int.Parse(millisecondsSpan);
|
||||
|
||||
return new TimeSpan(days, hours, minutes, seconds, milliseconds);
|
||||
var never = TimeSpan.FromMilliseconds(-1);
|
||||
return never;
|
||||
}
|
||||
|
||||
if (valueString.IsDurationExpression())
|
||||
{
|
||||
return valueString.FromPrefixedPeriod();
|
||||
}
|
||||
|
||||
public static string ConvertTimeSpanValueInDaprFormat(TimeSpan? value)
|
||||
{
|
||||
// write in format expected by Dapr, it only accepts h, m, s, ms, us(micro), ns
|
||||
var stringValue = string.Empty;
|
||||
if (value.Value >= TimeSpan.Zero)
|
||||
{
|
||||
var hours = (value.Value.Days * 24) + value.Value.Hours;
|
||||
stringValue = FormattableString.Invariant($"{hours}h{value.Value.Minutes}m{value.Value.Seconds}s{value.Value.Milliseconds}ms");
|
||||
}
|
||||
// TimeSpan is a string. Format returned by Dapr is: 1h4m5s4ms4us4ns
|
||||
// acceptable values are: m, s, ms, us(micro), ns
|
||||
var spanOfValue = valueString.AsSpan();
|
||||
|
||||
// Change the value returned by Dapr runtime, so that it can be parsed with TimeSpan.
|
||||
// Format returned by Dapr runtime: 4h15m50s60ms. It doesnt have days.
|
||||
// Dapr runtime should handle timespans in ISO 8601 format.
|
||||
// Replace ms before m & s. Also append 0 days for parsing correctly with TimeSpan
|
||||
int hIndex = spanOfValue.IndexOf('h');
|
||||
int mIndex = spanOfValue.IndexOf('m');
|
||||
int sIndex = spanOfValue.IndexOf('s');
|
||||
int msIndex = spanOfValue.IndexOf("ms");
|
||||
|
||||
// handle days from hours.
|
||||
var hoursSpan = spanOfValue[..hIndex];
|
||||
var hours = int.Parse(hoursSpan);
|
||||
var days = hours / 24;
|
||||
hours %= 24;
|
||||
|
||||
var minutesSpan = spanOfValue[(hIndex + 1)..mIndex];
|
||||
var minutes = int.Parse(minutesSpan);
|
||||
|
||||
var secondsSpan = spanOfValue[(mIndex + 1)..sIndex];
|
||||
var seconds = int.Parse(secondsSpan);
|
||||
|
||||
var millisecondsSpan = spanOfValue[(sIndex + 1)..msIndex];
|
||||
var milliseconds = int.Parse(millisecondsSpan);
|
||||
|
||||
return new TimeSpan(days, hours, minutes, seconds, milliseconds);
|
||||
}
|
||||
|
||||
public static string ConvertTimeSpanValueInDaprFormat(TimeSpan? value)
|
||||
{
|
||||
// write in format expected by Dapr, it only accepts h, m, s, ms, us(micro), ns
|
||||
var stringValue = string.Empty;
|
||||
if (value is null)
|
||||
return stringValue;
|
||||
}
|
||||
|
||||
public static string ConvertTimeSpanValueInISO8601Format(TimeSpan value, int? repetitions)
|
||||
|
||||
if (value.Value >= TimeSpan.Zero)
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
if (repetitions == null)
|
||||
{
|
||||
return ConvertTimeSpanValueInDaprFormat(value);
|
||||
}
|
||||
|
||||
if (value.Milliseconds > 0)
|
||||
{
|
||||
throw new ArgumentException("The TimeSpan value, combined with repetition cannot be in milliseconds.", nameof(value));
|
||||
}
|
||||
|
||||
builder.AppendFormat("R{0}/P", repetitions);
|
||||
|
||||
if(value.Days > 0)
|
||||
{
|
||||
builder.AppendFormat("{0}D", value.Days);
|
||||
}
|
||||
|
||||
builder.Append("T");
|
||||
|
||||
if(value.Hours > 0)
|
||||
{
|
||||
builder.AppendFormat("{0}H", value.Hours);
|
||||
}
|
||||
|
||||
if(value.Minutes > 0)
|
||||
{
|
||||
builder.AppendFormat("{0}M", value.Minutes);
|
||||
}
|
||||
|
||||
if(value.Seconds > 0)
|
||||
{
|
||||
builder.AppendFormat("{0}S", value.Seconds);
|
||||
}
|
||||
return builder.ToString();
|
||||
var hours = (value.Value.Days * 24) + value.Value.Hours;
|
||||
stringValue = FormattableString.Invariant($"{hours}h{value.Value.Minutes}m{value.Value.Seconds}s{value.Value.Milliseconds}ms");
|
||||
}
|
||||
|
||||
return stringValue;
|
||||
}
|
||||
|
||||
public static (TimeSpan, int?) ConvertTimeSpanValueFromISO8601Format(string valueString)
|
||||
public static string ConvertTimeSpanValueInISO8601Format(TimeSpan value, int? repetitions)
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
if (repetitions == null)
|
||||
{
|
||||
// ISO 8601 format can be Rn/PaYbMcHTdHeMfS or PaYbMcHTdHeMfS so if it does
|
||||
// not start with R or P then assuming it to default Dapr format without repetition
|
||||
if (!(valueString.StartsWith('R') || valueString.StartsWith('P')))
|
||||
{
|
||||
return (ConvertTimeSpanFromDaprFormat(valueString), -1);
|
||||
}
|
||||
|
||||
var matches = regex.Match(valueString);
|
||||
|
||||
var repetition = matches.Groups["repetition"].Success ? int.Parse(matches.Groups["repetition"].Value) : (int?)null;
|
||||
|
||||
var days = 0;
|
||||
var year = matches.Groups["year"].Success ? int.Parse(matches.Groups["year"].Value) : 0;
|
||||
days = year * 365;
|
||||
|
||||
var month = matches.Groups["month"].Success ? int.Parse(matches.Groups["month"].Value) : 0;
|
||||
days += month * 30;
|
||||
|
||||
var week = matches.Groups["week"].Success ? int.Parse(matches.Groups["week"].Value) : 0;
|
||||
days += week * 7;
|
||||
|
||||
var day = matches.Groups["day"].Success ? int.Parse(matches.Groups["day"].Value) : 0;
|
||||
days += day;
|
||||
|
||||
var hour = matches.Groups["hour"].Success ? int.Parse(matches.Groups["hour"].Value) : 0;
|
||||
var minute = matches.Groups["minute"].Success ? int.Parse(matches.Groups["minute"].Value) : 0;
|
||||
var second = matches.Groups["second"].Success ? int.Parse(matches.Groups["second"].Value) : 0;
|
||||
|
||||
return (new TimeSpan(days, hour, minute, second), repetition);
|
||||
return ConvertTimeSpanValueInDaprFormat(value);
|
||||
}
|
||||
|
||||
if (value.Milliseconds > 0)
|
||||
{
|
||||
throw new ArgumentException("The TimeSpan value, combined with repetition cannot be in milliseconds.", nameof(value));
|
||||
}
|
||||
|
||||
builder.Append($"R{repetitions}/P");
|
||||
|
||||
if(value.Days > 0)
|
||||
{
|
||||
builder.Append($"{value.Days}D");
|
||||
}
|
||||
|
||||
builder.Append('T');
|
||||
|
||||
if(value.Hours > 0)
|
||||
{
|
||||
builder.Append($"{value.Hours}H");
|
||||
}
|
||||
|
||||
if(value.Minutes > 0)
|
||||
{
|
||||
builder.Append($"{value.Minutes}M");
|
||||
}
|
||||
|
||||
if(value.Seconds > 0)
|
||||
{
|
||||
builder.Append($"{value.Seconds}S");
|
||||
}
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
public static (TimeSpan, int?) ConvertTimeSpanValueFromISO8601Format(string valueString)
|
||||
{
|
||||
// ISO 8601 format can be Rn/PaYbMcHTdHeMfS or PaYbMcHTdHeMfS so if it does
|
||||
// not start with R or P then assuming it to default Dapr format without repetition
|
||||
if (!(valueString.StartsWith('R') || valueString.StartsWith('P')))
|
||||
{
|
||||
return (ConvertTimeSpanFromDaprFormat(valueString), -1);
|
||||
}
|
||||
|
||||
var matches = regex.Match(valueString);
|
||||
|
||||
var repetition = matches.Groups["repetition"].Success ? int.Parse(matches.Groups["repetition"].Value) : (int?)null;
|
||||
|
||||
|
||||
var days = 0;
|
||||
var year = matches.Groups["year"].Success ? int.Parse(matches.Groups["year"].Value) : 0;
|
||||
days = year * 365;
|
||||
|
||||
var month = matches.Groups["month"].Success ? int.Parse(matches.Groups["month"].Value) : 0;
|
||||
days += month * 30;
|
||||
|
||||
var week = matches.Groups["week"].Success ? int.Parse(matches.Groups["week"].Value) : 0;
|
||||
days += week * 7;
|
||||
|
||||
var day = matches.Groups["day"].Success ? int.Parse(matches.Groups["day"].Value) : 0;
|
||||
days += day;
|
||||
|
||||
var hour = matches.Groups["hour"].Success ? int.Parse(matches.Groups["hour"].Value) : 0;
|
||||
var minute = matches.Groups["minute"].Success ? int.Parse(matches.Groups["minute"].Value) : 0;
|
||||
var second = matches.Groups["second"].Success ? int.Parse(matches.Groups["second"].Value) : 0;
|
||||
|
||||
return (new TimeSpan(days, hour, minute, second), repetition);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ using System;
|
|||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using System.IO;
|
||||
using Grpc.Core;
|
||||
|
||||
namespace Dapr.Actors.Runtime
|
||||
{
|
||||
|
@ -45,9 +46,14 @@ namespace Dapr.Actors.Runtime
|
|||
throw new ArgumentNullException(nameof(token));
|
||||
}
|
||||
|
||||
var responseStream = await this.interactor.GetReminderAsync(token.ActorType, token.ActorId.ToString(), token.Name);
|
||||
var reminder = await DeserializeReminderAsync(responseStream, token);
|
||||
return reminder;
|
||||
var response = await this.interactor.GetReminderAsync(token.ActorType, token.ActorId.ToString(), token.Name);
|
||||
if ((int)response.StatusCode == 500)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var responseStream = await response.Content.ReadAsStreamAsync();
|
||||
return await DeserializeReminderAsync(responseStream, token);
|
||||
}
|
||||
|
||||
public override async Task UnregisterReminderAsync(ActorReminderToken reminder)
|
||||
|
@ -84,24 +90,26 @@ namespace Dapr.Actors.Runtime
|
|||
await this.interactor.UnregisterTimerAsync(timer.ActorType, timer.ActorId.ToString(), timer.Name);
|
||||
}
|
||||
|
||||
private async ValueTask<string> SerializeReminderAsync(ActorReminder reminder)
|
||||
private static async ValueTask<string> SerializeReminderAsync(ActorReminder reminder)
|
||||
{
|
||||
var info = new ReminderInfo(reminder.State, reminder.DueTime, reminder.Period, reminder.Repetitions,
|
||||
reminder.Ttl);
|
||||
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)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(stream));
|
||||
}
|
||||
|
||||
var info = await ReminderInfo.DeserializeAsync(stream);
|
||||
if(info == null)
|
||||
if (info == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var reminder = new ActorReminder(token.ActorType, token.ActorId, token.Name, info.Data, info.DueTime,
|
||||
info.Period);
|
||||
return reminder;
|
||||
|
|
|
@ -11,99 +11,110 @@
|
|||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
namespace Dapr.Actors.Runtime
|
||||
#nullable enable
|
||||
|
||||
namespace Dapr.Actors.Runtime;
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
// represents the wire format used by Dapr to store reminder info with the runtime
|
||||
internal class ReminderInfo
|
||||
{
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
// represents the wire format used by Dapr to store reminder info with the runtime
|
||||
internal class ReminderInfo
|
||||
public ReminderInfo(
|
||||
byte[] data,
|
||||
TimeSpan dueTime,
|
||||
TimeSpan period,
|
||||
int? repetitions = null,
|
||||
TimeSpan? ttl = null)
|
||||
{
|
||||
public ReminderInfo(
|
||||
byte[] data,
|
||||
TimeSpan dueTime,
|
||||
TimeSpan period,
|
||||
int? repetitions = null,
|
||||
TimeSpan? ttl = null)
|
||||
{
|
||||
this.Data = data;
|
||||
this.DueTime = dueTime;
|
||||
this.Period = period;
|
||||
this.Ttl = ttl;
|
||||
this.Repetitions = repetitions;
|
||||
}
|
||||
this.Data = data;
|
||||
this.DueTime = dueTime;
|
||||
this.Period = period;
|
||||
this.Ttl = ttl;
|
||||
this.Repetitions = repetitions;
|
||||
}
|
||||
|
||||
public TimeSpan DueTime { get; private set; }
|
||||
public TimeSpan DueTime { get; private set; }
|
||||
|
||||
public TimeSpan Period { get; private set; }
|
||||
public TimeSpan Period { get; private set; }
|
||||
|
||||
public byte[] Data { get; private set; }
|
||||
public byte[] Data { get; private set; }
|
||||
|
||||
public TimeSpan? Ttl { get; private set; }
|
||||
public TimeSpan? Ttl { get; private set; }
|
||||
|
||||
public int? Repetitions { get; private set; }
|
||||
public int? Repetitions { get; private set; }
|
||||
|
||||
internal static async Task<ReminderInfo> DeserializeAsync(Stream stream)
|
||||
internal static async Task<ReminderInfo?> DeserializeAsync(Stream stream)
|
||||
{
|
||||
var json = await JsonSerializer.DeserializeAsync<JsonElement>(stream);
|
||||
if(json.ValueKind == JsonValueKind.Null)
|
||||
{
|
||||
var json = await JsonSerializer.DeserializeAsync<JsonElement>(stream);
|
||||
if(json.ValueKind == JsonValueKind.Null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var dueTime = default(TimeSpan);
|
||||
var period = default(TimeSpan);
|
||||
var data = default(byte[]);
|
||||
int? repetition = null;
|
||||
TimeSpan? ttl = null;
|
||||
if (json.TryGetProperty("dueTime", out var dueTimeProperty))
|
||||
{
|
||||
var dueTimeString = dueTimeProperty.GetString();
|
||||
dueTime = ConverterUtils.ConvertTimeSpanFromDaprFormat(dueTimeString);
|
||||
}
|
||||
|
||||
if (json.TryGetProperty("period", out var periodProperty))
|
||||
{
|
||||
var periodString = periodProperty.GetString();
|
||||
(period, repetition) = ConverterUtils.ConvertTimeSpanValueFromISO8601Format(periodString);
|
||||
}
|
||||
|
||||
if (json.TryGetProperty("data", out var dataProperty) && dataProperty.ValueKind != JsonValueKind.Null)
|
||||
{
|
||||
data = dataProperty.GetBytesFromBase64();
|
||||
}
|
||||
|
||||
if (json.TryGetProperty("ttl", out var ttlProperty))
|
||||
{
|
||||
var ttlString = ttlProperty.GetString();
|
||||
ttl = ConverterUtils.ConvertTimeSpanFromDaprFormat(ttlString);
|
||||
}
|
||||
|
||||
return new ReminderInfo(data, dueTime, period, repetition, ttl);
|
||||
return null;
|
||||
}
|
||||
|
||||
internal async ValueTask<string> SerializeAsync()
|
||||
var setAnyProperties = false; //Used to determine if anything was actually deserialized
|
||||
var dueTime = TimeSpan.Zero;
|
||||
var period = TimeSpan.Zero;
|
||||
var data = Array.Empty<byte>();
|
||||
int? repetition = null;
|
||||
TimeSpan? ttl = null;
|
||||
if (json.TryGetProperty("dueTime", out var dueTimeProperty))
|
||||
{
|
||||
using var stream = new MemoryStream();
|
||||
using Utf8JsonWriter writer = new Utf8JsonWriter(stream);
|
||||
|
||||
writer.WriteStartObject();
|
||||
writer.WriteString("dueTime", ConverterUtils.ConvertTimeSpanValueInDaprFormat(this.DueTime));
|
||||
writer.WriteString("period", ConverterUtils.ConvertTimeSpanValueInISO8601Format(
|
||||
this.Period, this.Repetitions));
|
||||
writer.WriteBase64String("data", this.Data);
|
||||
|
||||
if (Ttl != null)
|
||||
{
|
||||
writer.WriteString("ttl", ConverterUtils.ConvertTimeSpanValueInDaprFormat(Ttl));
|
||||
}
|
||||
|
||||
writer.WriteEndObject();
|
||||
await writer.FlushAsync();
|
||||
return Encoding.UTF8.GetString(stream.ToArray());
|
||||
setAnyProperties = true;
|
||||
var dueTimeString = dueTimeProperty.GetString();
|
||||
dueTime = ConverterUtils.ConvertTimeSpanFromDaprFormat(dueTimeString);
|
||||
}
|
||||
|
||||
if (json.TryGetProperty("period", out var periodProperty))
|
||||
{
|
||||
setAnyProperties = true;
|
||||
var periodString = periodProperty.GetString();
|
||||
(period, repetition) = ConverterUtils.ConvertTimeSpanValueFromISO8601Format(periodString);
|
||||
}
|
||||
|
||||
if (json.TryGetProperty("data", out var dataProperty) && dataProperty.ValueKind != JsonValueKind.Null)
|
||||
{
|
||||
setAnyProperties = true;
|
||||
data = dataProperty.GetBytesFromBase64();
|
||||
}
|
||||
|
||||
if (json.TryGetProperty("ttl", out var ttlProperty))
|
||||
{
|
||||
setAnyProperties = true;
|
||||
var ttlString = ttlProperty.GetString();
|
||||
ttl = ConverterUtils.ConvertTimeSpanFromDaprFormat(ttlString);
|
||||
}
|
||||
|
||||
if (!setAnyProperties)
|
||||
{
|
||||
return null; //No properties were ever deserialized, so return null instead of default values
|
||||
}
|
||||
|
||||
return new ReminderInfo(data, dueTime, period, repetition, ttl);
|
||||
}
|
||||
|
||||
internal async ValueTask<string> SerializeAsync()
|
||||
{
|
||||
using var stream = new MemoryStream();
|
||||
await using Utf8JsonWriter writer = new Utf8JsonWriter(stream);
|
||||
|
||||
writer.WriteStartObject();
|
||||
writer.WriteString("dueTime", ConverterUtils.ConvertTimeSpanValueInDaprFormat(this.DueTime));
|
||||
writer.WriteString("period", ConverterUtils.ConvertTimeSpanValueInISO8601Format(
|
||||
this.Period, this.Repetitions));
|
||||
writer.WriteBase64String("data", this.Data);
|
||||
|
||||
if (Ttl != null)
|
||||
{
|
||||
writer.WriteString("ttl", ConverterUtils.ConvertTimeSpanValueInDaprFormat(Ttl));
|
||||
}
|
||||
|
||||
writer.WriteEndObject();
|
||||
await writer.FlushAsync();
|
||||
return Encoding.UTF8.GetString(stream.ToArray());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,153 @@
|
|||
// ------------------------------------------------------------------------
|
||||
// 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.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Channels;
|
||||
using System.Threading.Tasks;
|
||||
using Google.Protobuf;
|
||||
using Grpc.Core;
|
||||
using Autogenerated = Dapr.Client.Autogen.Grpc.v1;
|
||||
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
|
||||
|
||||
namespace Dapr.Client.Crypto;
|
||||
|
||||
/// <summary>
|
||||
/// Provides the implementation to decrypt a stream of plaintext data with the Dapr runtime.
|
||||
/// </summary>
|
||||
internal sealed class DecryptionStreamProcessor : IDisposable
|
||||
{
|
||||
private bool disposed;
|
||||
private readonly Channel<ReadOnlyMemory<byte>> outputChannel = Channel.CreateUnbounded<ReadOnlyMemory<byte>>();
|
||||
|
||||
/// <summary>
|
||||
/// Surfaces any exceptions encountered while asynchronously processing the inbound and outbound streams.
|
||||
/// </summary>
|
||||
internal event EventHandler<Exception>? OnException;
|
||||
|
||||
/// <summary>
|
||||
/// Sends the provided bytes in chunks to the sidecar for the encryption operation.
|
||||
/// </summary>
|
||||
/// <param name="inputStream">The stream containing the bytes to decrypt.</param>
|
||||
/// <param name="call">The call to make to the sidecar to process the encryption operation.</param>
|
||||
/// <param name="streamingBlockSizeInBytes">The size, in bytes, of the streaming blocks.</param>
|
||||
/// <param name="options">The decryption options.</param>
|
||||
/// <param name="cancellationToken">Token used to cancel the ongoing request.</param>
|
||||
public async Task ProcessStreamAsync(
|
||||
Stream inputStream,
|
||||
AsyncDuplexStreamingCall<Autogenerated.DecryptRequest, Autogenerated.DecryptResponse> call,
|
||||
int streamingBlockSizeInBytes,
|
||||
Autogenerated.DecryptRequestOptions options,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
//Read from the input stream and write to the gRPC call
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await using var bufferedStream = new BufferedStream(inputStream, streamingBlockSizeInBytes);
|
||||
var buffer = new byte[streamingBlockSizeInBytes];
|
||||
int bytesRead;
|
||||
ulong sequenceNumber = 0;
|
||||
|
||||
while ((bytesRead = await bufferedStream.ReadAsync(buffer, cancellationToken)) > 0)
|
||||
{
|
||||
var request = new Autogenerated.DecryptRequest
|
||||
{
|
||||
Payload = new Autogenerated.StreamPayload
|
||||
{
|
||||
Data = ByteString.CopyFrom(buffer, 0, bytesRead), Seq = sequenceNumber
|
||||
}
|
||||
};
|
||||
|
||||
//Only include the options in the first message
|
||||
if (sequenceNumber == 0)
|
||||
{
|
||||
request.Options = options;
|
||||
}
|
||||
|
||||
await call.RequestStream.WriteAsync(request, cancellationToken);
|
||||
|
||||
//Increment the sequence number
|
||||
sequenceNumber++;
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
// Expected cancellation exception
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnException?.Invoke(this, ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
await call.RequestStream.CompleteAsync();
|
||||
}
|
||||
}, cancellationToken);
|
||||
|
||||
//Start reading from the gRPC call and writing to the output channel
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await foreach (var response in call.ResponseStream.ReadAllAsync(cancellationToken))
|
||||
{
|
||||
await outputChannel.Writer.WriteAsync(response.Payload.Data.Memory, cancellationToken);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnException?.Invoke(this, ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
outputChannel.Writer.Complete();
|
||||
}
|
||||
}, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the processed bytes from the operation from the sidecar and
|
||||
/// returns as an enumerable stream.
|
||||
/// </summary>
|
||||
public async IAsyncEnumerable<ReadOnlyMemory<byte>> GetProcessedDataAsync([EnumeratorCancellation] CancellationToken cancellationToken)
|
||||
{
|
||||
await foreach (var data in outputChannel.Reader.ReadAllAsync(cancellationToken))
|
||||
{
|
||||
yield return data;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (!disposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
outputChannel.Writer.TryComplete();
|
||||
}
|
||||
|
||||
disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,154 @@
|
|||
// ------------------------------------------------------------------------
|
||||
// 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.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Channels;
|
||||
using System.Threading.Tasks;
|
||||
using Google.Protobuf;
|
||||
using Grpc.Core;
|
||||
using Autogenerated = Dapr.Client.Autogen.Grpc.v1;
|
||||
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
|
||||
|
||||
namespace Dapr.Client.Crypto;
|
||||
|
||||
/// <summary>
|
||||
/// Provides the implementation to encrypt a stream of plaintext data with the Dapr runtime.
|
||||
/// </summary>
|
||||
internal sealed class EncryptionStreamProcessor : IDisposable
|
||||
{
|
||||
private bool disposed;
|
||||
private readonly Channel<ReadOnlyMemory<byte>> outputChannel = Channel.CreateUnbounded<ReadOnlyMemory<byte>>();
|
||||
|
||||
/// <summary>
|
||||
/// Surfaces any exceptions encountered while asynchronously processing the inbound and outbound streams.
|
||||
/// </summary>
|
||||
internal event EventHandler<Exception>? OnException;
|
||||
|
||||
/// <summary>
|
||||
/// Sends the provided bytes in chunks to the sidecar for the encryption operation.
|
||||
/// </summary>
|
||||
/// <param name="inputStream">The stream containing the bytes to encrypt.</param>
|
||||
/// <param name="call">The call to make to the sidecar to process the encryption operation.</param>
|
||||
/// <param name="options">The encryption options.</param>
|
||||
/// <param name="streamingBlockSizeInBytes">The size, in bytes, of the streaming blocks.</param>
|
||||
/// <param name="cancellationToken">Token used to cancel the ongoing request.</param>
|
||||
public async Task ProcessStreamAsync(
|
||||
Stream inputStream,
|
||||
AsyncDuplexStreamingCall<Autogenerated.EncryptRequest, Autogenerated.EncryptResponse> call,
|
||||
Autogenerated.EncryptRequestOptions options,
|
||||
int streamingBlockSizeInBytes,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
//Read from the input stream and write to the gRPC call
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await using var bufferedStream = new BufferedStream(inputStream, streamingBlockSizeInBytes);
|
||||
var buffer = new byte[streamingBlockSizeInBytes];
|
||||
int bytesRead;
|
||||
ulong sequenceNumber = 0;
|
||||
|
||||
while ((bytesRead = await bufferedStream.ReadAsync(buffer, cancellationToken)) > 0)
|
||||
{
|
||||
var request = new Autogenerated.EncryptRequest
|
||||
{
|
||||
Payload = new Autogenerated.StreamPayload
|
||||
{
|
||||
Data = ByteString.CopyFrom(buffer, 0, bytesRead), Seq = sequenceNumber
|
||||
}
|
||||
};
|
||||
|
||||
//Only include the options in the first message
|
||||
if (sequenceNumber == 0)
|
||||
{
|
||||
request.Options = options;
|
||||
}
|
||||
|
||||
await call.RequestStream.WriteAsync(request, cancellationToken);
|
||||
|
||||
//Increment the sequence number
|
||||
sequenceNumber++;
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
// Expected cancellation exception
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnException?.Invoke(this, ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
await call.RequestStream.CompleteAsync();
|
||||
}
|
||||
}, cancellationToken);
|
||||
|
||||
//Start reading from the gRPC call and writing to the output channel
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await foreach (var response in call.ResponseStream.ReadAllAsync(cancellationToken))
|
||||
{
|
||||
await outputChannel.Writer.WriteAsync(response.Payload.Data.Memory, cancellationToken);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
OnException?.Invoke(this, ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
outputChannel.Writer.Complete();
|
||||
}
|
||||
}, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the processed bytes from the operation from the sidecar and
|
||||
/// returns as an enumerable stream.
|
||||
/// </summary>
|
||||
public async IAsyncEnumerable<ReadOnlyMemory<byte>> GetProcessedDataAsync([EnumeratorCancellation] CancellationToken cancellationToken)
|
||||
{
|
||||
await foreach (var data in outputChannel.Reader.ReadAllAsync(cancellationToken))
|
||||
{
|
||||
yield return data;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (!disposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
outputChannel.Writer.TryComplete();
|
||||
}
|
||||
|
||||
disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1105,7 +1105,7 @@ namespace Dapr.Client
|
|||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
|
||||
/// <returns>An array of encrypted bytes.</returns>
|
||||
[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
public abstract Task<IAsyncEnumerable<ReadOnlyMemory<byte>>> EncryptAsync(string vaultResourceName, Stream plaintextStream, string keyName,
|
||||
public abstract IAsyncEnumerable<ReadOnlyMemory<byte>> EncryptAsync(string vaultResourceName, Stream plaintextStream, string keyName,
|
||||
EncryptionOptions encryptionOptions, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
|
@ -1144,7 +1144,7 @@ namespace Dapr.Client
|
|||
/// <returns>An asynchronously enumerable array of decrypted bytes.</returns>
|
||||
[Obsolete(
|
||||
"The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
public abstract Task<IAsyncEnumerable<ReadOnlyMemory<byte>>> DecryptAsync(string vaultResourceName, Stream ciphertextStream,
|
||||
public abstract IAsyncEnumerable<ReadOnlyMemory<byte>> DecryptAsync(string vaultResourceName, Stream ciphertextStream,
|
||||
string keyName, DecryptionOptions options, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
|
@ -1157,7 +1157,7 @@ namespace Dapr.Client
|
|||
/// <returns>An asynchronously enumerable array of decrypted bytes.</returns>
|
||||
[Obsolete(
|
||||
"The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
public abstract Task<IAsyncEnumerable<ReadOnlyMemory<byte>>> DecryptAsync(string vaultResourceName, Stream ciphertextStream,
|
||||
public abstract IAsyncEnumerable<ReadOnlyMemory<byte>> DecryptAsync(string vaultResourceName, Stream ciphertextStream,
|
||||
string keyName, CancellationToken cancellationToken = default);
|
||||
|
||||
#endregion
|
||||
|
|
|
@ -11,10 +11,10 @@
|
|||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
using Dapr.Common.Extensions;
|
||||
|
||||
namespace Dapr.Client;
|
||||
|
||||
using Crypto;
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
|
@ -23,7 +23,6 @@ using System.Linq;
|
|||
using System.Net.Http;
|
||||
using System.Net.Http.Json;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
@ -1665,42 +1664,38 @@ internal class DaprClientGrpc : DaprClient
|
|||
|
||||
/// <inheritdoc />
|
||||
[Obsolete(
|
||||
"The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
"As of Dapr v1.17, this method will be removed and should be used via the Dapr.Cryptography package on NuGet")]
|
||||
public override async Task<ReadOnlyMemory<byte>> EncryptAsync(string vaultResourceName,
|
||||
ReadOnlyMemory<byte> plaintextBytes, string keyName, EncryptionOptions encryptionOptions,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (MemoryMarshal.TryGetArray(plaintextBytes, out var plaintextSegment) && plaintextSegment.Array != null)
|
||||
using var memoryStream = plaintextBytes.CreateMemoryStream(true);
|
||||
|
||||
var encryptionResult = EncryptAsync(vaultResourceName, memoryStream, keyName, encryptionOptions, cancellationToken);
|
||||
|
||||
var bufferedResult = new ArrayBufferWriter<byte>();
|
||||
await foreach (var item in encryptionResult)
|
||||
{
|
||||
var encryptionResult = await EncryptAsync(vaultResourceName, new MemoryStream(plaintextSegment.Array),
|
||||
keyName, encryptionOptions,
|
||||
cancellationToken);
|
||||
|
||||
var bufferedResult = new ArrayBufferWriter<byte>();
|
||||
|
||||
await foreach (var item in encryptionResult.WithCancellation(cancellationToken))
|
||||
{
|
||||
bufferedResult.Write(item.Span);
|
||||
}
|
||||
|
||||
return bufferedResult.WrittenMemory;
|
||||
bufferedResult.Write(item.Span);
|
||||
}
|
||||
|
||||
throw new ArgumentException("The input instance doesn't have a valid underlying data store.",
|
||||
nameof(plaintextBytes));
|
||||
return bufferedResult.WrittenMemory;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[Obsolete(
|
||||
"The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
public override async Task<IAsyncEnumerable<ReadOnlyMemory<byte>>> EncryptAsync(string vaultResourceName,
|
||||
"As of Dapr v1.17, this method will be removed and should be used via the Dapr.Cryptography package on NuGet")]
|
||||
public override async IAsyncEnumerable<ReadOnlyMemory<byte>> EncryptAsync(string vaultResourceName,
|
||||
Stream plaintextStream,
|
||||
string keyName, EncryptionOptions encryptionOptions, CancellationToken cancellationToken = default)
|
||||
string keyName, EncryptionOptions encryptionOptions,
|
||||
[EnumeratorCancellation] CancellationToken cancellationToken = default)
|
||||
{
|
||||
ArgumentVerifier.ThrowIfNullOrEmpty(vaultResourceName, nameof(vaultResourceName));
|
||||
ArgumentVerifier.ThrowIfNullOrEmpty(keyName, nameof(keyName));
|
||||
ArgumentVerifier.ThrowIfNull(plaintextStream, nameof(plaintextStream));
|
||||
ArgumentVerifier.ThrowIfNull(encryptionOptions, nameof(encryptionOptions));
|
||||
|
||||
EventHandler<Exception> exceptionHandler = (_, ex) => throw ex;
|
||||
|
||||
var shouldOmitDecryptionKeyName =
|
||||
string.IsNullOrWhiteSpace(encryptionOptions
|
||||
|
@ -1723,199 +1718,101 @@ internal class DaprClientGrpc : DaprClient
|
|||
}
|
||||
|
||||
var options = CreateCallOptions(headers: null, cancellationToken);
|
||||
var duplexStream = client.EncryptAlpha1(options);
|
||||
var duplexStream = Client.EncryptAlpha1(options);
|
||||
|
||||
//Run both operations at the same time, but return the output of the streaming values coming from the operation
|
||||
var receiveResult = Task.FromResult(RetrieveEncryptedStreamAsync(duplexStream, cancellationToken));
|
||||
return await Task.WhenAll(
|
||||
//Stream the plaintext data to the sidecar in chunks
|
||||
SendPlaintextStreamAsync(plaintextStream, encryptionOptions.StreamingBlockSizeInBytes,
|
||||
duplexStream, encryptRequestOptions, cancellationToken),
|
||||
//At the same time, retrieve the encrypted response from the sidecar
|
||||
receiveResult).ContinueWith(_ => receiveResult.Result, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends the plaintext bytes in chunks to the sidecar to be encrypted.
|
||||
/// </summary>
|
||||
private async Task SendPlaintextStreamAsync(Stream plaintextStream,
|
||||
int streamingBlockSizeInBytes,
|
||||
AsyncDuplexStreamingCall<Autogenerated.EncryptRequest, Autogenerated.EncryptResponse> duplexStream,
|
||||
Autogenerated.EncryptRequestOptions encryptRequestOptions,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
//Start with passing the metadata about the encryption request itself in the first message
|
||||
await duplexStream.RequestStream.WriteAsync(
|
||||
new Autogenerated.EncryptRequest { Options = encryptRequestOptions }, cancellationToken);
|
||||
|
||||
//Send the plaintext bytes in blocks in subsequent messages
|
||||
await using (var bufferedStream = new BufferedStream(plaintextStream, streamingBlockSizeInBytes))
|
||||
using var streamProcessor = new EncryptionStreamProcessor();
|
||||
try
|
||||
{
|
||||
var buffer = new byte[streamingBlockSizeInBytes];
|
||||
int bytesRead;
|
||||
ulong sequenceNumber = 0;
|
||||
streamProcessor.OnException += exceptionHandler;
|
||||
await streamProcessor.ProcessStreamAsync(plaintextStream, duplexStream, encryptRequestOptions,
|
||||
encryptionOptions.StreamingBlockSizeInBytes,
|
||||
cancellationToken);
|
||||
|
||||
while ((bytesRead =
|
||||
await bufferedStream.ReadAsync(buffer.AsMemory(0, streamingBlockSizeInBytes),
|
||||
cancellationToken)) !=
|
||||
0)
|
||||
await foreach (var value in streamProcessor.GetProcessedDataAsync(cancellationToken))
|
||||
{
|
||||
await duplexStream.RequestStream.WriteAsync(
|
||||
new Autogenerated.EncryptRequest
|
||||
{
|
||||
Payload = new Autogenerated.StreamPayload
|
||||
{
|
||||
Data = ByteString.CopyFrom(buffer, 0, bytesRead), Seq = sequenceNumber
|
||||
}
|
||||
}, cancellationToken);
|
||||
|
||||
//Increment the sequence number
|
||||
sequenceNumber++;
|
||||
yield return value;
|
||||
}
|
||||
}
|
||||
|
||||
//Send the completion message
|
||||
await duplexStream.RequestStream.CompleteAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the encrypted bytes from the encryption operation on the sidecar and returns as an enumerable stream.
|
||||
/// </summary>
|
||||
private async IAsyncEnumerable<ReadOnlyMemory<byte>> RetrieveEncryptedStreamAsync(
|
||||
AsyncDuplexStreamingCall<Autogenerated.EncryptRequest, Autogenerated.EncryptResponse> duplexStream,
|
||||
[EnumeratorCancellation] CancellationToken cancellationToken)
|
||||
{
|
||||
await foreach (var encryptResponse in duplexStream.ResponseStream.ReadAllAsync(cancellationToken)
|
||||
.ConfigureAwait(false))
|
||||
finally
|
||||
{
|
||||
yield return encryptResponse.Payload.Data.Memory;
|
||||
streamProcessor.OnException -= exceptionHandler;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[Obsolete(
|
||||
"The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
public override async Task<IAsyncEnumerable<ReadOnlyMemory<byte>>> DecryptAsync(string vaultResourceName,
|
||||
"As of Dapr v1.17, this method will be removed and should be used via the Dapr.Cryptography package on NuGet")]
|
||||
public override async IAsyncEnumerable<ReadOnlyMemory<byte>> DecryptAsync(string vaultResourceName,
|
||||
Stream ciphertextStream, string keyName,
|
||||
DecryptionOptions decryptionOptions, CancellationToken cancellationToken = default)
|
||||
DecryptionOptions decryptionOptions,
|
||||
[EnumeratorCancellation] CancellationToken cancellationToken = default)
|
||||
{
|
||||
ArgumentVerifier.ThrowIfNullOrEmpty(vaultResourceName, nameof(vaultResourceName));
|
||||
ArgumentVerifier.ThrowIfNullOrEmpty(keyName, nameof(keyName));
|
||||
ArgumentVerifier.ThrowIfNull(ciphertextStream, nameof(ciphertextStream));
|
||||
ArgumentVerifier.ThrowIfNull(decryptionOptions, nameof(decryptionOptions));
|
||||
decryptionOptions ??= new DecryptionOptions();
|
||||
|
||||
EventHandler<Exception> exceptionHandler = (_, ex) => throw ex;
|
||||
|
||||
var decryptRequestOptions = new Autogenerated.DecryptRequestOptions
|
||||
{
|
||||
ComponentName = vaultResourceName, KeyName = keyName
|
||||
ComponentName = vaultResourceName,
|
||||
KeyName = keyName
|
||||
};
|
||||
|
||||
var options = CreateCallOptions(headers: null, cancellationToken);
|
||||
var duplexStream = client.DecryptAlpha1(options);
|
||||
|
||||
//Run both operations at the same time, but return the output of the streaming values coming from the operation
|
||||
var receiveResult = Task.FromResult(RetrieveDecryptedStreamAsync(duplexStream, cancellationToken));
|
||||
return await Task.WhenAll(
|
||||
//Stream the ciphertext data to the sidecar in chunks
|
||||
SendCiphertextStreamAsync(ciphertextStream, decryptionOptions.StreamingBlockSizeInBytes,
|
||||
duplexStream, decryptRequestOptions, cancellationToken),
|
||||
//At the same time, retrieve the decrypted response from the sidecar
|
||||
receiveResult)
|
||||
//Return only the result of the `RetrieveEncryptedStreamAsync` method
|
||||
.ContinueWith(t => receiveResult.Result, cancellationToken);
|
||||
using var streamProcessor = new DecryptionStreamProcessor();
|
||||
try
|
||||
{
|
||||
streamProcessor.OnException += exceptionHandler;
|
||||
await streamProcessor.ProcessStreamAsync(ciphertextStream, duplexStream, decryptionOptions.StreamingBlockSizeInBytes,
|
||||
decryptRequestOptions,
|
||||
cancellationToken);
|
||||
|
||||
await foreach (var value in streamProcessor.GetProcessedDataAsync(cancellationToken))
|
||||
{
|
||||
yield return value;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
streamProcessor.OnException -= exceptionHandler;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[Obsolete(
|
||||
"The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
public override Task<IAsyncEnumerable<ReadOnlyMemory<byte>>> DecryptAsync(string vaultResourceName,
|
||||
"As of Dapr v1.17, this method will be removed and should be used via the Dapr.Cryptography package on NuGet")]
|
||||
public override IAsyncEnumerable<ReadOnlyMemory<byte>> DecryptAsync(string vaultResourceName,
|
||||
Stream ciphertextStream, string keyName, CancellationToken cancellationToken = default) =>
|
||||
DecryptAsync(vaultResourceName, ciphertextStream, keyName, new DecryptionOptions(),
|
||||
cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Sends the ciphertext bytes in chunks to the sidecar to be decrypted.
|
||||
/// </summary>
|
||||
private async Task SendCiphertextStreamAsync(Stream ciphertextStream,
|
||||
int streamingBlockSizeInBytes,
|
||||
AsyncDuplexStreamingCall<Autogenerated.DecryptRequest, Autogenerated.DecryptResponse> duplexStream,
|
||||
Autogenerated.DecryptRequestOptions decryptRequestOptions,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
//Start with passing the metadata about the decryption request itself in the first message
|
||||
await duplexStream.RequestStream.WriteAsync(
|
||||
new Autogenerated.DecryptRequest { Options = decryptRequestOptions }, cancellationToken);
|
||||
|
||||
//Send the ciphertext bytes in blocks in subsequent messages
|
||||
await using (var bufferedStream = new BufferedStream(ciphertextStream, streamingBlockSizeInBytes))
|
||||
{
|
||||
var buffer = new byte[streamingBlockSizeInBytes];
|
||||
int bytesRead;
|
||||
ulong sequenceNumber = 0;
|
||||
|
||||
while ((bytesRead =
|
||||
await bufferedStream.ReadAsync(buffer.AsMemory(0, streamingBlockSizeInBytes),
|
||||
cancellationToken)) != 0)
|
||||
{
|
||||
await duplexStream.RequestStream.WriteAsync(
|
||||
new Autogenerated.DecryptRequest
|
||||
{
|
||||
Payload = new Autogenerated.StreamPayload
|
||||
{
|
||||
Data = ByteString.CopyFrom(buffer, 0, bytesRead), Seq = sequenceNumber
|
||||
}
|
||||
}, cancellationToken);
|
||||
|
||||
//Increment the sequence number
|
||||
sequenceNumber++;
|
||||
}
|
||||
}
|
||||
|
||||
//Send the completion message
|
||||
await duplexStream.RequestStream.CompleteAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the decrypted bytes from the decryption operation on the sidecar and returns as an enumerable stream.
|
||||
/// </summary>
|
||||
private async IAsyncEnumerable<ReadOnlyMemory<byte>> RetrieveDecryptedStreamAsync(
|
||||
AsyncDuplexStreamingCall<Autogenerated.DecryptRequest, Autogenerated.DecryptResponse> duplexStream,
|
||||
[EnumeratorCancellation] CancellationToken cancellationToken)
|
||||
{
|
||||
await foreach (var decryptResponse in duplexStream.ResponseStream.ReadAllAsync(cancellationToken)
|
||||
.ConfigureAwait(false))
|
||||
{
|
||||
yield return decryptResponse.Payload.Data.Memory;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[Obsolete(
|
||||
"The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
"As of Dapr v1.17, this method will be removed and should be used via the Dapr.Cryptography package on NuGet")]
|
||||
public override async Task<ReadOnlyMemory<byte>> DecryptAsync(string vaultResourceName,
|
||||
ReadOnlyMemory<byte> ciphertextBytes, string keyName, DecryptionOptions decryptionOptions,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (MemoryMarshal.TryGetArray(ciphertextBytes, out var ciphertextSegment) && ciphertextSegment.Array != null)
|
||||
using var memoryStream = ciphertextBytes.CreateMemoryStream(true);
|
||||
|
||||
var decryptionResult = DecryptAsync(vaultResourceName, memoryStream, keyName, decryptionOptions, cancellationToken);
|
||||
|
||||
var bufferedResult = new ArrayBufferWriter<byte>();
|
||||
await foreach (var item in decryptionResult)
|
||||
{
|
||||
var decryptionResult = await DecryptAsync(vaultResourceName, new MemoryStream(ciphertextSegment.Array),
|
||||
keyName, decryptionOptions, cancellationToken);
|
||||
|
||||
var bufferedResult = new ArrayBufferWriter<byte>();
|
||||
await foreach (var item in decryptionResult.WithCancellation(cancellationToken))
|
||||
{
|
||||
bufferedResult.Write(item.Span);
|
||||
}
|
||||
|
||||
return bufferedResult.WrittenMemory;
|
||||
bufferedResult.Write(item.Span);
|
||||
}
|
||||
|
||||
throw new ArgumentException("The input instance doesn't have a valid underlying data store",
|
||||
nameof(ciphertextBytes));
|
||||
return bufferedResult.WrittenMemory;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[Obsolete(
|
||||
"The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
|
||||
"As of Dapr v1.17, this method will be removed and should be used via the Dapr.Cryptography package on NuGet")]
|
||||
public override async Task<ReadOnlyMemory<byte>> DecryptAsync(string vaultResourceName,
|
||||
ReadOnlyMemory<byte> ciphertextBytes, string keyName, CancellationToken cancellationToken = default) =>
|
||||
await DecryptAsync(vaultResourceName, ciphertextBytes, keyName,
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -77,13 +77,23 @@ internal sealed class DaprJobsGrpcClient : DaprJobsClient
|
|||
ArgumentNullException.ThrowIfNull(jobName, nameof(jobName));
|
||||
ArgumentNullException.ThrowIfNull(schedule, nameof(schedule));
|
||||
|
||||
var job = new Autogenerated.Job { Name = jobName, Schedule = schedule.ExpressionValue };
|
||||
var job = new Autogenerated.Job { Name = jobName };
|
||||
|
||||
//Set up the schedule (recurring or point in time)
|
||||
if (schedule.IsPointInTimeExpression)
|
||||
{
|
||||
job.DueTime = schedule.ExpressionValue;
|
||||
}
|
||||
else if (schedule.IsCronExpression || schedule.IsPrefixedPeriodExpression || schedule.IsDurationExpression)
|
||||
{
|
||||
job.Schedule = schedule.ExpressionValue;
|
||||
}
|
||||
|
||||
if (startingFrom is not null)
|
||||
{
|
||||
job.DueTime = ((DateTimeOffset)startingFrom).ToString("O");
|
||||
}
|
||||
|
||||
|
||||
if (repeats is not null)
|
||||
{
|
||||
if (repeats < 0)
|
||||
|
@ -153,13 +163,7 @@ internal sealed class DaprJobsGrpcClient : DaprJobsClient
|
|||
var envelope = new Autogenerated.GetJobRequest { Name = jobName };
|
||||
var grpcCallOptions = DaprClientUtilities.ConfigureGrpcCallOptions(typeof(DaprJobsClient).Assembly, this.DaprApiToken, cancellationToken);
|
||||
var response = await Client.GetJobAlpha1Async(envelope, grpcCallOptions);
|
||||
return new DaprJobDetails(new DaprJobSchedule(response.Job.Schedule))
|
||||
{
|
||||
DueTime = response.Job.DueTime is not null ? DateTime.Parse(response.Job.DueTime) : null,
|
||||
Ttl = response.Job.Ttl is not null ? DateTime.Parse(response.Job.Ttl) : null,
|
||||
RepeatCount = response.Job.Repeats == default ? null : (int?)response.Job.Repeats,
|
||||
Payload = response.Job.Data.ToByteArray()
|
||||
};
|
||||
return DeserializeJobResponse(response);
|
||||
}
|
||||
catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
|
@ -178,6 +182,29 @@ internal sealed class DaprJobsGrpcClient : DaprJobsClient
|
|||
throw new DaprException("Get job operation failed: the Dapr endpoint did not return the expected value.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Testable method for performing job response deserialization.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is exposed strictly for testing purposes.
|
||||
/// </remarks>
|
||||
/// <param name="response">The job response to deserialize.</param>
|
||||
/// <returns>The deserialized job response.</returns>
|
||||
internal static DaprJobDetails DeserializeJobResponse(Autogenerated.GetJobResponse response)
|
||||
{
|
||||
var schedule = DateTime.TryParse(response.Job.DueTime, out var dueTime)
|
||||
? DaprJobSchedule.FromDateTime(dueTime)
|
||||
: new DaprJobSchedule(response.Job.Schedule);
|
||||
|
||||
return new DaprJobDetails(schedule)
|
||||
{
|
||||
DueTime = !string.IsNullOrWhiteSpace(response.Job.DueTime) ? DateTime.Parse(response.Job.DueTime) : null,
|
||||
Ttl = !string.IsNullOrWhiteSpace(response.Job.Ttl) ? DateTime.Parse(response.Job.Ttl) : null,
|
||||
RepeatCount = (int?)response.Job.Repeats ?? 0,
|
||||
Payload = response.Job.Data?.ToByteArray() ?? null
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the specified job.
|
||||
/// </summary>
|
||||
|
|
|
@ -11,8 +11,6 @@
|
|||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
using System.Text.Json;
|
||||
using Dapr.Jobs.Models.Responses;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
|
||||
|
@ -29,41 +27,41 @@ public static class EndpointRouteBuilderExtensions
|
|||
/// </summary>
|
||||
/// <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
|
||||
/// parameters must be a nullable <see cref="string"/> for the jobName and a nullable <see cref="DaprJobDetails"/> with the
|
||||
/// payload details, 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>
|
||||
/// parameters must be a <see cref="string"/> for the jobName and the originally registered ReadOnlyMemory<byte> with the
|
||||
/// payload value, but otherwise can be populated with additional services to be injected into the delegate.</param>
|
||||
/// <param name="timeout">Optional timeout to apply to a per-request cancellation token.</param>
|
||||
public static IEndpointRouteBuilder MapDaprScheduledJobHandler(this IEndpointRouteBuilder endpoints,
|
||||
Delegate action, CancellationToken cancellationToken = default)
|
||||
Delegate action, TimeSpan? timeout = null)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(endpoints, nameof(endpoints));
|
||||
ArgumentNullException.ThrowIfNull(action, nameof(action));
|
||||
|
||||
endpoints.MapPost("/job/{jobName}", async context =>
|
||||
{
|
||||
var jobName = (string?)context.Request.RouteValues["jobName"];
|
||||
DaprJobDetails? jobPayload = null;
|
||||
|
||||
if (context.Request.ContentLength is > 0)
|
||||
//Retrieve the name of the job from the request path
|
||||
var jobName = string.Empty;
|
||||
if (context.Request.RouteValues.TryGetValue("jobName", out var capturedJobName))
|
||||
{
|
||||
using var reader = new StreamReader(context.Request.Body);
|
||||
var body = await reader.ReadToEndAsync();
|
||||
|
||||
try
|
||||
{
|
||||
var deserializedJobPayload = JsonSerializer.Deserialize<DeserializableDaprJobDetails>(body);
|
||||
jobPayload = deserializedJobPayload?.ToType() ?? null;
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
jobPayload = null;
|
||||
}
|
||||
jobName = (string)capturedJobName!;
|
||||
}
|
||||
|
||||
var parameters = new Dictionary<Type, object?>
|
||||
//Retrieve the job payload from the request body
|
||||
ReadOnlyMemory<byte> payload = new();
|
||||
if (context.Request.ContentLength is > 0)
|
||||
{
|
||||
using var streamContent = new StreamContent(context.Request.Body);
|
||||
payload = await streamContent.ReadAsByteArrayAsync(CancellationToken.None);
|
||||
}
|
||||
|
||||
using var cts = timeout.HasValue
|
||||
? new CancellationTokenSource(timeout.Value)
|
||||
: new CancellationTokenSource();
|
||||
|
||||
var parameters = new Dictionary<Type, object>
|
||||
{
|
||||
{ typeof(string), jobName },
|
||||
{ typeof(DaprJobDetails), jobPayload },
|
||||
{ typeof(CancellationToken), CancellationToken.None }
|
||||
{ typeof(ReadOnlyMemory<byte>), payload },
|
||||
{ typeof(CancellationToken), cts.Token }
|
||||
};
|
||||
|
||||
var actionParameters = action.Method.GetParameters();
|
||||
|
|
|
@ -67,7 +67,7 @@ internal static class TimeSpanExtensions
|
|||
/// <returns>True if the string represents a parseable interval duration; false if not.</returns>
|
||||
public static bool IsDurationString(this string interval)
|
||||
{
|
||||
interval = interval.Replace("ms", "q");
|
||||
interval = interval.Replace("ms", "q").Replace("@every ", string.Empty);
|
||||
return hourRegex.Match(interval).Success ||
|
||||
minuteRegex.Match(interval).Success ||
|
||||
secondRegex.Match(interval).Success ||
|
||||
|
|
|
@ -65,11 +65,8 @@ public sealed class DaprJobSchedule
|
|||
/// </summary>
|
||||
/// <param name="scheduledTime">The date and time when the job should be triggered.</param>
|
||||
/// <returns></returns>
|
||||
public static DaprJobSchedule FromDateTime(DateTimeOffset scheduledTime)
|
||||
{
|
||||
return new DaprJobSchedule(scheduledTime.ToString("O"));
|
||||
}
|
||||
|
||||
public static DaprJobSchedule FromDateTime(DateTimeOffset scheduledTime) => new(scheduledTime.ToString("O"));
|
||||
|
||||
/// <summary>
|
||||
/// Specifies a schedule using a Cron-like expression or '@' prefixed period strings.
|
||||
/// </summary>
|
||||
|
@ -86,10 +83,7 @@ public sealed class DaprJobSchedule
|
|||
/// Specifies a schedule using a duration interval articulated via a <see cref="TimeSpan"/>.
|
||||
/// </summary>
|
||||
/// <param name="duration">The duration interval.</param>
|
||||
public static DaprJobSchedule FromDuration(TimeSpan duration)
|
||||
{
|
||||
return new DaprJobSchedule(duration.ToDurationString());
|
||||
}
|
||||
public static DaprJobSchedule FromDuration(TimeSpan duration) => new($"@every {duration.ToDurationString()}");
|
||||
|
||||
/// <summary>
|
||||
/// Specifies a schedule in which the job is triggered to run once a year.
|
||||
|
@ -104,7 +98,7 @@ public sealed class DaprJobSchedule
|
|||
/// <summary>
|
||||
/// Specifies a schedule in which the job is triggered weekly.
|
||||
/// </summary>
|
||||
public static DaprJobSchedule Weekly { get; } =new DaprJobSchedule("@weekly");
|
||||
public static DaprJobSchedule Weekly { get; } = new DaprJobSchedule("@weekly");
|
||||
|
||||
/// <summary>
|
||||
/// Specifies a schedule in which the job is triggered daily.
|
||||
|
|
|
@ -11,9 +11,6 @@
|
|||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
using System.Text.Json.Serialization;
|
||||
using Dapr.Jobs.JsonConverters;
|
||||
|
||||
namespace Dapr.Jobs.Models.Responses;
|
||||
|
||||
/// <summary>
|
||||
|
@ -46,52 +43,3 @@ public sealed record DaprJobDetails(DaprJobSchedule Schedule)
|
|||
/// </summary>
|
||||
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
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1225,7 +1225,6 @@ message Job {
|
|||
// Systemd timer style cron accepts 6 fields:
|
||||
// seconds | minutes | hours | day of month | month | day of week
|
||||
// 0-59 | 0-59 | 0-23 | 1-31 | 1-12/jan-dec | 0-6/sun-sat
|
||||
|
||||
//
|
||||
// "0 30 * * * *" - every hour on the half hour
|
||||
// "0 15 3 * * *" - every day at 03:15
|
||||
|
@ -1320,8 +1319,8 @@ message ConversationRequest {
|
|||
}
|
||||
|
||||
message ConversationInput {
|
||||
// The message to send to the llm
|
||||
string message = 1;
|
||||
// The content to send to the llm
|
||||
string content = 1;
|
||||
|
||||
// The role to set for the message
|
||||
optional string role = 2;
|
||||
|
@ -1345,4 +1344,4 @@ message ConversationResponse {
|
|||
|
||||
// An array of results.
|
||||
repeated ConversationResult outputs = 2;
|
||||
}
|
||||
}
|
|
@ -7,7 +7,6 @@
|
|||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="FluentAssertions"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" />
|
||||
<PackageReference Include="xunit" />
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="FluentAssertions" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" />
|
||||
<PackageReference Include="xunit" />
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="FluentAssertions" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.TestHost" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" />
|
||||
<PackageReference Include="Moq" />
|
||||
|
|
|
@ -11,44 +11,43 @@
|
|||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
namespace Dapr.Actors.Test
|
||||
namespace Dapr.Actors.Test;
|
||||
|
||||
using System.Threading.Tasks;
|
||||
using Dapr.Actors.Builder;
|
||||
using Dapr.Actors.Communication;
|
||||
using Dapr.Actors.Description;
|
||||
using Dapr.Actors.Runtime;
|
||||
using Xunit;
|
||||
|
||||
/// <summary>
|
||||
/// Test class for Actor Code builder.
|
||||
/// </summary>
|
||||
public class ActorCodeBuilderTests
|
||||
{
|
||||
using System.Threading.Tasks;
|
||||
using Dapr.Actors.Builder;
|
||||
using Dapr.Actors.Communication;
|
||||
using Dapr.Actors.Description;
|
||||
using Dapr.Actors.Runtime;
|
||||
using Xunit;
|
||||
|
||||
/// <summary>
|
||||
/// Test class for Actor Code builder.
|
||||
/// Tests Proxy Generation.
|
||||
/// </summary>
|
||||
public class ActorCodeBuilderTests
|
||||
[Fact]
|
||||
public void TestBuildActorProxyGenerator()
|
||||
{
|
||||
/// <summary>
|
||||
/// Tests Proxy Generation.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void TestBuildActorProxyGenerator()
|
||||
{
|
||||
ActorCodeBuilder.GetOrCreateProxyGenerator(typeof(ITestActor));
|
||||
}
|
||||
ActorCodeBuilder.GetOrCreateProxyGenerator(typeof(ITestActor));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ActorCodeBuilder_BuildDispatcher()
|
||||
{
|
||||
var host = ActorHost.CreateForTest<TestActor>();
|
||||
[Fact]
|
||||
public async Task ActorCodeBuilder_BuildDispatcher()
|
||||
{
|
||||
var host = ActorHost.CreateForTest<TestActor>();
|
||||
|
||||
var dispatcher = ActorCodeBuilder.GetOrCreateMethodDispatcher(typeof(ITestActor));
|
||||
var methodId = MethodDescription.Create("test", typeof(ITestActor).GetMethod("GetCountAsync"), true).Id;
|
||||
var dispatcher = ActorCodeBuilder.GetOrCreateMethodDispatcher(typeof(ITestActor));
|
||||
var methodId = MethodDescription.Create("test", typeof(ITestActor).GetMethod("GetCountAsync"), true).Id;
|
||||
|
||||
var impl = new TestActor(host);
|
||||
var request = new ActorRequestMessageBody(0);
|
||||
var response = new WrappedRequestMessageFactory();
|
||||
var impl = new TestActor(host);
|
||||
var request = new ActorRequestMessageBody(0);
|
||||
var response = new WrappedRequestMessageFactory();
|
||||
|
||||
var body = (WrappedMessage)await dispatcher.DispatchAsync(impl, methodId, request, response, default);
|
||||
dynamic bodyValue = body.Value;
|
||||
Assert.Equal(5, (int)bodyValue.retVal);
|
||||
}
|
||||
var body = (WrappedMessage)await dispatcher.DispatchAsync(impl, methodId, request, response, default);
|
||||
dynamic bodyValue = body.Value;
|
||||
Assert.Equal(5, (int)bodyValue.retVal);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,194 +11,193 @@
|
|||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
namespace Dapr.Actors.Test
|
||||
namespace Dapr.Actors.Test;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Xunit;
|
||||
|
||||
/// <summary>
|
||||
/// Contains tests for Actor ID.
|
||||
/// </summary>
|
||||
public class ActorIdTests
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Xunit;
|
||||
public static readonly IEnumerable<object[]> CompareToValues = new List<object[]>
|
||||
{
|
||||
new object[]
|
||||
{
|
||||
new ActorId("1"),
|
||||
null,
|
||||
1,
|
||||
},
|
||||
new object[]
|
||||
{
|
||||
new ActorId("1"),
|
||||
new ActorId("1"),
|
||||
0,
|
||||
},
|
||||
new object[]
|
||||
{
|
||||
new ActorId("1"),
|
||||
new ActorId("2"),
|
||||
-1,
|
||||
},
|
||||
new object[]
|
||||
{
|
||||
new ActorId("2"),
|
||||
new ActorId("1"),
|
||||
1,
|
||||
},
|
||||
};
|
||||
|
||||
public static readonly IEnumerable<object[]> EqualsValues = new List<object[]>
|
||||
{
|
||||
new object[]
|
||||
{
|
||||
new ActorId("1"),
|
||||
null,
|
||||
false,
|
||||
},
|
||||
new object[]
|
||||
{
|
||||
new ActorId("1"),
|
||||
new ActorId("1"),
|
||||
true,
|
||||
},
|
||||
new object[]
|
||||
{
|
||||
new ActorId("1"),
|
||||
new ActorId("2"),
|
||||
false,
|
||||
},
|
||||
};
|
||||
|
||||
public static readonly IEnumerable<object[]> EqualsOperatorValues = new List<object[]>
|
||||
{
|
||||
new object[]
|
||||
{
|
||||
null,
|
||||
null,
|
||||
true,
|
||||
},
|
||||
new object[]
|
||||
{
|
||||
new ActorId("1"),
|
||||
null,
|
||||
false,
|
||||
},
|
||||
new object[]
|
||||
{
|
||||
null,
|
||||
new ActorId("1"),
|
||||
false,
|
||||
},
|
||||
new object[]
|
||||
{
|
||||
new ActorId("1"),
|
||||
new ActorId("1"),
|
||||
true,
|
||||
},
|
||||
new object[]
|
||||
{
|
||||
new ActorId("1"),
|
||||
new ActorId("2"),
|
||||
false,
|
||||
},
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Contains tests for Actor ID.
|
||||
/// Throw exception if id is null.
|
||||
/// </summary>
|
||||
public class ActorIdTests
|
||||
[Theory]
|
||||
[InlineData(null)]
|
||||
[InlineData("")]
|
||||
[InlineData(" ")]
|
||||
public void Initialize_New_ActorId_Object_With_Null_Or_Whitespace_Id(string id)
|
||||
{
|
||||
public static readonly IEnumerable<object[]> CompareToValues = new List<object[]>
|
||||
{
|
||||
new object[]
|
||||
{
|
||||
new ActorId("1"),
|
||||
null,
|
||||
1,
|
||||
},
|
||||
new object[]
|
||||
{
|
||||
new ActorId("1"),
|
||||
new ActorId("1"),
|
||||
0,
|
||||
},
|
||||
new object[]
|
||||
{
|
||||
new ActorId("1"),
|
||||
new ActorId("2"),
|
||||
-1,
|
||||
},
|
||||
new object[]
|
||||
{
|
||||
new ActorId("2"),
|
||||
new ActorId("1"),
|
||||
1,
|
||||
},
|
||||
};
|
||||
Assert.Throws<ArgumentException>(() => new ActorId(id));
|
||||
}
|
||||
|
||||
public static readonly IEnumerable<object[]> EqualsValues = new List<object[]>
|
||||
{
|
||||
new object[]
|
||||
{
|
||||
new ActorId("1"),
|
||||
null,
|
||||
false,
|
||||
},
|
||||
new object[]
|
||||
{
|
||||
new ActorId("1"),
|
||||
new ActorId("1"),
|
||||
true,
|
||||
},
|
||||
new object[]
|
||||
{
|
||||
new ActorId("1"),
|
||||
new ActorId("2"),
|
||||
false,
|
||||
},
|
||||
};
|
||||
[Theory]
|
||||
[InlineData("one")]
|
||||
[InlineData("123")]
|
||||
public void Get_Id(string id)
|
||||
{
|
||||
ActorId actorId = new ActorId(id);
|
||||
Assert.Equal(id, actorId.GetId());
|
||||
}
|
||||
|
||||
public static readonly IEnumerable<object[]> EqualsOperatorValues = new List<object[]>
|
||||
{
|
||||
new object[]
|
||||
{
|
||||
null,
|
||||
null,
|
||||
true,
|
||||
},
|
||||
new object[]
|
||||
{
|
||||
new ActorId("1"),
|
||||
null,
|
||||
false,
|
||||
},
|
||||
new object[]
|
||||
{
|
||||
null,
|
||||
new ActorId("1"),
|
||||
false,
|
||||
},
|
||||
new object[]
|
||||
{
|
||||
new ActorId("1"),
|
||||
new ActorId("1"),
|
||||
true,
|
||||
},
|
||||
new object[]
|
||||
{
|
||||
new ActorId("1"),
|
||||
new ActorId("2"),
|
||||
false,
|
||||
},
|
||||
};
|
||||
[Theory]
|
||||
[InlineData("one")]
|
||||
[InlineData("123")]
|
||||
public void Verify_ToString(string id)
|
||||
{
|
||||
ActorId actorId = new ActorId(id);
|
||||
Assert.Equal(id, actorId.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Throw exception if id is null.
|
||||
/// </summary>
|
||||
[Theory]
|
||||
[InlineData(null)]
|
||||
[InlineData("")]
|
||||
[InlineData(" ")]
|
||||
public void Initialize_New_ActorId_Object_With_Null_Or_Whitespace_Id(string id)
|
||||
{
|
||||
Assert.Throws<ArgumentException>(() => new ActorId(id));
|
||||
}
|
||||
/// <summary>
|
||||
/// Verify Equals method by comparing two actorIds.
|
||||
/// </summary>
|
||||
/// <param name="id1">The first actorId to compare.</param>
|
||||
/// <param name="id2">The second actorId to compare, or null.</param>
|
||||
/// <param name="expectedValue">Expected value from comparison.</param>
|
||||
[Theory]
|
||||
[MemberData(nameof(EqualsValues))]
|
||||
public void Verify_Equals_By_Object(object id1, object id2, bool expectedValue)
|
||||
{
|
||||
Assert.Equal(expectedValue, id1.Equals(id2));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("one")]
|
||||
[InlineData("123")]
|
||||
public void Get_Id(string id)
|
||||
{
|
||||
ActorId actorId = new ActorId(id);
|
||||
Assert.Equal(id, actorId.GetId());
|
||||
}
|
||||
/// <summary>
|
||||
/// Verify Equals method by comparing two actorIds.
|
||||
/// </summary>
|
||||
/// <param name="id1">The first actorId to compare.</param>
|
||||
/// <param name="id2">The second actorId to compare, or null.</param>
|
||||
/// <param name="expectedValue">Expected value from comparison.</param>
|
||||
[Theory]
|
||||
[MemberData(nameof(EqualsValues))]
|
||||
public void Verify_Equals_By_ActorId(ActorId id1, ActorId id2, bool expectedValue)
|
||||
{
|
||||
Assert.Equal(expectedValue, id1.Equals(id2));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("one")]
|
||||
[InlineData("123")]
|
||||
public void Verify_ToString(string id)
|
||||
{
|
||||
ActorId actorId = new ActorId(id);
|
||||
Assert.Equal(id, actorId.ToString());
|
||||
}
|
||||
/// <summary>
|
||||
/// Verify equals operator by comparing two actorIds.
|
||||
/// </summary>
|
||||
/// <param name="id1">The first actorId to compare, or null.</param>
|
||||
/// <param name="id2">The second actorId to compare, or null.</param>
|
||||
/// <param name="expectedValue">Expected value from comparison.</param>
|
||||
[Theory]
|
||||
[MemberData(nameof(EqualsOperatorValues))]
|
||||
public void Verify_Equals_Operator(ActorId id1, ActorId id2, bool expectedValue)
|
||||
{
|
||||
Assert.Equal(expectedValue, id1 == id2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify Equals method by comparing two actorIds.
|
||||
/// </summary>
|
||||
/// <param name="id1">The first actorId to compare.</param>
|
||||
/// <param name="id2">The second actorId to compare, or null.</param>
|
||||
/// <param name="expectedValue">Expected value from comparison.</param>
|
||||
[Theory]
|
||||
[MemberData(nameof(EqualsValues))]
|
||||
public void Verify_Equals_By_Object(object id1, object id2, bool expectedValue)
|
||||
{
|
||||
Assert.Equal(expectedValue, id1.Equals(id2));
|
||||
}
|
||||
/// <summary>
|
||||
/// Verify not equals operator by comparing two actorIds.
|
||||
/// </summary>
|
||||
/// <param name="id1">The first actorId to compare, or null.</param>
|
||||
/// <param name="id2">The second actorId to compare, or null.</param>
|
||||
/// <param name="expectedValue">Expected value from comparison.</param>
|
||||
[Theory]
|
||||
[MemberData(nameof(EqualsOperatorValues))]
|
||||
public void Verify_Not_Equals_Operator(ActorId id1, ActorId id2, bool expectedValue)
|
||||
{
|
||||
Assert.Equal(!expectedValue, id1 != id2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify Equals method by comparing two actorIds.
|
||||
/// </summary>
|
||||
/// <param name="id1">The first actorId to compare.</param>
|
||||
/// <param name="id2">The second actorId to compare, or null.</param>
|
||||
/// <param name="expectedValue">Expected value from comparison.</param>
|
||||
[Theory]
|
||||
[MemberData(nameof(EqualsValues))]
|
||||
public void Verify_Equals_By_ActorId(ActorId id1, ActorId id2, bool expectedValue)
|
||||
{
|
||||
Assert.Equal(expectedValue, id1.Equals(id2));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify equals operator by comparing two actorIds.
|
||||
/// </summary>
|
||||
/// <param name="id1">The first actorId to compare, or null.</param>
|
||||
/// <param name="id2">The second actorId to compare, or null.</param>
|
||||
/// <param name="expectedValue">Expected value from comparison.</param>
|
||||
[Theory]
|
||||
[MemberData(nameof(EqualsOperatorValues))]
|
||||
public void Verify_Equals_Operator(ActorId id1, ActorId id2, bool expectedValue)
|
||||
{
|
||||
Assert.Equal(expectedValue, id1 == id2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify not equals operator by comparing two actorIds.
|
||||
/// </summary>
|
||||
/// <param name="id1">The first actorId to compare, or null.</param>
|
||||
/// <param name="id2">The second actorId to compare, or null.</param>
|
||||
/// <param name="expectedValue">Expected value from comparison.</param>
|
||||
[Theory]
|
||||
[MemberData(nameof(EqualsOperatorValues))]
|
||||
public void Verify_Not_Equals_Operator(ActorId id1, ActorId id2, bool expectedValue)
|
||||
{
|
||||
Assert.Equal(!expectedValue, id1 != id2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify CompareTo method by comparing two actorIds.
|
||||
/// </summary>
|
||||
/// <param name="id1">The first actorId to compare.</param>
|
||||
/// <param name="id2">The second actorId to compare, or null.</param>
|
||||
/// <param name="expectedValue">Expected value from comparison.</param>
|
||||
[Theory]
|
||||
[MemberData(nameof(CompareToValues))]
|
||||
public void Verify_CompareTo(ActorId id1, ActorId id2, int expectedValue)
|
||||
{
|
||||
Assert.Equal(expectedValue, id1.CompareTo(id2));
|
||||
}
|
||||
/// <summary>
|
||||
/// Verify CompareTo method by comparing two actorIds.
|
||||
/// </summary>
|
||||
/// <param name="id1">The first actorId to compare.</param>
|
||||
/// <param name="id2">The second actorId to compare, or null.</param>
|
||||
/// <param name="expectedValue">Expected value from comparison.</param>
|
||||
[Theory]
|
||||
[MemberData(nameof(CompareToValues))]
|
||||
public void Verify_CompareTo(ActorId id1, ActorId id2, int expectedValue)
|
||||
{
|
||||
Assert.Equal(expectedValue, id1.CompareTo(id2));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,51 +11,50 @@
|
|||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
namespace Dapr.Actors.Test
|
||||
namespace Dapr.Actors.Test;
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
|
||||
/// <summary>
|
||||
/// Contains tests for Actor method invocation exceptions.
|
||||
/// </summary>
|
||||
public class ActorMethodInvocationExceptionTests
|
||||
{
|
||||
using System;
|
||||
using System.IO;
|
||||
using FluentAssertions;
|
||||
using Xunit;
|
||||
|
||||
/// <summary>
|
||||
/// Contains tests for Actor method invocation exceptions.
|
||||
/// This test will verify:
|
||||
/// 1) the path for serialization and deserialization of the remote exception
|
||||
/// 2) and validating the inner exception.
|
||||
/// </summary>
|
||||
public class ActorMethodInvocationExceptionTests
|
||||
[Fact]
|
||||
public void TestThrowActorMethodInvocationException()
|
||||
{
|
||||
/// <summary>
|
||||
/// This test will verify:
|
||||
/// 1) the path for serialization and deserialization of the remote exception
|
||||
/// 2) and validating the inner exception.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void TestThrowActorMethodInvocationException()
|
||||
{
|
||||
// Create Remote Actor Method test Exception
|
||||
var message = "Remote Actor encountered an exception";
|
||||
var innerMessage = "Bad time zone";
|
||||
var exception = new InvalidOperationException(message, new InvalidTimeZoneException(innerMessage));
|
||||
// Create Remote Actor Method test Exception
|
||||
var message = "Remote Actor encountered an exception";
|
||||
var innerMessage = "Bad time zone";
|
||||
var exception = new InvalidOperationException(message, new InvalidTimeZoneException(innerMessage));
|
||||
|
||||
// Create Serialized Exception
|
||||
var serializedException = ActorInvokeException.FromException(exception);
|
||||
// Create Serialized Exception
|
||||
var serializedException = ActorInvokeException.FromException(exception);
|
||||
|
||||
// De Serialize Exception
|
||||
var isDeserialzied = ActorInvokeException.ToException(
|
||||
new MemoryStream(serializedException),
|
||||
out var remoteMethodException);
|
||||
isDeserialzied.Should().BeTrue();
|
||||
var ex = this.ThrowRemoteException(message, remoteMethodException);
|
||||
ex.Should().BeOfType<ActorMethodInvocationException>();
|
||||
ex.InnerException.Should().BeOfType<ActorInvokeException>();
|
||||
((ActorInvokeException)ex.InnerException).ActualExceptionType.Should().Be("System.InvalidOperationException");
|
||||
ex.InnerException.InnerException.Should().BeNull();
|
||||
ex.Message.Should().Be(message);
|
||||
ex.InnerException.Message.Should().Be(message);
|
||||
}
|
||||
// De Serialize Exception
|
||||
var isDeserialzied = ActorInvokeException.ToException(
|
||||
new MemoryStream(serializedException),
|
||||
out var remoteMethodException);
|
||||
isDeserialzied.ShouldBeTrue();
|
||||
var ex = this.ThrowRemoteException(message, remoteMethodException);
|
||||
ex.ShouldBeOfType<ActorMethodInvocationException>();
|
||||
ex.InnerException.ShouldBeOfType<ActorInvokeException>();
|
||||
((ActorInvokeException)ex.InnerException).ActualExceptionType.ShouldBe("System.InvalidOperationException");
|
||||
ex.InnerException.InnerException.ShouldBeNull();
|
||||
ex.Message.ShouldBe(message);
|
||||
ex.InnerException.Message.ShouldBe(message);
|
||||
}
|
||||
|
||||
private Exception ThrowRemoteException(string message, Exception exception)
|
||||
{
|
||||
return new ActorMethodInvocationException(message, exception, false);
|
||||
}
|
||||
private Exception ThrowRemoteException(string message, Exception exception)
|
||||
{
|
||||
return new ActorMethodInvocationException(message, exception, false);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,31 +11,30 @@
|
|||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
namespace Dapr.Actors.Client
|
||||
namespace Dapr.Actors.Client;
|
||||
|
||||
using System;
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
|
||||
/// <summary>
|
||||
/// Test class for Actor Code builder.
|
||||
/// </summary>
|
||||
public class ActorProxyOptionsTests
|
||||
{
|
||||
using System;
|
||||
using FluentAssertions;
|
||||
using Xunit;
|
||||
|
||||
/// <summary>
|
||||
/// Test class for Actor Code builder.
|
||||
/// </summary>
|
||||
public class ActorProxyOptionsTests
|
||||
[Fact]
|
||||
public void DefaultConstructor_Succeeds()
|
||||
{
|
||||
[Fact]
|
||||
public void DefaultConstructor_Succeeds()
|
||||
{
|
||||
var options = new ActorProxyOptions();
|
||||
Assert.NotNull(options);
|
||||
}
|
||||
var options = new ActorProxyOptions();
|
||||
Assert.NotNull(options);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SerializerOptionsCantBeNull_Fails()
|
||||
{
|
||||
var options = new ActorProxyOptions();
|
||||
Action action = () => options.JsonSerializerOptions = null;
|
||||
[Fact]
|
||||
public void SerializerOptionsCantBeNull_Fails()
|
||||
{
|
||||
var options = new ActorProxyOptions();
|
||||
Action action = () => options.JsonSerializerOptions = null;
|
||||
|
||||
action.Should().Throw<ArgumentNullException>();
|
||||
}
|
||||
action.ShouldThrow<ArgumentNullException>();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,113 +11,112 @@
|
|||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
namespace Dapr.Actors.Client
|
||||
namespace Dapr.Actors.Client;
|
||||
|
||||
using System;
|
||||
using System.Text.Json;
|
||||
using Dapr.Actors.Test;
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
|
||||
/// <summary>
|
||||
/// Test class for Actor Code builder.
|
||||
/// </summary>
|
||||
public class ActorProxyTests
|
||||
{
|
||||
using System;
|
||||
using System.Text.Json;
|
||||
using Dapr.Actors.Test;
|
||||
using FluentAssertions;
|
||||
using Xunit;
|
||||
|
||||
/// <summary>
|
||||
/// Test class for Actor Code builder.
|
||||
/// Tests Proxy Creation.
|
||||
/// </summary>
|
||||
public class ActorProxyTests
|
||||
[Fact]
|
||||
public void Create_WithIdAndActorTypeString_Succeeds()
|
||||
{
|
||||
/// <summary>
|
||||
/// Tests Proxy Creation.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Create_WithIdAndActorTypeString_Succeeds()
|
||||
var actorId = new ActorId("abc");
|
||||
var proxy = ActorProxy.Create(actorId, "TestActor");
|
||||
Assert.NotNull(proxy);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Create_WithValidActorInterface_Succeeds()
|
||||
{
|
||||
var actorId = new ActorId("abc");
|
||||
var proxy = ActorProxy.Create(actorId, typeof(ITestActor), "TestActor");
|
||||
Assert.NotNull(proxy);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Create_WithInvalidType_ThrowsArgumentException()
|
||||
{
|
||||
var actorId = new ActorId("abc");
|
||||
Action action = () => ActorProxy.Create(actorId, typeof(ActorId), "TestActor");
|
||||
action.ShouldThrow<ArgumentException>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Create_WithCustomSerializerOnDefaultActorProxyFactory_Succeeds()
|
||||
{
|
||||
var factory = new ActorProxyFactory();
|
||||
factory.DefaultOptions.JsonSerializerOptions = new JsonSerializerOptions();
|
||||
|
||||
var actorId = new ActorId("abc");
|
||||
var proxy = (ActorProxy)factory.Create(actorId, "TestActor");
|
||||
|
||||
Assert.Same(factory.DefaultOptions.JsonSerializerOptions, proxy.JsonSerializerOptions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Create_WithCustomSerializerArgument_Succeeds()
|
||||
{
|
||||
var options = new ActorProxyOptions()
|
||||
{
|
||||
var actorId = new ActorId("abc");
|
||||
var proxy = ActorProxy.Create(actorId, "TestActor");
|
||||
Assert.NotNull(proxy);
|
||||
}
|
||||
JsonSerializerOptions = new JsonSerializerOptions()
|
||||
};
|
||||
|
||||
[Fact]
|
||||
public void Create_WithValidActorInterface_Succeeds()
|
||||
var actorId = new ActorId("abc");
|
||||
var proxy = (ActorProxy)ActorProxy.Create(actorId, typeof(ITestActor), "TestActor", options);
|
||||
|
||||
Assert.Same(options.JsonSerializerOptions, proxy.JsonSerializerOptions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateGeneric_WithValidActorInterface_Succeeds()
|
||||
{
|
||||
var actorId = new ActorId("abc");
|
||||
var proxy = ActorProxy.Create<ITestActor>(actorId, "TestActor");
|
||||
Assert.NotNull(proxy);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateGeneric_WithCustomSerializerOnDefaultActorProxyFactory_Succeeds()
|
||||
{
|
||||
var factory = new ActorProxyFactory();
|
||||
factory.DefaultOptions.JsonSerializerOptions = new JsonSerializerOptions();
|
||||
|
||||
var actorId = new ActorId("abc");
|
||||
var proxy = (ActorProxy)factory.CreateActorProxy<ITestActor>(actorId, "TestActor");
|
||||
|
||||
Assert.Same(factory.DefaultOptions.JsonSerializerOptions, proxy.JsonSerializerOptions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateGeneric_WithCustomSerializerArgument_Succeeds()
|
||||
{
|
||||
var options = new ActorProxyOptions()
|
||||
{
|
||||
var actorId = new ActorId("abc");
|
||||
var proxy = ActorProxy.Create(actorId, typeof(ITestActor), "TestActor");
|
||||
Assert.NotNull(proxy);
|
||||
}
|
||||
JsonSerializerOptions = new JsonSerializerOptions()
|
||||
};
|
||||
|
||||
[Fact]
|
||||
public void Create_WithInvalidType_ThrowsArgumentException()
|
||||
{
|
||||
var actorId = new ActorId("abc");
|
||||
Action action = () => ActorProxy.Create(actorId, typeof(ActorId), "TestActor");
|
||||
action.Should().Throw<ArgumentException>();
|
||||
}
|
||||
var actorId = new ActorId("abc");
|
||||
var proxy = (ActorProxy)ActorProxy.Create<ITestActor>(actorId, "TestActor", options);
|
||||
|
||||
[Fact]
|
||||
public void Create_WithCustomSerializerOnDefaultActorProxyFactory_Succeeds()
|
||||
{
|
||||
var factory = new ActorProxyFactory();
|
||||
factory.DefaultOptions.JsonSerializerOptions = new JsonSerializerOptions();
|
||||
Assert.Same(options.JsonSerializerOptions, proxy.JsonSerializerOptions);
|
||||
}
|
||||
|
||||
var actorId = new ActorId("abc");
|
||||
var proxy = (ActorProxy)factory.Create(actorId, "TestActor");
|
||||
[Fact]
|
||||
public void SetActorProxyFactoryDefaultOptions_ToNull_ThrowsArgumentNullException()
|
||||
{
|
||||
var factory = new ActorProxyFactory();
|
||||
Action action = () => factory.DefaultOptions = null;
|
||||
|
||||
Assert.Same(factory.DefaultOptions.JsonSerializerOptions, proxy.JsonSerializerOptions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Create_WithCustomSerializerArgument_Succeeds()
|
||||
{
|
||||
var options = new ActorProxyOptions()
|
||||
{
|
||||
JsonSerializerOptions = new JsonSerializerOptions()
|
||||
};
|
||||
|
||||
var actorId = new ActorId("abc");
|
||||
var proxy = (ActorProxy)ActorProxy.Create(actorId, typeof(ITestActor), "TestActor", options);
|
||||
|
||||
Assert.Same(options.JsonSerializerOptions, proxy.JsonSerializerOptions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateGeneric_WithValidActorInterface_Succeeds()
|
||||
{
|
||||
var actorId = new ActorId("abc");
|
||||
var proxy = ActorProxy.Create<ITestActor>(actorId, "TestActor");
|
||||
Assert.NotNull(proxy);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateGeneric_WithCustomSerializerOnDefaultActorProxyFactory_Succeeds()
|
||||
{
|
||||
var factory = new ActorProxyFactory();
|
||||
factory.DefaultOptions.JsonSerializerOptions = new JsonSerializerOptions();
|
||||
|
||||
var actorId = new ActorId("abc");
|
||||
var proxy = (ActorProxy)factory.CreateActorProxy<ITestActor>(actorId, "TestActor");
|
||||
|
||||
Assert.Same(factory.DefaultOptions.JsonSerializerOptions, proxy.JsonSerializerOptions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateGeneric_WithCustomSerializerArgument_Succeeds()
|
||||
{
|
||||
var options = new ActorProxyOptions()
|
||||
{
|
||||
JsonSerializerOptions = new JsonSerializerOptions()
|
||||
};
|
||||
|
||||
var actorId = new ActorId("abc");
|
||||
var proxy = (ActorProxy)ActorProxy.Create<ITestActor>(actorId, "TestActor", options);
|
||||
|
||||
Assert.Same(options.JsonSerializerOptions, proxy.JsonSerializerOptions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SetActorProxyFactoryDefaultOptions_ToNull_ThrowsArgumentNullException()
|
||||
{
|
||||
var factory = new ActorProxyFactory();
|
||||
Action action = () => factory.DefaultOptions = null;
|
||||
|
||||
action.Should().Throw<ArgumentNullException>();
|
||||
}
|
||||
action.ShouldThrow<ArgumentNullException>();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,93 +1,105 @@
|
|||
using System;
|
||||
// ------------------------------------------------------------------------
|
||||
// Copyright 2021 The Dapr Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Dapr.Actors.Client;
|
||||
using Dapr.Actors.Runtime;
|
||||
using Dapr.Actors.Test;
|
||||
using Xunit;
|
||||
|
||||
namespace Dapr.Actors
|
||||
namespace Dapr.Actors;
|
||||
|
||||
public class ActorReferenceTests
|
||||
{
|
||||
public class ActorReferenceTests
|
||||
[Fact]
|
||||
public void Get_WhenActorIsNull_ReturnsNull()
|
||||
{
|
||||
[Fact]
|
||||
public void Get_WhenActorIsNull_ReturnsNull()
|
||||
{
|
||||
// Arrange
|
||||
object actor = null;
|
||||
// Arrange
|
||||
object actor = null;
|
||||
|
||||
// Act
|
||||
var result = ActorReference.Get(actor);
|
||||
// Act
|
||||
var result = ActorReference.Get(actor);
|
||||
|
||||
// Assert
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Get_FromActorProxy_ReturnsActorReference()
|
||||
{
|
||||
// Arrange
|
||||
var expectedActorId = new ActorId("abc");
|
||||
var expectedActorType = "TestActor";
|
||||
var proxy = ActorProxy.Create(expectedActorId, typeof(ITestActor), expectedActorType);
|
||||
|
||||
// Act
|
||||
var actorReference = ActorReference.Get(proxy);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(actorReference);
|
||||
Assert.Equal(expectedActorId, actorReference.ActorId);
|
||||
Assert.Equal(expectedActorType, actorReference.ActorType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Get_FromActorImplementation_ReturnsActorReference()
|
||||
{
|
||||
// Arrange
|
||||
var expectedActorId = new ActorId("abc");
|
||||
var expectedActorType = nameof(ActorReferenceTestActor);
|
||||
var host = ActorHost.CreateForTest<ActorReferenceTestActor>(new ActorTestOptions() { ActorId = expectedActorId });
|
||||
var actor = new ActorReferenceTestActor(host);
|
||||
|
||||
// Act
|
||||
var actorReference = await actor.GetActorReference();
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(actorReference);
|
||||
Assert.Equal(expectedActorId, actorReference.ActorId);
|
||||
Assert.Equal(expectedActorType, actorReference.ActorType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Get_WithInvalidObjectType_ThrowArgumentOutOfRangeException()
|
||||
{
|
||||
// Arrange
|
||||
var actor = new object();
|
||||
|
||||
// Act
|
||||
var act = () => ActorReference.Get(actor);
|
||||
|
||||
// Assert
|
||||
var exception = Assert.Throws<ArgumentOutOfRangeException>(act);
|
||||
Assert.Equal("actor", exception.ParamName);
|
||||
Assert.Equal("Invalid actor object type. (Parameter 'actor')", exception.Message);
|
||||
}
|
||||
// Assert
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
public interface IActorReferenceTestActor : IActor
|
||||
[Fact]
|
||||
public void Get_FromActorProxy_ReturnsActorReference()
|
||||
{
|
||||
Task<ActorReference> GetActorReference();
|
||||
// Arrange
|
||||
var expectedActorId = new ActorId("abc");
|
||||
var expectedActorType = "TestActor";
|
||||
var proxy = ActorProxy.Create(expectedActorId, typeof(ITestActor), expectedActorType);
|
||||
|
||||
// Act
|
||||
var actorReference = ActorReference.Get(proxy);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(actorReference);
|
||||
Assert.Equal(expectedActorId, actorReference.ActorId);
|
||||
Assert.Equal(expectedActorType, actorReference.ActorType);
|
||||
}
|
||||
|
||||
public class ActorReferenceTestActor : Actor, IActorReferenceTestActor
|
||||
[Fact]
|
||||
public async Task Get_FromActorImplementation_ReturnsActorReference()
|
||||
{
|
||||
public ActorReferenceTestActor(ActorHost host)
|
||||
: base(host)
|
||||
{
|
||||
}
|
||||
// Arrange
|
||||
var expectedActorId = new ActorId("abc");
|
||||
var expectedActorType = nameof(ActorReferenceTestActor);
|
||||
var host = ActorHost.CreateForTest<ActorReferenceTestActor>(new ActorTestOptions() { ActorId = expectedActorId });
|
||||
var actor = new ActorReferenceTestActor(host);
|
||||
|
||||
public Task<ActorReference> GetActorReference()
|
||||
{
|
||||
return Task.FromResult(ActorReference.Get(this));
|
||||
}
|
||||
// Act
|
||||
var actorReference = await actor.GetActorReference();
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(actorReference);
|
||||
Assert.Equal(expectedActorId, actorReference.ActorId);
|
||||
Assert.Equal(expectedActorType, actorReference.ActorType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Get_WithInvalidObjectType_ThrowArgumentOutOfRangeException()
|
||||
{
|
||||
// Arrange
|
||||
var actor = new object();
|
||||
|
||||
// Act
|
||||
var act = () => ActorReference.Get(actor);
|
||||
|
||||
// Assert
|
||||
var exception = Assert.Throws<ArgumentOutOfRangeException>(act);
|
||||
Assert.Equal("actor", exception.ParamName);
|
||||
Assert.Equal("Invalid actor object type. (Parameter 'actor')", exception.Message);
|
||||
}
|
||||
}
|
||||
|
||||
public interface IActorReferenceTestActor : IActor
|
||||
{
|
||||
Task<ActorReference> GetActorReference();
|
||||
}
|
||||
|
||||
public class ActorReferenceTestActor : Actor, IActorReferenceTestActor
|
||||
{
|
||||
public ActorReferenceTestActor(ActorHost host)
|
||||
: base(host)
|
||||
{
|
||||
}
|
||||
|
||||
public Task<ActorReference> GetActorReference()
|
||||
{
|
||||
return Task.FromResult(ActorReference.Get(this));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,7 +69,7 @@ namespace Dapr.Actors
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CanTestStartingAndStoppinReminder()
|
||||
public async Task CanTestStartingAndStoppingReminder()
|
||||
{
|
||||
var reminders = new List<ActorReminder>();
|
||||
IActorReminder getReminder = null;
|
||||
|
@ -120,6 +120,22 @@ namespace Dapr.Actors
|
|||
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
|
||||
{
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ using System;
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Dapr.Actors.Client;
|
||||
using FluentAssertions;
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
|
||||
namespace Dapr.Actors.Test
|
||||
|
@ -43,7 +43,7 @@ namespace Dapr.Actors.Test
|
|||
request.Dismiss();
|
||||
|
||||
var headerValues = request.Request.Headers.GetValues("dapr-api-token");
|
||||
headerValues.Should().Contain("test_token");
|
||||
headerValues.ShouldContain("test_token");
|
||||
}
|
||||
|
||||
[Fact(Skip = "https://github.com/dapr/dotnet-sdk/issues/596")]
|
||||
|
@ -89,7 +89,7 @@ namespace Dapr.Actors.Test
|
|||
request.Dismiss();
|
||||
|
||||
var headerValues = request.Request.Headers.GetValues("dapr-api-token");
|
||||
headerValues.Should().Contain("test_token");
|
||||
headerValues.ShouldContain("test_token");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -9,10 +9,10 @@
|
|||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="FluentAssertions" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" />
|
||||
<PackageReference Include="Moq" />
|
||||
<PackageReference Include="Shouldly"/>
|
||||
<PackageReference Include="xunit" />
|
||||
<PackageReference Include="xunit.runner.visualstudio">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
|
|
|
@ -21,7 +21,7 @@ namespace Dapr.Actors.Test
|
|||
using System.Security.Authentication;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
|
||||
/// <summary>
|
||||
|
@ -34,9 +34,9 @@ namespace Dapr.Actors.Test
|
|||
{
|
||||
await using var client = TestClient.CreateForDaprHttpInterator();
|
||||
|
||||
var actorType = "ActorType_Test";
|
||||
var actorId = "ActorId_Test";
|
||||
var keyName = "StateKey_Test";
|
||||
const string actorType = "ActorType_Test";
|
||||
const string actorId = "ActorId_Test";
|
||||
const string keyName = "StateKey_Test";
|
||||
|
||||
var request = await client.CaptureHttpRequestAsync(async httpInteractor =>
|
||||
{
|
||||
|
@ -48,8 +48,8 @@ namespace Dapr.Actors.Test
|
|||
var actualPath = request.Request.RequestUri.LocalPath.TrimStart('/');
|
||||
var expectedPath = string.Format(CultureInfo.InvariantCulture, Constants.ActorStateKeyRelativeUrlFormat, actorType, actorId, keyName);
|
||||
|
||||
actualPath.Should().Be(expectedPath);
|
||||
request.Request.Method.Should().Be(HttpMethod.Get);
|
||||
actualPath.ShouldBe(expectedPath);
|
||||
request.Request.Method.ShouldBe(HttpMethod.Get);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -57,9 +57,9 @@ namespace Dapr.Actors.Test
|
|||
{
|
||||
await using var client = TestClient.CreateForDaprHttpInterator();
|
||||
|
||||
var actorType = "ActorType_Test";
|
||||
var actorId = "ActorId_Test";
|
||||
var data = "StateData";
|
||||
const string actorType = "ActorType_Test";
|
||||
const string actorId = "ActorId_Test";
|
||||
const string data = "StateData";
|
||||
|
||||
var request = await client.CaptureHttpRequestAsync(async httpInteractor =>
|
||||
{
|
||||
|
@ -71,8 +71,8 @@ namespace Dapr.Actors.Test
|
|||
var actualPath = request.Request.RequestUri.LocalPath.TrimStart('/');
|
||||
var expectedPath = string.Format(CultureInfo.InvariantCulture, Constants.ActorStateRelativeUrlFormat, actorType, actorId);
|
||||
|
||||
actualPath.Should().Be(expectedPath);
|
||||
request.Request.Method.Should().Be(HttpMethod.Put);
|
||||
actualPath.ShouldBe(expectedPath);
|
||||
request.Request.Method.ShouldBe(HttpMethod.Put);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -80,10 +80,10 @@ namespace Dapr.Actors.Test
|
|||
{
|
||||
await using var client = TestClient.CreateForDaprHttpInterator();
|
||||
|
||||
var actorType = "ActorType_Test";
|
||||
var actorId = "ActorId_Test";
|
||||
var methodName = "MethodName";
|
||||
var payload = "JsonData";
|
||||
const string actorType = "ActorType_Test";
|
||||
const string actorId = "ActorId_Test";
|
||||
const string methodName = "MethodName";
|
||||
const string payload = "JsonData";
|
||||
|
||||
var request = await client.CaptureHttpRequestAsync(async httpInteractor =>
|
||||
{
|
||||
|
@ -95,8 +95,8 @@ namespace Dapr.Actors.Test
|
|||
var actualPath = request.Request.RequestUri.LocalPath.TrimStart('/');
|
||||
var expectedPath = string.Format(CultureInfo.InvariantCulture, Constants.ActorMethodRelativeUrlFormat, actorType, actorId, methodName);
|
||||
|
||||
actualPath.Should().Be(expectedPath);
|
||||
request.Request.Method.Should().Be(HttpMethod.Put);
|
||||
actualPath.ShouldBe(expectedPath);
|
||||
request.Request.Method.ShouldBe(HttpMethod.Put);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -104,10 +104,10 @@ namespace Dapr.Actors.Test
|
|||
{
|
||||
await using var client = TestClient.CreateForDaprHttpInterator();
|
||||
|
||||
var actorType = "ActorType_Test";
|
||||
var actorId = "ActorId_Test";
|
||||
var reminderName = "ReminderName";
|
||||
var payload = "JsonData";
|
||||
const string actorType = "ActorType_Test";
|
||||
const string actorId = "ActorId_Test";
|
||||
const string reminderName = "ReminderName";
|
||||
const string payload = "JsonData";
|
||||
|
||||
var request = await client.CaptureHttpRequestAsync(async httpInteractor =>
|
||||
{
|
||||
|
@ -119,8 +119,8 @@ namespace Dapr.Actors.Test
|
|||
var actualPath = request.Request.RequestUri.LocalPath.TrimStart('/');
|
||||
var expectedPath = string.Format(CultureInfo.InvariantCulture, Constants.ActorReminderRelativeUrlFormat, actorType, actorId, reminderName);
|
||||
|
||||
actualPath.Should().Be(expectedPath);
|
||||
request.Request.Method.Should().Be(HttpMethod.Put);
|
||||
actualPath.ShouldBe(expectedPath);
|
||||
request.Request.Method.ShouldBe(HttpMethod.Put);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -128,9 +128,9 @@ namespace Dapr.Actors.Test
|
|||
{
|
||||
await using var client = TestClient.CreateForDaprHttpInterator();
|
||||
|
||||
var actorType = "ActorType_Test";
|
||||
var actorId = "ActorId_Test";
|
||||
var reminderName = "ReminderName";
|
||||
const string actorType = "ActorType_Test";
|
||||
const string actorId = "ActorId_Test";
|
||||
const string reminderName = "ReminderName";
|
||||
|
||||
var request = await client.CaptureHttpRequestAsync(async httpInteractor =>
|
||||
{
|
||||
|
@ -142,8 +142,32 @@ namespace Dapr.Actors.Test
|
|||
var actualPath = request.Request.RequestUri.LocalPath.TrimStart('/');
|
||||
var expectedPath = string.Format(CultureInfo.InvariantCulture, Constants.ActorReminderRelativeUrlFormat, actorType, actorId, reminderName);
|
||||
|
||||
actualPath.Should().Be(expectedPath);
|
||||
request.Request.Method.Should().Be(HttpMethod.Delete);
|
||||
actualPath.ShouldBe(expectedPath);
|
||||
request.Request.Method.ShouldBe(HttpMethod.Delete);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetReminder_ValidateRequest()
|
||||
{
|
||||
await using var client = TestClient.CreateForDaprHttpInterator();
|
||||
|
||||
const string actorType = "ActorType_Test";
|
||||
const string actorId = "ActorId_Test";
|
||||
const string reminderName = "ReminderName";
|
||||
|
||||
var request = await client.CaptureHttpRequestAsync(async httpInteractor =>
|
||||
{
|
||||
await httpInteractor.GetReminderAsync(actorType, actorId, reminderName);
|
||||
});
|
||||
|
||||
request.Dismiss();
|
||||
|
||||
var actualPath = request.Request.RequestUri.LocalPath.TrimStart('/');
|
||||
var expectedPath = string.Format(CultureInfo.InvariantCulture, Constants.ActorReminderRelativeUrlFormat,
|
||||
actorType, actorId, reminderName);
|
||||
|
||||
actualPath.ShouldBe(expectedPath);
|
||||
request.Request.Method.ShouldBe(HttpMethod.Get);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -151,10 +175,10 @@ namespace Dapr.Actors.Test
|
|||
{
|
||||
await using var client = TestClient.CreateForDaprHttpInterator();
|
||||
|
||||
var actorType = "ActorType_Test";
|
||||
var actorId = "ActorId_Test";
|
||||
var timerName = "TimerName";
|
||||
var payload = "JsonData";
|
||||
const string actorType = "ActorType_Test";
|
||||
const string actorId = "ActorId_Test";
|
||||
const string timerName = "TimerName";
|
||||
const string payload = "JsonData";
|
||||
|
||||
var request = await client.CaptureHttpRequestAsync(async httpInteractor =>
|
||||
{
|
||||
|
@ -166,8 +190,8 @@ namespace Dapr.Actors.Test
|
|||
var actualPath = request.Request.RequestUri.LocalPath.TrimStart('/');
|
||||
var expectedPath = string.Format(CultureInfo.InvariantCulture, Constants.ActorTimerRelativeUrlFormat, actorType, actorId, timerName);
|
||||
|
||||
actualPath.Should().Be(expectedPath);
|
||||
request.Request.Method.Should().Be(HttpMethod.Put);
|
||||
actualPath.ShouldBe(expectedPath);
|
||||
request.Request.Method.ShouldBe(HttpMethod.Put);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -189,8 +213,8 @@ namespace Dapr.Actors.Test
|
|||
var actualPath = request.Request.RequestUri.LocalPath.TrimStart('/');
|
||||
var expectedPath = string.Format(CultureInfo.InvariantCulture, Constants.ActorTimerRelativeUrlFormat, actorType, actorId, timerName);
|
||||
|
||||
actualPath.Should().Be(expectedPath);
|
||||
request.Request.Method.Should().Be(HttpMethod.Delete);
|
||||
actualPath.ShouldBe(expectedPath);
|
||||
request.Request.Method.ShouldBe(HttpMethod.Delete);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -198,9 +222,9 @@ namespace Dapr.Actors.Test
|
|||
{
|
||||
await using var client = TestClient.CreateForDaprHttpInterator(apiToken: "test_token");
|
||||
|
||||
var actorType = "ActorType_Test";
|
||||
var actorId = "ActorId_Test";
|
||||
var timerName = "TimerName";
|
||||
const string actorType = "ActorType_Test";
|
||||
const string actorId = "ActorId_Test";
|
||||
const string timerName = "TimerName";
|
||||
|
||||
var request = await client.CaptureHttpRequestAsync(async httpInteractor =>
|
||||
{
|
||||
|
@ -210,8 +234,8 @@ namespace Dapr.Actors.Test
|
|||
request.Dismiss();
|
||||
|
||||
request.Request.Headers.TryGetValues("dapr-api-token", out var headerValues);
|
||||
headerValues.Count().Should().Be(1);
|
||||
headerValues.First().Should().Be("test_token");
|
||||
headerValues.Count().ShouldBe(1);
|
||||
headerValues.First().ShouldBe("test_token");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -219,9 +243,9 @@ namespace Dapr.Actors.Test
|
|||
{
|
||||
await using var client = TestClient.CreateForDaprHttpInterator();
|
||||
|
||||
var actorType = "ActorType_Test";
|
||||
var actorId = "ActorId_Test";
|
||||
var timerName = "TimerName";
|
||||
const string actorType = "ActorType_Test";
|
||||
const string actorId = "ActorId_Test";
|
||||
const string timerName = "TimerName";
|
||||
|
||||
var request = await client.CaptureHttpRequestAsync(async httpInteractor =>
|
||||
{
|
||||
|
@ -231,7 +255,7 @@ namespace Dapr.Actors.Test
|
|||
request.Dismiss();
|
||||
|
||||
request.Request.Headers.TryGetValues("dapr-api-token", out var headerValues);
|
||||
headerValues.Should().BeNull();
|
||||
headerValues.ShouldBeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -239,9 +263,9 @@ namespace Dapr.Actors.Test
|
|||
{
|
||||
await using var client = TestClient.CreateForDaprHttpInterator();
|
||||
|
||||
var actorType = "ActorType_Test";
|
||||
var actorId = "ActorId_Test";
|
||||
var timerName = "TimerName";
|
||||
const string actorType = "ActorType_Test";
|
||||
const string actorId = "ActorId_Test";
|
||||
const string timerName = "TimerName";
|
||||
|
||||
var request = await client.CaptureHttpRequestAsync(async httpInteractor =>
|
||||
{
|
||||
|
@ -272,9 +296,9 @@ namespace Dapr.Actors.Test
|
|||
{
|
||||
await using var client = TestClient.CreateForDaprHttpInterator();
|
||||
|
||||
var actorType = "ActorType_Test";
|
||||
var actorId = "ActorId_Test";
|
||||
var timerName = "TimerName";
|
||||
const string actorType = "ActorType_Test";
|
||||
const string actorId = "ActorId_Test";
|
||||
const string timerName = "TimerName";
|
||||
|
||||
var request = await client.CaptureHttpRequestAsync(async httpInteractor =>
|
||||
{
|
||||
|
@ -294,9 +318,9 @@ namespace Dapr.Actors.Test
|
|||
{
|
||||
await using var client = TestClient.CreateForDaprHttpInterator();
|
||||
|
||||
var actorType = "ActorType_Test";
|
||||
var actorId = "ActorId_Test";
|
||||
var timerName = "TimerName";
|
||||
const string actorType = "ActorType_Test";
|
||||
const string actorId = "ActorId_Test";
|
||||
const string timerName = "TimerName";
|
||||
|
||||
var request = await client.CaptureHttpRequestAsync(async httpInteractor =>
|
||||
{
|
||||
|
@ -316,10 +340,10 @@ namespace Dapr.Actors.Test
|
|||
{
|
||||
await using var client = TestClient.CreateForDaprHttpInterator();
|
||||
|
||||
var actorType = "ActorType_Test";
|
||||
var actorId = "ActorId_Test";
|
||||
var methodName = "MethodName";
|
||||
var payload = "JsonData";
|
||||
const string actorType = "ActorType_Test";
|
||||
const string actorId = "ActorId_Test";
|
||||
const string methodName = "MethodName";
|
||||
const string payload = "JsonData";
|
||||
|
||||
ActorReentrancyContextAccessor.ReentrancyContext = "1";
|
||||
var request = await client.CaptureHttpRequestAsync(async httpInteractor =>
|
||||
|
@ -337,10 +361,10 @@ namespace Dapr.Actors.Test
|
|||
{
|
||||
await using var client = TestClient.CreateForDaprHttpInterator();
|
||||
|
||||
var actorType = "ActorType_Test";
|
||||
var actorId = "ActorId_Test";
|
||||
var methodName = "MethodName";
|
||||
var payload = "JsonData";
|
||||
const string actorType = "ActorType_Test";
|
||||
const string actorId = "ActorId_Test";
|
||||
const string methodName = "MethodName";
|
||||
const string payload = "JsonData";
|
||||
|
||||
var request = await client.CaptureHttpRequestAsync(async httpInteractor =>
|
||||
{
|
||||
|
@ -356,9 +380,9 @@ namespace Dapr.Actors.Test
|
|||
{
|
||||
await using var client = TestClient.CreateForDaprHttpInterator();
|
||||
|
||||
var actorType = "ActorType_Test";
|
||||
var actorId = "ActorId_Test";
|
||||
var keyName = "StateKey_Test";
|
||||
const string actorType = "ActorType_Test";
|
||||
const string actorId = "ActorId_Test";
|
||||
const string keyName = "StateKey_Test";
|
||||
|
||||
var request = await client.CaptureHttpRequestAsync(async httpInteractor =>
|
||||
{
|
||||
|
@ -385,9 +409,9 @@ namespace Dapr.Actors.Test
|
|||
{
|
||||
await using var client = TestClient.CreateForDaprHttpInterator();
|
||||
|
||||
var actorType = "ActorType_Test";
|
||||
var actorId = "ActorId_Test";
|
||||
var keyName = "StateKey_Test";
|
||||
const string actorType = "ActorType_Test";
|
||||
const string actorId = "ActorId_Test";
|
||||
const string keyName = "StateKey_Test";
|
||||
|
||||
var request = await client.CaptureHttpRequestAsync(async httpInteractor =>
|
||||
{
|
||||
|
|
|
@ -1,20 +1,19 @@
|
|||
// ------------------------------------------------------------------------
|
||||
// Copyright 2021 The Dapr Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
// ------------------------------------------------------------------------
|
||||
// Copyright 2021 The Dapr Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using FluentAssertions.Execution;
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
|
||||
namespace Dapr.Actors.Description
|
||||
|
@ -31,13 +30,12 @@ namespace Dapr.Actors.Description
|
|||
var description = ActorInterfaceDescription.Create(type);
|
||||
|
||||
// Assert
|
||||
description.Should().NotBeNull();
|
||||
description.ShouldNotBeNull();
|
||||
|
||||
using var _ = new AssertionScope();
|
||||
description.InterfaceType.Should().Be(type);
|
||||
description.Id.Should().NotBe(0);
|
||||
description.V1Id.Should().Be(0);
|
||||
description.Methods.Should().HaveCount(2);
|
||||
description.InterfaceType.ShouldBe(type);
|
||||
description.Id.ShouldNotBe(0);
|
||||
description.V1Id.ShouldBe(0);
|
||||
description.Methods.Length.ShouldBe(2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -50,10 +48,9 @@ namespace Dapr.Actors.Description
|
|||
Action action = () => ActorInterfaceDescription.Create(type);
|
||||
|
||||
// Assert
|
||||
action.Should().ThrowExactly<ArgumentException>()
|
||||
.WithMessage("The type 'System.Object' is not an Actor interface as it is not an interface.*")
|
||||
.And.ParamName.Should().Be("actorInterfaceType");
|
||||
|
||||
var exception = Should.Throw<ArgumentException>(action);
|
||||
exception.Message.ShouldMatch(@"The type 'System.Object' is not an Actor interface as it is not an interface.*");
|
||||
exception.ParamName.ShouldBe("actorInterfaceType");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -66,9 +63,9 @@ namespace Dapr.Actors.Description
|
|||
Action action = () => ActorInterfaceDescription.Create(type);
|
||||
|
||||
// Assert
|
||||
action.Should().ThrowExactly<ArgumentException>()
|
||||
.WithMessage("The type 'System.ICloneable' is not an actor interface as it does not derive from the interface 'Dapr.Actors.IActor'.*")
|
||||
.And.ParamName.Should().Be("actorInterfaceType");
|
||||
var exception = Should.Throw<ArgumentException>(action);
|
||||
exception.Message.ShouldMatch(@"The type 'System.ICloneable' is not an actor interface as it does not derive from the interface 'Dapr.Actors.IActor'.*");
|
||||
exception.ParamName.ShouldBe("actorInterfaceType");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -81,9 +78,9 @@ namespace Dapr.Actors.Description
|
|||
Action action = () => ActorInterfaceDescription.Create(type);
|
||||
|
||||
// Assert
|
||||
action.Should().ThrowExactly<ArgumentException>()
|
||||
.WithMessage("The type '*+IClonableActor' is not an actor interface as it derive from a non actor interface 'System.ICloneable'. All actor interfaces must derive from 'Dapr.Actors.IActor'.*")
|
||||
.And.ParamName.Should().Be("actorInterfaceType");
|
||||
var exception = Should.Throw<ArgumentException>(action);
|
||||
exception.Message.ShouldMatch(@"The type '.*\+IClonableActor' is not an actor interface as it derive from a non actor interface 'System.ICloneable'. All actor interfaces must derive from 'Dapr.Actors.IActor'.*");
|
||||
exception.ParamName.ShouldBe("actorInterfaceType");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -96,13 +93,12 @@ namespace Dapr.Actors.Description
|
|||
var description = ActorInterfaceDescription.CreateUsingCRCId(type);
|
||||
|
||||
// Assert
|
||||
description.Should().NotBeNull();
|
||||
description.ShouldNotBeNull();
|
||||
|
||||
using var _ = new AssertionScope();
|
||||
description.InterfaceType.Should().Be(type);
|
||||
description.Id.Should().Be(-934188464);
|
||||
description.V1Id.Should().NotBe(0);
|
||||
description.Methods.Should().HaveCount(2);
|
||||
description.InterfaceType.ShouldBe(type);
|
||||
description.Id.ShouldBe(-934188464);
|
||||
description.V1Id.ShouldNotBe(0);
|
||||
description.Methods.Length.ShouldBe(2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -115,9 +111,9 @@ namespace Dapr.Actors.Description
|
|||
Action action = () => ActorInterfaceDescription.CreateUsingCRCId(type);
|
||||
|
||||
// Assert
|
||||
action.Should().ThrowExactly<ArgumentException>()
|
||||
.WithMessage("The type 'System.Object' is not an Actor interface as it is not an interface.*")
|
||||
.And.ParamName.Should().Be("actorInterfaceType");
|
||||
var exception = Should.Throw<ArgumentException>(action);
|
||||
exception.Message.ShouldMatch(@"The type 'System.Object' is not an Actor interface as it is not an interface.*");
|
||||
exception.ParamName.ShouldBe("actorInterfaceType");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -130,9 +126,9 @@ namespace Dapr.Actors.Description
|
|||
Action action = () => ActorInterfaceDescription.CreateUsingCRCId(type);
|
||||
|
||||
// Assert
|
||||
action.Should().ThrowExactly<ArgumentException>()
|
||||
.WithMessage("The type 'System.ICloneable' is not an actor interface as it does not derive from the interface 'Dapr.Actors.IActor'.*")
|
||||
.And.ParamName.Should().Be("actorInterfaceType");
|
||||
var exception = Should.Throw<ArgumentException>(action);
|
||||
exception.Message.ShouldMatch(@"The type 'System.ICloneable' is not an actor interface as it does not derive from the interface 'Dapr.Actors.IActor'.*");
|
||||
exception.ParamName.ShouldBe("actorInterfaceType");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -145,9 +141,9 @@ namespace Dapr.Actors.Description
|
|||
Action action = () => ActorInterfaceDescription.CreateUsingCRCId(type);
|
||||
|
||||
// Assert
|
||||
action.Should().ThrowExactly<ArgumentException>()
|
||||
.WithMessage("The type '*+IClonableActor' is not an actor interface as it derive from a non actor interface 'System.ICloneable'. All actor interfaces must derive from 'Dapr.Actors.IActor'.*")
|
||||
.And.ParamName.Should().Be("actorInterfaceType");
|
||||
var exception = Should.Throw<ArgumentException>(action);
|
||||
exception.Message.ShouldMatch(@"The type '.*\+IClonableActor' is not an actor interface as it derive from a non actor interface 'System.ICloneable'. All actor interfaces must derive from 'Dapr.Actors.IActor'.*");
|
||||
exception.ParamName.ShouldBe("actorInterfaceType");
|
||||
}
|
||||
|
||||
internal interface IClonableActor : ICloneable, IActor
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
// ------------------------------------------------------------------------
|
||||
// Copyright 2021 The Dapr Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
// ------------------------------------------------------------------------
|
||||
// Copyright 2021 The Dapr Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using FluentAssertions.Execution;
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
|
||||
namespace Dapr.Actors.Description
|
||||
|
@ -31,13 +31,12 @@ namespace Dapr.Actors.Description
|
|||
TestDescription description = new(type);
|
||||
|
||||
// Assert
|
||||
description.Should().NotBeNull();
|
||||
description.ShouldNotBeNull();
|
||||
|
||||
using var _ = new AssertionScope();
|
||||
description.InterfaceType.Should().Be(type);
|
||||
description.Id.Should().NotBe(0);
|
||||
description.V1Id.Should().Be(0);
|
||||
description.Methods.Should().BeEmpty();
|
||||
description.InterfaceType.ShouldBe(type);
|
||||
description.Id.ShouldNotBe(0);
|
||||
description.V1Id.ShouldBe(0);
|
||||
description.Methods.ShouldBeEmpty();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -50,9 +49,8 @@ namespace Dapr.Actors.Description
|
|||
TestDescription description = new(type, useCRCIdGeneration: true);
|
||||
|
||||
// Assert
|
||||
using var _ = new AssertionScope();
|
||||
description.Id.Should().Be(-934188464);
|
||||
description.V1Id.Should().NotBe(0);
|
||||
description.Id.ShouldBe(-934188464);
|
||||
description.V1Id.ShouldNotBe(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -65,9 +63,9 @@ namespace Dapr.Actors.Description
|
|||
Action action = () => { TestDescription _ = new(type); };
|
||||
|
||||
// Assert
|
||||
action.Should().ThrowExactly<ArgumentException>()
|
||||
.WithMessage("The actor interface '*+IGenericActor`1' is using generics. Generic interfaces cannot be remoted.*")
|
||||
.And.ParamName.Should().Be("actorInterfaceType");
|
||||
var exception = Should.Throw<ArgumentException>(action);
|
||||
exception.Message.ShouldMatch(@"The actor interface '.*\+IGenericActor`1' is using generics. Generic interfaces cannot be remoted.*");
|
||||
exception.ParamName.ShouldBe("actorInterfaceType");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -80,9 +78,9 @@ namespace Dapr.Actors.Description
|
|||
Action action = () => { TestDescription _ = new(type); };
|
||||
|
||||
// Assert
|
||||
action.Should().ThrowExactly<ArgumentException>()
|
||||
.WithMessage("The actor interface '*+IGenericActor`1[*IActor*]' is using generics. Generic interfaces cannot be remoted.*")
|
||||
.And.ParamName.Should().Be("actorInterfaceType");
|
||||
var exception = Should.Throw<ArgumentException>(action);
|
||||
exception.Message.ShouldMatch(@"The actor interface '.*\+IGenericActor`1\[.*IActor.*\]' is using generics. Generic interfaces cannot be remoted.*");
|
||||
exception.ParamName.ShouldBe("actorInterfaceType");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -95,12 +93,9 @@ namespace Dapr.Actors.Description
|
|||
TestDescription description = new(type);
|
||||
|
||||
// Assert
|
||||
using var _ = new AssertionScope();
|
||||
description.Methods.Should().NotContainNulls();
|
||||
description.Methods.Should().AllBeOfType<MethodDescription>();
|
||||
description.Methods.Should().BeEquivalentTo(
|
||||
new { Name = "GetInt" }
|
||||
);
|
||||
description.Methods.ShouldNotBeNull();
|
||||
description.Methods.ShouldBeOfType<MethodDescription[]>();
|
||||
description.Methods.Select(m => new {m.Name}).ShouldBe(new[] {new {Name = "GetInt"}});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -113,12 +108,9 @@ namespace Dapr.Actors.Description
|
|||
TestDescription description = new(type, methodReturnCheck: MethodReturnCheck.EnsureReturnsVoid);
|
||||
|
||||
// Assert
|
||||
using var _ = new AssertionScope();
|
||||
description.Methods.Should().NotContainNulls();
|
||||
description.Methods.Should().AllBeOfType<MethodDescription>();
|
||||
description.Methods.Should().BeEquivalentTo(
|
||||
new { Name = "GetString" },
|
||||
new { Name = "MethodWithArguments" });
|
||||
description.Methods.ShouldNotBeNull();
|
||||
description.Methods.ShouldBeOfType<MethodDescription[]>();
|
||||
description.Methods.Select(m => new {m.Name}).ShouldBe(new[] {new {Name = "GetString"}, new {Name="MethodWithArguments"}});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -128,12 +120,15 @@ namespace Dapr.Actors.Description
|
|||
Type type = typeof(IVoidActor);
|
||||
|
||||
// Act
|
||||
Action action = () => { TestDescription _ = new(type); };
|
||||
Action action = () =>
|
||||
{
|
||||
TestDescription _ = new(type);
|
||||
};
|
||||
|
||||
// Assert
|
||||
action.Should().ThrowExactly<ArgumentException>()
|
||||
.WithMessage("Method 'GetString' of actor interface '*+IVoidActor' does not return Task or Task<>. The actor interface methods must be async and must return either Task or Task<>.*")
|
||||
.And.ParamName.Should().Be("actorInterfaceType");
|
||||
var exception = Should.Throw<ArgumentException>(action);
|
||||
exception.Message.ShouldMatch(@"Method 'GetString' of actor interface '.*\+IVoidActor' does not return Task or Task<>. The actor interface methods must be async and must return either Task or Task<>.*");
|
||||
exception.ParamName.ShouldBe("actorInterfaceType");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -146,9 +141,9 @@ namespace Dapr.Actors.Description
|
|||
Action action = () => { TestDescription _ = new(type, methodReturnCheck: MethodReturnCheck.EnsureReturnsVoid); };
|
||||
|
||||
// Assert
|
||||
action.Should().ThrowExactly<ArgumentException>()
|
||||
.WithMessage("Method 'GetString' of actor interface '*+IMethodActor' returns '*.Task`1[*System.String*]'*")
|
||||
.And.ParamName.Should().Be("actorInterfaceType");
|
||||
var exception = Should.Throw<ArgumentException>(action);
|
||||
exception.Message.ShouldMatch(@"Method 'GetString' of actor interface '.*\+IMethodActor' returns '.*\.Task`1\[.*System.String.*\]'.*");
|
||||
exception.ParamName.ShouldBe("actorInterfaceType");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -161,9 +156,9 @@ namespace Dapr.Actors.Description
|
|||
Action action = () => { TestDescription _ = new(type); };
|
||||
|
||||
// Assert
|
||||
action.Should().ThrowExactly<ArgumentException>()
|
||||
.WithMessage("Method 'GetString' of actor interface '*+IOverloadedMethodActor' is overloaded. The actor interface methods cannot be overloaded.*")
|
||||
.And.ParamName.Should().Be("actorInterfaceType");
|
||||
var exception = Should.Throw<ArgumentException>(action);
|
||||
exception.Message.ShouldMatch(@"Method 'GetString' of actor interface '.*\+IOverloadedMethodActor' is overloaded. The actor interface methods cannot be overloaded.*");
|
||||
exception.ParamName.ShouldBe("actorInterfaceType");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -176,9 +171,9 @@ namespace Dapr.Actors.Description
|
|||
Action action = () => { TestDescription _ = new(type); };
|
||||
|
||||
// Assert
|
||||
action.Should().ThrowExactly<ArgumentException>()
|
||||
.WithMessage("Method 'Get' of actor interface '*+IGenericMethodActor' is using generics. The actor interface methods cannot use generics.*")
|
||||
.And.ParamName.Should().Be("actorInterfaceType");
|
||||
var exception = Should.Throw<ArgumentException>(action);
|
||||
exception.Message.ShouldMatch(@"Method 'Get' of actor interface '.*\+IGenericMethodActor' is using generics. The actor interface methods cannot use generics.*");
|
||||
exception.ParamName.ShouldBe("actorInterfaceType");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -191,9 +186,9 @@ namespace Dapr.Actors.Description
|
|||
Action action = () => { TestDescription _ = new(type); };
|
||||
|
||||
// Assert
|
||||
action.Should().ThrowExactly<ArgumentException>()
|
||||
.WithMessage("Method 'MethodWithVarArgs' of actor interface '*+IVariableActor' is using a variable argument list. The actor interface methods cannot have a variable argument list.*")
|
||||
.And.ParamName.Should().Be("actorInterfaceType");
|
||||
var exception = Should.Throw<ArgumentException>(action);
|
||||
exception.Message.ShouldMatch(@"Method 'MethodWithVarArgs' of actor interface '.*\+IVariableActor' is using a variable argument list. The actor interface methods cannot have a variable argument list.*");
|
||||
exception.ParamName.ShouldBe("actorInterfaceType");
|
||||
}
|
||||
|
||||
internal interface ITestActor : IActor
|
||||
|
|
|
@ -1,21 +1,20 @@
|
|||
// ------------------------------------------------------------------------
|
||||
// Copyright 2021 The Dapr Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
// ------------------------------------------------------------------------
|
||||
// Copyright 2021 The Dapr Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using FluentAssertions.Execution;
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
|
||||
namespace Dapr.Actors.Description
|
||||
|
@ -33,11 +32,10 @@ namespace Dapr.Actors.Description
|
|||
var description = MethodArgumentDescription.Create("actor", methodInfo, parameterInfo);
|
||||
|
||||
// Assert
|
||||
description.Should().NotBeNull();
|
||||
description.ShouldNotBeNull();
|
||||
|
||||
using var _ = new AssertionScope();
|
||||
description.Name.Should().Be("number");
|
||||
description.ArgumentType.Should().Be<int>();
|
||||
description.Name.ShouldBe("number");
|
||||
description.ArgumentType.ShouldBe(typeof(int));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -52,9 +50,9 @@ namespace Dapr.Actors.Description
|
|||
Action action = () => MethodArgumentDescription.Create("actor", methodInfo, parameterInfo);
|
||||
|
||||
// Assert
|
||||
action.Should().ThrowExactly<ArgumentException>()
|
||||
.WithMessage("Method 'MethodWithParams' of actor interface '*ITestActor' has variable length parameter 'values'. The actor interface methods must not have variable length parameters.*")
|
||||
.And.ParamName.Should().Be("actorInterfaceType");
|
||||
var exception = Should.Throw<ArgumentException>(action);
|
||||
exception.Message.ShouldMatch(@"Method 'MethodWithParams' of actor interface '.*\+ITestActor' has variable length parameter 'values'. The actor interface methods must not have variable length parameters.*");
|
||||
exception.ParamName.ShouldBe("actorInterfaceType");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -69,9 +67,9 @@ namespace Dapr.Actors.Description
|
|||
Action action = () => MethodArgumentDescription.Create("actor", methodInfo, parameterInfo);
|
||||
|
||||
// Assert
|
||||
action.Should().ThrowExactly<ArgumentException>()
|
||||
.WithMessage("Method 'MethodWithIn' of actor interface '*ITestActor' has out/ref/optional parameter 'value'. The actor interface methods must not have out, ref or optional parameters.*")
|
||||
.And.ParamName.Should().Be("actorInterfaceType");
|
||||
var exception = Should.Throw<ArgumentException>(action);
|
||||
exception.Message.ShouldMatch(@"Method 'MethodWithIn' of actor interface '.*\+ITestActor' has out/ref/optional parameter 'value'. The actor interface methods must not have out, ref or optional parameters.*");
|
||||
exception.ParamName.ShouldBe("actorInterfaceType");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -86,9 +84,9 @@ namespace Dapr.Actors.Description
|
|||
Action action = () => MethodArgumentDescription.Create("actor", methodInfo, parameterInfo);
|
||||
|
||||
// Assert
|
||||
action.Should().ThrowExactly<ArgumentException>()
|
||||
.WithMessage("Method 'MethodWithOut' of actor interface '*ITestActor' has out/ref/optional parameter 'value'. The actor interface methods must not have out, ref or optional parameters.*")
|
||||
.And.ParamName.Should().Be("actorInterfaceType");
|
||||
var exception = Should.Throw<ArgumentException>(action);
|
||||
exception.Message.ShouldMatch(@"Method 'MethodWithOut' of actor interface '.*\+ITestActor' has out/ref/optional parameter 'value'. The actor interface methods must not have out, ref or optional parameters.*");
|
||||
exception.ParamName.ShouldBe("actorInterfaceType");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -103,9 +101,9 @@ namespace Dapr.Actors.Description
|
|||
Action action = () => MethodArgumentDescription.Create("actor", methodInfo, parameterInfo);
|
||||
|
||||
// Assert
|
||||
action.Should().ThrowExactly<ArgumentException>()
|
||||
.WithMessage("Method 'MethodWithOptional' of actor interface '*ITestActor' has out/ref/optional parameter 'value'. The actor interface methods must not have out, ref or optional parameters.*")
|
||||
.And.ParamName.Should().Be("actorInterfaceType");
|
||||
var exception = Should.Throw<ArgumentException>(action);
|
||||
exception.Message.ShouldMatch(@"Method 'MethodWithOptional' of actor interface '.*\+ITestActor' has out/ref/optional parameter 'value'. The actor interface methods must not have out, ref or optional parameters.*");
|
||||
exception.ParamName.ShouldBe("actorInterfaceType");
|
||||
}
|
||||
|
||||
internal interface ITestActor : IActor
|
||||
|
|
|
@ -1,22 +1,22 @@
|
|||
// ------------------------------------------------------------------------
|
||||
// Copyright 2021 The Dapr Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Copyright 2021 The Dapr Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using FluentAssertions.Execution;
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
|
||||
namespace Dapr.Actors.Description
|
||||
|
@ -33,15 +33,14 @@ namespace Dapr.Actors.Description
|
|||
var description = MethodDescription.Create("actor", methodInfo, false);
|
||||
|
||||
// Assert
|
||||
description.Should().NotBeNull();
|
||||
description.ShouldNotBeNull();
|
||||
|
||||
using var _ = new AssertionScope();
|
||||
description.MethodInfo.Should().BeSameAs(methodInfo);
|
||||
description.Name.Should().Be("GetString");
|
||||
description.ReturnType.Should().Be<Task<string>>();
|
||||
description.Id.Should().NotBe(0);
|
||||
description.Arguments.Should().BeEmpty();
|
||||
description.HasCancellationToken.Should().BeFalse();
|
||||
description.MethodInfo.ShouldBeSameAs(methodInfo);
|
||||
description.Name.ShouldBe("GetString");
|
||||
description.ReturnType.ShouldBe(typeof(Task<string>));
|
||||
description.Id.ShouldNotBe(0);
|
||||
description.Arguments.ShouldBeEmpty();
|
||||
description.HasCancellationToken.ShouldBeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -54,7 +53,7 @@ namespace Dapr.Actors.Description
|
|||
var description = MethodDescription.Create("actor", methodInfo, true);
|
||||
|
||||
// Assert
|
||||
description.Id.Should().Be(70257263);
|
||||
description.Id.ShouldBe(70257263);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -67,13 +66,9 @@ namespace Dapr.Actors.Description
|
|||
var description = MethodDescription.Create("actor", methodInfo, false);
|
||||
|
||||
// Assert
|
||||
using var _ = new AssertionScope();
|
||||
description.Arguments.Should().NotContainNulls();
|
||||
description.Arguments.Should().AllBeOfType<MethodArgumentDescription>();
|
||||
description.Arguments.Should().BeEquivalentTo(
|
||||
new { Name = "number" },
|
||||
new { Name = "choice" },
|
||||
new { Name = "information" });
|
||||
description.Arguments.ShouldNotBeNull();
|
||||
description.Arguments.ShouldBeOfType<MethodArgumentDescription[]>();
|
||||
description.Arguments.Select(m => new {m.Name}).ShouldBe(new[] {new {Name = "number"}, new {Name = "choice"}, new {Name = "information"}});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -86,7 +81,7 @@ namespace Dapr.Actors.Description
|
|||
var description = MethodDescription.Create("actor", methodInfo, false);
|
||||
|
||||
// Assert
|
||||
description.HasCancellationToken.Should().BeTrue();
|
||||
description.HasCancellationToken.ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -100,9 +95,9 @@ namespace Dapr.Actors.Description
|
|||
Action action = () => MethodDescription.Create("actor", methodInfo, false);
|
||||
|
||||
// Assert
|
||||
action.Should().ThrowExactly<ArgumentException>()
|
||||
.WithMessage("Method 'MethodWithTokenNotLast' of actor interface '*+ITestActor' has a '*.CancellationToken' parameter that is not the last parameter. If an actor method accepts a '*.CancellationToken' parameter, it must be the last parameter.*")
|
||||
.And.ParamName.Should().Be("actorInterfaceType");
|
||||
var exception = Should.Throw<ArgumentException>(action);
|
||||
exception.Message.ShouldMatch(@"Method 'MethodWithTokenNotLast' of actor interface '.*\+ITestActor' has a '.*\.CancellationToken' parameter that is not the last parameter. If an actor method accepts a '.*\.CancellationToken' parameter, it must be the last parameter\..*");
|
||||
exception.ParamName.ShouldBe("actorInterfaceType");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -116,9 +111,9 @@ namespace Dapr.Actors.Description
|
|||
Action action = () => MethodDescription.Create("actor", methodInfo, false);
|
||||
|
||||
// Assert
|
||||
action.Should().ThrowExactly<ArgumentException>()
|
||||
.WithMessage("Method 'MethodWithMultipleTokens' of actor interface '*+ITestActor' has a '*.CancellationToken' parameter that is not the last parameter. If an actor method accepts a '*.CancellationToken' parameter, it must be the last parameter.*")
|
||||
.And.ParamName.Should().Be("actorInterfaceType");
|
||||
var exception = Should.Throw<ArgumentException>(action);
|
||||
exception.Message.ShouldMatch(@"Method 'MethodWithMultipleTokens' of actor interface '.*\+ITestActor' has a '.*\.CancellationToken' parameter that is not the last parameter. If an actor method accepts a '.*\.CancellationToken' parameter, it must be the last parameter.*");
|
||||
exception.ParamName.ShouldBe("actorInterfaceType");
|
||||
}
|
||||
|
||||
internal interface ITestActor : IActor
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -11,118 +11,117 @@
|
|||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
namespace Dapr.Actors.Test
|
||||
namespace Dapr.Actors.Test;
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Dapr.Actors.Runtime;
|
||||
|
||||
/// <summary>
|
||||
/// Interface for test actor.
|
||||
/// </summary>
|
||||
public interface ITestActor : IActor
|
||||
{
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Dapr.Actors.Runtime;
|
||||
/// <summary>
|
||||
/// GetCount method for TestActor.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">Cancellation token to cancel the operation.</param>
|
||||
/// <returns>The current count as stored in actor.</returns>
|
||||
Task<int> GetCountAsync(CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Interface for test actor.
|
||||
/// SetCount method for test actor.
|
||||
/// </summary>
|
||||
public interface ITestActor : IActor
|
||||
{
|
||||
/// <summary>
|
||||
/// GetCount method for TestActor.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">Cancellation token to cancel the operation.</param>
|
||||
/// <returns>The current count as stored in actor.</returns>
|
||||
Task<int> GetCountAsync(CancellationToken cancellationToken);
|
||||
/// <param name="count">Count to set for the actor.</param>
|
||||
/// <param name="cancellationToken">Cancellation token to cancel the operation.</param>
|
||||
/// <returns>Task.</returns>
|
||||
Task SetCountAsync(int count, CancellationToken cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SetCount method for test actor.
|
||||
/// </summary>
|
||||
/// <param name="count">Count to set for the actor.</param>
|
||||
/// <param name="cancellationToken">Cancellation token to cancel the operation.</param>
|
||||
/// <returns>Task.</returns>
|
||||
Task SetCountAsync(int count, CancellationToken cancellationToken);
|
||||
/// <summary>
|
||||
/// Test Actor Class.
|
||||
/// </summary>
|
||||
public class TestActor : Actor, ITestActor
|
||||
{
|
||||
public TestActor(ActorHost host, IActorStateManager stateManager = null)
|
||||
: base(host)
|
||||
{
|
||||
if (stateManager != null)
|
||||
{
|
||||
this.StateManager = stateManager;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test Actor Class.
|
||||
/// </summary>
|
||||
public class TestActor : Actor, ITestActor
|
||||
/// <inheritdoc/>
|
||||
public Task<int> GetCountAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
public TestActor(ActorHost host, IActorStateManager stateManager = null)
|
||||
: base(host)
|
||||
{
|
||||
if (stateManager != null)
|
||||
{
|
||||
this.StateManager = stateManager;
|
||||
}
|
||||
}
|
||||
return Task.FromResult(5);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task<int> GetCountAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult(5);
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public Task SetCountAsync(int count, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task SetCountAsync(int count, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
public Task SaveTestState()
|
||||
{
|
||||
return this.SaveStateAsync();
|
||||
}
|
||||
|
||||
public Task SaveTestState()
|
||||
{
|
||||
return this.SaveStateAsync();
|
||||
}
|
||||
public Task ResetTestStateAsync()
|
||||
{
|
||||
return this.ResetStateAsync();
|
||||
}
|
||||
|
||||
public Task ResetTestStateAsync()
|
||||
{
|
||||
return this.ResetStateAsync();
|
||||
}
|
||||
public void TimerCallbackNonTaskReturnType()
|
||||
{
|
||||
}
|
||||
|
||||
public void TimerCallbackNonTaskReturnType()
|
||||
{
|
||||
}
|
||||
public Task TimerCallbackTwoArguments(int i, int j)
|
||||
{
|
||||
Console.WriteLine(i + j);
|
||||
return default;
|
||||
}
|
||||
|
||||
public Task TimerCallbackTwoArguments(int i, int j)
|
||||
{
|
||||
Console.WriteLine(i + j);
|
||||
return default;
|
||||
}
|
||||
private Task TimerCallbackPrivate()
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
private Task TimerCallbackPrivate()
|
||||
{
|
||||
return default;
|
||||
}
|
||||
protected Task TimerCallbackProtected()
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
protected Task TimerCallbackProtected()
|
||||
{
|
||||
return default;
|
||||
}
|
||||
internal Task TimerCallbackInternal()
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
internal Task TimerCallbackInternal()
|
||||
{
|
||||
return default;
|
||||
}
|
||||
public Task TimerCallbackPublicWithNoArguments()
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
public Task TimerCallbackPublicWithNoArguments()
|
||||
{
|
||||
return default;
|
||||
}
|
||||
public Task TimerCallbackPublicWithOneArgument(int i)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
public Task TimerCallbackPublicWithOneArgument(int i)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
public Task TimerCallbackOverloaded()
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
public Task TimerCallbackOverloaded()
|
||||
{
|
||||
return default;
|
||||
}
|
||||
public Task TimerCallbackOverloaded(int i)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
public Task TimerCallbackOverloaded(int i)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
public static Task TimerCallbackStatic()
|
||||
{
|
||||
return default;
|
||||
}
|
||||
public static Task TimerCallbackStatic()
|
||||
{
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
// ------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Dapr.Actors.Client;
|
||||
|
@ -175,6 +177,160 @@ namespace Dapr.Actors.Runtime
|
|||
Assert.Equal(1, activator.DeleteCallCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeserializeTimer_Period_Iso8601_Time()
|
||||
{
|
||||
const string timerJson = "{\"callback\": \"TimerCallback\", \"period\": \"0h0m7s10ms\"}";
|
||||
await using var stream = new MemoryStream(Encoding.UTF8.GetBytes(timerJson));
|
||||
var result = await ActorManager.DeserializeAsync(stream);
|
||||
|
||||
Assert.Equal("TimerCallback", result.Callback);
|
||||
Assert.Equal(Array.Empty<byte>(), result.Data);
|
||||
Assert.Null(result.Ttl);
|
||||
Assert.Equal(TimeSpan.Zero, result.DueTime);
|
||||
Assert.Equal(TimeSpan.FromSeconds(7).Add(TimeSpan.FromMilliseconds(10)), result.Period);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeserializeTimer_Period_DaprFormat_Every()
|
||||
{
|
||||
const string timerJson = "{\"callback\": \"TimerCallback\", \"period\": \"@every 15s\"}";
|
||||
await using var stream = new MemoryStream(Encoding.UTF8.GetBytes(timerJson));
|
||||
var result = await ActorManager.DeserializeAsync(stream);
|
||||
|
||||
Assert.Equal("TimerCallback", result.Callback);
|
||||
Assert.Equal(Array.Empty<byte>(), result.Data);
|
||||
Assert.Null(result.Ttl);
|
||||
Assert.Equal(TimeSpan.Zero, result.DueTime);
|
||||
Assert.Equal(TimeSpan.FromSeconds(15), result.Period);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeserializeTimer_Period_DaprFormat_Every2()
|
||||
{
|
||||
const string timerJson = "{\"callback\": \"TimerCallback\", \"period\": \"@every 3h2m15s\"}";
|
||||
await using var stream = new MemoryStream(Encoding.UTF8.GetBytes(timerJson));
|
||||
var result = await ActorManager.DeserializeAsync(stream);
|
||||
|
||||
Assert.Equal("TimerCallback", result.Callback);
|
||||
Assert.Equal(Array.Empty<byte>(), result.Data);
|
||||
Assert.Null(result.Ttl);
|
||||
Assert.Equal(TimeSpan.Zero, result.DueTime);
|
||||
Assert.Equal(TimeSpan.FromHours(3).Add(TimeSpan.FromMinutes(2)).Add(TimeSpan.FromSeconds(15)), result.Period);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeserializeTimer_Period_DaprFormat_Monthly()
|
||||
{
|
||||
const string timerJson = "{\"callback\": \"TimerCallback\", \"period\": \"@monthly\"}";
|
||||
await using var stream = new MemoryStream(Encoding.UTF8.GetBytes(timerJson));
|
||||
var result = await ActorManager.DeserializeAsync(stream);
|
||||
|
||||
Assert.Equal("TimerCallback", result.Callback);
|
||||
Assert.Equal(Array.Empty<byte>(), result.Data);
|
||||
Assert.Null(result.Ttl);
|
||||
Assert.Equal(TimeSpan.Zero, result.DueTime);
|
||||
Assert.Equal(TimeSpan.FromDays(30), result.Period);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeserializeTimer_Period_DaprFormat_Weekly()
|
||||
{
|
||||
const string timerJson = "{\"callback\": \"TimerCallback\", \"period\": \"@weekly\"}";
|
||||
await using var stream = new MemoryStream(Encoding.UTF8.GetBytes(timerJson));
|
||||
var result = await ActorManager.DeserializeAsync(stream);
|
||||
|
||||
Assert.Equal("TimerCallback", result.Callback);
|
||||
Assert.Equal(Array.Empty<byte>(), result.Data);
|
||||
Assert.Null(result.Ttl);
|
||||
Assert.Equal(TimeSpan.Zero, result.DueTime);
|
||||
Assert.Equal(TimeSpan.FromDays(7), result.Period);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeserializeTimer_Period_DaprFormat_Daily()
|
||||
{
|
||||
const string timerJson = "{\"callback\": \"TimerCallback\", \"period\": \"@daily\"}";
|
||||
await using var stream = new MemoryStream(Encoding.UTF8.GetBytes(timerJson));
|
||||
var result = await ActorManager.DeserializeAsync(stream);
|
||||
|
||||
Assert.Equal("TimerCallback", result.Callback);
|
||||
Assert.Equal(Array.Empty<byte>(), result.Data);
|
||||
Assert.Null(result.Ttl);
|
||||
Assert.Equal(TimeSpan.Zero, result.DueTime);
|
||||
Assert.Equal(TimeSpan.FromDays(1), result.Period);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeserializeTimer_Period_DaprFormat_Hourly()
|
||||
{
|
||||
const string timerJson = "{\"callback\": \"TimerCallback\", \"period\": \"@hourly\"}";
|
||||
await using var stream = new MemoryStream(Encoding.UTF8.GetBytes(timerJson));
|
||||
var result = await ActorManager.DeserializeAsync(stream);
|
||||
|
||||
Assert.Equal("TimerCallback", result.Callback);
|
||||
Assert.Equal(Array.Empty<byte>(), result.Data);
|
||||
Assert.Null(result.Ttl);
|
||||
Assert.Equal(TimeSpan.Zero, result.DueTime);
|
||||
Assert.Equal(TimeSpan.FromHours(1), result.Period);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeserializeTimer_DueTime_DaprFormat_Hourly()
|
||||
{
|
||||
const string timerJson = "{\"callback\": \"TimerCallback\", \"dueTime\": \"@hourly\"}";
|
||||
await using var stream = new MemoryStream(Encoding.UTF8.GetBytes(timerJson));
|
||||
var result = await ActorManager.DeserializeAsync(stream);
|
||||
|
||||
Assert.Equal("TimerCallback", result.Callback);
|
||||
Assert.Equal(Array.Empty<byte>(), result.Data);
|
||||
Assert.Null(result.Ttl);
|
||||
Assert.Equal(TimeSpan.FromHours(1), result.DueTime);
|
||||
Assert.Equal(TimeSpan.Zero, result.Period);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeserializeTimer_DueTime_Iso8601Times()
|
||||
{
|
||||
const string timerJson = "{\"callback\": \"TimerCallback\", \"dueTime\": \"0h0m7s10ms\"}";
|
||||
await using var stream = new MemoryStream(Encoding.UTF8.GetBytes(timerJson));
|
||||
var result = await ActorManager.DeserializeAsync(stream);
|
||||
|
||||
Assert.Equal("TimerCallback", result.Callback);
|
||||
Assert.Equal(Array.Empty<byte>(), result.Data);
|
||||
Assert.Null(result.Ttl);
|
||||
Assert.Equal(TimeSpan.Zero, result.Period);
|
||||
Assert.Equal(TimeSpan.FromSeconds(7).Add(TimeSpan.FromMilliseconds(10)), result.DueTime);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeserializeTimer_Ttl_DaprFormat_Hourly()
|
||||
{
|
||||
const string timerJson = "{\"callback\": \"TimerCallback\", \"ttl\": \"@hourly\"}";
|
||||
await using var stream = new MemoryStream(Encoding.UTF8.GetBytes(timerJson));
|
||||
var result = await ActorManager.DeserializeAsync(stream);
|
||||
|
||||
Assert.Equal("TimerCallback", result.Callback);
|
||||
Assert.Equal(Array.Empty<byte>(), result.Data);
|
||||
Assert.Equal(TimeSpan.Zero, result.DueTime);
|
||||
Assert.Equal(TimeSpan.Zero, result.Period);
|
||||
Assert.Equal(TimeSpan.FromHours(1), result.Ttl);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeserializeTimer_Ttl_Iso8601Times()
|
||||
{
|
||||
const string timerJson = "{\"callback\": \"TimerCallback\", \"ttl\": \"0h0m7s10ms\"}";
|
||||
await using var stream = new MemoryStream(Encoding.UTF8.GetBytes(timerJson));
|
||||
var result = await ActorManager.DeserializeAsync(stream);
|
||||
|
||||
Assert.Equal("TimerCallback", result.Callback);
|
||||
Assert.Equal(Array.Empty<byte>(), result.Data);
|
||||
Assert.Equal(TimeSpan.Zero, result.DueTime);
|
||||
Assert.Equal(TimeSpan.Zero, result.Period);
|
||||
Assert.Equal(TimeSpan.FromSeconds(7).Add(TimeSpan.FromMilliseconds(10)), result.Ttl);
|
||||
}
|
||||
|
||||
private interface ITestActor : IActor { }
|
||||
|
||||
private class TestActor : Actor, ITestActor, IDisposable
|
||||
|
|
|
@ -17,7 +17,7 @@ namespace Dapr.Actors.Test.Runtime
|
|||
using Moq;
|
||||
using Xunit;
|
||||
using System;
|
||||
using FluentAssertions;
|
||||
using Shouldly;
|
||||
|
||||
public sealed class ActorRuntimeOptionsTests
|
||||
{
|
||||
|
@ -59,7 +59,7 @@ namespace Dapr.Actors.Test.Runtime
|
|||
var options = new ActorRuntimeOptions();
|
||||
Action action = () => options.ActorIdleTimeout = TimeSpan.FromSeconds(-1);
|
||||
|
||||
action.Should().Throw<ArgumentOutOfRangeException>();
|
||||
action.ShouldThrow<ArgumentOutOfRangeException>();
|
||||
}
|
||||
|
||||
|
||||
|
@ -78,7 +78,7 @@ namespace Dapr.Actors.Test.Runtime
|
|||
var options = new ActorRuntimeOptions();
|
||||
Action action = () => options.ActorScanInterval = TimeSpan.FromSeconds(-1);
|
||||
|
||||
action.Should().Throw<ArgumentOutOfRangeException>();
|
||||
action.ShouldThrow<ArgumentOutOfRangeException>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -96,7 +96,7 @@ namespace Dapr.Actors.Test.Runtime
|
|||
var options = new ActorRuntimeOptions();
|
||||
Action action = () => options.DrainOngoingCallTimeout = TimeSpan.FromSeconds(-1);
|
||||
|
||||
action.Should().Throw<ArgumentOutOfRangeException>();
|
||||
action.ShouldThrow<ArgumentOutOfRangeException>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -115,7 +115,7 @@ namespace Dapr.Actors.Test.Runtime
|
|||
var options = new ActorRuntimeOptions();
|
||||
Action action = () => options.JsonSerializerOptions = null;
|
||||
|
||||
action.Should().Throw<ArgumentNullException>();
|
||||
action.ShouldThrow<ArgumentNullException>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -124,7 +124,7 @@ namespace Dapr.Actors.Test.Runtime
|
|||
var options = new ActorRuntimeOptions();
|
||||
Action action = () => options.RemindersStoragePartitions = -1;
|
||||
|
||||
action.Should().Throw<ArgumentOutOfRangeException>();
|
||||
action.ShouldThrow<ArgumentOutOfRangeException>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
|
@ -11,119 +11,112 @@
|
|||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
namespace Dapr.Actors.Test.Runtime
|
||||
namespace Dapr.Actors.Test.Runtime;
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Dapr.Actors.Runtime;
|
||||
using Shouldly;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
public sealed class ActorTests
|
||||
{
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Dapr.Actors.Runtime;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
public sealed class ActorTests
|
||||
[Fact]
|
||||
public void TestNewActorWithMockStateManager()
|
||||
{
|
||||
[Fact]
|
||||
public void TestNewActorWithMockStateManager()
|
||||
{
|
||||
var mockStateManager = new Mock<IActorStateManager>();
|
||||
var testDemoActor = this.CreateTestDemoActor(mockStateManager.Object);
|
||||
testDemoActor.Host.Should().NotBeNull();
|
||||
testDemoActor.Id.Should().NotBeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task TestSaveState()
|
||||
{
|
||||
var mockStateManager = new Mock<IActorStateManager>();
|
||||
mockStateManager.Setup(manager => manager.SaveStateAsync(It.IsAny<CancellationToken>()));
|
||||
var testDemoActor = this.CreateTestDemoActor(mockStateManager.Object);
|
||||
await testDemoActor.SaveTestState();
|
||||
mockStateManager.Verify(manager => manager.SaveStateAsync(It.IsAny<CancellationToken>()), Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task TestResetStateAsync()
|
||||
{
|
||||
var mockStateManager = new Mock<IActorStateManager>();
|
||||
mockStateManager.Setup(manager => manager.ClearCacheAsync(It.IsAny<CancellationToken>()));
|
||||
var testDemoActor = this.CreateTestDemoActor(mockStateManager.Object);
|
||||
await testDemoActor.ResetTestStateAsync();
|
||||
mockStateManager.Verify(manager => manager.ClearCacheAsync(It.IsAny<CancellationToken>()), Times.Once);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("NonExistentMethod", "Timer callback method: NonExistentMethod does not exist in the Actor class: TestActor")]
|
||||
[InlineData("TimerCallbackTwoArguments", "Timer callback can accept only zero or one parameters")]
|
||||
[InlineData("TimerCallbackNonTaskReturnType", "Timer callback can only return type Task")]
|
||||
[InlineData("TimerCallbackOverloaded", "Timer callback method: TimerCallbackOverloaded cannot be overloaded.")]
|
||||
public void ValidateTimerCallback_CallbackMethodDoesNotMeetRequirements(string callback, string expectedErrorMessage)
|
||||
{
|
||||
var mockStateManager = new Mock<IActorStateManager>();
|
||||
mockStateManager.Setup(manager => manager.ClearCacheAsync(It.IsAny<CancellationToken>()));
|
||||
var testDemoActor = this.CreateTestDemoActor(mockStateManager.Object);
|
||||
|
||||
;
|
||||
FluentActions.Invoking(() =>
|
||||
testDemoActor.ValidateTimerCallback(testDemoActor.Host, callback))
|
||||
.Should().Throw<ArgumentException>()
|
||||
.WithMessage(expectedErrorMessage);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("TimerCallbackPrivate")]
|
||||
[InlineData("TimerCallbackProtected")]
|
||||
[InlineData("TimerCallbackInternal")]
|
||||
[InlineData("TimerCallbackPublicWithNoArguments")]
|
||||
[InlineData("TimerCallbackPublicWithOneArgument")]
|
||||
[InlineData("TimerCallbackStatic")]
|
||||
public void ValidateTimerCallback_CallbackMethodMeetsRequirements(string callback)
|
||||
{
|
||||
var mockStateManager = new Mock<IActorStateManager>();
|
||||
mockStateManager.Setup(manager => manager.ClearCacheAsync(It.IsAny<CancellationToken>()));
|
||||
var testDemoActor = this.CreateTestDemoActor(mockStateManager.Object);
|
||||
|
||||
;
|
||||
FluentActions.Invoking(() =>
|
||||
testDemoActor.ValidateTimerCallback(testDemoActor.Host, callback))
|
||||
.Should().NotThrow();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("TimerCallbackPrivate")]
|
||||
[InlineData("TimerCallbackPublicWithOneArgument")]
|
||||
[InlineData("TimerCallbackStatic")]
|
||||
public void GetMethodInfoUsingReflection_MethodsMatchingBindingFlags(string callback)
|
||||
{
|
||||
var mockStateManager = new Mock<IActorStateManager>();
|
||||
mockStateManager.Setup(manager => manager.ClearCacheAsync(It.IsAny<CancellationToken>()));
|
||||
var testDemoActor = this.CreateTestDemoActor(mockStateManager.Object);
|
||||
var methodInfo = testDemoActor.GetMethodInfoUsingReflection(testDemoActor.Host.ActorTypeInfo.ImplementationType, callback);
|
||||
Assert.NotNull(methodInfo);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("TestActor")] // Constructor
|
||||
public void GetMethodInfoUsingReflection_MethodsNotMatchingBindingFlags(string callback)
|
||||
{
|
||||
var mockStateManager = new Mock<IActorStateManager>();
|
||||
mockStateManager.Setup(manager => manager.ClearCacheAsync(It.IsAny<CancellationToken>()));
|
||||
var testDemoActor = this.CreateTestDemoActor(mockStateManager.Object);
|
||||
var methodInfo = testDemoActor.GetMethodInfoUsingReflection(testDemoActor.Host.ActorTypeInfo.ImplementationType, callback);
|
||||
Assert.Null(methodInfo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// On my test code I want to pass the mock statemanager all the time.
|
||||
/// </summary>
|
||||
/// <param name="actorStateManager">Mock StateManager.</param>
|
||||
/// <returns>TestActor.</returns>
|
||||
private TestActor CreateTestDemoActor(IActorStateManager actorStateManager)
|
||||
{
|
||||
var host = ActorHost.CreateForTest<TestActor>();
|
||||
var testActor = new TestActor(host, actorStateManager);
|
||||
return testActor;
|
||||
}
|
||||
|
||||
var mockStateManager = new Mock<IActorStateManager>();
|
||||
var testDemoActor = this.CreateTestDemoActor(mockStateManager.Object);
|
||||
testDemoActor.Host.ShouldNotBeNull();
|
||||
testDemoActor.Id.ShouldNotBeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task TestSaveState()
|
||||
{
|
||||
var mockStateManager = new Mock<IActorStateManager>();
|
||||
mockStateManager.Setup(manager => manager.SaveStateAsync(It.IsAny<CancellationToken>()));
|
||||
var testDemoActor = this.CreateTestDemoActor(mockStateManager.Object);
|
||||
await testDemoActor.SaveTestState();
|
||||
mockStateManager.Verify(manager => manager.SaveStateAsync(It.IsAny<CancellationToken>()), Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task TestResetStateAsync()
|
||||
{
|
||||
var mockStateManager = new Mock<IActorStateManager>();
|
||||
mockStateManager.Setup(manager => manager.ClearCacheAsync(It.IsAny<CancellationToken>()));
|
||||
var testDemoActor = this.CreateTestDemoActor(mockStateManager.Object);
|
||||
await testDemoActor.ResetTestStateAsync();
|
||||
mockStateManager.Verify(manager => manager.ClearCacheAsync(It.IsAny<CancellationToken>()), Times.Once);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("NonExistentMethod", "Timer callback method: NonExistentMethod does not exist in the Actor class: TestActor")]
|
||||
[InlineData("TimerCallbackTwoArguments", "Timer callback can accept only zero or one parameters")]
|
||||
[InlineData("TimerCallbackNonTaskReturnType", "Timer callback can only return type Task")]
|
||||
[InlineData("TimerCallbackOverloaded", "Timer callback method: TimerCallbackOverloaded cannot be overloaded.")]
|
||||
public void ValidateTimerCallback_CallbackMethodDoesNotMeetRequirements(string callback, string expectedErrorMessage)
|
||||
{
|
||||
var mockStateManager = new Mock<IActorStateManager>();
|
||||
mockStateManager.Setup(manager => manager.ClearCacheAsync(It.IsAny<CancellationToken>()));
|
||||
var testDemoActor = this.CreateTestDemoActor(mockStateManager.Object);
|
||||
|
||||
Should.Throw<ArgumentException>(() => testDemoActor.ValidateTimerCallback(testDemoActor.Host, callback))
|
||||
.Message.ShouldBe(expectedErrorMessage);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("TimerCallbackPrivate")]
|
||||
[InlineData("TimerCallbackProtected")]
|
||||
[InlineData("TimerCallbackInternal")]
|
||||
[InlineData("TimerCallbackPublicWithNoArguments")]
|
||||
[InlineData("TimerCallbackPublicWithOneArgument")]
|
||||
[InlineData("TimerCallbackStatic")]
|
||||
public void ValidateTimerCallback_CallbackMethodMeetsRequirements(string callback)
|
||||
{
|
||||
var mockStateManager = new Mock<IActorStateManager>();
|
||||
mockStateManager.Setup(manager => manager.ClearCacheAsync(It.IsAny<CancellationToken>()));
|
||||
var testDemoActor = this.CreateTestDemoActor(mockStateManager.Object);
|
||||
|
||||
Should.NotThrow(() => testDemoActor.ValidateTimerCallback(testDemoActor.Host, callback));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("TimerCallbackPrivate")]
|
||||
[InlineData("TimerCallbackPublicWithOneArgument")]
|
||||
[InlineData("TimerCallbackStatic")]
|
||||
public void GetMethodInfoUsingReflection_MethodsMatchingBindingFlags(string callback)
|
||||
{
|
||||
var mockStateManager = new Mock<IActorStateManager>();
|
||||
mockStateManager.Setup(manager => manager.ClearCacheAsync(It.IsAny<CancellationToken>()));
|
||||
var testDemoActor = this.CreateTestDemoActor(mockStateManager.Object);
|
||||
var methodInfo = testDemoActor.GetMethodInfoUsingReflection(testDemoActor.Host.ActorTypeInfo.ImplementationType, callback);
|
||||
Assert.NotNull(methodInfo);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("TestActor")] // Constructor
|
||||
public void GetMethodInfoUsingReflection_MethodsNotMatchingBindingFlags(string callback)
|
||||
{
|
||||
var mockStateManager = new Mock<IActorStateManager>();
|
||||
mockStateManager.Setup(manager => manager.ClearCacheAsync(It.IsAny<CancellationToken>()));
|
||||
var testDemoActor = this.CreateTestDemoActor(mockStateManager.Object);
|
||||
var methodInfo = testDemoActor.GetMethodInfoUsingReflection(testDemoActor.Host.ActorTypeInfo.ImplementationType, callback);
|
||||
Assert.Null(methodInfo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// On my test code I want to pass the mock statemanager all the time.
|
||||
/// </summary>
|
||||
/// <param name="actorStateManager">Mock StateManager.</param>
|
||||
/// <returns>TestActor.</returns>
|
||||
private TestActor CreateTestDemoActor(IActorStateManager actorStateManager)
|
||||
{
|
||||
var host = ActorHost.CreateForTest<TestActor>();
|
||||
var testActor = new TestActor(host, actorStateManager);
|
||||
return testActor;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,9 +1,24 @@
|
|||
// ------------------------------------------------------------
|
||||
// ------------------------------------------------------------------------
|
||||
// Copyright 2025 The Dapr Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
// ------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
@ -11,78 +26,156 @@ using Moq;
|
|||
using Xunit;
|
||||
using JsonSerializer = System.Text.Json.JsonSerializer;
|
||||
|
||||
namespace Dapr.Actors.Runtime
|
||||
namespace Dapr.Actors.Runtime;
|
||||
|
||||
public sealed class DefaultActorTimerManagerTests
|
||||
{
|
||||
public sealed class DefaultActorTimerManagerTests
|
||||
/// <summary>
|
||||
/// When register reminder is called, interactor is called with correct data.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[Fact]
|
||||
public async Task RegisterReminderAsync_CallsInteractor_WithCorrectData()
|
||||
{
|
||||
/// <summary>
|
||||
/// When register reminder is called, interactor is called with correct data.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[Fact]
|
||||
public async Task RegisterReminderAsync_CallsInteractor_WithCorrectData()
|
||||
var actorId = "123";
|
||||
var actorType = "abc";
|
||||
var interactor = new Mock<TestDaprInteractor>();
|
||||
var defaultActorTimerManager = new DefaultActorTimerManager(interactor.Object);
|
||||
var actorReminder = new ActorReminder(actorType, new ActorId(actorId), "remindername", Array.Empty<byte>(), TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1));
|
||||
var actualData = string.Empty;
|
||||
|
||||
interactor
|
||||
.Setup(d => d.RegisterReminderAsync(actorType, actorId, "remindername", It.Is<string>(data => !string.IsNullOrEmpty(data)), It.IsAny<CancellationToken>()))
|
||||
.Callback<string, string, string, string, CancellationToken>((innerType, innerId, reminderName, data, token) => {
|
||||
actualData = data;
|
||||
})
|
||||
.Returns(Task.CompletedTask);
|
||||
|
||||
await defaultActorTimerManager.RegisterReminderAsync(actorReminder);
|
||||
|
||||
JsonElement json = JsonSerializer.Deserialize<dynamic>(actualData);
|
||||
|
||||
var isPeriodSet = json.TryGetProperty("period", out var period);
|
||||
var isdDueTimeSet = json.TryGetProperty("dueTime", out var dueTime);
|
||||
|
||||
Assert.True(isPeriodSet);
|
||||
Assert.True(isdDueTimeSet);
|
||||
|
||||
Assert.Equal("0h1m0s0ms", period.GetString());
|
||||
Assert.Equal("0h1m0s0ms", dueTime.GetString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When register reminder is called with repetition, interactor is called with correct data.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[Fact]
|
||||
public async Task RegisterReminderAsync_WithRepetition_CallsInteractor_WithCorrectData()
|
||||
{
|
||||
const string actorId = "123";
|
||||
const string actorType = "abc";
|
||||
var interactor = new Mock<TestDaprInteractor>();
|
||||
var defaultActorTimerManager = new DefaultActorTimerManager(interactor.Object);
|
||||
var actorReminder = new ActorReminder(actorType, new ActorId(actorId), "remindername", new byte[] { }, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1), 10);
|
||||
var actualData = string.Empty;
|
||||
|
||||
interactor
|
||||
.Setup(d => d.RegisterReminderAsync(actorType, actorId, "remindername", It.Is<string>(data => !string.IsNullOrEmpty(data)), It.IsAny<CancellationToken>()))
|
||||
.Callback<string, string, string, string, CancellationToken>((innerType, innerActorId, reminderName, data, token) => {
|
||||
actualData = data;
|
||||
})
|
||||
.Returns(Task.CompletedTask);
|
||||
|
||||
await defaultActorTimerManager.RegisterReminderAsync(actorReminder);
|
||||
|
||||
JsonElement json = JsonSerializer.Deserialize<dynamic>(actualData);
|
||||
|
||||
var isPeriodSet = json.TryGetProperty("period", out var period);
|
||||
var isdDueTimeSet = json.TryGetProperty("dueTime", out var dueTime);
|
||||
|
||||
Assert.True(isPeriodSet);
|
||||
Assert.True(isdDueTimeSet);
|
||||
|
||||
Assert.Equal("R10/PT1M", period.GetString());
|
||||
Assert.Equal("0h1m0s0ms", dueTime.GetString());
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
var actorId = "123";
|
||||
var actorType = "abc";
|
||||
var interactor = new Mock<TestDaprInteractor>();
|
||||
var defaultActorTimerManager = new DefaultActorTimerManager(interactor.Object);
|
||||
var actorReminder = new ActorReminder(actorType, new ActorId(actorId), "remindername", new byte[] { }, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1));
|
||||
var actualData = string.Empty;
|
||||
Content = new StringContent(serializedActorReminderInfo)
|
||||
};
|
||||
|
||||
interactor
|
||||
.Setup(d => d.RegisterReminderAsync(actorType, actorId, "remindername", It.Is<string>(data => !string.IsNullOrEmpty(data)), It.IsAny<CancellationToken>()))
|
||||
.Callback<string, string, string, string, CancellationToken>((actorType, actorID, reminderName, data, token) => {
|
||||
actualData = data;
|
||||
})
|
||||
.Returns(Task.CompletedTask);
|
||||
|
||||
await defaultActorTimerManager.RegisterReminderAsync(actorReminder);
|
||||
|
||||
JsonElement json = JsonSerializer.Deserialize<dynamic>(actualData);
|
||||
|
||||
var isPeriodSet = json.TryGetProperty("period", out var period);
|
||||
var isdDueTimeSet = json.TryGetProperty("dueTime", out var dueTime);
|
||||
//Register the response
|
||||
interactor
|
||||
.Setup(d => d.GetReminderAsync(actorType, actorId, reminderName, It.IsAny<CancellationToken>()))
|
||||
.Callback<string, string, string, CancellationToken>((type, id, name, token) => {
|
||||
})
|
||||
.Returns(Task.FromResult(reminderResponse));
|
||||
var defaultActorTimerManager = new DefaultActorTimerManager(interactor.Object);
|
||||
|
||||
Assert.True(isPeriodSet);
|
||||
Assert.True(isdDueTimeSet);
|
||||
var reminderResult = await defaultActorTimerManager.GetReminderAsync(new ActorReminderToken(actorType, new ActorId(actorId), reminderName));
|
||||
Assert.NotNull(reminderResult);
|
||||
|
||||
Assert.Equal("0h1m0s0ms", period.GetString());
|
||||
Assert.Equal("0h1m0s0ms", dueTime.GetString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When register reminder is called with repetition, interactor is called with correct data.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[Fact]
|
||||
public async Task RegisterReminderAsync_WithRepetition_CallsInteractor_WithCorrectData()
|
||||
{
|
||||
var actorId = "123";
|
||||
var actorType = "abc";
|
||||
var interactor = new Mock<TestDaprInteractor>();
|
||||
var defaultActorTimerManager = new DefaultActorTimerManager(interactor.Object);
|
||||
var actorReminder = new ActorReminder(actorType, new ActorId(actorId), "remindername", new byte[] { }, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1), 10);
|
||||
var actualData = string.Empty;
|
||||
|
||||
interactor
|
||||
.Setup(d => d.RegisterReminderAsync(actorType, actorId, "remindername", It.Is<string>(data => !string.IsNullOrEmpty(data)), It.IsAny<CancellationToken>()))
|
||||
.Callback<string, string, string, string, CancellationToken>((actorType, actorID, reminderName, data, token) => {
|
||||
actualData = data;
|
||||
})
|
||||
.Returns(Task.CompletedTask);
|
||||
|
||||
await defaultActorTimerManager.RegisterReminderAsync(actorReminder);
|
||||
|
||||
JsonElement json = JsonSerializer.Deserialize<dynamic>(actualData);
|
||||
|
||||
var isPeriodSet = json.TryGetProperty("period", out var period);
|
||||
var isdDueTimeSet = json.TryGetProperty("dueTime", out var dueTime);
|
||||
|
||||
Assert.True(isPeriodSet);
|
||||
Assert.True(isdDueTimeSet);
|
||||
|
||||
Assert.Equal("R10/PT1M", period.GetString());
|
||||
Assert.Equal("0h1m0s0ms", dueTime.GetString());
|
||||
}
|
||||
Assert.Equal(dueTime, reminderResult.DueTime);
|
||||
Assert.Equal(state, reminderResult.State);
|
||||
Assert.Equal(period, reminderResult.Period);
|
||||
Assert.Equal(reminderName, reminderResult.Name);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,69 +14,68 @@
|
|||
using System.IO;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Xml;
|
||||
using FluentAssertions;
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
|
||||
namespace Dapr.Actors.Serialization
|
||||
namespace Dapr.Actors.Serialization;
|
||||
|
||||
public class ActorReferenceDataContractSerializationTest
|
||||
{
|
||||
public class ActorReferenceDataContractSerializationTest
|
||||
[Fact]
|
||||
public void CanSerializeAndDeserializeActorId()
|
||||
{
|
||||
[Fact]
|
||||
public void CanSerializeAndDeserializeActorId()
|
||||
var id = ActorId.CreateRandom();
|
||||
DataContractSerializer serializer = new DataContractSerializer(id.GetType());
|
||||
MemoryStream ms = new MemoryStream();
|
||||
serializer.WriteObject(ms, id);
|
||||
ms.Position = 0;
|
||||
|
||||
XmlDocument xmlDoc = new XmlDocument();
|
||||
xmlDoc.Load(ms);
|
||||
xmlDoc.DocumentElement.Name.ShouldBe("ActorId");
|
||||
xmlDoc.DocumentElement.InnerText.ShouldBe(id.ToString());
|
||||
ms.Position = 0;
|
||||
|
||||
var deserializedActorId = serializer.ReadObject(ms) as ActorId;
|
||||
deserializedActorId.ShouldBe(id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanSerializeAndDeserializeNullActorId()
|
||||
{
|
||||
ActorId id = null;
|
||||
DataContractSerializer serializer = new DataContractSerializer(typeof(ActorId));
|
||||
MemoryStream ms = new MemoryStream();
|
||||
serializer.WriteObject(ms, id);
|
||||
ms.Position = 0;
|
||||
|
||||
XmlDocument xmlDoc = new XmlDocument();
|
||||
xmlDoc.Load(ms);
|
||||
xmlDoc.DocumentElement.Name.ShouldBe("ActorId");
|
||||
xmlDoc.DocumentElement.InnerText.ShouldBe(string.Empty);
|
||||
|
||||
ms.Position = 0;
|
||||
var deserializedActorId = serializer.ReadObject(ms) as ActorId;
|
||||
deserializedActorId.ShouldBe(id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanRoundTripActorReference()
|
||||
{
|
||||
var actorId = new ActorId("abc");
|
||||
var actorReference = new ActorReference()
|
||||
{
|
||||
var id = ActorId.CreateRandom();
|
||||
DataContractSerializer serializer = new DataContractSerializer(id.GetType());
|
||||
MemoryStream ms = new MemoryStream();
|
||||
serializer.WriteObject(ms, id);
|
||||
ms.Position = 0;
|
||||
ActorId = actorId,
|
||||
ActorType = "TestActor"
|
||||
};
|
||||
|
||||
XmlDocument xmlDoc = new XmlDocument();
|
||||
xmlDoc.Load(ms);
|
||||
xmlDoc.DocumentElement.Name.Should().Be("ActorId");
|
||||
xmlDoc.DocumentElement.InnerText.Should().Be(id.ToString());
|
||||
ms.Position = 0;
|
||||
DataContractSerializer serializer = new DataContractSerializer(actorReference.GetType());
|
||||
MemoryStream ms = new MemoryStream();
|
||||
serializer.WriteObject(ms, actorReference);
|
||||
ms.Position = 0;
|
||||
|
||||
var deserializedActorId = serializer.ReadObject(ms) as ActorId;
|
||||
deserializedActorId.Should().Be(id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanSerializeAndDeserializeNullActorId()
|
||||
{
|
||||
ActorId id = null;
|
||||
DataContractSerializer serializer = new DataContractSerializer(typeof(ActorId));
|
||||
MemoryStream ms = new MemoryStream();
|
||||
serializer.WriteObject(ms, id);
|
||||
ms.Position = 0;
|
||||
|
||||
XmlDocument xmlDoc = new XmlDocument();
|
||||
xmlDoc.Load(ms);
|
||||
xmlDoc.DocumentElement.Name.Should().Be("ActorId");
|
||||
xmlDoc.DocumentElement.InnerText.Should().Be(string.Empty);
|
||||
|
||||
ms.Position = 0;
|
||||
var deserializedActorId = serializer.ReadObject(ms) as ActorId;
|
||||
deserializedActorId.Should().Be(id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanRoundTripActorReference()
|
||||
{
|
||||
var actorId = new ActorId("abc");
|
||||
var actorReference = new ActorReference()
|
||||
{
|
||||
ActorId = actorId,
|
||||
ActorType = "TestActor"
|
||||
};
|
||||
|
||||
DataContractSerializer serializer = new DataContractSerializer(actorReference.GetType());
|
||||
MemoryStream ms = new MemoryStream();
|
||||
serializer.WriteObject(ms, actorReference);
|
||||
ms.Position = 0;
|
||||
|
||||
var deserializedActorRef = serializer.ReadObject(ms) as ActorReference;
|
||||
deserializedActorRef.ActorId.Should().Be(actorId);
|
||||
deserializedActorRef.ActorType.Should().Be("TestActor");
|
||||
}
|
||||
var deserializedActorRef = serializer.ReadObject(ms) as ActorReference;
|
||||
deserializedActorRef.ActorId.ShouldBe(actorId);
|
||||
deserializedActorRef.ActorType.ShouldBe("TestActor");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,101 +15,100 @@ using System.Text.Json;
|
|||
using System.Text.Json.Serialization;
|
||||
using Xunit;
|
||||
|
||||
namespace Dapr.Actors.Serialization
|
||||
namespace Dapr.Actors.Serialization;
|
||||
|
||||
public class ActorIdJsonConverterTest
|
||||
{
|
||||
public class ActorIdJsonConverterTest
|
||||
[Fact]
|
||||
public void CanSerializeActorId()
|
||||
{
|
||||
[Fact]
|
||||
public void CanSerializeActorId()
|
||||
{
|
||||
var id = ActorId.CreateRandom();
|
||||
var document = new { actor = id, };
|
||||
var id = ActorId.CreateRandom();
|
||||
var document = new { actor = id, };
|
||||
|
||||
// We use strings for ActorId - the result should be the same as passing the Id directly.
|
||||
var expected = JsonSerializer.Serialize(new { actor = id.GetId(), });
|
||||
// We use strings for ActorId - the result should be the same as passing the Id directly.
|
||||
var expected = JsonSerializer.Serialize(new { actor = id.GetId(), });
|
||||
|
||||
var serialized = JsonSerializer.Serialize(document);
|
||||
var serialized = JsonSerializer.Serialize(document);
|
||||
|
||||
Assert.Equal(expected, serialized);
|
||||
}
|
||||
Assert.Equal(expected, serialized);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanSerializeNullActorId()
|
||||
{
|
||||
var document = new { actor = (ActorId)null, };
|
||||
[Fact]
|
||||
public void CanSerializeNullActorId()
|
||||
{
|
||||
var document = new { actor = (ActorId)null, };
|
||||
|
||||
var expected = JsonSerializer.Serialize(new { actor = (string)null, });
|
||||
var expected = JsonSerializer.Serialize(new { actor = (string)null, });
|
||||
|
||||
var serialized = JsonSerializer.Serialize(document);
|
||||
var serialized = JsonSerializer.Serialize(document);
|
||||
|
||||
Assert.Equal(expected, serialized);
|
||||
}
|
||||
Assert.Equal(expected, serialized);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanDeserializeActorId()
|
||||
{
|
||||
var id = ActorId.CreateRandom().GetId();
|
||||
var document = $@"
|
||||
[Fact]
|
||||
public void CanDeserializeActorId()
|
||||
{
|
||||
var id = ActorId.CreateRandom().GetId();
|
||||
var document = $@"
|
||||
{{
|
||||
""actor"": ""{id}""
|
||||
}}";
|
||||
|
||||
var deserialized = JsonSerializer.Deserialize<ActorHolder>(document);
|
||||
var deserialized = JsonSerializer.Deserialize<ActorHolder>(document);
|
||||
|
||||
Assert.Equal(id, deserialized.Actor.GetId());
|
||||
}
|
||||
Assert.Equal(id, deserialized.Actor.GetId());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanDeserializeNullActorId()
|
||||
{
|
||||
const string document = @"
|
||||
[Fact]
|
||||
public void CanDeserializeNullActorId()
|
||||
{
|
||||
const string document = @"
|
||||
{
|
||||
""actor"": null
|
||||
}";
|
||||
|
||||
var deserialized = JsonSerializer.Deserialize<ActorHolder>(document);
|
||||
var deserialized = JsonSerializer.Deserialize<ActorHolder>(document);
|
||||
|
||||
Assert.Null(deserialized.Actor);
|
||||
}
|
||||
Assert.Null(deserialized.Actor);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("{ \"actor\": ")]
|
||||
[InlineData("{ \"actor\": \"hi")]
|
||||
[InlineData("{ \"actor\": }")]
|
||||
[InlineData("{ \"actor\": 3 }")]
|
||||
[InlineData("{ \"actor\": \"\"}")]
|
||||
[InlineData("{ \"actor\": \" \"}")]
|
||||
public void CanReportErrorsFromInvalidData(string document)
|
||||
[Theory]
|
||||
[InlineData("{ \"actor\": ")]
|
||||
[InlineData("{ \"actor\": \"hi")]
|
||||
[InlineData("{ \"actor\": }")]
|
||||
[InlineData("{ \"actor\": 3 }")]
|
||||
[InlineData("{ \"actor\": \"\"}")]
|
||||
[InlineData("{ \"actor\": \" \"}")]
|
||||
public void CanReportErrorsFromInvalidData(string document)
|
||||
{
|
||||
// The error messages are provided by the serializer, don't test them here
|
||||
// that would be fragile.
|
||||
Assert.Throws<JsonException>(() =>
|
||||
{
|
||||
// The error messages are provided by the serializer, don't test them here
|
||||
// that would be fragile.
|
||||
Assert.Throws<JsonException>(() =>
|
||||
{
|
||||
JsonSerializer.Deserialize<ActorHolder>(document);
|
||||
});
|
||||
}
|
||||
JsonSerializer.Deserialize<ActorHolder>(document);
|
||||
});
|
||||
}
|
||||
|
||||
// Regression test for #444
|
||||
[Fact]
|
||||
public void CanRoundTripActorReference()
|
||||
// Regression test for #444
|
||||
[Fact]
|
||||
public void CanRoundTripActorReference()
|
||||
{
|
||||
var reference = new ActorReference()
|
||||
{
|
||||
var reference = new ActorReference()
|
||||
{
|
||||
ActorId = ActorId.CreateRandom(),
|
||||
ActorType = "TestActor",
|
||||
};
|
||||
ActorId = ActorId.CreateRandom(),
|
||||
ActorType = "TestActor",
|
||||
};
|
||||
|
||||
var serialized = JsonSerializer.Serialize(reference);
|
||||
var deserialized = JsonSerializer.Deserialize<ActorReference>(serialized);
|
||||
var serialized = JsonSerializer.Serialize(reference);
|
||||
var deserialized = JsonSerializer.Deserialize<ActorReference>(serialized);
|
||||
|
||||
Assert.Equal(reference.ActorId.GetId(), deserialized.ActorId.GetId());
|
||||
Assert.Equal(reference.ActorType, deserialized.ActorType);
|
||||
}
|
||||
Assert.Equal(reference.ActorId.GetId(), deserialized.ActorId.GetId());
|
||||
Assert.Equal(reference.ActorType, deserialized.ActorType);
|
||||
}
|
||||
|
||||
private class ActorHolder
|
||||
{
|
||||
[JsonPropertyName("actor")]
|
||||
public ActorId Actor { get; set; }
|
||||
}
|
||||
private class ActorHolder
|
||||
{
|
||||
[JsonPropertyName("actor")]
|
||||
public ActorId Actor { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,30 +16,29 @@ using System.Text.Json;
|
|||
using Xunit;
|
||||
|
||||
#pragma warning disable 0618
|
||||
namespace Dapr.Actors.Runtime
|
||||
namespace Dapr.Actors.Runtime;
|
||||
|
||||
public class TimerInfoJsonConverterTest
|
||||
{
|
||||
public class TimerInfoJsonConverterTest
|
||||
[Theory]
|
||||
[InlineData("test", new byte[] {1, 2, 3}, 2, 4)]
|
||||
[InlineData(null, new byte[] {1}, 2, 4)]
|
||||
[InlineData("test", null, 2, 4)]
|
||||
[InlineData("test", new byte[] {1}, 0, 4)]
|
||||
[InlineData("test", new byte[] {1}, 2, 0)]
|
||||
public void CanSerializeAndDeserializeTimerInfo(string callback, byte[] state, int dueTime, int period)
|
||||
{
|
||||
[Theory]
|
||||
[InlineData("test", new byte[] {1, 2, 3}, 2, 4)]
|
||||
[InlineData(null, new byte[] {1}, 2, 4)]
|
||||
[InlineData("test", null, 2, 4)]
|
||||
[InlineData("test", new byte[] {1}, 0, 4)]
|
||||
[InlineData("test", new byte[] {1}, 2, 0)]
|
||||
public void CanSerializeAndDeserializeTimerInfo(string callback, byte[] state, int dueTime, int period)
|
||||
{
|
||||
var timerInfo = new TimerInfo(callback, state, TimeSpan.FromSeconds(dueTime), TimeSpan.FromSeconds(period));
|
||||
var timerInfo = new TimerInfo(callback, state, TimeSpan.FromSeconds(dueTime), TimeSpan.FromSeconds(period));
|
||||
|
||||
// We use strings for ActorId - the result should be the same as passing the Id directly.
|
||||
var serializedTimerInfo = JsonSerializer.Serialize<TimerInfo>(timerInfo);
|
||||
// We use strings for ActorId - the result should be the same as passing the Id directly.
|
||||
var serializedTimerInfo = JsonSerializer.Serialize<TimerInfo>(timerInfo);
|
||||
|
||||
var deserializedTimerInfo = JsonSerializer.Deserialize<TimerInfo>(serializedTimerInfo);
|
||||
var deserializedTimerInfo = JsonSerializer.Deserialize<TimerInfo>(serializedTimerInfo);
|
||||
|
||||
Assert.Equal(timerInfo.Callback, deserializedTimerInfo.Callback);
|
||||
Assert.Equal(timerInfo.Data, deserializedTimerInfo.Data);
|
||||
Assert.Equal(timerInfo.DueTime, deserializedTimerInfo.DueTime);
|
||||
Assert.Equal(timerInfo.Period, deserializedTimerInfo.Period);
|
||||
}
|
||||
Assert.Equal(timerInfo.Callback, deserializedTimerInfo.Callback);
|
||||
Assert.Equal(timerInfo.Data, deserializedTimerInfo.Data);
|
||||
Assert.Equal(timerInfo.DueTime, deserializedTimerInfo.DueTime);
|
||||
Assert.Equal(timerInfo.Period, deserializedTimerInfo.Period);
|
||||
}
|
||||
}
|
||||
#pragma warning restore 0618
|
||||
|
|
|
@ -1,159 +1,159 @@
|
|||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Dapr.Actors.Communication;
|
||||
|
||||
namespace Dapr.Actors
|
||||
namespace Dapr.Actors;
|
||||
|
||||
/// <summary>
|
||||
/// A Wrapper class for IDaprInteractor which is mainly created for testing.
|
||||
/// </summary>
|
||||
public class TestDaprInteractor : IDaprInteractor
|
||||
{
|
||||
private TestDaprInteractor _testDaprInteractor;
|
||||
|
||||
/// <summary>
|
||||
/// A Wrapper class for IDaprInteractor which is mainly created for testing.
|
||||
/// The TestDaprInteractor constructor.
|
||||
/// </summary>
|
||||
public class TestDaprInteractor : IDaprInteractor
|
||||
/// <param name="testDaprInteractor"></param>
|
||||
public TestDaprInteractor(TestDaprInteractor testDaprInteractor)
|
||||
{
|
||||
private TestDaprInteractor _testDaprInteractor;
|
||||
_testDaprInteractor = testDaprInteractor;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The TestDaprInteractor constructor.
|
||||
/// </summary>
|
||||
/// <param name="testDaprInteractor"></param>
|
||||
public TestDaprInteractor(TestDaprInteractor testDaprInteractor)
|
||||
{
|
||||
_testDaprInteractor = testDaprInteractor;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The TestDaprInteractor constructor.
|
||||
/// </summary>
|
||||
public TestDaprInteractor()
|
||||
{
|
||||
/// <summary>
|
||||
/// The TestDaprInteractor constructor.
|
||||
/// </summary>
|
||||
public TestDaprInteractor()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register a reminder.
|
||||
/// </summary>
|
||||
/// <param name="actorType">Type of actor.</param>
|
||||
/// <param name="actorId">ActorId.</param>
|
||||
/// <param name="reminderName">Name of reminder to register.</param>
|
||||
/// <param name="data">JSON reminder data as per the Dapr spec.</param>
|
||||
/// <param name="cancellationToken">Cancels the operation.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the result of the asynchronous operation.</returns>
|
||||
public virtual async Task RegisterReminderAsync(string actorType, string actorId, string reminderName, string data,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
await _testDaprInteractor.RegisterReminderAsync(actorType, actorId, reminderName, data);
|
||||
}
|
||||
/// <summary>
|
||||
/// Register a reminder.
|
||||
/// </summary>
|
||||
/// <param name="actorType">Type of actor.</param>
|
||||
/// <param name="actorId">ActorId.</param>
|
||||
/// <param name="reminderName">Name of reminder to register.</param>
|
||||
/// <param name="data">JSON reminder data as per the Dapr spec.</param>
|
||||
/// <param name="cancellationToken">Cancels the operation.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the result of the asynchronous operation.</returns>
|
||||
public virtual async Task RegisterReminderAsync(string actorType, string actorId, string reminderName, string data,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
await _testDaprInteractor.RegisterReminderAsync(actorType, actorId, reminderName, data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes an Actor method on Dapr without remoting.
|
||||
/// </summary>
|
||||
/// <param name="actorType">Type of actor.</param>
|
||||
/// <param name="actorId">ActorId.</param>
|
||||
/// <param name="methodName">Method name to invoke.</param>
|
||||
/// <param name="jsonPayload">Serialized body.</param>
|
||||
/// <param name="cancellationToken">Cancels the operation.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
public Task<Stream> InvokeActorMethodWithoutRemotingAsync(string actorType, string actorId, string methodName,
|
||||
string jsonPayload, CancellationToken cancellationToken = default)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
/// <summary>
|
||||
/// Invokes an Actor method on Dapr without remoting.
|
||||
/// </summary>
|
||||
/// <param name="actorType">Type of actor.</param>
|
||||
/// <param name="actorId">ActorId.</param>
|
||||
/// <param name="methodName">Method name to invoke.</param>
|
||||
/// <param name="jsonPayload">Serialized body.</param>
|
||||
/// <param name="cancellationToken">Cancels the operation.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
public Task<Stream> InvokeActorMethodWithoutRemotingAsync(string actorType, string actorId, string methodName,
|
||||
string jsonPayload, CancellationToken cancellationToken = default)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves state batch to Dapr.
|
||||
/// </summary>
|
||||
/// <param name="actorType">Type of actor.</param>
|
||||
/// <param name="actorId">ActorId.</param>
|
||||
/// <param name="data">JSON data with state changes as per the Dapr spec for transaction state update.</param>
|
||||
/// <param name="cancellationToken">Cancels the operation.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
public virtual async Task SaveStateTransactionallyAsync(string actorType, string actorId, string data,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
await _testDaprInteractor.SaveStateTransactionallyAsync(actorType, actorId, data);
|
||||
}
|
||||
/// <summary>
|
||||
/// Saves state batch to Dapr.
|
||||
/// </summary>
|
||||
/// <param name="actorType">Type of actor.</param>
|
||||
/// <param name="actorId">ActorId.</param>
|
||||
/// <param name="data">JSON data with state changes as per the Dapr spec for transaction state update.</param>
|
||||
/// <param name="cancellationToken">Cancels the operation.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
public virtual async Task SaveStateTransactionallyAsync(string actorType, string actorId, string data,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
await _testDaprInteractor.SaveStateTransactionallyAsync(actorType, actorId, data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves a state to Dapr.
|
||||
/// </summary>
|
||||
/// <param name="actorType">Type of actor.</param>
|
||||
/// <param name="actorId">ActorId.</param>
|
||||
/// <param name="keyName">Name of key to get value for.</param>
|
||||
/// <param name="cancellationToken">Cancels the operation.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
public virtual async Task<ActorStateResponse<string>> GetStateAsync(string actorType, string actorId, string keyName, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await _testDaprInteractor.GetStateAsync(actorType, actorId, keyName);
|
||||
}
|
||||
/// <summary>
|
||||
/// Saves a state to Dapr.
|
||||
/// </summary>
|
||||
/// <param name="actorType">Type of actor.</param>
|
||||
/// <param name="actorId">ActorId.</param>
|
||||
/// <param name="keyName">Name of key to get value for.</param>
|
||||
/// <param name="cancellationToken">Cancels the operation.</param>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
public virtual async Task<ActorStateResponse<string>> GetStateAsync(string actorType, string actorId, string keyName, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await _testDaprInteractor.GetStateAsync(actorType, actorId, keyName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes Actor method.
|
||||
/// </summary>
|
||||
/// <param name="serializersManager">Serializers manager for remoting calls.</param>
|
||||
/// <param name="remotingRequestRequestMessage">Actor Request Message.</param>
|
||||
/// <param name="cancellationToken">Cancels the operation.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the result of the asynchronous operation.</returns>
|
||||
Task<IActorResponseMessage> IDaprInteractor.InvokeActorMethodWithRemotingAsync(ActorMessageSerializersManager serializersManager,
|
||||
IActorRequestMessage remotingRequestRequestMessage, CancellationToken cancellationToken)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
/// <summary>
|
||||
/// Invokes Actor method.
|
||||
/// </summary>
|
||||
/// <param name="serializersManager">Serializers manager for remoting calls.</param>
|
||||
/// <param name="remotingRequestRequestMessage">Actor Request Message.</param>
|
||||
/// <param name="cancellationToken">Cancels the operation.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the result of the asynchronous operation.</returns>
|
||||
Task<IActorResponseMessage> IDaprInteractor.InvokeActorMethodWithRemotingAsync(ActorMessageSerializersManager serializersManager,
|
||||
IActorRequestMessage remotingRequestRequestMessage, CancellationToken cancellationToken)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a reminder.
|
||||
/// </summary>
|
||||
/// <param name="actorType">Type of actor.</param>
|
||||
/// <param name="actorId">ActorId.</param>
|
||||
/// <param name="reminderName">Name of reminder to unregister.</param>
|
||||
/// <param name="cancellationToken">Cancels the operation.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the result of the asynchronous operation.</returns>
|
||||
public Task<Stream> GetReminderAsync(string actorType, string actorId, string reminderName,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets a reminder.
|
||||
/// </summary>
|
||||
/// <param name="actorType">Type of actor.</param>
|
||||
/// <param name="actorId">ActorId.</param>
|
||||
/// <param name="reminderName">Name of reminder to unregister.</param>
|
||||
/// <param name="cancellationToken">Cancels the operation.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the result of the asynchronous operation.</returns>
|
||||
public virtual async Task<HttpResponseMessage> GetReminderAsync(string actorType, string actorId, string reminderName,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await _testDaprInteractor.GetReminderAsync(actorType, actorId, reminderName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unregisters a reminder.
|
||||
/// </summary>
|
||||
/// <param name="actorType">Type of actor.</param>
|
||||
/// <param name="actorId">ActorId.</param>
|
||||
/// <param name="reminderName">Name of reminder to unregister.</param>
|
||||
/// <param name="cancellationToken">Cancels the operation.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the result of the asynchronous operation.</returns>
|
||||
public Task UnregisterReminderAsync(string actorType, string actorId, string reminderName,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
/// <summary>
|
||||
/// Unregisters a reminder.
|
||||
/// </summary>
|
||||
/// <param name="actorType">Type of actor.</param>
|
||||
/// <param name="actorId">ActorId.</param>
|
||||
/// <param name="reminderName">Name of reminder to unregister.</param>
|
||||
/// <param name="cancellationToken">Cancels the operation.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the result of the asynchronous operation.</returns>
|
||||
public Task UnregisterReminderAsync(string actorType, string actorId, string reminderName,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a timer.
|
||||
/// </summary>
|
||||
/// <param name="actorType">Type of actor.</param>
|
||||
/// <param name="actorId">ActorId.</param>
|
||||
/// <param name="timerName">Name of timer to register.</param>
|
||||
/// <param name="data">JSON reminder data as per the Dapr spec.</param>
|
||||
/// <param name="cancellationToken">Cancels the operation.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the result of the asynchronous operation.</returns>
|
||||
public Task RegisterTimerAsync(string actorType, string actorId, string timerName, string data,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
/// <summary>
|
||||
/// Registers a timer.
|
||||
/// </summary>
|
||||
/// <param name="actorType">Type of actor.</param>
|
||||
/// <param name="actorId">ActorId.</param>
|
||||
/// <param name="timerName">Name of timer to register.</param>
|
||||
/// <param name="data">JSON reminder data as per the Dapr spec.</param>
|
||||
/// <param name="cancellationToken">Cancels the operation.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the result of the asynchronous operation.</returns>
|
||||
public Task RegisterTimerAsync(string actorType, string actorId, string timerName, string data,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unregisters a timer.
|
||||
/// </summary>
|
||||
/// <param name="actorType">Type of actor.</param>
|
||||
/// <param name="actorId">ActorId.</param>
|
||||
/// <param name="timerName">Name of timer to register.</param>
|
||||
/// <param name="cancellationToken">Cancels the operation.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the result of the asynchronous operation.</returns>
|
||||
public Task UnregisterTimerAsync(string actorType, string actorId, string timerName,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
/// <summary>
|
||||
/// Unregisters a timer.
|
||||
/// </summary>
|
||||
/// <param name="actorType">Type of actor.</param>
|
||||
/// <param name="actorId">ActorId.</param>
|
||||
/// <param name="timerName">Name of timer to register.</param>
|
||||
/// <param name="cancellationToken">Cancels the operation.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the result of the asynchronous operation.</returns>
|
||||
public Task UnregisterTimerAsync(string actorType, string actorId, string timerName,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ using System.Net.Http;
|
|||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Dapr.AspNetCore.IntegrationTest.App;
|
||||
using FluentAssertions;
|
||||
using Shouldly;
|
||||
using Newtonsoft.Json;
|
||||
using Xunit;
|
||||
|
||||
|
@ -43,7 +43,7 @@ namespace Dapr.AspNetCore.IntegrationTest
|
|||
response.EnsureSuccessStatusCode();
|
||||
var responseContent = await response.Content.ReadAsStringAsync();
|
||||
var responseUserInfo = JsonConvert.DeserializeObject<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");
|
||||
var response = await httpClient.SendAsync(request);
|
||||
|
||||
response.StatusCode.Should().Be(HttpStatusCode.Unauthorized);
|
||||
response.StatusCode.ShouldBe(HttpStatusCode.Unauthorized);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ namespace Dapr.AspNetCore.IntegrationTest
|
|||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Dapr.AspNetCore.IntegrationTest.App;
|
||||
using FluentAssertions;
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
|
||||
public class CloudEventsIntegrationTest
|
||||
|
@ -48,6 +48,7 @@ namespace Dapr.AspNetCore.IntegrationTest
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task CanSendStructuredCloudEvent()
|
||||
{
|
||||
|
@ -74,7 +75,7 @@ namespace Dapr.AspNetCore.IntegrationTest
|
|||
response.EnsureSuccessStatusCode();
|
||||
|
||||
var userInfo = await JsonSerializer.DeserializeAsync<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();
|
||||
|
||||
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();
|
||||
|
||||
var user = await response.Content.ReadAsStringAsync();
|
||||
user.Should().Be("jimmy \"the cool guy\" smith");
|
||||
user.ShouldBe("jimmy \"the cool guy\" smith");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -163,7 +164,7 @@ namespace Dapr.AspNetCore.IntegrationTest
|
|||
response.EnsureSuccessStatusCode();
|
||||
|
||||
var userInfo = await JsonSerializer.DeserializeAsync<UserInfo>(await response.Content.ReadAsStreamAsync(), this.options);
|
||||
userInfo.Name.Should().Be("jimmy");
|
||||
userInfo.Name.ShouldBe("jimmy");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ namespace Dapr.AspNetCore.IntegrationTest
|
|||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Dapr.AspNetCore.IntegrationTest.App;
|
||||
using FluentAssertions;
|
||||
using Shouldly;
|
||||
using Newtonsoft.Json;
|
||||
using Xunit;
|
||||
|
||||
|
@ -37,7 +37,7 @@ namespace Dapr.AspNetCore.IntegrationTest
|
|||
response.EnsureSuccessStatusCode();
|
||||
|
||||
var widget = await daprClient.GetStateAsync<Widget>("testStore", "test");
|
||||
widget.Count.Should().Be(18);
|
||||
widget.Count.ShouldBe(18);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -56,8 +56,8 @@ namespace Dapr.AspNetCore.IntegrationTest
|
|||
response.EnsureSuccessStatusCode();
|
||||
var responseContent = await response.Content.ReadAsStringAsync();
|
||||
var responseWidget = JsonConvert.DeserializeObject<Widget>(responseContent);
|
||||
responseWidget.Size.Should().Be(widget.Size);
|
||||
responseWidget.Count.Should().Be(widget.Count);
|
||||
responseWidget.Size.ShouldBe(widget.Size);
|
||||
responseWidget.Count.ShouldBe(widget.Count);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -92,7 +92,7 @@ namespace Dapr.AspNetCore.IntegrationTest
|
|||
response.EnsureSuccessStatusCode();
|
||||
|
||||
var widget = await daprClient.GetStateAsync<Widget>("testStore", "test");
|
||||
widget.Count.Should().Be(18);
|
||||
widget.Count.ShouldBe(18);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -111,7 +111,7 @@ namespace Dapr.AspNetCore.IntegrationTest
|
|||
response.EnsureSuccessStatusCode();
|
||||
|
||||
var widget = await daprClient.GetStateAsync<Widget>("testStore", "test");
|
||||
widget.Count.Should().Be(18);
|
||||
widget.Count.ShouldBe(18);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -130,8 +130,8 @@ namespace Dapr.AspNetCore.IntegrationTest
|
|||
response.EnsureSuccessStatusCode();
|
||||
var responseContent = await response.Content.ReadAsStringAsync();
|
||||
var responseWidget = JsonConvert.DeserializeObject<Widget>(responseContent);
|
||||
responseWidget.Size.Should().Be(widget.Size);
|
||||
responseWidget.Count.Should().Be(widget.Count);
|
||||
responseWidget.Size.ShouldBe(widget.Size);
|
||||
responseWidget.Count.ShouldBe(widget.Count);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="FluentAssertions" />
|
||||
<PackageReference Include="Shouldly"/>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" />
|
||||
<!-- <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" />-->
|
||||
<PackageReference Include="xunit" />
|
||||
|
|
|
@ -16,7 +16,7 @@ namespace Dapr.AspNetCore.IntegrationTest
|
|||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Dapr.AspNetCore.IntegrationTest.App;
|
||||
using FluentAssertions;
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
|
||||
public class RoutingIntegrationTest
|
||||
|
@ -36,7 +36,7 @@ namespace Dapr.AspNetCore.IntegrationTest
|
|||
response.EnsureSuccessStatusCode();
|
||||
|
||||
var widget = await daprClient.GetStateAsync<Widget>("testStore", "test");
|
||||
widget.Count.Should().Be(18);
|
||||
widget.Count.ShouldBe(18);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ namespace Dapr.AspNetCore.IntegrationTest
|
|||
using System.Net.Http;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
|
||||
public class SubscribeEndpointTest
|
||||
|
@ -39,8 +39,8 @@ namespace Dapr.AspNetCore.IntegrationTest
|
|||
{
|
||||
var json = await JsonSerializer.DeserializeAsync<JsonElement>(stream);
|
||||
|
||||
json.ValueKind.Should().Be(JsonValueKind.Array);
|
||||
json.GetArrayLength().Should().Be(18);
|
||||
json.ValueKind.ShouldBe(JsonValueKind.Array);
|
||||
json.GetArrayLength().ShouldBe(18);
|
||||
|
||||
var subscriptions = new List<(string PubsubName, string Topic, string Route, string rawPayload,
|
||||
string match, string metadata, string DeadLetterTopic, string bulkSubscribeMetadata)>();
|
||||
|
@ -109,34 +109,34 @@ namespace Dapr.AspNetCore.IntegrationTest
|
|||
}
|
||||
}
|
||||
|
||||
subscriptions.Should().Contain(("testpubsub", "A", "topic-a", string.Empty, string.Empty, string.Empty, string.Empty, String.Empty));
|
||||
subscriptions.Should().Contain(("testpubsub", "A.1", "topic-a", string.Empty, string.Empty, string.Empty, string.Empty, String.Empty));
|
||||
subscriptions.Should().Contain(("pubsub", "B", "B", string.Empty, string.Empty, string.Empty, string.Empty, String.Empty));
|
||||
subscriptions.Should().Contain(("custom-pubsub", "custom-C", "C", string.Empty, string.Empty, string.Empty, string.Empty, String.Empty));
|
||||
subscriptions.Should().Contain(("pubsub", "register-user", "register-user", string.Empty, string.Empty, string.Empty, string.Empty, String.Empty));
|
||||
subscriptions.Should().Contain(("pubsub", "register-user-plaintext", "register-user-plaintext", string.Empty, string.Empty, string.Empty, string.Empty, String.Empty));
|
||||
subscriptions.Should().Contain(("pubsub", "D", "D", "true", string.Empty, string.Empty, string.Empty, String.Empty));
|
||||
subscriptions.Should().Contain(("pubsub", "E", "E", string.Empty, string.Empty, string.Empty, string.Empty, String.Empty));
|
||||
subscriptions.Should().Contain(("pubsub", "E", "E-Critical", string.Empty, "event.type == \"critical\"", string.Empty, string.Empty, String.Empty));
|
||||
subscriptions.Should().Contain(("pubsub", "E", "E-Important", string.Empty, "event.type == \"important\"", string.Empty, string.Empty, String.Empty));
|
||||
subscriptions.Should().Contain(("pubsub", "F", "multiTopicAttr", string.Empty, string.Empty, string.Empty, string.Empty,
|
||||
subscriptions.ShouldContain(("testpubsub", "A", "topic-a", string.Empty, string.Empty, string.Empty, string.Empty, String.Empty));
|
||||
subscriptions.ShouldContain(("testpubsub", "A.1", "topic-a", string.Empty, string.Empty, string.Empty, string.Empty, String.Empty));
|
||||
subscriptions.ShouldContain(("pubsub", "B", "B", string.Empty, string.Empty, string.Empty, string.Empty, String.Empty));
|
||||
subscriptions.ShouldContain(("custom-pubsub", "custom-C", "C", string.Empty, string.Empty, string.Empty, string.Empty, String.Empty));
|
||||
subscriptions.ShouldContain(("pubsub", "register-user", "register-user", string.Empty, string.Empty, string.Empty, string.Empty, String.Empty));
|
||||
subscriptions.ShouldContain(("pubsub", "register-user-plaintext", "register-user-plaintext", string.Empty, string.Empty, string.Empty, string.Empty, String.Empty));
|
||||
subscriptions.ShouldContain(("pubsub", "D", "D", "true", string.Empty, string.Empty, string.Empty, String.Empty));
|
||||
subscriptions.ShouldContain(("pubsub", "E", "E", string.Empty, string.Empty, string.Empty, string.Empty, String.Empty));
|
||||
subscriptions.ShouldContain(("pubsub", "E", "E-Critical", string.Empty, "event.type == \"critical\"", string.Empty, string.Empty, String.Empty));
|
||||
subscriptions.ShouldContain(("pubsub", "E", "E-Important", string.Empty, "event.type == \"important\"", string.Empty, string.Empty, String.Empty));
|
||||
subscriptions.ShouldContain(("pubsub", "F", "multiTopicAttr", string.Empty, string.Empty, string.Empty, string.Empty,
|
||||
"{\"enabled\":true,\"maxMessagesCount\":100,\"maxAwaitDurationMs\":1000}"));
|
||||
subscriptions.Should().Contain(("pubsub", "F.1", "multiTopicAttr", "true", string.Empty, string.Empty, string.Empty, String.Empty));
|
||||
subscriptions.Should().Contain(("pubsub", "G", "G", string.Empty, string.Empty, string.Empty, "deadLetterTopicName",
|
||||
subscriptions.ShouldContain(("pubsub", "F.1", "multiTopicAttr", "true", string.Empty, string.Empty, string.Empty, String.Empty));
|
||||
subscriptions.ShouldContain(("pubsub", "G", "G", string.Empty, string.Empty, string.Empty, "deadLetterTopicName",
|
||||
"{\"enabled\":true,\"maxMessagesCount\":300,\"maxAwaitDurationMs\":1000}"));
|
||||
subscriptions.Should().Contain(("pubsub", "splitTopicBuilder", "splitTopics", string.Empty, string.Empty, string.Empty, string.Empty, String.Empty));
|
||||
subscriptions.Should().Contain(("pubsub", "splitTopicAttr", "splitTopics", "true", string.Empty, string.Empty, string.Empty, String.Empty));
|
||||
subscriptions.Should().Contain(("pubsub", "metadata", "multiMetadataTopicAttr", string.Empty, string.Empty, "n1=v1;n2=v2,v3", string.Empty, String.Empty));
|
||||
subscriptions.Should().Contain(("pubsub", "metadata.1", "multiMetadataTopicAttr", "true", string.Empty, "n1=v1", string.Empty,
|
||||
subscriptions.ShouldContain(("pubsub", "splitTopicBuilder", "splitTopics", string.Empty, string.Empty, string.Empty, string.Empty, String.Empty));
|
||||
subscriptions.ShouldContain(("pubsub", "splitTopicAttr", "splitTopics", "true", string.Empty, string.Empty, string.Empty, String.Empty));
|
||||
subscriptions.ShouldContain(("pubsub", "metadata", "multiMetadataTopicAttr", string.Empty, string.Empty, "n1=v1;n2=v2,v3", string.Empty, String.Empty));
|
||||
subscriptions.ShouldContain(("pubsub", "metadata.1", "multiMetadataTopicAttr", "true", string.Empty, "n1=v1", string.Empty,
|
||||
"{\"enabled\":true,\"maxMessagesCount\":500,\"maxAwaitDurationMs\":2000}"));
|
||||
subscriptions.Should().Contain(("pubsub", "splitMetadataTopicBuilder", "splitMetadataTopics", string.Empty, string.Empty, "n1=v1;n2=v1", string.Empty, String.Empty));
|
||||
subscriptions.Should().Contain(("pubsub", "metadataseparatorbyemptytring", "topicmetadataseparatorattrbyemptytring", string.Empty, string.Empty, "n1=v1,", string.Empty, String.Empty));
|
||||
subscriptions.ShouldContain(("pubsub", "splitMetadataTopicBuilder", "splitMetadataTopics", string.Empty, string.Empty, "n1=v1;n2=v1", string.Empty, String.Empty));
|
||||
subscriptions.ShouldContain(("pubsub", "metadataseparatorbyemptytring", "topicmetadataseparatorattrbyemptytring", string.Empty, string.Empty, "n1=v1,", string.Empty, String.Empty));
|
||||
// Test priority route sorting
|
||||
var eTopic = subscriptions.FindAll(e => e.Topic == "E");
|
||||
eTopic.Count.Should().Be(3);
|
||||
eTopic[0].Route.Should().Be("E-Critical");
|
||||
eTopic[1].Route.Should().Be("E-Important");
|
||||
eTopic[2].Route.Should().Be("E");
|
||||
eTopic.Count.ShouldBe(3);
|
||||
eTopic[0].Route.ShouldBe("E-Critical");
|
||||
eTopic[1].Route.ShouldBe("E-Important");
|
||||
eTopic[2].Route.ShouldBe("E");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ namespace Dapr.AspNetCore.Test
|
|||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Shouldly;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Xunit;
|
||||
|
@ -44,8 +44,8 @@ namespace Dapr.AspNetCore.Test
|
|||
// Do verification in the scope of the middleware
|
||||
app.Run(httpContext =>
|
||||
{
|
||||
httpContext.Request.ContentType.Should().Be(contentType);
|
||||
ReadBody(httpContext.Request.Body).Should().Be("Hello, world!");
|
||||
httpContext.Request.ContentType.ShouldBe(contentType);
|
||||
ReadBody(httpContext.Request.Body).ShouldBe("Hello, world!");
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
|
||||
|
@ -77,8 +77,8 @@ namespace Dapr.AspNetCore.Test
|
|||
// Do verification in the scope of the middleware
|
||||
app.Run(httpContext =>
|
||||
{
|
||||
httpContext.Request.ContentType.Should().Be(dataContentType ?? "application/json");
|
||||
ReadBody(httpContext.Request.Body).Should().Be("{\"name\":\"jimmy\"}");
|
||||
httpContext.Request.ContentType.ShouldBe(dataContentType ?? "application/json");
|
||||
ReadBody(httpContext.Request.Body).ShouldBe("{\"name\":\"jimmy\"}");
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
|
||||
|
@ -117,8 +117,8 @@ namespace Dapr.AspNetCore.Test
|
|||
// Do verification in the scope of the middleware
|
||||
app.Run(httpContext =>
|
||||
{
|
||||
httpContext.Request.ContentType.Should().Be(dataContentType ?? "application/json");
|
||||
ReadBody(httpContext.Request.Body).Should().Be("{\"name\":\"jimmy\"}");
|
||||
httpContext.Request.ContentType.ShouldBe(dataContentType ?? "application/json");
|
||||
ReadBody(httpContext.Request.Body).ShouldBe("{\"name\":\"jimmy\"}");
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
|
||||
|
@ -160,11 +160,13 @@ namespace Dapr.AspNetCore.Test
|
|||
// Do verification in the scope of the middleware
|
||||
app.Run(httpContext =>
|
||||
{
|
||||
httpContext.Request.ContentType.Should().Be(dataContentType ?? "application/json");
|
||||
ReadBody(httpContext.Request.Body).Should().Be("{\"name\":\"jimmy\"}");
|
||||
httpContext.Request.ContentType.ShouldBe(dataContentType ?? "application/json");
|
||||
ReadBody(httpContext.Request.Body).ShouldBe("{\"name\":\"jimmy\"}");
|
||||
|
||||
httpContext.Request.Headers.Should().ContainKey("Cloudevent.type").WhichValue.Should().BeEquivalentTo("Test.Type");
|
||||
httpContext.Request.Headers.Should().ContainKey("Cloudevent.subject").WhichValue.Should().BeEquivalentTo("Test.Subject");
|
||||
httpContext.Request.Headers.ShouldContainKey("Cloudevent.type");
|
||||
httpContext.Request.Headers["Cloudevent.type"].ToString().ShouldBe("Test.Type");
|
||||
httpContext.Request.Headers.ShouldContainKey("Cloudevent.subject");
|
||||
httpContext.Request.Headers["Cloudevent.subject"].ToString().ShouldBe("Test.Subject");
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
|
||||
|
@ -207,11 +209,12 @@ namespace Dapr.AspNetCore.Test
|
|||
// Do verification in the scope of the middleware
|
||||
app.Run(httpContext =>
|
||||
{
|
||||
httpContext.Request.ContentType.Should().Be(dataContentType ?? "application/json");
|
||||
ReadBody(httpContext.Request.Body).Should().Be("{\"name\":\"jimmy\"}");
|
||||
httpContext.Request.ContentType.ShouldBe(dataContentType ?? "application/json");
|
||||
ReadBody(httpContext.Request.Body).ShouldBe("{\"name\":\"jimmy\"}");
|
||||
|
||||
httpContext.Request.Headers.Should().ContainKey("Cloudevent.type").WhichValue.Should().BeEquivalentTo("Test.Type");
|
||||
httpContext.Request.Headers.Should().NotContainKey("Cloudevent.subject");
|
||||
httpContext.Request.Headers.ShouldContainKey("Cloudevent.type");
|
||||
httpContext.Request.Headers["Cloudevent.type"].ToString().ShouldBe("Test.Type");
|
||||
httpContext.Request.Headers.ShouldNotContainKey("Cloudevent.subject");
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
|
||||
|
@ -254,11 +257,12 @@ namespace Dapr.AspNetCore.Test
|
|||
// Do verification in the scope of the middleware
|
||||
app.Run(httpContext =>
|
||||
{
|
||||
httpContext.Request.ContentType.Should().Be(dataContentType ?? "application/json");
|
||||
ReadBody(httpContext.Request.Body).Should().Be("{\"name\":\"jimmy\"}");
|
||||
httpContext.Request.ContentType.ShouldBe(dataContentType ?? "application/json");
|
||||
ReadBody(httpContext.Request.Body).ShouldBe("{\"name\":\"jimmy\"}");
|
||||
|
||||
httpContext.Request.Headers.Should().NotContainKey("Cloudevent.type");
|
||||
httpContext.Request.Headers.Should().ContainKey("Cloudevent.subject").WhichValue.Should().BeEquivalentTo("Test.Subject");
|
||||
httpContext.Request.Headers.ShouldNotContainKey("Cloudevent.type");
|
||||
httpContext.Request.Headers.ShouldContainKey("Cloudevent.subject");
|
||||
httpContext.Request.Headers["Cloudevent.subject"].ToString().ShouldBe("Test.Subject");
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
|
||||
|
@ -297,8 +301,8 @@ namespace Dapr.AspNetCore.Test
|
|||
// Do verification in the scope of the middleware
|
||||
app.Run(httpContext =>
|
||||
{
|
||||
httpContext.Request.ContentType.Should().Be("text/plain");
|
||||
ReadBody(httpContext.Request.Body).Should().Be(expected);
|
||||
httpContext.Request.ContentType.ShouldBe("text/plain");
|
||||
ReadBody(httpContext.Request.Body).ShouldBe(expected);
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
|
||||
|
@ -331,8 +335,8 @@ namespace Dapr.AspNetCore.Test
|
|||
// Do verification in the scope of the middleware
|
||||
app.Run(httpContext =>
|
||||
{
|
||||
httpContext.Request.ContentType.Should().Be("text/plain");
|
||||
ReadBody(httpContext.Request.Body).Should().Be(expected);
|
||||
httpContext.Request.ContentType.ShouldBe("text/plain");
|
||||
ReadBody(httpContext.Request.Body).ShouldBe(expected);
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
|
||||
|
@ -366,8 +370,8 @@ namespace Dapr.AspNetCore.Test
|
|||
// Do verification in the scope of the middleware
|
||||
app.Run(httpContext =>
|
||||
{
|
||||
httpContext.Request.ContentType.Should().Be("application/person+json");
|
||||
ReadBody(httpContext.Request.Body).Should().Be("{\"name\":\"jimmy\"}");
|
||||
httpContext.Request.ContentType.ShouldBe("application/person+json");
|
||||
ReadBody(httpContext.Request.Body).ShouldBe("{\"name\":\"jimmy\"}");
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
|
||||
|
@ -396,14 +400,14 @@ namespace Dapr.AspNetCore.Test
|
|||
// Do verification in the scope of the middleware
|
||||
app.Run(httpContext =>
|
||||
{
|
||||
httpContext.Request.ContentType.Should().Be(dataContentType);
|
||||
httpContext.Request.ContentType.ShouldBe(dataContentType);
|
||||
var bytes = new byte[httpContext.Request.Body.Length];
|
||||
#if NET9_0
|
||||
httpContext.Request.Body.ReadExactly(bytes, 0, bytes.Length);
|
||||
#else
|
||||
httpContext.Request.Body.Read(bytes, 0, bytes.Length);
|
||||
#endif
|
||||
bytes.Should().Equal(data);
|
||||
bytes.ShouldBe(data);
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
|
||||
|
@ -432,9 +436,9 @@ namespace Dapr.AspNetCore.Test
|
|||
// Do verification in the scope of the middleware
|
||||
app.Run(httpContext =>
|
||||
{
|
||||
httpContext.Request.ContentType.Should().Be("application/json");
|
||||
httpContext.Request.ContentType.ShouldBe("application/json");
|
||||
var body = ReadBody(httpContext.Request.Body);
|
||||
body.Should().Equals(data);
|
||||
body.ShouldBe(data);
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
|
||||
|
@ -447,7 +451,7 @@ namespace Dapr.AspNetCore.Test
|
|||
MakeBody($"{{ \"datacontenttype\": \"{dataContentType}\", \"data_base64\": \"{base64Str}\", \"data\": {data} }}");
|
||||
|
||||
await pipeline.Invoke(context);
|
||||
context.Response.StatusCode.Should().Be((int)HttpStatusCode.BadRequest);
|
||||
context.Response.StatusCode.ShouldBe((int)HttpStatusCode.BadRequest);
|
||||
}
|
||||
|
||||
private static Stream MakeBody(string text, Encoding encoding = null)
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="FluentAssertions"/>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" />
|
||||
<PackageReference Include="Shouldly"/>
|
||||
<PackageReference Include="xunit" />
|
||||
<PackageReference Include="xunit.runner.visualstudio">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
|
|
|
@ -18,7 +18,7 @@ namespace Dapr.AspNetCore.Test
|
|||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Dapr.AspNetCore.Resources;
|
||||
using FluentAssertions;
|
||||
using Shouldly;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
|
@ -34,9 +34,7 @@ namespace Dapr.AspNetCore.Test
|
|||
|
||||
Action action = () => provider.OnProvidersExecuted(context);
|
||||
|
||||
action
|
||||
.Should()
|
||||
.NotThrow<NullReferenceException>();
|
||||
action.ShouldNotThrow();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -48,8 +46,7 @@ namespace Dapr.AspNetCore.Test
|
|||
Action action = () => provider.OnProvidersExecuted(context);
|
||||
|
||||
action
|
||||
.Should()
|
||||
.Throw<InvalidOperationException>(SR.ErrorStateStoreNameNotProvidedForStateEntry);
|
||||
.ShouldThrow<InvalidOperationException>(SR.ErrorStateStoreNameNotProvidedForStateEntry);
|
||||
}
|
||||
|
||||
private ApplicationModelProviderContext CreateContext(string methodName)
|
||||
|
|
|
@ -18,7 +18,7 @@ namespace Dapr.AspNetCore.Test
|
|||
using System.Threading.Tasks;
|
||||
using Dapr.Client;
|
||||
using Dapr.Client.Autogen.Grpc.v1;
|
||||
using FluentAssertions;
|
||||
using Shouldly;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
|
@ -38,9 +38,9 @@ namespace Dapr.AspNetCore.Test
|
|||
|
||||
await binder.BindModelAsync(context);
|
||||
|
||||
context.Result.IsModelSet.Should().BeFalse();
|
||||
context.ModelState.ErrorCount.Should().Be(1);
|
||||
context.ModelState["testParameter"].Errors.Count.Should().Be(1);
|
||||
context.Result.IsModelSet.ShouldBeFalse();
|
||||
context.ModelState.ErrorCount.ShouldBe(1);
|
||||
context.ModelState["testParameter"].Errors.Count.ShouldBe(1);
|
||||
|
||||
// No request to state store, validated by disposing client
|
||||
}
|
||||
|
@ -66,12 +66,12 @@ namespace Dapr.AspNetCore.Test
|
|||
await SendResponseWithState(state, request);
|
||||
|
||||
// Get response and validate
|
||||
context.Result.IsModelSet.Should().BeTrue();
|
||||
context.Result.Model.As<Widget>().Size.Should().Be("small");
|
||||
context.Result.Model.As<Widget>().Color.Should().Be("yellow");
|
||||
context.Result.IsModelSet.ShouldBeTrue();
|
||||
((Widget)context.Result.Model).Size.ShouldBe("small");
|
||||
((Widget)context.Result.Model).Color.ShouldBe("yellow");
|
||||
|
||||
context.ValidationState.Count.Should().Be(1);
|
||||
context.ValidationState[context.Result.Model].SuppressValidation.Should().BeTrue();
|
||||
context.ValidationState.Count.ShouldBe(1);
|
||||
context.ValidationState[context.Result.Model].SuppressValidation.ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -95,13 +95,13 @@ namespace Dapr.AspNetCore.Test
|
|||
await SendResponseWithState(state, request);
|
||||
|
||||
// Get response and validate
|
||||
context.Result.IsModelSet.Should().BeTrue();
|
||||
context.Result.Model.As<StateEntry<Widget>>().Key.Should().Be("test");
|
||||
context.Result.Model.As<StateEntry<Widget>>().Value.Size.Should().Be("small");
|
||||
context.Result.Model.As<StateEntry<Widget>>().Value.Color.Should().Be("yellow");
|
||||
context.Result.IsModelSet.ShouldBeTrue();
|
||||
((StateEntry<Widget>)context.Result.Model).Key.ShouldBe("test");
|
||||
((StateEntry<Widget>)context.Result.Model).Value.Size.ShouldBe("small");
|
||||
((StateEntry<Widget>)context.Result.Model).Value.Color.ShouldBe("yellow");
|
||||
|
||||
context.ValidationState.Count.Should().Be(1);
|
||||
context.ValidationState[context.Result.Model].SuppressValidation.Should().BeTrue();
|
||||
context.ValidationState.Count.ShouldBe(1);
|
||||
context.ValidationState[context.Result.Model].SuppressValidation.ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -122,9 +122,9 @@ namespace Dapr.AspNetCore.Test
|
|||
|
||||
await SendResponseWithState<string>(null, request);
|
||||
|
||||
context.ModelState.IsValid.Should().BeTrue();
|
||||
context.Result.IsModelSet.Should().BeFalse();
|
||||
context.Result.Should().Be(ModelBindingResult.Failed());
|
||||
context.ModelState.IsValid.ShouldBeTrue();
|
||||
context.Result.IsModelSet.ShouldBeFalse();
|
||||
context.Result.ShouldBe(ModelBindingResult.Failed());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -145,9 +145,9 @@ namespace Dapr.AspNetCore.Test
|
|||
|
||||
await SendResponseWithState<string>(null, request);
|
||||
|
||||
context.ModelState.IsValid.Should().BeTrue();
|
||||
context.Result.IsModelSet.Should().BeTrue();
|
||||
((StateEntry<Widget>)context.Result.Model).Value.Should().BeNull();
|
||||
context.ModelState.IsValid.ShouldBeTrue();
|
||||
context.Result.IsModelSet.ShouldBeTrue();
|
||||
((StateEntry<Widget>)context.Result.Model).Value.ShouldBeNull();
|
||||
}
|
||||
|
||||
private static ModelBindingContext CreateContext(IServiceProvider services)
|
||||
|
|
|
@ -19,7 +19,7 @@ namespace Dapr.Client.Test
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Autogenerated = Dapr.Client.Autogen.Grpc.v1;
|
||||
using FluentAssertions;
|
||||
using Shouldly;
|
||||
using Grpc.Core;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
@ -43,26 +43,24 @@ namespace Dapr.Client.Test
|
|||
|
||||
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.BulkPublishRequest>();
|
||||
|
||||
envelope.Entries.Count.Should().Be(2);
|
||||
envelope.PubsubName.Should().Be(TestPubsubName);
|
||||
envelope.Topic.Should().Be(TestTopicName);
|
||||
envelope.Metadata.Count.Should().Be(0);
|
||||
envelope.Entries.Count.ShouldBe(2);
|
||||
envelope.PubsubName.ShouldBe(TestPubsubName);
|
||||
envelope.Topic.ShouldBe(TestTopicName);
|
||||
envelope.Metadata.Count.ShouldBe(0);
|
||||
|
||||
var firstEntry = envelope.Entries[0];
|
||||
|
||||
firstEntry.EntryId.Should().Be("0");
|
||||
firstEntry.ContentType.Should().Be(TestContentType);
|
||||
firstEntry.Event.ToStringUtf8().Should()
|
||||
.Be(JsonSerializer.Serialize(bulkPublishData[0], client.InnerClient.JsonSerializerOptions));
|
||||
firstEntry.Metadata.Should().BeEmpty();
|
||||
firstEntry.EntryId.ShouldBe("0");
|
||||
firstEntry.ContentType.ShouldBe(TestContentType);
|
||||
firstEntry.Event.ToStringUtf8().ShouldBe(JsonSerializer.Serialize(bulkPublishData[0], client.InnerClient.JsonSerializerOptions));
|
||||
firstEntry.Metadata.ShouldBeEmpty();
|
||||
|
||||
var secondEntry = envelope.Entries[1];
|
||||
|
||||
secondEntry.EntryId.Should().Be("1");
|
||||
secondEntry.ContentType.Should().Be(TestContentType);
|
||||
secondEntry.Event.ToStringUtf8().Should()
|
||||
.Be(JsonSerializer.Serialize(bulkPublishData[1], client.InnerClient.JsonSerializerOptions));
|
||||
secondEntry.Metadata.Should().BeEmpty();
|
||||
secondEntry.EntryId.ShouldBe("1");
|
||||
secondEntry.ContentType.ShouldBe(TestContentType);
|
||||
secondEntry.Event.ToStringUtf8().ShouldBe(JsonSerializer.Serialize(bulkPublishData[1], client.InnerClient.JsonSerializerOptions));
|
||||
secondEntry.Metadata.ShouldBeEmpty();
|
||||
|
||||
// Create Response & Respond
|
||||
var response = new Autogenerated.BulkPublishResponse
|
||||
|
@ -72,7 +70,7 @@ namespace Dapr.Client.Test
|
|||
var bulkPublishResponse = await request.CompleteWithMessageAsync(response);
|
||||
|
||||
// Get response and validate
|
||||
bulkPublishResponse.FailedEntries.Count.Should().Be(0);
|
||||
bulkPublishResponse.FailedEntries.Count.ShouldBe(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -90,31 +88,29 @@ namespace Dapr.Client.Test
|
|||
|
||||
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.BulkPublishRequest>();
|
||||
|
||||
envelope.Entries.Count.Should().Be(2);
|
||||
envelope.PubsubName.Should().Be(TestPubsubName);
|
||||
envelope.Topic.Should().Be(TestTopicName);
|
||||
envelope.Entries.Count.ShouldBe(2);
|
||||
envelope.PubsubName.ShouldBe(TestPubsubName);
|
||||
envelope.Topic.ShouldBe(TestTopicName);
|
||||
|
||||
envelope.Metadata.Count.Should().Be(2);
|
||||
envelope.Metadata.Keys.Contains("key1").Should().BeTrue();
|
||||
envelope.Metadata.Keys.Contains("key2").Should().BeTrue();
|
||||
envelope.Metadata["key1"].Should().Be("value1");
|
||||
envelope.Metadata["key2"].Should().Be("value2");
|
||||
envelope.Metadata.Count.ShouldBe(2);
|
||||
envelope.Metadata.Keys.Contains("key1").ShouldBeTrue();
|
||||
envelope.Metadata.Keys.Contains("key2").ShouldBeTrue();
|
||||
envelope.Metadata["key1"].ShouldBe("value1");
|
||||
envelope.Metadata["key2"].ShouldBe("value2");
|
||||
|
||||
var firstEntry = envelope.Entries[0];
|
||||
|
||||
firstEntry.EntryId.Should().Be("0");
|
||||
firstEntry.ContentType.Should().Be(TestContentType);
|
||||
firstEntry.Event.ToStringUtf8().Should()
|
||||
.Be(JsonSerializer.Serialize(bulkPublishData[0], client.InnerClient.JsonSerializerOptions));
|
||||
firstEntry.Metadata.Should().BeEmpty();
|
||||
firstEntry.EntryId.ShouldBe("0");
|
||||
firstEntry.ContentType.ShouldBe(TestContentType);
|
||||
firstEntry.Event.ToStringUtf8().ShouldBe(JsonSerializer.Serialize(bulkPublishData[0], client.InnerClient.JsonSerializerOptions));
|
||||
firstEntry.Metadata.ShouldBeEmpty();
|
||||
|
||||
var secondEntry = envelope.Entries[1];
|
||||
|
||||
secondEntry.EntryId.Should().Be("1");
|
||||
secondEntry.ContentType.Should().Be(TestContentType);
|
||||
secondEntry.Event.ToStringUtf8().Should()
|
||||
.Be(JsonSerializer.Serialize(bulkPublishData[1], client.InnerClient.JsonSerializerOptions));
|
||||
secondEntry.Metadata.Should().BeEmpty();
|
||||
secondEntry.EntryId.ShouldBe("1");
|
||||
secondEntry.ContentType.ShouldBe(TestContentType);
|
||||
secondEntry.Event.ToStringUtf8().ShouldBe(JsonSerializer.Serialize(bulkPublishData[1], client.InnerClient.JsonSerializerOptions));
|
||||
secondEntry.Metadata.ShouldBeEmpty();
|
||||
|
||||
// Create Response & Respond
|
||||
var response = new Autogenerated.BulkPublishResponse
|
||||
|
@ -124,7 +120,7 @@ namespace Dapr.Client.Test
|
|||
var bulkPublishResponse = await request.CompleteWithMessageAsync(response);
|
||||
|
||||
// Get response and validate
|
||||
bulkPublishResponse.FailedEntries.Count.Should().Be(0);
|
||||
bulkPublishResponse.FailedEntries.Count.ShouldBe(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -139,26 +135,24 @@ namespace Dapr.Client.Test
|
|||
request.Dismiss();
|
||||
|
||||
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.BulkPublishRequest>();
|
||||
envelope.Entries.Count.Should().Be(2);
|
||||
envelope.PubsubName.Should().Be(TestPubsubName);
|
||||
envelope.Topic.Should().Be(TestTopicName);
|
||||
envelope.Metadata.Count.Should().Be(0);
|
||||
envelope.Entries.Count.ShouldBe(2);
|
||||
envelope.PubsubName.ShouldBe(TestPubsubName);
|
||||
envelope.Topic.ShouldBe(TestTopicName);
|
||||
envelope.Metadata.Count.ShouldBe(0);
|
||||
|
||||
var firstEntry = envelope.Entries[0];
|
||||
|
||||
firstEntry.EntryId.Should().Be("0");
|
||||
firstEntry.ContentType.Should().Be(TestContentType);
|
||||
firstEntry.Event.ToStringUtf8().Should()
|
||||
.Be(JsonSerializer.Serialize(bulkPublishData[0], client.InnerClient.JsonSerializerOptions));
|
||||
firstEntry.Metadata.Should().BeEmpty();
|
||||
firstEntry.EntryId.ShouldBe("0");
|
||||
firstEntry.ContentType.ShouldBe(TestContentType);
|
||||
firstEntry.Event.ToStringUtf8().ShouldBe(JsonSerializer.Serialize(bulkPublishData[0], client.InnerClient.JsonSerializerOptions));
|
||||
firstEntry.Metadata.ShouldBeEmpty();
|
||||
|
||||
var secondEntry = envelope.Entries[1];
|
||||
|
||||
secondEntry.EntryId.Should().Be("1");
|
||||
secondEntry.ContentType.Should().Be(TestContentType);
|
||||
secondEntry.Event.ToStringUtf8().Should()
|
||||
.Be(JsonSerializer.Serialize(bulkPublishData[1], client.InnerClient.JsonSerializerOptions));
|
||||
secondEntry.Metadata.Should().BeEmpty();
|
||||
secondEntry.EntryId.ShouldBe("1");
|
||||
secondEntry.ContentType.ShouldBe(TestContentType);
|
||||
secondEntry.Event.ToStringUtf8().ShouldBe(JsonSerializer.Serialize(bulkPublishData[1], client.InnerClient.JsonSerializerOptions));
|
||||
secondEntry.Metadata.ShouldBeEmpty();
|
||||
|
||||
// Create Response & Respond
|
||||
var response = new Autogenerated.BulkPublishResponse
|
||||
|
@ -168,7 +162,7 @@ namespace Dapr.Client.Test
|
|||
var bulkPublishResponse = await request.CompleteWithMessageAsync(response);
|
||||
|
||||
// Get response and validate
|
||||
bulkPublishResponse.FailedEntries.Count.Should().Be(0);
|
||||
bulkPublishResponse.FailedEntries.Count.ShouldBe(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -185,31 +179,29 @@ namespace Dapr.Client.Test
|
|||
request.Dismiss();
|
||||
|
||||
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.BulkPublishRequest>();
|
||||
envelope.Entries.Count.Should().Be(2);
|
||||
envelope.PubsubName.Should().Be(TestPubsubName);
|
||||
envelope.Topic.Should().Be(TestTopicName);
|
||||
envelope.Entries.Count.ShouldBe(2);
|
||||
envelope.PubsubName.ShouldBe(TestPubsubName);
|
||||
envelope.Topic.ShouldBe(TestTopicName);
|
||||
|
||||
envelope.Metadata.Count.Should().Be(2);
|
||||
envelope.Metadata.Keys.Contains("key1").Should().BeTrue();
|
||||
envelope.Metadata.Keys.Contains("key2").Should().BeTrue();
|
||||
envelope.Metadata["key1"].Should().Be("value1");
|
||||
envelope.Metadata["key2"].Should().Be("value2");
|
||||
envelope.Metadata.Count.ShouldBe(2);
|
||||
envelope.Metadata.Keys.Contains("key1").ShouldBeTrue();
|
||||
envelope.Metadata.Keys.Contains("key2").ShouldBeTrue();
|
||||
envelope.Metadata["key1"].ShouldBe("value1");
|
||||
envelope.Metadata["key2"].ShouldBe("value2");
|
||||
|
||||
var firstEntry = envelope.Entries[0];
|
||||
|
||||
firstEntry.EntryId.Should().Be("0");
|
||||
firstEntry.ContentType.Should().Be(TestContentType);
|
||||
firstEntry.Event.ToStringUtf8().Should()
|
||||
.Be(JsonSerializer.Serialize(bulkPublishData[0], client.InnerClient.JsonSerializerOptions));
|
||||
firstEntry.Metadata.Should().BeEmpty();
|
||||
firstEntry.EntryId.ShouldBe("0");
|
||||
firstEntry.ContentType.ShouldBe(TestContentType);
|
||||
firstEntry.Event.ToStringUtf8().ShouldBe(JsonSerializer.Serialize(bulkPublishData[0], client.InnerClient.JsonSerializerOptions));
|
||||
firstEntry.Metadata.ShouldBeEmpty();
|
||||
|
||||
var secondEntry = envelope.Entries[1];
|
||||
|
||||
secondEntry.EntryId.Should().Be("1");
|
||||
secondEntry.ContentType.Should().Be(TestContentType);
|
||||
secondEntry.Event.ToStringUtf8().Should()
|
||||
.Be(JsonSerializer.Serialize(bulkPublishData[1], client.InnerClient.JsonSerializerOptions));
|
||||
secondEntry.Metadata.Should().BeEmpty();
|
||||
secondEntry.EntryId.ShouldBe("1");
|
||||
secondEntry.ContentType.ShouldBe(TestContentType);
|
||||
secondEntry.Event.ToStringUtf8().ShouldBe(JsonSerializer.Serialize(bulkPublishData[1], client.InnerClient.JsonSerializerOptions));
|
||||
secondEntry.Metadata.ShouldBeEmpty();
|
||||
|
||||
// Create Response & Respond
|
||||
var response = new Autogenerated.BulkPublishResponse
|
||||
|
@ -219,7 +211,7 @@ namespace Dapr.Client.Test
|
|||
var bulkPublishResponse = await request.CompleteWithMessageAsync(response);
|
||||
|
||||
// Get response and validate
|
||||
bulkPublishResponse.FailedEntries.Count.Should().Be(0);
|
||||
bulkPublishResponse.FailedEntries.Count.ShouldBe(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -236,18 +228,17 @@ namespace Dapr.Client.Test
|
|||
request.Dismiss();
|
||||
|
||||
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.BulkPublishRequest>();
|
||||
envelope.Entries.Count.Should().Be(1);
|
||||
envelope.PubsubName.Should().Be(TestPubsubName);
|
||||
envelope.Topic.Should().Be(TestTopicName);
|
||||
envelope.Metadata.Count.Should().Be(0);
|
||||
envelope.Entries.Count.ShouldBe(1);
|
||||
envelope.PubsubName.ShouldBe(TestPubsubName);
|
||||
envelope.Topic.ShouldBe(TestTopicName);
|
||||
envelope.Metadata.Count.ShouldBe(0);
|
||||
|
||||
var firstEntry = envelope.Entries[0];
|
||||
|
||||
firstEntry.EntryId.Should().Be("0");
|
||||
firstEntry.ContentType.Should().Be(TestContentType);
|
||||
firstEntry.Event.ToStringUtf8().Should()
|
||||
.Be(JsonSerializer.Serialize(bulkPublishDataObject, client.InnerClient.JsonSerializerOptions));
|
||||
firstEntry.Metadata.Should().BeEmpty();
|
||||
firstEntry.EntryId.ShouldBe("0");
|
||||
firstEntry.ContentType.ShouldBe(TestContentType);
|
||||
firstEntry.Event.ToStringUtf8().ShouldBe(JsonSerializer.Serialize(bulkPublishDataObject, client.InnerClient.JsonSerializerOptions));
|
||||
firstEntry.Metadata.ShouldBeEmpty();
|
||||
|
||||
// Create Response & Respond
|
||||
var response = new Autogenerated.BulkPublishResponse
|
||||
|
@ -257,7 +248,7 @@ namespace Dapr.Client.Test
|
|||
var bulkPublishResponse = await request.CompleteWithMessageAsync(response);
|
||||
|
||||
// Get response and validate
|
||||
bulkPublishResponse.FailedEntries.Count.Should().Be(0);
|
||||
bulkPublishResponse.FailedEntries.Count.ShouldBe(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -333,11 +324,11 @@ namespace Dapr.Client.Test
|
|||
var bulkPublishResponse = await request.CompleteWithMessageAsync(response);
|
||||
|
||||
// Get response and validate
|
||||
bulkPublishResponse.FailedEntries[0].Entry.EntryId.Should().Be("0");
|
||||
bulkPublishResponse.FailedEntries[0].ErrorMessage.Should().Be("Failed to publish");
|
||||
bulkPublishResponse.FailedEntries[0].Entry.EntryId.ShouldBe("0");
|
||||
bulkPublishResponse.FailedEntries[0].ErrorMessage.ShouldBe("Failed to publish");
|
||||
|
||||
bulkPublishResponse.FailedEntries[1].Entry.EntryId.Should().Be("1");
|
||||
bulkPublishResponse.FailedEntries[1].ErrorMessage.Should().Be("Failed to publish");
|
||||
bulkPublishResponse.FailedEntries[1].Entry.EntryId.ShouldBe("1");
|
||||
bulkPublishResponse.FailedEntries[1].ErrorMessage.ShouldBe("Failed to publish");
|
||||
}
|
||||
|
||||
private class Widget
|
||||
|
|
|
@ -15,7 +15,7 @@ using System.Collections.Generic;
|
|||
using System.Threading.Tasks;
|
||||
using Autogenerated = Dapr.Client.Autogen.Grpc.v1;
|
||||
using Xunit;
|
||||
using FluentAssertions;
|
||||
using Shouldly;
|
||||
|
||||
namespace Dapr.Client.Test
|
||||
{
|
||||
|
@ -37,13 +37,13 @@ namespace Dapr.Client.Test
|
|||
|
||||
// Get Request and validate
|
||||
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.GetConfigurationRequest>();
|
||||
envelope.StoreName.Should().Be("testStore");
|
||||
envelope.Keys.Should().Contain("test_key");
|
||||
envelope.Metadata.Count.Should().Be(2);
|
||||
envelope.Metadata.Keys.Contains("key1").Should().BeTrue();
|
||||
envelope.Metadata.Keys.Contains("key2").Should().BeTrue();
|
||||
envelope.Metadata["key1"].Should().Be("value1");
|
||||
envelope.Metadata["key2"].Should().Be("value2");
|
||||
envelope.StoreName.ShouldBe("testStore");
|
||||
envelope.Keys.ShouldContain("test_key");
|
||||
envelope.Metadata.Count.ShouldBe(2);
|
||||
envelope.Metadata.Keys.Contains("key1").ShouldBeTrue();
|
||||
envelope.Metadata.Keys.Contains("key2").ShouldBeTrue();
|
||||
envelope.Metadata["key1"].ShouldBe("value1");
|
||||
envelope.Metadata["key2"].ShouldBe("value2");
|
||||
|
||||
// Get response and validate
|
||||
var invokeResponse = new Autogenerated.GetConfigurationResponse();
|
||||
|
@ -55,8 +55,8 @@ namespace Dapr.Client.Test
|
|||
|
||||
var domainResponse = await request.CompleteWithMessageAsync(invokeResponse);
|
||||
var configItem = domainResponse.Items["testKey"];
|
||||
configItem.Value.Should().Be("testValue");
|
||||
configItem.Version.Should().Be("v1");
|
||||
configItem.Value.ShouldBe("testValue");
|
||||
configItem.Version.ShouldBe("v1");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -75,13 +75,13 @@ namespace Dapr.Client.Test
|
|||
|
||||
// Get Request and validate
|
||||
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.GetConfigurationRequest>();
|
||||
envelope.StoreName.Should().Be("testStore");
|
||||
envelope.Keys.Should().BeEmpty();
|
||||
envelope.Metadata.Count.Should().Be(2);
|
||||
envelope.Metadata.Keys.Contains("key1").Should().BeTrue();
|
||||
envelope.Metadata.Keys.Contains("key2").Should().BeTrue();
|
||||
envelope.Metadata["key1"].Should().Be("value1");
|
||||
envelope.Metadata["key2"].Should().Be("value2");
|
||||
envelope.StoreName.ShouldBe("testStore");
|
||||
envelope.Keys.ShouldBeEmpty();
|
||||
envelope.Metadata.Count.ShouldBe(2);
|
||||
envelope.Metadata.Keys.Contains("key1").ShouldBeTrue();
|
||||
envelope.Metadata.Keys.Contains("key2").ShouldBeTrue();
|
||||
envelope.Metadata["key1"].ShouldBe("value1");
|
||||
envelope.Metadata["key2"].ShouldBe("value2");
|
||||
|
||||
// Get response and validate
|
||||
var invokeResponse = new Autogenerated.GetConfigurationResponse();
|
||||
|
@ -93,8 +93,8 @@ namespace Dapr.Client.Test
|
|||
|
||||
var domainResponse = await request.CompleteWithMessageAsync(invokeResponse);
|
||||
var configItem = domainResponse.Items["testKey"];
|
||||
configItem.Value.Should().Be("testValue");
|
||||
configItem.Version.Should().Be("v1");
|
||||
configItem.Value.ShouldBe("testValue");
|
||||
configItem.Version.ShouldBe("v1");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -108,9 +108,9 @@ namespace Dapr.Client.Test
|
|||
|
||||
// Get Request and validate
|
||||
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.GetConfigurationRequest>();
|
||||
envelope.StoreName.Should().Be("testStore");
|
||||
envelope.Keys.Should().Contain("test_key");
|
||||
envelope.Metadata.Count.Should().Be(0);
|
||||
envelope.StoreName.ShouldBe("testStore");
|
||||
envelope.Keys.ShouldContain("test_key");
|
||||
envelope.Metadata.Count.ShouldBe(0);
|
||||
|
||||
// Get response and validate
|
||||
var invokeResponse = new Autogenerated.GetConfigurationResponse();
|
||||
|
@ -122,8 +122,8 @@ namespace Dapr.Client.Test
|
|||
|
||||
var domainResponse = await request.CompleteWithMessageAsync(invokeResponse);
|
||||
var configItem = domainResponse.Items["testKey"];
|
||||
configItem.Value.Should().Be("testValue");
|
||||
configItem.Version.Should().Be("v1");
|
||||
configItem.Value.ShouldBe("testValue");
|
||||
configItem.Version.ShouldBe("v1");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -136,8 +136,8 @@ namespace Dapr.Client.Test
|
|||
});
|
||||
|
||||
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.UnsubscribeConfigurationRequest>();
|
||||
envelope.StoreName.Should().Be("testStore");
|
||||
envelope.Id.Should().Be("testId");
|
||||
envelope.StoreName.ShouldBe("testStore");
|
||||
envelope.Id.ShouldBe("testId");
|
||||
request.Dismiss();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
@ -30,28 +29,6 @@ namespace Dapr.Client.Test
|
|||
(ReadOnlyMemory<byte>) Array.Empty<byte>(), keyName, new EncryptionOptions(KeyWrapAlgorithm.Rsa), CancellationToken.None));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task EncryptAsync_Stream_VaultResourceName_ArgumentVerifierException()
|
||||
{
|
||||
var client = new DaprClientBuilder().Build();
|
||||
const string vaultResourceName = "";
|
||||
//Get response and validate
|
||||
await Assert.ThrowsAsync<ArgumentException>(async () => await client.EncryptAsync(vaultResourceName,
|
||||
new MemoryStream(), "MyKey", new EncryptionOptions(KeyWrapAlgorithm.Rsa),
|
||||
CancellationToken.None));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task EncryptAsync_Stream_KeyName_ArgumentVerifierException()
|
||||
{
|
||||
var client = new DaprClientBuilder().Build();
|
||||
const string keyName = "";
|
||||
//Get response and validate
|
||||
await Assert.ThrowsAsync<ArgumentException>(async () => await client.EncryptAsync("myVault",
|
||||
(Stream) new MemoryStream(), keyName, new EncryptionOptions(KeyWrapAlgorithm.Rsa),
|
||||
CancellationToken.None));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DecryptAsync_ByteArray_VaultResourceName_ArgumentVerifierException()
|
||||
{
|
||||
|
@ -71,25 +48,5 @@ namespace Dapr.Client.Test
|
|||
await Assert.ThrowsAsync<ArgumentException>(async () => await client.DecryptAsync("myVault",
|
||||
Array.Empty<byte>(), keyName, new DecryptionOptions(), CancellationToken.None));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DecryptAsync_Stream_VaultResourceName_ArgumentVerifierException()
|
||||
{
|
||||
var client = new DaprClientBuilder().Build();
|
||||
const string vaultResourceName = "";
|
||||
//Get response and validate
|
||||
await Assert.ThrowsAsync<ArgumentException>(async () => await client.DecryptAsync(vaultResourceName,
|
||||
new MemoryStream(), "MyKey", new DecryptionOptions(), CancellationToken.None));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DecryptAsync_Stream_KeyName_ArgumentVerifierException()
|
||||
{
|
||||
var client = new DaprClientBuilder().Build();
|
||||
const string keyName = "";
|
||||
//Get response and validate
|
||||
await Assert.ThrowsAsync<ArgumentException>(async () => await client.DecryptAsync("myVault",
|
||||
new MemoryStream(), keyName, new DecryptionOptions(), CancellationToken.None));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="FluentAssertions" />
|
||||
<PackageReference Include="Google.Protobuf" />
|
||||
<PackageReference Include="Grpc.Core.Testing" />
|
||||
<PackageReference Include="Grpc.Net.Client" />
|
||||
|
@ -14,6 +13,7 @@
|
|||
<PackageReference Include="moq" />
|
||||
<PackageReference Include="protobuf-net.Grpc.AspNetCore" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" />
|
||||
<PackageReference Include="Shouldly"/>
|
||||
<PackageReference Include="xunit" />
|
||||
<PackageReference Include="xunit.runner.visualstudio">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
|
|
|
@ -15,7 +15,7 @@ namespace Dapr.Client.Test
|
|||
{
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
using Autogenerated = Dapr.Client.Autogen.Grpc.v1;
|
||||
|
||||
|
@ -38,8 +38,8 @@ namespace Dapr.Client.Test
|
|||
await request.GetRequestEnvelopeAsync<Autogenerated.GetSecretRequest>();
|
||||
|
||||
request.Request.Headers.TryGetValues("dapr-api-token", out var headerValues);
|
||||
headerValues.Count().Should().Be(1);
|
||||
headerValues.First().Should().Be("test_token");
|
||||
headerValues.Count().ShouldBe(1);
|
||||
headerValues.First().ShouldBe("test_token");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -59,7 +59,7 @@ namespace Dapr.Client.Test
|
|||
await request.GetRequestEnvelopeAsync<Autogenerated.GetSecretRequest>();
|
||||
|
||||
request.Request.Headers.TryGetValues("dapr-api-token", out var headerValues);
|
||||
headerValues.Should().BeNull();
|
||||
headerValues.ShouldBeNull();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ using System.Threading;
|
|||
using System.Threading.Tasks;
|
||||
using Dapr.Client.Autogen.Grpc.v1;
|
||||
using Dapr.Client.Autogen.Test.Grpc.v1;
|
||||
using FluentAssertions;
|
||||
using Shouldly;
|
||||
using Google.Protobuf.WellKnownTypes;
|
||||
using Grpc.Core;
|
||||
using Grpc.Net.Client;
|
||||
|
@ -62,9 +62,9 @@ namespace Dapr.Client.Test
|
|||
|
||||
// Get Request and validate
|
||||
var envelope = await request.GetRequestEnvelopeAsync<InvokeServiceRequest>();
|
||||
envelope.Id.Should().Be("test");
|
||||
envelope.Message.Method.Should().Be("test");
|
||||
envelope.Message.ContentType.Should().Be(Constants.ContentTypeApplicationGrpc);
|
||||
envelope.Id.ShouldBe("test");
|
||||
envelope.Message.Method.ShouldBe("test");
|
||||
envelope.Message.ContentType.ShouldBe(Constants.ContentTypeApplicationGrpc);
|
||||
|
||||
// Create Response & Respond
|
||||
var data = new Response() { Name = "Look, I was invoked!" };
|
||||
|
@ -75,7 +75,7 @@ namespace Dapr.Client.Test
|
|||
|
||||
// Validate Response
|
||||
var invokedResponse = await request.CompleteWithMessageAsync(response);
|
||||
invokedResponse.Name.Should().Be("Look, I was invoked!");
|
||||
invokedResponse.Name.ShouldBe("Look, I was invoked!");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -126,9 +126,9 @@ namespace Dapr.Client.Test
|
|||
|
||||
// Get Request and validate
|
||||
var envelope = await request.GetRequestEnvelopeAsync<InvokeServiceRequest>();
|
||||
envelope.Id.Should().Be("test");
|
||||
envelope.Message.Method.Should().Be("test");
|
||||
envelope.Message.ContentType.Should().Be(string.Empty);
|
||||
envelope.Id.ShouldBe("test");
|
||||
envelope.Message.Method.ShouldBe("test");
|
||||
envelope.Message.ContentType.ShouldBe(string.Empty);
|
||||
|
||||
// Create Response & Respond
|
||||
var data = new Response() { Name = "Look, I was invoked!" };
|
||||
|
@ -139,7 +139,7 @@ namespace Dapr.Client.Test
|
|||
|
||||
// Validate Response
|
||||
var invokedResponse = await request.CompleteWithMessageAsync(response);
|
||||
invokedResponse.Name.Should().Be("Look, I was invoked!");
|
||||
invokedResponse.Name.ShouldBe("Look, I was invoked!");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -176,7 +176,7 @@ namespace Dapr.Client.Test
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public void InvokeMethodGrpcAsync_CanInvokeMethodWithNoReturnTypeAndData()
|
||||
public async Task InvokeMethodGrpcAsync_CanInvokeMethodWithNoReturnTypeAndData()
|
||||
{
|
||||
var request = new Request() { RequestParameter = "Hello " };
|
||||
var client = new MockClient();
|
||||
|
@ -195,7 +195,7 @@ namespace Dapr.Client.Test
|
|||
.Setup(m => m.InvokeServiceAsync(It.IsAny<Autogen.Grpc.v1.InvokeServiceRequest>(), It.IsAny<CallOptions>()))
|
||||
.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]
|
||||
|
@ -250,9 +250,9 @@ namespace Dapr.Client.Test
|
|||
|
||||
// Get Request and validate
|
||||
var envelope = await request.GetRequestEnvelopeAsync<InvokeServiceRequest>();
|
||||
envelope.Id.Should().Be("test");
|
||||
envelope.Message.Method.Should().Be("test");
|
||||
envelope.Message.ContentType.Should().Be(Constants.ContentTypeApplicationGrpc);
|
||||
envelope.Id.ShouldBe("test");
|
||||
envelope.Message.Method.ShouldBe("test");
|
||||
envelope.Message.ContentType.ShouldBe(Constants.ContentTypeApplicationGrpc);
|
||||
|
||||
var actual = envelope.Message.Data.Unpack<Request>();
|
||||
Assert.Equal(invokeRequest.RequestParameter, actual.RequestParameter);
|
||||
|
@ -275,9 +275,9 @@ namespace Dapr.Client.Test
|
|||
|
||||
// Get Request and validate
|
||||
var envelope = await request.GetRequestEnvelopeAsync<InvokeServiceRequest>();
|
||||
envelope.Id.Should().Be("test");
|
||||
envelope.Message.Method.Should().Be("test");
|
||||
envelope.Message.ContentType.Should().Be(Constants.ContentTypeApplicationGrpc);
|
||||
envelope.Id.ShouldBe("test");
|
||||
envelope.Message.Method.ShouldBe("test");
|
||||
envelope.Message.ContentType.ShouldBe(Constants.ContentTypeApplicationGrpc);
|
||||
|
||||
var actual = envelope.Message.Data.Unpack<Request>();
|
||||
Assert.Equal(invokeRequest.RequestParameter, actual.RequestParameter);
|
||||
|
@ -291,7 +291,7 @@ namespace Dapr.Client.Test
|
|||
|
||||
// Validate Response
|
||||
var invokedResponse = await request.CompleteWithMessageAsync(response);
|
||||
invokedResponse.Name.Should().Be(invokeResponse.Name);
|
||||
invokedResponse.Name.ShouldBe(invokeResponse.Name);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -308,7 +308,7 @@ namespace Dapr.Client.Test
|
|||
|
||||
var response = await daprClient.InvokeMethodGrpcAsync<Request, Response>("test", "SayHello", request);
|
||||
|
||||
response.Name.Should().Be("Hello Look, I was invoked!");
|
||||
response.Name.ShouldBe("Hello Look, I was invoked!");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -328,10 +328,10 @@ namespace Dapr.Client.Test
|
|||
|
||||
var response = await daprClient.InvokeMethodGrpcAsync<TestRun, TestRun>("test", "TestRun", testRun);
|
||||
|
||||
response.Tests.Count.Should().Be(3);
|
||||
response.Tests[0].Name.Should().Be("test1");
|
||||
response.Tests[1].Name.Should().Be("test2");
|
||||
response.Tests[2].Name.Should().Be("test3");
|
||||
response.Tests.Count.ShouldBe(3);
|
||||
response.Tests[0].Name.ShouldBe("test1");
|
||||
response.Tests[1].Name.ShouldBe("test2");
|
||||
response.Tests[2].Name.ShouldBe("test3");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -348,7 +348,7 @@ namespace Dapr.Client.Test
|
|||
|
||||
var response = await daprClient.InvokeMethodGrpcAsync<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
|
||||
var metadata = await request.CompleteWithMessageAsync(response);
|
||||
metadata.Id.Should().Be("testId");
|
||||
metadata.Extended.Should().Contain(new System.Collections.Generic.KeyValuePair<string, string>("e1", "v1"));
|
||||
metadata.Actors.Should().Contain(actors => actors.Count == 1 && actors.Type == "testType");
|
||||
metadata.Components.Should().Contain(components => components.Name == "testName" && components.Type == "testType" && components.Version == "V1" && components.Capabilities.Length == 0);
|
||||
metadata.Id.ShouldBe("testId");
|
||||
metadata.Extended.ShouldContain(new System.Collections.Generic.KeyValuePair<string, string>("e1", "v1"));
|
||||
metadata.Actors.ShouldContain(actors => actors.Count == 1 && actors.Type == "testType");
|
||||
metadata.Components.ShouldContain(components => components.Name == "testName" && components.Type == "testType" && components.Version == "V1" && components.Capabilities.Length == 0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -445,8 +445,8 @@ namespace Dapr.Client.Test
|
|||
|
||||
// Get Request and validate
|
||||
var envelope = await request.GetRequestEnvelopeAsync<SetMetadataRequest>();
|
||||
envelope.Key.Should().Be("test");
|
||||
envelope.Value.Should().Be("testv");
|
||||
envelope.Key.ShouldBe("test");
|
||||
envelope.Value.ShouldBe("testv");
|
||||
|
||||
await request.CompleteWithMessageAsync(new Empty());
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
using System.Threading.Tasks;
|
||||
using Autogenerated = Dapr.Client.Autogen.Grpc.v1;
|
||||
using Xunit;
|
||||
using FluentAssertions;
|
||||
using Shouldly;
|
||||
using System;
|
||||
|
||||
namespace Dapr.Client.Test
|
||||
|
@ -37,10 +37,10 @@ namespace Dapr.Client.Test
|
|||
|
||||
// Get Request and validate
|
||||
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.TryLockRequest>();
|
||||
envelope.StoreName.Should().Be("redis");
|
||||
envelope.ResourceId.Should().Be("resourceId");
|
||||
envelope.LockOwner.Should().Be("owner1");
|
||||
envelope.ExpiryInSeconds.Should().Be(1000);
|
||||
envelope.StoreName.ShouldBe("redis");
|
||||
envelope.ResourceId.ShouldBe("resourceId");
|
||||
envelope.LockOwner.ShouldBe("owner1");
|
||||
envelope.ExpiryInSeconds.ShouldBe(1000);
|
||||
|
||||
// Get response and validate
|
||||
var invokeResponse = new Autogenerated.TryLockResponse{
|
||||
|
@ -48,7 +48,7 @@ namespace Dapr.Client.Test
|
|||
};
|
||||
|
||||
var domainResponse = await request.CompleteWithMessageAsync(invokeResponse);
|
||||
domainResponse.Success.Should().Be(true);
|
||||
domainResponse.Success.ShouldBe(true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -80,9 +80,9 @@ namespace Dapr.Client.Test
|
|||
|
||||
// Get Request and validate
|
||||
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.UnlockRequest>();
|
||||
envelope.StoreName.Should().Be("redis");
|
||||
envelope.ResourceId.Should().Be("resourceId");
|
||||
envelope.LockOwner.Should().Be("owner1");
|
||||
envelope.StoreName.ShouldBe("redis");
|
||||
envelope.ResourceId.ShouldBe("resourceId");
|
||||
envelope.LockOwner.ShouldBe("owner1");
|
||||
|
||||
// Get response and validate
|
||||
var invokeResponse = new Autogenerated.UnlockResponse{
|
||||
|
@ -90,7 +90,7 @@ namespace Dapr.Client.Test
|
|||
};
|
||||
|
||||
var domainResponse = await request.CompleteWithMessageAsync(invokeResponse);
|
||||
domainResponse.status.Should().Be(LockStatus.LockDoesNotExist);
|
||||
domainResponse.status.ShouldBe(LockStatus.LockDoesNotExist);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ namespace Dapr.Client.Test
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Dapr.Client.Autogen.Grpc.v1;
|
||||
using FluentAssertions;
|
||||
using Shouldly;
|
||||
using Google.Protobuf;
|
||||
using Grpc.Core;
|
||||
using Moq;
|
||||
|
@ -43,11 +43,11 @@ namespace Dapr.Client.Test
|
|||
|
||||
// Get Request and validate
|
||||
var envelope = await request.GetRequestEnvelopeAsync<InvokeBindingRequest>();
|
||||
envelope.Name.Should().Be("test");
|
||||
envelope.Metadata.Count.Should().Be(0);
|
||||
envelope.Name.ShouldBe("test");
|
||||
envelope.Metadata.Count.ShouldBe(0);
|
||||
var json = envelope.Data.ToStringUtf8();
|
||||
var typeFromRequest = JsonSerializer.Deserialize<InvokeRequest>(json, client.InnerClient.JsonSerializerOptions);
|
||||
typeFromRequest.RequestParameter.Should().Be("Hello ");
|
||||
typeFromRequest.RequestParameter.ShouldBe("Hello ");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -70,15 +70,15 @@ namespace Dapr.Client.Test
|
|||
|
||||
// Get Request and validate
|
||||
var envelope = await request.GetRequestEnvelopeAsync<InvokeBindingRequest>();
|
||||
envelope.Name.Should().Be("test");
|
||||
envelope.Metadata.Count.Should().Be(2);
|
||||
envelope.Metadata.Keys.Contains("key1").Should().BeTrue();
|
||||
envelope.Metadata.Keys.Contains("key2").Should().BeTrue();
|
||||
envelope.Metadata["key1"].Should().Be("value1");
|
||||
envelope.Metadata["key2"].Should().Be("value2");
|
||||
envelope.Name.ShouldBe("test");
|
||||
envelope.Metadata.Count.ShouldBe(2);
|
||||
envelope.Metadata.Keys.Contains("key1").ShouldBeTrue();
|
||||
envelope.Metadata.Keys.Contains("key2").ShouldBeTrue();
|
||||
envelope.Metadata["key1"].ShouldBe("value1");
|
||||
envelope.Metadata["key2"].ShouldBe("value2");
|
||||
var json = envelope.Data.ToStringUtf8();
|
||||
var typeFromRequest = JsonSerializer.Deserialize<InvokeRequest>(json, client.InnerClient.JsonSerializerOptions);
|
||||
typeFromRequest.RequestParameter.Should().Be("Hello ");
|
||||
typeFromRequest.RequestParameter.ShouldBe("Hello ");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -95,8 +95,8 @@ namespace Dapr.Client.Test
|
|||
|
||||
// Get Request and validate
|
||||
var envelope = await request.GetRequestEnvelopeAsync<InvokeBindingRequest>();
|
||||
envelope.Name.Should().Be("test");
|
||||
envelope.Metadata.Count.Should().Be(0);
|
||||
envelope.Name.ShouldBe("test");
|
||||
envelope.Metadata.Count.ShouldBe(0);
|
||||
var json = envelope.Data.ToStringUtf8();
|
||||
Assert.Equal("null", json);
|
||||
}
|
||||
|
@ -133,16 +133,16 @@ namespace Dapr.Client.Test
|
|||
var response = await request.CompleteWithMessageAsync(gRpcResponse);
|
||||
|
||||
var envelope = await request.GetRequestEnvelopeAsync<InvokeBindingRequest>();
|
||||
envelope.Name.Should().Be("test");
|
||||
envelope.Metadata.Count.Should().Be(2);
|
||||
envelope.Metadata.Keys.Contains("key1").Should().BeTrue();
|
||||
envelope.Metadata.Keys.Contains("key2").Should().BeTrue();
|
||||
envelope.Metadata["key1"].Should().Be("value1");
|
||||
envelope.Metadata["key2"].Should().Be("value2");
|
||||
envelope.Name.ShouldBe("test");
|
||||
envelope.Metadata.Count.ShouldBe(2);
|
||||
envelope.Metadata.Keys.Contains("key1").ShouldBeTrue();
|
||||
envelope.Metadata.Keys.Contains("key2").ShouldBeTrue();
|
||||
envelope.Metadata["key1"].ShouldBe("value1");
|
||||
envelope.Metadata["key2"].ShouldBe("value2");
|
||||
|
||||
var json = envelope.Data.ToStringUtf8();
|
||||
var typeFromRequest = JsonSerializer.Deserialize<InvokeRequest>(json, client.InnerClient.JsonSerializerOptions);
|
||||
typeFromRequest.RequestParameter.Should().Be("Hello ");
|
||||
typeFromRequest.RequestParameter.ShouldBe("Hello ");
|
||||
|
||||
Assert.Same(bindingRequest, response.Request);
|
||||
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 envelope = await req.GetRequestEnvelopeAsync<InvokeBindingRequest>();
|
||||
envelope.Name.Should().Be("binding");
|
||||
envelope.Operation.Should().Be("operation");
|
||||
envelope.Metadata.Count.Should().Be(1);
|
||||
envelope.Metadata.Keys.Contains("key1").Should().BeTrue();
|
||||
envelope.Metadata["key1"].Should().Be("value1");
|
||||
envelope.Name.ShouldBe("binding");
|
||||
envelope.Operation.ShouldBe("operation");
|
||||
envelope.Metadata.Count.ShouldBe(1);
|
||||
envelope.Metadata.Keys.Contains("key1").ShouldBeTrue();
|
||||
envelope.Metadata["key1"].ShouldBe("value1");
|
||||
|
||||
var json = envelope.Data.ToStringUtf8();
|
||||
var typeFromRequest = JsonSerializer.Deserialize<InvokeRequest>(json, client.InnerClient.JsonSerializerOptions);
|
||||
typeFromRequest.RequestParameter.Should().Be("Test");
|
||||
typeFromRequest.RequestParameter.ShouldBe("Test");
|
||||
|
||||
Assert.Equal("red", response.Color);
|
||||
}
|
||||
|
@ -269,8 +269,8 @@ namespace Dapr.Client.Test
|
|||
var response = await req.CompleteWithMessageAsync(resp);
|
||||
|
||||
var envelope = await req.GetRequestEnvelopeAsync<InvokeBindingRequest>();
|
||||
envelope.Name.Should().Be("binding");
|
||||
envelope.Operation.Should().Be("operation");
|
||||
envelope.Name.ShouldBe("binding");
|
||||
envelope.Operation.ShouldBe("operation");
|
||||
|
||||
Assert.Equal("red", response.Color);
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ namespace Dapr.Client.Test
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Dapr.Client.Autogen.Grpc.v1;
|
||||
using FluentAssertions;
|
||||
using Shouldly;
|
||||
using Grpc.Core;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
@ -49,11 +49,11 @@ namespace Dapr.Client.Test
|
|||
var envelope = await request.GetRequestEnvelopeAsync<PublishEventRequest>();
|
||||
var jsonFromRequest = envelope.Data.ToStringUtf8();
|
||||
|
||||
envelope.DataContentType.Should().Be("application/json");
|
||||
envelope.PubsubName.Should().Be(TestPubsubName);
|
||||
envelope.Topic.Should().Be("test");
|
||||
jsonFromRequest.Should().Be(JsonSerializer.Serialize(publishData, client.InnerClient.JsonSerializerOptions));
|
||||
envelope.Metadata.Count.Should().Be(0);
|
||||
envelope.DataContentType.ShouldBe("application/json");
|
||||
envelope.PubsubName.ShouldBe(TestPubsubName);
|
||||
envelope.Topic.ShouldBe("test");
|
||||
jsonFromRequest.ShouldBe(JsonSerializer.Serialize(publishData, client.InnerClient.JsonSerializerOptions));
|
||||
envelope.Metadata.Count.ShouldBe(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -75,9 +75,8 @@ namespace Dapr.Client.Test
|
|||
var client = new TestClient<DaprClient>(clientBuilder.Build(), handler);
|
||||
|
||||
//Ensure that the JsonStringEnumConverter is registered
|
||||
client.InnerClient.JsonSerializerOptions.Converters.Count.Should().Be(1);
|
||||
client.InnerClient.JsonSerializerOptions.Converters.First().GetType().Name.Should()
|
||||
.Match(nameof(JsonStringEnumConverter));
|
||||
client.InnerClient.JsonSerializerOptions.Converters.Count.ShouldBe(1);
|
||||
client.InnerClient.JsonSerializerOptions.Converters.First().GetType().Name.ShouldMatch(nameof(JsonStringEnumConverter));
|
||||
|
||||
var publishData = new Widget {Size = "Large", Color = WidgetColor.Red};
|
||||
var request = await client.CaptureGrpcRequestAsync(async daprClient =>
|
||||
|
@ -89,9 +88,8 @@ namespace Dapr.Client.Test
|
|||
|
||||
var envelope = await request.GetRequestEnvelopeAsync<PublishEventRequest>();
|
||||
var jsonFromRequest = envelope.Data.ToStringUtf8();
|
||||
jsonFromRequest.Should()
|
||||
.Be(JsonSerializer.Serialize(publishData, client.InnerClient.JsonSerializerOptions));
|
||||
jsonFromRequest.Should().Match("{\"Size\":\"Large\",\"Color\":\"Red\"}");
|
||||
jsonFromRequest.ShouldBe(JsonSerializer.Serialize(publishData, client.InnerClient.JsonSerializerOptions));
|
||||
jsonFromRequest.ShouldMatch("{\"Size\":\"Large\",\"Color\":\"Red\"}");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -116,16 +114,16 @@ namespace Dapr.Client.Test
|
|||
var envelope = await request.GetRequestEnvelopeAsync<PublishEventRequest>();
|
||||
var jsonFromRequest = envelope.Data.ToStringUtf8();
|
||||
|
||||
envelope.DataContentType.Should().Be("application/json");
|
||||
envelope.PubsubName.Should().Be(TestPubsubName);
|
||||
envelope.Topic.Should().Be("test");
|
||||
jsonFromRequest.Should().Be(JsonSerializer.Serialize(publishData, client.InnerClient.JsonSerializerOptions));
|
||||
envelope.DataContentType.ShouldBe("application/json");
|
||||
envelope.PubsubName.ShouldBe(TestPubsubName);
|
||||
envelope.Topic.ShouldBe("test");
|
||||
jsonFromRequest.ShouldBe(JsonSerializer.Serialize(publishData, client.InnerClient.JsonSerializerOptions));
|
||||
|
||||
envelope.Metadata.Count.Should().Be(2);
|
||||
envelope.Metadata.Keys.Contains("key1").Should().BeTrue();
|
||||
envelope.Metadata.Keys.Contains("key2").Should().BeTrue();
|
||||
envelope.Metadata["key1"].Should().Be("value1");
|
||||
envelope.Metadata["key2"].Should().Be("value2");
|
||||
envelope.Metadata.Count.ShouldBe(2);
|
||||
envelope.Metadata.Keys.Contains("key1").ShouldBeTrue();
|
||||
envelope.Metadata.Keys.Contains("key2").ShouldBeTrue();
|
||||
envelope.Metadata["key1"].ShouldBe("value1");
|
||||
envelope.Metadata["key2"].ShouldBe("value2");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -142,10 +140,10 @@ namespace Dapr.Client.Test
|
|||
|
||||
var envelope = await request.GetRequestEnvelopeAsync<PublishEventRequest>();
|
||||
|
||||
envelope.PubsubName.Should().Be(TestPubsubName);
|
||||
envelope.Topic.Should().Be("test");
|
||||
envelope.Data.Length.Should().Be(0);
|
||||
envelope.Metadata.Count.Should().Be(0);
|
||||
envelope.PubsubName.ShouldBe(TestPubsubName);
|
||||
envelope.Topic.ShouldBe("test");
|
||||
envelope.Data.Length.ShouldBe(0);
|
||||
envelope.Metadata.Count.ShouldBe(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -167,15 +165,15 @@ namespace Dapr.Client.Test
|
|||
request.Dismiss();
|
||||
|
||||
var envelope = await request.GetRequestEnvelopeAsync<PublishEventRequest>();
|
||||
envelope.PubsubName.Should().Be(TestPubsubName);
|
||||
envelope.Topic.Should().Be("test");
|
||||
envelope.Data.Length.Should().Be(0);
|
||||
envelope.PubsubName.ShouldBe(TestPubsubName);
|
||||
envelope.Topic.ShouldBe("test");
|
||||
envelope.Data.Length.ShouldBe(0);
|
||||
|
||||
envelope.Metadata.Count.Should().Be(2);
|
||||
envelope.Metadata.Keys.Contains("key1").Should().BeTrue();
|
||||
envelope.Metadata.Keys.Contains("key2").Should().BeTrue();
|
||||
envelope.Metadata["key1"].Should().Be("value1");
|
||||
envelope.Metadata["key2"].Should().Be("value2");
|
||||
envelope.Metadata.Count.ShouldBe(2);
|
||||
envelope.Metadata.Keys.Contains("key1").ShouldBeTrue();
|
||||
envelope.Metadata.Keys.Contains("key2").ShouldBeTrue();
|
||||
envelope.Metadata["key1"].ShouldBe("value1");
|
||||
envelope.Metadata["key2"].ShouldBe("value2");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -200,11 +198,11 @@ namespace Dapr.Client.Test
|
|||
var envelope = await request.GetRequestEnvelopeAsync<PublishEventRequest>();
|
||||
var jsonFromRequest = envelope.Data.ToStringUtf8();
|
||||
|
||||
envelope.DataContentType.Should().Be("application/cloudevents+json");
|
||||
envelope.PubsubName.Should().Be(TestPubsubName);
|
||||
envelope.Topic.Should().Be("test");
|
||||
jsonFromRequest.Should().Be(JsonSerializer.Serialize(cloudEvent, client.InnerClient.JsonSerializerOptions));
|
||||
envelope.Metadata.Count.Should().Be(0);
|
||||
envelope.DataContentType.ShouldBe("application/cloudevents+json");
|
||||
envelope.PubsubName.ShouldBe(TestPubsubName);
|
||||
envelope.Topic.ShouldBe("test");
|
||||
jsonFromRequest.ShouldBe(JsonSerializer.Serialize(cloudEvent, client.InnerClient.JsonSerializerOptions));
|
||||
envelope.Metadata.Count.ShouldBe(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -228,11 +226,11 @@ namespace Dapr.Client.Test
|
|||
var envelope = await request.GetRequestEnvelopeAsync<PublishEventRequest>();
|
||||
var jsonFromRequest = envelope.Data.ToStringUtf8();
|
||||
|
||||
envelope.DataContentType.Should().Be("application/cloudevents+json");
|
||||
envelope.PubsubName.Should().Be(TestPubsubName);
|
||||
envelope.Topic.Should().Be("test");
|
||||
jsonFromRequest.Should().Be(JsonSerializer.Serialize(cloudEvent, client.InnerClient.JsonSerializerOptions));
|
||||
envelope.Metadata.Count.Should().Be(0);
|
||||
envelope.DataContentType.ShouldBe("application/cloudevents+json");
|
||||
envelope.PubsubName.ShouldBe(TestPubsubName);
|
||||
envelope.Topic.ShouldBe("test");
|
||||
jsonFromRequest.ShouldBe(JsonSerializer.Serialize(cloudEvent, client.InnerClient.JsonSerializerOptions));
|
||||
envelope.Metadata.Count.ShouldBe(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -287,13 +285,13 @@ namespace Dapr.Client.Test
|
|||
var envelope = await request.GetRequestEnvelopeAsync<PublishEventRequest>();
|
||||
var jsonFromRequest = envelope.Data.ToStringUtf8();
|
||||
|
||||
envelope.DataContentType.Should().Be("application/json");
|
||||
envelope.PubsubName.Should().Be(TestPubsubName);
|
||||
envelope.Topic.Should().Be("test");
|
||||
jsonFromRequest.Should().Be(JsonSerializer.Serialize(publishData));
|
||||
envelope.DataContentType.ShouldBe("application/json");
|
||||
envelope.PubsubName.ShouldBe(TestPubsubName);
|
||||
envelope.Topic.ShouldBe("test");
|
||||
jsonFromRequest.ShouldBe(JsonSerializer.Serialize(publishData));
|
||||
// The default serializer forces camel case, so this should be different from our serialization above.
|
||||
jsonFromRequest.Should().NotBe(JsonSerializer.Serialize(publishBytes, client.InnerClient.JsonSerializerOptions));
|
||||
envelope.Metadata.Count.Should().Be(0);
|
||||
jsonFromRequest.ShouldNotBe(JsonSerializer.Serialize(publishBytes, client.InnerClient.JsonSerializerOptions));
|
||||
envelope.Metadata.Count.ShouldBe(0);
|
||||
}
|
||||
|
||||
private class PublishData
|
||||
|
|
|
@ -17,7 +17,7 @@ namespace Dapr.Client.Test
|
|||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Shouldly;
|
||||
using Grpc.Core;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
@ -44,13 +44,13 @@ namespace Dapr.Client.Test
|
|||
|
||||
// Get Request and validate
|
||||
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.GetSecretRequest>();
|
||||
envelope.StoreName.Should().Be("testStore");
|
||||
envelope.Key.Should().Be("test_key");
|
||||
envelope.Metadata.Count.Should().Be(2);
|
||||
envelope.Metadata.Keys.Contains("key1").Should().BeTrue();
|
||||
envelope.Metadata.Keys.Contains("key2").Should().BeTrue();
|
||||
envelope.Metadata["key1"].Should().Be("value1");
|
||||
envelope.Metadata["key2"].Should().Be("value2");
|
||||
envelope.StoreName.ShouldBe("testStore");
|
||||
envelope.Key.ShouldBe("test_key");
|
||||
envelope.Metadata.Count.ShouldBe(2);
|
||||
envelope.Metadata.Keys.Contains("key1").ShouldBeTrue();
|
||||
envelope.Metadata.Keys.Contains("key2").ShouldBeTrue();
|
||||
envelope.Metadata["key1"].ShouldBe("value1");
|
||||
envelope.Metadata["key2"].ShouldBe("value2");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -72,13 +72,13 @@ namespace Dapr.Client.Test
|
|||
|
||||
// Get Request and validate
|
||||
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.GetSecretRequest>();
|
||||
envelope.StoreName.Should().Be("testStore");
|
||||
envelope.Key.Should().Be("test_key");
|
||||
envelope.Metadata.Count.Should().Be(2);
|
||||
envelope.Metadata.Keys.Contains("key1").Should().BeTrue();
|
||||
envelope.Metadata.Keys.Contains("key2").Should().BeTrue();
|
||||
envelope.Metadata["key1"].Should().Be("value1");
|
||||
envelope.Metadata["key2"].Should().Be("value2");
|
||||
envelope.StoreName.ShouldBe("testStore");
|
||||
envelope.Key.ShouldBe("test_key");
|
||||
envelope.Metadata.Count.ShouldBe(2);
|
||||
envelope.Metadata.Keys.Contains("key1").ShouldBeTrue();
|
||||
envelope.Metadata.Keys.Contains("key2").ShouldBeTrue();
|
||||
envelope.Metadata["key1"].ShouldBe("value1");
|
||||
envelope.Metadata["key2"].ShouldBe("value2");
|
||||
|
||||
// Create Response & Respond
|
||||
var secrets = new Dictionary<string, string>
|
||||
|
@ -88,9 +88,9 @@ namespace Dapr.Client.Test
|
|||
var secretsResponse = await SendResponseWithSecrets(secrets, request);
|
||||
|
||||
// Get response and validate
|
||||
secretsResponse.Count.Should().Be(1);
|
||||
secretsResponse.ContainsKey("redis_secret").Should().BeTrue();
|
||||
secretsResponse["redis_secret"].Should().Be("Guess_Redis");
|
||||
secretsResponse.Count.ShouldBe(1);
|
||||
secretsResponse.ContainsKey("redis_secret").ShouldBeTrue();
|
||||
secretsResponse["redis_secret"].ShouldBe("Guess_Redis");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -107,16 +107,16 @@ namespace Dapr.Client.Test
|
|||
|
||||
//Get Request and validate
|
||||
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.GetSecretRequest>();
|
||||
envelope.StoreName.Should().Be("testStore");
|
||||
envelope.Key.Should().Be("us-west-1/org/xpto/secretabc");
|
||||
envelope.StoreName.ShouldBe("testStore");
|
||||
envelope.Key.ShouldBe("us-west-1/org/xpto/secretabc");
|
||||
|
||||
var secrets = new Dictionary<string, string> { { "us-west-1/org/xpto/secretabc", "abc123" } };
|
||||
var secretsResponse = await SendResponseWithSecrets(secrets, request);
|
||||
|
||||
//Get response and validate
|
||||
secretsResponse.Count.Should().Be(1);
|
||||
secretsResponse.ContainsKey("us-west-1/org/xpto/secretabc").Should().BeTrue();
|
||||
secretsResponse["us-west-1/org/xpto/secretabc"].Should().Be("abc123");
|
||||
secretsResponse.Count.ShouldBe(1);
|
||||
secretsResponse.ContainsKey("us-west-1/org/xpto/secretabc").ShouldBeTrue();
|
||||
secretsResponse["us-west-1/org/xpto/secretabc"].ShouldBe("abc123");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -138,13 +138,13 @@ namespace Dapr.Client.Test
|
|||
|
||||
// Get Request and validate
|
||||
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.GetSecretRequest>();
|
||||
envelope.StoreName.Should().Be("testStore");
|
||||
envelope.Key.Should().Be("test_key");
|
||||
envelope.Metadata.Count.Should().Be(2);
|
||||
envelope.Metadata.Keys.Contains("key1").Should().BeTrue();
|
||||
envelope.Metadata.Keys.Contains("key2").Should().BeTrue();
|
||||
envelope.Metadata["key1"].Should().Be("value1");
|
||||
envelope.Metadata["key2"].Should().Be("value2");
|
||||
envelope.StoreName.ShouldBe("testStore");
|
||||
envelope.Key.ShouldBe("test_key");
|
||||
envelope.Metadata.Count.ShouldBe(2);
|
||||
envelope.Metadata.Keys.Contains("key1").ShouldBeTrue();
|
||||
envelope.Metadata.Keys.Contains("key2").ShouldBeTrue();
|
||||
envelope.Metadata["key1"].ShouldBe("value1");
|
||||
envelope.Metadata["key2"].ShouldBe("value2");
|
||||
|
||||
// Create Response & Respond
|
||||
var secrets = new Dictionary<string, string>
|
||||
|
@ -155,11 +155,11 @@ namespace Dapr.Client.Test
|
|||
var secretsResponse = await SendResponseWithSecrets(secrets, request);
|
||||
|
||||
// Get response and validate
|
||||
secretsResponse.Count.Should().Be(2);
|
||||
secretsResponse.ContainsKey("redis_secret").Should().BeTrue();
|
||||
secretsResponse["redis_secret"].Should().Be("Guess_Redis");
|
||||
secretsResponse.ContainsKey("kafka_secret").Should().BeTrue();
|
||||
secretsResponse["kafka_secret"].Should().Be("Guess_Kafka");
|
||||
secretsResponse.Count.ShouldBe(2);
|
||||
secretsResponse.ContainsKey("redis_secret").ShouldBeTrue();
|
||||
secretsResponse["redis_secret"].ShouldBe("Guess_Redis");
|
||||
secretsResponse.ContainsKey("kafka_secret").ShouldBeTrue();
|
||||
secretsResponse["kafka_secret"].ShouldBe("Guess_Kafka");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -220,12 +220,12 @@ namespace Dapr.Client.Test
|
|||
|
||||
// Get Request and validate
|
||||
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.GetBulkSecretRequest>();
|
||||
envelope.StoreName.Should().Be("testStore");
|
||||
envelope.Metadata.Count.Should().Be(2);
|
||||
envelope.Metadata.Keys.Contains("key1").Should().BeTrue();
|
||||
envelope.Metadata.Keys.Contains("key2").Should().BeTrue();
|
||||
envelope.Metadata["key1"].Should().Be("value1");
|
||||
envelope.Metadata["key2"].Should().Be("value2");
|
||||
envelope.StoreName.ShouldBe("testStore");
|
||||
envelope.Metadata.Count.ShouldBe(2);
|
||||
envelope.Metadata.Keys.Contains("key1").ShouldBeTrue();
|
||||
envelope.Metadata.Keys.Contains("key2").ShouldBeTrue();
|
||||
envelope.Metadata["key1"].ShouldBe("value1");
|
||||
envelope.Metadata["key2"].ShouldBe("value2");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -244,12 +244,12 @@ namespace Dapr.Client.Test
|
|||
|
||||
// Get Request and validate
|
||||
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.GetBulkSecretRequest>();
|
||||
envelope.StoreName.Should().Be("testStore");
|
||||
envelope.Metadata.Count.Should().Be(2);
|
||||
envelope.Metadata.Keys.Contains("key1").Should().BeTrue();
|
||||
envelope.Metadata.Keys.Contains("key2").Should().BeTrue();
|
||||
envelope.Metadata["key1"].Should().Be("value1");
|
||||
envelope.Metadata["key2"].Should().Be("value2");
|
||||
envelope.StoreName.ShouldBe("testStore");
|
||||
envelope.Metadata.Count.ShouldBe(2);
|
||||
envelope.Metadata.Keys.Contains("key1").ShouldBeTrue();
|
||||
envelope.Metadata.Keys.Contains("key2").ShouldBeTrue();
|
||||
envelope.Metadata["key1"].ShouldBe("value1");
|
||||
envelope.Metadata["key2"].ShouldBe("value2");
|
||||
|
||||
// Create Response & Respond
|
||||
var secrets = new Dictionary<string, string>();
|
||||
|
@ -257,9 +257,9 @@ namespace Dapr.Client.Test
|
|||
var secretsResponse = await SendBulkResponseWithSecrets(secrets, request);
|
||||
|
||||
// Get response and validate
|
||||
secretsResponse.Count.Should().Be(1);
|
||||
secretsResponse.ContainsKey("redis_secret").Should().BeTrue();
|
||||
secretsResponse["redis_secret"]["redis_secret"].Should().Be("Guess_Redis");
|
||||
secretsResponse.Count.ShouldBe(1);
|
||||
secretsResponse.ContainsKey("redis_secret").ShouldBeTrue();
|
||||
secretsResponse["redis_secret"]["redis_secret"].ShouldBe("Guess_Redis");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -279,12 +279,12 @@ namespace Dapr.Client.Test
|
|||
|
||||
// Get Request and validate
|
||||
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.GetBulkSecretRequest>();
|
||||
envelope.StoreName.Should().Be("testStore");
|
||||
envelope.Metadata.Count.Should().Be(2);
|
||||
envelope.Metadata.Keys.Contains("key1").Should().BeTrue();
|
||||
envelope.Metadata.Keys.Contains("key2").Should().BeTrue();
|
||||
envelope.Metadata["key1"].Should().Be("value1");
|
||||
envelope.Metadata["key2"].Should().Be("value2");
|
||||
envelope.StoreName.ShouldBe("testStore");
|
||||
envelope.Metadata.Count.ShouldBe(2);
|
||||
envelope.Metadata.Keys.Contains("key1").ShouldBeTrue();
|
||||
envelope.Metadata.Keys.Contains("key2").ShouldBeTrue();
|
||||
envelope.Metadata["key1"].ShouldBe("value1");
|
||||
envelope.Metadata["key2"].ShouldBe("value2");
|
||||
|
||||
// Create Response & Respond
|
||||
var secrets = new Dictionary<string, string>();
|
||||
|
@ -293,11 +293,11 @@ namespace Dapr.Client.Test
|
|||
var secretsResponse = await SendBulkResponseWithSecrets(secrets, request);
|
||||
|
||||
// Get response and validate
|
||||
secretsResponse.Count.Should().Be(2);
|
||||
secretsResponse.ContainsKey("redis_secret").Should().BeTrue();
|
||||
secretsResponse["redis_secret"]["redis_secret"].Should().Be("Guess_Redis");
|
||||
secretsResponse.ContainsKey("kafka_secret").Should().BeTrue();
|
||||
secretsResponse["kafka_secret"]["kafka_secret"].Should().Be("Guess_Kafka");
|
||||
secretsResponse.Count.ShouldBe(2);
|
||||
secretsResponse.ContainsKey("redis_secret").ShouldBeTrue();
|
||||
secretsResponse["redis_secret"]["redis_secret"].ShouldBe("Guess_Redis");
|
||||
secretsResponse.ContainsKey("kafka_secret").ShouldBeTrue();
|
||||
secretsResponse["kafka_secret"]["kafka_secret"].ShouldBe("Guess_Kafka");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
|
@ -18,12 +18,12 @@ using System.Net;
|
|||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Autogenerated = Dapr.Client.Autogen.Grpc.v1;
|
||||
using FluentAssertions;
|
||||
using Google.Protobuf;
|
||||
using Grpc.Core;
|
||||
using Moq;
|
||||
using StateConsistency = Dapr.Client.Autogen.Grpc.v1.StateOptions.Types.StateConsistency;
|
||||
using StateConcurrency = Dapr.Client.Autogen.Grpc.v1.StateOptions.Types.StateConcurrency;
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
using System.Threading;
|
||||
using System.Net.Http;
|
||||
|
@ -48,8 +48,8 @@ namespace Dapr.Client.Test
|
|||
var state = await request.CompleteWithMessageAsync(envelope);
|
||||
|
||||
// Get response and validate
|
||||
state.Size.Should().Be("small");
|
||||
state.Color.Should().Be("yellow");
|
||||
state.Size.ShouldBe("small");
|
||||
state.Color.ShouldBe("yellow");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -66,7 +66,7 @@ namespace Dapr.Client.Test
|
|||
var state = await request.CompleteWithMessageAsync(envelope);
|
||||
|
||||
// Get response and validate
|
||||
state.Should().HaveCount(1);
|
||||
state.Count.ShouldBe(1);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -85,9 +85,9 @@ namespace Dapr.Client.Test
|
|||
var state = await request.CompleteWithMessageAsync(envelope);
|
||||
|
||||
// Get response and validate
|
||||
state.Should().HaveCount(1);
|
||||
state[0].Value.Size.Should().Match(size);
|
||||
state[0].Value.Color.Should().Match(color);
|
||||
state.Count.ShouldBe(1);
|
||||
state[0].Value.Size.ShouldMatch(size);
|
||||
state[0].Value.Color.ShouldMatch(color);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -122,8 +122,8 @@ namespace Dapr.Client.Test
|
|||
|
||||
// Create Response & Validate
|
||||
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.GetBulkStateRequest>();
|
||||
envelope.StoreName.Should().Be("testStore");
|
||||
envelope.Metadata.Should().BeEquivalentTo(metadata);
|
||||
envelope.StoreName.ShouldBe("testStore");
|
||||
envelope.Metadata.ShouldBe(metadata);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -139,9 +139,9 @@ namespace Dapr.Client.Test
|
|||
var (state, etag) = await request.CompleteWithMessageAsync(envelope);
|
||||
|
||||
// Get response and validate
|
||||
state.Size.Should().Be("small");
|
||||
state.Color.Should().Be("yellow");
|
||||
etag.Should().Be("Test_Etag");
|
||||
state.Size.ShouldBe("small");
|
||||
state.Color.ShouldBe("yellow");
|
||||
etag.ShouldBe("Test_Etag");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -191,7 +191,7 @@ namespace Dapr.Client.Test
|
|||
var state = await request.CompleteWithMessageAsync(envelope);
|
||||
|
||||
// Get response and validate
|
||||
state.Should().BeNull();
|
||||
state.ShouldBeNull();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
@ -205,15 +205,15 @@ namespace Dapr.Client.Test
|
|||
|
||||
// Get Request & Validate
|
||||
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.GetStateRequest>();
|
||||
envelope.StoreName.Should().Be("testStore");
|
||||
envelope.Key.Should().Be("test");
|
||||
envelope.Consistency.Should().Be(expectedConsistencyMode);
|
||||
envelope.StoreName.ShouldBe("testStore");
|
||||
envelope.Key.ShouldBe("test");
|
||||
envelope.Consistency.ShouldBe(expectedConsistencyMode);
|
||||
|
||||
// Create Response & Respond
|
||||
var state = await request.CompleteWithMessageAsync(MakeGetStateResponse<Widget>(null));
|
||||
|
||||
// Get response and validate
|
||||
state.Should().BeNull();
|
||||
state.ShouldBeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -229,15 +229,15 @@ namespace Dapr.Client.Test
|
|||
|
||||
// Get Request & Validate
|
||||
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.GetStateRequest>();
|
||||
envelope.StoreName.Should().Be("testStore");
|
||||
envelope.Key.Should().Be("test");
|
||||
envelope.Metadata.Should().BeEquivalentTo(metadata);
|
||||
envelope.StoreName.ShouldBe("testStore");
|
||||
envelope.Key.ShouldBe("test");
|
||||
envelope.Metadata.ShouldBe(metadata);
|
||||
|
||||
// Create Response & Respond
|
||||
var state = await request.CompleteWithMessageAsync(MakeGetStateResponse<Widget>(null));
|
||||
|
||||
// Get response and validate
|
||||
state.Should().BeNull();
|
||||
state.ShouldBeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -291,15 +291,15 @@ namespace Dapr.Client.Test
|
|||
|
||||
// Get Request and validate
|
||||
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.SaveStateRequest>();
|
||||
envelope.StoreName.Should().Be("testStore");
|
||||
envelope.States.Count.Should().Be(1);
|
||||
envelope.StoreName.ShouldBe("testStore");
|
||||
envelope.States.Count.ShouldBe(1);
|
||||
var state = envelope.States[0];
|
||||
state.Key.Should().Be("test");
|
||||
state.Key.ShouldBe("test");
|
||||
|
||||
var stateJson = state.Value.ToStringUtf8();
|
||||
var stateFromRequest = JsonSerializer.Deserialize<Widget>(stateJson, client.InnerClient.JsonSerializerOptions);
|
||||
stateFromRequest.Size.Should().Be(widget.Size);
|
||||
stateFromRequest.Color.Should().Be(widget.Color);
|
||||
stateFromRequest.Size.ShouldBe(widget.Size);
|
||||
stateFromRequest.Color.ShouldBe(widget.Color);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -329,20 +329,20 @@ namespace Dapr.Client.Test
|
|||
|
||||
// Create Response & Validate
|
||||
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.SaveStateRequest>();
|
||||
envelope.StoreName.Should().Be("testStore");
|
||||
envelope.States.Count.Should().Be(3);
|
||||
envelope.StoreName.ShouldBe("testStore");
|
||||
envelope.States.Count.ShouldBe(3);
|
||||
|
||||
envelope.States[0].Key.Should().Be("testKey1");
|
||||
envelope.States[0].Value.Should().Equal(ByteString.CopyFromUtf8(JsonSerializer.Serialize("testValue1")));
|
||||
envelope.States[0].Metadata.Should().ContainKey("partitionKey1");
|
||||
envelope.States[0].Key.ShouldBe("testKey1");
|
||||
envelope.States[0].Value.ShouldBe(ByteString.CopyFromUtf8(JsonSerializer.Serialize("testValue1")));
|
||||
envelope.States[0].Metadata.ShouldContainKey("partitionKey1");
|
||||
|
||||
envelope.States[1].Key.Should().Be("testKey2");
|
||||
envelope.States[1].Value.Should().Equal(ByteString.CopyFromUtf8(JsonSerializer.Serialize("testValue2")));
|
||||
envelope.States[1].Metadata.Should().ContainKey("partitionKey2");
|
||||
envelope.States[1].Key.ShouldBe("testKey2");
|
||||
envelope.States[1].Value.ShouldBe(ByteString.CopyFromUtf8(JsonSerializer.Serialize("testValue2")));
|
||||
envelope.States[1].Metadata.ShouldContainKey("partitionKey2");
|
||||
|
||||
envelope.States[2].Key.Should().Be("testKey3");
|
||||
envelope.States[2].Value.Should().Equal(ByteString.CopyFromUtf8(JsonSerializer.Serialize("testValue3")));
|
||||
envelope.States[2].Metadata.Should().ContainKey("partitionKey3");
|
||||
envelope.States[2].Key.ShouldBe("testKey3");
|
||||
envelope.States[2].Value.ShouldBe(ByteString.CopyFromUtf8(JsonSerializer.Serialize("testValue3")));
|
||||
envelope.States[2].Metadata.ShouldContainKey("partitionKey3");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -374,11 +374,11 @@ namespace Dapr.Client.Test
|
|||
// Get Request and validate
|
||||
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.SaveStateRequest>();
|
||||
|
||||
envelope.StoreName.Should().Be("testStore");
|
||||
envelope.States.Count.Should().Be(1);
|
||||
envelope.StoreName.ShouldBe("testStore");
|
||||
envelope.States.Count.ShouldBe(1);
|
||||
var state = envelope.States[0];
|
||||
state.Key.Should().Be("test");
|
||||
state.Value.Should().Equal(ByteString.Empty);
|
||||
state.Key.ShouldBe("test");
|
||||
state.Value.ShouldBe(ByteString.Empty);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -453,34 +453,34 @@ namespace Dapr.Client.Test
|
|||
// Get Request and validate
|
||||
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.ExecuteStateTransactionRequest>();
|
||||
|
||||
envelope.StoreName.Should().Be("testStore");
|
||||
envelope.Operations.Count.Should().Be(3);
|
||||
envelope.StoreName.ShouldBe("testStore");
|
||||
envelope.Operations.Count.ShouldBe(3);
|
||||
|
||||
var req1 = envelope.Operations[0];
|
||||
req1.Request.Key.Should().Be("stateKey1");
|
||||
req1.OperationType.Should().Be(StateOperationType.Upsert.ToString().ToLower());
|
||||
req1.Request.Key.ShouldBe("stateKey1");
|
||||
req1.OperationType.ShouldBe(StateOperationType.Upsert.ToString().ToLower());
|
||||
var valueJson1 = req1.Request.Value.ToStringUtf8();
|
||||
var value1 = JsonSerializer.Deserialize<Widget>(valueJson1, client.InnerClient.JsonSerializerOptions);
|
||||
value1.Size.Should().Be(stateValue1.Size);
|
||||
value1.Color.Should().Be(stateValue1.Color);
|
||||
req1.Request.Etag.Value.Should().Be("testEtag");
|
||||
req1.Request.Metadata.Count.Should().Be(1);
|
||||
req1.Request.Metadata["a"].Should().Be("b");
|
||||
req1.Request.Options.Concurrency.Should().Be(StateConcurrency.ConcurrencyLastWrite);
|
||||
value1.Size.ShouldBe(stateValue1.Size);
|
||||
value1.Color.ShouldBe(stateValue1.Color);
|
||||
req1.Request.Etag.Value.ShouldBe("testEtag");
|
||||
req1.Request.Metadata.Count.ShouldBe(1);
|
||||
req1.Request.Metadata["a"].ShouldBe("b");
|
||||
req1.Request.Options.Concurrency.ShouldBe(StateConcurrency.ConcurrencyLastWrite);
|
||||
|
||||
var req2 = envelope.Operations[1];
|
||||
req2.Request.Key.Should().Be("stateKey2");
|
||||
req2.OperationType.Should().Be(StateOperationType.Delete.ToString().ToLower());
|
||||
req2.Request.Key.ShouldBe("stateKey2");
|
||||
req2.OperationType.ShouldBe(StateOperationType.Delete.ToString().ToLower());
|
||||
var valueJson2 = req2.Request.Value.ToStringUtf8();
|
||||
var value2 = JsonSerializer.Deserialize<int>(valueJson2, client.InnerClient.JsonSerializerOptions);
|
||||
value2.Should().Be(100);
|
||||
value2.ShouldBe(100);
|
||||
|
||||
var req3 = envelope.Operations[2];
|
||||
req3.Request.Key.Should().Be("stateKey3");
|
||||
req3.OperationType.Should().Be(StateOperationType.Upsert.ToString().ToLower());
|
||||
req3.Request.Key.ShouldBe("stateKey3");
|
||||
req3.OperationType.ShouldBe(StateOperationType.Upsert.ToString().ToLower());
|
||||
var valueJson3 = req3.Request.Value.ToStringUtf8();
|
||||
var value3 = JsonSerializer.Deserialize<string>(valueJson3, client.InnerClient.JsonSerializerOptions);
|
||||
value3.Should().Be("teststring");
|
||||
value3.ShouldBe("teststring");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -541,8 +541,8 @@ namespace Dapr.Client.Test
|
|||
request.Dismiss();
|
||||
|
||||
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.DeleteStateRequest>();
|
||||
envelope.StoreName.Should().Be("testStore");
|
||||
envelope.Key.Should().Be("test");
|
||||
envelope.StoreName.ShouldBe("testStore");
|
||||
envelope.Key.ShouldBe("test");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -590,8 +590,8 @@ namespace Dapr.Client.Test
|
|||
var state = await request.CompleteWithMessageAsync(envelope);
|
||||
|
||||
// Get response and validate
|
||||
state.Value.Size.Should().Be("small");
|
||||
state.Value.Color.Should().Be("yellow");
|
||||
state.Value.Size.ShouldBe("small");
|
||||
state.Value.Color.ShouldBe("yellow");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -605,8 +605,8 @@ namespace Dapr.Client.Test
|
|||
var envelope = MakeGetStateResponse<Widget>(null);
|
||||
var state = await request.CompleteWithMessageAsync(envelope);
|
||||
|
||||
state.Key.Should().Be("test");
|
||||
state.Value.Should().BeNull();
|
||||
state.Key.ShouldBe("test");
|
||||
state.Value.ShouldBeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -620,9 +620,9 @@ namespace Dapr.Client.Test
|
|||
var data = new Widget() { Size = "small", Color = "yellow", };
|
||||
var state = await request.CompleteWithMessageAsync(MakeGetStateResponse(data));
|
||||
|
||||
state.Key.Should().Be("test");
|
||||
state.Value.Size.Should().Be("small");
|
||||
state.Value.Color.Should().Be("yellow");
|
||||
state.Key.ShouldBe("test");
|
||||
state.Value.Size.ShouldBe("small");
|
||||
state.Value.Color.ShouldBe("yellow");
|
||||
|
||||
// Modify the state and save it
|
||||
state.Value.Color = "green";
|
||||
|
@ -637,15 +637,15 @@ namespace Dapr.Client.Test
|
|||
// Get Request and validate
|
||||
var envelope = await request2.GetRequestEnvelopeAsync<Autogenerated.SaveStateRequest>();
|
||||
|
||||
envelope.StoreName.Should().Be("testStore");
|
||||
envelope.States.Count.Should().Be(1);
|
||||
envelope.StoreName.ShouldBe("testStore");
|
||||
envelope.States.Count.ShouldBe(1);
|
||||
var requestState = envelope.States[0];
|
||||
requestState.Key.Should().Be("test");
|
||||
requestState.Key.ShouldBe("test");
|
||||
|
||||
var stateJson = requestState.Value.ToStringUtf8();
|
||||
var stateFromRequest = JsonSerializer.Deserialize<Widget>(stateJson, client.InnerClient.JsonSerializerOptions);
|
||||
stateFromRequest.Size.Should().Be("small");
|
||||
stateFromRequest.Color.Should().Be("green");
|
||||
stateFromRequest.Size.ShouldBe("small");
|
||||
stateFromRequest.Color.ShouldBe("green");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -659,9 +659,9 @@ namespace Dapr.Client.Test
|
|||
var data = new Widget() { Size = "small", Color = "yellow", };
|
||||
var state = await request.CompleteWithMessageAsync(MakeGetStateResponse(data));
|
||||
|
||||
state.Key.Should().Be("test");
|
||||
state.Value.Size.Should().Be("small");
|
||||
state.Value.Color.Should().Be("yellow");
|
||||
state.Key.ShouldBe("test");
|
||||
state.Value.Size.ShouldBe("small");
|
||||
state.Value.Color.ShouldBe("yellow");
|
||||
|
||||
state.Value.Color = "green";
|
||||
var request2 = await client.CaptureGrpcRequestAsync(async daprClient =>
|
||||
|
@ -673,8 +673,8 @@ namespace Dapr.Client.Test
|
|||
|
||||
// Get Request and validate
|
||||
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.DeleteStateRequest>();
|
||||
envelope.StoreName.Should().Be("testStore");
|
||||
envelope.Key.Should().Be("test");
|
||||
envelope.StoreName.ShouldBe("testStore");
|
||||
envelope.Key.ShouldBe("test");
|
||||
|
||||
}
|
||||
|
||||
|
@ -713,22 +713,22 @@ namespace Dapr.Client.Test
|
|||
|
||||
// Get Request and validate
|
||||
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.SaveStateRequest>();
|
||||
envelope.StoreName.Should().Be("testStore");
|
||||
envelope.States.Count.Should().Be(1);
|
||||
envelope.StoreName.ShouldBe("testStore");
|
||||
envelope.States.Count.ShouldBe(1);
|
||||
var state = envelope.States[0];
|
||||
state.Key.Should().Be("test");
|
||||
state.Metadata.Count.Should().Be(2);
|
||||
state.Metadata.Keys.Contains("key1").Should().BeTrue();
|
||||
state.Metadata.Keys.Contains("key2").Should().BeTrue();
|
||||
state.Metadata["key1"].Should().Be("value1");
|
||||
state.Metadata["key2"].Should().Be("value2");
|
||||
state.Options.Concurrency.Should().Be(expectedConcurrency);
|
||||
state.Options.Consistency.Should().Be(expectedConsistency);
|
||||
state.Key.ShouldBe("test");
|
||||
state.Metadata.Count.ShouldBe(2);
|
||||
state.Metadata.Keys.Contains("key1").ShouldBeTrue();
|
||||
state.Metadata.Keys.Contains("key2").ShouldBeTrue();
|
||||
state.Metadata["key1"].ShouldBe("value1");
|
||||
state.Metadata["key2"].ShouldBe("value2");
|
||||
state.Options.Concurrency.ShouldBe(expectedConcurrency);
|
||||
state.Options.Consistency.ShouldBe(expectedConsistency);
|
||||
|
||||
var stateJson = state.Value.ToStringUtf8();
|
||||
var stateFromRequest = JsonSerializer.Deserialize<Widget>(stateJson, client.InnerClient.JsonSerializerOptions);
|
||||
stateFromRequest.Size.Should().Be(widget.Size);
|
||||
stateFromRequest.Color.Should().Be(widget.Color);
|
||||
stateFromRequest.Size.ShouldBe(widget.Size);
|
||||
stateFromRequest.Color.ShouldBe(widget.Color);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
@ -762,22 +762,22 @@ namespace Dapr.Client.Test
|
|||
|
||||
// Get Request and validate
|
||||
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.SaveStateRequest>();
|
||||
envelope.StoreName.Should().Be("testStore");
|
||||
envelope.States.Count.Should().Be(1);
|
||||
envelope.StoreName.ShouldBe("testStore");
|
||||
envelope.States.Count.ShouldBe(1);
|
||||
var state = envelope.States[0];
|
||||
state.Etag.Value.Should().Be("Test_Etag");
|
||||
state.Metadata.Count.Should().Be(2);
|
||||
state.Metadata.Keys.Contains("key1").Should().BeTrue();
|
||||
state.Metadata.Keys.Contains("key2").Should().BeTrue();
|
||||
state.Metadata["key1"].Should().Be("value1");
|
||||
state.Metadata["key2"].Should().Be("value2");
|
||||
state.Options.Concurrency.Should().Be(expectedConcurrency);
|
||||
state.Options.Consistency.Should().Be(expectedConsistency);
|
||||
state.Etag.Value.ShouldBe("Test_Etag");
|
||||
state.Metadata.Count.ShouldBe(2);
|
||||
state.Metadata.Keys.Contains("key1").ShouldBeTrue();
|
||||
state.Metadata.Keys.Contains("key2").ShouldBeTrue();
|
||||
state.Metadata["key1"].ShouldBe("value1");
|
||||
state.Metadata["key2"].ShouldBe("value2");
|
||||
state.Options.Concurrency.ShouldBe(expectedConcurrency);
|
||||
state.Options.Consistency.ShouldBe(expectedConsistency);
|
||||
|
||||
var stateJson = state.Value.ToStringUtf8();
|
||||
var stateFromRequest = JsonSerializer.Deserialize<Widget>(stateJson, client.InnerClient.JsonSerializerOptions);
|
||||
stateFromRequest.Size.Should().Be(widget.Size);
|
||||
stateFromRequest.Color.Should().Be(widget.Color);
|
||||
stateFromRequest.Size.ShouldBe(widget.Size);
|
||||
stateFromRequest.Color.ShouldBe(widget.Color);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -827,8 +827,7 @@ namespace Dapr.Client.Test
|
|||
await client.CallStateApi<string>()
|
||||
.Build();
|
||||
|
||||
await FluentActions.Awaiting(async () => await client.DaprClient.TrySaveStateAsync("test", "test", "testValue", null))
|
||||
.Should().ThrowAsync<ArgumentException>();
|
||||
await Should.ThrowAsync<ArgumentException>(async () => await client.DaprClient.TrySaveStateAsync("test", "test", "testValue", null));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -877,8 +876,7 @@ namespace Dapr.Client.Test
|
|||
await client.CallStateApi<string>()
|
||||
.Build();
|
||||
|
||||
await FluentActions.Awaiting(async () => await client.DaprClient.TryDeleteStateAsync("test", "test", null))
|
||||
.Should().ThrowAsync<ArgumentException>();
|
||||
await Should.ThrowAsync<ArgumentException>(async () => await client.DaprClient.TryDeleteStateAsync("test", "test", null));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -943,10 +941,10 @@ namespace Dapr.Client.Test
|
|||
|
||||
// Get Request and validate
|
||||
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.DeleteStateRequest>();
|
||||
envelope.StoreName.Should().Be("testStore");
|
||||
envelope.Key.Should().Be("test");
|
||||
envelope.Options.Concurrency.Should().Be(expectedConcurrency);
|
||||
envelope.Options.Consistency.Should().Be(expectedConsistency);
|
||||
envelope.StoreName.ShouldBe("testStore");
|
||||
envelope.Key.ShouldBe("test");
|
||||
envelope.Options.Concurrency.ShouldBe(expectedConcurrency);
|
||||
envelope.Options.Consistency.ShouldBe(expectedConsistency);
|
||||
|
||||
}
|
||||
|
||||
|
@ -975,11 +973,11 @@ namespace Dapr.Client.Test
|
|||
|
||||
// Get Request and validate
|
||||
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.DeleteStateRequest>();
|
||||
envelope.StoreName.Should().Be("testStore");
|
||||
envelope.Key.Should().Be("test");
|
||||
envelope.Etag.Value.Should().Be("Test_Etag");
|
||||
envelope.Options.Concurrency.Should().Be(expectedConcurrency);
|
||||
envelope.Options.Consistency.Should().Be(expectedConsistency);
|
||||
envelope.StoreName.ShouldBe("testStore");
|
||||
envelope.Key.ShouldBe("test");
|
||||
envelope.Etag.Value.ShouldBe("Test_Etag");
|
||||
envelope.Options.Concurrency.ShouldBe(expectedConcurrency);
|
||||
envelope.Options.Consistency.ShouldBe(expectedConsistency);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -1003,10 +1001,10 @@ namespace Dapr.Client.Test
|
|||
|
||||
// Create Response & Validate
|
||||
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.DeleteBulkStateRequest>();
|
||||
envelope.StoreName.Should().Be("testStore");
|
||||
envelope.States.Count.Should().Be(1);
|
||||
envelope.States[0].Key.Should().Be(key);
|
||||
envelope.States[0].Metadata.Should().ContainKey("partitionKey");
|
||||
envelope.StoreName.ShouldBe("testStore");
|
||||
envelope.States.Count.ShouldBe(1);
|
||||
envelope.States[0].Key.ShouldBe(key);
|
||||
envelope.States[0].Metadata.ShouldContainKey("partitionKey");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -1019,9 +1017,9 @@ namespace Dapr.Client.Test
|
|||
|
||||
// Validate request.
|
||||
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.QueryStateRequest>();
|
||||
envelope.StoreName.Should().Be("testStore");
|
||||
envelope.Query.Should().Be(queryJson);
|
||||
envelope.Metadata.Should().BeEmpty();
|
||||
envelope.StoreName.ShouldBe("testStore");
|
||||
envelope.Query.ShouldBe(queryJson);
|
||||
envelope.Metadata.ShouldBeEmpty();
|
||||
|
||||
// Validate response.
|
||||
var testData = new Widget() { Color = "Green", Size = "Small" };
|
||||
|
@ -1029,11 +1027,11 @@ namespace Dapr.Client.Test
|
|||
wireResponse.Results.Add(MakeQueryStateItem("test", testData, "an etag"));
|
||||
|
||||
var response = await request.CompleteWithMessageAsync(wireResponse);
|
||||
response.Results.Count.Should().Be(1);
|
||||
response.Results[0].Key.Should().Be("test");
|
||||
response.Results[0].Data.Should().Be(testData);
|
||||
response.Results[0].ETag.Should().Be("an etag");
|
||||
response.Results[0].Error.Should().BeNullOrEmpty();
|
||||
response.Results.Count.ShouldBe(1);
|
||||
response.Results[0].Key.ShouldBe("test");
|
||||
response.Results[0].Data.ShouldBe(testData);
|
||||
response.Results[0].ETag.ShouldBe("an etag");
|
||||
response.Results[0].Error.ShouldBeNullOrEmpty();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -1046,9 +1044,9 @@ namespace Dapr.Client.Test
|
|||
|
||||
// Validate request.
|
||||
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.QueryStateRequest>();
|
||||
envelope.StoreName.Should().Be("testStore");
|
||||
envelope.Query.Should().Be(queryJson);
|
||||
envelope.Metadata.Should().BeEmpty();
|
||||
envelope.StoreName.ShouldBe("testStore");
|
||||
envelope.Query.ShouldBe(queryJson);
|
||||
envelope.Metadata.ShouldBeEmpty();
|
||||
|
||||
// Validate response, we expect to only get the first object as the 2nd will present an error.
|
||||
var testData1 = new Widget() { Color = "Green", Size = "Small" };
|
||||
|
@ -1061,21 +1059,21 @@ namespace Dapr.Client.Test
|
|||
wireResponse.Results.Add(MakeQueryStateItem("test3", testData3));
|
||||
|
||||
var ex = await Assert.ThrowsAsync<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;
|
||||
response.Results.Count.Should().Be(2);
|
||||
response.Results[0].Key.Should().Be("test1");
|
||||
response.Results[0].Data.Should().Be(testData1);
|
||||
response.Results[0].ETag.Should().BeNullOrEmpty();
|
||||
response.Results[0].Error.Should().BeNullOrEmpty();
|
||||
response.Results[1].Key.Should().Be("test3");
|
||||
response.Results[1].Data.Should().Be(testData3);
|
||||
response.Results[1].ETag.Should().BeNullOrEmpty();
|
||||
response.Results[1].Error.Should().BeNullOrEmpty();
|
||||
response.Results.Count.ShouldBe(2);
|
||||
response.Results[0].Key.ShouldBe("test1");
|
||||
response.Results[0].Data.ShouldBe(testData1);
|
||||
response.Results[0].ETag.ShouldBeNullOrEmpty();
|
||||
response.Results[0].Error.ShouldBeNullOrEmpty();
|
||||
response.Results[1].Key.ShouldBe("test3");
|
||||
response.Results[1].Data.ShouldBe(testData3);
|
||||
response.Results[1].ETag.ShouldBeNullOrEmpty();
|
||||
response.Results[1].Error.ShouldBeNullOrEmpty();
|
||||
|
||||
var failedKeys = ex.FailedKeys;
|
||||
failedKeys.Count.Should().Be(1);
|
||||
failedKeys[0].Should().Be("test2");
|
||||
failedKeys.Count.ShouldBe(1);
|
||||
failedKeys[0].ShouldBe("test2");
|
||||
}
|
||||
|
||||
private Autogenerated.GetStateResponse MakeGetStateResponse<T>(T state, string etag = null)
|
||||
|
@ -1157,20 +1155,20 @@ namespace Dapr.Client.Test
|
|||
|
||||
// Get Request and validate
|
||||
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.SaveStateRequest>();
|
||||
envelope.StoreName.Should().Be("testStore");
|
||||
envelope.States.Count.Should().Be(1);
|
||||
envelope.StoreName.ShouldBe("testStore");
|
||||
envelope.States.Count.ShouldBe(1);
|
||||
var state = envelope.States[0];
|
||||
state.Key.Should().Be("test");
|
||||
state.Metadata.Count.Should().Be(2);
|
||||
state.Metadata.Keys.Contains("key1").Should().BeTrue();
|
||||
state.Metadata.Keys.Contains("key2").Should().BeTrue();
|
||||
state.Metadata["key1"].Should().Be("value1");
|
||||
state.Metadata["key2"].Should().Be("value2");
|
||||
state.Options.Concurrency.Should().Be(expectedConcurrency);
|
||||
state.Options.Consistency.Should().Be(expectedConsistency);
|
||||
state.Key.ShouldBe("test");
|
||||
state.Metadata.Count.ShouldBe(2);
|
||||
state.Metadata.Keys.Contains("key1").ShouldBeTrue();
|
||||
state.Metadata.Keys.Contains("key2").ShouldBeTrue();
|
||||
state.Metadata["key1"].ShouldBe("value1");
|
||||
state.Metadata["key2"].ShouldBe("value2");
|
||||
state.Options.Concurrency.ShouldBe(expectedConcurrency);
|
||||
state.Options.Consistency.ShouldBe(expectedConsistency);
|
||||
|
||||
var stateBinaryData = state.Value.ToStringUtf8();
|
||||
stateBinaryData.Should().Be(data);
|
||||
stateBinaryData.ShouldBe(data);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -1189,13 +1187,13 @@ namespace Dapr.Client.Test
|
|||
|
||||
// Get Request and validate
|
||||
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.SaveStateRequest>();
|
||||
envelope.StoreName.Should().Be("testStore");
|
||||
envelope.States.Count.Should().Be(1);
|
||||
envelope.StoreName.ShouldBe("testStore");
|
||||
envelope.States.Count.ShouldBe(1);
|
||||
var state = envelope.States[0];
|
||||
state.Key.Should().Be("test");
|
||||
state.Key.ShouldBe("test");
|
||||
|
||||
var stateBinaryData = state.Value.ToStringUtf8();
|
||||
stateBinaryData.Should().Be(data);
|
||||
stateBinaryData.ShouldBe(data);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -1213,11 +1211,11 @@ namespace Dapr.Client.Test
|
|||
// Get Request and validate
|
||||
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.SaveStateRequest>();
|
||||
|
||||
envelope.StoreName.Should().Be("testStore");
|
||||
envelope.States.Count.Should().Be(1);
|
||||
envelope.StoreName.ShouldBe("testStore");
|
||||
envelope.States.Count.ShouldBe(1);
|
||||
var state = envelope.States[0];
|
||||
state.Key.Should().Be("test");
|
||||
state.Value.Should().Equal(ByteString.Empty);
|
||||
state.Key.ShouldBe("test");
|
||||
state.Value.ShouldBe(ByteString.Empty);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -1265,20 +1263,20 @@ namespace Dapr.Client.Test
|
|||
|
||||
// Get Request and validate
|
||||
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.SaveStateRequest>();
|
||||
envelope.StoreName.Should().Be("testStore");
|
||||
envelope.States.Count.Should().Be(1);
|
||||
envelope.StoreName.ShouldBe("testStore");
|
||||
envelope.States.Count.ShouldBe(1);
|
||||
var state = envelope.States[0];
|
||||
state.Etag.Value.Should().Be("Test_Etag");
|
||||
state.Metadata.Count.Should().Be(2);
|
||||
state.Metadata.Keys.Contains("key1").Should().BeTrue();
|
||||
state.Metadata.Keys.Contains("key2").Should().BeTrue();
|
||||
state.Metadata["key1"].Should().Be("value1");
|
||||
state.Metadata["key2"].Should().Be("value2");
|
||||
state.Options.Concurrency.Should().Be(expectedConcurrency);
|
||||
state.Options.Consistency.Should().Be(expectedConsistency);
|
||||
state.Etag.Value.ShouldBe("Test_Etag");
|
||||
state.Metadata.Count.ShouldBe(2);
|
||||
state.Metadata.Keys.Contains("key1").ShouldBeTrue();
|
||||
state.Metadata.Keys.Contains("key2").ShouldBeTrue();
|
||||
state.Metadata["key1"].ShouldBe("value1");
|
||||
state.Metadata["key2"].ShouldBe("value2");
|
||||
state.Options.Concurrency.ShouldBe(expectedConcurrency);
|
||||
state.Options.Consistency.ShouldBe(expectedConsistency);
|
||||
|
||||
var stateBinaryData = state.Value.ToStringUtf8();
|
||||
stateBinaryData.Should().Be(data);
|
||||
stateBinaryData.ShouldBe(data);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -1332,8 +1330,7 @@ namespace Dapr.Client.Test
|
|||
var response = client.CallStateApi<string>()
|
||||
.Build();
|
||||
|
||||
await FluentActions.Awaiting(async () => await client.DaprClient.TrySaveByteStateAsync("test", "test", stateBytes.AsMemory(), null))
|
||||
.Should().ThrowAsync<ArgumentException>();
|
||||
await Should.ThrowAsync<ArgumentException>(async () => await client.DaprClient.TrySaveByteStateAsync("test", "test", stateBytes.AsMemory(), null));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -1365,7 +1362,7 @@ namespace Dapr.Client.Test
|
|||
var state = await request.CompleteWithMessageAsync(envelope);
|
||||
|
||||
// Get response and validate
|
||||
state.ToArray().Should().BeNullOrEmpty();
|
||||
state.ToArray().ShouldBeEmpty();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
@ -1379,15 +1376,15 @@ namespace Dapr.Client.Test
|
|||
|
||||
// Get Request & Validate
|
||||
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.GetStateRequest>();
|
||||
envelope.StoreName.Should().Be("testStore");
|
||||
envelope.Key.Should().Be("test");
|
||||
envelope.Consistency.Should().Be(expectedConsistencyMode);
|
||||
envelope.StoreName.ShouldBe("testStore");
|
||||
envelope.Key.ShouldBe("test");
|
||||
envelope.Consistency.ShouldBe(expectedConsistencyMode);
|
||||
var binaryData = Encoding.ASCII.GetBytes("test data");
|
||||
// Create Response & Respond
|
||||
var state = await request.CompleteWithMessageAsync(MakeGetByteStateResponse(binaryData.AsMemory()));
|
||||
var stateStr = ByteString.CopyFrom(state.Span).ToByteArray();
|
||||
// Get response and validate
|
||||
stateStr.Should().BeEquivalentTo(binaryData);
|
||||
stateStr.ShouldBeEquivalentTo(binaryData);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -1400,18 +1397,17 @@ namespace Dapr.Client.Test
|
|||
{ "partitionKey", "mypartition" }
|
||||
};
|
||||
var request = await client.CaptureGrpcRequestAsync(async daprClient => await daprClient.GetByteStateAndETagAsync("testStore", "test", metadata: metadata));
|
||||
|
||||
// Get Request & Validate
|
||||
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.GetStateRequest>();
|
||||
envelope.StoreName.Should().Be("testStore");
|
||||
envelope.Key.Should().Be("test");
|
||||
envelope.Metadata.Should().BeEquivalentTo(metadata);
|
||||
envelope.StoreName.ShouldBe("testStore");
|
||||
envelope.Key.ShouldBe("test");
|
||||
envelope.Metadata.ShouldBe(metadata);
|
||||
var binaryData = Encoding.ASCII.GetBytes("test data");
|
||||
// Create Response & Respond
|
||||
var (state, etag) = await request.CompleteWithMessageAsync((MakeGetByteStateResponse(binaryData.AsMemory())));
|
||||
var stateStr = ByteString.CopyFrom(state.Span).ToByteArray();
|
||||
// Get response and validate
|
||||
stateStr.Should().BeEquivalentTo(binaryData);
|
||||
stateStr.ShouldBe(binaryData);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -1442,8 +1438,8 @@ namespace Dapr.Client.Test
|
|||
var (state, etag) = await request.CompleteWithMessageAsync(envelope);
|
||||
var stateStr = ByteString.CopyFrom(state.Span).ToByteArray();
|
||||
// Get response and validate
|
||||
stateStr.Should().BeEquivalentTo(binaryData);
|
||||
etag.Should().Be("Test_Etag");
|
||||
stateStr.ShouldBeEquivalentTo(binaryData);
|
||||
etag.ShouldBe("Test_Etag");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
using System.Threading.Tasks;
|
||||
using Autogenerated = Dapr.Client.Autogen.Grpc.v1;
|
||||
using Xunit;
|
||||
using FluentAssertions;
|
||||
using Shouldly;
|
||||
using System;
|
||||
|
||||
namespace Dapr.Client.Test
|
||||
|
@ -37,10 +37,10 @@ namespace Dapr.Client.Test
|
|||
|
||||
// Get Request and validate
|
||||
var envelope = await request.GetRequestEnvelopeAsync<Autogenerated.TryLockRequest>();
|
||||
envelope.StoreName.Should().Be("redis");
|
||||
envelope.ResourceId.Should().Be("resourceId");
|
||||
envelope.LockOwner.Should().Be("owner1");
|
||||
envelope.ExpiryInSeconds.Should().Be(1000);
|
||||
envelope.StoreName.ShouldBe("redis");
|
||||
envelope.ResourceId.ShouldBe("resourceId");
|
||||
envelope.LockOwner.ShouldBe("owner1");
|
||||
envelope.ExpiryInSeconds.ShouldBe(1000);
|
||||
|
||||
// Get response and validate
|
||||
var invokeResponse = new Autogenerated.TryLockResponse{
|
||||
|
@ -56,16 +56,16 @@ namespace Dapr.Client.Test
|
|||
return await daprClient.Unlock(storeName, resourceId, lockOwner);
|
||||
});
|
||||
var unlockEnvelope = await unlockRequest.GetRequestEnvelopeAsync<Autogenerated.UnlockRequest>();
|
||||
unlockEnvelope.StoreName.Should().Be("redis");
|
||||
unlockEnvelope.ResourceId.Should().Be("resourceId");
|
||||
unlockEnvelope.LockOwner.Should().Be("owner1");
|
||||
unlockEnvelope.StoreName.ShouldBe("redis");
|
||||
unlockEnvelope.ResourceId.ShouldBe("resourceId");
|
||||
unlockEnvelope.LockOwner.ShouldBe("owner1");
|
||||
|
||||
var invokeUnlockResponse = new Autogenerated.UnlockResponse{
|
||||
Status = Autogenerated.UnlockResponse.Types.Status.LockDoesNotExist
|
||||
};
|
||||
|
||||
var domainUnlockResponse = await unlockRequest.CompleteWithMessageAsync(invokeUnlockResponse);
|
||||
domainUnlockResponse.status.Should().Be(LockStatus.LockDoesNotExist);
|
||||
domainUnlockResponse.status.ShouldBe(LockStatus.LockDoesNotExist);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
namespace Dapr.Client.Test
|
||||
{
|
||||
using System.Text.Json;
|
||||
using FluentAssertions;
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
|
||||
public class TypeConvertersTest
|
||||
|
@ -32,9 +32,9 @@ namespace Dapr.Client.Test
|
|||
var any = TypeConverters.ToJsonAny(response, options);
|
||||
var type = TypeConverters.FromJsonAny<Response>(any, options);
|
||||
|
||||
type.Should().BeEquivalentTo(response);
|
||||
any.TypeUrl.Should().Be(string.Empty);
|
||||
type.Name.Should().Be("test");
|
||||
type.ShouldBeEquivalentTo(response);
|
||||
any.TypeUrl.ShouldBe(string.Empty);
|
||||
type.Name.ShouldBe("test");
|
||||
}
|
||||
|
||||
private class Response
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="FluentAssertions"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" />
|
||||
<PackageReference Include="xunit" />
|
||||
|
|
|
@ -97,7 +97,7 @@ namespace Dapr.E2E.Test
|
|||
/// <see cref="ISerializationActor"/>. (it's defined in the base of it.)
|
||||
/// That why <see cref="ISerializationActor.AnotherMethod(DateTime)"/> was created,
|
||||
/// so there are now more then one method.
|
||||
/// </remark>
|
||||
/// </remarks>
|
||||
[Fact]
|
||||
public async Task ActorCanSupportCustomSerializerAndCallMoreThenOneDefinedMethod()
|
||||
{
|
||||
|
|
|
@ -17,7 +17,7 @@ namespace Dapr.E2E.Test
|
|||
using System.Threading.Tasks;
|
||||
using Dapr.Actors;
|
||||
using Dapr.E2E.Test.Actors.WeaklyTypedTesting;
|
||||
using FluentAssertions;
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
|
||||
public partial class E2ETests : IAsyncLifetime
|
||||
|
@ -34,7 +34,7 @@ namespace Dapr.E2E.Test
|
|||
|
||||
var result = await proxy.InvokeMethodAsync<ResponseBase>(nameof(IWeaklyTypedTestingActor.GetPolymorphicResponse));
|
||||
|
||||
result.Should().BeOfType<DerivedResponse>().Which.DerivedProperty.Should().NotBeNullOrWhiteSpace();
|
||||
result.ShouldBeOfType<DerivedResponse>().DerivedProperty.ShouldNotBeNullOrWhiteSpace();
|
||||
}
|
||||
#else
|
||||
[Fact]
|
||||
|
@ -48,7 +48,7 @@ namespace Dapr.E2E.Test
|
|||
|
||||
var result = await proxy.InvokeMethodAsync<DerivedResponse>(nameof(IWeaklyTypedTestingActor.GetPolymorphicResponse));
|
||||
|
||||
result.Should().BeOfType<DerivedResponse>().Which.DerivedProperty.Should().NotBeNullOrWhiteSpace();
|
||||
result.ShouldBeOfType<DerivedResponse>().DerivedProperty.ShouldNotBeNullOrWhiteSpace();
|
||||
}
|
||||
#endif
|
||||
[Fact]
|
||||
|
@ -62,7 +62,7 @@ namespace Dapr.E2E.Test
|
|||
|
||||
var result = await proxy.InvokeMethodAsync<ResponseBase>(nameof(IWeaklyTypedTestingActor.GetNullResponse));
|
||||
|
||||
result.Should().BeNull();
|
||||
result.ShouldBeNull();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FluentAssertions" />
|
||||
<PackageReference Include="Google.Protobuf" />
|
||||
<PackageReference Include="Grpc.Net.ClientFactory" />
|
||||
<PackageReference Include="Grpc.Tools" PrivateAssets="All" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" />
|
||||
<PackageReference Include="Shouldly"/>
|
||||
<PackageReference Include="xunit" />
|
||||
<PackageReference Include="xunit.runner.visualstudio">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
|
|
|
@ -45,7 +45,7 @@ namespace Dapr.E2E.Test
|
|||
{
|
||||
var (appPort, httpPort, grpcPort, metricsPort) = GetFreePorts();
|
||||
|
||||
var componentsPath = Combine(".", "..", "..", "..", "..", "..", "test", "Dapr.E2E.Test", "components");
|
||||
var resourcesPath = Combine(".", "..", "..", "..", "..", "..", "test", "Dapr.E2E.Test", "components");
|
||||
var configPath = Combine(".", "..", "..", "..", "..", "..", "test", "Dapr.E2E.Test", "configuration", "featureconfig.yaml");
|
||||
var arguments = new List<string>()
|
||||
{
|
||||
|
@ -55,11 +55,10 @@ namespace Dapr.E2E.Test
|
|||
"--dapr-http-port", httpPort.ToString(CultureInfo.InvariantCulture),
|
||||
"--dapr-grpc-port", grpcPort.ToString(CultureInfo.InvariantCulture),
|
||||
"--metrics-port", metricsPort.ToString(CultureInfo.InvariantCulture),
|
||||
"--components-path", componentsPath,
|
||||
"--resources-path", resourcesPath,
|
||||
"--config", configPath,
|
||||
"--log-level", "debug",
|
||||
"--dapr-http-max-request-size", "32",
|
||||
|
||||
"--max-body-size", "8Mi"
|
||||
};
|
||||
|
||||
if (configuration.UseAppPort)
|
||||
|
|
|
@ -5,9 +5,9 @@
|
|||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="FluentAssertions" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" />
|
||||
<PackageReference Include="Moq" />
|
||||
<PackageReference Include="Shouldly" />
|
||||
<PackageReference Include="xunit"/>
|
||||
<PackageReference Include="xunit.runner.visualstudio">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
|
|
|
@ -16,11 +16,10 @@ using System.Collections.Generic;
|
|||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using Dapr.Client;
|
||||
using Dapr;
|
||||
using FluentAssertions;
|
||||
using Grpc.Net.Client;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Moq;
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
using Autogenerated = Dapr.Client.Autogen.Grpc.v1;
|
||||
|
||||
|
@ -163,7 +162,7 @@ namespace Dapr.Extensions.Configuration.Test
|
|||
.AddDaprSecretStore(storeName, secretDescriptors, daprClient.Object)
|
||||
.Build();
|
||||
|
||||
config["secretName"].Should().Be("secret");
|
||||
config["secretName"].ShouldBe("secret");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -195,8 +194,8 @@ namespace Dapr.Extensions.Configuration.Test
|
|||
.AddDaprSecretStore(storeName, secretDescriptors, daprClient.Object)
|
||||
.Build();
|
||||
|
||||
config[firstSecretKey].Should().Be(firstSecretValue);
|
||||
config[secondSecretKey].Should().Be(secondSecretValue);
|
||||
config[firstSecretKey].ShouldBe(firstSecretValue);
|
||||
config[secondSecretKey].ShouldBe(secondSecretValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -224,8 +223,8 @@ namespace Dapr.Extensions.Configuration.Test
|
|||
.AddDaprSecretStore(storeName, secretDescriptors, daprClient.Object)
|
||||
.Build();
|
||||
|
||||
config[firstSecretKey].Should().Be(firstSecretValue);
|
||||
config[secondSecretKey].Should().Be(secondSecretValue);
|
||||
config[firstSecretKey].ShouldBe(firstSecretValue);
|
||||
config[secondSecretKey].ShouldBe(secondSecretValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -252,7 +251,7 @@ namespace Dapr.Extensions.Configuration.Test
|
|||
.AddDaprSecretStore(storeName, secretDescriptors, daprClient.Object)
|
||||
.Build();
|
||||
|
||||
config[secretName].Should().Be(secretValue);
|
||||
config[secretName].ShouldBe(secretValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -282,7 +281,7 @@ namespace Dapr.Extensions.Configuration.Test
|
|||
.AddDaprSecretStore(storeName, secretDescriptors, daprClient)
|
||||
.Build();
|
||||
|
||||
config[secretName].Should().BeNull();
|
||||
config[secretName].ShouldBeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -415,7 +414,7 @@ namespace Dapr.Extensions.Configuration.Test
|
|||
.AddDaprSecretStore("store", daprClient)
|
||||
.Build();
|
||||
|
||||
config["secretName"].Should().Be("secret");
|
||||
config["secretName"].ShouldBe("secret");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -442,8 +441,8 @@ namespace Dapr.Extensions.Configuration.Test
|
|||
.AddDaprSecretStore("store", daprClient)
|
||||
.Build();
|
||||
|
||||
config["first_secret"].Should().Be("secret1");
|
||||
config["second_secret"].Should().Be("secret2");
|
||||
config["first_secret"].ShouldBe("secret1");
|
||||
config["second_secret"].ShouldBe("secret2");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -468,7 +467,7 @@ namespace Dapr.Extensions.Configuration.Test
|
|||
.AddDaprSecretStore("store", new DaprSecretDescriptor[] { new DaprSecretDescriptor("secretName__value") }, daprClient)
|
||||
.Build();
|
||||
|
||||
config["secretName:value"].Should().Be("secret");
|
||||
config["secretName:value"].ShouldBe("secret");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -494,7 +493,7 @@ namespace Dapr.Extensions.Configuration.Test
|
|||
.AddDaprSecretStore("store", daprClient)
|
||||
.Build();
|
||||
|
||||
config["first_secret:value"].Should().Be("secret1");
|
||||
config["first_secret:value"].ShouldBe("secret1");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -525,7 +524,7 @@ namespace Dapr.Extensions.Configuration.Test
|
|||
})
|
||||
.Build();
|
||||
|
||||
config["secretName__value"].Should().Be("secret");
|
||||
config["secretName__value"].ShouldBe("secret");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -556,7 +555,7 @@ namespace Dapr.Extensions.Configuration.Test
|
|||
})
|
||||
.Build();
|
||||
|
||||
config["first_secret__value"].Should().Be("secret1");
|
||||
config["first_secret__value"].ShouldBe("secret1");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -590,7 +589,7 @@ namespace Dapr.Extensions.Configuration.Test
|
|||
})
|
||||
.Build();
|
||||
|
||||
config["secretName:value"].Should().Be("secret");
|
||||
config["secretName:value"].ShouldBe("secret");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -624,7 +623,7 @@ namespace Dapr.Extensions.Configuration.Test
|
|||
})
|
||||
.Build();
|
||||
|
||||
config["secretName--value"].Should().Be("secret");
|
||||
config["secretName--value"].ShouldBe("secret");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -658,7 +657,7 @@ namespace Dapr.Extensions.Configuration.Test
|
|||
})
|
||||
.Build();
|
||||
|
||||
config["secretName--value"].Should().Be("secret");
|
||||
config["secretName--value"].ShouldBe("secret");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -714,8 +713,8 @@ namespace Dapr.Extensions.Configuration.Test
|
|||
})
|
||||
.Build();
|
||||
|
||||
config["secretName:value"].Should().Be("secret");
|
||||
config["otherSecretName:value"].Should().Be("secret");
|
||||
config["secretName:value"].ShouldBe("secret");
|
||||
config["otherSecretName:value"].ShouldBe("secret");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -743,7 +742,7 @@ namespace Dapr.Extensions.Configuration.Test
|
|||
.AddDaprSecretStore("store", daprClient, new[] { "--" })
|
||||
.Build();
|
||||
|
||||
config["first_secret:value"].Should().Be("secret1");
|
||||
config["first_secret:value"].ShouldBe("secret1");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -772,8 +771,8 @@ namespace Dapr.Extensions.Configuration.Test
|
|||
.AddDaprSecretStore("store", daprClient, new[] { "--", "≡" })
|
||||
.Build();
|
||||
|
||||
config["first_secret:value"].Should().Be("secret1");
|
||||
config["second_secret:value"].Should().Be("secret2");
|
||||
config["first_secret:value"].ShouldBe("secret1");
|
||||
config["second_secret:value"].ShouldBe("secret2");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -803,9 +802,9 @@ namespace Dapr.Extensions.Configuration.Test
|
|||
.AddDaprSecretStore("store", daprClient, new[] { "--", "≡" })
|
||||
.Build();
|
||||
|
||||
config["first_secret:value"].Should().Be("secret1");
|
||||
config["second_secret:value"].Should().Be("secret2");
|
||||
config["third_secret:value"].Should().Be("secret3");
|
||||
config["first_secret:value"].ShouldBe("secret1");
|
||||
config["second_secret:value"].ShouldBe("secret2");
|
||||
config["third_secret:value"].ShouldBe("secret3");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -840,7 +839,7 @@ namespace Dapr.Extensions.Configuration.Test
|
|||
})
|
||||
.Build();
|
||||
|
||||
config["secretName--value"].Should().Be("secret");
|
||||
config["secretName--value"].ShouldBe("secret");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -874,7 +873,7 @@ namespace Dapr.Extensions.Configuration.Test
|
|||
})
|
||||
.Build();
|
||||
|
||||
config["first_secret--value"].Should().Be("secret1");
|
||||
config["first_secret--value"].ShouldBe("secret1");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using Dapr.Client.Autogen.Grpc.v1;
|
||||
using Dapr.Jobs.Models;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
@ -167,6 +168,21 @@ public sealed class DaprJobsGrpcClientTests
|
|||
});
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShouldDeserialize_EveryExpression()
|
||||
{
|
||||
const string scheduleText = "@every 1m";
|
||||
var response = new GetJobResponse { Job = new Job { Name = "test", Schedule = scheduleText } };
|
||||
var schedule = DaprJobSchedule.FromExpression(scheduleText);
|
||||
|
||||
var jobDetails = DaprJobsGrpcClient.DeserializeJobResponse(response);
|
||||
Assert.Null(jobDetails.Payload);
|
||||
Assert.Equal(0, jobDetails.RepeatCount);
|
||||
Assert.Null(jobDetails.Ttl);
|
||||
Assert.Null(jobDetails.DueTime);
|
||||
Assert.Equal(jobDetails.Schedule.ExpressionValue, schedule.ExpressionValue);
|
||||
}
|
||||
|
||||
private sealed record TestPayload(string Name, string Color);
|
||||
}
|
||||
|
|
|
@ -14,6 +14,9 @@
|
|||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
|
@ -25,8 +28,11 @@ using Dapr.Jobs.Models.Responses;
|
|||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.AspNetCore.TestHost;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Dapr.Jobs.Test.Extensions;
|
||||
|
@ -40,15 +46,7 @@ public class EndpointRouteBuilderExtensionsTest
|
|||
var client = server.CreateClient();
|
||||
|
||||
var serializedPayload = JsonSerializer.Serialize(new SamplePayload("Dapr", 789));
|
||||
var serializedPayloadBytes = Encoding.UTF8.GetBytes(serializedPayload);
|
||||
var jobDetails = new DaprJobDetails(new DaprJobSchedule("0 0 * * *"))
|
||||
{
|
||||
RepeatCount = 5,
|
||||
DueTime = DateTimeOffset.UtcNow,
|
||||
Ttl = DateTimeOffset.UtcNow.AddHours(1),
|
||||
Payload = serializedPayloadBytes
|
||||
};
|
||||
var content = new StringContent(JsonSerializer.Serialize(jobDetails), Encoding.UTF8, "application/json");
|
||||
var content = new StringContent(serializedPayload, Encoding.UTF8, "application/json");
|
||||
|
||||
const string jobName = "testJob";
|
||||
var response = await client.PostAsync($"/job/{jobName}", content);
|
||||
|
@ -68,15 +66,7 @@ public class EndpointRouteBuilderExtensionsTest
|
|||
var client = server.CreateClient();
|
||||
|
||||
var serializedPayload = JsonSerializer.Serialize(new SamplePayload("Dapr", 789));
|
||||
var serializedPayloadBytes = Encoding.UTF8.GetBytes(serializedPayload);
|
||||
var jobDetails = new DaprJobDetails(new DaprJobSchedule("0 0 * * *"))
|
||||
{
|
||||
RepeatCount = 5,
|
||||
DueTime = DateTimeOffset.UtcNow,
|
||||
Ttl = DateTimeOffset.UtcNow.AddHours(1),
|
||||
Payload = serializedPayloadBytes
|
||||
};
|
||||
var content = new StringContent(JsonSerializer.Serialize(jobDetails), Encoding.UTF8, "application/json");
|
||||
var content = new StringContent(serializedPayload, Encoding.UTF8, "application/json");
|
||||
|
||||
const string jobName = "testJob";
|
||||
var response = await client.PostAsync($"/job/{jobName}", content);
|
||||
|
@ -88,32 +78,117 @@ public class EndpointRouteBuilderExtensionsTest
|
|||
Assert.Equal(jobName, validator.JobName);
|
||||
Assert.Equal(serializedPayload, validator.SerializedPayload);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task MapDaprScheduledJobHandler_InvalidPayload()
|
||||
public async Task MapDaprScheduledJobHandler_HandlesTimeoutCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
var server = CreateTestServer();
|
||||
var client = server.CreateClient();
|
||||
var timeout = TimeSpan.FromSeconds(5);
|
||||
const string testJobName = "testJob";
|
||||
var testJobPayload = Encoding.UTF8.GetBytes("testPayload");
|
||||
|
||||
var content = new StringContent("", Encoding.UTF8, "application/json");
|
||||
var builder = new WebHostBuilder()
|
||||
.ConfigureServices(services =>
|
||||
{
|
||||
services.AddLogging();
|
||||
services.AddRouting();
|
||||
})
|
||||
.Configure(app =>
|
||||
{
|
||||
app.UseRouting();
|
||||
app.UseEndpoints(endpoints =>
|
||||
{
|
||||
endpoints.MapDaprScheduledJobHandler(async (
|
||||
string jobName,
|
||||
ReadOnlyMemory<byte> jobPayload,
|
||||
ILogger? logger,
|
||||
CancellationToken cancellationToken) =>
|
||||
{
|
||||
logger?.LogInformation("Received trigger invocation for job '{jobName}'", jobName);
|
||||
|
||||
// Act
|
||||
const string jobName = "testJob";
|
||||
var response = await client.PostAsync($"/job/{jobName}", content);
|
||||
var deserializedPayload = Encoding.UTF8.GetString(jobPayload.Span);
|
||||
logger?.LogInformation(
|
||||
"Received invocation for the job '{jobName}' with payload '{deserializedPayload}'",
|
||||
jobName, deserializedPayload);
|
||||
await Task.Delay(TimeSpan.FromSeconds(1),
|
||||
cancellationToken); //Less than the timeout, so this should work without throwing
|
||||
|
||||
var validator = server.Services.GetRequiredService<Validator>();
|
||||
Assert.Equal(jobName, validator.JobName);
|
||||
Assert.Null(validator.SerializedPayload);
|
||||
return Task.CompletedTask;
|
||||
}, timeout);
|
||||
});
|
||||
});
|
||||
|
||||
var testServer = new TestServer(builder);
|
||||
var client = testServer.CreateClient();
|
||||
|
||||
var requestContent = new ByteArrayContent(testJobPayload);
|
||||
var request = new HttpRequestMessage(HttpMethod.Post, $"/job/{testJobName}")
|
||||
{
|
||||
Content = requestContent
|
||||
};
|
||||
|
||||
// Act & Assert
|
||||
var response = await client.SendAsync(request);
|
||||
Assert.True(response.IsSuccessStatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task MapDaprScheduledJobHandler_AppliesTimeoutCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
var timeout = TimeSpan.FromSeconds(1);
|
||||
const string testJobName = "testJob";
|
||||
var testJobPayload = Encoding.UTF8.GetBytes("testPayload");
|
||||
|
||||
var builder = new WebHostBuilder()
|
||||
.ConfigureServices(services =>
|
||||
{
|
||||
services.AddLogging();
|
||||
services.AddRouting();
|
||||
})
|
||||
.Configure(app =>
|
||||
{
|
||||
app.UseRouting();
|
||||
app.UseEndpoints(endpoints =>
|
||||
{
|
||||
endpoints.MapDaprScheduledJobHandler(async (
|
||||
string jobName,
|
||||
ReadOnlyMemory<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);
|
||||
|
||||
public sealed class Validator
|
||||
{
|
||||
public string? JobName { get; set; }
|
||||
|
||||
public string? SerializedPayload { get; set; }
|
||||
}
|
||||
|
||||
|
@ -130,15 +205,10 @@ public class EndpointRouteBuilderExtensionsTest
|
|||
app.UseRouting();
|
||||
app.UseEndpoints(endpoints =>
|
||||
{
|
||||
endpoints.MapDaprScheduledJobHandler(async (string? jobName, DaprJobDetails? jobDetails, Validator validator, CancellationToken cancellationToken) =>
|
||||
endpoints.MapDaprScheduledJobHandler(async (string jobName, ReadOnlyMemory<byte> jobPayload, Validator validator, CancellationToken cancellationToken) =>
|
||||
{
|
||||
if (jobName is not null)
|
||||
validator.JobName = jobName;
|
||||
if (jobDetails?.Payload is not null)
|
||||
{
|
||||
var payloadString = Encoding.UTF8.GetString(jobDetails.Payload);
|
||||
validator.SerializedPayload = payloadString;
|
||||
}
|
||||
validator.JobName = jobName;
|
||||
validator.SerializedPayload = Encoding.UTF8.GetString(jobPayload.Span);
|
||||
await Task.CompletedTask;
|
||||
});
|
||||
});
|
||||
|
@ -160,15 +230,12 @@ public class EndpointRouteBuilderExtensionsTest
|
|||
app.UseRouting();
|
||||
app.UseEndpoints(endpoints =>
|
||||
{
|
||||
endpoints.MapDaprScheduledJobHandler(async (string? jobName, Validator validator, DaprJobDetails? jobDetails) =>
|
||||
endpoints.MapDaprScheduledJobHandler(async (string jobName, Validator validator, ReadOnlyMemory<byte> payload) =>
|
||||
{
|
||||
if (jobName is not null)
|
||||
validator.JobName = jobName;
|
||||
if (jobDetails?.Payload is not null)
|
||||
{
|
||||
var payloadString = Encoding.UTF8.GetString(jobDetails.Payload);
|
||||
validator.SerializedPayload = payloadString;
|
||||
}
|
||||
validator.JobName = jobName;
|
||||
|
||||
var payloadString = Encoding.UTF8.GetString(payload.Span);
|
||||
validator.SerializedPayload = payloadString;
|
||||
await Task.CompletedTask;
|
||||
});
|
||||
});
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue