Files
Umbraco-CMS/tests/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs
Emma L Garland 862820c6a6 Resolved more warnings, and marked more warning types as errors (#16990)
* Fix warnings SA1111, SA1028, SA1500, IDE1270  in Umbraco.Web.Website, and updated rules.

* Remove warnings: IDE0270: Null check can be simplified

* More SqlServer project warnings resolved

* CS0105 namespace appeared already

* Suppress warning until implementation:

#pragma warning disable CS0162 // Unreachable code detected
#pragma warning disable CS0618 // Type or member is obsolete

CS0162 remove unreachable code
SA1028 remove trailing whitespace
SA1106 no empty statements
CS1570 malformed XML
CS1572 corrected xml parameter
CS1573 param tag added
IDE0007 var not explicit
IDE0008 explicit not var
IDE0057 simplify substring
IDE0074 compound assignment
CA1825 array.empty

Down to 3479 warnings
2024-09-10 15:17:29 +01:00

354 lines
14 KiB
C#

using System.Linq.Expressions;
using System.Reflection;
using Asp.Versioning;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Moq;
using NUnit.Framework;
using Umbraco.Cms.Api.Common.Attributes;
using Umbraco.Cms.Api.Delivery.Controllers.Content;
using Umbraco.Cms.Api.Management.Controllers;
using Umbraco.Cms.Api.Management.Controllers.ModelsBuilder;
using Umbraco.Cms.Api.Management.DependencyInjection;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.Composing;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Web;
using Umbraco.Cms.Core.Persistence.Repositories;
using Umbraco.Cms.Persistence.Sqlite;
using Umbraco.Cms.Persistence.SqlServer;
using Umbraco.Cms.Tests.Common.Testing;
using Umbraco.Cms.Tests.Integration.DependencyInjection;
using Umbraco.Cms.Tests.Integration.Testing;
using Umbraco.Cms.Web.Common.Controllers;
using Umbraco.Cms.Web.Website.Controllers;
namespace Umbraco.Cms.Tests.Integration.TestServerTest
{
[TestFixture]
[UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest, Logger = UmbracoTestOptions.Logger.Console, Boot = true)]
public abstract class UmbracoTestServerTestBase : UmbracoIntegrationTestBase
{
protected HttpClient Client { get; private set; }
protected LinkGenerator LinkGenerator => Factory.Services.GetRequiredService<LinkGenerator>();
protected WebApplicationFactory<UmbracoTestServerTestBase> Factory { get; private set; }
/// <summary>
/// Hook for altering UmbracoBuilder setup
/// </summary>
/// <remarks>
/// Can also be used for registering test doubles.
/// </remarks>
protected virtual void CustomTestSetup(IUmbracoBuilder builder)
{
}
protected virtual void CustomTestAuthSetup(IServiceCollection services)
{
// Add a test auth scheme with a test auth handler to authn and assign the user
services.AddAuthentication(TestAuthHandler.TestAuthenticationScheme)
.AddScheme<AuthenticationSchemeOptions, TestAuthHandler>(
TestAuthHandler.TestAuthenticationScheme, options => { });
}
[SetUp]
public void Setup()
{
/*
* It's worth noting that our usage of WebApplicationFactory is non-standard,
* the intent is that your Startup.ConfigureServices is called just like
* when the app starts up, then replacements are registered in this class with
* builder.ConfigureServices (builder.ConfigureTestServices has hung around from before the
* generic host switchover).
*
* This is currently a pain to refactor towards due to UmbracoBuilder+TypeFinder+TypeLoader setup but
* we should get there one day.
*
* However we need to separate the testing framework we provide for downstream projects from our own tests.
* We cannot use the Umbraco.Web.UI startup yet as that is not available downstream.
*
* See https://docs.microsoft.com/en-us/aspnet/core/test/integration-tests
*/
var factory = new UmbracoWebApplicationFactory<UmbracoTestServerTestBase>(CreateHostBuilder);
// additional host configuration for web server integration tests
Factory = factory.WithWebHostBuilder(builder =>
{
// Otherwise inferred as $(SolutionDir)/Umbraco.Tests.Integration (note lack of src/tests)
builder.UseContentRoot(Assembly.GetExecutingAssembly().GetRootDirectorySafe());
// Executes after the standard ConfigureServices method
builder.ConfigureTestServices(services =>
{
services.AddSingleton<IWebProfilerRepository, TestWebProfilerRepository>();
CustomTestAuthSetup(services);
});
});
Client = Factory.CreateClient(new WebApplicationFactoryClientOptions
{
AllowAutoRedirect = false,
BaseAddress = new Uri("https://localhost/", UriKind.Absolute)
});
}
/// <summary>
/// Prepare a url before using <see cref="Client"/>.
/// This returns the url but also sets the HttpContext.request into to use this url.
/// </summary>
/// <returns>The string URL of the controller action.</returns>
protected string PrepareApiControllerUrl<T>(Expression<Func<T, object>> methodSelector)
where T : UmbracoApiController
{
var url = LinkGenerator.GetUmbracoApiService(methodSelector);
return PrepareUrl(url);
}
protected string GetManagementApiUrl<T>(Expression<Func<T, object>> methodSelector)
where T : ManagementApiControllerBase
{
MethodInfo? method = ExpressionHelper.GetMethodInfo(methodSelector);
IDictionary<string, object?> methodParams = ExpressionHelper.GetMethodParams(methodSelector) ?? new Dictionary<string, object?>();
// Remove the CancellationToken from the method params, this is automatically added by the framework
// So we do not want to add this to the query string
var cancellationTokenKey = methodParams.FirstOrDefault(x => x.Value is CancellationToken).Key;
if (cancellationTokenKey is not null)
{
methodParams.Remove(cancellationTokenKey);
}
methodParams["version"] = method?.GetCustomAttribute<MapToApiVersionAttribute>()?.Versions?.First().MajorVersion.ToString();
if (method == null)
{
throw new MissingMethodException(
$"Could not find the method {methodSelector} on type {typeof(T)} or the result ");
}
var controllerName = ControllerExtensions.GetControllerName(typeof(T));
return LinkGenerator.GetUmbracoControllerUrl(method.Name, controllerName, null, methodParams);
}
/// <summary>
/// Prepare a url before using <see cref="Client"/>.
/// This returns the url but also sets the HttpContext.request into to use this url.
/// </summary>
/// <returns>The string URL of the controller action.</returns>
protected string PrepareSurfaceControllerUrl<T>(Expression<Func<T, object>> methodSelector)
where T : SurfaceController
{
var url = LinkGenerator.GetUmbracoSurfaceUrl(methodSelector);
return PrepareUrl(url);
}
/// <summary>
/// Prepare a url before using <see cref="Client"/>.
/// This returns the url but also sets the HttpContext.request into to use this url.
/// </summary>
/// <returns>The string URL of the controller action.</returns>
protected string PrepareUrl(string url)
{
IUmbracoContextFactory umbracoContextFactory = GetRequiredService<IUmbracoContextFactory>();
IHttpContextAccessor httpContextAccessor = GetRequiredService<IHttpContextAccessor>();
httpContextAccessor.HttpContext = new DefaultHttpContext
{
Request =
{
Scheme = "https",
Host = new HostString("localhost", 80),
Path = url,
QueryString = new QueryString(string.Empty)
}
};
umbracoContextFactory.EnsureUmbracoContext();
return url;
}
private IHostBuilder CreateHostBuilder()
{
IHostBuilder hostBuilder = Host.CreateDefaultBuilder()
.ConfigureUmbracoDefaults()
.ConfigureAppConfiguration((context, configBuilder) =>
{
context.HostingEnvironment = TestHelper.GetWebHostEnvironment();
configBuilder.Sources.Clear();
configBuilder.AddInMemoryCollection(InMemoryConfiguration);
configBuilder.AddConfiguration(GlobalSetupTeardown.TestConfiguration);
Configuration = configBuilder.Build();
})
.ConfigureWebHost(builder =>
{
builder.ConfigureServices((context, services) =>
{
context.HostingEnvironment = TestHelper.GetWebHostEnvironment();
ConfigureServices(services);
ConfigureTestServices(services);
services.AddUnique(CreateLoggerFactory());
if (!TestOptions.Boot)
{
// If boot is false, we don't want the CoreRuntime hosted service to start
// So we replace it with a Mock
services.AddUnique(Mock.Of<IRuntime>());
}
});
// call startup
builder.Configure(Configure);
})
.UseDefaultServiceProvider(cfg =>
{
// These default to true *if* WebHostEnvironment.EnvironmentName == Development
// When running tests, EnvironmentName used to be null on the mock that we register into services.
// Enable opt in for tests so that validation occurs regardless of environment name.
// Would be nice to have this on for UmbracoIntegrationTest also but requires a lot more effort to resolve issues.
cfg.ValidateOnBuild = true;
cfg.ValidateScopes = true;
});
return hostBuilder;
}
protected virtual IServiceProvider Services => Factory.Services;
protected virtual T GetRequiredService<T>() => Factory.Services.GetRequiredService<T>();
protected void ConfigureServices(IServiceCollection services)
{
services.AddTransient<TestUmbracoDatabaseFactoryProvider>();
Core.Hosting.IHostingEnvironment hostingEnvironment = TestHelper.GetHostingEnvironment();
TypeLoader typeLoader = services.AddTypeLoader(
GetType().Assembly,
hostingEnvironment,
TestHelper.ConsoleLoggerFactory,
AppCaches.NoCache,
Configuration,
TestHelper.Profiler);
services.AddLogger(TestHelper.GetWebHostEnvironment(), Configuration);
var builder = new UmbracoBuilder(services, Configuration, typeLoader, TestHelper.ConsoleLoggerFactory, TestHelper.Profiler, AppCaches.NoCache, hostingEnvironment);
builder.Services.AddTransient<IHostedService>(sp => new TestDatabaseHostedLifecycleService(() => UseTestDatabase(sp)));
builder
.AddConfiguration()
.AddUmbracoCore()
.AddWebComponents()
.AddNuCache()
.AddBackOfficeCore()
.AddBackOfficeAuthentication()
.AddBackOfficeIdentity()
.AddMembersIdentity()
// .AddBackOfficeAuthorizationPolicies(TestAuthHandler.TestAuthenticationScheme)
.AddMvcAndRazor(mvcBuilding: mvcBuilder =>
{
// Adds Umbraco.Web.Common
mvcBuilder.AddApplicationPart(typeof(RenderController).Assembly);
// Adds Umbraco.Web.Website
mvcBuilder.AddApplicationPart(typeof(SurfaceController).Assembly);
// Adds Umbraco.Cms.Api.ManagementApi
mvcBuilder.AddApplicationPart(typeof(ModelsBuilderControllerBase).Assembly);
// Adds Umbraco.Cms.Api.DeliveryApi
mvcBuilder.AddApplicationPart(typeof(ContentApiItemControllerBase).Assembly);
// Adds Umbraco.Tests.Integration
mvcBuilder.AddApplicationPart(typeof(UmbracoTestServerTestBase).Assembly);
CustomMvcSetup(mvcBuilder);
})
.AddWebServer()
.AddWebsite()
.AddUmbracoSqlServerSupport()
.AddUmbracoSqliteSupport()
.AddDeliveryApi()
.AddUmbracoManagementApi()
.AddComposers()
.AddTestServices(TestHelper); // This is the important one!
CustomTestSetup(builder);
builder.Build();
}
protected virtual void CustomMvcSetup(IMvcBuilder mvcBuilder)
{
}
/// <summary>
/// Hook for registering test doubles.
/// </summary>
protected virtual void ConfigureTestServices(IServiceCollection services)
{
}
protected void Configure(IApplicationBuilder app)
{
app.UseUmbraco()
.WithMiddleware(u =>
{
u.UseBackOffice();
u.UseWebsite();
})
.WithEndpoints(u =>
{
u.UseBackOfficeEndpoints();
u.UseWebsiteEndpoints();
});
}
}
}
public class TestDatabaseHostedLifecycleService : IHostedLifecycleService
{
private readonly Action _action;
public TestDatabaseHostedLifecycleService(Action action)
{
_action = action;
}
public Task StartAsync(CancellationToken cancellationToken)=> Task.CompletedTask;
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
public Task StartedAsync(CancellationToken cancellationToken) => Task.CompletedTask;
public Task StartingAsync(CancellationToken cancellationToken)
{
_action();
return Task.CompletedTask;
}
public Task StoppedAsync(CancellationToken cancellationToken) => Task.CompletedTask;
public Task StoppingAsync(CancellationToken cancellationToken) => Task.CompletedTask;
}