Files
server/test/Infrastructure.IntegrationTest/DatabaseDataAttribute.cs
Thomas Rittson 195a712b6d [PM-37482] Disable migration tester (#7633)
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.
2026-05-13 19:36:32 -05:00

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;
}
}
}