Fixing tests, removing old files, adds notes

This commit is contained in:
Shannon
2020-12-21 15:58:47 +11:00
parent cc1404747b
commit 75796e3eae
10 changed files with 68 additions and 212 deletions

View File

@@ -144,7 +144,7 @@ namespace Umbraco.Tests.Integration.TestServerTest.Controllers
Assert.Multiple(() => Assert.Multiple(() =>
{ {
Assert.AreEqual(HttpStatusCode.NotFound, response.StatusCode); Assert.AreEqual(HttpStatusCode.NotFound, response.StatusCode);
Assert.AreEqual(")]}',\n{\"Message\":\"No variants flagged for saving\"}", body); Assert.AreEqual(AngularJsonMediaTypeFormatter.XsrfPrefix + "{\"Message\":\"No variants flagged for saving\"}", body);
}); });
} }

View File

@@ -2,7 +2,6 @@
using System; using System;
using System.Linq.Expressions; using System.Linq.Expressions;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
@@ -11,20 +10,18 @@ using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.TestHost; using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Hosting;
using NUnit.Framework; using NUnit.Framework;
using Umbraco.Core; using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.DependencyInjection;
using Umbraco.Extensions; using Umbraco.Extensions;
using Umbraco.Tests.Integration.Testing; using Umbraco.Tests.Integration.Testing;
using Umbraco.Tests.Testing; using Umbraco.Tests.Testing;
using Umbraco.Web; using Umbraco.Web;
using Umbraco.Core.DependencyInjection;
using Umbraco.Web.Common.Controllers;
using Microsoft.Extensions.Hosting;
using Umbraco.Core.Cache;
using Umbraco.Core.Persistence;
using Umbraco.Core.Runtime;
using Umbraco.Web.BackOffice.Controllers; using Umbraco.Web.BackOffice.Controllers;
using Umbraco.Web.Common.Controllers;
using Umbraco.Web.Website.Controllers;
namespace Umbraco.Tests.Integration.TestServerTest namespace Umbraco.Tests.Integration.TestServerTest
{ {
@@ -133,8 +130,14 @@ namespace Umbraco.Tests.Integration.TestServerTest
public override void ConfigureServices(IServiceCollection services) public override void ConfigureServices(IServiceCollection services)
{ {
services.AddTransient<TestUmbracoDatabaseFactoryProvider>(); services.AddTransient<TestUmbracoDatabaseFactoryProvider>();
var typeLoader = services.AddTypeLoader(GetType().Assembly, TestHelper.GetWebHostEnvironment(), TestHelper.GetHostingEnvironment(), var typeLoader = services.AddTypeLoader(
TestHelper.ConsoleLoggerFactory, AppCaches.NoCache, Configuration, TestHelper.Profiler); GetType().Assembly,
TestHelper.GetWebHostEnvironment(),
TestHelper.GetHostingEnvironment(),
TestHelper.ConsoleLoggerFactory,
AppCaches.NoCache,
Configuration,
TestHelper.Profiler);
var builder = new UmbracoBuilder(services, Configuration, typeLoader); var builder = new UmbracoBuilder(services, Configuration, typeLoader);
@@ -147,10 +150,16 @@ namespace Umbraco.Tests.Integration.TestServerTest
.AddBackOfficeIdentity() .AddBackOfficeIdentity()
.AddBackOfficeAuthorizationPolicies(TestAuthHandler.TestAuthenticationScheme) .AddBackOfficeAuthorizationPolicies(TestAuthHandler.TestAuthenticationScheme)
.AddPreviewSupport() .AddPreviewSupport()
//.WithMiniProfiler() // we don't want this running in tests
.AddMvcAndRazor(mvcBuilding: mvcBuilder => .AddMvcAndRazor(mvcBuilding: mvcBuilder =>
{ {
// Adds Umbraco.Web.BackOffice
mvcBuilder.AddApplicationPart(typeof(ContentController).Assembly); mvcBuilder.AddApplicationPart(typeof(ContentController).Assembly);
// Adds Umbraco.Web.Common
mvcBuilder.AddApplicationPart(typeof(RenderController).Assembly);
// Adds Umbraco.Web.Website
mvcBuilder.AddApplicationPart(typeof(SurfaceController).Assembly);
}) })
.AddWebServer() .AddWebServer()
.Build(); .Build();
@@ -159,6 +168,8 @@ namespace Umbraco.Tests.Integration.TestServerTest
public override void Configure(IApplicationBuilder app) public override void Configure(IApplicationBuilder app)
{ {
app.UseUmbraco(); app.UseUmbraco();
app.UseUmbracoBackOffice();
app.UseUmbracoWebsite();
} }
} }
} }

View File

@@ -1,95 +0,0 @@
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, IUmbracoContextAccessor, ApiController> _controllerFactory;
public TestRunner(Func<HttpRequestMessage, IUmbracoContextAccessor, ApiController> controllerFactory)
{
_controllerFactory = controllerFactory;
}
public async Task<Tuple<HttpResponseMessage, string>> Execute(string controllerName, string actionName, HttpMethod method,
HttpContent content = null,
MediaTypeWithQualityHeaderValue mediaTypeHeader = null,
bool assertOkResponse = true, object routeDefaults = null, string url = null)
{
if (mediaTypeHeader == null)
{
mediaTypeHeader = new MediaTypeWithQualityHeaderValue("application/json");
}
if (routeDefaults == null)
{
routeDefaults = new { controller = controllerName, action = actionName, id = RouteParameter.Optional };
}
var startup = new TestStartup(
configuration =>
{
configuration.Routes.MapHttpRoute("Default",
routeTemplate: "{controller}/{action}/{id}",
defaults: routeDefaults);
},
_controllerFactory);
using (var server = TestServer.Create(builder => startup.Configuration(builder)))
{
var request = new HttpRequestMessage
{
RequestUri = new Uri("https://testserver/" + (url ?? "")),
Method = method
};
if (content != null)
request.Content = content;
request.Headers.Accept.Add(mediaTypeHeader);
Console.WriteLine(request);
var response = await server.HttpClient.SendAsync(request);
Console.WriteLine(response);
if (response.IsSuccessStatusCode == false)
{
WriteResponseError(response);
}
var json = (await ((StreamContent)response.Content).ReadAsStringAsync()).TrimStart(AngularJsonMediaTypeFormatter.XsrfPrefix);
if (!json.IsNullOrWhiteSpace())
{
var deserialized = JsonConvert.DeserializeObject(json);
Console.Write(JsonConvert.SerializeObject(deserialized, Formatting.Indented));
}
if (assertOkResponse)
{
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($"Status: '{response.StatusCode}'");
Console.Out.WriteLine($"Reason: '{response.ReasonPhrase}'");
Console.Out.WriteLine(result);
}
}
}

View File

@@ -174,7 +174,6 @@
<Compile Include="TestHelpers\ControllerTesting\SpecificAssemblyResolver.cs" /> <Compile Include="TestHelpers\ControllerTesting\SpecificAssemblyResolver.cs" />
<Compile Include="TestHelpers\ControllerTesting\TestControllerActivator.cs" /> <Compile Include="TestHelpers\ControllerTesting\TestControllerActivator.cs" />
<Compile Include="TestHelpers\ControllerTesting\TestControllerActivatorBase.cs" /> <Compile Include="TestHelpers\ControllerTesting\TestControllerActivatorBase.cs" />
<Compile Include="TestHelpers\ControllerTesting\TestRunner.cs" />
<Compile Include="TestHelpers\ControllerTesting\TestStartup.cs" /> <Compile Include="TestHelpers\ControllerTesting\TestStartup.cs" />
<Compile Include="TestHelpers\ControllerTesting\TraceExceptionLogger.cs" /> <Compile Include="TestHelpers\ControllerTesting\TraceExceptionLogger.cs" />
<Compile Include="Testing\Objects\Accessors\NoHttpContextAccessor.cs" /> <Compile Include="Testing\Objects\Accessors\NoHttpContextAccessor.cs" />

View File

@@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@@ -636,19 +636,20 @@ namespace Umbraco.Web.BackOffice.Controllers
/// <summary> /// <summary>
/// Saves content /// Saves content
/// </summary> /// </summary>
/// <returns></returns>
[FileUploadCleanupFilter] [FileUploadCleanupFilter]
[ContentSaveValidation] [ContentSaveValidation]
public async Task<ContentItemDisplay> PostSaveBlueprint([ModelBinder(typeof(BlueprintItemBinder))] ContentItemSave contentItem) public async Task<ContentItemDisplay> PostSaveBlueprint([ModelBinder(typeof(BlueprintItemBinder))] ContentItemSave contentItem)
{ {
var contentItemDisplay = await PostSaveInternal(contentItem, var contentItemDisplay = await PostSaveInternal(
contentItem,
content => content =>
{ {
EnsureUniqueName(content.Name, content, "Name"); EnsureUniqueName(content.Name, content, "Name");
_contentService.SaveBlueprint(contentItem.PersistedContent, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Id); _contentService.SaveBlueprint(contentItem.PersistedContent, _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.Id);
//we need to reuse the underlying logic so return the result that it wants
return OperationResult.Succeed(new EventMessages()); // we need to reuse the underlying logic so return the result that it wants
return OperationResult.Succeed(new EventMessages());
}, },
content => content =>
{ {
@@ -663,7 +664,6 @@ namespace Umbraco.Web.BackOffice.Controllers
/// <summary> /// <summary>
/// Saves content /// Saves content
/// </summary> /// </summary>
/// <returns></returns>
[FileUploadCleanupFilter] [FileUploadCleanupFilter]
[ContentSaveValidation] [ContentSaveValidation]
[OutgoingEditorModelEvent] [OutgoingEditorModelEvent]
@@ -679,9 +679,9 @@ namespace Umbraco.Web.BackOffice.Controllers
private async Task<ContentItemDisplay> PostSaveInternal(ContentItemSave contentItem, Func<IContent, OperationResult> saveMethod, Func<IContent, ContentItemDisplay> mapToDisplay) private async Task<ContentItemDisplay> PostSaveInternal(ContentItemSave contentItem, Func<IContent, OperationResult> saveMethod, Func<IContent, ContentItemDisplay> mapToDisplay)
{ {
//Recent versions of IE/Edge may send in the full client side file path instead of just the file name. // Recent versions of IE/Edge may send in the full client side file path instead of just the file name.
//To ensure similar behavior across all browsers no matter what they do - we strip the FileName property of all // To ensure similar behavior across all browsers no matter what they do - we strip the FileName property of all
//uploaded files to being *only* the actual file name (as it should be). // uploaded files to being *only* the actual file name (as it should be).
if (contentItem.UploadedFiles != null && contentItem.UploadedFiles.Any()) if (contentItem.UploadedFiles != null && contentItem.UploadedFiles.Any())
{ {
foreach (var file in contentItem.UploadedFiles) foreach (var file in contentItem.UploadedFiles)
@@ -690,7 +690,7 @@ namespace Umbraco.Web.BackOffice.Controllers
} }
} }
//If we've reached here it means: // If we've reached here it means:
// * Our model has been bound // * Our model has been bound
// * and validated // * and validated
// * any file attachments have been saved to their temporary location for us to use // * any file attachments have been saved to their temporary location for us to use
@@ -700,20 +700,20 @@ namespace Umbraco.Web.BackOffice.Controllers
var passesCriticalValidationRules = ValidateCriticalData(contentItem, out var variantCount); var passesCriticalValidationRules = ValidateCriticalData(contentItem, out var variantCount);
//we will continue to save if model state is invalid, however we cannot save if critical data is missing. // we will continue to save if model state is invalid, however we cannot save if critical data is missing.
if (!ModelState.IsValid) if (!ModelState.IsValid)
{ {
//check for critical data validation issues, we can't continue saving if this data is invalid // check for critical data validation issues, we can't continue saving if this data is invalid
if (!passesCriticalValidationRules) if (!passesCriticalValidationRules)
{ {
//ok, so the absolute mandatory data is invalid and it's new, we cannot actually continue! // ok, so the absolute mandatory data is invalid and it's new, we cannot actually continue!
// add the model state to the outgoing object and throw a validation message // add the model state to the outgoing object and throw a validation message
var forDisplay = mapToDisplay(contentItem.PersistedContent); var forDisplay = mapToDisplay(contentItem.PersistedContent);
forDisplay.Errors = ModelState.ToErrorDictionary(); forDisplay.Errors = ModelState.ToErrorDictionary();
throw HttpResponseException.CreateValidationErrorResponse(forDisplay); throw HttpResponseException.CreateValidationErrorResponse(forDisplay);
} }
//if there's only one variant and the model state is not valid we cannot publish so change it to save // if there's only one variant and the model state is not valid we cannot publish so change it to save
if (variantCount == 1) if (variantCount == 1)
{ {
switch (contentItem.Action) switch (contentItem.Action)

View File

@@ -1,4 +1,4 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Net.Http; using System.Net.Http;
@@ -29,14 +29,12 @@ namespace Umbraco.Web.BackOffice.Controllers
[JsonDateTimeFormat] [JsonDateTimeFormat]
public abstract class ContentControllerBase : BackOfficeNotificationsController public abstract class ContentControllerBase : BackOfficeNotificationsController
{ {
protected ICultureDictionary CultureDictionary { get; }
protected ILoggerFactory LoggerFactory { get; }
protected IShortStringHelper ShortStringHelper { get; }
protected IEventMessagesFactory EventMessages { get; }
protected ILocalizedTextService LocalizedTextService { get; }
private readonly ILogger<ContentControllerBase> _logger; private readonly ILogger<ContentControllerBase> _logger;
private readonly IJsonSerializer _serializer; private readonly IJsonSerializer _serializer;
/// <summary>
/// Initializes a new instance of the <see cref="ContentControllerBase"/> class.
/// </summary>
protected ContentControllerBase( protected ContentControllerBase(
ICultureDictionary cultureDictionary, ICultureDictionary cultureDictionary,
ILoggerFactory loggerFactory, ILoggerFactory loggerFactory,
@@ -54,6 +52,31 @@ namespace Umbraco.Web.BackOffice.Controllers
_serializer = serializer; _serializer = serializer;
} }
/// <summary>
/// Gets the <see cref="ICultureDictionary"/>
/// </summary>
protected ICultureDictionary CultureDictionary { get; }
/// <summary>
/// Gets the <see cref="ILoggerFactory"/>
/// </summary>
protected ILoggerFactory LoggerFactory { get; }
/// <summary>
/// Gets the <see cref="IShortStringHelper"/>
/// </summary>
protected IShortStringHelper ShortStringHelper { get; }
/// <summary>
/// Gets the <see cref="IEventMessagesFactory"/>
/// </summary>
protected IEventMessagesFactory EventMessages { get; }
/// <summary>
/// Gets the <see cref="ILocalizedTextService"/>
/// </summary>
protected ILocalizedTextService LocalizedTextService { get; }
protected NotFoundObjectResult HandleContentNotFound(object id, bool throwException = true) protected NotFoundObjectResult HandleContentNotFound(object id, bool throwException = true)
{ {
ModelState.AddModelError("id", $"content with id: {id} was not found"); ModelState.AddModelError("id", $"content with id: {id} was not found");

View File

@@ -1,4 +1,4 @@
using System.Buffers; using System.Buffers;
using System.Linq; using System.Linq;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.Filters;
@@ -34,6 +34,7 @@ namespace Umbraco.Web.Common.Filters
_arrayPool = arrayPool; _arrayPool = arrayPool;
_options = options; _options = options;
} }
public void OnResultExecuted(ResultExecutedContext context) public void OnResultExecuted(ResultExecutedContext context)
{ {
} }

View File

@@ -192,8 +192,6 @@
<Compile Include="Mvc\EnsurePublishedContentRequestAttribute.cs" /> <Compile Include="Mvc\EnsurePublishedContentRequestAttribute.cs" />
<Compile Include="Mvc\UmbracoVirtualNodeRouteHandler.cs" /> <Compile Include="Mvc\UmbracoVirtualNodeRouteHandler.cs" />
<Compile Include="Security\AuthenticationOptionsExtensions.cs" /> <Compile Include="Security\AuthenticationOptionsExtensions.cs" />
<Compile Include="WebApi\AngularJsonMediaTypeFormatter.cs" />
<Compile Include="WebApi\AngularJsonOnlyConfigurationAttribute.cs" />
<Compile Include="PublishedPropertyExtension.cs" /> <Compile Include="PublishedPropertyExtension.cs" />
<Compile Include="Mvc\MergeParentContextViewDataAttribute.cs" /> <Compile Include="Mvc\MergeParentContextViewDataAttribute.cs" />
<Compile Include="Mvc\ViewDataDictionaryExtensions.cs" /> <Compile Include="Mvc\ViewDataDictionaryExtensions.cs" />

View File

@@ -1,57 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Formatting;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Umbraco.Core.Logging;
namespace Umbraco.Web.WebApi
{
/// <summary>
/// This will format the JSON output for use with AngularJs's approach to JSON Vulnerability attacks
/// </summary>
/// <remarks>
/// See: http://docs.angularjs.org/api/ng.$http (Security considerations)
/// </remarks>
public class AngularJsonMediaTypeFormatter : JsonMediaTypeFormatter
{
public const string XsrfPrefix = ")]}',\n";
/// <summary>
/// This will prepend the special chars to the stream output that angular will strip
/// </summary>
/// <param name="type"></param>
/// <param name="value"></param>
/// <param name="writeStream"></param>
/// <param name="content"></param>
/// <param name="transportContext"></param>
/// <returns></returns>
public override async Task WriteToStreamAsync(Type type, object value, Stream writeStream, HttpContent content, TransportContext transportContext)
{
if (type == null) throw new ArgumentNullException("type");
if (writeStream == null) throw new ArgumentNullException("writeStream");
var effectiveEncoding = SelectCharacterEncoding(content == null ? null : content.Headers);
using (var streamWriter = new StreamWriter(writeStream, effectiveEncoding,
//we are only writing a few chars so we don't need to allocate a large buffer
128,
//this is important! We don't want to close the stream, the base class is in charge of stream management, we just want to write to it.
leaveOpen:true))
{
//write the special encoding for angular json to the start
// (see: http://docs.angularjs.org/api/ng.$http)
streamWriter.Write(XsrfPrefix);
streamWriter.Flush();
await base.WriteToStreamAsync(type, value, writeStream, content, transportContext);
}
}
}
}

View File

@@ -1,24 +0,0 @@
using System;
using System.Linq;
using System.Net.Http.Formatting;
using System.Web.Http.Controllers;
namespace Umbraco.Web.WebApi
{
/// <summary>
/// Applying this attribute to any webapi controller will ensure that it only contains one json formatter compatible with the angular json vulnerability prevention.
/// </summary>
public class AngularJsonOnlyConfigurationAttribute : Attribute, IControllerConfiguration
{
public virtual void Initialize(HttpControllerSettings controllerSettings, HttpControllerDescriptor controllerDescriptor)
{
//remove all json/xml formatters then add our custom one
var toRemove = controllerSettings.Formatters.Where(t => (t is JsonMediaTypeFormatter) || (t is XmlMediaTypeFormatter)).ToList();
foreach (var r in toRemove)
{
controllerSettings.Formatters.Remove(r);
}
controllerSettings.Formatters.Add(new AngularJsonMediaTypeFormatter());
}
}
}