dotnet-sdk/test/Dapr.AspNetCore.Test/StateEntryModelBinderTest.cs

197 lines
7.4 KiB
C#

// ------------------------------------------------------------------------
// Copyright 2021 The Dapr Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ------------------------------------------------------------------------
namespace Dapr.AspNetCore.Test
{
using System;
using System.Text.Json;
using System.Threading.Tasks;
using Dapr.Client;
using Dapr.Client.Autogen.Grpc.v1;
using Shouldly;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using Microsoft.Extensions.DependencyInjection;
using Xunit;
public class StateEntryModelBinderTest
{
[Fact]
public async Task BindAsync_WithoutMatchingRouteValue_ReportsError()
{
await using var client = TestClient.CreateForDaprClient();
var binder = new StateEntryModelBinder("testStore", "test", isStateEntry: false, typeof(Widget));
var context = CreateContext(CreateServices(client.InnerClient));
await binder.BindModelAsync(context);
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
}
[Fact]
public async Task BindAsync_CanBindValue()
{
await using var client = TestClient.CreateForDaprClient();
var binder = new StateEntryModelBinder("testStore", "id", isStateEntry: false, typeof(Widget));
// Configure Client
var context = CreateContext(CreateServices(client.InnerClient));
context.HttpContext.Request.RouteValues["id"] = "test";
var request = await client.CaptureGrpcRequestAsync(async _ =>
{
await binder.BindModelAsync(context);
});
// Create Response & Respond
var state = new Widget() { Size = "small", Color = "yellow", };
await SendResponseWithState(state, request);
// Get response and validate
context.Result.IsModelSet.ShouldBeTrue();
((Widget)context.Result.Model).Size.ShouldBe("small");
((Widget)context.Result.Model).Color.ShouldBe("yellow");
context.ValidationState.Count.ShouldBe(1);
context.ValidationState[context.Result.Model].SuppressValidation.ShouldBeTrue();
}
[Fact]
public async Task BindAsync_CanBindStateEntry()
{
await using var client = TestClient.CreateForDaprClient();
var binder = new StateEntryModelBinder("testStore", "id", isStateEntry: true, typeof(Widget));
// Configure Client
var context = CreateContext(CreateServices(client.InnerClient));
context.HttpContext.Request.RouteValues["id"] = "test";
var request = await client.CaptureGrpcRequestAsync(async _ =>
{
await binder.BindModelAsync(context);
});
// Create Response & Respond
var state = new Widget() { Size = "small", Color = "yellow", };
await SendResponseWithState(state, request);
// Get response and validate
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.ShouldBe(1);
context.ValidationState[context.Result.Model].SuppressValidation.ShouldBeTrue();
}
[Fact]
public async Task BindAsync_ReturnsNullForNonExistentStateEntry()
{
await using var client = TestClient.CreateForDaprClient();
var binder = new StateEntryModelBinder("testStore", "id", isStateEntry: false, typeof(Widget));
// Configure Client
var context = CreateContext(CreateServices(client.InnerClient));
context.HttpContext.Request.RouteValues["id"] = "test";
var request = await client.CaptureGrpcRequestAsync(async _ =>
{
await binder.BindModelAsync(context);
});
await SendResponseWithState<string>(null, request);
context.ModelState.IsValid.ShouldBeTrue();
context.Result.IsModelSet.ShouldBeFalse();
context.Result.ShouldBe(ModelBindingResult.Failed());
}
[Fact]
public async Task BindAsync_WithStateEntry_ForNonExistentStateEntry()
{
await using var client = TestClient.CreateForDaprClient();
var binder = new StateEntryModelBinder("testStore", "id", isStateEntry: true, typeof(Widget));
// Configure Client
var context = CreateContext(CreateServices(client.InnerClient));
context.HttpContext.Request.RouteValues["id"] = "test";
var request = await client.CaptureGrpcRequestAsync(async _ =>
{
await binder.BindModelAsync(context);
});
await SendResponseWithState<string>(null, request);
context.ModelState.IsValid.ShouldBeTrue();
context.Result.IsModelSet.ShouldBeTrue();
((StateEntry<Widget>)context.Result.Model).Value.ShouldBeNull();
}
private static ModelBindingContext CreateContext(IServiceProvider services)
{
return new DefaultModelBindingContext()
{
ActionContext = new ActionContext()
{
HttpContext = new DefaultHttpContext()
{
RequestServices = services,
},
},
ModelState = new ModelStateDictionary(),
ModelName = "testParameter",
ValidationState = new ValidationStateDictionary(),
};
}
private async Task SendResponseWithState<T>(T state, TestClient<DaprClient>.TestGrpcRequest request)
{
var stateData = TypeConverters.ToJsonByteString(state, new JsonSerializerOptions(JsonSerializerDefaults.Web));
var stateResponse = new GetStateResponse()
{
Data = stateData,
Etag = "test",
};
await request.CompleteWithMessageAsync(stateResponse);
}
private static IServiceProvider CreateServices(DaprClient daprClient)
{
var services = new ServiceCollection();
services.AddSingleton(daprClient);
return services.BuildServiceProvider();
}
private class Widget
{
public string Size { get; set; }
public string Color { get; set; }
}
}
}