// 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; using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Cache.PartialViewCacheInvalidators; 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.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(Mock.Of()); builder.Services.AddUnique(testHelper.MainDom); builder.Services.AddUnique(); #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>(); 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 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); if (!currFolder.Exists) { // When Umbraco.Integration.Tests is installed in a "consumer" Umbraco site, the src/tests path might not be there. // This replaces the folder reference with an empty folder under temp // such that the LocalizedTextServiceFileSources don't blow up from directory not found, // or reading random xml files from the base temp folder. var tempPath = Path.GetTempPath(); currFolder = new DirectoryInfo(Path.GetFullPath("Umbraco.Integration.Tests.Fake.SrcRoot", tempPath)); currFolder.Create(); } return new LocalizedTextServiceFileSources( loggerFactory.CreateLogger(), appCaches, currFolder, 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 sealed class TestBackgroundIndexRebuilder : ExamineIndexRebuilder { public TestBackgroundIndexRebuilder( IMainDom mainDom, IRuntimeState runtimeState, ILogger logger, IExamineManager examineManager, IEnumerable populators, ILongRunningOperationService longRunningOperationService) : base( mainDom, runtimeState, logger, examineManager, populators, longRunningOperationService) { } 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() { } } }