using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Moq; using NUnit.Framework; using Umbraco.Cms.Api.Management.DependencyInjection; using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Strings; using Umbraco.Cms.Core.Web; using Umbraco.Cms.Infrastructure.DependencyInjection; using Umbraco.Cms.Infrastructure.Persistence.Mappers; using Umbraco.Cms.Infrastructure.Scoping; using Umbraco.Cms.Persistence.Sqlite; using Umbraco.Cms.Persistence.SqlServer; using Umbraco.Cms.Tests.Common.Builders; using Umbraco.Cms.Tests.Integration.DependencyInjection; using Umbraco.Cms.Tests.Integration.Extensions; using Umbraco.Cms.Tests.Integration.TestServerTest; using Constants = Umbraco.Cms.Core.Constants; namespace Umbraco.Cms.Tests.Integration.Testing; /// /// Abstract class for integration tests /// /// /// This will use a Host Builder to boot and install Umbraco ready for use /// public abstract class UmbracoIntegrationTest : UmbracoIntegrationTestBase { private IHost _host; protected IServiceProvider Services => _host.Services; /// /// Gets the /// protected IScopeProvider ScopeProvider => Services.GetRequiredService(); /// /// Gets the /// protected IScopeAccessor ScopeAccessor => Services.GetRequiredService(); /// /// Gets the /// protected ILoggerFactory LoggerFactory => Services.GetRequiredService(); protected AppCaches AppCaches => Services.GetRequiredService(); protected IIOHelper IOHelper => Services.GetRequiredService(); protected IShortStringHelper ShortStringHelper => Services.GetRequiredService(); protected GlobalSettings GlobalSettings => Services.GetRequiredService>().Value; protected IMapperCollection Mappers => Services.GetRequiredService(); protected UserBuilder UserBuilderInstance { get; } = new(); protected UserGroupBuilder UserGroupBuilderInstance { get; } = new(); [SetUp] public void Setup() { InMemoryConfiguration[Constants.Configuration.ConfigUnattended + ":" + nameof(UnattendedSettings.InstallUnattended)] = "true"; var hostBuilder = CreateHostBuilder(); _host = hostBuilder.Build(); UseTestDatabase(_host.Services); _host.Start(); if (TestOptions.Boot) { Services.GetRequiredService().EnsureUmbracoContext(); } } [TearDown] public void TearDownAsync() => _host.StopAsync(); /// /// Create the Generic Host and execute startup ConfigureServices/Configure calls /// private IHostBuilder CreateHostBuilder() { var hostBuilder = Host.CreateDefaultBuilder() .ConfigureUmbracoDefaults() // IMPORTANT: We Cannot use UseStartup, there's all sorts of threads about this with testing. Although this can work // if you want to setup your tests this way, it is a bit annoying to do that as the WebApplicationFactory will // create separate Host instances. So instead of UseStartup, we just call ConfigureServices/Configure ourselves, // and in the case of the UmbracoTestServerTestBase it will use the ConfigureWebHost to Configure the IApplicationBuilder directly. .ConfigureAppConfiguration((context, configBuilder) => { context.HostingEnvironment = TestHelper.GetWebHostEnvironment(); configBuilder.Sources.Clear(); configBuilder.AddInMemoryCollection(InMemoryConfiguration); SetUpTestConfiguration(configBuilder); Configuration = configBuilder.Build(); }) .ConfigureServices((_, services) => { 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()); } }); return hostBuilder; } protected void ConfigureServices(IServiceCollection services) { services.AddTransient(); var webHostEnvironment = TestHelper.GetWebHostEnvironment(); services.AddRequiredNetCoreServices(TestHelper, webHostEnvironment); // We register this service because we need it for IRuntimeState, if we don't this breaks 900 tests services.AddSingleton(); services.AddSingleton(); services.AddLogger(webHostEnvironment, Configuration); // Add it! var hostingEnvironment = TestHelper.GetHostingEnvironment(); var typeLoader = services.AddTypeLoader( GetType().Assembly, hostingEnvironment, TestHelper.ConsoleLoggerFactory, AppCaches.NoCache, Configuration, TestHelper.Profiler); var builder = new UmbracoBuilder(services, Configuration, typeLoader, TestHelper.ConsoleLoggerFactory, TestHelper.Profiler, AppCaches.NoCache, hostingEnvironment); builder.AddConfiguration() .AddUmbracoCore() .AddWebComponents() .AddRuntimeMinifier() .AddBackOfficeAuthentication() .AddBackOfficeIdentity() .AddMembersIdentity() .AddExamine() .AddUmbracoSqlServerSupport() .AddUmbracoSqliteSupport() .AddTestServices(TestHelper); if (TestOptions.Mapper) { // TODO: Should these just be called from within AddUmbracoCore/AddWebComponents? builder .AddCoreMappingProfiles(); } services.AddSignalR(); services.AddMvc(); CustomTestSetup(builder); builder.Build(); } /// /// Hook for altering UmbracoBuilder setup /// /// /// Can also be used for registering test doubles. /// protected virtual void CustomTestSetup(IUmbracoBuilder builder) { } /// /// Hook for registering test doubles. /// protected virtual void ConfigureTestServices(IServiceCollection services) { } protected virtual T GetRequiredService() => Services.GetRequiredService(); protected virtual void SetUpTestConfiguration(IConfigurationBuilder configBuilder) { if (GlobalSetupTeardown.TestConfiguration is not null) { configBuilder.AddConfiguration(GlobalSetupTeardown.TestConfiguration); } } protected void DeleteAllTemplateViewFiles() { var fileSystems = GetRequiredService(); var viewFileSystem = fileSystems.MvcViewsFileSystem!; foreach (var file in viewFileSystem.GetFiles(string.Empty).ToArray()) { viewFileSystem.DeleteFile(file); } } }