mirror of https://github.com/abpframework/abp.git
43 changed files with 651 additions and 53 deletions
@ -1,13 +0,0 @@ |
|||
namespace Volo.Abp.Application.Dtos |
|||
{ |
|||
/// <summary>
|
|||
/// This interface is defined to standardize to set "Total Count of Items" to a DTO for long type.
|
|||
/// </summary>
|
|||
public interface IHasLongTotalCount |
|||
{ |
|||
/// <summary>
|
|||
/// Total count of Items.
|
|||
/// </summary>
|
|||
long TotalCount { get; set; } |
|||
} |
|||
} |
|||
@ -1,6 +1,6 @@ |
|||
using AutoMapper; |
|||
|
|||
namespace Volo.Abp.Identity.ObjectMappings |
|||
namespace Volo.Abp.Identity |
|||
{ |
|||
public class AbpIdentityApplicationModuleAutoMapperProfile : Profile |
|||
{ |
|||
@ -0,0 +1,21 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<AssemblyName>Volo.Abp.MultiTenancy.Application.Contracts</AssemblyName> |
|||
<PackageId>Volo.Abp.MultiTenancy.Application.Contracts</PackageId> |
|||
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback> |
|||
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute> |
|||
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute> |
|||
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\Volo.Abp.Ddd\Volo.Abp.Ddd.csproj" /> |
|||
<ProjectReference Include="..\Volo.Abp.MultiTenancy.Domain.Shared\Volo.Abp.MultiTenancy.Domain.Shared.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,15 @@ |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace Volo.Abp.MultiTenancy |
|||
{ |
|||
[DependsOn(typeof(AbpDddModule))] |
|||
[DependsOn(typeof(AbpMultiTenancyDomainSharedModule))] |
|||
public class AbpMultiTenancyApplicationContractsModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(IServiceCollection services) |
|||
{ |
|||
services.AddAssemblyOf<AbpMultiTenancyApplicationContractsModule>(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
using Volo.Abp.Application.Dtos; |
|||
|
|||
namespace Volo.Abp.MultiTenancy |
|||
{ |
|||
public class GetTenantsInput : PagedAndSortedResultRequestDto |
|||
{ |
|||
public string Filter { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,10 @@ |
|||
using System; |
|||
using Volo.Abp.Application.Services; |
|||
|
|||
namespace Volo.Abp.MultiTenancy |
|||
{ |
|||
public interface ITenantAppService : IAsyncCrudAppService<TenantDto, Guid, GetTenantsInput, TenantCreateDto, TenantUpdateDto> |
|||
{ |
|||
//TODO: Manage connection strings
|
|||
} |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
namespace Volo.Abp.MultiTenancy |
|||
{ |
|||
public class TenantCreateDto : TenantCreateOrUpdateDtoBase |
|||
{ |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
namespace Volo.Abp.MultiTenancy |
|||
{ |
|||
public abstract class TenantCreateOrUpdateDtoBase |
|||
{ |
|||
public string Name { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,10 @@ |
|||
using System; |
|||
using Volo.Abp.Application.Dtos; |
|||
|
|||
namespace Volo.Abp.MultiTenancy |
|||
{ |
|||
public class TenantDto : EntityDto<Guid> |
|||
{ |
|||
public string Name { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
namespace Volo.Abp.MultiTenancy |
|||
{ |
|||
public class TenantUpdateDto : TenantCreateOrUpdateDtoBase |
|||
{ |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<AssemblyName>Volo.Abp.MultiTenancy.Application</AssemblyName> |
|||
<PackageId>Volo.Abp.MultiTenancy.Application</PackageId> |
|||
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback> |
|||
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute> |
|||
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute> |
|||
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\Volo.Abp.MultiTenancy.Application.Contracts\Volo.Abp.MultiTenancy.Application.Contracts.csproj" /> |
|||
<ProjectReference Include="..\Volo.Abp.MultiTenancy.Domain\Volo.Abp.MultiTenancy.Domain.csproj" /> |
|||
<ProjectReference Include="..\Volo.Abp.UI\Volo.Abp.UI.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,21 @@ |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.AutoMapper; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace Volo.Abp.MultiTenancy |
|||
{ |
|||
[DependsOn(typeof(AbpMultiTenancyDomainModule))] |
|||
[DependsOn(typeof(AbpMultiTenancyApplicationContractsModule))] |
|||
public class AbpMultiTenancyApplicationModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(IServiceCollection services) |
|||
{ |
|||
services.Configure<AbpAutoMapperOptions>(options => |
|||
{ |
|||
options.AddProfile<AbpMultiTenancyApplicationModuleAutoMapperProfile>(validate: true); |
|||
}); |
|||
|
|||
services.AddAssemblyOf<AbpMultiTenancyApplicationModule>(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
using AutoMapper; |
|||
|
|||
namespace Volo.Abp.MultiTenancy |
|||
{ |
|||
public class AbpMultiTenancyApplicationModuleAutoMapperProfile : Profile |
|||
{ |
|||
public AbpMultiTenancyApplicationModuleAutoMapperProfile() |
|||
{ |
|||
CreateMap<Tenant, TenantDto>(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
using Volo.Abp.Application.Services; |
|||
|
|||
namespace Volo.Abp.MultiTenancy |
|||
{ |
|||
public class MultiTenancyAppServiceBase : ApplicationService |
|||
{ |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,63 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Application.Dtos; |
|||
|
|||
namespace Volo.Abp.MultiTenancy |
|||
{ |
|||
public class TenantAppService : MultiTenancyAppServiceBase, ITenantAppService |
|||
{ |
|||
private readonly ITenantRepository _tenantRepository; |
|||
private readonly ITenantManager _tenantManager; |
|||
|
|||
public TenantAppService(ITenantRepository tenantRepository, ITenantManager tenantManager) |
|||
{ |
|||
_tenantRepository = tenantRepository; |
|||
_tenantManager = tenantManager; |
|||
} |
|||
|
|||
public async Task<TenantDto> GetAsync(Guid id) |
|||
{ |
|||
return ObjectMapper.Map<Tenant, TenantDto>( |
|||
await _tenantRepository.GetAsync(id) |
|||
); |
|||
} |
|||
|
|||
public async Task<PagedResultDto<TenantDto>> GetListAsync(GetTenantsInput input) |
|||
{ |
|||
var count = await _tenantRepository.GetCountAsync(); |
|||
var list = await _tenantRepository.GetListAsync(input.Sorting, input.MaxResultCount, input.SkipCount, input.Filter); |
|||
|
|||
return new PagedResultDto<TenantDto>( |
|||
count, |
|||
ObjectMapper.Map<List<Tenant>, List<TenantDto>>(list) |
|||
); |
|||
} |
|||
|
|||
public async Task<TenantDto> CreateAsync(TenantCreateDto input) |
|||
{ |
|||
var tenant = await _tenantManager.CreateAsync(input.Name); |
|||
await _tenantRepository.InsertAsync(tenant); |
|||
return ObjectMapper.Map<Tenant, TenantDto>(tenant); |
|||
} |
|||
|
|||
public async Task<TenantDto> UpdateAsync(Guid id, TenantUpdateDto input) |
|||
{ |
|||
var tenant = await _tenantRepository.GetAsync(id); |
|||
await _tenantManager.ChangeNameAsync(tenant, input.Name); |
|||
await _tenantRepository.UpdateAsync(tenant); |
|||
return ObjectMapper.Map<Tenant, TenantDto>(tenant); |
|||
} |
|||
|
|||
public async Task DeleteAsync(Guid id) |
|||
{ |
|||
var tenant = await _tenantRepository.FindAsync(id); |
|||
if (tenant == null) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
await _tenantRepository.DeleteAsync(tenant); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,2 @@ |
|||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> |
|||
<s:String x:Key="/Default/CodeInspection/CSharpLanguageProject/LanguageLevel/@EntryValue">CSharp71</s:String></wpf:ResourceDictionary> |
|||
@ -0,0 +1,14 @@ |
|||
using System.Threading.Tasks; |
|||
using JetBrains.Annotations; |
|||
using Volo.Abp.Domain.Services; |
|||
|
|||
namespace Volo.Abp.MultiTenancy |
|||
{ |
|||
public interface ITenantManager : IDomainService |
|||
{ |
|||
[NotNull] |
|||
Task<Tenant> CreateAsync([NotNull] string name); |
|||
|
|||
Task ChangeNameAsync([NotNull] Tenant tenant, [NotNull] string name); |
|||
} |
|||
} |
|||
@ -0,0 +1,43 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Domain.Services; |
|||
using Volo.Abp.Ui; |
|||
|
|||
namespace Volo.Abp.MultiTenancy |
|||
{ |
|||
public class TenantManager : DomainService, ITenantManager |
|||
{ |
|||
private readonly ITenantRepository _tenantRepository; |
|||
|
|||
public TenantManager(ITenantRepository tenantRepository) |
|||
{ |
|||
_tenantRepository = tenantRepository; |
|||
} |
|||
|
|||
public async Task<Tenant> CreateAsync(string name) |
|||
{ |
|||
Check.NotNull(name, nameof(name)); |
|||
|
|||
await ValidateNameAsync(name); |
|||
return new Tenant(GuidGenerator.Create(), name); |
|||
} |
|||
|
|||
public async Task ChangeNameAsync(Tenant tenant, string name) |
|||
{ |
|||
Check.NotNull(tenant, nameof(tenant)); |
|||
Check.NotNull(name, nameof(name)); |
|||
|
|||
await ValidateNameAsync(name, tenant.Id); |
|||
tenant.SetName(name); |
|||
} |
|||
|
|||
protected virtual async Task ValidateNameAsync(string name, Guid? expectedId = null) |
|||
{ |
|||
var tenant = await _tenantRepository.FindByNameAsync(name); |
|||
if (tenant != null && tenant.Id != expectedId) |
|||
{ |
|||
throw new UserFriendlyException("Duplicate tenancy name: " + name); //TODO: A domain exception would be better..?
|
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,2 @@ |
|||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> |
|||
<s:String x:Key="/Default/CodeInspection/CSharpLanguageProject/LanguageLevel/@EntryValue">CSharp71</s:String></wpf:ResourceDictionary> |
|||
@ -1,30 +1,37 @@ |
|||
using System; |
|||
using JetBrains.Annotations; |
|||
using Microsoft.EntityFrameworkCore; |
|||
|
|||
namespace Volo.Abp.MultiTenancy.EntityFrameworkCore |
|||
{ |
|||
public static class MultiTenancyDbContextModelCreatingExtensions |
|||
{ |
|||
public static void ConfigureMultiTenancy(this IMultiTenancyDbContext dbContext, ModelBuilder builder) |
|||
public static void ConfigureMultiTenancy(this IMultiTenancyDbContext dbContext, ModelBuilder builder, string tablePrefix = "", [CanBeNull] string schema = null) |
|||
{ |
|||
if (tablePrefix.IsNullOrWhiteSpace()) |
|||
{ |
|||
tablePrefix = ""; |
|||
} |
|||
|
|||
builder.Entity<Tenant>(b => |
|||
{ |
|||
b.ToTable("MtTenants"); //TODO: Make all table and schema names changeable
|
|||
b.ToTable(tablePrefix + "Tenants", schema); |
|||
|
|||
b.Property(t => t.Name).IsRequired().HasMaxLength(TenantConsts.MaxNameLength); |
|||
|
|||
b.HasMany(u => u.ConnectionStrings).WithOne().HasForeignKey(uc => uc.TenantId).IsRequired(); |
|||
|
|||
b.HasIndex(u => u.Name); |
|||
b.HasIndex(u => u.Name).IsUnique(); |
|||
}); |
|||
|
|||
builder.Entity<TenantConnectionString>(b => |
|||
{ |
|||
b.ToTable("MtTenantConnectionStrings"); |
|||
b.ToTable(tablePrefix + "TenantConnectionStrings", schema); |
|||
|
|||
b.HasKey(x => new {x.TenantId, x.Name}); |
|||
|
|||
b.Property(cs => cs.Name).IsRequired().HasMaxLength(TenantConnectionStringConsts.MaxNameLength); |
|||
b.Property(cs => cs.Value).IsRequired().HasMaxLength(TenantConnectionStringConsts.MaxValueLength); |
|||
|
|||
b.HasIndex(cs => cs.TenantId); |
|||
}); |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,26 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netcoreapp2.0</TargetFramework> |
|||
<AssemblyName>Volo.Abp.MultiTenancy.Application.Tests</AssemblyName> |
|||
<PackageId>Volo.Abp.MultiTenancy.Application.Tests</PackageId> |
|||
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles> |
|||
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute> |
|||
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute> |
|||
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\src\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" /> |
|||
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" /> |
|||
<ProjectReference Include="..\..\src\Volo.Abp.MultiTenancy.Application\Volo.Abp.MultiTenancy.Application.csproj" /> |
|||
<ProjectReference Include="..\..\src\Volo.Abp.MultiTenancy.EntityFrameworkCore\Volo.Abp.MultiTenancy.EntityFrameworkCore.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.3.0" /> |
|||
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="2.0.0" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,30 @@ |
|||
using System; |
|||
using Volo.Abp.MultiTenancy.EntityFrameworkCore; |
|||
using Volo.Abp.TestBase; |
|||
|
|||
namespace Volo.Abp.MultiTenancy |
|||
{ |
|||
public class AbpMultiTenancyApplicationTestBase : AbpIntegratedTest<AbpMultiTenancyApplicationTestModule> |
|||
{ |
|||
protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) |
|||
{ |
|||
options.UseAutofac(); |
|||
} |
|||
|
|||
protected virtual void UsingDbContext(Action<IMultiTenancyDbContext> action) |
|||
{ |
|||
using (var dbContext = GetRequiredService<IMultiTenancyDbContext>()) |
|||
{ |
|||
action.Invoke(dbContext); |
|||
} |
|||
} |
|||
|
|||
protected virtual T UsingDbContext<T>(Func<IMultiTenancyDbContext, T> action) |
|||
{ |
|||
using (var dbContext = GetRequiredService<IMultiTenancyDbContext>()) |
|||
{ |
|||
return action.Invoke(dbContext); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,55 @@ |
|||
using System; |
|||
using Microsoft.EntityFrameworkCore; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.Autofac; |
|||
using Volo.Abp.EntityFrameworkCore; |
|||
using Volo.Abp.MultiTenancy.EntityFrameworkCore; |
|||
using Volo.Abp.Modularity; |
|||
using Volo.Abp.Uow; |
|||
|
|||
namespace Volo.Abp.MultiTenancy |
|||
{ |
|||
[DependsOn( |
|||
typeof(AbpMultiTenancyApplicationModule), |
|||
typeof(AbpMultiTenancyEntityFrameworkCoreModule), |
|||
typeof(AbpAutofacModule))] |
|||
public class AbpMultiTenancyApplicationTestModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(IServiceCollection services) |
|||
{ |
|||
services.AddAssemblyOf<AbpMultiTenancyApplicationTestModule>(); |
|||
|
|||
services.AddEntityFrameworkInMemoryDatabase(); |
|||
|
|||
var databaseName = Guid.NewGuid().ToString(); |
|||
|
|||
services.Configure<AbpDbContextOptions>(options => |
|||
{ |
|||
options.Configure(context => |
|||
{ |
|||
context.DbContextOptions.UseInMemoryDatabase(databaseName); |
|||
}); |
|||
}); |
|||
|
|||
services.Configure<UnitOfWorkDefaultOptions>(options => |
|||
{ |
|||
options.TransactionBehavior = UnitOfWorkTransactionBehavior.Disabled; //EF in-memory database does not support transactions
|
|||
}); |
|||
} |
|||
|
|||
public override void OnApplicationInitialization(ApplicationInitializationContext context) |
|||
{ |
|||
SeedTestData(context); |
|||
} |
|||
|
|||
private static void SeedTestData(ApplicationInitializationContext context) |
|||
{ |
|||
using (var scope = context.ServiceProvider.CreateScope()) |
|||
{ |
|||
scope.ServiceProvider |
|||
.GetRequiredService<AbpMultiTenancyTestDataBuilder>() |
|||
.Build(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Threading; |
|||
|
|||
namespace Volo.Abp.MultiTenancy |
|||
{ |
|||
public class AbpMultiTenancyTestDataBuilder : ITransientDependency |
|||
{ |
|||
private readonly ITenantRepository _tenantRepository; |
|||
private readonly ITenantManager _tenantManager; |
|||
|
|||
public AbpMultiTenancyTestDataBuilder( |
|||
ITenantRepository tenantRepository, |
|||
ITenantManager tenantManager) |
|||
{ |
|||
_tenantRepository = tenantRepository; |
|||
_tenantManager = tenantManager; |
|||
} |
|||
|
|||
public void Build() |
|||
{ |
|||
AsyncHelper.RunSync(AddTenantsAsync); |
|||
} |
|||
|
|||
private async Task AddTenantsAsync() |
|||
{ |
|||
var acme = await _tenantManager.CreateAsync("acme"); |
|||
await _tenantRepository.InsertAsync(acme); |
|||
|
|||
var volosoft = await _tenantManager.CreateAsync("volosoft"); |
|||
await _tenantRepository.InsertAsync(volosoft); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,113 @@ |
|||
using System; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Shouldly; |
|||
using Volo.Abp.Ui; |
|||
using Xunit; |
|||
|
|||
namespace Volo.Abp.MultiTenancy |
|||
{ |
|||
public class TenantAppService_Tests : AbpMultiTenancyApplicationTestBase |
|||
{ |
|||
private readonly ITenantAppService _tenantAppService; |
|||
|
|||
public TenantAppService_Tests() |
|||
{ |
|||
_tenantAppService = GetRequiredService<ITenantAppService>(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task GetAsync() |
|||
{ |
|||
var tenantInDb = UsingDbContext(dbContext => dbContext.Tenants.First()); |
|||
var tenant = await _tenantAppService.GetAsync(tenantInDb.Id); |
|||
tenant.Name.ShouldBe(tenantInDb.Name); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task GetListAsync() |
|||
{ |
|||
var result = await _tenantAppService.GetListAsync(new GetTenantsInput()); |
|||
result.TotalCount.ShouldBeGreaterThan(0); |
|||
result.Items.ShouldContain(t => t.Name == "acme"); |
|||
result.Items.ShouldContain(t => t.Name == "volosoft"); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task GetListAsync_Filtered() |
|||
{ |
|||
var result = await _tenantAppService.GetListAsync(new GetTenantsInput { Filter = "volo" }); |
|||
result.TotalCount.ShouldBeGreaterThan(0); |
|||
result.Items.ShouldNotContain(t => t.Name == "acme"); |
|||
result.Items.ShouldContain(t => t.Name == "volosoft"); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task GetListAsync_Sorted_Descending_By_Name() |
|||
{ |
|||
var result = await _tenantAppService.GetListAsync(new GetTenantsInput { Sorting = "Name DESC" }); |
|||
result.TotalCount.ShouldBeGreaterThan(0); |
|||
var tenants = result.Items.ToList(); |
|||
|
|||
tenants.ShouldContain(t => t.Name == "acme"); |
|||
tenants.ShouldContain(t => t.Name == "volosoft"); |
|||
|
|||
tenants.FindIndex(t => t.Name == "acme").ShouldBeGreaterThan(tenants.FindIndex(t => t.Name == "volosoft")); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task CreateAsync() |
|||
{ |
|||
var tenancyName = Guid.NewGuid().ToString("N").ToLowerInvariant(); |
|||
var tenant = await _tenantAppService.CreateAsync(new TenantCreateDto { Name = tenancyName }); |
|||
tenant.Name.ShouldBe(tenancyName); |
|||
tenant.Id.ShouldNotBe(default(Guid)); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task CreateAsync_Should_Not_Allow_Duplicate_Names() |
|||
{ |
|||
await Assert.ThrowsAsync<UserFriendlyException>(async () => |
|||
{ |
|||
await _tenantAppService.CreateAsync(new TenantCreateDto { Name = "acme" }); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task UpdateAsync() |
|||
{ |
|||
var acme = UsingDbContext(dbContext => dbContext.Tenants.Single(t => t.Name == "acme")); |
|||
|
|||
var result = await _tenantAppService.UpdateAsync(acme.Id, new TenantUpdateDto { Name = "acme-renamed" }); |
|||
result.Id.ShouldBe(acme.Id); |
|||
result.Name.ShouldBe("acme-renamed"); |
|||
|
|||
var acmeUpdated = UsingDbContext(dbContext => dbContext.Tenants.Single(t => t.Id == acme.Id)); |
|||
acmeUpdated.Name.ShouldBe("acme-renamed"); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task UpdateAsync_Should_Not_Allow_Duplicate_Names() |
|||
{ |
|||
var acme = UsingDbContext(dbContext => dbContext.Tenants.Single(t => t.Name == "acme")); |
|||
|
|||
await Assert.ThrowsAsync<UserFriendlyException>(async () => |
|||
{ |
|||
await _tenantAppService.UpdateAsync(acme.Id, new TenantUpdateDto { Name = "volosoft" }); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task DeleteAsync() |
|||
{ |
|||
var acme = UsingDbContext(dbContext => dbContext.Tenants.Single(t => t.Name == "acme")); |
|||
|
|||
await _tenantAppService.DeleteAsync(acme.Id); |
|||
|
|||
UsingDbContext(dbContext => |
|||
{ |
|||
dbContext.Tenants.Any(t => t.Id == acme.Id).ShouldBeFalse(); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue