// Copyright (c) Umbraco. // See LICENSE for more details. using Examine; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Moq; using NUnit.Framework; using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Composing; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.DistributedLocking; using Umbraco.Cms.Core.Logging; using Umbraco.Cms.Core.Runtime; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Sync; using Umbraco.Cms.Core.WebAssets; using Umbraco.Cms.Infrastructure.Examine; using Umbraco.Cms.Infrastructure.HostedServices; using Umbraco.Cms.Infrastructure.PublishedCache; using Umbraco.Cms.Persistence.EFCore.Locking; using Umbraco.Cms.Persistence.EFCore.Scoping; using Umbraco.Cms.Tests.Common.TestHelpers.Stubs; using Umbraco.Cms.Tests.Integration.Implementations; using Umbraco.Cms.Tests.Integration.Testing; using Umbraco.Cms.Tests.Integration.Umbraco.Persistence.EFCore.DbContext; namespace Umbraco.Cms.Tests.Integration.DependencyInjection; /// /// This is used to replace certain services that are normally registered from our Core / Infrastructure that /// we do not want active within integration tests /// public static class UmbracoBuilderExtensions { /// /// Uses/Replaces services with testing services /// public static IUmbracoBuilder AddTestServices(this IUmbracoBuilder builder, TestHelper testHelper) { builder.Services.AddUnique(AppCaches.NoCache); builder.Services.AddUnique(Mock.Of()); builder.Services.AddUnique(testHelper.MainDom); builder.Services.AddUnique(); builder.Services.AddUnique(factory => Mock.Of()); // we don't want persisted nucache files in tests builder.Services.AddTransient(factory => new PublishedSnapshotServiceOptions { IgnoreLocalDb = true }); #if IS_WINDOWS // ensure all lucene indexes are using RAM directory (no file system) builder.Services.AddUnique(); #endif // replace this service so that it can lookup the correct file locations builder.Services.AddUnique(GetLocalizedTextService); builder.Services.AddUnique(); builder.Services.AddUnique(); builder.Services.AddDbContext( (serviceProvider, options) => { var testDatabaseType = builder.Config.GetValue("Tests:Database:DatabaseType"); if (testDatabaseType is TestDatabaseSettings.TestDatabaseType.Sqlite) { options.UseSqlite(serviceProvider.GetRequiredService>().CurrentValue.ConnectionString); } else { // If not Sqlite, assume SqlServer options.UseSqlServer(serviceProvider.GetRequiredService>().CurrentValue.ConnectionString); } }, optionsLifetime: ServiceLifetime.Singleton); builder.Services.AddDbContextFactory( (serviceProvider, options) => { var testDatabaseType = builder.Config.GetValue("Tests:Database:DatabaseType"); if (testDatabaseType is TestDatabaseSettings.TestDatabaseType.Sqlite) { options.UseSqlite(serviceProvider.GetRequiredService>().CurrentValue.ConnectionString); } else { // If not Sqlite, assume SqlServer options.UseSqlServer(serviceProvider.GetRequiredService>().CurrentValue.ConnectionString); } }); builder.Services.AddUnique, AmbientEFCoreScopeStack>(); builder.Services.AddUnique, EFCoreScopeAccessor>(); builder.Services.AddUnique, EFCoreScopeProvider>(); builder.Services.AddSingleton>(); builder.Services.AddSingleton>(); return builder; } /// /// Used to register a replacement for where the file sources are the ones within /// the netcore project so /// we don't need to copy files /// private static ILocalizedTextService GetLocalizedTextService(IServiceProvider factory) { var globalSettings = factory.GetRequiredService>(); var loggerFactory = factory.GetRequiredService(); var appCaches = factory.GetRequiredService(); var localizedTextService = new LocalizedTextService( new Lazy(() => { // get the src folder var root = TestContext.CurrentContext.TestDirectory.Split("tests")[0]; var srcFolder = Path.Combine(root, "src"); var currFolder = new DirectoryInfo(srcFolder); var uiProject = currFolder.GetDirectories("Umbraco.Web.UI", SearchOption.TopDirectoryOnly).First(); var mainLangFolder = new DirectoryInfo(Path.Combine(uiProject.FullName, globalSettings.Value.UmbracoPath.TrimStart("~/"), "config", "lang")); return new LocalizedTextServiceFileSources( loggerFactory.CreateLogger(), appCaches, mainLangFolder, Array.Empty(), new EmbeddedFileProvider(typeof(IAssemblyProvider).Assembly, "Umbraco.Cms.Core.EmbeddedResources.Lang").GetDirectoryContents(string.Empty)); }), loggerFactory.CreateLogger()); return localizedTextService; } // replace the default so there is no background index rebuilder private class TestBackgroundIndexRebuilder : ExamineIndexRebuilder { public TestBackgroundIndexRebuilder( IMainDom mainDom, IRuntimeState runtimeState, ILogger logger, IExamineManager examineManager, IEnumerable populators, IBackgroundTaskQueue backgroundTaskQueue) : base( mainDom, runtimeState, logger, examineManager, populators, backgroundTaskQueue) { } public override void RebuildIndex(string indexName, TimeSpan? delay = null, bool useBackgroundThread = true) { // noop } public override void RebuildIndexes(bool onlyEmptyIndexes, TimeSpan? delay = null, bool useBackgroundThread = true) { // noop } } private class NoopServerMessenger : IServerMessenger { public void QueueRefresh(ICacheRefresher refresher, TPayload[] payload) { } public void QueueRefresh(ICacheRefresher refresher, Func getNumericId, params T[] instances) { } public void QueueRefresh(ICacheRefresher refresher, Func getGuidId, params T[] instances) { } public void QueueRemove(ICacheRefresher refresher, Func getNumericId, params T[] instances) { } public void QueueRemove(ICacheRefresher refresher, params int[] numericIds) { } public void QueueRefresh(ICacheRefresher refresher, params int[] numericIds) { } public void QueueRefresh(ICacheRefresher refresher, params Guid[] guidIds) { } public void QueueRefreshAll(ICacheRefresher refresher) { } public void Sync() { } public void SendMessages() { } } }