mirror of
https://github.com/bitwarden/server.git
synced 2024-11-25 12:45:18 +01:00
Merge branch 'main' into bre-385-create-seeded-database
This commit is contained in:
commit
b0f2129120
1
.github/workflows/build.yml
vendored
1
.github/workflows/build.yml
vendored
@ -527,6 +527,7 @@ jobs:
|
||||
|
||||
self-host-build:
|
||||
name: Trigger self-host build
|
||||
if: github.event_name != 'pull_request_target' && github.ref == 'refs/heads/main'
|
||||
runs-on: ubuntu-22.04
|
||||
needs: build-docker
|
||||
steps:
|
||||
|
@ -1,11 +1,15 @@
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Bit.Core.Tools.Entities;
|
||||
|
||||
public class PasswordHealthReportApplication : ITableObject<Guid>, IRevisable
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public Guid OrganizationId { get; set; }
|
||||
public string Uri { get; set; }
|
||||
public string? Uri { get; set; }
|
||||
public DateTime CreationDate { get; set; } = DateTime.UtcNow;
|
||||
public DateTime RevisionDate { get; set; } = DateTime.UtcNow;
|
||||
|
||||
|
@ -0,0 +1,9 @@
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Tools.Entities;
|
||||
|
||||
namespace Bit.Core.Tools.Repositories;
|
||||
|
||||
public interface IPasswordHealthReportApplicationRepository : IRepository<PasswordHealthReportApplication, Guid>
|
||||
{
|
||||
Task<ICollection<PasswordHealthReportApplication>> GetByOrganizationIdAsync(Guid organizationId);
|
||||
}
|
@ -58,6 +58,7 @@ public static class DapperServiceCollectionExtensions
|
||||
services.AddSingleton<INotificationStatusRepository, NotificationStatusRepository>();
|
||||
services
|
||||
.AddSingleton<IClientOrganizationMigrationRecordRepository, ClientOrganizationMigrationRecordRepository>();
|
||||
services.AddSingleton<IPasswordHealthReportApplicationRepository, PasswordHealthReportApplicationRepository>();
|
||||
|
||||
if (selfHosted)
|
||||
{
|
||||
|
@ -0,0 +1,33 @@
|
||||
using System.Data;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Core.Tools.Repositories;
|
||||
using Bit.Infrastructure.Dapper.Repositories;
|
||||
using Dapper;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using ToolsEntities = Bit.Core.Tools.Entities;
|
||||
|
||||
namespace Bit.Infrastructure.Dapper.Tools.Repositories;
|
||||
|
||||
public class PasswordHealthReportApplicationRepository : Repository<ToolsEntities.PasswordHealthReportApplication, Guid>, IPasswordHealthReportApplicationRepository
|
||||
{
|
||||
public PasswordHealthReportApplicationRepository(GlobalSettings globalSettings)
|
||||
: this(globalSettings.SqlServer.ConnectionString, globalSettings.SqlServer.ReadOnlyConnectionString)
|
||||
{ }
|
||||
|
||||
public PasswordHealthReportApplicationRepository(string connectionString, string readOnlyConnectionString)
|
||||
: base(connectionString, readOnlyConnectionString)
|
||||
{ }
|
||||
|
||||
public async Task<ICollection<ToolsEntities.PasswordHealthReportApplication>> GetByOrganizationIdAsync(Guid organizationId)
|
||||
{
|
||||
using (var connection = new SqlConnection(ReadOnlyConnectionString))
|
||||
{
|
||||
var results = await connection.QueryAsync<ToolsEntities.PasswordHealthReportApplication>(
|
||||
$"[{Schema}].[PasswordHealthReportApplication_ReadByOrganizationId]",
|
||||
new { OrganizationId = organizationId },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
|
||||
return results.ToList();
|
||||
}
|
||||
}
|
||||
}
|
@ -7,6 +7,7 @@ using Bit.Infrastructure.EntityFramework.Converters;
|
||||
using Bit.Infrastructure.EntityFramework.Models;
|
||||
using Bit.Infrastructure.EntityFramework.NotificationCenter.Models;
|
||||
using Bit.Infrastructure.EntityFramework.SecretsManager.Models;
|
||||
using Bit.Infrastructure.EntityFramework.Tools.Models;
|
||||
using Bit.Infrastructure.EntityFramework.Vault.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
@ -75,6 +76,7 @@ public class DatabaseContext : DbContext
|
||||
public DbSet<Notification> Notifications { get; set; }
|
||||
public DbSet<NotificationStatus> NotificationStatuses { get; set; }
|
||||
public DbSet<ClientOrganizationMigrationRecord> ClientOrganizationMigrationRecords { get; set; }
|
||||
public DbSet<PasswordHealthReportApplication> PasswordHealthReportApplications { get; set; }
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder builder)
|
||||
{
|
||||
|
@ -0,0 +1,24 @@
|
||||
using Bit.Infrastructure.EntityFramework.Tools.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
|
||||
namespace Bit.Infrastructure.EntityFramework.Tools.Configurations;
|
||||
|
||||
public class PasswordHealthReportApplicationEntityTypeConfiguration : IEntityTypeConfiguration<PasswordHealthReportApplication>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<PasswordHealthReportApplication> builder)
|
||||
{
|
||||
builder
|
||||
.Property(s => s.Id)
|
||||
.ValueGeneratedNever();
|
||||
|
||||
builder.HasIndex(s => s.Id)
|
||||
.IsClustered(true);
|
||||
|
||||
builder
|
||||
.HasIndex(s => s.OrganizationId)
|
||||
.IsClustered(false);
|
||||
|
||||
builder.ToTable(nameof(PasswordHealthReportApplication));
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
using AutoMapper;
|
||||
using Bit.Infrastructure.EntityFramework.AdminConsole.Models;
|
||||
|
||||
namespace Bit.Infrastructure.EntityFramework.Tools.Models;
|
||||
|
||||
public class PasswordHealthReportApplication : Core.Tools.Entities.PasswordHealthReportApplication
|
||||
{
|
||||
public virtual Organization Organization { get; set; }
|
||||
}
|
||||
|
||||
public class PasswordHealthReportApplicationProfile : Profile
|
||||
{
|
||||
public PasswordHealthReportApplicationProfile()
|
||||
{
|
||||
CreateMap<Core.Tools.Entities.PasswordHealthReportApplication, PasswordHealthReportApplication>()
|
||||
.ReverseMap();
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
using AutoMapper;
|
||||
using Bit.Core.Tools.Repositories;
|
||||
using Bit.Infrastructure.EntityFramework.Repositories;
|
||||
using Bit.Infrastructure.EntityFramework.Tools.Models;
|
||||
using LinqToDB;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using AdminConsoleEntities = Bit.Core.Tools.Entities;
|
||||
|
||||
namespace Bit.Infrastructure.EntityFramework.Tools.Repositories;
|
||||
|
||||
public class PasswordHealthReportApplicationRepository :
|
||||
Repository<AdminConsoleEntities.PasswordHealthReportApplication, PasswordHealthReportApplication, Guid>,
|
||||
IPasswordHealthReportApplicationRepository
|
||||
{
|
||||
public PasswordHealthReportApplicationRepository(IServiceScopeFactory serviceScopeFactory,
|
||||
IMapper mapper) : base(serviceScopeFactory, mapper, (DatabaseContext context) => context.PasswordHealthReportApplications)
|
||||
{ }
|
||||
|
||||
public async Task<ICollection<AdminConsoleEntities.PasswordHealthReportApplication>> GetByOrganizationIdAsync(Guid organizationId)
|
||||
{
|
||||
using (var scope = ServiceScopeFactory.CreateScope())
|
||||
{
|
||||
var dbContext = GetDatabaseContext(scope);
|
||||
var results = await dbContext.PasswordHealthReportApplications
|
||||
.Where(p => p.OrganizationId == organizationId)
|
||||
.ToListAsync();
|
||||
return Mapper.Map<ICollection<AdminConsoleEntities.PasswordHealthReportApplication>>(results);
|
||||
}
|
||||
}
|
||||
}
|
@ -9,6 +9,7 @@ using Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider;
|
||||
using Bit.Infrastructure.EntityFramework.Auth.Models;
|
||||
using Bit.Infrastructure.EntityFramework.Models;
|
||||
using Bit.Infrastructure.EntityFramework.Repositories;
|
||||
using Bit.Infrastructure.EntityFramework.Tools.Models;
|
||||
using Bit.Infrastructure.EntityFramework.Vault.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
@ -89,6 +90,7 @@ public class EfRepositoryListBuilder<T> : ISpecimenBuilder where T : BaseEntityF
|
||||
cfg.AddProfile<TaxRateMapperProfile>();
|
||||
cfg.AddProfile<TransactionMapperProfile>();
|
||||
cfg.AddProfile<UserMapperProfile>();
|
||||
cfg.AddProfile<PasswordHealthReportApplicationProfile>();
|
||||
})
|
||||
.CreateMapper()));
|
||||
|
||||
|
@ -0,0 +1,82 @@
|
||||
using AutoFixture;
|
||||
using AutoFixture.Kernel;
|
||||
using Bit.Core.Tools.Entities;
|
||||
using Bit.Infrastructure.EntityFramework.AdminConsole.Repositories;
|
||||
using Bit.Infrastructure.EntityFramework.Repositories;
|
||||
using Bit.Infrastructure.EntityFramework.Tools.Repositories;
|
||||
using Bit.Test.Common.AutoFixture;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
|
||||
namespace Bit.Infrastructure.EFIntegration.Test.AutoFixture;
|
||||
|
||||
internal class PasswordHealthReportApplicationBuilder : ISpecimenBuilder
|
||||
{
|
||||
public object Create(object request, ISpecimenContext context)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
var type = request as Type;
|
||||
if (type == null || type != typeof(PasswordHealthReportApplication))
|
||||
{
|
||||
return new NoSpecimen();
|
||||
}
|
||||
|
||||
var fixture = new Fixture();
|
||||
var obj = fixture.WithAutoNSubstitutions().Create<PasswordHealthReportApplication>();
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
internal class EfPasswordHealthReportApplication : ICustomization
|
||||
{
|
||||
public void Customize(IFixture fixture)
|
||||
{
|
||||
fixture.Customizations.Add(new IgnoreVirtualMembersCustomization());
|
||||
fixture.Customizations.Add(new GlobalSettingsBuilder());
|
||||
fixture.Customizations.Add(new PasswordHealthReportApplicationBuilder());
|
||||
fixture.Customizations.Add(new OrganizationBuilder());
|
||||
fixture.Customizations.Add(new EfRepositoryListBuilder<PasswordHealthReportApplicationRepository>());
|
||||
fixture.Customizations.Add(new EfRepositoryListBuilder<OrganizationRepository>());
|
||||
}
|
||||
}
|
||||
|
||||
internal class EfPasswordHealthReportApplicationApplicableToUser : ICustomization
|
||||
{
|
||||
public void Customize(IFixture fixture)
|
||||
{
|
||||
fixture.Customizations.Add(new IgnoreVirtualMembersCustomization());
|
||||
fixture.Customizations.Add(new GlobalSettingsBuilder());
|
||||
fixture.Customizations.Add(new PasswordHealthReportApplicationBuilder());
|
||||
fixture.Customizations.Add(new OrganizationBuilder());
|
||||
fixture.Customizations.Add(new EfRepositoryListBuilder<PasswordHealthReportApplicationRepository>());
|
||||
fixture.Customizations.Add(new EfRepositoryListBuilder<UserRepository>());
|
||||
fixture.Customizations.Add(new EfRepositoryListBuilder<OrganizationRepository>());
|
||||
fixture.Customizations.Add(new EfRepositoryListBuilder<OrganizationUserRepository>());
|
||||
fixture.Customizations.Add(new EfRepositoryListBuilder<ProviderRepository>());
|
||||
fixture.Customizations.Add(new EfRepositoryListBuilder<ProviderUserRepository>());
|
||||
fixture.Customizations.Add(new EfRepositoryListBuilder<ProviderOrganizationRepository>());
|
||||
}
|
||||
}
|
||||
|
||||
internal class EfPasswordHealthReportApplicationAutoDataAttribute : CustomAutoDataAttribute
|
||||
{
|
||||
public EfPasswordHealthReportApplicationAutoDataAttribute() : base(new SutProviderCustomization(), new EfPasswordHealthReportApplication())
|
||||
{ }
|
||||
}
|
||||
|
||||
internal class EfPasswordHealthReportApplicationApplicableToUserInlineAutoDataAttribute : InlineCustomAutoDataAttribute
|
||||
{
|
||||
public EfPasswordHealthReportApplicationApplicableToUserInlineAutoDataAttribute(params object[] values) :
|
||||
base(new[] { typeof(SutProviderCustomization), typeof(EfPasswordHealthReportApplicationApplicableToUser) }, values)
|
||||
{ }
|
||||
}
|
||||
|
||||
internal class InlineEfPasswordHealthReportApplicationAutoDataAttribute : InlineCustomAutoDataAttribute
|
||||
{
|
||||
public InlineEfPasswordHealthReportApplicationAutoDataAttribute(params object[] values) : base(new[] { typeof(SutProviderCustomization),
|
||||
typeof(EfPolicy) }, values)
|
||||
{ }
|
||||
}
|
@ -0,0 +1,269 @@
|
||||
using AutoFixture;
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Test.AutoFixture.Attributes;
|
||||
using Bit.Core.Tools.Entities;
|
||||
using Bit.Core.Tools.Repositories;
|
||||
using Bit.Infrastructure.EFIntegration.Test.AutoFixture;
|
||||
using Xunit;
|
||||
using EfRepo = Bit.Infrastructure.EntityFramework.Repositories;
|
||||
using EfToolsRepo = Bit.Infrastructure.EntityFramework.Tools.Repositories;
|
||||
using SqlAdminConsoleRepo = Bit.Infrastructure.Dapper.Tools.Repositories;
|
||||
using SqlRepo = Bit.Infrastructure.Dapper.Repositories;
|
||||
|
||||
namespace Bit.Infrastructure.EFIntegration.Test.Tools.Repositories;
|
||||
|
||||
public class PasswordHealthReportApplicationRepositoryTests
|
||||
{
|
||||
[CiSkippedTheory, EfPasswordHealthReportApplicationAutoData]
|
||||
public async Task CreateAsync_Works_DataMatches(
|
||||
PasswordHealthReportApplication passwordHealthReportApplication,
|
||||
Organization organization,
|
||||
List<EfToolsRepo.PasswordHealthReportApplicationRepository> suts,
|
||||
List<EfRepo.OrganizationRepository> efOrganizationRepos,
|
||||
SqlAdminConsoleRepo.PasswordHealthReportApplicationRepository sqlPasswordHealthReportApplicationRepo,
|
||||
SqlRepo.OrganizationRepository sqlOrganizationRepo
|
||||
)
|
||||
{
|
||||
var passwordHealthReportApplicationRecords = new List<PasswordHealthReportApplication>();
|
||||
foreach (var sut in suts)
|
||||
{
|
||||
var i = suts.IndexOf(sut);
|
||||
|
||||
var efOrganization = await efOrganizationRepos[i].CreateAsync(organization);
|
||||
sut.ClearChangeTracking();
|
||||
|
||||
passwordHealthReportApplication.OrganizationId = efOrganization.Id;
|
||||
var postEfPasswordHeathReportApp = await sut.CreateAsync(passwordHealthReportApplication);
|
||||
sut.ClearChangeTracking();
|
||||
|
||||
var savedPasswordHealthReportApplication = await sut.GetByIdAsync(postEfPasswordHeathReportApp.Id);
|
||||
passwordHealthReportApplicationRecords.Add(savedPasswordHealthReportApplication);
|
||||
}
|
||||
|
||||
var sqlOrganization = await sqlOrganizationRepo.CreateAsync(organization);
|
||||
|
||||
passwordHealthReportApplication.OrganizationId = sqlOrganization.Id;
|
||||
var sqlPasswordHealthReportApplicationRecord = await sqlPasswordHealthReportApplicationRepo.CreateAsync(passwordHealthReportApplication);
|
||||
var savedSqlPasswordHealthReportApplicationRecord = await sqlPasswordHealthReportApplicationRepo.GetByIdAsync(sqlPasswordHealthReportApplicationRecord.Id);
|
||||
passwordHealthReportApplicationRecords.Add(savedSqlPasswordHealthReportApplicationRecord);
|
||||
|
||||
Assert.True(passwordHealthReportApplicationRecords.Count == 4);
|
||||
}
|
||||
|
||||
[CiSkippedTheory, EfPasswordHealthReportApplicationAutoData]
|
||||
public async Task RetrieveByOrganisation_Works(
|
||||
SqlAdminConsoleRepo.PasswordHealthReportApplicationRepository sqlPasswordHealthReportApplicationRepo,
|
||||
SqlRepo.OrganizationRepository sqlOrganizationRepo)
|
||||
{
|
||||
var (firstOrg, firstRecord) = await CreateSampleRecord(sqlOrganizationRepo, sqlPasswordHealthReportApplicationRepo);
|
||||
var (secondOrg, secondRecord) = await CreateSampleRecord(sqlOrganizationRepo, sqlPasswordHealthReportApplicationRepo);
|
||||
|
||||
var firstSetOfRecords = await sqlPasswordHealthReportApplicationRepo.GetByOrganizationIdAsync(firstOrg.Id);
|
||||
var nextSetOfRecords = await sqlPasswordHealthReportApplicationRepo.GetByOrganizationIdAsync(secondOrg.Id);
|
||||
|
||||
Assert.True(firstSetOfRecords.Count == 1 && firstSetOfRecords.First().OrganizationId == firstOrg.Id);
|
||||
Assert.True(nextSetOfRecords.Count == 1 && nextSetOfRecords.First().OrganizationId == secondOrg.Id);
|
||||
}
|
||||
|
||||
[CiSkippedTheory, EfPasswordHealthReportApplicationAutoData]
|
||||
public async Task ReplaceQuery_Works(
|
||||
List<EfToolsRepo.PasswordHealthReportApplicationRepository> suts,
|
||||
List<EfRepo.OrganizationRepository> efOrganizationRepos,
|
||||
SqlAdminConsoleRepo.PasswordHealthReportApplicationRepository sqlPasswordHealthReportApplicationRepo,
|
||||
SqlRepo.OrganizationRepository sqlOrganizationRepo)
|
||||
{
|
||||
var (org, pwdRecord) = await CreateSampleRecord(sqlOrganizationRepo, sqlPasswordHealthReportApplicationRepo);
|
||||
var exampleUri = "http://www.example.com";
|
||||
var exampleRevisionDate = new DateTime(2021, 1, 1);
|
||||
var dbRecords = new List<PasswordHealthReportApplication>();
|
||||
|
||||
foreach (var sut in suts)
|
||||
{
|
||||
var i = suts.IndexOf(sut);
|
||||
|
||||
// create a new organization for each repository
|
||||
var organization = await efOrganizationRepos[i].CreateAsync(org);
|
||||
|
||||
// map the organization Id and create the PasswordHealthReportApp record
|
||||
pwdRecord.OrganizationId = organization.Id;
|
||||
var passwordHealthReportApplication = await sut.CreateAsync(pwdRecord);
|
||||
|
||||
// update the record with new values
|
||||
passwordHealthReportApplication.Uri = exampleUri;
|
||||
passwordHealthReportApplication.RevisionDate = exampleRevisionDate;
|
||||
|
||||
// apply update to the database
|
||||
await sut.ReplaceAsync(passwordHealthReportApplication);
|
||||
sut.ClearChangeTracking();
|
||||
|
||||
// retrieve the data and add to the list for assertions
|
||||
var recordFromDb = await sut.GetByIdAsync(passwordHealthReportApplication.Id);
|
||||
sut.ClearChangeTracking();
|
||||
|
||||
dbRecords.Add(recordFromDb);
|
||||
}
|
||||
|
||||
// sql - create a new organization and PasswordHealthReportApplication record
|
||||
var (sqlOrg, sqlPwdRecord) = await CreateSampleRecord(sqlOrganizationRepo, sqlPasswordHealthReportApplicationRepo);
|
||||
var sqlPasswordHealthReportApplicationRecord = await sqlPasswordHealthReportApplicationRepo.GetByIdAsync(sqlPwdRecord.Id);
|
||||
|
||||
// sql - update the record with new values
|
||||
sqlPasswordHealthReportApplicationRecord.Uri = exampleUri;
|
||||
sqlPasswordHealthReportApplicationRecord.RevisionDate = exampleRevisionDate;
|
||||
await sqlPasswordHealthReportApplicationRepo.ReplaceAsync(sqlPasswordHealthReportApplicationRecord);
|
||||
|
||||
// sql - retrieve the data and add to the list for assertions
|
||||
var sqlDbRecord = await sqlPasswordHealthReportApplicationRepo.GetByIdAsync(sqlPasswordHealthReportApplicationRecord.Id);
|
||||
dbRecords.Add(sqlDbRecord);
|
||||
|
||||
// assertions
|
||||
// the Guids must be distinct across all records
|
||||
Assert.True(dbRecords.Select(_ => _.Id).Distinct().Count() == dbRecords.Count);
|
||||
|
||||
// the Uri and RevisionDate must match the updated values
|
||||
Assert.True(dbRecords.All(_ => _.Uri == exampleUri && _.RevisionDate == exampleRevisionDate));
|
||||
}
|
||||
|
||||
[CiSkippedTheory, EfPasswordHealthReportApplicationAutoData]
|
||||
public async Task Upsert_Works(
|
||||
List<EfToolsRepo.PasswordHealthReportApplicationRepository> suts,
|
||||
List<EfRepo.OrganizationRepository> efOrganizationRepos,
|
||||
SqlAdminConsoleRepo.PasswordHealthReportApplicationRepository sqlPasswordHealthReportApplicationRepo,
|
||||
SqlRepo.OrganizationRepository sqlOrganizationRepo)
|
||||
{
|
||||
var fixture = new Fixture();
|
||||
var rawOrg = fixture.Build<Organization>().Create();
|
||||
var rawPwdRecord = fixture.Build<PasswordHealthReportApplication>()
|
||||
.With(_ => _.OrganizationId, rawOrg.Id)
|
||||
.Without(_ => _.Id)
|
||||
.Create();
|
||||
var exampleUri = "http://www.example.com";
|
||||
var exampleRevisionDate = new DateTime(2021, 1, 1);
|
||||
var dbRecords = new List<PasswordHealthReportApplication>();
|
||||
|
||||
foreach (var sut in suts)
|
||||
{
|
||||
var i = suts.IndexOf(sut);
|
||||
|
||||
// create a new organization for each repository
|
||||
var organization = await efOrganizationRepos[i].CreateAsync(rawOrg);
|
||||
|
||||
// map the organization Id and use Upsert to save new record
|
||||
rawPwdRecord.OrganizationId = organization.Id;
|
||||
rawPwdRecord.Id = default(Guid);
|
||||
await sut.UpsertAsync(rawPwdRecord);
|
||||
sut.ClearChangeTracking();
|
||||
|
||||
// retrieve the data and add to the list for assertions
|
||||
var passwordHealthReportApplication = await sut.GetByIdAsync(rawPwdRecord.Id);
|
||||
|
||||
// update the record with new values
|
||||
passwordHealthReportApplication.Uri = exampleUri;
|
||||
passwordHealthReportApplication.RevisionDate = exampleRevisionDate;
|
||||
|
||||
// apply update using Upsert to make changes to db
|
||||
await sut.UpsertAsync(passwordHealthReportApplication);
|
||||
sut.ClearChangeTracking();
|
||||
|
||||
// retrieve the data and add to the list for assertions
|
||||
var recordFromDb = await sut.GetByIdAsync(passwordHealthReportApplication.Id);
|
||||
dbRecords.Add(recordFromDb);
|
||||
|
||||
sut.ClearChangeTracking();
|
||||
}
|
||||
|
||||
// sql - create new records
|
||||
var organizationForSql = fixture.Create<Organization>();
|
||||
var passwordHealthReportApplicationForSql = fixture.Build<PasswordHealthReportApplication>()
|
||||
.With(_ => _.OrganizationId, organizationForSql.Id)
|
||||
.Without(_ => _.Id)
|
||||
.Create();
|
||||
|
||||
// sql - use Upsert to insert this data
|
||||
var sqlOrganization = await sqlOrganizationRepo.CreateAsync(organizationForSql);
|
||||
await sqlPasswordHealthReportApplicationRepo.UpsertAsync(passwordHealthReportApplicationForSql);
|
||||
var sqlPasswordHealthReportApplicationRecord = await sqlPasswordHealthReportApplicationRepo.GetByIdAsync(passwordHealthReportApplicationForSql.Id);
|
||||
|
||||
// sql - update the record with new values
|
||||
sqlPasswordHealthReportApplicationRecord.Uri = exampleUri;
|
||||
sqlPasswordHealthReportApplicationRecord.RevisionDate = exampleRevisionDate;
|
||||
await sqlPasswordHealthReportApplicationRepo.UpsertAsync(sqlPasswordHealthReportApplicationRecord);
|
||||
|
||||
// sql - retrieve the data and add to the list for assertions
|
||||
var sqlDbRecord = await sqlPasswordHealthReportApplicationRepo.GetByIdAsync(sqlPasswordHealthReportApplicationRecord.Id);
|
||||
dbRecords.Add(sqlDbRecord);
|
||||
|
||||
// assertions
|
||||
// the Guids must be distinct across all records
|
||||
Assert.True(dbRecords.Select(_ => _.Id).Distinct().Count() == dbRecords.Count);
|
||||
|
||||
// the Uri and RevisionDate must match the updated values
|
||||
Assert.True(dbRecords.All(_ => _.Uri == exampleUri && _.RevisionDate == exampleRevisionDate));
|
||||
}
|
||||
|
||||
[CiSkippedTheory, EfPasswordHealthReportApplicationAutoData]
|
||||
public async Task Delete_Works(
|
||||
List<EfToolsRepo.PasswordHealthReportApplicationRepository> suts,
|
||||
List<EfRepo.OrganizationRepository> efOrganizationRepos,
|
||||
SqlAdminConsoleRepo.PasswordHealthReportApplicationRepository sqlPasswordHealthReportApplicationRepo,
|
||||
SqlRepo.OrganizationRepository sqlOrganizationRepo)
|
||||
{
|
||||
var fixture = new Fixture();
|
||||
var rawOrg = fixture.Build<Organization>().Create();
|
||||
var rawPwdRecord = fixture.Build<PasswordHealthReportApplication>()
|
||||
.With(_ => _.OrganizationId, rawOrg.Id)
|
||||
.Create();
|
||||
var dbRecords = new List<PasswordHealthReportApplication>();
|
||||
|
||||
foreach (var sut in suts)
|
||||
{
|
||||
var i = suts.IndexOf(sut);
|
||||
|
||||
// create a new organization for each repository
|
||||
var organization = await efOrganizationRepos[i].CreateAsync(rawOrg);
|
||||
|
||||
// map the organization Id and use Upsert to save new record
|
||||
rawPwdRecord.OrganizationId = organization.Id;
|
||||
rawPwdRecord = await sut.CreateAsync(rawPwdRecord);
|
||||
sut.ClearChangeTracking();
|
||||
|
||||
// apply update using Upsert to make changes to db
|
||||
await sut.DeleteAsync(rawPwdRecord);
|
||||
sut.ClearChangeTracking();
|
||||
|
||||
// retrieve the data and add to the list for assertions
|
||||
var recordFromDb = await sut.GetByIdAsync(rawPwdRecord.Id);
|
||||
dbRecords.Add(recordFromDb);
|
||||
|
||||
sut.ClearChangeTracking();
|
||||
}
|
||||
|
||||
// sql - create new records
|
||||
var (org, passwordHealthReportApplication) = await CreateSampleRecord(sqlOrganizationRepo, sqlPasswordHealthReportApplicationRepo);
|
||||
await sqlPasswordHealthReportApplicationRepo.DeleteAsync(passwordHealthReportApplication);
|
||||
var sqlDbRecord = await sqlPasswordHealthReportApplicationRepo.GetByIdAsync(passwordHealthReportApplication.Id);
|
||||
dbRecords.Add(sqlDbRecord);
|
||||
|
||||
// assertions
|
||||
// all records should be null - as they were deleted before querying
|
||||
Assert.True(dbRecords.Where(_ => _ == null).Count() == 4);
|
||||
}
|
||||
|
||||
private async Task<(Organization, PasswordHealthReportApplication)> CreateSampleRecord(
|
||||
IOrganizationRepository organizationRepo,
|
||||
IPasswordHealthReportApplicationRepository passwordHealthReportApplicationRepo
|
||||
)
|
||||
{
|
||||
var fixture = new Fixture();
|
||||
var organization = fixture.Create<Organization>();
|
||||
var passwordHealthReportApplication = fixture.Build<PasswordHealthReportApplication>()
|
||||
.With(_ => _.OrganizationId, organization.Id)
|
||||
.Create();
|
||||
|
||||
organization = await organizationRepo.CreateAsync(organization);
|
||||
passwordHealthReportApplication = await passwordHealthReportApplicationRepo.CreateAsync(passwordHealthReportApplication);
|
||||
|
||||
return (organization, passwordHealthReportApplication);
|
||||
}
|
||||
}
|
2888
util/MySqlMigrations/Migrations/20241105195202_FixPasswordHealthReportApplication.Designer.cs
generated
Normal file
2888
util/MySqlMigrations/Migrations/20241105195202_FixPasswordHealthReportApplication.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,22 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Bit.MySqlMigrations.Migrations;
|
||||
|
||||
/// <inheritdoc />
|
||||
public partial class FixPasswordHealthReportApplication : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
// this file is not required, but the designer file is required
|
||||
// in order to keep the database models in sync with the database
|
||||
// without this - the unit tests will fail when run on your local machine
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
}
|
||||
}
|
@ -1887,6 +1887,34 @@ namespace Bit.MySqlMigrations.Migrations
|
||||
b.ToTable("ServiceAccount", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Tools.Models.PasswordHealthReportApplication", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<DateTime>("CreationDate")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<Guid>("OrganizationId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<DateTime>("RevisionDate")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("Uri")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Id")
|
||||
.HasAnnotation("SqlServer:Clustered", true);
|
||||
|
||||
b.HasIndex("OrganizationId")
|
||||
.HasAnnotation("SqlServer:Clustered", false);
|
||||
|
||||
b.ToTable("PasswordHealthReportApplication", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
@ -2578,6 +2606,17 @@ namespace Bit.MySqlMigrations.Migrations
|
||||
b.Navigation("Organization");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Tools.Models.PasswordHealthReportApplication", b =>
|
||||
{
|
||||
b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization")
|
||||
.WithMany()
|
||||
.HasForeignKey("OrganizationId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Organization");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b =>
|
||||
{
|
||||
b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization")
|
||||
|
2894
util/PostgresMigrations/Migrations/20241105202053_FixPasswordHealthReportApplication.Designer.cs
generated
Normal file
2894
util/PostgresMigrations/Migrations/20241105202053_FixPasswordHealthReportApplication.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,22 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Bit.PostgresMigrations.Migrations;
|
||||
|
||||
/// <inheritdoc />
|
||||
public partial class FixPasswordHealthReportApplication : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
// this file is not required, but the designer file is required
|
||||
// in order to keep the database models in sync with the database
|
||||
// without this - the unit tests will fail when run on your local machine
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
}
|
||||
}
|
@ -1893,6 +1893,34 @@ namespace Bit.PostgresMigrations.Migrations
|
||||
b.ToTable("ServiceAccount", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Tools.Models.PasswordHealthReportApplication", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTime>("CreationDate")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<Guid>("OrganizationId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTime>("RevisionDate")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("Uri")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Id")
|
||||
.HasAnnotation("SqlServer:Clustered", true);
|
||||
|
||||
b.HasIndex("OrganizationId")
|
||||
.HasAnnotation("SqlServer:Clustered", false);
|
||||
|
||||
b.ToTable("PasswordHealthReportApplication", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
@ -2584,6 +2612,17 @@ namespace Bit.PostgresMigrations.Migrations
|
||||
b.Navigation("Organization");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Tools.Models.PasswordHealthReportApplication", b =>
|
||||
{
|
||||
b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization")
|
||||
.WithMany()
|
||||
.HasForeignKey("OrganizationId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Organization");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b =>
|
||||
{
|
||||
b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization")
|
||||
|
2877
util/SqliteMigrations/Migrations/20241105202413_FixPasswordHealthReportApplication.Designer.cs
generated
Normal file
2877
util/SqliteMigrations/Migrations/20241105202413_FixPasswordHealthReportApplication.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,22 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Bit.SqliteMigrations.Migrations;
|
||||
|
||||
/// <inheritdoc />
|
||||
public partial class FixPasswordHealthReportApplication : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
// this file is not required, but the designer file is required
|
||||
// in order to keep the database models in sync with the database
|
||||
// without this - the unit tests will fail when run on your local machine
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
}
|
||||
}
|
@ -1876,6 +1876,34 @@ namespace Bit.SqliteMigrations.Migrations
|
||||
b.ToTable("ServiceAccount", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Tools.Models.PasswordHealthReportApplication", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("CreationDate")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid>("OrganizationId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("RevisionDate")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Uri")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Id")
|
||||
.HasAnnotation("SqlServer:Clustered", true);
|
||||
|
||||
b.HasIndex("OrganizationId")
|
||||
.HasAnnotation("SqlServer:Clustered", false);
|
||||
|
||||
b.ToTable("PasswordHealthReportApplication", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
@ -2567,6 +2595,17 @@ namespace Bit.SqliteMigrations.Migrations
|
||||
b.Navigation("Organization");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Tools.Models.PasswordHealthReportApplication", b =>
|
||||
{
|
||||
b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization")
|
||||
.WithMany()
|
||||
.HasForeignKey("OrganizationId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Organization");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Vault.Models.Cipher", b =>
|
||||
{
|
||||
b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization")
|
||||
|
Loading…
Reference in New Issue
Block a user