Commit 53688778 authored by Michael Herndon's avatar Michael Herndon

WIP: Identity Tests refactor

- Refactor tests to use Mettle.
- Fix hoisting with the UseModelConvention extensions. Now requires to pass in ConventionSet.
   - Requires the conventionSet that matches the provider
parent edded5d8
{
<<<<<<< HEAD
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
......@@ -23,42 +22,4 @@
"processId": "${command:pickProcess}"
}
]
=======
// Use IntelliSense to find out which attributes exist for C# debugging
// Use hover for the description of the existing attributes
// For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
"version": "0.2.0",
"configurations": [
{
"name": "Nexus",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/src/Apps/ConsoleTemplate/bin/Debug/netcoreapp3.0/ConsoleTemplate.exe",
"args": [],
"cwd": "${workspaceFolder}",
"stopAtEntry": false,
"launchBrowser": {
"enabled": true,
"args": "${auto-detect-url}",
"windows": {
"command": "cmd.exe",
"args": "/C start ${auto-detect-url}"
},
"osx": {
"command": "open"
},
"linux": {
"command": "xdg-open"
}
},
"env": {
"DOTNET_ENVIRONMENT": "Development"
},
"sourceFileMap": {
"/Views": "${workspaceFolder}/Views"
}
}]
>>>>>>> parent of 85893e8... WIP: AB#1 finish up importin XUnit runners
}
\ No newline at end of file
......@@ -61,6 +61,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mettle.Xunit.Tests", "test\
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mettle.Xunit", "src\Testing\Mettle.Xunit\Mettle.Xunit.csproj", "{7837AD7B-6DB1-4B12-B13F-4221C897E717}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NerdyMishka.Identity.Tests", "test\Extensions\Identity.Tests\NerdyMishka.Identity.Tests.csproj", "{04239ACC-5ACE-4FCF-9555-F706C2A5B50B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
......@@ -135,6 +137,10 @@ Global
{7837AD7B-6DB1-4B12-B13F-4221C897E717}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7837AD7B-6DB1-4B12-B13F-4221C897E717}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7837AD7B-6DB1-4B12-B13F-4221C897E717}.Release|Any CPU.Build.0 = Release|Any CPU
{04239ACC-5ACE-4FCF-9555-F706C2A5B50B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{04239ACC-5ACE-4FCF-9555-F706C2A5B50B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{04239ACC-5ACE-4FCF-9555-F706C2A5B50B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{04239ACC-5ACE-4FCF-9555-F706C2A5B50B}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
......@@ -164,6 +170,7 @@ Global
{70060B63-843B-4DC7-A53F-92AE4B66878B} = {6444BF0F-2EED-404A-AC00-CC756F4F4ED0}
{62C1F2BF-BB1B-4EDE-BDB0-839F5B41FE5F} = {DB042499-08A3-4C34-9231-1372043915F2}
{CBFD856A-D652-4726-B3D5-C285265E9D2D} = {62C1F2BF-BB1B-4EDE-BDB0-839F5B41FE5F}
{04239ACC-5ACE-4FCF-9555-F706C2A5B50B} = {8CEB02B9-A9A9-4660-A023-41E357B52DFA}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {16666A01-3CC0-4111-8A58-9A6CA34CD76C}
......
......@@ -18,6 +18,6 @@ namespace NerdyMishka.EfCore.Identity
[Encrypt]
public string Value { get; set; }
public EmailPurpose Purpose { get; set; }
public int Purpose { get; set; } = 0;
}
}
\ No newline at end of file
......@@ -47,10 +47,12 @@ namespace NerdyMishka.EfCore.Identity
public DbSet<UserToken> UserTokens { get; set; }
/*
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
var configuration = new IdentityEfCoreConfiguration();
configuration.Apply(modelBuilder);
}
configuration.ApplyConfiguration(modelBuilder);
}*/
}
}
\ No newline at end of file
......@@ -133,8 +133,12 @@ namespace NerdyMishka.EfCore.Identity
builder.Property(o => o.Name)
.HasMaxLength(50);
builder.Property(o => o.Purpose)
.HasConversion(new EmailPurposeConverter());
builder.Property(o => o.Purpose);
//builder.HasOne(o => o.User);
//builder.Property(o => o.Purpose)
// .HasConversion(new EmailPurposeConverter());
builder.HasIndex(o => o.Purpose);
}
......
......@@ -5,6 +5,7 @@ namespace NerdyMishka.EfCore.Identity
UppercaseLetter = 1,
LowercaseLetter = 2,
Digit = 3,
SpecialCharacter = 4
SpecialCharacter = 4,
All = UppercaseLetter | LowercaseLetter | Digit | SpecialCharacter
}
}
\ No newline at end of file
......@@ -5,6 +5,11 @@ namespace NerdyMishka.EfCore.Identity
{
public class PasswordPolicy
{
public PasswordPolicy()
{
this.SyncKey = Guid.NewGuid();
}
public int Id { get; set; }
public virtual ICollection<User> Users { get; set; }
......
......@@ -6,8 +6,9 @@ using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Metadata.Conventions;
namespace NerdyMishka.EfCore.Identity
namespace NerdyMishka.EfCore
{
public static class DbContextOptionsBuilderExtenions
{
......@@ -47,7 +48,7 @@ namespace NerdyMishka.EfCore.Identity
/// <param name="builder"></param>
/// <param name="configure"></param>
/// <typeparam name="T"></typeparam>
public static void UseModelConfiguration<T>(this DbContextOptionsBuilder builder,
public static void UseModelConfiguration<T>(this DbContextOptionsBuilder<T> builder,
Action<ModelBuilder> configure) where T: DbContext
{
var configurationSet = new Microsoft.EntityFrameworkCore.Metadata.Conventions.ConventionSet();
......@@ -57,5 +58,23 @@ namespace NerdyMishka.EfCore.Identity
builder.UseModel(mb.FinalizeModel());
}
public static void UseModelConfiguration(this DbContextOptionsBuilder builder,
ConventionSet conventionSet, Action<ModelBuilder> configure)
{
// each provider has it's own convention set with a static method "Build"
// Most are located under Microsoft.EntityFrameworkCore.Metadata.Conventions
// e.g.
// Microsoft.EntityFrameworkCore.Metadata.Conventions.SqliteConventionSetBuilder.Build()
// InMemory's is slightly different e.g.
// Microsoft.EntityFrameworkCore.InMemory.Metadata.Conventions.InMemoryConventionSetBuilder.Build()
var mb = new ModelBuilder(conventionSet);
if(configure != null)
configure(mb);
builder.UseModel(mb.FinalizeModel());
}
}
}
\ No newline at end of file
using Microsoft.Extensions.DependencyInjection;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
namespace NerdyMishka.EfCore.Identity
{
public static class DbContextOptionsBuilderExtenions
{
/// <summary>
/// Replaces the Migration Table.
/// </summary>
/// <param name="builder"></param>
/// <param name="migrationTableName"></param>
/// <param name="migrationSchemaName"></param>
/// <typeparam name="TProviderRespository"></typeparam>
/// <typeparam name="TProviderReplacement"></typeparam>
/// <returns></returns>
public static void UseHistoryRespository<TProviderRespository, TProviderReplacement>(
this DbContextOptionsBuilder builder,
string migrationTableName = null,
string migrationSchemaName = null)
where TProviderReplacement: TProviderRespository
where TProviderRespository: IHistoryRepository
{
if(!string.IsNullOrWhiteSpace(migrationTableName))
{
var opts = RelationalOptionsExtension.Extract(builder.Options);
opts.WithMigrationsHistoryTableName(migrationTableName);
if(!string.IsNullOrWhiteSpace(migrationSchemaName))
opts.WithMigrationsHistoryTableSchema(migrationSchemaName);
}
builder.ReplaceService<TProviderRespository, TProviderReplacement>();
}
/// <summary>
/// Hoists the ModelConfiguration to service configration.
/// </summary>
/// <param name="builder"></param>
/// <param name="configure"></param>
/// <typeparam name="T"></typeparam>
public static void UseModelConfiguration<T>(this DbContextOptionsBuilder builder,
Action<ModelBuilder> configure) where T: DbContext
{
var configurationSet = new Microsoft.EntityFrameworkCore.Metadata.Conventions.ConventionSet();
var mb = new ModelBuilder(configurationSet);
if(configure != null)
configure(mb);
builder.UseModel(mb.FinalizeModel());
}
}
}
\ No newline at end of file
......@@ -4,6 +4,8 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using NerdyMishka.EfCore.Identity;
using System.Linq;
using System.Text;
namespace NerdyMishka.Identity
{
......@@ -47,9 +49,10 @@ namespace NerdyMishka.Identity
throw new ArgumentNullException(nameof(user));
var store = this.Db.Set<EmailAddress>();
int purpose = (int)EmailPurpose.Primary;
var email = await store.SingleOrDefaultAsync(o =>
o.UserId == user.Id &&
o.Purpose == EmailPurpose.Primary);
o.Purpose == purpose);
return email?.Value;
}
......@@ -81,9 +84,10 @@ namespace NerdyMishka.Identity
return user.Email;
var store = this.Db.Set<EmailAddress>();
int purpose = (int)EmailPurpose.Primary;
var email = await store.SingleOrDefaultAsync(o =>
o.UserId == user.Id &&
o.Purpose == EmailPurpose.Primary);
o.Purpose == purpose);
return email?.Value?.ToLowerInvariant();
......@@ -134,17 +138,24 @@ namespace NerdyMishka.Identity
if(user == null)
throw new ArgumentNullException(nameof(user));
var store = this.Db.Set<EmailAddress>();
var model = await store.SingleOrDefaultAsync(o =>
o.UserId == user.Id &&
o.Purpose == EmailPurpose.Primary);
int id = user.Id;
int purpose = (int)EmailPurpose.Primary;
var model = store.SingleOrDefault(
o => o.UserId == id && o.Purpose == purpose);
if(model == null){
model = new EmailAddress() {
UserId = user.Id,
Value = email,
Purpose = EmailPurpose.Primary
Purpose = (int)EmailPurpose.Primary
};
this.Db.Add(model);
......
......@@ -3,12 +3,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Xunit.Abstractions;
namespace Mettle
{
internal static class Extensions
{
internal static object GetDefaultValue(this TypeInfo typeInfo)
{
if (typeInfo.IsValueType)
return Activator.CreateInstance(typeInfo.AsType());
return null;
}
public static void Add<TKey, TValue>(this IDictionary<TKey, List<TValue>> dictionary, TKey key, TValue value)
{
dictionary.GetOrAdd(key).Add(value);
......
......@@ -93,7 +93,8 @@ namespace Mettle.Xunit.Sdk
if(serviceProviderFactoryAttribute != null)
{
var factoryType = serviceProviderFactoryAttribute.GetNamedArgument<Type>("FactoryType");
var factoryType = (Type)serviceProviderFactoryAttribute.GetConstructorArguments().First();
//var factoryType = serviceProviderFactoryAttribute.GetNamedArgument<Type>("FactoryType");
// TODO: consider diagnostic message if the type exists but not the interface.
if(factoryType != null && factoryType.GetInterface("IServiceProviderFactory") != null)
......
......@@ -142,6 +142,13 @@ namespace Mettle.Xunit.Sdk
.GetCustomAttributes(typeof(ServiceProviderFactoryAttribute))
.SingleOrDefault();
if(serviceProviderFactoryAttribute == null)
{
serviceProviderFactoryAttribute = TestMethod.TestClass.Class.Assembly
.GetCustomAttributes(typeof(ServiceProviderFactoryAttribute))
.SingleOrDefault();
}
if(serviceProviderFactoryAttribute != null)
{
var factoryType = serviceProviderFactoryAttribute.GetNamedArgument<Type>("FactoryType");
......@@ -152,6 +159,8 @@ namespace Mettle.Xunit.Sdk
var serviceFactory = (IServiceProviderFactory)Activator.CreateInstance(factoryType);
this.serviceProvider = serviceFactory.CreateProvider();
}
} else {
this.serviceProvider = new SimpleServiceProvider();
}
bool isTestCase = true;
......
......@@ -42,19 +42,48 @@ namespace Mettle.Xunit.Sdk
{
DisplayName = displayName;
SkipReason = skipReason;
ConstructorArguments = constructorArguments;
TestClass = TestCase.TestMethod.TestClass.Class.ToRuntimeType();
TestMethod = TestCase.Method.ToRuntimeMethod();
this.serviceProvider = serviceProvider;
var parameters = TestMethod.GetParameters();
var parameterTypes = new Type[parameters.Length];
var ctor = TestClass.GetConstructors().FirstOrDefault();
var parameters = ctor.GetParameters();
Type[] parameterTypes = null;
object[] args = null;
if(constructorArguments.Length > 0)
{
parameterTypes = new Type[parameters.Length];
for (int i = 0; i < parameters.Length; i++)
parameterTypes[i] = parameters[i].ParameterType;
args = constructorArguments ?? new object[0];
var ctorArgs = new object[parameters.Length];
Array.Copy(args, ctorArgs, args.Length);
for(int i = 0; i < parameters.Length; i++)
{
var obj = ctorArgs[i];
if(obj == null)
obj = this.serviceProvider?.GetService(parameters[i].ParameterType);
ctorArgs[i] = obj;
}
ConstructorArguments = Reflector.ConvertArguments(ctorArgs, parameterTypes);
} else {
ConstructorArguments = constructorArguments;
}
parameters = TestMethod.GetParameters();
parameterTypes = new Type[parameters.Length];
for (int i = 0; i < parameters.Length; i++)
parameterTypes[i] = parameters[i].ParameterType;
var args = testMethodArguments ?? new object[0];
args = testMethodArguments ?? new object[0];
if(parameters.Length != args.Length)
{
var methodArgs = new object[parameters.Length];
......@@ -74,6 +103,8 @@ namespace Mettle.Xunit.Sdk
TestMethodArguments = Reflector.ConvertArguments(args, parameterTypes);
}
/// <summary>
/// Gets the list of <see cref="BeforeAfterTestAttribute"/>s that will be used for this test case.
/// </summary>
......@@ -159,7 +190,19 @@ namespace Mettle.Xunit.Sdk
/// <inheritdoc/>
protected override Task<RunSummary> RunTestAsync()
=> CreateTestRunner(CreateTest(TestCase, DisplayName), MessageBus, TestClass, ConstructorArguments, TestMethod, TestMethodArguments,
SkipReason, BeforeAfterAttributes, Aggregator, CancellationTokenSource).RunAsync();
{
var test = this.CreateTest(this.TestCase, this.DisplayName);
var runner = this.CreateTestRunner(
test,
MessageBus,
TestClass,
ConstructorArguments,
TestMethod,
TestMethodArguments,
SkipReason, BeforeAfterAttributes, Aggregator, CancellationTokenSource);
return runner.RunAsync();
}
}
}
......@@ -44,6 +44,28 @@ namespace Mettle.Xunit.Sdk
{
this.collectionFixtureMappings = collectionFixtureMappings;
this.serviceProvider = serviceProvider;
if(this.serviceProvider == null)
{
var serviceProviderFactoryAttribute = testClass.Class.Assembly
.GetCustomAttributes(typeof(ServiceProviderFactoryAttribute))
.SingleOrDefault();
if(serviceProviderFactoryAttribute != null)
{
var factoryType = serviceProviderFactoryAttribute.GetNamedArgument<Type>("FactoryType");
// TODO: consider diagnostic message if the type exists but not the interface.
if(factoryType != null && factoryType.GetInterface("IServiceProviderFactory") != null)
{
var serviceFactory = (IServiceProviderFactory)Activator.CreateInstance(factoryType);
this.serviceProvider = serviceFactory.CreateProvider();
}
}
this.serviceProvider = this.serviceProvider ?? new SimpleServiceProvider();
}
}
/// <summary>
......@@ -56,6 +78,52 @@ namespace Mettle.Xunit.Sdk
/// </summary>
protected HashSet<IAsyncLifetime> InitializedAsyncFixtures { get; set; } = new HashSet<IAsyncLifetime>();
/// <summary>
/// Creates the arguments for the test class constructor. Attempts to resolve each parameter
/// individually, and adds an error when the constructor arguments cannot all be provided.
/// If the class is static, does not look for constructor, since one will not be needed.
/// </summary>
/// <returns>The test class constructor arguments.</returns>
protected override object[] CreateTestClassConstructorArguments()
{
Console.WriteLine("Create Test Called");
var isStaticClass = Class.Type.GetTypeInfo().IsAbstract && Class.Type.GetTypeInfo().IsSealed;
if (!isStaticClass)
{
var ctor = SelectTestClassConstructor();
if (ctor != null)
{
var unusedArguments = new List<Tuple<int, ParameterInfo>>();
var parameters = ctor.GetParameters();
object[] constructorArguments = new object[parameters.Length];
for (int idx = 0; idx < parameters.Length; ++idx)
{
var parameter = parameters[idx];
object argumentValue;
if (TryGetConstructorArgument(ctor, idx, parameter, out argumentValue))
constructorArguments[idx] = argumentValue;
else if (parameter.HasDefaultValue)
constructorArguments[idx] = parameter.DefaultValue;
else if (parameter.IsOptional)
constructorArguments[idx] = parameter.ParameterType.GetTypeInfo().GetDefaultValue();
else if (parameter.GetCustomAttribute<ParamArrayAttribute>() != null)
constructorArguments[idx] = Array.CreateInstance(parameter.ParameterType, 0);
else
unusedArguments.Add(Tuple.Create(idx, parameter));
}
if (unusedArguments.Count > 0)
Aggregator.Add(new TestClassException(FormatConstructorArgsMissingMessage(ctor, unusedArguments)));
return constructorArguments;
}
}
return new object[0];
}
/// <summary>
/// Creates the instance of a class fixture type to be used by the test class. If the fixture can be created,
/// it should be placed into the <see cref="ClassFixtureMappings"/> dictionary; if it cannot, then the method
......
......@@ -94,6 +94,7 @@ namespace Mettle.Xunit.Sdk
/// <inheritdoc/>
protected override async void RunTestCases(IEnumerable<IXunitTestCase> testCases, IMessageSink executionMessageSink, ITestFrameworkExecutionOptions executionOptions)
{
Console.WriteLine("Run Test Cases called");
using (var assemblyRunner = new MettleTestAssemblyRunner(TestAssembly, testCases, DiagnosticMessageSink, executionMessageSink, executionOptions))
await assemblyRunner.RunAsync();
}
......
......@@ -43,7 +43,26 @@ namespace Mettle.Xunit.Sdk
: base(test, messageBus, testClass, constructorArguments, testMethod, testMethodArguments, aggregator, cancellationTokenSource)
{
this.beforeAfterAttributes = beforeAfterAttributes;
this.serviceProvider = serviceProvider;
this.serviceProvider = serviceProvider;
}
/// <summary>
/// Creates the test class, unless the test method is static or there have already been errors. Note that
/// this method times the creation of the test class (using <see cref="Timer"/>). It is also responsible for
/// sending the <see cref="ITestClassConstructionStarting"/>and <see cref="ITestClassConstructionFinished"/>
/// messages, so if you override this method without calling the base, you are responsible for all of this behavior.
/// This method should NEVER throw; any exceptions should be placed into the <see cref="Aggregator"/>.
/// </summary>
/// <returns>The class instance, if appropriate; <c>null</c>, otherwise</returns>
protected override object CreateTestClass()
{
object testClass = null;
if (!TestMethod.IsStatic && !Aggregator.HasExceptions)
testClass = Test.CreateTestClass(TestClass, ConstructorArguments, MessageBus, Timer, CancellationTokenSource);
return testClass;
}
/// <summary>
......@@ -55,6 +74,7 @@ namespace Mettle.Xunit.Sdk
/// <inheritdoc/>
protected override Task BeforeTestMethodInvokedAsync()
{
foreach (var beforeAfterAttribute in beforeAfterAttributes)
{
var attributeName = beforeAfterAttribute.GetType().Name;
......
......@@ -44,9 +44,11 @@ namespace Mettle.Xunit.Sdk
: base(test, messageBus, testClass, constructorArguments, testMethod, testMethodArguments, skipReason, aggregator, cancellationTokenSource)
{
this.beforeAfterAttributes = beforeAfterAttributes;
this.serviceProvider = serviceProvider;
this.serviceProvider = serviceProvider;
}
/// <summary>
/// Gets the list of <see cref="BeforeAfterTestAttribute"/>s for this test.
/// </summary>
......@@ -56,6 +58,7 @@ namespace Mettle.Xunit.Sdk
/// <inheritdoc/>
protected override async Task<Tuple<decimal, string>> InvokeTestAsync(ExceptionAggregator aggregator)
{
var output = string.Empty;
TestOutputHelper testOutputHelper = null;
......@@ -86,7 +89,8 @@ namespace Mettle.Xunit.Sdk
/// <param name="aggregator">The exception aggregator used to run code and collect exceptions.</param>
/// <returns>Returns the execution time (in seconds) spent running the test method.</returns>
protected virtual Task<decimal> InvokeTestMethodAsync(ExceptionAggregator aggregator)
=> new MettleTestInvoker(
{
var invoker = new MettleTestInvoker(
Test,
MessageBus,
TestClass,
......@@ -96,6 +100,10 @@ namespace Mettle.Xunit.Sdk
BeforeAfterAttributes,
aggregator,
CancellationTokenSource,
this.serviceProvider).RunAsync();
this.serviceProvider
);
return invoker.RunAsync();
}
}
}
using System;
using System.Collections.Concurrent;
using Xunit.Abstractions;
using Xunit.Sdk;
namespace Mettle
{
......@@ -13,6 +15,7 @@ namespace Mettle
public SimpleServiceProvider()
{
factories.TryAdd(typeof(IAssert), (s) => { return AssertImpl.Current; });
factories.TryAdd(typeof(ITestOutputHelper), (s) => {return new TestOutputHelper(); });
}
public void AddSingleton(Type type, object instance)
......
using Mettle;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using NerdyMishka.EfCore;
using NerdyMishka.EfCore.Identity;
namespace Tests
{
public class IdentityDbContextSchemaTests
{
[UnitTest]
public void Test(IAssert assert)
{
var collection = new Microsoft.Extensions.DependencyInjection.ServiceCollection();
collection.AddDbContext<IdentityDb>((builder) => {
builder.UseInMemoryDatabase("DataSource=:memory:");
builder.UseModelConfiguration(
Microsoft.EntityFrameworkCore.InMemory.Metadata.Conventions.InMemoryConventionSetBuilder.Build(),
(mb) => {
var module = new IdentityEfCoreConfiguration(null, null);
module.Configure(mb.Entity<User>());
module.Configure(mb.Entity<Permission>());
module.Configure(mb.Entity<Role>());
module.Configure(mb.Entity<Organization>());
module.Configure(mb.Entity<PasswordPolicy>());
module.Configure(mb.Entity<PasswordLogin>());
module.Configure(mb.Entity<ApiKey>());
module.Configure(mb.Entity<Domain>());
module.Configure(mb.Entity<EmailAddress>());
module.Configure(mb.Entity<ApiKeyRole>());
module.Configure(mb.Entity<RolePermission>());
module.Configure(mb.Entity<UserRole>());
module.Configure(mb.Entity<UserClaim>());
module.Configure(mb.Entity<UserToken>());
module.Configure(mb.Entity<UserLogin>());
module.Configure(mb.Entity<RoleClaim>());
SeedData.ApplyUsers(mb);
});
});
var sp = collection.BuildServiceProvider();
var db = sp.GetService<IdentityDb>();
assert.NotNull(db);
// if there are any issues, this will throw