Files
Umbraco-CMS/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs

429 lines
19 KiB
C#
Raw Normal View History

2016-10-13 21:08:07 +02:00
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data.SqlServerCe;
using System.Linq;
2018-03-30 19:31:42 +02:00
using System.Threading;
2016-10-13 21:08:07 +02:00
using System.Web.Routing;
using System.Xml;
using Moq;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.SqlSyntax;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.Services;
using Umbraco.Web;
using Umbraco.Web.PublishedCache;
using Umbraco.Web.Security;
using Umbraco.Web.Routing;
using File = System.IO.File;
using Umbraco.Web.Composing;
2016-11-08 17:38:05 +01:00
using Umbraco.Core.Persistence.Mappers;
2017-05-12 14:49:44 +02:00
using Umbraco.Core.Scoping;
2016-12-16 10:40:14 +01:00
using Umbraco.Tests.Testing;
using Umbraco.Core.Migrations.Install;
2017-10-17 17:43:15 +02:00
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.Persistence.Repositories;
using Umbraco.Tests.LegacyXmlPublishedCache;
using Umbraco.Tests.Testing.Objects.Accessors;
using Umbraco.Web.WebApi;
2016-10-13 21:08:07 +02:00
namespace Umbraco.Tests.TestHelpers
{
/// <summary>
/// Provides a base class for all Umbraco tests that require a database.
/// </summary>
/// <remarks>
2016-12-16 18:45:41 +01:00
/// <para>Can provide a SqlCE database populated with the Umbraco schema. The database should
2016-12-14 14:06:30 +01:00
/// be accessed through the UmbracoDatabaseFactory.</para>
2016-10-13 21:08:07 +02:00
/// <para>Provides an Umbraco context and Xml content.</para>
/// <para>fixme what else?</para>
/// </remarks>
2018-03-30 19:31:42 +02:00
[Apartment(ApartmentState.STA)] // why?
[UmbracoTest(WithApplication = true)]
public abstract class TestWithDatabaseBase : UmbracoTestBase
2016-10-13 21:08:07 +02:00
{
private string _databasePath;
private static byte[] _databaseBytes;
protected PublishedContentTypeCache ContentTypesCache { get; private set; }
protected override ISqlSyntaxProvider SqlSyntax => GetSyntaxProvider();
2017-05-30 19:41:37 +02:00
protected ServiceContext ServiceContext => Current.Services;
protected IVariationContextAccessor VariationContextAccessor => new TestVariationContextAccessor();
2016-10-13 21:08:07 +02:00
2017-05-30 19:41:37 +02:00
internal ScopeProvider ScopeProvider => Current.ScopeProvider as ScopeProvider;
2016-10-13 21:08:07 +02:00
2018-11-28 12:59:40 +01:00
protected ISqlContext SqlContext => Factory.GetInstance<ISqlContext>();
2016-10-13 21:08:07 +02:00
public override void SetUp()
{
base.SetUp();
var path = TestHelper.CurrentAssemblyDirectory;
AppDomain.CurrentDomain.SetData("DataDirectory", path);
}
protected override void Compose()
{
base.Compose();
2018-11-28 12:59:40 +01:00
Composition.Register<ISqlSyntaxProvider, SqlCeSyntaxProvider>();
Composition.Register(factory => PublishedSnapshotService);
Composition.Register(factory => DefaultCultureAccessor);
2016-10-13 21:08:07 +02:00
2018-11-28 17:35:12 +01:00
Composition.WithCollectionBuilder<DataEditorCollectionBuilder>()
2016-11-08 17:38:05 +01:00
.Clear()
2018-11-28 11:05:41 +01:00
.Add(() => Composition.TypeLoader.GetDataEditors());
2016-10-13 21:08:07 +02:00
Composition.WithCollectionBuilder<UmbracoApiControllerTypeCollectionBuilder>()
.Add(Composition.TypeLoader.GetUmbracoApiControllers());
2018-11-29 10:35:16 +01:00
Composition.RegisterUnique(f =>
2016-10-13 21:08:07 +02:00
{
2016-11-05 19:23:55 +01:00
if (Options.Database == UmbracoTestOptions.Database.None)
2016-10-13 21:08:07 +02:00
return TestObjects.GetDatabaseFactoryMock();
2019-03-29 08:30:51 +01:00
var lazyMappers = new Lazy<IMapperCollection>(f.GetInstance<IMapperCollection>);
var factory = new UmbracoDatabaseFactory(GetDbConnectionString(), GetDbProviderName(), f.GetInstance<ILogger>(), lazyMappers, TestHelper.DbProviderFactoryCreator);
2016-10-13 21:08:07 +02:00
factory.ResetForTests();
return factory;
});
}
2018-03-30 19:31:42 +02:00
[OneTimeTearDown]
2016-10-13 21:08:07 +02:00
public void FixtureTearDown()
{
2017-05-12 14:49:44 +02:00
RemoveDatabaseFile();
2016-10-13 21:08:07 +02:00
}
public override void TearDown()
{
2018-11-28 12:59:40 +01:00
var profilingLogger = Factory.TryGetInstance<IProfilingLogger>();
var timer = profilingLogger?.TraceDuration<TestWithDatabaseBase>("teardown"); // FIXME: move that one up
2016-11-08 17:38:05 +01:00
try
2016-10-13 21:08:07 +02:00
{
// FIXME: should we first kill all scopes?
2016-11-05 19:23:55 +01:00
if (Options.Database == UmbracoTestOptions.Database.NewSchemaPerTest)
2017-05-12 14:49:44 +02:00
RemoveDatabaseFile();
2016-10-13 21:08:07 +02:00
AppDomain.CurrentDomain.SetData("DataDirectory", null);
// make sure we dispose of the service to unbind events
2017-10-31 12:48:24 +01:00
PublishedSnapshotService?.Dispose();
PublishedSnapshotService = null;
2016-10-13 21:08:07 +02:00
}
2016-11-08 17:38:05 +01:00
finally
{
timer?.Dispose();
}
2016-10-13 21:08:07 +02:00
base.TearDown();
}
private void CreateAndInitializeDatabase()
{
using (ProfilingLogger.TraceDuration<TestWithDatabaseBase>("Create database."))
{
CreateSqlCeDatabase(); // TODO: faster!
2016-10-13 21:08:07 +02:00
}
// ensure the configuration matches the current version for tests
2019-01-07 10:43:28 +01:00
var globalSettingsMock = Mock.Get(Factory.GetInstance<IGlobalSettings>()); //this will modify the IGlobalSettings instance stored in the container
globalSettingsMock.Setup(x => x.ConfigurationStatus).Returns(UmbracoVersion.Current.ToString(3));
2016-10-13 21:08:07 +02:00
using (ProfilingLogger.TraceDuration<TestWithDatabaseBase>("Initialize database."))
{
InitializeDatabase(); // TODO: faster!
2016-10-13 21:08:07 +02:00
}
}
protected virtual ISqlSyntaxProvider GetSyntaxProvider()
{
return new SqlCeSyntaxProvider();
}
protected virtual string GetDbProviderName()
{
return Constants.DbProviderNames.SqlCe;
}
protected virtual string GetDbConnectionString()
{
return @"Datasource=|DataDirectory|UmbracoNPocoTests.sdf;Flush Interval=1;";
}
/// <summary>
/// Creates the SqlCe database if required
/// </summary>
protected virtual void CreateSqlCeDatabase()
{
2016-11-05 19:23:55 +01:00
if (Options.Database == UmbracoTestOptions.Database.None)
2016-10-13 21:08:07 +02:00
return;
var path = TestHelper.CurrentAssemblyDirectory;
//Get the connectionstring settings from config
2017-05-12 14:49:44 +02:00
var settings = ConfigurationManager.ConnectionStrings[Constants.System.UmbracoConnectionName];
2016-10-13 21:08:07 +02:00
ConfigurationManager.AppSettings.Set(
2017-05-12 14:49:44 +02:00
Constants.System.UmbracoConnectionName,
2016-10-13 21:08:07 +02:00
GetDbConnectionString());
_databasePath = string.Concat(path, "\\UmbracoNPocoTests.sdf");
//create a new database file if
// - is the first test in the session
// - the database file doesn't exist
// - NewDbFileAndSchemaPerTest
// - _isFirstTestInFixture + DbInitBehavior.NewDbFileAndSchemaPerFixture
//if this is the first test in the session, always ensure a new db file is created
2016-12-16 18:45:41 +01:00
if (FirstTestInSession
|| File.Exists(_databasePath) == false
|| Options.Database == UmbracoTestOptions.Database.NewSchemaPerTest
|| Options.Database == UmbracoTestOptions.Database.NewEmptyPerTest
2016-12-16 10:40:14 +01:00
|| (FirstTestInFixture && Options.Database == UmbracoTestOptions.Database.NewSchemaPerFixture))
2016-10-13 21:08:07 +02:00
{
using (ProfilingLogger.TraceDuration<TestWithDatabaseBase>("Remove database file"))
{
RemoveDatabaseFile(null, ex =>
{
//if this doesn't work we have to make sure everything is reset! otherwise
// well run into issues because we've already set some things up
TearDown();
throw ex;
});
}
//Create the Sql CE database
using (ProfilingLogger.TraceDuration<TestWithDatabaseBase>("Create database file"))
{
2016-11-05 19:23:55 +01:00
if (Options.Database != UmbracoTestOptions.Database.NewEmptyPerTest && _databaseBytes != null)
2016-10-13 21:08:07 +02:00
{
File.WriteAllBytes(_databasePath, _databaseBytes);
}
else
{
using (var engine = new SqlCeEngine(settings.ConnectionString))
{
engine.CreateDatabase();
}
}
}
}
}
2018-04-30 21:29:49 +02:00
protected IDefaultCultureAccessor DefaultCultureAccessor { get; set; }
2018-04-27 17:36:50 +02:00
2017-10-31 12:48:24 +01:00
protected IPublishedSnapshotService PublishedSnapshotService { get; set; }
2017-07-17 17:59:46 +02:00
protected override void Initialize() // FIXME: should NOT be here!
2016-10-13 21:08:07 +02:00
{
2016-11-07 20:50:14 +01:00
base.Initialize();
2018-04-30 21:29:49 +02:00
DefaultCultureAccessor = new TestDefaultCultureAccessor();
2018-04-27 17:36:50 +02:00
2016-11-25 14:08:23 +01:00
CreateAndInitializeDatabase();
2017-10-31 12:48:24 +01:00
// ensure we have a PublishedSnapshotService
if (PublishedSnapshotService == null)
2016-10-13 21:08:07 +02:00
{
2017-10-31 12:48:24 +01:00
PublishedSnapshotService = CreatePublishedSnapshotService();
2016-10-13 21:08:07 +02:00
}
}
2017-10-31 12:48:24 +01:00
protected virtual IPublishedSnapshotService CreatePublishedSnapshotService()
2017-07-17 17:59:46 +02:00
{
2019-01-17 11:01:23 +01:00
var cache = NoAppCache.Instance;
2017-07-17 17:59:46 +02:00
ContentTypesCache = new PublishedContentTypeCache(
2018-11-28 12:59:40 +01:00
Factory.GetInstance<IContentTypeService>(),
Factory.GetInstance<IMediaTypeService>(),
Factory.GetInstance<IMemberTypeService>(),
Factory.GetInstance<IPublishedContentTypeFactory>(),
Logger);
2017-07-17 17:59:46 +02:00
// testing=true so XmlStore will not use the file nor the database
2017-10-31 12:48:24 +01:00
var publishedSnapshotAccessor = new UmbracoContextPublishedSnapshotAccessor(Umbraco.Web.Composing.Current.UmbracoContextAccessor);
2018-04-30 21:29:49 +02:00
var variationContextAccessor = new TestVariationContextAccessor();
var service = new XmlPublishedSnapshotService(
ServiceContext,
2018-11-28 12:59:40 +01:00
Factory.GetInstance<IPublishedContentTypeFactory>(),
ScopeProvider,
cache, publishedSnapshotAccessor, variationContextAccessor,
Factory.GetInstance<IUmbracoContextAccessor>(),
2018-11-28 12:59:40 +01:00
Factory.GetInstance<IDocumentRepository>(), Factory.GetInstance<IMediaRepository>(), Factory.GetInstance<IMemberRepository>(),
2018-04-30 21:29:49 +02:00
DefaultCultureAccessor,
Logger,
Factory.GetInstance<IGlobalSettings>(),
HostingEnvironment,
ShortStringHelper,
new SiteDomainHelper(),
Factory.GetInstance<IEntityXmlSerializer>(),
ContentTypesCache,
null, true, Options.PublishedRepositoryEvents);
2017-07-17 17:59:46 +02:00
// initialize PublishedCacheService content with an Xml source
service.XmlStore.GetXmlDocument = () =>
{
var doc = new XmlDocument();
doc.LoadXml(GetXmlContent(0));
return doc;
};
return service;
}
2016-10-13 21:08:07 +02:00
/// <summary>
/// Creates the tables and data for the database
/// </summary>
protected virtual void InitializeDatabase()
{
2016-11-05 19:23:55 +01:00
if (Options.Database == UmbracoTestOptions.Database.None || Options.Database == UmbracoTestOptions.Database.NewEmptyPerTest)
2016-10-13 21:08:07 +02:00
return;
//create the schema and load default data if:
// - is the first test in the session
// - NewDbFileAndSchemaPerTest
// - _isFirstTestInFixture + DbInitBehavior.NewDbFileAndSchemaPerFixture
if (_databaseBytes == null &&
2016-12-16 10:40:14 +01:00
(FirstTestInSession
2016-11-05 19:23:55 +01:00
|| Options.Database == UmbracoTestOptions.Database.NewSchemaPerTest
2017-05-12 14:49:44 +02:00
|| FirstTestInFixture && Options.Database == UmbracoTestOptions.Database.NewSchemaPerFixture))
2016-10-13 21:08:07 +02:00
{
2017-05-12 14:49:44 +02:00
using (var scope = ScopeProvider.CreateScope())
{
2019-12-18 10:32:22 +01:00
var schemaHelper = new DatabaseSchemaCreator(scope.Database, Logger, UmbracoVersion, TestObjects.GetGlobalSettings());
2017-05-12 14:49:44 +02:00
//Create the umbraco database and its base data
schemaHelper.InitializeDatabaseSchema();
//Special case, we need to create the xml cache tables manually since they are not part of the default
//setup.
//TODO: Remove this when we update all tests to use nucache
schemaHelper.CreateTable<ContentXmlDto>();
schemaHelper.CreateTable<PreviewXmlDto>();
2017-05-12 14:49:44 +02:00
scope.Complete();
}
2016-10-13 21:08:07 +02:00
_databaseBytes = File.ReadAllBytes(_databasePath);
}
}
// FIXME: is this needed?
2016-12-16 14:18:37 +01:00
private void CloseDbConnections(IUmbracoDatabase database)
2016-10-13 21:08:07 +02:00
{
//Ensure that any database connections from a previous test is disposed.
//This is really just double safety as its also done in the TearDown.
database?.Dispose();
2017-06-20 12:12:29 +02:00
//SqlCeContextGuardian.CloseBackgroundConnection();
2016-10-13 21:08:07 +02:00
}
2016-12-16 14:18:37 +01:00
private void RemoveDatabaseFile(IUmbracoDatabase database, Action<Exception> onFail = null)
2016-10-13 21:08:07 +02:00
{
if (database != null) CloseDbConnections(database);
2017-05-12 14:49:44 +02:00
RemoveDatabaseFile(onFail);
}
private void RemoveDatabaseFile(Action<Exception> onFail = null)
{
2016-10-13 21:08:07 +02:00
var path = TestHelper.CurrentAssemblyDirectory;
try
{
var filePath = string.Concat(path, "\\UmbracoNPocoTests.sdf");
if (File.Exists(filePath))
File.Delete(filePath);
}
catch (Exception ex)
{
Logger.Error<TestWithDatabaseBase>(ex, "Could not remove the old database file");
2016-10-13 21:08:07 +02:00
// swallow this exception - that's because a sub class might require further teardown logic
onFail?.Invoke(ex);
}
}
protected IUmbracoContext GetUmbracoContext(string url, int templateId = 1234, RouteData routeData = null, bool setSingleton = false, IGlobalSettings globalSettings = null, IPublishedSnapshotService snapshotService = null)
2016-10-13 21:08:07 +02:00
{
// ensure we have a PublishedCachesService
var service = snapshotService ?? PublishedSnapshotService as XmlPublishedSnapshotService;
2016-10-13 21:08:07 +02:00
if (service == null)
throw new Exception("Not a proper XmlPublishedCache.PublishedCachesService.");
if (service is XmlPublishedSnapshotService)
2016-10-13 21:08:07 +02:00
{
2018-04-27 11:38:50 +10:00
// re-initialize PublishedCacheService content with an Xml source with proper template id
((XmlPublishedSnapshotService)service).XmlStore.GetXmlDocument = () =>
2018-04-27 11:38:50 +10:00
{
var doc = new XmlDocument();
doc.LoadXml(GetXmlContent(templateId));
return doc;
};
}
2016-10-13 21:08:07 +02:00
var httpContext = GetHttpContextFactory(url, routeData).HttpContext;
var httpContextAccessor = TestHelper.GetHttpContextAccessor(httpContext);
2016-10-18 17:09:26 +02:00
var umbracoContext = new UmbracoContext(
httpContextAccessor,
2016-10-13 21:08:07 +02:00
service,
2020-02-13 12:29:08 +01:00
new WebSecurity(httpContextAccessor, Factory.GetInstance<IUserService>(),
Factory.GetInstance<IGlobalSettings>(), IOHelper),
2018-11-28 12:59:40 +01:00
globalSettings ?? Factory.GetInstance<IGlobalSettings>(),
2019-12-04 14:03:39 +01:00
new TestVariationContextAccessor(),
IOHelper,
UriUtility);
2016-10-13 21:08:07 +02:00
if (setSingleton)
2017-05-30 18:13:11 +02:00
Umbraco.Web.Composing.Current.UmbracoContextAccessor.UmbracoContext = umbracoContext;
2016-10-13 21:08:07 +02:00
return umbracoContext;
}
protected virtual string GetXmlContent(int templateId)
{
return @"<?xml version=""1.0"" encoding=""utf-8""?>
<!DOCTYPE root[
<!ELEMENT Home ANY>
<!ATTLIST Home id ID #REQUIRED>
<!ELEMENT CustomDocument ANY>
<!ATTLIST CustomDocument id ID #REQUIRED>
]>
<root id=""-1"">
2017-07-20 11:21:28 +02:00
<Home id=""1046"" parentID=""-1"" level=""1"" writerID=""0"" creatorID=""0"" nodeType=""1044"" template=""" + templateId + @""" sortOrder=""1"" createDate=""2012-06-12T14:13:17"" updateDate=""2012-07-20T18:50:43"" nodeName=""Home"" urlName=""home"" writerName=""admin"" creatorName=""admin"" path=""-1,1046"" isDoc="""">
<content><![CDATA[]]></content>
<umbracoUrlAlias><![CDATA[this/is/my/alias, anotheralias]]></umbracoUrlAlias>
<umbracoNaviHide>1</umbracoNaviHide>
<Home id=""1173"" parentID=""1046"" level=""2"" writerID=""0"" creatorID=""0"" nodeType=""1044"" template=""" + templateId + @""" sortOrder=""2"" createDate=""2012-07-20T18:06:45"" updateDate=""2012-07-20T19:07:31"" nodeName=""Sub1"" urlName=""sub1"" writerName=""admin"" creatorName=""admin"" path=""-1,1046,1173"" isDoc="""">
<content><![CDATA[<div>This is some content</div>]]></content>
<umbracoUrlAlias><![CDATA[page2/alias, 2ndpagealias]]></umbracoUrlAlias>
<Home id=""1174"" parentID=""1173"" level=""3"" writerID=""0"" creatorID=""0"" nodeType=""1044"" template=""" + templateId + @""" sortOrder=""2"" createDate=""2012-07-20T18:07:54"" updateDate=""2012-07-20T19:10:27"" nodeName=""Sub2"" urlName=""sub2"" writerName=""admin"" creatorName=""admin"" path=""-1,1046,1173,1174"" isDoc="""">
<content><![CDATA[]]></content>
<umbracoUrlAlias><![CDATA[only/one/alias]]></umbracoUrlAlias>
<creatorName><![CDATA[Custom data with same property name as the member name]]></creatorName>
</Home>
<Home id=""1176"" parentID=""1173"" level=""3"" writerID=""0"" creatorID=""0"" nodeType=""1044"" template=""" + templateId + @""" sortOrder=""3"" createDate=""2012-07-20T18:08:08"" updateDate=""2012-07-20T19:10:52"" nodeName=""Sub 3"" urlName=""sub-3"" writerName=""admin"" creatorName=""admin"" path=""-1,1046,1173,1176"" isDoc="""">
<content><![CDATA[]]></content>
</Home>
<CustomDocument id=""1177"" parentID=""1173"" level=""3"" writerID=""0"" creatorID=""0"" nodeType=""1234"" template=""" + templateId + @""" sortOrder=""4"" createDate=""2012-07-16T15:26:59"" updateDate=""2012-07-18T14:23:35"" nodeName=""custom sub 1"" urlName=""custom-sub-1"" writerName=""admin"" creatorName=""admin"" path=""-1,1046,1173,1177"" isDoc="""" />
<CustomDocument id=""1178"" parentID=""1173"" level=""3"" writerID=""0"" creatorID=""0"" nodeType=""1234"" template=""" + templateId + @""" sortOrder=""4"" createDate=""2012-07-16T15:26:59"" updateDate=""2012-07-16T14:23:35"" nodeName=""custom sub 2"" urlName=""custom-sub-2"" writerName=""admin"" creatorName=""admin"" path=""-1,1046,1173,1178"" isDoc="""" />
</Home>
<Home id=""1175"" parentID=""1046"" level=""2"" writerID=""0"" creatorID=""0"" nodeType=""1044"" template=""" + templateId + @""" sortOrder=""3"" createDate=""2012-07-20T18:08:01"" updateDate=""2012-07-20T18:49:32"" nodeName=""Sub 2"" urlName=""sub-2"" writerName=""admin"" creatorName=""admin"" path=""-1,1046,1175"" isDoc=""""><content><![CDATA[]]></content>
</Home>
</Home>
<CustomDocument id=""1172"" parentID=""-1"" level=""1"" writerID=""0"" creatorID=""0"" nodeType=""1234"" template=""" + templateId + @""" sortOrder=""2"" createDate=""2012-07-16T15:26:59"" updateDate=""2012-07-18T14:23:35"" nodeName=""Test"" urlName=""test-page"" writerName=""admin"" creatorName=""admin"" path=""-1,1172"" isDoc="""" />
2016-10-13 21:08:07 +02:00
</root>";
}
}
2017-07-20 11:21:28 +02:00
}