Updated db migration testing framework so we only run migrations up to the one we're testing.

fixes #902
This commit is contained in:
Taloth Saldono
2016-02-13 22:23:37 +01:00
parent 8818e39c63
commit bdb1076100
30 changed files with 710 additions and 333 deletions
+5 -59
View File
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using FluentMigrator;
@@ -7,11 +8,11 @@ using FluentMigrator.Runner;
using Marr.Data;
using Moq;
using NUnit.Framework;
using NzbDrone.Common.Serializer;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Datastore.Migration.Framework;
using NzbDrone.Core.Messaging.Events;
namespace NzbDrone.Core.Test.Framework
{
public abstract class DbTest<TSubject, TModel> : DbTest
@@ -70,7 +71,6 @@ namespace NzbDrone.Core.Test.Framework
get
{
return MigrationType.Main;
}
}
@@ -85,10 +85,10 @@ namespace NzbDrone.Core.Test.Framework
}
}
protected virtual TestDatabase WithTestDb(Action<MigrationBase> beforeMigration)
protected virtual ITestDatabase WithTestDb(MigrationContext migrationContext)
{
var factory = Mocker.Resolve<DbFactory>();
var database = factory.Create(MigrationType, beforeMigration);
var database = factory.Create(migrationContext);
Mocker.SetConstant(database);
switch (MigrationType)
@@ -118,7 +118,6 @@ namespace NzbDrone.Core.Test.Framework
return testDb;
}
protected void SetupContainer()
{
WithTempAsAppPath();
@@ -134,7 +133,7 @@ namespace NzbDrone.Core.Test.Framework
public virtual void SetupDb()
{
SetupContainer();
_db = WithTestDb(null);
_db = WithTestDb(new MigrationContext(MigrationType));
}
[TearDown]
@@ -158,57 +157,4 @@ namespace NzbDrone.Core.Test.Framework
}
}
}
public interface ITestDatabase
{
void InsertMany<T>(IEnumerable<T> items) where T : ModelBase, new();
T Insert<T>(T item) where T : ModelBase, new();
List<T> All<T>() where T : ModelBase, new();
T Single<T>() where T : ModelBase, new();
void Update<T>(T childModel) where T : ModelBase, new();
void Delete<T>(T childModel) where T : ModelBase, new();
}
public class TestDatabase : ITestDatabase
{
private readonly IDatabase _dbConnection;
private IEventAggregator _eventAggregator;
public TestDatabase(IDatabase dbConnection)
{
_eventAggregator = new Mock<IEventAggregator>().Object;
_dbConnection = dbConnection;
}
public void InsertMany<T>(IEnumerable<T> items) where T : ModelBase, new()
{
new BasicRepository<T>(_dbConnection, _eventAggregator).InsertMany(items.ToList());
}
public T Insert<T>(T item) where T : ModelBase, new()
{
return new BasicRepository<T>(_dbConnection, _eventAggregator).Insert(item);
}
public List<T> All<T>() where T : ModelBase, new()
{
return new BasicRepository<T>(_dbConnection, _eventAggregator).All().ToList();
}
public T Single<T>() where T : ModelBase, new()
{
return All<T>().SingleOrDefault();
}
public void Update<T>(T childModel) where T : ModelBase, new()
{
new BasicRepository<T>(_dbConnection, _eventAggregator).Update(childModel);
}
public void Delete<T>(T childModel) where T : ModelBase, new()
{
new BasicRepository<T>(_dbConnection, _eventAggregator).Delete(childModel);
}
}
}
@@ -0,0 +1,130 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Linq;
using NzbDrone.Common.Serializer;
using NzbDrone.Core.Datastore;
namespace NzbDrone.Core.Test.Framework
{
public interface IDirectDataMapper
{
List<Dictionary<string, object>> Query(string sql);
List<T> Query<T>(string sql) where T : new();
}
public class DirectDataMapper : IDirectDataMapper
{
private readonly DbProviderFactory _providerFactory;
private readonly string _connectionString;
public DirectDataMapper(IDatabase database)
{
var dataMapper = database.GetDataMapper();
_providerFactory = dataMapper.ProviderFactory;
_connectionString = dataMapper.ConnectionString;
}
private DbConnection OpenConnection()
{
var connection = _providerFactory.CreateConnection();
connection.ConnectionString = _connectionString;
connection.Open();
return connection;
}
public DataTable GetDataTable(string sql)
{
using (var connection = OpenConnection())
{
using (var cmd = connection.CreateCommand())
{
var dataTable = new DataTable();
cmd.CommandText = sql;
dataTable.Load(cmd.ExecuteReader());
return dataTable;
}
}
}
public List<Dictionary<string, object>> Query(string sql)
{
var dataTable = GetDataTable(sql);
return dataTable.Rows.Cast<DataRow>().Select(MapToDictionary).ToList();
}
public List<T> Query<T>(string sql) where T : new()
{
var dataTable = GetDataTable(sql);
return dataTable.Rows.Cast<DataRow>().Select(MapToObject<T>).ToList();
}
protected Dictionary<string, object> MapToDictionary(DataRow dataRow)
{
var item = new Dictionary<string, object>();
for (var i = 0; i < dataRow.Table.Columns.Count; i++)
{
var columnName = dataRow.Table.Columns[i].ColumnName;
object value;
if (dataRow.ItemArray[i] == DBNull.Value)
{
value = null;
}
else
{
value = dataRow.ItemArray[i];
}
item[columnName] = dataRow.ItemArray[i];
}
return item;
}
protected T MapToObject<T>(DataRow dataRow) where T : new()
{
var item = new T();
for (var i = 0; i < dataRow.Table.Columns.Count; i++)
{
var columnName = dataRow.Table.Columns[i].ColumnName;
var propertyInfo = typeof(T).GetProperty(columnName);
if (propertyInfo == null)
{
throw new Exception(string.Format("Column {0} doesn't exist on type {1}.", columnName, typeof(T)));
}
var propertyType = propertyInfo.PropertyType;
if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
{
propertyType = propertyType.GetGenericArguments()[0];
}
object value;
if (dataRow.ItemArray[i] == DBNull.Value)
{
value = null;
}
else if (dataRow.Table.Columns[i].DataType == typeof(string) && propertyType != typeof(string))
{
value = Json.Deserialize((string)dataRow.ItemArray[i], propertyType);
}
else
{
value = Convert.ChangeType(dataRow.ItemArray[i], propertyType);
}
propertyInfo.SetValue(item, value, null);
}
return item;
}
}
}
@@ -1,23 +1,41 @@
using System;
using System.Data;
using FluentMigrator;
using NUnit.Framework;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Datastore.Migration.Framework;
using NzbDrone.Test.Common.AutoMoq;
namespace NzbDrone.Core.Test.Framework
{
[Category("DbMigrationTest")]
[Category("DbTest")]
public abstract class MigrationTest<TMigration> : DbTest where TMigration : MigrationBase
public abstract class MigrationTest<TMigration> : DbTest where TMigration : NzbDroneMigrationBase
{
protected override TestDatabase WithTestDb(Action<MigrationBase> beforeMigration)
protected long MigrationVersion
{
return base.WithTestDb(m =>
get
{
if (m.GetType() == typeof(TMigration))
var attrib = (MigrationAttribute)Attribute.GetCustomAttribute(typeof(TMigration), typeof(MigrationAttribute));
return attrib.Version;
}
}
protected virtual IDirectDataMapper WithMigrationTestDb(Action<TMigration> beforeMigration = null)
{
var db = WithTestDb(new MigrationContext(MigrationType, MigrationVersion)
{
BeforeMigration = m =>
{
beforeMigration(m);
var migration = m as TMigration;
if (beforeMigration != null && migration is TMigration)
{
beforeMigration(migration);
}
}
});
return db.GetDirectDataMapper();
}
[SetUp]
@@ -25,5 +43,11 @@ namespace NzbDrone.Core.Test.Framework
{
SetupContainer();
}
[Obsolete("Don't use Mocker/Repositories in MigrationTests, query the DB.", true)]
public new AutoMoqer Mocker
{
get { return base.Mocker; }
}
}
}
@@ -0,0 +1,69 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using Moq;
using NzbDrone.Common.Serializer;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Messaging.Events;
namespace NzbDrone.Core.Test.Framework
{
public interface ITestDatabase
{
void InsertMany<T>(IEnumerable<T> items) where T : ModelBase, new();
T Insert<T>(T item) where T : ModelBase, new();
List<T> All<T>() where T : ModelBase, new();
T Single<T>() where T : ModelBase, new();
void Update<T>(T childModel) where T : ModelBase, new();
void Delete<T>(T childModel) where T : ModelBase, new();
IDirectDataMapper GetDirectDataMapper();
}
public class TestDatabase : ITestDatabase
{
private readonly IDatabase _dbConnection;
private readonly IEventAggregator _eventAggregator;
public TestDatabase(IDatabase dbConnection)
{
_eventAggregator = new Mock<IEventAggregator>().Object;
_dbConnection = dbConnection;
}
public void InsertMany<T>(IEnumerable<T> items) where T : ModelBase, new()
{
new BasicRepository<T>(_dbConnection, _eventAggregator).InsertMany(items.ToList());
}
public T Insert<T>(T item) where T : ModelBase, new()
{
return new BasicRepository<T>(_dbConnection, _eventAggregator).Insert(item);
}
public List<T> All<T>() where T : ModelBase, new()
{
return new BasicRepository<T>(_dbConnection, _eventAggregator).All().ToList();
}
public T Single<T>() where T : ModelBase, new()
{
return All<T>().SingleOrDefault();
}
public void Update<T>(T childModel) where T : ModelBase, new()
{
new BasicRepository<T>(_dbConnection, _eventAggregator).Update(childModel);
}
public void Delete<T>(T childModel) where T : ModelBase, new()
{
new BasicRepository<T>(_dbConnection, _eventAggregator).Delete(childModel);
}
public IDirectDataMapper GetDirectDataMapper()
{
return new DirectDataMapper(_dbConnection);
}
}
}
@@ -1,7 +0,0 @@
namespace NzbDrone.Core.Test.Framework
{
internal static class TestDbHelper
{
}
}