Port 7.7 - WIP

This commit is contained in:
Stephan
2017-09-14 19:29:12 +02:00
parent 4634eb28e7
commit d9aaba192c
63 changed files with 2701 additions and 1384 deletions

View File

@@ -0,0 +1,18 @@
using System;
using Microsoft.Owin.Extensions;
using Owin;
namespace Umbraco.Tests.TestHelpers.ControllerTesting
{
public static class AuthenticateEverythingExtensions
{
public static IAppBuilder AuthenticateEverything(this IAppBuilder app)
{
if (app == null)
throw new ArgumentNullException("app");
app.Use(typeof(AuthenticateEverythingMiddleware), (object)app, (object)new AuthenticateEverythingMiddleware.AuthenticateEverythingAuthenticationOptions());
app.UseStageMarker(PipelineStage.Authenticate);
return app;
}
}
}

View File

@@ -0,0 +1,55 @@
using System;
using System.Threading.Tasks;
using Microsoft.Owin;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Infrastructure;
using Owin;
using Umbraco.Core.Security;
namespace Umbraco.Tests.TestHelpers.ControllerTesting
{
public class AuthenticateEverythingMiddleware : AuthenticationMiddleware<AuthenticationOptions>
{
public AuthenticateEverythingMiddleware(OwinMiddleware next, IAppBuilder app, AuthenticationOptions options)
: base(next, options)
{
}
protected override AuthenticationHandler<AuthenticationOptions> CreateHandler()
{
return new AuthenticateEverythingHandler();
}
public class AuthenticateEverythingHandler : AuthenticationHandler<AuthenticationOptions>
{
protected override Task<AuthenticationTicket> AuthenticateCoreAsync()
{
var identity = new UmbracoBackOfficeIdentity(
new UserData(Guid.NewGuid().ToString())
{
Id = 0,
Roles = new[] { "admin" },
AllowedApplications = new[] { "content", "media", "members" },
Culture = "en-US",
RealName = "Admin",
Username = "admin"
});
return Task.FromResult(new AuthenticationTicket(identity,
new AuthenticationProperties()
{
ExpiresUtc = DateTime.Now.AddDays(1)
}));
}
}
public class AuthenticateEverythingAuthenticationOptions : AuthenticationOptions
{
public AuthenticateEverythingAuthenticationOptions()
: base("AuthenticateEverything")
{
AuthenticationMode = AuthenticationMode.Active;
}
}
}
}

View File

@@ -0,0 +1,21 @@
using System.Collections.Generic;
using System.Reflection;
using System.Web.Http.Dispatcher;
namespace Umbraco.Tests.TestHelpers.ControllerTesting
{
public class SpecificAssemblyResolver : IAssembliesResolver
{
private readonly Assembly[] _assemblies;
public SpecificAssemblyResolver(Assembly[] assemblies)
{
_assemblies = assemblies;
}
public ICollection<Assembly> GetAssemblies()
{
return _assemblies;
}
}
}

View File

@@ -0,0 +1,22 @@
using System;
using System.Net.Http;
using System.Web.Http;
using Umbraco.Web;
namespace Umbraco.Tests.TestHelpers.ControllerTesting
{
public class TestControllerActivator : TestControllerActivatorBase
{
private readonly Func<HttpRequestMessage, UmbracoHelper, ApiController> _factory;
public TestControllerActivator(Func<HttpRequestMessage, UmbracoHelper, ApiController> factory)
{
_factory = factory;
}
protected override ApiController CreateController(Type controllerType, HttpRequestMessage msg, UmbracoHelper helper)
{
return _factory(msg, helper);
}
}
}

View File

@@ -0,0 +1,144 @@
using System;
using System.Collections;
using System.Linq;
using System.Net.Http;
using System.Web;
using System.Web.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Dispatcher;
using System.Web.Security;
using Moq;
using Semver;
using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Composing;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Dictionary;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.Security;
using Umbraco.Core.Services;
using Umbraco.Tests.TestHelpers.Stubs;
using Umbraco.Web;
using Umbraco.Web.PublishedCache;
using Umbraco.Web.Routing;
using Umbraco.Web.Security;
using Umbraco.Web.WebApi;
namespace Umbraco.Tests.TestHelpers.ControllerTesting
{
/// <summary>
/// Used to mock all of the services required for re-mocking and testing controllers
/// </summary>
/// <remarks>
/// A more complete version of this is found in the Umbraco REST API project but this has the basics covered
/// </remarks>
public abstract class TestControllerActivatorBase : DefaultHttpControllerActivator, IHttpControllerActivator
{
IHttpController IHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)
{
if (typeof(UmbracoApiControllerBase).IsAssignableFrom(controllerType))
{
var owinContext = request.TryGetOwinContext().Result;
var mockedUserService = Mock.Of<IUserService>();
var mockedMigrationService = new Mock<IMigrationEntryService>();
//set it up to return anything so that the app ctx is 'Configured'
mockedMigrationService.Setup(x => x.FindEntry(It.IsAny<string>(), It.IsAny<SemVersion>())).Returns(Mock.Of<IMigrationEntry>());
var serviceContext = new ServiceContext(
userService: mockedUserService,
migrationEntryService: mockedMigrationService.Object,
localizedTextService:Mock.Of<ILocalizedTextService>(),
sectionService:Mock.Of<ISectionService>());
//ensure the configuration matches the current version for tests
SettingsForTests.ConfigurationStatus = Current.RuntimeState.SemanticVersion.ToSemanticString();
// fixme v8?
////new app context
//var dbCtx = new Mock<DatabaseContext>(Mock.Of<IDatabaseFactory>(), Mock.Of<ILogger>(), Mock.Of<ISqlSyntaxProvider>(), "test");
////ensure these are set so that the appctx is 'Configured'
//dbCtx.Setup(x => x.CanConnect).Returns(true);
//dbCtx.Setup(x => x.IsDatabaseConfigured).Returns(true);
//var appCtx = ApplicationContext.EnsureContext(
// dbCtx.Object,
// //pass in mocked services
// serviceContext,
// CacheHelper.CreateDisabledCacheHelper(),
// new ProfilingLogger(Mock.Of<ILogger>(), Mock.Of<IProfiler>()),
// true);
//httpcontext with an auth'd user
var httpContext = Mock.Of<HttpContextBase>(
http => http.User == owinContext.Authentication.User
//ensure the request exists with a cookies collection
&& http.Request == Mock.Of<HttpRequestBase>(r => r.Cookies == new HttpCookieCollection())
//ensure the request exists with an items collection
&& http.Items == Mock.Of<IDictionary>());
//chuck it into the props since this is what MS does when hosted and it's needed there
request.Properties["MS_HttpContext"] = httpContext;
var backofficeIdentity = (UmbracoBackOfficeIdentity) owinContext.Authentication.User.Identity;
var webSecurity = new Mock<WebSecurity>(null, null);
//mock CurrentUser
webSecurity.Setup(x => x.CurrentUser)
.Returns(Mock.Of<IUser>(u => u.IsApproved == true
&& u.IsLockedOut == false
&& u.AllowedSections == backofficeIdentity.AllowedApplications
&& u.Email == "admin@admin.com"
&& u.Id == (int) backofficeIdentity.Id
&& u.Language == "en"
&& u.Name == backofficeIdentity.RealName
&& u.StartContentIds == backofficeIdentity.StartContentNodes
&& u.StartMediaIds == backofficeIdentity.StartMediaNodes
&& u.Username == backofficeIdentity.Username));
//mock Validate
webSecurity.Setup(x => x.ValidateCurrentUser())
.Returns(() => true);
webSecurity.Setup(x => x.UserHasAppAccess(It.IsAny<string>(), It.IsAny<IUser>()))
.Returns(() => true);
var umbCtx = UmbracoContext.EnsureContext(
//set the user of the HttpContext
new TestUmbracoContextAccessor(),
httpContext,
Mock.Of<IFacadeService>(),
webSecurity.Object,
Mock.Of<IUmbracoSettingsSection>(section => section.WebRouting == Mock.Of<IWebRoutingSection>(routingSection => routingSection.UrlProviderMode == UrlProviderMode.Auto.ToString())),
Enumerable.Empty<IUrlProvider>(),
true); //replace it
var urlHelper = new Mock<IUrlProvider>();
urlHelper.Setup(provider => provider.GetUrl(It.IsAny<UmbracoContext>(), It.IsAny<int>(), It.IsAny<Uri>(), It.IsAny<UrlProviderMode>()))
.Returns("/hello/world/1234");
var membershipHelper = new MembershipHelper(umbCtx, Mock.Of<MembershipProvider>(), Mock.Of<RoleProvider>());
var mockedTypedContent = Mock.Of<IPublishedContentQuery>();
var umbHelper = new UmbracoHelper(umbCtx,
Mock.Of<IPublishedContent>(),
mockedTypedContent,
Mock.Of<ITagQuery>(),
Mock.Of<IDataTypeService>(),
Mock.Of<ICultureDictionary>(),
Mock.Of<IUmbracoComponentRenderer>(),
membershipHelper,
new ServiceContext(), // fixme 'course that won't work
CacheHelper.NoCache);
return CreateController(controllerType, request, umbHelper);
}
//default
return base.Create(request, controllerDescriptor, controllerType);
}
protected abstract ApiController CreateController(Type controllerType, HttpRequestMessage msg, UmbracoHelper helper);
}
}

View File

@@ -0,0 +1,79 @@
using System;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using System.Web.Http;
using Microsoft.Owin.Testing;
using Newtonsoft.Json;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Web;
using Umbraco.Web.WebApi;
namespace Umbraco.Tests.TestHelpers.ControllerTesting
{
public class TestRunner
{
private readonly Func<HttpRequestMessage, UmbracoHelper, ApiController> _controllerFactory;
public TestRunner(Func<HttpRequestMessage, UmbracoHelper, ApiController> controllerFactory)
{
_controllerFactory = controllerFactory;
}
public async Task<Tuple<HttpResponseMessage, string>> Execute(string controllerName, string actionName, HttpMethod method, HttpContent content = null)
{
var startup = new TestStartup(
configuration =>
{
configuration.Routes.MapHttpRoute("Default",
routeTemplate: "{controller}/{action}/{id}",
defaults: new { controller = controllerName, action = actionName, id = RouteParameter.Optional });
},
_controllerFactory);
using (var server = TestServer.Create(builder => startup.Configuration(builder)))
{
var request = new HttpRequestMessage
{
RequestUri = new Uri("https://testserver/"),
Method = method
};
if (content != null)
request.Content = content;
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
Console.WriteLine(request);
var response = await server.HttpClient.SendAsync(request);
Console.WriteLine(response);
string json = "";
if (response.IsSuccessStatusCode == false)
{
WriteResponseError(response);
}
else
{
json = (await ((StreamContent)response.Content).ReadAsStringAsync()).TrimStart(AngularJsonMediaTypeFormatter.XsrfPrefix);
var deserialized = JsonConvert.DeserializeObject(json);
Console.Write(JsonConvert.SerializeObject(deserialized, Formatting.Indented));
}
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
return Tuple.Create(response, json);
}
}
private static void WriteResponseError(HttpResponseMessage response)
{
var result = response.Content.ReadAsStringAsync().Result;
Console.Out.WriteLine("Http operation unsuccessfull");
Console.Out.WriteLine(string.Format("Status: '{0}'", response.StatusCode));
Console.Out.WriteLine(string.Format("Reason: '{0}'", response.ReasonPhrase));
Console.Out.WriteLine(result);
}
}
}

View File

@@ -0,0 +1,54 @@
using System;
using System.Net.Http;
using System.Web.Http;
using System.Web.Http.Dispatcher;
using System.Web.Http.ExceptionHandling;
using System.Web.Http.Tracing;
using Owin;
using Umbraco.Web;
using Umbraco.Web.Editors;
using Umbraco.Web.WebApi;
namespace Umbraco.Tests.TestHelpers.ControllerTesting
{
/// <summary>
/// Startup class for the self-hosted web server works for OWIN and WebAPI
/// </summary>
public class TestStartup
{
private readonly Func<HttpRequestMessage, UmbracoHelper, ApiController> _controllerFactory;
private readonly Action<HttpConfiguration> _initialize;
public TestStartup(Action<HttpConfiguration> initialize, Func<HttpRequestMessage, UmbracoHelper, ApiController> controllerFactory)
{
_controllerFactory = controllerFactory;
_initialize = initialize;
}
public void Configuration(IAppBuilder app)
{
var httpConfig = new HttpConfiguration();
//TODO: Enable this if you can't see the errors produced
//var traceWriter = httpConfig.EnableSystemDiagnosticsTracing();
//traceWriter.IsVerbose = true;
//traceWriter.MinimumLevel = TraceLevel.Debug;
httpConfig.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
// Add in a simple exception tracer so we can see what is causing the 500 Internal Server Error
httpConfig.Services.Add(typeof(IExceptionLogger), new TraceExceptionLogger());
httpConfig.Services.Replace(typeof(IAssembliesResolver), new SpecificAssemblyResolver(new[] { typeof(UsersController).Assembly }));
httpConfig.Services.Replace(typeof(IHttpControllerActivator), new TestControllerActivator(_controllerFactory));
httpConfig.Services.Replace(typeof(IHttpControllerSelector), new NamespaceHttpControllerSelector(httpConfig));
//auth everything
app.AuthenticateEverything();
_initialize(httpConfig);
app.UseWebApi(httpConfig);
}
}
}

View File

@@ -0,0 +1,16 @@
using System.Diagnostics;
using System.Web.Http.ExceptionHandling;
namespace Umbraco.Tests.TestHelpers.ControllerTesting
{
/// <summary>
/// Traces any errors for WebApi to the output window
/// </summary>
public class TraceExceptionLogger : ExceptionLogger
{
public override void Log(ExceptionLoggerContext context)
{
Trace.TraceError(context.ExceptionContext.Exception.ToString());
}
}
}