mirror of
https://github.com/Readarr/Readarr.git
synced 2026-04-26 22:46:37 -04:00
Updated db migration testing framework so we only run migrations up to the one we're testing.
fixes #902
This commit is contained in:
@@ -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
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user