mirror of
https://github.com/bitwarden/server.git
synced 2026-06-01 01:55:55 -05:00
This produces inherently flaky tests because it works by fully migrating a database, and then re-running a specified migration (the data migration) out of order. This means that subsequent changes to the db schema will cause the migration tests to fail, because the data migration will be re-run after the schema has changed. Needs to be fixed before use.
190 lines
6.1 KiB
C#
190 lines
6.1 KiB
C#
using System.Reflection;
|
|
using Bit.Core.Enums;
|
|
using Bit.Core.Settings;
|
|
using Bit.Infrastructure.Dapper;
|
|
using Bit.Infrastructure.EntityFramework;
|
|
using Microsoft.Extensions.Caching.Distributed;
|
|
using Microsoft.Extensions.Configuration;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using Microsoft.Extensions.Logging;
|
|
using Microsoft.Extensions.Time.Testing;
|
|
using Xunit;
|
|
using Xunit.Sdk;
|
|
using Xunit.v3;
|
|
|
|
namespace Bit.Infrastructure.IntegrationTest;
|
|
|
|
public class DatabaseDataAttribute : DataAttribute
|
|
{
|
|
private static IConfiguration? _cachedConfiguration;
|
|
private static IConfiguration GetConfiguration()
|
|
{
|
|
return _cachedConfiguration ??= new ConfigurationBuilder()
|
|
.AddUserSecrets<DatabaseDataAttribute>(optional: true, reloadOnChange: false)
|
|
.AddEnvironmentVariables("BW_TEST_")
|
|
.AddCommandLine(Environment.GetCommandLineArgs())
|
|
.Build();
|
|
}
|
|
|
|
|
|
public bool SelfHosted { get; set; }
|
|
public bool UseFakeTimeProvider { get; set; }
|
|
|
|
public override ValueTask<IReadOnlyCollection<ITheoryDataRow>> GetData(MethodInfo testMethod, DisposalTracker disposalTracker)
|
|
{
|
|
var config = GetConfiguration();
|
|
|
|
HashSet<SupportedDatabaseProviders> unconfiguredDatabases =
|
|
[
|
|
SupportedDatabaseProviders.MySql,
|
|
SupportedDatabaseProviders.Postgres,
|
|
SupportedDatabaseProviders.Sqlite,
|
|
SupportedDatabaseProviders.SqlServer
|
|
];
|
|
|
|
var theories = new List<ITheoryDataRow>();
|
|
|
|
foreach (var database in config.GetDatabases())
|
|
{
|
|
unconfiguredDatabases.Remove(database.Type);
|
|
|
|
if (!database.Enabled)
|
|
{
|
|
var theory = new TheoryDataRow()
|
|
.WithSkip("Not-Enabled")
|
|
.WithTrait("Database", database.Type.ToString());
|
|
theory.Label = database.Type.ToString();
|
|
theories.Add(theory);
|
|
continue;
|
|
}
|
|
|
|
var services = new ServiceCollection();
|
|
AddCommonServices(services);
|
|
|
|
if (database.Type == SupportedDatabaseProviders.SqlServer && !database.UseEf)
|
|
{
|
|
// Dapper services
|
|
AddDapperServices(services, database);
|
|
}
|
|
else
|
|
{
|
|
// Ef services
|
|
AddEfServices(services, database);
|
|
}
|
|
|
|
var serviceProvider = services.BuildServiceProvider();
|
|
disposalTracker.Add(serviceProvider);
|
|
|
|
var serviceTheory = new ServiceBasedTheoryDataRow(serviceProvider, testMethod)
|
|
.WithTrait("Database", database.Type.ToString())
|
|
.WithTrait("ConnectionString", database.ConnectionString);
|
|
|
|
serviceTheory.Label = database.Type.ToString();
|
|
theories.Add(serviceTheory);
|
|
}
|
|
|
|
foreach (var unconfiguredDatabase in unconfiguredDatabases)
|
|
{
|
|
var theory = new TheoryDataRow()
|
|
.WithSkip("Unconfigured")
|
|
.WithTrait("Database", unconfiguredDatabase.ToString());
|
|
theory.Label = unconfiguredDatabase.ToString();
|
|
theories.Add(theory);
|
|
}
|
|
|
|
return new(theories);
|
|
}
|
|
|
|
private void AddCommonServices(IServiceCollection services)
|
|
{
|
|
// Common services
|
|
services.AddDataProtection();
|
|
services.AddLogging(logging =>
|
|
{
|
|
logging.AddProvider(new XUnitLoggerProvider());
|
|
});
|
|
if (UseFakeTimeProvider)
|
|
{
|
|
services.AddSingleton<TimeProvider, FakeTimeProvider>();
|
|
}
|
|
}
|
|
|
|
private void AddDapperServices(IServiceCollection services, Database database)
|
|
{
|
|
var globalSettings = new GlobalSettings
|
|
{
|
|
DatabaseProvider = "sqlServer",
|
|
SqlServer = new GlobalSettings.SqlSettings
|
|
{
|
|
ConnectionString = database.ConnectionString,
|
|
},
|
|
PasswordlessAuth = new GlobalSettings.PasswordlessAuthSettings
|
|
{
|
|
UserRequestExpiration = TimeSpan.FromMinutes(15),
|
|
}
|
|
};
|
|
services.AddDapperRepositories(SelfHosted);
|
|
services.AddSingleton(globalSettings);
|
|
services.AddSingleton<IGlobalSettings>(globalSettings);
|
|
services.AddSingleton(database);
|
|
services.AddDistributedSqlServerCache(o =>
|
|
{
|
|
o.ConnectionString = database.ConnectionString;
|
|
o.SchemaName = "dbo";
|
|
o.TableName = "Cache";
|
|
});
|
|
}
|
|
|
|
private void AddEfServices(IServiceCollection services, Database database)
|
|
{
|
|
services.SetupEntityFramework(database.ConnectionString, database.Type);
|
|
|
|
var globalSettings = new GlobalSettings
|
|
{
|
|
PasswordlessAuth = new GlobalSettings.PasswordlessAuthSettings
|
|
{
|
|
UserRequestExpiration = TimeSpan.FromMinutes(15),
|
|
},
|
|
};
|
|
services.AddPasswordManagerEFRepositories(SelfHosted);
|
|
services.AddSingleton(globalSettings);
|
|
services.AddSingleton<IGlobalSettings>(globalSettings);
|
|
|
|
services.AddSingleton(database);
|
|
services.AddSingleton<IDistributedCache, EntityFrameworkCache>();
|
|
}
|
|
|
|
public override bool SupportsDiscoveryEnumeration()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
private class ServiceBasedTheoryDataRow : TheoryDataRowBase
|
|
{
|
|
private readonly IServiceProvider _serviceProvider;
|
|
private readonly MethodInfo _testMethod;
|
|
|
|
public ServiceBasedTheoryDataRow(IServiceProvider serviceProvider, MethodInfo testMethod)
|
|
{
|
|
_serviceProvider = serviceProvider;
|
|
_testMethod = testMethod;
|
|
}
|
|
|
|
protected override object?[] GetData()
|
|
{
|
|
var parameters = _testMethod.GetParameters();
|
|
|
|
var services = new object?[parameters.Length];
|
|
|
|
for (var i = 0; i < parameters.Length; i++)
|
|
{
|
|
var parameter = parameters[i];
|
|
// TODO: Could support keyed services/optional/nullable
|
|
services[i] = _serviceProvider.GetRequiredService(parameter.ParameterType);
|
|
}
|
|
|
|
return services;
|
|
}
|
|
}
|
|
}
|