2016-10-13 21:08:07 +02:00
using System ;
using System.Collections.Generic ;
using System.Configuration ;
using System.Data.SqlServerCe ;
using System.IO ;
using System.Linq ;
using System.Web.Routing ;
using System.Xml ;
using Moq ;
using NUnit.Framework ;
using SQLCE4Umbraco ;
using Umbraco.Core ;
using Umbraco.Core.Cache ;
using Umbraco.Core.Configuration ;
using Umbraco.Core.Configuration.UmbracoSettings ;
using Umbraco.Core.IO ;
using Umbraco.Core.Logging ;
using Umbraco.Core.Manifest ;
using Umbraco.Core.Persistence ;
using Umbraco.Core.Persistence.SqlSyntax ;
using Umbraco.Core.Persistence.UnitOfWork ;
using Umbraco.Core.PropertyEditors ;
using Umbraco.Core.Services ;
using Umbraco.Web ;
using Umbraco.Web.PublishedCache ;
using Umbraco.Web.PublishedCache.XmlPublishedCache ;
using Umbraco.Web.Security ;
using Umbraco.Core.Plugins ;
using Umbraco.Web.Routing ;
using File = System . IO . File ;
using Umbraco.Core.DI ;
using Umbraco.Core.Events ;
using Umbraco.Core.Strings ;
namespace Umbraco.Tests.TestHelpers
{
/// <summary>
/// Provides a base class for all Umbraco tests that require a database.
/// </summary>
/// <remarks>
/// <para>Can provide a SqlCE database populated with the Umbraco schema. The database should
/// be accessed through the DefaultDatabaseFactory.</para>
/// <para>Provides an Umbraco context and Xml content.</para>
/// <para>fixme what else?</para>
/// </remarks>
[TestFixture, RequiresSTA]
public abstract class TestWithDatabaseBase : TestWithApplicationBase
{
private CacheHelper _disabledCacheHelper ;
private IFacadeService _facadeService ;
// note: a fixture class is created once for all the tests in that fixture
// these flags are used to ensure a new database file is used when appropriate
private static bool _isFirstInSession = true ; // first test in the entire test session
private bool _isFirstInFixture = true ; // first test in the test fixture
private string _databasePath ;
private static byte [ ] _databaseBytes ;
protected CacheHelper DisabledCache = > _disabledCacheHelper ? ? ( _disabledCacheHelper = CacheHelper . CreateDisabledCacheHelper ( ) ) ;
protected IDatabaseUnitOfWorkProvider UowProvider = > Core . DI . Current . Container . GetInstance < IDatabaseUnitOfWorkProvider > ( ) ;
protected PublishedContentTypeCache ContentTypesCache { get ; private set ; }
protected override ISqlSyntaxProvider SqlSyntax = > GetSyntaxProvider ( ) ;
protected ServiceContext ServiceContext = > Core . DI . Current . Services ;
protected DatabaseContext DatabaseContext = > Core . DI . Current . DatabaseContext ;
public override void SetUp ( )
{
base . SetUp ( ) ;
var path = TestHelper . CurrentAssemblyDirectory ;
AppDomain . CurrentDomain . SetData ( "DataDirectory" , path ) ;
CreateAndInitializeDatabase ( ) ;
}
protected override void Compose ( )
{
base . Compose ( ) ;
Container . Register < ISqlSyntaxProvider , SqlCeSyntaxProvider > ( ) ;
Container . Register ( factory = > _facadeService ) ;
var manifestBuilder = new ManifestBuilder (
new NullCacheProvider ( ) ,
new ManifestParser ( Logger , new DirectoryInfo ( IOHelper . MapPath ( "~/App_Plugins" ) ) , new NullCacheProvider ( ) ) ) ;
Container . Register ( _ = > manifestBuilder ) ;
Container . RegisterCollectionBuilder < PropertyEditorCollectionBuilder > ( )
. Add ( ( ) = > Core . DI . Current . PluginManager . ResolvePropertyEditors ( ) ) ;
Container . RegisterSingleton ( f = >
{
if ( DatabaseTestBehavior = = DatabaseBehavior . NoDatabasePerFixture )
return TestObjects . GetDatabaseFactoryMock ( ) ;
var sqlSyntaxProviders = new [ ] { new SqlCeSyntaxProvider ( ) } ;
var factory = new DefaultDatabaseFactory ( GetDbConnectionString ( ) , GetDbProviderName ( ) , sqlSyntaxProviders , Logger , f . GetInstance < IUmbracoDatabaseAccessor > ( ) , Mappers ) ;
factory . ResetForTests ( ) ;
return factory ;
} ) ;
}
[TestFixtureTearDown]
public void FixtureTearDown ( )
{
RemoveDatabaseFile ( Core . DI . Current . HasContainer ? Core . DI . Current . DatabaseContext . Database : null ) ;
}
public override void TearDown ( )
{
// before anything else...
_isFirstInFixture = false ;
_isFirstInSession = false ;
using ( ProfilingLogger . TraceDuration < TestWithDatabaseBase > ( "teardown" ) )
{
if ( DatabaseTestBehavior = = DatabaseBehavior . NewDbFileAndSchemaPerTest )
RemoveDatabaseFile ( Core . DI . Current . HasContainer ? Core . DI . Current . DatabaseContext . Database : null ) ;
AppDomain . CurrentDomain . SetData ( "DataDirectory" , null ) ;
// make sure we dispose of the service to unbind events
_facadeService ? . Dispose ( ) ;
_facadeService = null ;
}
base . TearDown ( ) ;
}
private void CreateAndInitializeDatabase ( )
{
using ( ProfilingLogger . TraceDuration < TestWithDatabaseBase > ( "Create database." ) )
{
CreateSqlCeDatabase ( ) ; // todo faster!
}
// ensure the configuration matches the current version for tests
SettingsForTests . ConfigurationStatus = UmbracoVersion . Current . ToString ( 3 ) ;
using ( ProfilingLogger . TraceDuration < TestWithDatabaseBase > ( "Initialize database." ) )
{
InitializeDatabase ( ) ; // todo faster!
}
}
// fixme implement this one way or another!!!
//
//protected override ApplicationContext CreateApplicationContext()
//{
// var sqlSyntaxProviders = new[] { new SqlCeSyntaxProvider() };
// // 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<BaseDatabaseFactoryTest>("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.
// IDatabaseFactory databaseFactory;
// if (DatabaseTestBehavior == DatabaseBehavior.NoDatabasePerFixture)
// {
// databaseFactory = TestObjects.GetIDatabaseFactoryMock();
// }
// else
// {
// var f = new DefaultDatabaseFactory(GetDbConnectionString(), GetDbProviderName(), sqlSyntaxProviders, Logger, new TestUmbracoDatabaseAccessor(), Mappers);
// f.ResetForTests();
// databaseFactory = f;
// }
// // 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 TransientEventMessagesFactory();
// var databaseContext = new DatabaseContext(databaseFactory, Logger, Mock.Of<IRuntimeState>(), Mock.Of<IMigrationEntryService>());
// var repositoryFactory = Container.GetInstance<RepositoryFactory>();
// var serviceContext = TestObjects.GetServiceContext(
// repositoryFactory,
// _uowProvider = new NPocoUnitOfWorkProvider(databaseFactory, repositoryFactory),
// new FileUnitOfWorkProvider(),
// CacheHelper,
// Logger,
// evtMsgs,
// Enumerable.Empty<IUrlSegmentProvider>());
// // 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.
// var appContextMock = new Mock<ApplicationContext>(databaseContext, serviceContext, CacheHelper, ProfilingLogger);
// //if (DatabaseTestBehavior == DatabaseBehavior.NoDatabasePerFixture // no db at all
// // || DatabaseTestBehavior == DatabaseBehavior.EmptyDbFilePerTest) // empty db
// // appContextMock.Setup(x => x.IsConfigured).Returns(true);
// _appContext = appContextMock.Object;
// // initialize the database if required
// // note: must do after creating the application context as
// // it is using it
// using (ProfilingLogger.TraceDuration<BaseDatabaseFactoryTest>("Initialize database."))
// {
// // TODO make it faster
// InitializeDatabase(_appContext);
// }
// // application is ready
// //_appContext.IsReady = true;
// return _appContext;
//}
protected DatabaseBehavior DatabaseTestBehavior
{
get
{
var att = GetType ( ) . GetCustomAttribute < DatabaseTestBehaviorAttribute > ( false ) ;
return att ? . Behavior ? ? DatabaseBehavior . NoDatabasePerFixture ;
}
}
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;" ;
}
protected FakeHttpContextFactory GetHttpContextFactory ( string url , RouteData routeData = null )
{
var factory = routeData ! = null
? new FakeHttpContextFactory ( url , routeData )
: new FakeHttpContextFactory ( url ) ;
return factory ;
}
/// <summary>
/// Creates the SqlCe database if required
/// </summary>
protected virtual void CreateSqlCeDatabase ( )
{
if ( DatabaseTestBehavior = = DatabaseBehavior . NoDatabasePerFixture )
return ;
var path = TestHelper . CurrentAssemblyDirectory ;
//Get the connectionstring settings from config
var settings = ConfigurationManager . ConnectionStrings [ Core . Configuration . GlobalSettings . UmbracoConnectionName ] ;
ConfigurationManager . AppSettings . Set (
Core . Configuration . GlobalSettings . UmbracoConnectionName ,
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
if ( _isFirstInSession
| | File . Exists ( _databasePath ) = = false
| | DatabaseTestBehavior = = DatabaseBehavior . NewDbFileAndSchemaPerTest
| | DatabaseTestBehavior = = DatabaseBehavior . EmptyDbFilePerTest
| | ( _isFirstInFixture & & DatabaseTestBehavior = = DatabaseBehavior . NewDbFileAndSchemaPerFixture ) )
{
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" ) )
{
if ( DatabaseTestBehavior ! = DatabaseBehavior . EmptyDbFilePerTest & & _databaseBytes ! = null )
{
File . WriteAllBytes ( _databasePath , _databaseBytes ) ;
}
else
{
using ( var engine = new SqlCeEngine ( settings . ConnectionString ) )
{
engine . CreateDatabase ( ) ;
}
}
}
}
}
/// <summary>
/// sets up resolvers before resolution is frozen
/// </summary>
protected override void MoreSetUp ( )
{
// fixme - what about if (PropertyValueConvertersResolver.HasCurrent == false) ??
Container . RegisterCollectionBuilder < PropertyValueConverterCollectionBuilder > ( ) ;
// ensure we have a FacadeService
if ( _facadeService = = null )
{
var behavior = GetType ( ) . GetCustomAttribute < TestSetup . FacadeServiceAttribute > ( false ) ;
var cache = new NullCacheProvider ( ) ;
var enableRepositoryEvents = behavior ! = null & & behavior . EnableRepositoryEvents ;
ContentTypesCache = new PublishedContentTypeCache (
Core . DI . Current . Services . ContentTypeService ,
Core . DI . Current . Services . MediaTypeService ,
Core . DI . Current . Services . MemberTypeService ,
Core . DI . Current . Logger ) ;
// testing=true so XmlStore will not use the file nor the database
var facadeAccessor = new TestFacadeAccessor ( ) ;
var service = new FacadeService (
Core . DI . Current . Services ,
UowProvider ,
cache , facadeAccessor , Core . DI . Current . Logger , ContentTypesCache , null , true , enableRepositoryEvents ) ;
// initialize PublishedCacheService content with an Xml source
service . XmlStore . GetXmlDocument = ( ) = >
{
var doc = new XmlDocument ( ) ;
doc . LoadXml ( GetXmlContent ( 0 ) ) ;
return doc ;
} ;
_facadeService = service ;
}
base . MoreSetUp ( ) ;
}
/// <summary>
/// Creates the tables and data for the database
/// </summary>
protected virtual void InitializeDatabase ( )
{
if ( DatabaseTestBehavior = = DatabaseBehavior . NoDatabasePerFixture | | DatabaseTestBehavior = = DatabaseBehavior . EmptyDbFilePerTest )
return ;
//create the schema and load default data if:
// - is the first test in the session
// - NewDbFileAndSchemaPerTest
// - _isFirstTestInFixture + DbInitBehavior.NewDbFileAndSchemaPerFixture
if ( _databaseBytes = = null & &
( _isFirstInSession
| | DatabaseTestBehavior = = DatabaseBehavior . NewDbFileAndSchemaPerTest
| | ( _isFirstInFixture & & DatabaseTestBehavior = = DatabaseBehavior . NewDbFileAndSchemaPerFixture ) ) )
{
var database = Core . DI . Current . DatabaseContext . Database ;
var schemaHelper = new DatabaseSchemaHelper ( database , Logger ) ;
//Create the umbraco database and its base data
schemaHelper . CreateDatabaseSchema ( Mock . Of < IRuntimeState > ( ) , Mock . Of < IMigrationEntryService > ( ) ) ;
//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
CloseDbConnections ( database ) ;
_databaseBytes = File . ReadAllBytes ( _databasePath ) ;
}
}
private void CloseDbConnections ( UmbracoDatabase database )
{
//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 ( ) ;
SqlCeContextGuardian . CloseBackgroundConnection ( ) ;
}
private void RemoveDatabaseFile ( UmbracoDatabase database , Action < Exception > onFail = null )
{
if ( database ! = null ) CloseDbConnections ( database ) ;
var path = TestHelper . CurrentAssemblyDirectory ;
try
{
var filePath = string . Concat ( path , "\\UmbracoNPocoTests.sdf" ) ;
if ( File . Exists ( filePath ) )
File . Delete ( filePath ) ;
}
catch ( Exception ex )
{
Core . DI . Current . Logger . Error < TestWithDatabaseBase > ( "Could not remove the old database file" , ex ) ;
// swallow this exception - that's because a sub class might require further teardown logic
onFail ? . Invoke ( ex ) ;
}
}
protected UmbracoContext GetUmbracoContext ( string url , int templateId = 1234 , RouteData routeData = null , bool setSingleton = false , IUmbracoSettingsSection umbracoSettings = null , IEnumerable < IUrlProvider > urlProviders = null )
{
// ensure we have a PublishedCachesService
var service = _facadeService as FacadeService ;
if ( service = = null )
throw new Exception ( "Not a proper XmlPublishedCache.PublishedCachesService." ) ;
// re-initialize PublishedCacheService content with an Xml source with proper template id
service . XmlStore . GetXmlDocument = ( ) = >
{
var doc = new XmlDocument ( ) ;
doc . LoadXml ( GetXmlContent ( templateId ) ) ;
return doc ;
} ;
var httpContext = GetHttpContextFactory ( url , routeData ) . HttpContext ;
var umbracoContext = UmbracoContext . CreateContext (
httpContext ,
service ,
new WebSecurity ( httpContext , Core . DI . Current . Services . UserService ) ,
umbracoSettings ? ? SettingsForTests . GetDefault ( ) ,
urlProviders ? ? Enumerable . Empty < IUrlProvider > ( ) ) ;
if ( setSingleton )
Umbraco . Web . Current . SetUmbracoContext ( umbracoContext , true ) ;
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 "" >
< Home id = "" 1046 "" parentID = "" - 1 "" level = "" 1 "" writerID = "" 0 "" creatorID = "" 0 "" nodeType = "" 1044 "" template = "" " + templateId + @" "" sortOrder = "" 1 "" createDate = "" 2012 - 06 - 12 T14 : 13 : 17 "" updateDate = "" 2012 - 07 - 20 T18 : 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 - 20 T18 : 06 : 45 "" updateDate = "" 2012 - 07 - 20 T19 : 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 , 2 ndpagealias ] ] > < / umbracoUrlAlias >
< Home id = "" 1174 "" parentID = "" 1173 "" level = "" 3 "" writerID = "" 0 "" creatorID = "" 0 "" nodeType = "" 1044 "" template = "" " + templateId + @" "" sortOrder = "" 2 "" createDate = "" 2012 - 07 - 20 T18 : 07 : 54 "" updateDate = "" 2012 - 07 - 20 T19 : 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 - 20 T18 : 08 : 08 "" updateDate = "" 2012 - 07 - 20 T19 : 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 - 16 T15 : 26 : 59 "" updateDate = "" 2012 - 07 - 18 T14 : 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 - 16 T15 : 26 : 59 "" updateDate = "" 2012 - 07 - 16 T14 : 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 - 20 T18 : 08 : 01 "" updateDate = "" 2012 - 07 - 20 T18 : 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 - 16 T15 : 26 : 59 "" updateDate = "" 2012 - 07 - 18 T14 : 23 : 35 "" nodeName = "" Test "" urlName = "" test - page "" writerName = "" admin "" creatorName = "" admin "" path = "" - 1 , 1172 "" isDoc = "" "" / >
< / root > ";
}
}
2012-10-18 11:49:44 -02:00
}