diff --git a/src/Umbraco.Core/ApplicationContext.cs b/src/Umbraco.Core/ApplicationContext.cs index db905be43f..8c7f62f980 100644 --- a/src/Umbraco.Core/ApplicationContext.cs +++ b/src/Umbraco.Core/ApplicationContext.cs @@ -1,114 +1,105 @@ using System; using System.Configuration; using System.Threading; -using System.Threading.Tasks; -using System.Web; -using System.Web.Caching; -using Umbraco.Core.Cache; using Umbraco.Core.Configuration; -using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Logging; using Umbraco.Core.ObjectResolution; -using Umbraco.Core.Profiling; using Umbraco.Core.Services; using Umbraco.Core.Sync; namespace Umbraco.Core { /// - /// the Umbraco Application context + /// Represents the Umbraco application context. /// - /// - /// one per AppDomain, represents the global Umbraco application - /// + /// Only one singleton instance per running Umbraco application (AppDomain) public class ApplicationContext : IDisposable { + private volatile bool _disposed; + private readonly ReaderWriterLockSlim _disposalLocker = new ReaderWriterLockSlim(); + private bool _isReady; + readonly ManualResetEventSlim _isReadyEvent = new ManualResetEventSlim(false); + private DatabaseContext _databaseContext; + private ServiceContext _services; + private Lazy _configured; + + // ReSharper disable once InconsistentNaming + internal string _umbracoApplicationUrl; + /// - /// Constructor + /// Initializes a new instance of the class. /// - /// - /// - /// - /// + /// A database context. + /// A service context. + /// A cache helper. + /// A logger. public ApplicationContext(DatabaseContext dbContext, ServiceContext serviceContext, CacheHelper cache, ProfilingLogger logger) { - if (dbContext == null) throw new ArgumentNullException("dbContext"); - if (serviceContext == null) throw new ArgumentNullException("serviceContext"); - if (cache == null) throw new ArgumentNullException("cache"); - if (logger == null) throw new ArgumentNullException("logger"); + if (dbContext == null) throw new ArgumentNullException(nameof(dbContext)); + if (serviceContext == null) throw new ArgumentNullException(nameof(serviceContext)); + if (cache == null) throw new ArgumentNullException(nameof(cache)); + if (logger == null) throw new ArgumentNullException(nameof(logger)); + _databaseContext = dbContext; _services = serviceContext; ApplicationCache = cache; ProfilingLogger = logger; - Init(); + Initialize(); } - /// - /// Creates a basic app context - /// - /// - /// - public ApplicationContext(CacheHelper cache, ProfilingLogger logger) + /// + /// Initializes a new instance of the class. + /// + /// A cache helper. + /// A logger. + /// For Unit Tests only. + public ApplicationContext(CacheHelper cache, ProfilingLogger logger) { - if (cache == null) throw new ArgumentNullException("cache"); - if (logger == null) throw new ArgumentNullException("logger"); + if (cache == null) throw new ArgumentNullException(nameof(cache)); + if (logger == null) throw new ArgumentNullException(nameof(logger)); + ApplicationCache = cache; ProfilingLogger = logger; - Init(); + + Initialize(); } /// - /// A method used to set and/or ensure that a global ApplicationContext singleton is created. + /// Sets and/or ensures that a global application context exists. /// - /// - /// The instance to set on the global application singleton - /// - /// If set to true and the singleton is already set, it will be replaced - /// - /// - /// This is NOT thread safe - /// + /// The application context instance. + /// A value indicating whether to replace the existing context, if any. + /// The current global application context. + /// This is NOT thread safe. For Unit Tests only. public static ApplicationContext EnsureContext(ApplicationContext appContext, bool replaceContext) { - if (Current != null) - { - if (!replaceContext) - return Current; - } - Current = appContext; - return Current; + if (Current != null && replaceContext == false) + return Current; + + return Current = appContext; } - /// - /// A method used to create and ensure that a global ApplicationContext singleton is created. - /// - /// - /// - /// - /// If set to true will replace the current singleton instance - This should only be used for unit tests or on app - /// startup if for some reason the boot manager is not the umbraco boot manager. - /// - /// - /// - /// - /// - /// This is NOT thread safe - /// + /// + /// Sets and/or ensures that a global application context exists. + /// + /// A cache helper. + /// A logger. + /// A database context. + /// A service context. + /// A value indicating whether to replace the existing context, if any. + /// The current global application context. + /// This is NOT thread safe. For Unit Tests only. public static ApplicationContext EnsureContext(DatabaseContext dbContext, ServiceContext serviceContext, CacheHelper cache, ProfilingLogger logger, bool replaceContext) { - if (Current != null) - { - if (!replaceContext) + if (Current != null && replaceContext == false) return Current; - } - var ctx = new ApplicationContext(dbContext, serviceContext, cache, logger); - Current = ctx; - return Current; + + return Current = new ApplicationContext(dbContext, serviceContext, cache, logger); } /// - /// Singleton accessor + /// Gets the current global application context. /// public static ApplicationContext Current { get; internal set; } @@ -121,20 +112,30 @@ namespace Umbraco.Core public CacheHelper ApplicationCache { get; private set; } /// - /// Exposes the global ProfilingLogger - this should generally not be accessed via the UmbracoContext and should normally just be exposed - /// on most base classes or injected with IoC + /// Gets the profiling logger. /// - public ProfilingLogger ProfilingLogger { get; private set; } + public ProfilingLogger ProfilingLogger { get; } - // IsReady is set to true by the boot manager once it has successfully booted - // note - the original umbraco module checks on content.Instance in umbraco.dll - // now, the boot task that setup the content store ensures that it is ready - bool _isReady = false; - readonly ManualResetEventSlim _isReadyEvent = new ManualResetEventSlim(false); - private DatabaseContext _databaseContext; - private ServiceContext _services; + /// + /// Gets the MainDom. + /// + internal MainDom MainDom { get; private set; } - public bool IsReady + /// + /// Gets a value indicating whether the application is configured. + /// + /// Meaning: installed and no need to upgrade anything. + public virtual bool IsConfigured => _configured.Value; + + /// + /// Gets a value indicating whether the application is ready. + /// + /// Meaning: ready to run, boot has completed, though maybe the application is not configured. + /// IsReady is set to true by the boot manager once it has successfully booted. The original + /// Umbraco module checked on content.Instance, now the boot task that sets the content + /// store ensures that it is ready. + /// + public bool IsReady { get { @@ -142,50 +143,49 @@ namespace Umbraco.Core } internal set { - AssertIsNotReady(); - _isReady = value; + if (IsReady) + throw new Exception("ApplicationContext has already been initialized."); + if (value == false) + throw new Exception("Value must be true."); + + _isReady = true; _isReadyEvent.Set(); } } + /// + /// Blocks until the application is ready. + /// + /// The time to wait, or -1 to wait indefinitely. + /// A value indicating whether the application is ready. public bool WaitForReady(int timeout) { return _isReadyEvent.WaitHandle.WaitOne(timeout); } - - // notes - // GlobalSettings.ConfigurationStatus returns the value that's in the web.config, so it's the "configured version" - // GlobalSettings.CurrentVersion returns the hard-coded "current version" - // the system is configured if they match - // if they don't, install runs, updates web.config (presumably) and updates GlobalSettings.ConfiguredStatus - - public bool IsConfigured - { - get { return _configured.Value; } - } - - /// - /// If the db is configured, there is a database context and there is an umbraco schema, but we are not 'configured' , then it means we are upgrading + /// + /// Gets a value indicating whether the application is upgrading. /// + /// Meaning: the database is configured and the database context has access to an existing Umbraco schema, + /// however we are not 'configured' because we still need to upgrade. public bool IsUpgrading { get { - if (IsConfigured == false - && DatabaseContext != null - && DatabaseContext.IsDatabaseConfigured) - { - var schemaresult = DatabaseContext.ValidateDatabaseSchema(); - if (schemaresult.ValidTables.Count > 0) return true; - } + if (IsConfigured // configured already + || DatabaseContext == null // no database context + || DatabaseContext.IsDatabaseConfigured == false // database is not configured + || DatabaseContext.CanConnect == false) // database cannot connect + return false; - return false; + // upgrading if we have some valid tables (else, no schema, need to install) + var schemaresult = DatabaseContext.ValidateDatabaseSchema(); + return schemaresult.ValidTables.Count > 0; } } /// - /// The application url. + /// Gets the application Url. /// /// /// The application url is the url that should be used by services to talk to the application, @@ -215,64 +215,63 @@ namespace Umbraco.Core } } - // ReSharper disable once InconsistentNaming - internal string _umbracoApplicationUrl; - - private Lazy _configured; - internal MainDom MainDom { get; private set; } - - private void Init() + private void Initialize() { MainDom = new MainDom(ProfilingLogger.Logger); MainDom.Acquire(); - - //Create the lazy value to resolve whether or not the application is 'configured' + + ResetConfigured(); + } + + /// + /// Resets the IsConfigured value, which will then be discovered again. + /// + /// For Unit Tests usage, though it is harmless. + public void ResetConfigured() + { + // create the lazy value to resolve whether or not the application is 'configured' + // meaning: installed and no need to upgrade anything _configured = new Lazy(() => { try { - var configStatus = ConfigurationStatus; - var currentVersion = UmbracoVersion.GetSemanticVersion(); + var configStatus = ConfigurationStatus; // the value in the web.config + var currentVersion = UmbracoVersion.GetSemanticVersion(); // the hard-coded current version of the binaries that are executing - var ok = - //we are not configured if this is null - string.IsNullOrWhiteSpace(configStatus) == false - //they must match - && configStatus == currentVersion; - - if (ok) + // if we have no value in web.config or value differs, we are not configured yet + if (string.IsNullOrWhiteSpace(configStatus) || configStatus != currentVersion) { - //The versions are the same in config, but are they the same in the database. We can only check this - // if we have a db context available, if we don't then we are not installed anyways - if (DatabaseContext.IsDatabaseConfigured && DatabaseContext.CanConnect) - { - var found = Services.MigrationEntryService.FindEntry(GlobalSettings.UmbracoMigrationName, UmbracoVersion.GetSemanticVersion()); - if (found == null) - { - //we haven't executed this migration in this environment, so even though the config versions match, - // this db has not been updated. - ProfilingLogger.Logger.Debug(string.Format("The migration for version: '{0} has not been executed, there is no record in the database", currentVersion.ToSemanticString())); - ok = false; - } - } - } - else - { - ProfilingLogger.Logger.Debug(string.Format("CurrentVersion different from configStatus: '{0}','{1}'", currentVersion.ToSemanticString(), configStatus)); + ProfilingLogger.Logger.Debug($"CurrentVersion different from configStatus: '{currentVersion.ToSemanticString()}','{configStatus}'."); + return false; } - return ok; + // versions match, now look for database state and migrations + // which requires that we do have a database that we can connect to + if (DatabaseContext.IsDatabaseConfigured == false || DatabaseContext.CanConnect == false) + { + ProfilingLogger.Logger.Debug("Database is not configured, or could not connect to the database."); + return false; + } + + // look for a migration entry for the current version + var entry = Services.MigrationEntryService.FindEntry(GlobalSettings.UmbracoMigrationName, UmbracoVersion.GetSemanticVersion()); + if (entry != null) + return true; // all clear! + + // even though the versions match, the db has not been properly upgraded + ProfilingLogger.Logger.Debug($"The migration for version: '{currentVersion.ToSemanticString()} has not been executed, there is no record in the database."); + return false; } catch (Exception ex) { - LogHelper.Error("Error determining if application is configured, returning false", ex); + LogHelper.Error("Error determining if application is configured, returning false.", ex); return false; } + }); + } - }); - } - - private string ConfigurationStatus + // gets the configuration status, ie the version that's in web.config + private string ConfigurationStatus { get { @@ -282,102 +281,91 @@ namespace Umbraco.Core } catch { - return String.Empty; + return string.Empty; } - } + } } - private void AssertIsNotReady() - { - if (this.IsReady) - throw new Exception("ApplicationContext has already been initialized."); - } - /// - /// Gets the current DatabaseContext + /// Gets the current database context. /// - /// - /// Internal set is generally only used for unit tests - /// public DatabaseContext DatabaseContext { get { if (_databaseContext == null) - throw new InvalidOperationException("The DatabaseContext has not been set on the ApplicationContext"); + throw new InvalidOperationException("The DatabaseContext has not been set on the ApplicationContext."); return _databaseContext; } + // INTERNAL FOR UNIT TESTS internal set { _databaseContext = value; } } - + /// - /// Gets the current ServiceContext + /// Gets the current service context. /// - /// - /// Internal set is generally only used for unit tests - /// public ServiceContext Services { get { if (_services == null) - throw new InvalidOperationException("The ServiceContext has not been set on the ApplicationContext"); + throw new InvalidOperationException("The ServiceContext has not been set on the ApplicationContext."); return _services; } - internal set { _services = value; } + // INTERNAL FOR UNIT TESTS + internal set { _services = value; } } + /// + /// Gets the server role. + /// + /// internal ServerRole GetCurrentServerRole() { var registrar = ServerRegistrarResolver.Current.Registrar as IServerRegistrar2; - return registrar == null ? ServerRole.Unknown : registrar.GetCurrentServerRole(); + return registrar?.GetCurrentServerRole() ?? ServerRole.Unknown; } - private volatile bool _disposed; - private readonly ReaderWriterLockSlim _disposalLocker = new ReaderWriterLockSlim(); - /// - /// This will dispose and reset all resources used to run the application + /// Disposes the application context. /// - /// - /// IMPORTANT: Never dispose this object if you require the Umbraco application to run, disposing this object + /// Do not this object if you require the Umbraco application to run. Disposing this object /// is generally used for unit testing and when your application is shutting down after you have booted Umbraco. /// void IDisposable.Dispose() { - // Only operate if we haven't already disposed if (_disposed) return; using (new WriteLock(_disposalLocker)) { - // Check again now we're inside the lock + // double check... bah... if (_disposed) return; - //clear the cache + // clear the cache if (ApplicationCache != null) { ApplicationCache.RuntimeCache.ClearAllCache(); ApplicationCache.IsolatedRuntimeCache.ClearAllCaches(); } - //reset all resolvers + + // reset all resolvers ResolverCollection.ResetAll(); - //reset resolution itself (though this should be taken care of by resetting any of the resolvers above) + + // reset resolution itself (though this should be taken care of by resetting any of the resolvers above) Resolution.Reset(); - - //reset the instance objects - this.ApplicationCache = null; + + // reset the instance objects + ApplicationCache = null; if (_databaseContext != null) //need to check the internal field here { - if (DatabaseContext.IsDatabaseConfigured && DatabaseContext.Database != null) - { - DatabaseContext.Database.Dispose(); - } + if (DatabaseContext.IsDatabaseConfigured) + DatabaseContext.Database?.Dispose(); } - this.DatabaseContext = null; - this.Services = null; - this._isReady = false; //set the internal field - - // Indicate that the instance has been disposed. + + DatabaseContext = null; + Services = null; + _isReady = false; //set the internal field + _disposed = true; } } diff --git a/src/Umbraco.Core/DatabaseContext.cs b/src/Umbraco.Core/DatabaseContext.cs index c77f3f2002..11034023a0 100644 --- a/src/Umbraco.Core/DatabaseContext.cs +++ b/src/Umbraco.Core/DatabaseContext.cs @@ -63,7 +63,7 @@ namespace Umbraco.Core /// This should not be used for CRUD operations or queries against the /// standard Umbraco tables! Use the Public services for that. /// - public virtual UmbracoDatabase Database => _factory.GetDatabase(); + public UmbracoDatabase Database => _factory.GetDatabase(); /// /// Gets a value indicating whether the database is configured, ie whether it exists @@ -75,7 +75,7 @@ namespace Umbraco.Core /// /// Gets a value indicating whether it is possible to connect to the database. /// - public virtual bool CanConnect + public bool CanConnect { get { diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/RemoveMasterContentTypeColumn.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/RemoveMasterContentTypeColumn.cs index ab423a8e34..e21cab814f 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/RemoveMasterContentTypeColumn.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSix/RemoveMasterContentTypeColumn.cs @@ -15,7 +15,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSix public override void Up() { //NOTE Don't think we can remove this column yet as it seems to be used by some starterkits - IfDatabase(DatabaseType.SQLCe, DatabaseType.SqlServer2005) + IfDatabase(DatabaseType.SQLCe, DatabaseType.SqlServer2008) .Delete.DefaultConstraint().OnTable("cmsContentType").OnColumn("masterContentType"); Delete.Column("masterContentType").FromTable("cmsContentType"); diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/SqlSyntaxProviders.cs b/src/Umbraco.Core/Persistence/SqlSyntax/SqlSyntaxProviders.cs deleted file mode 100644 index f5e118e653..0000000000 --- a/src/Umbraco.Core/Persistence/SqlSyntax/SqlSyntaxProviders.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Collections.Generic; -using Umbraco.Core.Logging; - -namespace Umbraco.Core.Persistence.SqlSyntax -{ - // fixme - this exists ONLY for unit tests at the moment! - public sealed class SqlSyntaxProviders - { - public static IEnumerable GetDefaultProviders(ILogger logger) - { - return new ISqlSyntaxProvider[] - { - new MySqlSyntaxProvider(logger), - new SqlCeSyntaxProvider(), - new SqlServerSyntaxProvider(new Lazy(() => null)) - }; - } - } -} \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/UmbracoDatabase.cs b/src/Umbraco.Core/Persistence/UmbracoDatabase.cs index 9ad14529f6..50c89aedb7 100644 --- a/src/Umbraco.Core/Persistence/UmbracoDatabase.cs +++ b/src/Umbraco.Core/Persistence/UmbracoDatabase.cs @@ -79,6 +79,17 @@ namespace Umbraco.Core.Persistence EnableSqlTrace = false; } + // INTERNAL FOR UNIT TESTS + internal UmbracoDatabase(DbConnection connection, + ISqlSyntaxProvider sqlSyntax, DatabaseType databaseType, DbProviderFactory provider, + ILogger logger) + : base(connection, databaseType, provider, DefaultIsolationLevel) + { + SqlSyntax = sqlSyntax; + _logger = logger; + EnableSqlTrace = false; + } + // fixme: that could be an extension method of IUmbracoDatabaseConfig public Sql Sql() { diff --git a/src/Umbraco.Core/Persistence/UnitOfWork/NPocoUnitOfWorkProvider.cs b/src/Umbraco.Core/Persistence/UnitOfWork/NPocoUnitOfWorkProvider.cs index d6ac4cc36f..17e05dc3ed 100644 --- a/src/Umbraco.Core/Persistence/UnitOfWork/NPocoUnitOfWorkProvider.cs +++ b/src/Umbraco.Core/Persistence/UnitOfWork/NPocoUnitOfWorkProvider.cs @@ -1,4 +1,6 @@ -using Umbraco.Core.Configuration; +using System; +using System.Collections.Generic; +using Umbraco.Core.Configuration; using Umbraco.Core.Logging; using Umbraco.Core.Persistence.SqlSyntax; @@ -31,9 +33,22 @@ namespace Umbraco.Core.Persistence.UnitOfWork /// with the default connection name, and default sql syntax providers. /// internal NPocoUnitOfWorkProvider(ILogger logger) - : this(new DefaultDatabaseFactory(GlobalSettings.UmbracoConnectionName, SqlSyntaxProviders.GetDefaultProviders(logger), logger)) + : this(new DefaultDatabaseFactory(GlobalSettings.UmbracoConnectionName, GetDefaultSqlSyntaxProviders(logger), logger)) { } + // this should NOT be here, all tests should supply the appropriate providers, + // however the above ctor is used in hundreds of tests at the moment, so... + // will refactor later + private static IEnumerable GetDefaultSqlSyntaxProviders(ILogger logger) + { + return new ISqlSyntaxProvider[] + { + new MySqlSyntaxProvider(logger), + new SqlCeSyntaxProvider(), + new SqlServerSyntaxProvider(new Lazy(() => null)) + }; + } + #region Implement IUnitOfWorkProvider /// diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 5c4591e4df..0ed691e96e 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -1043,7 +1043,6 @@ - diff --git a/src/Umbraco.Tests/ApplicationContextTests.cs b/src/Umbraco.Tests/ApplicationContextTests.cs index 61dabf6e3b..b27b496fbb 100644 --- a/src/Umbraco.Tests/ApplicationContextTests.cs +++ b/src/Umbraco.Tests/ApplicationContextTests.cs @@ -11,6 +11,7 @@ using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Profiling; using Umbraco.Core.Services; +using Umbraco.Tests.TestHelpers; namespace Umbraco.Tests { @@ -26,15 +27,15 @@ namespace Umbraco.Tests migrationEntryService.Setup(x => x.FindEntry(It.IsAny(), It.IsAny())) .Returns(Mock.Of()); - var dbCtx = new Mock(Mock.Of(), Mock.Of(), new SqlCeSyntaxProvider(), "test"); - dbCtx.Setup(x => x.IsDatabaseConfigured).Returns(true); - dbCtx.Setup(x => x.CanConnect).Returns(true); + var databaseFactory = TestObjects.GetIDatabaseFactoryMock(); + var logger = Mock.Of(); + var dbCtx = new Mock(databaseFactory, logger); var appCtx = new ApplicationContext( dbCtx.Object, - new ServiceContext(migrationEntryService:migrationEntryService.Object), + new ServiceContext(migrationEntryService:migrationEntryService.Object), CacheHelper.CreateDisabledCacheHelper(), - new ProfilingLogger(Mock.Of(), Mock.Of())); + new ProfilingLogger(logger, Mock.Of())); Assert.IsTrue(appCtx.IsConfigured); } @@ -48,15 +49,15 @@ namespace Umbraco.Tests migrationEntryService.Setup(x => x.FindEntry(It.IsAny(), It.IsAny())) .Returns((IMigrationEntry)null); - var dbCtx = new Mock(Mock.Of(), Mock.Of(), new SqlCeSyntaxProvider(), "test"); - dbCtx.Setup(x => x.IsDatabaseConfigured).Returns(true); - dbCtx.Setup(x => x.CanConnect).Returns(true); + var databaseFactory = TestObjects.GetIDatabaseFactoryMock(); + var logger = Mock.Of(); + var dbCtx = new Mock(databaseFactory, logger); var appCtx = new ApplicationContext( dbCtx.Object, new ServiceContext(migrationEntryService: migrationEntryService.Object), CacheHelper.CreateDisabledCacheHelper(), - new ProfilingLogger(Mock.Of(), Mock.Of())); + new ProfilingLogger(logger, Mock.Of())); Assert.IsFalse(appCtx.IsConfigured); } @@ -68,15 +69,15 @@ namespace Umbraco.Tests var migrationEntryService = new Mock(); - var dbCtx = new Mock(Mock.Of(), Mock.Of(), new SqlCeSyntaxProvider(), "test"); - dbCtx.Setup(x => x.IsDatabaseConfigured).Returns(true); - dbCtx.Setup(x => x.CanConnect).Returns(true); + var databaseFactory = TestObjects.GetIDatabaseFactoryMock(); + var logger = Mock.Of(); + var dbCtx = new Mock(databaseFactory, logger); var appCtx = new ApplicationContext( dbCtx.Object, new ServiceContext(migrationEntryService: migrationEntryService.Object), CacheHelper.CreateDisabledCacheHelper(), - new ProfilingLogger(Mock.Of(), Mock.Of())); + new ProfilingLogger(logger, Mock.Of())); Assert.IsFalse(appCtx.IsConfigured); } @@ -88,9 +89,9 @@ namespace Umbraco.Tests var migrationEntryService = new Mock(); - var dbCtx = new Mock(Mock.Of(), Mock.Of(), new SqlCeSyntaxProvider(), "test"); - dbCtx.Setup(x => x.IsDatabaseConfigured).Returns(false); - dbCtx.Setup(x => x.CanConnect).Returns(true); + var databaseFactory = TestObjects.GetIDatabaseFactoryMock(false); + var logger = Mock.Of(); + var dbCtx = new Mock(databaseFactory, logger); var appCtx = new ApplicationContext( dbCtx.Object, @@ -108,9 +109,9 @@ namespace Umbraco.Tests var migrationEntryService = new Mock(); - var dbCtx = new Mock(Mock.Of(), Mock.Of(), new SqlCeSyntaxProvider(), "test"); - dbCtx.Setup(x => x.IsDatabaseConfigured).Returns(true); - dbCtx.Setup(x => x.CanConnect).Returns(false); + var databaseFactory = TestObjects.GetIDatabaseFactoryMock(true, false); + var logger = Mock.Of(); + var dbCtx = new Mock(databaseFactory, logger); var appCtx = new ApplicationContext( dbCtx.Object, diff --git a/src/Umbraco.Tests/Migrations/Upgrades/ValidateV7UpgradeTest.cs b/src/Umbraco.Tests/Migrations/Upgrades/ValidateV7UpgradeTest.cs index c21da772b6..31494ec8a3 100644 --- a/src/Umbraco.Tests/Migrations/Upgrades/ValidateV7UpgradeTest.cs +++ b/src/Umbraco.Tests/Migrations/Upgrades/ValidateV7UpgradeTest.cs @@ -46,7 +46,7 @@ namespace Umbraco.Tests.Migrations.Upgrades [Test] public void Validate_AddIndexToCmsMacroPropertyTable() { - var migration = new AddIndexToCmsMacroTable(true, _logger); + var migration = new AddIndexToCmsMacroPropertyTable(true, _logger); var migrationContext = new MigrationContext(_database, _logger); migration.GetUpExpressions(migrationContext); diff --git a/src/Umbraco.Tests/MockTests.cs b/src/Umbraco.Tests/MockTests.cs index 353923ec30..3b4f72aa4e 100644 --- a/src/Umbraco.Tests/MockTests.cs +++ b/src/Umbraco.Tests/MockTests.cs @@ -39,25 +39,30 @@ namespace Umbraco.Tests [Test] public void Can_Create_Service_Context() { - var svcCtx = MockHelper.GetMockedServiceContext(); + var svcCtx = TestObjects.GetServiceContextMock(); Assert.Pass(); } [Test] public void Can_Create_Db_Context() { - var dbCtx = new DatabaseContext(new Mock().Object, Mock.Of()); + var databaseFactory = TestObjects.GetIDatabaseFactoryMock(); + var logger = Mock.Of(); + var dbCtx = new DatabaseContext(databaseFactory, logger); Assert.Pass(); } [Test] public void Can_Create_App_Context_With_Services() { + var databaseFactory = TestObjects.GetIDatabaseFactoryMock(); + var logger = Mock.Of(); + var appCtx = new ApplicationContext( - new DatabaseContext(new Mock().Object, Mock.Of()), - MockHelper.GetMockedServiceContext(), + new DatabaseContext(databaseFactory, logger), + TestObjects.GetServiceContextMock(), CacheHelper.CreateDisabledCacheHelper(), - new ProfilingLogger(Mock.Of(), Mock.Of())); + new ProfilingLogger(logger, Mock.Of())); Assert.Pass(); } diff --git a/src/Umbraco.Tests/Models/Mapping/ContentTypeModelMappingTests.cs b/src/Umbraco.Tests/Models/Mapping/ContentTypeModelMappingTests.cs index 6839d6f2c6..50df11aea4 100644 --- a/src/Umbraco.Tests/Models/Mapping/ContentTypeModelMappingTests.cs +++ b/src/Umbraco.Tests/Models/Mapping/ContentTypeModelMappingTests.cs @@ -43,9 +43,11 @@ namespace Umbraco.Tests.Models.Mapping var nullCacheHelper = CacheHelper.CreateDisabledCacheHelper(); var logger = Mock.Of(); + var databaseFactory = TestObjects.GetIDatabaseFactoryMock(); + //Create an app context using mocks var appContext = new ApplicationContext( - new DatabaseContext(Mock.Of(), logger), + new DatabaseContext(databaseFactory, logger), //Create service context using mocks new ServiceContext( diff --git a/src/Umbraco.Tests/Persistence/DatabaseContextTests.cs b/src/Umbraco.Tests/Persistence/DatabaseContextTests.cs index 40063cdd8b..3d1e4a268e 100644 --- a/src/Umbraco.Tests/Persistence/DatabaseContextTests.cs +++ b/src/Umbraco.Tests/Persistence/DatabaseContextTests.cs @@ -33,28 +33,12 @@ namespace Umbraco.Tests.Persistence _logger = Mock.Of(); var dbFactory = new DefaultDatabaseFactory(Core.Configuration.GlobalSettings.UmbracoConnectionName, _sqlSyntaxProviders, _logger); _dbContext = new DatabaseContext(dbFactory, _logger); - - // unfortunately we have to set this up because the NPocoExtensions require singleton access - // fixme - there is no need for this! - /* - ApplicationContext.Current = new ApplicationContext( - CacheHelper.CreateDisabledCacheHelper(), - new ProfilingLogger(Mock.Of(), Mock.Of())) - { - DatabaseContext = _dbContext, - IsReady = true - }; - */ } [TearDown] public void TearDown() { _dbContext = null; - // fixme - no need - /* - ApplicationContext.Current = null; - */ } [Test] @@ -95,7 +79,7 @@ namespace Umbraco.Tests.Persistence engine.CreateDatabase(); // re-create the database factory and database context with proper connection string - var dbFactory = new DefaultDatabaseFactory(engine.LocalConnectionString, _sqlSyntaxProviders, _logger); + var dbFactory = new DefaultDatabaseFactory(engine.LocalConnectionString, Constants.DbProviderNames.SqlCe, _sqlSyntaxProviders, _logger); _dbContext = new DatabaseContext(dbFactory, _logger); // create application context diff --git a/src/Umbraco.Tests/Persistence/Migrations/MigrationStartupHandlerTests.cs b/src/Umbraco.Tests/Persistence/Migrations/MigrationStartupHandlerTests.cs index ed2bbb20d9..a59a500ac6 100644 --- a/src/Umbraco.Tests/Persistence/Migrations/MigrationStartupHandlerTests.cs +++ b/src/Umbraco.Tests/Persistence/Migrations/MigrationStartupHandlerTests.cs @@ -17,6 +17,7 @@ using Umbraco.Core.Persistence.Migrations; using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Profiling; using Umbraco.Core.Services; +using Umbraco.Tests.TestHelpers; using Umbraco.Web.Strategies.Migrations; namespace Umbraco.Tests.Persistence.Migrations @@ -24,53 +25,18 @@ namespace Umbraco.Tests.Persistence.Migrations [TestFixture] public class MigrationStartupHandlerTests { - // NPoco wants a DbConnection and NOT an IDbConnection - // and DbConnection is hard to mock... - private class MockConnection : DbConnection - { - protected override DbTransaction BeginDbTransaction(IsolationLevel isolationLevel) - { - return Mock.Of(); // enough here - } - - public override void Close() - { - throw new NotImplementedException(); - } - - public override void ChangeDatabase(string databaseName) - { - throw new NotImplementedException(); - } - - public override void Open() - { - throw new NotImplementedException(); - } - - public override string ConnectionString { get; set; } - - protected override DbCommand CreateDbCommand() - { - throw new NotImplementedException(); - } - - public override string Database { get; } - public override string DataSource { get; } - public override string ServerVersion { get; } - public override ConnectionState State => ConnectionState.Open; // else NPoco reopens - } - [Test] public void Executes_For_Any_Product_Name_When_Not_Specified() { + var logger = Mock.Of(); + var changed1 = new Args { CountExecuted = 0 }; var testHandler1 = new TestMigrationHandler(changed1); - testHandler1.OnApplicationStarting(Mock.Of(), new ApplicationContext(CacheHelper.CreateDisabledCacheHelper(), new ProfilingLogger(Mock.Of(), Mock.Of()))); + testHandler1.OnApplicationStarting(Mock.Of(), new ApplicationContext(CacheHelper.CreateDisabledCacheHelper(), new ProfilingLogger(logger, Mock.Of()))); - var db = new UmbracoDatabase("cstr", new SqlCeSyntaxProvider(), DatabaseType.SQLCe, DbProviderFactories.GetFactory(Constants.DbProviderNames.SqlCe), Mock.Of()); + var db = TestObjects.GetUmbracoSqlCeDatabase(logger); - var runner1 = new MigrationRunner(Mock.Of(), Mock.Of(), Mock.Of(), new SemVersion(1), new SemVersion(2), "Test1", + var runner1 = new MigrationRunner(Mock.Of(), Mock.Of(), logger, new SemVersion(1), new SemVersion(2), "Test1", new IMigration[] { Mock.Of() }); var result1 = runner1.Execute(db /*, false*/); Assert.AreEqual(1, changed1.CountExecuted); @@ -79,22 +45,25 @@ namespace Umbraco.Tests.Persistence.Migrations [Test] public void Executes_Only_For_Specified_Product_Name() { + var logger = Mock.Of(); + var changed1 = new Args { CountExecuted = 0}; var testHandler1 = new TestMigrationHandler("Test1", changed1); - testHandler1.OnApplicationStarting(Mock.Of(), new ApplicationContext(CacheHelper.CreateDisabledCacheHelper(), new ProfilingLogger(Mock.Of(), Mock.Of()))); + testHandler1.OnApplicationStarting(Mock.Of(), new ApplicationContext(CacheHelper.CreateDisabledCacheHelper(), new ProfilingLogger(logger, Mock.Of()))); + var changed2 = new Args { CountExecuted = 0 }; var testHandler2 = new TestMigrationHandler("Test2", changed2); - testHandler2.OnApplicationStarting(Mock.Of(), new ApplicationContext(CacheHelper.CreateDisabledCacheHelper(), new ProfilingLogger(Mock.Of(), Mock.Of()))); + testHandler2.OnApplicationStarting(Mock.Of(), new ApplicationContext(CacheHelper.CreateDisabledCacheHelper(), new ProfilingLogger(logger, Mock.Of()))); - var db = new UmbracoDatabase("cstr", new SqlCeSyntaxProvider(), DatabaseType.SQLCe, DbProviderFactories.GetFactory(Constants.DbProviderNames.SqlCe), Mock.Of()); + var db = TestObjects.GetUmbracoSqlCeDatabase(logger); - var runner1 = new MigrationRunner(Mock.Of(), Mock.Of(), Mock.Of(), new SemVersion(1), new SemVersion(2), "Test1", + var runner1 = new MigrationRunner(Mock.Of(), Mock.Of(), logger, new SemVersion(1), new SemVersion(2), "Test1", new IMigration[] { Mock.Of()}); var result1 = runner1.Execute(db /*, false*/); Assert.AreEqual(1, changed1.CountExecuted); Assert.AreEqual(0, changed2.CountExecuted); - var runner2 = new MigrationRunner(Mock.Of(), Mock.Of(), Mock.Of(), new SemVersion(1), new SemVersion(2), "Test2", + var runner2 = new MigrationRunner(Mock.Of(), Mock.Of(), logger, new SemVersion(1), new SemVersion(2), "Test2", new IMigration[] { Mock.Of() }); var result2 = runner2.Execute(db /*, false*/); Assert.AreEqual(1, changed1.CountExecuted); diff --git a/src/Umbraco.Tests/Persistence/SyntaxProvider/SqlCeSyntaxProviderTests.cs b/src/Umbraco.Tests/Persistence/SyntaxProvider/SqlCeSyntaxProviderTests.cs index ed06a5e003..72e58295a6 100644 --- a/src/Umbraco.Tests/Persistence/SyntaxProvider/SqlCeSyntaxProviderTests.cs +++ b/src/Umbraco.Tests/Persistence/SyntaxProvider/SqlCeSyntaxProviderTests.cs @@ -100,7 +100,7 @@ WHERE (([umbracoNode].[nodeObjectType] = @0))) x)".Replace(Environment.NewLine, { var sqlSyntax = new SqlServerSyntaxProvider(new Lazy(() => null)); var logger = Mock.Of(); - var db = new UmbracoDatabase("cstr", sqlSyntax, DatabaseType.SqlServer2005, DbProviderFactories.GetFactory(Constants.DbProviderNames.SqlServer), logger); + var db = TestObjects.GetUmbracoSqlServerDatabase(logger); var context = new MigrationContext(db, logger); var createExpression = new CreateIndexExpression(context, new []{ DatabaseType.SqlServer2005 }) { @@ -116,7 +116,7 @@ WHERE (([umbracoNode].[nodeObjectType] = @0))) x)".Replace(Environment.NewLine, { var sqlSyntax = new SqlServerSyntaxProvider(new Lazy(() => null)); var logger = Mock.Of(); - var db = new UmbracoDatabase("cstr", sqlSyntax, DatabaseType.SqlServer2005, DbProviderFactories.GetFactory(Constants.DbProviderNames.SqlServer), logger); + var db = TestObjects.GetUmbracoSqlServerDatabase(logger); var context = new MigrationContext(db, logger); var createExpression = new CreateIndexExpression(context, new[] { DatabaseType.SqlServer2005 }) { @@ -132,7 +132,7 @@ WHERE (([umbracoNode].[nodeObjectType] = @0))) x)".Replace(Environment.NewLine, { var sqlSyntax = new SqlServerSyntaxProvider(new Lazy(() => null)); var logger = Mock.Of(); - var db = new UmbracoDatabase("cstr", sqlSyntax, DatabaseType.SqlServer2005, DbProviderFactories.GetFactory(Constants.DbProviderNames.SqlServer), logger); + var db = TestObjects.GetUmbracoSqlServerDatabase(logger); var context = new MigrationContext(db, logger); var createExpression = new CreateIndexExpression(context, new[] { DatabaseType.SqlServer2005 }) { diff --git a/src/Umbraco.Tests/Routing/UrlRoutingTestBase.cs b/src/Umbraco.Tests/Routing/UrlRoutingTestBase.cs index 18d4e1c080..fe17ea20da 100644 --- a/src/Umbraco.Tests/Routing/UrlRoutingTestBase.cs +++ b/src/Umbraco.Tests/Routing/UrlRoutingTestBase.cs @@ -35,7 +35,7 @@ namespace Umbraco.Tests.Routing protected ServiceContext GetServiceContext(IUmbracoSettingsSection umbracoSettings, ILogger logger) { //get the mocked service context to get the mocked domain service - var svcCtx = MockHelper.GetMockedServiceContext(); + var svcCtx = TestObjects.GetServiceContextMock(); var domainService = Mock.Get(svcCtx.DomainService); //setup mock domain service @@ -60,8 +60,9 @@ namespace Umbraco.Tests.Routing protected override void SetupApplicationContext() { var settings = SettingsForTests.GetDefault(); + var databaseFactory = TestObjects.GetIDatabaseFactoryMock(); ApplicationContext.Current = new ApplicationContext( - new DatabaseContext(Mock.Of(), Logger), + new DatabaseContext(databaseFactory, Logger), GetServiceContext(settings, Logger), CacheHelper, ProfilingLogger) diff --git a/src/Umbraco.Tests/Security/BackOfficeCookieManagerTests.cs b/src/Umbraco.Tests/Security/BackOfficeCookieManagerTests.cs index 320ab400d3..9536813b1c 100644 --- a/src/Umbraco.Tests/Security/BackOfficeCookieManagerTests.cs +++ b/src/Umbraco.Tests/Security/BackOfficeCookieManagerTests.cs @@ -28,19 +28,19 @@ namespace Umbraco.Tests.Security //should force app ctx to show not-configured ConfigurationManager.AppSettings.Set("umbracoConfigurationStatus", ""); - var dbCtx = new Mock(Mock.Of(), Mock.Of(), Mock.Of(), "test"); - dbCtx.Setup(x => x.IsDatabaseConfigured).Returns(false); + var databaseFactory = TestObjects.GetIDatabaseFactoryMock(false); + var dbCtx = new Mock(databaseFactory, Mock.Of()); var appCtx = new ApplicationContext( dbCtx.Object, - MockHelper.GetMockedServiceContext(), + TestObjects.GetServiceContextMock(), CacheHelper.CreateDisabledCacheHelper(), new ProfilingLogger(Mock.Of(), Mock.Of())); - + var umbCtx = UmbracoContext.CreateContext( - Mock.Of(), - appCtx, - new WebSecurity(Mock.Of(), appCtx), + Mock.Of(), + appCtx, + new WebSecurity(Mock.Of(), appCtx), Mock.Of(), new List(), false); var mgr = new BackOfficeCookieManager(Mock.Of(accessor => accessor.Value == umbCtx)); @@ -53,12 +53,12 @@ namespace Umbraco.Tests.Security [Test] public void ShouldAuthenticateRequest_When_Configured() { - var dbCtx = new Mock(Mock.Of(), Mock.Of(), Mock.Of(), "test"); - dbCtx.Setup(x => x.IsDatabaseConfigured).Returns(true); + var databaseFactory = TestObjects.GetIDatabaseFactoryMock(); + var dbCtx = new Mock(databaseFactory, Mock.Of()); var appCtx = new ApplicationContext( dbCtx.Object, - MockHelper.GetMockedServiceContext(), + TestObjects.GetServiceContextMock(), CacheHelper.CreateDisabledCacheHelper(), new ProfilingLogger(Mock.Of(), Mock.Of())); @@ -74,7 +74,7 @@ namespace Umbraco.Tests.Security request.Setup(owinRequest => owinRequest.Uri).Returns(new Uri("http://localhost/umbraco")); var result = mgr.ShouldAuthenticateRequest( - Mock.Of(context => context.Request == request.Object), + Mock.Of(context => context.Request == request.Object), new Uri("http://localhost/umbraco")); Assert.IsTrue(result); diff --git a/src/Umbraco.Tests/Services/ContentServiceTests.cs b/src/Umbraco.Tests/Services/ContentServiceTests.cs index 274b644738..ca965eaca0 100644 --- a/src/Umbraco.Tests/Services/ContentServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentServiceTests.cs @@ -1333,7 +1333,7 @@ namespace Umbraco.Tests.Services [Test] public void Can_Save_Lazy_Content() { - var databaseFactory = new DefaultDatabaseFactory(Umbraco.Core.Configuration.GlobalSettings.UmbracoConnectionName, SqlSyntaxProviders.GetDefaultProviders(Logger), Logger); + var databaseFactory = new DefaultDatabaseFactory(Umbraco.Core.Configuration.GlobalSettings.UmbracoConnectionName, TestObjects.GetDefaultSqlSyntaxProviders(Logger), Logger); var provider = new NPocoUnitOfWorkProvider(databaseFactory); var unitOfWork = provider.GetUnitOfWork(); var contentType = ServiceContext.ContentTypeService.GetContentType("umbTextpage"); diff --git a/src/Umbraco.Tests/Services/ThreadSafetyServiceTest.cs b/src/Umbraco.Tests/Services/ThreadSafetyServiceTest.cs index 1b7037a601..bea1110155 100644 --- a/src/Umbraco.Tests/Services/ThreadSafetyServiceTest.cs +++ b/src/Umbraco.Tests/Services/ThreadSafetyServiceTest.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Configuration; using System.Data.Common; using System.Diagnostics; using System.Linq; @@ -81,9 +82,9 @@ namespace Umbraco.Tests.Services _error = null; // dispose! - _dbFactory.Dispose(); + _dbFactory?.Dispose(); - base.TearDown(); + base.TearDown(); } /// @@ -264,9 +265,10 @@ namespace Umbraco.Tests.Services public UmbracoDatabase GetDatabase() { + var settings = ConfigurationManager.ConnectionStrings[Core.Configuration.GlobalSettings.UmbracoConnectionName]; return _databases.GetOrAdd( Thread.CurrentThread.ManagedThreadId, - i => new UmbracoDatabase(Core.Configuration.GlobalSettings.UmbracoConnectionName, SqlSyntax, DatabaseType, _dbProviderFactory, _logger)); + i => new UmbracoDatabase(settings.ConnectionString, SqlSyntax, DatabaseType, _dbProviderFactory, _logger)); } protected override void DisposeResources() diff --git a/src/Umbraco.Tests/Templates/TemplateRepositoryTests.cs b/src/Umbraco.Tests/Templates/TemplateRepositoryTests.cs index ece4a1a0fd..be0ee5d824 100644 --- a/src/Umbraco.Tests/Templates/TemplateRepositoryTests.cs +++ b/src/Umbraco.Tests/Templates/TemplateRepositoryTests.cs @@ -17,29 +17,29 @@ using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.Persistence.Repositories; using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Persistence.UnitOfWork; +using Umbraco.Tests.TestHelpers; namespace Umbraco.Tests.Templates { [TestFixture] public class TemplateRepositoryTests { - private readonly Mock _unitOfWorkMock = new Mock(); private readonly Mock _cacheMock = new Mock(); - private TemplateRepository _templateRepository; private readonly Mock _viewFileSystemMock = new Mock(); private readonly Mock _masterpageFileSystemMock = new Mock(); private readonly Mock _templateConfigMock = new Mock(); + private TemplateRepository _templateRepository; [SetUp] public void Setup() { - var uowMock = new Mock(); - var db = new UmbracoDatabase("cstr", new SqlCeSyntaxProvider(), DatabaseType.SQLCe, DbProviderFactories.GetFactory(Constants.DbProviderNames.SqlCe), Mock.Of()); - uowMock.Setup(x => x.Database).Returns(db); + var logger = Mock.Of(); - var loggerMock = new Mock(); + var unitOfWorkMock = new Mock(); + var db = TestObjects.GetUmbracoSqlCeDatabase(logger); + unitOfWorkMock.Setup(x => x.Database).Returns(db); - _templateRepository = new TemplateRepository(_unitOfWorkMock.Object, _cacheMock.Object, loggerMock.Object, _masterpageFileSystemMock.Object, _viewFileSystemMock.Object, _templateConfigMock.Object, Mock.Of()); + _templateRepository = new TemplateRepository(unitOfWorkMock.Object, _cacheMock.Object, logger, _masterpageFileSystemMock.Object, _viewFileSystemMock.Object, _templateConfigMock.Object, Mock.Of()); } diff --git a/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs b/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs index b2b3b7f47a..34127d7560 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseDatabaseFactoryTest.cs @@ -34,6 +34,8 @@ using Umbraco.Web.Routing; using Umbraco.Web.Security; using umbraco.BusinessLogic; using Umbraco.Core.Events; +using Umbraco.Core.Models; +using File = System.IO.File; namespace Umbraco.Tests.TestHelpers { @@ -89,44 +91,67 @@ namespace Umbraco.Tests.TestHelpers { var sqlSyntaxProviders = new[] { new SqlCeSyntaxProvider() }; - var dbFactory = new DefaultDatabaseFactory( - GetDbConnectionString(), - GetDbProviderName(), - sqlSyntaxProviders, - Logger); + // create the database if required + // note: must do before instanciating the database factory else it will + // not find the database and will remain un-configured. + using (ProfilingLogger.TraceDuration("Create database.")) + { + //TODO make it faster + CreateSqlCeDatabase(); + } + + // ensure the configuration matches the current version for tests + SettingsForTests.ConfigurationStatus = UmbracoVersion.Current.ToString(3); + + // create the database factory - if the test does not require an actual database, + // use a mock factory; otherwise use a real factory. + var databaseFactory = DatabaseTestBehavior == DatabaseBehavior.NoDatabasePerFixture + ? TestObjects.GetIDatabaseFactoryMock() + : new DefaultDatabaseFactory(GetDbConnectionString(), GetDbProviderName(), sqlSyntaxProviders, Logger); + + // so, using the above code to create a mock IDatabaseFactory if we don't have a real database + // but, that will NOT prevent _appContext from NOT being configured, because it cannot connect + // to the database to check the migrations ;-( var evtMsgs = new TransientMessagesFactory(); - _appContext = new ApplicationContext( - //assign the db context - new DatabaseContext(dbFactory, Logger), - //assign the service context - new ServiceContext( - Container.GetInstance(), - new NPocoUnitOfWorkProvider(dbFactory), - new FileUnitOfWorkProvider(), - new PublishingStrategy(evtMsgs, Logger), - CacheHelper, - Logger, - evtMsgs, - Enumerable.Empty()), + var databaseContext = new DatabaseContext(databaseFactory, Logger); + var serviceContext = new ServiceContext( + Container.GetInstance(), + new NPocoUnitOfWorkProvider(databaseFactory), + new FileUnitOfWorkProvider(), + new PublishingStrategy(evtMsgs, Logger), CacheHelper, - ProfilingLogger) + Logger, + evtMsgs, + Enumerable.Empty()); + + //var appContextMock = new Mock(databaseContext, serviceContext, CacheHelper, ProfilingLogger); + //// if the test does not require an actual database, or runs with an empty database, the application + //// context will not be able to check the migration status in the database, so we have to force it + //// to think it is configured. + //if (DatabaseTestBehavior == DatabaseBehavior.NoDatabasePerFixture // no db at all + // || DatabaseTestBehavior == DatabaseBehavior.EmptyDbFilePerTest) // empty db + // appContextMock.Setup(x => x.IsConfigured).Returns(true); + //_appContext = appContextMock.Object; + _appContext = new ApplicationContext(databaseContext, serviceContext, CacheHelper, ProfilingLogger); + + // initialize the database if required + // note: must do after creating the application context as + // it is using it + using (ProfilingLogger.TraceDuration("Initialize database.")) { - IsReady = true - }; + // TODO make it faster + InitializeDatabase(_appContext); + } + + // ensure the application context understand we are configured now + SettingsForTests.ConfigurationStatus = UmbracoVersion.GetSemanticVersion().ToSemanticString(); + _appContext.ResetConfigured(); + + // application is ready + _appContext.IsReady = true; ApplicationContext.Current = _appContext; - - using (ProfilingLogger.TraceDuration("init")) - { - //TODO: Somehow make this faster - takes 5s + - - CreateSqlCeDatabase(); - InitializeDatabase(); - - //ensure the configuration matches the current version for tests - SettingsForTests.ConfigurationStatus = UmbracoVersion.Current.ToString(3); - } } /// @@ -136,8 +161,8 @@ namespace Umbraco.Tests.TestHelpers { get { - var att = this.GetType().GetCustomAttribute(false); - return att != null ? att.Behavior : DatabaseBehavior.NoDatabasePerFixture; + var att = GetType().GetCustomAttribute(false); + return att?.Behavior ?? DatabaseBehavior.NoDatabasePerFixture; } } @@ -238,7 +263,7 @@ namespace Umbraco.Tests.TestHelpers /// /// Creates the tables and data for the database /// - protected virtual void InitializeDatabase() + protected virtual void InitializeDatabase(ApplicationContext appContext) { if (DatabaseTestBehavior == DatabaseBehavior.NoDatabasePerFixture || DatabaseTestBehavior == DatabaseBehavior.EmptyDbFilePerTest) return; @@ -254,9 +279,9 @@ namespace Umbraco.Tests.TestHelpers || (_isFirstTestInFixture && DatabaseTestBehavior == DatabaseBehavior.NewDbFileAndSchemaPerFixture))) { - var schemaHelper = new DatabaseSchemaHelper(DatabaseContext.Database, Logger); + var schemaHelper = new DatabaseSchemaHelper(appContext.DatabaseContext.Database, Logger); //Create the umbraco database and its base data - schemaHelper.CreateDatabaseSchema(_appContext); + schemaHelper.CreateDatabaseSchema(appContext); //close the connections, we're gonna read this baby in as a byte array so we don't have to re-initialize the // damn db for each test @@ -352,15 +377,9 @@ namespace Umbraco.Tests.TestHelpers } } - protected ServiceContext ServiceContext - { - get { return ApplicationContext.Services; } - } + protected ServiceContext ServiceContext => ApplicationContext.Services; - protected DatabaseContext DatabaseContext - { - get { return ApplicationContext.DatabaseContext; } - } + protected DatabaseContext DatabaseContext => ApplicationContext.DatabaseContext; protected UmbracoContext GetUmbracoContext(string url, int templateId, RouteData routeData = null, bool setSingleton = false) { diff --git a/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs b/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs index f95c0acd70..e0c5ab9c38 100644 --- a/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs +++ b/src/Umbraco.Tests/TestHelpers/BaseUmbracoApplicationTest.cs @@ -213,7 +213,7 @@ namespace Umbraco.Tests.TestHelpers var evtMsgs = new TransientMessagesFactory(); ApplicationContext.Current = new ApplicationContext( //assign the db context - new DatabaseContext(new DefaultDatabaseFactory(Core.Configuration.GlobalSettings.UmbracoConnectionName, SqlSyntaxProviders.GetDefaultProviders(Logger), Logger), Logger), + new DatabaseContext(new DefaultDatabaseFactory(Core.Configuration.GlobalSettings.UmbracoConnectionName, TestObjects.GetDefaultSqlSyntaxProviders(Logger), Logger), Logger), //assign the service context new ServiceContext( Container.GetInstance(), @@ -262,15 +262,9 @@ namespace Umbraco.Tests.TestHelpers Resolution.Freeze(); } - protected ApplicationContext ApplicationContext - { - get { return ApplicationContext.Current; } - } + protected ApplicationContext ApplicationContext => ApplicationContext.Current; - protected ILogger Logger - { - get { return ProfilingLogger.Logger; } - } + protected ILogger Logger => ProfilingLogger.Logger; protected ProfilingLogger ProfilingLogger { get; private set; } protected CacheHelper CacheHelper { get; private set; } @@ -278,9 +272,6 @@ namespace Umbraco.Tests.TestHelpers // and the number of these will hopefully start getting greatly reduced now that most things are mockable. internal IServiceContainer Container { get; private set; } - protected virtual ISqlSyntaxProvider SqlSyntax - { - get { return new SqlCeSyntaxProvider(); } - } + protected virtual ISqlSyntaxProvider SqlSyntax => new SqlCeSyntaxProvider(); } } \ No newline at end of file diff --git a/src/Umbraco.Tests/TestHelpers/MockHelper.cs b/src/Umbraco.Tests/TestHelpers/MockHelper.cs deleted file mode 100644 index f81c7439d7..0000000000 --- a/src/Umbraco.Tests/TestHelpers/MockHelper.cs +++ /dev/null @@ -1,41 +0,0 @@ -using Moq; -using Umbraco.Core; -using Umbraco.Core.Configuration.UmbracoSettings; -using Umbraco.Core.Logging; -using Umbraco.Core.Persistence; -using Umbraco.Core.Persistence.SqlSyntax; -using Umbraco.Core.Persistence.UnitOfWork; -using Umbraco.Core.Services; - -namespace Umbraco.Tests.TestHelpers -{ - public static class MockHelper - { - public static ServiceContext GetMockedServiceContext() - { - return new ServiceContext( - new Mock().Object, - new Mock().Object, - new Mock().Object, - new Mock().Object, - new Mock().Object, - new Mock().Object, - new Mock().Object, - new Mock().Object, - new Mock().Object, - new Mock().Object, - new Mock().Object, - new Mock().Object, - new Mock().Object, - new Mock().Object, - new Mock().Object, - new Mock().Object, - new Mock().Object, - new Mock().Object, - new Mock().Object, - new Mock().Object, - new Mock().Object, - new Mock().Object); - } - } -} \ No newline at end of file diff --git a/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs b/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs new file mode 100644 index 0000000000..ceb94bb34d --- /dev/null +++ b/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs @@ -0,0 +1,111 @@ +using System; +using System.Data; +using System.Data.Common; +using Moq; +using Umbraco.Core.Persistence; +using Umbraco.Core.Services; + +namespace Umbraco.Tests.TestHelpers +{ + /// + /// Provides objects for tests. + /// + internal static partial class TestObjects + { + /// + /// Gets a mocked IDatabaseFactory. + /// + /// An IDatabaseFactory. + /// A value indicating whether the factory is configured. + /// A value indicating whether the factory can connect to the database. + /// This is just a void factory that has no actual database. + public static IDatabaseFactory GetIDatabaseFactoryMock(bool configured = true, bool canConnect = true) + { + var databaseFactoryMock = new Mock(); + databaseFactoryMock.Setup(x => x.Configured).Returns(configured); + databaseFactoryMock.Setup(x => x.CanConnect).Returns(canConnect); + return databaseFactoryMock.Object; + } + + /// + /// Gets a mocked service context built with mocked services. + /// + /// A ServiceContext. + public static ServiceContext GetServiceContextMock() + { + return new ServiceContext( + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + new Mock().Object); + } + + /// + /// Gets an opened database connection that can begin a transaction. + /// + /// A DbConnection. + /// This is because NPoco wants a DbConnection, NOT an IDbConnection, + /// and DbConnection is hard to mock so we create our own class here. + public static DbConnection GetDbConnection() + { + return new MockDbConnection(); + } + + #region Inner classes + + private class MockDbConnection : DbConnection + { + protected override DbTransaction BeginDbTransaction(IsolationLevel isolationLevel) + { + return Mock.Of(); // enough here + } + + public override void Close() + { + throw new NotImplementedException(); + } + + public override void ChangeDatabase(string databaseName) + { + throw new NotImplementedException(); + } + + public override void Open() + { + throw new NotImplementedException(); + } + + public override string ConnectionString { get; set; } + + protected override DbCommand CreateDbCommand() + { + throw new NotImplementedException(); + } + + public override string Database { get; } + public override string DataSource { get; } + public override string ServerVersion { get; } + public override ConnectionState State => ConnectionState.Open; // else NPoco reopens + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/TestHelpers/TestObjects.cs b/src/Umbraco.Tests/TestHelpers/TestObjects.cs new file mode 100644 index 0000000000..4454be3444 --- /dev/null +++ b/src/Umbraco.Tests/TestHelpers/TestObjects.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.Data.Common; +using Moq; +using NPoco; +using Umbraco.Core; +using Umbraco.Core.Logging; +using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.SqlSyntax; + +namespace Umbraco.Tests.TestHelpers +{ + /// + /// Provides objects for tests. + /// + static partial class TestObjects + { + /// + /// Gets the default ISqlSyntaxProvider objects. + /// + /// A logger. + /// A (lazy) database factory. + /// The default ISqlSyntaxProvider objects. + public static IEnumerable GetDefaultSqlSyntaxProviders(ILogger logger, Lazy lazyFactory = null) + { + return new ISqlSyntaxProvider[] + { + new MySqlSyntaxProvider(logger), + new SqlCeSyntaxProvider(), + new SqlServerSyntaxProvider(lazyFactory ?? new Lazy(() => null)) + }; + } + + /// + /// Gets an UmbracoDatabase. + /// + /// A logger. + /// An UmbracoDatabase. + /// This is just a void database that has no actual database but pretends to have an open connection + /// that can begin a transaction. + public static UmbracoDatabase GetUmbracoSqlCeDatabase(ILogger logger) + { + var syntax = new SqlCeSyntaxProvider(); + var dbProviderFactory = DbProviderFactories.GetFactory(Constants.DbProviderNames.SqlCe); + var connection = TestObjects.GetDbConnection(); + return new UmbracoDatabase(connection, syntax, DatabaseType.SQLCe, dbProviderFactory, logger); + } + + /// + /// Gets an UmbracoDatabase. + /// + /// A logger. + /// An UmbracoDatabase. + /// This is just a void database that has no actual database but pretends to have an open connection + /// that can begin a transaction. + public static UmbracoDatabase GetUmbracoSqlServerDatabase(ILogger logger) + { + var syntax = new SqlServerSyntaxProvider(new Lazy(() => null)); // do NOT try to get the server's version! + var dbProviderFactory = DbProviderFactories.GetFactory(Constants.DbProviderNames.SqlServer); + var connection = TestObjects.GetDbConnection(); + return new UmbracoDatabase(connection, syntax, DatabaseType.SqlServer2008, dbProviderFactory, logger); + } + } +} diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 64e1911d32..afb12b3f3f 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -185,6 +185,8 @@ + + @@ -436,7 +438,6 @@ - diff --git a/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs b/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs index 357b601e41..667f214a37 100644 --- a/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs @@ -73,9 +73,11 @@ namespace Umbraco.Tests.Web.Mvc [Test] public void Umbraco_Helper_Not_Null() { + var databaseFactory = TestObjects.GetIDatabaseFactoryMock(); + var appCtx = new ApplicationContext( - new DatabaseContext(new Mock().Object, Mock.Of()), - MockHelper.GetMockedServiceContext(), + new DatabaseContext(databaseFactory, Mock.Of()), + TestObjects.GetServiceContextMock(), CacheHelper.CreateDisabledCacheHelper(), new ProfilingLogger(Mock.Of(), Mock.Of())); diff --git a/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs b/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs index d9b66a2d2a..f853a90c89 100644 --- a/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs @@ -373,7 +373,7 @@ namespace Umbraco.Tests.Web.Mvc ServiceContext GetServiceContext() { - return MockHelper.GetMockedServiceContext(); + return TestObjects.GetServiceContextMock(); } ViewContext GetViewContext() @@ -427,8 +427,10 @@ namespace Umbraco.Tests.Web.Mvc // ApplicationContext.Current = new ApplicationContext(false) { IsReady = true }; var svcCtx = GetServiceContext(); + var databaseFactory = TestObjects.GetIDatabaseFactoryMock(); + var appCtx = new ApplicationContext( - new DatabaseContext(Mock.Of(), logger), + new DatabaseContext(databaseFactory, logger), svcCtx, CacheHelper.CreateDisabledCacheHelper(), new ProfilingLogger(logger, Mock.Of())) { IsReady = true }; diff --git a/src/Umbraco.Web.UI/config/ClientDependency.config b/src/Umbraco.Web.UI/config/ClientDependency.config index 0023483deb..b086b8a90b 100644 --- a/src/Umbraco.Web.UI/config/ClientDependency.config +++ b/src/Umbraco.Web.UI/config/ClientDependency.config @@ -10,7 +10,7 @@ NOTES: * Compression/Combination/Minification is not enabled unless debug="false" is specified on the 'compiliation' element in the web.config * A new version will invalidate both client and server cache and create new persisted files --> - +