Enable running integrations tests on Linux
This commit is contained in:
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -43,6 +43,7 @@
|
||||
*.hs text=auto
|
||||
*.json text=auto
|
||||
*.xml text=auto
|
||||
*.resx text=auto
|
||||
|
||||
*.csproj text=auto merge=union
|
||||
*.vbproj text=auto merge=union
|
||||
|
||||
@@ -937,7 +937,7 @@ namespace Umbraco.Core.Persistence
|
||||
/// This is a C# implementation of T-SQL QUOTEDNAME.
|
||||
/// <paramref name="quote"/> is optional, it can be '[' (default), ']', '\'' or '"'.
|
||||
/// </remarks>
|
||||
private static string QuotedName(string name, char quote = '[')
|
||||
internal static string QuotedName(string name, char quote = '[')
|
||||
{
|
||||
switch (quote)
|
||||
{
|
||||
|
||||
10
src/Umbraco.Tests.Integration/Testing/ITestDatabase.cs
Normal file
10
src/Umbraco.Tests.Integration/Testing/ITestDatabase.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace Umbraco.Tests.Integration.Testing
|
||||
{
|
||||
public interface ITestDatabase
|
||||
{
|
||||
string ConnectionString { get; }
|
||||
int AttachEmpty();
|
||||
int AttachSchema();
|
||||
void Detach(int id);
|
||||
}
|
||||
}
|
||||
@@ -49,8 +49,10 @@ namespace Umbraco.Tests.Integration.Testing
|
||||
// we don't want persisted nucache files in tests
|
||||
builder.Services.AddTransient(factory => new PublishedSnapshotServiceOptions { IgnoreLocalDb = true });
|
||||
|
||||
#if IS_WINDOWS
|
||||
// ensure all lucene indexes are using RAM directory (no file system)
|
||||
builder.Services.AddUnique<ILuceneDirectoryFactory, LuceneRAMDirectoryFactory>();
|
||||
#endif
|
||||
|
||||
// replace this service so that it can lookup the correct file locations
|
||||
builder.Services.AddUnique<ILocalizedTextService>(GetLocalizedTextService);
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace Umbraco.Tests.Integration.Testing
|
||||
/// <summary>
|
||||
/// Manages a pool of LocalDb databases for integration testing
|
||||
/// </summary>
|
||||
public class LocalDbTestDatabase
|
||||
public class LocalDbTestDatabase : ITestDatabase
|
||||
{
|
||||
public const string InstanceName = "UmbracoTests";
|
||||
public const string DatabaseName = "UmbracoTests";
|
||||
@@ -139,7 +139,7 @@ namespace Umbraco.Tests.Integration.Testing
|
||||
|
||||
}
|
||||
|
||||
private static void AddParameter(IDbCommand cmd, UmbracoDatabase.ParameterInfo parameterInfo)
|
||||
internal static void AddParameter(IDbCommand cmd, UmbracoDatabase.ParameterInfo parameterInfo)
|
||||
{
|
||||
var p = cmd.CreateParameter();
|
||||
p.ParameterName = parameterInfo.Name;
|
||||
@@ -149,22 +149,6 @@ namespace Umbraco.Tests.Integration.Testing
|
||||
cmd.Parameters.Add(p);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
var filename = Path.Combine(_filesPath, DatabaseName).ToUpper();
|
||||
|
||||
foreach (var database in _instance.GetDatabases())
|
||||
{
|
||||
if (database.StartsWith(filename))
|
||||
_instance.DropDatabase(database);
|
||||
}
|
||||
|
||||
foreach (var file in Directory.EnumerateFiles(_filesPath))
|
||||
{
|
||||
if (file.EndsWith(".mdf") == false && file.EndsWith(".ldf") == false) continue;
|
||||
File.Delete(file);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ResetLocalDb(IDbCommand cmd)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,163 @@
|
||||
using System;
|
||||
using System.Data;
|
||||
using System.Data.SqlClient;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Migrations.Install;
|
||||
using Umbraco.Core.Persistence;
|
||||
|
||||
// ReSharper disable ConvertToUsingDeclaration
|
||||
|
||||
namespace Umbraco.Tests.Integration.Testing
|
||||
{
|
||||
/// <remarks>
|
||||
/// It's not meant to be pretty, rushed port of LocalDb.cs + LocalDbTestDatabase.cs
|
||||
/// </remarks>
|
||||
public class SqlDeveloperTestDatabase : ITestDatabase
|
||||
{
|
||||
private readonly string _masterConnectionString;
|
||||
private readonly string _databaseName;
|
||||
private readonly ILoggerFactory _loggerFactory;
|
||||
private readonly ILogger _log;
|
||||
private readonly IUmbracoDatabaseFactory _databaseFactory;
|
||||
private UmbracoDatabase.CommandInfo[] _cachedDatabaseInitCommands;
|
||||
|
||||
public SqlDeveloperTestDatabase(ILoggerFactory loggerFactory, IUmbracoDatabaseFactory databaseFactory, string masterConnectionString)
|
||||
{
|
||||
_loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory));
|
||||
_databaseFactory = databaseFactory ?? throw new ArgumentNullException(nameof(databaseFactory));
|
||||
_masterConnectionString = masterConnectionString;
|
||||
_databaseName = $"Umbraco_Integration_{Guid.NewGuid()}".Replace("-", string.Empty);
|
||||
_log = loggerFactory.CreateLogger<SqlDeveloperTestDatabase>();
|
||||
}
|
||||
|
||||
public string ConnectionString { get; private set; }
|
||||
|
||||
public int AttachEmpty()
|
||||
{
|
||||
CreateDatabase();
|
||||
return -1;
|
||||
}
|
||||
|
||||
public int AttachSchema()
|
||||
{
|
||||
CreateDatabase();
|
||||
|
||||
_log.LogInformation($"Attaching schema {_databaseName}");
|
||||
|
||||
using (var connection = new SqlConnection(ConnectionString))
|
||||
{
|
||||
connection.Open();
|
||||
using (var command = connection.CreateCommand())
|
||||
{
|
||||
RebuildSchema(command);
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public void Detach(int id)
|
||||
{
|
||||
_log.LogInformation($"Dropping database {_databaseName}");
|
||||
using (var connection = new SqlConnection(_masterConnectionString))
|
||||
{
|
||||
connection.Open();
|
||||
using (var command = connection.CreateCommand())
|
||||
{
|
||||
SetCommand(command, $@"
|
||||
ALTER DATABASE{LocalDb.QuotedName(_databaseName)}
|
||||
SET SINGLE_USER
|
||||
WITH ROLLBACK IMMEDIATE
|
||||
");
|
||||
command.ExecuteNonQuery();
|
||||
|
||||
SetCommand(command, $@"DROP DATABASE {LocalDb.QuotedName(_databaseName)}");
|
||||
command.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateDatabase()
|
||||
{
|
||||
_log.LogInformation($"Creating database {_databaseName}");
|
||||
using (var connection = new SqlConnection(_masterConnectionString))
|
||||
{
|
||||
connection.Open();
|
||||
using (var command = connection.CreateCommand())
|
||||
{
|
||||
SetCommand(command, $@"CREATE DATABASE {LocalDb.QuotedName(_databaseName)}");
|
||||
var unused = command.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
|
||||
ConnectionString = ConstructConnectionString(_masterConnectionString, _databaseName);
|
||||
}
|
||||
|
||||
private static string ConstructConnectionString(string masterConnectionString, string databaseName)
|
||||
{
|
||||
var prefix = Regex.Replace(masterConnectionString, "Database=.+?;", string.Empty);
|
||||
var connectionString = $"{prefix};Database={databaseName};";
|
||||
return connectionString.Replace(";;", ";");
|
||||
}
|
||||
|
||||
private static void SetCommand(SqlCommand command, string sql, params object[] args)
|
||||
{
|
||||
command.CommandType = CommandType.Text;
|
||||
command.CommandText = sql;
|
||||
command.Parameters.Clear();
|
||||
|
||||
for (var i = 0; i < args.Length; i++)
|
||||
{
|
||||
command.Parameters.AddWithValue("@" + i, args[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private void RebuildSchema(IDbCommand command)
|
||||
{
|
||||
if (_cachedDatabaseInitCommands != null)
|
||||
{
|
||||
foreach (var dbCommand in _cachedDatabaseInitCommands)
|
||||
{
|
||||
|
||||
if (dbCommand.Text.StartsWith("SELECT "))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
command.CommandText = dbCommand.Text;
|
||||
command.Parameters.Clear();
|
||||
|
||||
foreach (var parameterInfo in dbCommand.Parameters)
|
||||
{
|
||||
LocalDbTestDatabase.AddParameter(command, parameterInfo);
|
||||
}
|
||||
|
||||
command.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_databaseFactory.Configure(ConnectionString, Constants.DatabaseProviders.SqlServer);
|
||||
|
||||
using (var database = (UmbracoDatabase)_databaseFactory.CreateDatabase())
|
||||
{
|
||||
database.LogCommands = true;
|
||||
|
||||
using (var transaction = database.GetTransaction())
|
||||
{
|
||||
var schemaCreator = new DatabaseSchemaCreator(database, _loggerFactory.CreateLogger<DatabaseSchemaCreator>(), _loggerFactory, new UmbracoVersion());
|
||||
schemaCreator.InitializeDatabaseSchema();
|
||||
|
||||
transaction.Complete();
|
||||
|
||||
_cachedDatabaseInitCommands = database.Commands.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
44
src/Umbraco.Tests.Integration/Testing/TestDatabaseFactory.cs
Normal file
44
src/Umbraco.Tests.Integration/Testing/TestDatabaseFactory.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Umbraco.Core.Persistence;
|
||||
|
||||
namespace Umbraco.Tests.Integration.Testing
|
||||
{
|
||||
public class TestDatabaseFactory
|
||||
{
|
||||
public static ITestDatabase Create(string filesPath, ILoggerFactory loggerFactory, IUmbracoDatabaseFactory dbFactory)
|
||||
{
|
||||
return RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
|
||||
? CreateLocalDb(filesPath, loggerFactory, dbFactory)
|
||||
: CreateSqlDeveloper(loggerFactory, dbFactory);
|
||||
}
|
||||
|
||||
private static ITestDatabase CreateLocalDb(string filesPath, ILoggerFactory loggerFactory, IUmbracoDatabaseFactory dbFactory)
|
||||
{
|
||||
var localDb = new LocalDb();
|
||||
|
||||
if (!localDb.IsAvailable)
|
||||
{
|
||||
throw new InvalidOperationException("LocalDB is not available.");
|
||||
}
|
||||
|
||||
return new LocalDbTestDatabase(loggerFactory, localDb, filesPath, dbFactory);
|
||||
}
|
||||
|
||||
private static ITestDatabase CreateSqlDeveloper(ILoggerFactory loggerFactory, IUmbracoDatabaseFactory dbFactory)
|
||||
{
|
||||
// $ export SA_PASSWORD=Foobar123!
|
||||
// $ export UmbracoIntegrationTestConnectionString="Server=localhost,1433;User Id=sa;Password=$SA_PASSWORD;"
|
||||
// $ docker run -e 'ACCEPT_EULA=Y' -e "SA_PASSWORD=$SA_PASSWORD" -e 'MSSQL_PID=Developer' -p 1433:1433 -d mcr.microsoft.com/mssql/server:2017-latest-ubuntu
|
||||
var connectionString = Environment.GetEnvironmentVariable("UmbracoIntegrationTestConnectionString");
|
||||
|
||||
if (string.IsNullOrEmpty(connectionString))
|
||||
{
|
||||
throw new InvalidOperationException("ENV: UmbracoIntegrationTestConnectionString is not set");
|
||||
}
|
||||
|
||||
return new SqlDeveloperTestDatabase(loggerFactory, dbFactory, connectionString);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -242,7 +242,7 @@ namespace Umbraco.Tests.Integration.Testing
|
||||
#region LocalDb
|
||||
|
||||
private static readonly object _dbLocker = new object();
|
||||
private static LocalDbTestDatabase _dbInstance;
|
||||
private static ITestDatabase _dbInstance;
|
||||
|
||||
protected void UseTestLocalDb(IServiceProvider serviceProvider)
|
||||
{
|
||||
@@ -267,17 +267,14 @@ namespace Umbraco.Tests.Integration.Testing
|
||||
/// <remarks>
|
||||
/// There must only be ONE instance shared between all tests in a session
|
||||
/// </remarks>
|
||||
private static LocalDbTestDatabase GetOrCreateDatabase(string filesPath, ILoggerFactory loggerFactory, IUmbracoDatabaseFactory dbFactory)
|
||||
private static ITestDatabase GetOrCreateDatabase(string filesPath, ILoggerFactory loggerFactory, IUmbracoDatabaseFactory dbFactory)
|
||||
{
|
||||
lock (_dbLocker)
|
||||
{
|
||||
if (_dbInstance != null)
|
||||
return _dbInstance;
|
||||
|
||||
var localDb = new LocalDb();
|
||||
if (localDb.IsAvailable == false)
|
||||
throw new InvalidOperationException("LocalDB is not available.");
|
||||
_dbInstance = new LocalDbTestDatabase(loggerFactory, localDb, filesPath, dbFactory);
|
||||
_dbInstance = TestDatabaseFactory.Create(filesPath, loggerFactory, dbFactory);
|
||||
return _dbInstance;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,6 +119,6 @@
|
||||
</resheader>
|
||||
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
|
||||
<data name="Dictionary_Package" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>dictionary-package.xml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value>
|
||||
<value>Dictionary-Package.xml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value>
|
||||
</data>
|
||||
</root>
|
||||
</root>
|
||||
|
||||
@@ -7,6 +7,10 @@
|
||||
<LangVersion>8</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(OS)' == 'Windows_NT' ">
|
||||
<DefineConstants>IS_WINDOWS</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="TEMP\**" />
|
||||
<EmbeddedResource Remove="TEMP\**" />
|
||||
|
||||
Reference in New Issue
Block a user