aspnetcore/test/Microsoft.AspNet.Mvc.Core.Test/DefaultActionDiscoveryConve...

326 lines
10 KiB
C#

// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
#if ASPNET50
using System;
using System.Collections.Generic;
using System.ComponentModel.Design;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Mvc.Routing;
using Microsoft.AspNet.Routing;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.DependencyInjection.NestedProviders;
using Moq;
using Xunit;
namespace Microsoft.AspNet.Mvc
{
public class DefaultActionDiscoveryConventionsActionSelectionTests
{
[Theory]
[InlineData("GET")]
[InlineData("POST")]
public async Task ActionSelection_IndexSelectedByDefaultInAbsenceOfVerbOnlyMethod(string verb)
{
// Arrange
var routeContext = new RouteContext(GetHttpContext(verb));
routeContext.RouteData.Values = new Dictionary<string, object>
{
{ "controller", "RpcOnly" }
};
// Act
var result = await InvokeActionSelector(routeContext);
// Assert
Assert.Equal("Index", result.Name);
}
[Theory]
[InlineData("GET")]
[InlineData("POST")]
public async Task ActionSelection_PrefersVerbOnlyMethodOverIndex(string verb)
{
// Arrange
var routeContext = new RouteContext(GetHttpContext(verb));
routeContext.RouteData.Values = new Dictionary<string, object>
{
{ "controller", "MixedRpcAndRest" }
};
// Act
var result = await InvokeActionSelector(routeContext);
// Assert
Assert.Equal(verb, result.Name, StringComparer.OrdinalIgnoreCase);
}
[Theory]
[InlineData("PUT")]
[InlineData("DELETE")]
[InlineData("PATCH")]
public async Task ActionSelection_IndexNotSelectedByDefaultExceptGetAndPostVerbs(string verb)
{
// Arrange
var routeContext = new RouteContext(GetHttpContext(verb));
routeContext.RouteData.Values = new Dictionary<string, object>
{
{ "controller", "RpcOnly" }
};
// Act
var result = await InvokeActionSelector(routeContext);
// Assert
Assert.Equal(null, result);
}
[Theory]
[InlineData("HEAD")]
[InlineData("OPTIONS")]
public async Task ActionSelection_NoConventionBasedRoutingForHeadAndOptions(string verb)
{
// Arrange
var routeContext = new RouteContext(GetHttpContext(verb));
routeContext.RouteData.Values = new Dictionary<string, object>
{
{ "controller", "MixedRpcAndRest" },
};
// Act
var result = await InvokeActionSelector(routeContext);
// Assert
Assert.Equal(null, result);
}
[Theory]
[InlineData("HEAD")]
[InlineData("OPTIONS")]
public async Task ActionSelection_ActionNameBasedRoutingForHeadAndOptions(string verb)
{
// Arrange
var routeContext = new RouteContext(GetHttpContext(verb));
routeContext.RouteData.Values = new Dictionary<string, object>
{
{ "controller", "MixedRpcAndRest" },
{ "action", verb },
};
// Act
var result = await InvokeActionSelector(routeContext);
// Assert
Assert.Equal(verb, result.Name, StringComparer.OrdinalIgnoreCase);
}
[Fact]
public async Task ActionSelection_ChangeDefaultConventionPicksCustomMethodForPost_DefaultMethodIsSelectedForGet()
{
// Arrange
var routeContext = new RouteContext(GetHttpContext("GET"));
routeContext.RouteData.Values = new Dictionary<string, object>
{
{ "controller", "RpcOnly" }
};
// Act
var result = await InvokeActionSelector(routeContext, new CustomActionConvention());
// Assert
Assert.Equal("INDEX", result.Name, StringComparer.OrdinalIgnoreCase);
}
[Fact]
public async Task ActionSelection_ChangeDefaultConventionPicksCustomMethodForPost_CutomMethodIsSelected()
{
// Arrange
var routeContext = new RouteContext(GetHttpContext("POST"));
routeContext.RouteData.Values = new Dictionary<string, object>
{
{ "controller", "RpcOnly" }
};
// Act
var result = await InvokeActionSelector(routeContext, new CustomActionConvention());
// Assert
Assert.Equal("PostSomething", result.Name);
}
private async Task<ActionDescriptor> InvokeActionSelector(RouteContext context)
{
var controllerTypeInfos = typeof(DefaultActionDiscoveryConventionsActionSelectionTests)
.GetNestedTypes(BindingFlags.NonPublic)
.Select(ct => ct.GetTypeInfo())
.ToArray();
var conventions = new StaticActionDiscoveryConventions(controllerTypeInfos);
return await InvokeActionSelector(context, conventions);
}
private async Task<ActionDescriptor> InvokeActionSelector(RouteContext context,
DefaultActionDiscoveryConventions actionDiscoveryConventions)
{
var actionDescriptorProvider = GetActionDescriptorProvider(actionDiscoveryConventions);
var descriptorProvider =
new NestedProviderManager<ActionDescriptorProviderContext>(new[] { actionDescriptorProvider });
var serviceContainer = new ServiceContainer();
serviceContainer.AddService(typeof(INestedProviderManager<ActionDescriptorProviderContext>),
descriptorProvider);
var actionCollectionDescriptorProvider = new DefaultActionDescriptorsCollectionProvider(serviceContainer);
var decisionTreeProvider = new ActionSelectorDecisionTreeProvider(actionCollectionDescriptorProvider);
var bindingProvider = new Mock<IActionBindingContextProvider>();
var defaultActionSelector = new DefaultActionSelector(
actionCollectionDescriptorProvider,
decisionTreeProvider,
bindingProvider.Object,
NullLoggerFactory.Instance);
return await defaultActionSelector.SelectAsync(context);
}
private ReflectedActionDescriptorProvider GetActionDescriptorProvider(DefaultActionDiscoveryConventions actionDiscoveryConventions)
{
var assemblies = new Assembly[] { typeof(DefaultActionDiscoveryConventionsActionSelectionTests).GetTypeInfo().Assembly, };
var controllerAssemblyProvider = new Mock<IControllerAssemblyProvider>();
controllerAssemblyProvider.SetupGet(x => x.CandidateAssemblies).Returns(assemblies);
return new ReflectedActionDescriptorProvider(
controllerAssemblyProvider.Object,
actionDiscoveryConventions,
new TestGlobalFilterProvider(),
new MockMvcOptionsAccessor(),
Mock.Of<IInlineConstraintResolver>());
}
private static HttpContext GetHttpContext(string httpMethod)
{
var request = new Mock<HttpRequest>();
var headers = new Mock<IHeaderDictionary>();
request.SetupGet(r => r.Headers).Returns(headers.Object);
request.SetupGet(x => x.Method).Returns(httpMethod);
var httpContext = new Mock<HttpContext>();
httpContext.SetupGet(c => c.Request).Returns(request.Object);
return httpContext.Object;
}
private class CustomActionConvention : DefaultActionDiscoveryConventions
{
public override bool IsController([NotNull]TypeInfo typeInfo)
{
return
typeof(DefaultActionDiscoveryConventionsActionSelectionTests)
.GetNestedTypes(BindingFlags.NonPublic)
.Select(ct => ct.GetTypeInfo())
.Contains(typeInfo);
}
public override IEnumerable<string> GetSupportedHttpMethods(MethodInfo methodInfo)
{
if (methodInfo.Name.Equals("PostSomething", StringComparison.OrdinalIgnoreCase))
{
return new[] { "POST" };
}
return null;
}
}
private class MixedRpcAndRestController
{
public void Index()
{
}
public void Get()
{
}
public void Post()
{ }
public void GetSomething()
{ }
// This will be treated as an RPC method.
public void Head()
{
}
// This will be treated as an RPC method.
public void Options()
{
}
}
private class RestOnlyController
{
public void Get()
{
}
public void Put()
{
}
public void Post()
{
}
public void Delete()
{
}
public void Patch()
{
}
}
private class RpcOnlyController
{
public void Index()
{
}
public void GetSomething()
{
}
public void PutSomething()
{
}
public void PostSomething()
{
}
public void DeleteSomething()
{
}
public void PatchSomething()
{
}
}
private class AmbiguousController
{
public void Index(int i)
{ }
public void Index(string s)
{ }
}
}
}
#endif