diff --git a/src/Infrastructure.EntityFramework/Repositories/CollectionCipherRepository.cs b/src/Infrastructure.EntityFramework/Repositories/CollectionCipherRepository.cs index f929b8d92bfa..52cc1947dbfe 100644 --- a/src/Infrastructure.EntityFramework/Repositories/CollectionCipherRepository.cs +++ b/src/Infrastructure.EntityFramework/Repositories/CollectionCipherRepository.cs @@ -81,32 +81,34 @@ public async Task UpdateCollectionsAsync(Guid cipherId, Guid userId, IEnumerable .Select(c => c.OrganizationId) .FirstAsync(); - var availableCollections = await (from c in dbContext.Collections - join o in dbContext.Organizations on c.OrganizationId equals o.Id - join ou in dbContext.OrganizationUsers - on new { OrganizationId = o.Id, UserId = (Guid?)userId } equals - new { ou.OrganizationId, ou.UserId } - join cu in dbContext.CollectionUsers - on new { ou.AccessAll, CollectionId = c.Id, OrganizationUserId = ou.Id } equals - new { AccessAll = false, cu.CollectionId, cu.OrganizationUserId } into cu_g - from cu in cu_g.DefaultIfEmpty() - join gu in dbContext.GroupUsers - on new { CollectionId = (Guid?)cu.CollectionId, ou.AccessAll, OrganizationUserId = ou.Id } equals - new { CollectionId = (Guid?)null, AccessAll = false, gu.OrganizationUserId } into gu_g - from gu in gu_g.DefaultIfEmpty() - join g in dbContext.Groups on gu.GroupId equals g.Id into g_g - from g in g_g.DefaultIfEmpty() - join cg in dbContext.CollectionGroups - on new { g.AccessAll, CollectionId = c.Id, gu.GroupId } equals - new { AccessAll = false, cg.CollectionId, cg.GroupId } into cg_g - from cg in cg_g.DefaultIfEmpty() - where o.Id == organizationId && o.Enabled && ou.Status == OrganizationUserStatusType.Confirmed - && (ou.AccessAll || !cu.ReadOnly || g.AccessAll || !cg.ReadOnly) - select c.Id).ToListAsync(); - - var collectionCiphers = await (from cc in dbContext.CollectionCiphers - where cc.CipherId == cipherId - select cc).ToListAsync(); + var availableCollections = await ( + from c in dbContext.Collections + join o in dbContext.Organizations on c.OrganizationId equals o.Id + join ou in dbContext.OrganizationUsers + on new { OrganizationId = o.Id, UserId = (Guid?)userId } equals + new { ou.OrganizationId, ou.UserId } + join cu in dbContext.CollectionUsers + on new { ou.AccessAll, CollectionId = c.Id, OrganizationUserId = ou.Id } equals + new { AccessAll = false, cu.CollectionId, cu.OrganizationUserId } into cu_g + from cu in cu_g.DefaultIfEmpty() + join gu in dbContext.GroupUsers + on new { CollectionId = (Guid?)cu.CollectionId, ou.AccessAll, OrganizationUserId = ou.Id } equals + new { CollectionId = (Guid?)null, AccessAll = false, gu.OrganizationUserId } into gu_g + from gu in gu_g.DefaultIfEmpty() + join g in dbContext.Groups on gu.GroupId equals g.Id into g_g + from g in g_g.DefaultIfEmpty() + join cg in dbContext.CollectionGroups + on new { g.AccessAll, CollectionId = c.Id, gu.GroupId } equals + new { AccessAll = false, cg.CollectionId, cg.GroupId } into cg_g + from cg in cg_g.DefaultIfEmpty() + where o.Id == organizationId && o.Enabled && ou.Status == OrganizationUserStatusType.Confirmed + && (ou.AccessAll || !cu.ReadOnly || g.AccessAll || !cg.ReadOnly) + select c.Id).ToListAsync(); + + var collectionCiphers = await ( + from cc in dbContext.CollectionCiphers + where cc.CipherId == cipherId + select cc).ToListAsync(); foreach (var requestedCollectionId in collectionIds) { @@ -181,44 +183,49 @@ public async Task UpdateCollectionsForCiphersAsync(IEnumerable cipherIds, using (var scope = ServiceScopeFactory.CreateScope()) { var dbContext = GetDatabaseContext(scope); - var availableCollections = from c in dbContext.Collections - join o in dbContext.Organizations - on c.OrganizationId equals o.Id - join ou in dbContext.OrganizationUsers - on o.Id equals ou.OrganizationId - where ou.UserId == userId - join cu in dbContext.CollectionUsers - on ou.Id equals cu.OrganizationUserId into cu_g - from cu in cu_g.DefaultIfEmpty() - where !ou.AccessAll && cu.CollectionId == c.Id - join gu in dbContext.GroupUsers - on ou.Id equals gu.OrganizationUserId into gu_g - from gu in gu_g.DefaultIfEmpty() - where cu.CollectionId == null && !ou.AccessAll - join g in dbContext.Groups - on gu.GroupId equals g.Id into g_g - from g in g_g.DefaultIfEmpty() - join cg in dbContext.CollectionGroups - on gu.GroupId equals cg.GroupId into cg_g - from cg in cg_g.DefaultIfEmpty() - where !g.AccessAll && cg.CollectionId == c.Id && - (o.Id == organizationId && o.Enabled && ou.Status == OrganizationUserStatusType.Confirmed && - (ou.AccessAll || !cu.ReadOnly || g.AccessAll || !cg.ReadOnly)) - select new { c, o, ou, cu, gu, g, cg }; - var count = await availableCollections.CountAsync(); - if (await availableCollections.CountAsync() < 1) + var availableCollections = ( + from c in dbContext.Collections + join o in dbContext.Organizations + on c.OrganizationId equals o.Id + join ou in dbContext.OrganizationUsers + on new { OrganizationId = o.Id, UserId = (Guid?)userId } + equals new { ou.OrganizationId, ou.UserId } + join cu in dbContext.CollectionUsers + on new { ou.AccessAll, CollectionId = c.Id } + equals new { AccessAll = false, cu.CollectionId } into cu_g + from cu in cu_g.DefaultIfEmpty() + join gu in dbContext.GroupUsers + on new { CollectionId = (Guid?)cu.CollectionId, ou.AccessAll, OrganizationUserId = ou.Id } + equals new { CollectionId = (Guid?)null, AccessAll = false, gu.OrganizationUserId } into gu_g + from gu in gu_g.DefaultIfEmpty() + join g in dbContext.Groups + on gu.GroupId equals g.Id into g_g + from g in g_g.DefaultIfEmpty() + join cg in dbContext.CollectionGroups + on new { g.AccessAll, CollectionId = c.Id, gu.GroupId } + equals new { AccessAll = false, cg.CollectionId, cg.GroupId } into cg_g + from cg in cg_g.DefaultIfEmpty() + where o.Id == organizationId + && o.Enabled + && ou.Status == OrganizationUserStatusType.Confirmed + && (ou.AccessAll || !cu.ReadOnly || g.AccessAll || !cg.ReadOnly) + select c.Id + ); + + if (!await availableCollections.AnyAsync()) { return; } - var insertData = from collectionId in collectionIds - from cipherId in cipherIds - where availableCollections.Select(x => x.c.Id).Contains(collectionId) - select new Models.CollectionCipher - { - CollectionId = collectionId, - CipherId = cipherId, - }; + var insertData = ( + from collectionId in collectionIds + from cipherId in cipherIds + where availableCollections.Contains(collectionId) + select new Models.CollectionCipher + { + CollectionId = collectionId, + CipherId = cipherId, + }); await dbContext.AddRangeAsync(insertData); await dbContext.UserBumpAccountRevisionDateByOrganizationIdAsync(organizationId); await dbContext.SaveChangesAsync(); diff --git a/src/Infrastructure.EntityFramework/Repositories/GroupRepository.cs b/src/Infrastructure.EntityFramework/Repositories/GroupRepository.cs index 9efd5e90c39f..3fa795868971 100644 --- a/src/Infrastructure.EntityFramework/Repositories/GroupRepository.cs +++ b/src/Infrastructure.EntityFramework/Repositories/GroupRepository.cs @@ -57,16 +57,16 @@ public async Task DeleteUserAsync(Guid groupId, Guid organizationUserId) using (var scope = ServiceScopeFactory.CreateScope()) { var dbContext = GetDatabaseContext(scope); - var query = await ( - from cg in dbContext.CollectionGroups - where cg.GroupId == id - select cg).ToListAsync(); - var collections = query.Select(c => new CollectionAccessSelection - { - Id = c.CollectionId, - ReadOnly = c.ReadOnly, - HidePasswords = c.HidePasswords, - }).ToList(); + var collections = await dbContext.CollectionGroups + .Where(cg => cg.GroupId == id) + .Select(cg => new CollectionAccessSelection + { + Id = cg.CollectionId, + ReadOnly = cg.ReadOnly, + HidePasswords = cg.HidePasswords, + }) + .ToListAsync(); + return new Tuple>( grp, collections); } @@ -120,10 +120,9 @@ from cg in dbContext.CollectionGroups using (var scope = ServiceScopeFactory.CreateScope()) { var dbContext = GetDatabaseContext(scope); - var query = from g in dbContext.Groups - where groupIds.Contains(g.Id) - select g; - var groups = await query.ToListAsync(); + var groups = await dbContext.Groups + .Where(g => groupIds.Contains(g.Id)) + .ToListAsync(); return Mapper.Map>(groups); } } @@ -149,12 +148,10 @@ public async Task> GetManyIdsByUserIdAsync(Guid organizationUs using (var scope = ServiceScopeFactory.CreateScope()) { var dbContext = GetDatabaseContext(scope); - var query = - from gu in dbContext.GroupUsers - where gu.OrganizationUserId == organizationUserId - select gu; - var groupIds = await query.Select(x => x.GroupId).ToListAsync(); - return groupIds; + return await dbContext.GroupUsers + .Where(gu => gu.OrganizationUserId == organizationUserId) + .Select(gu => gu.GroupId) + .ToListAsync(); } } diff --git a/test/Infrastructure.EFIntegration.Test/AutoFixture/CollectionFixtures.cs b/test/Infrastructure.EFIntegration.Test/AutoFixture/CollectionFixtures.cs index 8068bfe5496d..6010f9e0c827 100644 --- a/test/Infrastructure.EFIntegration.Test/AutoFixture/CollectionFixtures.cs +++ b/test/Infrastructure.EFIntegration.Test/AutoFixture/CollectionFixtures.cs @@ -2,9 +2,7 @@ using AutoFixture.Kernel; using Bit.Core.Entities; using Bit.Infrastructure.EFIntegration.Test.AutoFixture.Relays; -using Bit.Infrastructure.EntityFramework.Repositories; using Bit.Test.Common.AutoFixture; -using Bit.Test.Common.AutoFixture.Attributes; namespace Bit.Infrastructure.EFIntegration.Test.AutoFixture; @@ -29,21 +27,3 @@ public object Create(object request, ISpecimenContext context) return obj; } } - -internal class EfCollection : ICustomization -{ - public void Customize(IFixture fixture) - { - fixture.Customizations.Add(new IgnoreVirtualMembersCustomization()); - fixture.Customizations.Add(new GlobalSettingsBuilder()); - fixture.Customizations.Add(new CollectionBuilder()); - fixture.Customizations.Add(new OrganizationBuilder()); - fixture.Customizations.Add(new EfRepositoryListBuilder()); - fixture.Customizations.Add(new EfRepositoryListBuilder()); - } -} - -internal class EfCollectionCustomize : BitCustomizeAttribute -{ - public override ICustomization GetCustomization() => new EfCollection(); -} diff --git a/test/Infrastructure.EFIntegration.Test/AutoFixture/DeviceFixtures.cs b/test/Infrastructure.EFIntegration.Test/AutoFixture/DeviceFixtures.cs index da5b5b767660..379f8be64db6 100644 --- a/test/Infrastructure.EFIntegration.Test/AutoFixture/DeviceFixtures.cs +++ b/test/Infrastructure.EFIntegration.Test/AutoFixture/DeviceFixtures.cs @@ -1,11 +1,8 @@ using AutoFixture; using AutoFixture.Kernel; using Bit.Core.Entities; -using Bit.Core.Test.AutoFixture.UserFixtures; using Bit.Infrastructure.EFIntegration.Test.AutoFixture.Relays; -using Bit.Infrastructure.EntityFramework.Repositories; using Bit.Test.Common.AutoFixture; -using Bit.Test.Common.AutoFixture.Attributes; namespace Bit.Infrastructure.EFIntegration.Test.AutoFixture; @@ -30,30 +27,3 @@ public object Create(object request, ISpecimenContext context) return obj; } } - -internal class EfDevice : ICustomization -{ - public void Customize(IFixture fixture) - { - fixture.Customizations.Add(new IgnoreVirtualMembersCustomization()); - fixture.Customizations.Add(new GlobalSettingsBuilder()); - fixture.Customizations.Add(new DeviceBuilder()); - fixture.Customizations.Add(new UserBuilder()); - fixture.Customizations.Add(new EfRepositoryListBuilder()); - fixture.Customizations.Add(new EfRepositoryListBuilder()); - } -} - -internal class EfDeviceAutoDataAttribute : CustomAutoDataAttribute -{ - public EfDeviceAutoDataAttribute() : base(new SutProviderCustomization(), new EfDevice()) - { } -} - -internal class InlineEfDeviceAutoDataAttribute : InlineCustomAutoDataAttribute -{ - public InlineEfDeviceAutoDataAttribute(params object[] values) : base(new[] { typeof(SutProviderCustomization), - typeof(EfDevice) }, values) - { } -} - diff --git a/test/Infrastructure.EFIntegration.Test/AutoFixture/InstallationFixtures.cs b/test/Infrastructure.EFIntegration.Test/AutoFixture/InstallationFixtures.cs deleted file mode 100644 index c090a2e38e0d..000000000000 --- a/test/Infrastructure.EFIntegration.Test/AutoFixture/InstallationFixtures.cs +++ /dev/null @@ -1,54 +0,0 @@ -using AutoFixture; -using AutoFixture.Kernel; -using Bit.Core.Entities; -using Bit.Infrastructure.EntityFramework.Repositories; -using Bit.Test.Common.AutoFixture; -using Bit.Test.Common.AutoFixture.Attributes; - -namespace Bit.Infrastructure.EFIntegration.Test.AutoFixture; - -internal class InstallationBuilder : 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(Installation)) - { - return new NoSpecimen(); - } - - var fixture = new Fixture(); - var obj = fixture.WithAutoNSubstitutions().Create(); - return obj; - } -} - -internal class EfInstallation : ICustomization -{ - public void Customize(IFixture fixture) - { - fixture.Customizations.Add(new IgnoreVirtualMembersCustomization()); - fixture.Customizations.Add(new GlobalSettingsBuilder()); - fixture.Customizations.Add(new InstallationBuilder()); - fixture.Customizations.Add(new EfRepositoryListBuilder()); - } -} - -internal class EfInstallationAutoDataAttribute : CustomAutoDataAttribute -{ - public EfInstallationAutoDataAttribute() : base(new SutProviderCustomization(), new EfInstallation()) - { } -} - -internal class InlineEfInstallationAutoDataAttribute : InlineCustomAutoDataAttribute -{ - public InlineEfInstallationAutoDataAttribute(params object[] values) : base(new[] { typeof(SutProviderCustomization), - typeof(EfInstallation) }, values) - { } -} - diff --git a/test/Infrastructure.EFIntegration.Test/AutoFixture/PolicyFixtures.cs b/test/Infrastructure.EFIntegration.Test/AutoFixture/PolicyFixtures.cs index 70cea3e01189..987aa0dc755b 100644 --- a/test/Infrastructure.EFIntegration.Test/AutoFixture/PolicyFixtures.cs +++ b/test/Infrastructure.EFIntegration.Test/AutoFixture/PolicyFixtures.cs @@ -28,19 +28,6 @@ public object Create(object request, ISpecimenContext context) } } -internal class EfPolicy : ICustomization -{ - public void Customize(IFixture fixture) - { - fixture.Customizations.Add(new IgnoreVirtualMembersCustomization()); - fixture.Customizations.Add(new GlobalSettingsBuilder()); - fixture.Customizations.Add(new PolicyBuilder()); - fixture.Customizations.Add(new OrganizationBuilder()); - fixture.Customizations.Add(new EfRepositoryListBuilder()); - fixture.Customizations.Add(new EfRepositoryListBuilder()); - } -} - internal class EfPolicyApplicableToUser : ICustomization { public void Customize(IFixture fixture) @@ -59,21 +46,8 @@ public void Customize(IFixture fixture) } } -internal class EfPolicyAutoDataAttribute : CustomAutoDataAttribute -{ - public EfPolicyAutoDataAttribute() : base(new SutProviderCustomization(), new EfPolicy()) - { } -} - internal class EfPolicyApplicableToUserInlineAutoDataAttribute : InlineCustomAutoDataAttribute { public EfPolicyApplicableToUserInlineAutoDataAttribute(params object[] values) : base(new[] { typeof(SutProviderCustomization), typeof(EfPolicyApplicableToUser) }, values) { } } - -internal class InlineEfPolicyAutoDataAttribute : InlineCustomAutoDataAttribute -{ - public InlineEfPolicyAutoDataAttribute(params object[] values) : base(new[] { typeof(SutProviderCustomization), - typeof(EfPolicy) }, values) - { } -} diff --git a/test/Infrastructure.EFIntegration.Test/AutoFixture/TaxRateFixtures.cs b/test/Infrastructure.EFIntegration.Test/AutoFixture/TaxRateFixtures.cs deleted file mode 100644 index c8cd8c692c44..000000000000 --- a/test/Infrastructure.EFIntegration.Test/AutoFixture/TaxRateFixtures.cs +++ /dev/null @@ -1,56 +0,0 @@ -using AutoFixture; -using AutoFixture.Kernel; -using Bit.Core.Entities; -using Bit.Infrastructure.EFIntegration.Test.AutoFixture.Relays; -using Bit.Infrastructure.EntityFramework.Repositories; -using Bit.Test.Common.AutoFixture; -using Bit.Test.Common.AutoFixture.Attributes; - -namespace Bit.Infrastructure.EFIntegration.Test.AutoFixture; - -internal class TaxRateBuilder : 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(TaxRate)) - { - return new NoSpecimen(); - } - - var fixture = new Fixture(); - fixture.Customizations.Insert(0, new MaxLengthStringRelay()); - var obj = fixture.WithAutoNSubstitutions().Create(); - return obj; - } -} - -internal class EfTaxRate : ICustomization -{ - public void Customize(IFixture fixture) - { - fixture.Customizations.Add(new IgnoreVirtualMembersCustomization()); - fixture.Customizations.Add(new GlobalSettingsBuilder()); - fixture.Customizations.Add(new TaxRateBuilder()); - fixture.Customizations.Add(new EfRepositoryListBuilder()); - } -} - -internal class EfTaxRateAutoDataAttribute : CustomAutoDataAttribute -{ - public EfTaxRateAutoDataAttribute() : base(new SutProviderCustomization(), new EfTaxRate()) - { } -} - -internal class InlineEfTaxRateAutoDataAttribute : InlineCustomAutoDataAttribute -{ - public InlineEfTaxRateAutoDataAttribute(params object[] values) : base(new[] { typeof(SutProviderCustomization), - typeof(EfTaxRate) }, values) - { } -} - diff --git a/test/Infrastructure.EFIntegration.Test/Repositories/CollectionRepository.cs b/test/Infrastructure.EFIntegration.Test/Repositories/CollectionRepository.cs deleted file mode 100644 index eb3b269467dd..000000000000 --- a/test/Infrastructure.EFIntegration.Test/Repositories/CollectionRepository.cs +++ /dev/null @@ -1,51 +0,0 @@ -using Bit.Core.Entities; -using Bit.Core.Test.AutoFixture.Attributes; -using Bit.Infrastructure.EFIntegration.Test.AutoFixture; -using Bit.Infrastructure.EFIntegration.Test.Repositories.EqualityComparers; -using Bit.Test.Common.AutoFixture.Attributes; -using Xunit; -using EfRepo = Bit.Infrastructure.EntityFramework.Repositories; -using SqlRepo = Bit.Infrastructure.Dapper.Repositories; - -namespace Bit.Infrastructure.EFIntegration.Test.Repositories; - -[EfCollectionCustomize] -public class CollectionRepositoryTests -{ - [CiSkippedTheory, BitAutoData] - public async void CreateAsync_Works_DataMatches( - Collection collection, - Organization organization, - CollectionCompare equalityComparer, - List suts, - List efOrganizationRepos, - SqlRepo.CollectionRepository sqlCollectionRepo, - SqlRepo.OrganizationRepository sqlOrganizationRepo - ) - { - var savedCollections = new List(); - foreach (var sut in suts) - { - var i = suts.IndexOf(sut); - var efOrganization = await efOrganizationRepos[i].CreateAsync(organization); - sut.ClearChangeTracking(); - - collection.OrganizationId = efOrganization.Id; - var postEfCollection = await sut.CreateAsync(collection); - sut.ClearChangeTracking(); - - var savedCollection = await sut.GetByIdAsync(postEfCollection.Id); - savedCollections.Add(savedCollection); - } - - var sqlOrganization = await sqlOrganizationRepo.CreateAsync(organization); - collection.OrganizationId = sqlOrganization.Id; - - var sqlCollection = await sqlCollectionRepo.CreateAsync(collection); - var savedSqlCollection = await sqlCollectionRepo.GetByIdAsync(sqlCollection.Id); - savedCollections.Add(savedSqlCollection); - - var distinctItems = savedCollections.Distinct(equalityComparer); - Assert.True(!distinctItems.Skip(1).Any()); - } -} diff --git a/test/Infrastructure.EFIntegration.Test/Repositories/DeviceRepositoryTests.cs b/test/Infrastructure.EFIntegration.Test/Repositories/DeviceRepositoryTests.cs deleted file mode 100644 index fc1f5c8b3184..000000000000 --- a/test/Infrastructure.EFIntegration.Test/Repositories/DeviceRepositoryTests.cs +++ /dev/null @@ -1,46 +0,0 @@ -using Bit.Core.Entities; -using Bit.Core.Test.AutoFixture.Attributes; -using Bit.Infrastructure.EFIntegration.Test.AutoFixture; -using Bit.Infrastructure.EFIntegration.Test.Repositories.EqualityComparers; -using Xunit; -using EfRepo = Bit.Infrastructure.EntityFramework.Repositories; -using SqlRepo = Bit.Infrastructure.Dapper.Repositories; - -namespace Bit.Infrastructure.EFIntegration.Test.Repositories; - -public class DeviceRepositoryTests -{ - [CiSkippedTheory, EfDeviceAutoData] - public async void CreateAsync_Works_DataMatches(Device device, User user, - DeviceCompare equalityComparer, List suts, - List efUserRepos, SqlRepo.DeviceRepository sqlDeviceRepo, - SqlRepo.UserRepository sqlUserRepo) - { - var savedDevices = new List(); - foreach (var sut in suts) - { - var i = suts.IndexOf(sut); - - var efUser = await efUserRepos[i].CreateAsync(user); - device.UserId = efUser.Id; - sut.ClearChangeTracking(); - - var postEfDevice = await sut.CreateAsync(device); - sut.ClearChangeTracking(); - - var savedDevice = await sut.GetByIdAsync(postEfDevice.Id); - savedDevices.Add(savedDevice); - } - - var sqlUser = await sqlUserRepo.CreateAsync(user); - device.UserId = sqlUser.Id; - - var sqlDevice = await sqlDeviceRepo.CreateAsync(device); - var savedSqlDevice = await sqlDeviceRepo.GetByIdAsync(sqlDevice.Id); - savedDevices.Add(savedSqlDevice); - - var distinctItems = savedDevices.Distinct(equalityComparer); - Assert.True(!distinctItems.Skip(1).Any()); - } - -} diff --git a/test/Infrastructure.EFIntegration.Test/Repositories/EqualityComparers/CollectionCompare.cs b/test/Infrastructure.EFIntegration.Test/Repositories/EqualityComparers/CollectionCompare.cs deleted file mode 100644 index 56cb0acf7afd..000000000000 --- a/test/Infrastructure.EFIntegration.Test/Repositories/EqualityComparers/CollectionCompare.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using Bit.Core.Entities; - -namespace Bit.Infrastructure.EFIntegration.Test.Repositories.EqualityComparers; - -public class CollectionCompare : IEqualityComparer -{ - public bool Equals(Collection x, Collection y) - { - return x.Name == y.Name && - x.ExternalId == y.ExternalId; - } - - public int GetHashCode([DisallowNull] Collection obj) - { - return base.GetHashCode(); - } -} diff --git a/test/Infrastructure.EFIntegration.Test/Repositories/EqualityComparers/DeviceCompare.cs b/test/Infrastructure.EFIntegration.Test/Repositories/EqualityComparers/DeviceCompare.cs deleted file mode 100644 index 086199b3800c..000000000000 --- a/test/Infrastructure.EFIntegration.Test/Repositories/EqualityComparers/DeviceCompare.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using Bit.Core.Entities; - -namespace Bit.Infrastructure.EFIntegration.Test.Repositories.EqualityComparers; - -public class DeviceCompare : IEqualityComparer -{ - public bool Equals(Device x, Device y) - { - return x.Name == y.Name && - x.Type == y.Type && - x.Identifier == y.Identifier && - x.PushToken == y.PushToken; - } - - public int GetHashCode([DisallowNull] Device obj) - { - return base.GetHashCode(); - } -} diff --git a/test/Infrastructure.EFIntegration.Test/Repositories/EqualityComparers/InstallationCompare.cs b/test/Infrastructure.EFIntegration.Test/Repositories/EqualityComparers/InstallationCompare.cs deleted file mode 100644 index 7794785b31f4..000000000000 --- a/test/Infrastructure.EFIntegration.Test/Repositories/EqualityComparers/InstallationCompare.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using Bit.Core.Entities; - -namespace Bit.Infrastructure.EFIntegration.Test.Repositories.EqualityComparers; - -public class InstallationCompare : IEqualityComparer -{ - public bool Equals(Installation x, Installation y) - { - return x.Email == y.Email && - x.Key == y.Key && - x.Enabled == y.Enabled; - } - - public int GetHashCode([DisallowNull] Installation obj) - { - return base.GetHashCode(); - } -} diff --git a/test/Infrastructure.EFIntegration.Test/Repositories/EqualityComparers/TaxRateCompare.cs b/test/Infrastructure.EFIntegration.Test/Repositories/EqualityComparers/TaxRateCompare.cs deleted file mode 100644 index ff3c0a600f85..000000000000 --- a/test/Infrastructure.EFIntegration.Test/Repositories/EqualityComparers/TaxRateCompare.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using Bit.Core.Entities; - -namespace Bit.Infrastructure.EFIntegration.Test.Repositories.EqualityComparers; - -public class TaxRateCompare : IEqualityComparer -{ - public bool Equals(TaxRate x, TaxRate y) - { - return x.Country == y.Country && - x.State == y.State && - x.PostalCode == y.PostalCode && - x.Rate == y.Rate && - x.Active == y.Active; - } - - public int GetHashCode([DisallowNull] TaxRate obj) - { - return base.GetHashCode(); - } -} diff --git a/test/Infrastructure.EFIntegration.Test/Repositories/InstallationRepositoryTests.cs b/test/Infrastructure.EFIntegration.Test/Repositories/InstallationRepositoryTests.cs deleted file mode 100644 index 9827b0c03f52..000000000000 --- a/test/Infrastructure.EFIntegration.Test/Repositories/InstallationRepositoryTests.cs +++ /dev/null @@ -1,38 +0,0 @@ -using Bit.Core.Entities; -using Bit.Core.Test.AutoFixture.Attributes; -using Bit.Infrastructure.EFIntegration.Test.AutoFixture; -using Bit.Infrastructure.EFIntegration.Test.Repositories.EqualityComparers; -using Xunit; -using EfRepo = Bit.Infrastructure.EntityFramework.Repositories; -using SqlRepo = Bit.Infrastructure.Dapper.Repositories; - -namespace Bit.Infrastructure.EFIntegration.Test.Repositories; - -public class InstallationRepositoryTests -{ - [CiSkippedTheory, EfInstallationAutoData] - public async void CreateAsync_Works_DataMatches( - Installation installation, - InstallationCompare equalityComparer, - List suts, - SqlRepo.InstallationRepository sqlInstallationRepo - ) - { - var savedInstallations = new List(); - foreach (var sut in suts) - { - var postEfInstallation = await sut.CreateAsync(installation); - sut.ClearChangeTracking(); - - var savedInstallation = await sut.GetByIdAsync(postEfInstallation.Id); - savedInstallations.Add(savedInstallation); - } - - var sqlInstallation = await sqlInstallationRepo.CreateAsync(installation); - var savedSqlInstallation = await sqlInstallationRepo.GetByIdAsync(sqlInstallation.Id); - savedInstallations.Add(savedSqlInstallation); - - var distinctItems = savedInstallations.Distinct(equalityComparer); - Assert.True(!distinctItems.Skip(1).Any()); - } -} diff --git a/test/Infrastructure.EFIntegration.Test/Repositories/OrganizationRepositoryTests.cs b/test/Infrastructure.EFIntegration.Test/Repositories/OrganizationRepositoryTests.cs index 40732709b4a7..3769bdf4d4ed 100644 --- a/test/Infrastructure.EFIntegration.Test/Repositories/OrganizationRepositoryTests.cs +++ b/test/Infrastructure.EFIntegration.Test/Repositories/OrganizationRepositoryTests.cs @@ -1,6 +1,4 @@ -using Bit.Core.Entities; -using Bit.Core.Enums; -using Bit.Core.Models.Data.Organizations; +using Bit.Core.Models.Data.Organizations; using Bit.Core.Test.AutoFixture.Attributes; using Bit.Infrastructure.EFIntegration.Test.AutoFixture; using Bit.Infrastructure.EFIntegration.Test.Repositories.EqualityComparers; @@ -13,29 +11,6 @@ namespace Bit.Infrastructure.EFIntegration.Test.Repositories; public class OrganizationRepositoryTests { - [CiSkippedTheory, EfOrganizationAutoData] - public async void CreateAsync_Works_DataMatches( - Organization organization, - SqlRepo.OrganizationRepository sqlOrganizationRepo, OrganizationCompare equalityComparer, - List suts) - { - var savedOrganizations = new List(); - foreach (var sut in suts) - { - var postEfOrganization = await sut.CreateAsync(organization); - sut.ClearChangeTracking(); - - var savedOrganization = await sut.GetByIdAsync(organization.Id); - savedOrganizations.Add(savedOrganization); - } - - var sqlOrganization = await sqlOrganizationRepo.CreateAsync(organization); - savedOrganizations.Add(await sqlOrganizationRepo.GetByIdAsync(sqlOrganization.Id)); - - var distinctItems = savedOrganizations.Distinct(equalityComparer); - Assert.True(!distinctItems.Skip(1).Any()); - } - [CiSkippedTheory, EfOrganizationAutoData] public async void ReplaceAsync_Works_DataMatches(Organization postOrganization, Organization replaceOrganization, SqlRepo.OrganizationRepository sqlOrganizationRepo, @@ -93,48 +68,6 @@ public async void DeleteAsync_Works_DataMatches(Organization organization, Assert.True(savedSqlOrganization == null); } - [CiSkippedTheory, EfOrganizationAutoData] - public async void GetByIdentifierAsync_Works_DataMatches(Organization organization, - SqlRepo.OrganizationRepository sqlOrganizationRepo, OrganizationCompare equalityComparer, - List suts) - { - var returnedOrgs = new List(); - foreach (var sut in suts) - { - var postEfOrg = await sut.CreateAsync(organization); - sut.ClearChangeTracking(); - - var returnedOrg = await sut.GetByIdentifierAsync(postEfOrg.Identifier.ToUpperInvariant()); - returnedOrgs.Add(returnedOrg); - } - - var postSqlOrg = await sqlOrganizationRepo.CreateAsync(organization); - returnedOrgs.Add(await sqlOrganizationRepo.GetByIdentifierAsync(postSqlOrg.Identifier.ToUpperInvariant())); - - var distinctItems = returnedOrgs.Distinct(equalityComparer); - Assert.True(!distinctItems.Skip(1).Any()); - } - - [CiSkippedTheory, EfOrganizationAutoData] - public async void GetManyByEnabledAsync_Works_DataMatches(Organization organization, - SqlRepo.OrganizationRepository sqlOrganizationRepo, List suts) - { - var returnedOrgs = new List(); - foreach (var sut in suts) - { - var postEfOrg = await sut.CreateAsync(organization); - sut.ClearChangeTracking(); - - var efReturnedOrgs = await sut.GetManyByEnabledAsync(); - returnedOrgs.Concat(efReturnedOrgs); - } - - var postSqlOrg = await sqlOrganizationRepo.CreateAsync(organization); - returnedOrgs.Concat(await sqlOrganizationRepo.GetManyByEnabledAsync()); - - Assert.True(returnedOrgs.All(o => o.Enabled)); - } - // testing data matches here would require manipulating all organization abilities in the db [CiSkippedTheory, EfOrganizationAutoData] public async void GetManyAbilitiesAsync_Works(SqlRepo.OrganizationRepository sqlOrganizationRepo, List suts) @@ -148,42 +81,4 @@ public async void GetManyAbilitiesAsync_Works(SqlRepo.OrganizationRepository sql list.Concat(await sqlOrganizationRepo.GetManyAbilitiesAsync()); Assert.True(list.All(x => x.GetType() == typeof(OrganizationAbility))); } - - [CiSkippedTheory, EfOrganizationUserAutoData] - public async void SearchUnassignedAsync_Works(OrganizationUser orgUser, User user, Organization org, - List efOrgUserRepos, List efOrgRepos, List efUserRepos, - SqlRepo.OrganizationUserRepository sqlOrgUserRepo, SqlRepo.OrganizationRepository sqlOrgRepo, SqlRepo.UserRepository sqlUserRepo) - { - orgUser.Type = OrganizationUserType.Owner; - org.PlanType = PlanType.EnterpriseAnnually; - - var efList = new List(); - foreach (var efOrgUserRepo in efOrgUserRepos) - { - var i = efOrgUserRepos.IndexOf(efOrgUserRepo); - var postEfUser = await efUserRepos[i].CreateAsync(user); - var postEfOrg = await efOrgRepos[i].CreateAsync(org); - efOrgUserRepo.ClearChangeTracking(); - - orgUser.UserId = postEfUser.Id; - orgUser.OrganizationId = postEfOrg.Id; - await efOrgUserRepo.CreateAsync(orgUser); - efOrgUserRepo.ClearChangeTracking(); - - efList.AddRange(await efOrgRepos[i].SearchUnassignedToProviderAsync(org.Name, user.Email, 0, 10)); - } - - var postSqlUser = await sqlUserRepo.CreateAsync(user); - var postSqlOrg = await sqlOrgRepo.CreateAsync(org); - - orgUser.UserId = postSqlUser.Id; - orgUser.OrganizationId = postSqlOrg.Id; - await sqlOrgUserRepo.CreateAsync(orgUser); - var sqlResult = await sqlOrgRepo.SearchUnassignedToProviderAsync(org.Name, user.Email, 0, 10); - - Assert.Equal(efOrgRepos.Count, efList.Count); - Assert.True(efList.All(o => o.Name == org.Name)); - Assert.Equal(1, sqlResult.Count); - Assert.True(sqlResult.All(o => o.Name == org.Name)); - } } diff --git a/test/Infrastructure.EFIntegration.Test/Repositories/PolicyRepositoryTests.cs b/test/Infrastructure.EFIntegration.Test/Repositories/PolicyRepositoryTests.cs deleted file mode 100644 index db05c52a3211..000000000000 --- a/test/Infrastructure.EFIntegration.Test/Repositories/PolicyRepositoryTests.cs +++ /dev/null @@ -1,51 +0,0 @@ -using Bit.Core.Entities; -using Bit.Core.Test.AutoFixture.Attributes; -using Bit.Infrastructure.EFIntegration.Test.AutoFixture; -using Bit.Infrastructure.EFIntegration.Test.Repositories.EqualityComparers; -using Xunit; -using EfRepo = Bit.Infrastructure.EntityFramework.Repositories; -using Policy = Bit.Core.Entities.Policy; -using SqlRepo = Bit.Infrastructure.Dapper.Repositories; - -namespace Bit.Infrastructure.EFIntegration.Test.Repositories; - -public class PolicyRepositoryTests -{ - [CiSkippedTheory, EfPolicyAutoData] - public async void CreateAsync_Works_DataMatches( - Policy policy, - Organization organization, - PolicyCompare equalityComparer, - List suts, - List efOrganizationRepos, - SqlRepo.PolicyRepository sqlPolicyRepo, - SqlRepo.OrganizationRepository sqlOrganizationRepo - ) - { - var savedPolicys = new List(); - foreach (var sut in suts) - { - var i = suts.IndexOf(sut); - - var efOrganization = await efOrganizationRepos[i].CreateAsync(organization); - sut.ClearChangeTracking(); - - policy.OrganizationId = efOrganization.Id; - var postEfPolicy = await sut.CreateAsync(policy); - sut.ClearChangeTracking(); - - var savedPolicy = await sut.GetByIdAsync(postEfPolicy.Id); - savedPolicys.Add(savedPolicy); - } - - var sqlOrganization = await sqlOrganizationRepo.CreateAsync(organization); - - policy.OrganizationId = sqlOrganization.Id; - var sqlPolicy = await sqlPolicyRepo.CreateAsync(policy); - var savedSqlPolicy = await sqlPolicyRepo.GetByIdAsync(sqlPolicy.Id); - savedPolicys.Add(savedSqlPolicy); - - var distinctItems = savedPolicys.Distinct(equalityComparer); - Assert.True(!distinctItems.Skip(1).Any()); - } -} diff --git a/test/Infrastructure.EFIntegration.Test/Repositories/TaxRateRepositoryTests.cs b/test/Infrastructure.EFIntegration.Test/Repositories/TaxRateRepositoryTests.cs deleted file mode 100644 index 8892f6c70d68..000000000000 --- a/test/Infrastructure.EFIntegration.Test/Repositories/TaxRateRepositoryTests.cs +++ /dev/null @@ -1,39 +0,0 @@ -using Bit.Core.Entities; -using Bit.Core.Test.AutoFixture.Attributes; -using Bit.Infrastructure.EFIntegration.Test.AutoFixture; -using Bit.Infrastructure.EFIntegration.Test.Repositories.EqualityComparers; -using Xunit; -using EfRepo = Bit.Infrastructure.EntityFramework.Repositories; -using SqlRepo = Bit.Infrastructure.Dapper.Repositories; - -namespace Bit.Infrastructure.EFIntegration.Test.Repositories; - -public class TaxRateRepositoryTests -{ - [CiSkippedTheory, EfTaxRateAutoData] - public async void CreateAsync_Works_DataMatches( - TaxRate taxRate, - TaxRateCompare equalityComparer, - List suts, - SqlRepo.TaxRateRepository sqlTaxRateRepo - ) - { - var savedTaxRates = new List(); - foreach (var sut in suts) - { - var i = suts.IndexOf(sut); - var postEfTaxRate = await sut.CreateAsync(taxRate); - sut.ClearChangeTracking(); - - var savedTaxRate = await sut.GetByIdAsync(postEfTaxRate.Id); - savedTaxRates.Add(savedTaxRate); - } - - var sqlTaxRate = await sqlTaxRateRepo.CreateAsync(taxRate); - var savedSqlTaxRate = await sqlTaxRateRepo.GetByIdAsync(sqlTaxRate.Id); - savedTaxRates.Add(savedSqlTaxRate); - - var distinctItems = savedTaxRates.Distinct(equalityComparer); - Assert.True(!distinctItems.Skip(1).Any()); - } -} diff --git a/test/Infrastructure.IntegrationTest/Repositories/CollectionCipherRepositoryTests.cs b/test/Infrastructure.IntegrationTest/Repositories/CollectionCipherRepositoryTests.cs new file mode 100644 index 000000000000..9d9e38131394 --- /dev/null +++ b/test/Infrastructure.IntegrationTest/Repositories/CollectionCipherRepositoryTests.cs @@ -0,0 +1,179 @@ +using Bit.Core.Entities; +using Bit.Core.Models.Data; +using Bit.Core.Repositories; +using Bit.Core.Vault.Entities; +using Bit.Core.Vault.Repositories; +using Xunit; + +namespace Bit.Infrastructure.IntegrationTest.Repositories; + +public class CollectionCipherRepositoryTests +{ + [DatabaseTheory, DatabaseData] + public async Task UpdateCollectionsAsync_Works(ICollectionCipherRepository collectionCipherRepository, + IServiceProvider services, + ICipherRepository cipherRepository, + ICollectionRepository collectionRepository) + { + var organizationUser = await services.CreateOrganizationUserAsync(); + + var originalExternalId = Guid.NewGuid().ToString(); + + await collectionRepository.CreateAsync(new Collection + { + OrganizationId = organizationUser.OrganizationId, + ExternalId = originalExternalId, + Name = "Test Collection", + }, + groups: Array.Empty(), + users: new[] + { + new CollectionAccessSelection + { + Id = organizationUser.Id, + HidePasswords = false, + ReadOnly = false, + }, + }); + + var cipher = await cipherRepository.CreateAsync(new Cipher + { + OrganizationId = organizationUser.OrganizationId, + Data = "", + }); + + var newExternalIds = new string[] + { + Guid.NewGuid().ToString(), + Guid.NewGuid().ToString() + }; + + var newCollections = await services.CreateManyAsync(async (ICollectionRepository cr, int index) => + { + var externalId = newExternalIds[index]; + await cr.CreateAsync(new Collection + { + OrganizationId = organizationUser.OrganizationId, + Name = "Test Collection", + ExternalId = externalId, + }, + groups: Array.Empty(), + users: new[] + { + new CollectionAccessSelection + { + Id = organizationUser.Id, + HidePasswords = true, + ReadOnly = false, + } + }); + + return (await cr.GetManyByOrganizationIdAsync(organizationUser.OrganizationId)) + .Single(c => c.ExternalId == externalId); + }, newExternalIds.Length); + + // Act + await collectionCipherRepository.UpdateCollectionsAsync(cipher.Id, + organizationUser.UserId!.Value, + newCollections.Select(c => c.Id)); + + // Assert + var collectionCiphers = (await collectionCipherRepository.GetManyByOrganizationIdAsync(organizationUser.OrganizationId)) + .ToArray(); + + Assert.Equal(2, collectionCiphers.Length); + } + + [DatabaseTheory, DatabaseData] + public async Task UpdateCollectionsForCiphersAsync_Works( + ICollectionCipherRepository collectionCipherRepository, + IUserRepository userRepository, + IServiceProvider services, + ICollectionRepository collectionRepository) + { + var organizationUser = await services.CreateOrganizationUserAsync(); + var originalUser = await userRepository.GetByIdAsync(organizationUser.UserId!.Value); + + var originalExternalId = Guid.NewGuid().ToString(); + + await collectionRepository.CreateAsync(new Collection + { + OrganizationId = organizationUser.OrganizationId, + ExternalId = originalExternalId, + Name = "Test Collection", + }, + groups: Array.Empty(), + users: new[] + { + new CollectionAccessSelection + { + Id = organizationUser.Id, + HidePasswords = false, + ReadOnly = false, + }, + }); + + var ciphers = await services.CreateManyAsync((ICipherRepository cr) => + { + return cr.CreateAsync(new Cipher + { + OrganizationId = organizationUser.OrganizationId, + Data = "", + }); + }, 5); + + var newExternalIds = new string[] + { + Guid.NewGuid().ToString(), + Guid.NewGuid().ToString() + }; + + var newCollections = await services.CreateManyAsync(async (ICollectionRepository cr, int index) => + { + var externalId = newExternalIds[index]; + await cr.CreateAsync(new Collection + { + OrganizationId = organizationUser.OrganizationId, + Name = "Test Collection", + ExternalId = externalId, + }, + groups: Array.Empty(), + users: new[] + { + new CollectionAccessSelection + { + Id = organizationUser.Id, + HidePasswords = true, + ReadOnly = false, + } + }); + + return (await cr.GetManyByOrganizationIdAsync(organizationUser.OrganizationId)) + .Single(c => c.ExternalId == externalId); + }, newExternalIds.Length); + + await collectionCipherRepository.UpdateCollectionsForCiphersAsync( + ciphers.Select(c => c.Id), + organizationUser.UserId!.Value, + organizationUser.OrganizationId, + newCollections.Select(c => c.Id)); + + var collectionCiphers = await collectionCipherRepository.GetManyByOrganizationIdAsync( + organizationUser.OrganizationId + ); + + Assert.Equal(10, collectionCiphers.Count); + foreach (var cipher in ciphers) + { + Assert.Equal(2, collectionCiphers.Where(cc => cc.CipherId == cipher.Id).Count()); + } + + foreach (var collection in newCollections) + { + Assert.Equal(5, collectionCiphers.Where(cc => cc.CollectionId == collection.Id).Count()); + } + + var updatedUser = await userRepository.GetByIdAsync(originalUser.Id); + Assert.NotEqual(originalUser.AccountRevisionDate, updatedUser.AccountRevisionDate); + } +} diff --git a/test/Infrastructure.IntegrationTest/Repositories/CollectionRepositoryTests.cs b/test/Infrastructure.IntegrationTest/Repositories/CollectionRepositoryTests.cs new file mode 100644 index 000000000000..77bab04541dd --- /dev/null +++ b/test/Infrastructure.IntegrationTest/Repositories/CollectionRepositoryTests.cs @@ -0,0 +1,130 @@ +using Bit.Core.Entities; +using Bit.Core.Models.Data; +using Bit.Core.Repositories; +using Xunit; + +namespace Bit.Infrastructure.IntegrationTest.Repositories; + +public class CollectionRepositoryTests +{ + [DatabaseTheory, DatabaseData] + public async Task CreateAsync_Works(ICollectionRepository collectionRepository, + IServiceProvider services) + { + var organizationUser = await services.CreateOrganizationUserAsync(); + + var externalId = Guid.NewGuid().ToString(); + + var createdCollection = await collectionRepository.CreateAsync(new Collection + { + Name = "Test Collection", + OrganizationId = organizationUser.OrganizationId, + ExternalId = externalId, + }); + + // Assert that we get the Id back right away so that we know we set the Id client side + Assert.NotEqual(createdCollection.Id, Guid.Empty); + + // Assert that we can find one from the database + var collection = await collectionRepository.GetByIdAsync(createdCollection.Id); + Assert.NotNull(collection); + + // Assert the found item has all the data we expect + Assert.Equal("Test Collection", collection.Name); + Assert.Equal(organizationUser.OrganizationId, collection.OrganizationId); + Assert.Equal(externalId, collection.ExternalId); + + // Assert the items returned from CreateAsync match what is found by id + Assert.Equal(createdCollection.Id, collection.Id); + Assert.Equal(createdCollection.Name, collection.Name); + Assert.Equal(createdCollection.OrganizationId, collection.OrganizationId); + // TODO: Assert dates + } + + [DatabaseTheory, DatabaseData] + public async Task GetManyByUserIdAsync_UserInCollection_ReturnsCollection(ICollectionRepository collectionRepository, + IServiceProvider services) + { + var organizationUser = await services.CreateOrganizationUserAsync(); + + var externalId = Guid.NewGuid().ToString(); + + await collectionRepository.CreateAsync(new Collection + { + Name = "Test Collection", + OrganizationId = organizationUser.OrganizationId, + ExternalId = externalId, + }, + groups: Array.Empty(), + users: new[] { new CollectionAccessSelection { Id = organizationUser.Id, HidePasswords = true, ReadOnly = false } }); + + await collectionRepository.CreateAsync(new Collection + { + Name = "Don't Match Collection", + OrganizationId = organizationUser.OrganizationId, + }); + + var collections = await collectionRepository.GetManyByUserIdAsync(organizationUser.UserId!.Value); + var collection = Assert.Single(collections); + Assert.Equal(externalId, collection.ExternalId); + Assert.True(collection.HidePasswords); + Assert.False(collection.ReadOnly); + } + + [DatabaseTheory, DatabaseData] + public async Task GetManyByUserIdAsync_UserInMultipleGroups_ReturnsCollection(ICollectionRepository collectionRepository, + IGroupRepository groupRepository, + IServiceProvider services) + { + var organizationUser = await services.CreateOrganizationUserAsync(); + + var externalId = Guid.NewGuid().ToString(); + + var group1 = await groupRepository.CreateAsync(new Group + { + Name = "Test Group #1", + OrganizationId = organizationUser.OrganizationId, + }); + + var group2 = await groupRepository.CreateAsync(new Group + { + Name = "Test Group #2", + OrganizationId = organizationUser.OrganizationId, + }); + + await groupRepository.UpdateUsersAsync(group1.Id, new[] { organizationUser.Id }); + await groupRepository.UpdateUsersAsync(group2.Id, new[] { organizationUser.Id }); + + await collectionRepository.CreateAsync(new Collection + { + Name = "Test Collection", + OrganizationId = organizationUser.OrganizationId, + ExternalId = externalId, + }, + // The user will be apart of two groups, one that gives them little access to the collection + // and one that gives them fully access, via our rules, they should be given the full rights. + groups: new[] + { + new CollectionAccessSelection + { + Id = group1.Id, + HidePasswords = true, + ReadOnly = true, + }, + new CollectionAccessSelection + { + Id = group2.Id, + HidePasswords = false, + ReadOnly = false, + } + }, + users: Array.Empty()); + + var collections = await collectionRepository.GetManyByUserIdAsync(organizationUser.UserId!.Value); + + var collection = Assert.Single(collections); + Assert.Equal(externalId, collection.ExternalId); + Assert.False(collection.ReadOnly); + Assert.False(collection.HidePasswords); + } +} diff --git a/test/Infrastructure.IntegrationTest/Repositories/DeviceRepositoryTests.cs b/test/Infrastructure.IntegrationTest/Repositories/DeviceRepositoryTests.cs new file mode 100644 index 000000000000..2078f5e492d1 --- /dev/null +++ b/test/Infrastructure.IntegrationTest/Repositories/DeviceRepositoryTests.cs @@ -0,0 +1,57 @@ +using Bit.Core.Entities; +using Bit.Core.Enums; +using Bit.Core.Repositories; +using Xunit; + +namespace Bit.Infrastructure.IntegrationTest.Repositories; + +public class DeviceRepositoryTests +{ + [DatabaseTheory, DatabaseData] + public async Task CreateAsync_Works(IDeviceRepository deviceRepository, + IServiceProvider services) + { + var user = await services.CreateUserAsync(); + + var identifier = Guid.NewGuid().ToString(); + + var createdDevice = await deviceRepository.CreateAsync(new Device + { + UserId = user.Id, + Identifier = identifier, + Name = "Android", + Type = DeviceType.Android, + PushToken = "my_push_token", + EncryptedPrivateKey = "encrypted_private_key", + EncryptedPublicKey = "encrypted_public_key", + EncryptedUserKey = "encrypted_user_key", + }); + + // Assert that we get the Id back right away so that we know we set the Id client side + Assert.NotEqual(createdDevice.Id, Guid.Empty); + + // Assert that we can find one from the database + var device = await deviceRepository.GetByIdAsync(createdDevice.Id); + Assert.NotNull(device); + + // Assert the found item has all the data we expect + Assert.Equal(user.Id, device.UserId); + Assert.Equal(identifier, device.Identifier); + Assert.Equal("Android", device.Name); + Assert.Equal(DeviceType.Android, device.Type); + Assert.Equal("my_push_token", device.PushToken); + Assert.Equal("encrypted_private_key", device.EncryptedPrivateKey); + Assert.Equal("encrypted_public_key", device.EncryptedPublicKey); + Assert.Equal("encrypted_user_key", device.EncryptedUserKey); + + // Assert the items returned from CreateAsync match what is found by id + Assert.Equal(createdDevice.Id, device.Id); + Assert.Equal(createdDevice.Identifier, device.Identifier); + Assert.Equal(createdDevice.Name, device.Name); + Assert.Equal(createdDevice.Type, device.Type); + Assert.Equal(createdDevice.PushToken, device.PushToken); + Assert.Equal(createdDevice.EncryptedPrivateKey, device.EncryptedPrivateKey); + Assert.Equal(createdDevice.EncryptedPublicKey, device.EncryptedPublicKey); + Assert.Equal(createdDevice.EncryptedUserKey, device.EncryptedUserKey); + } +} diff --git a/test/Infrastructure.IntegrationTest/Repositories/GroupRepositoryTests.cs b/test/Infrastructure.IntegrationTest/Repositories/GroupRepositoryTests.cs new file mode 100644 index 000000000000..5ac36062f22e --- /dev/null +++ b/test/Infrastructure.IntegrationTest/Repositories/GroupRepositoryTests.cs @@ -0,0 +1,209 @@ +using Bit.Core.Entities; +using Bit.Core.Models.Data; +using Bit.Core.Repositories; +using Xunit; + +namespace Bit.Infrastructure.IntegrationTest; + +public class GroupRepositoryTests +{ + [DatabaseTheory, DatabaseData] + public async Task GetByIdWithCollectionsAsync_Works(IGroupRepository groupRepository, + IServiceProvider services) + { + var organizationUser = await services.CreateOrganizationUserAsync(); + + var collections = await services.CreateManyAsync((ICollectionRepository c) => + c.CreateAsync(new Collection + { + OrganizationId = organizationUser.OrganizationId, + Name = "Test Collection", + }), 5); + + var groupExternalId = Guid.NewGuid().ToString(); + + await groupRepository.CreateAsync(new Group + { + OrganizationId = organizationUser.OrganizationId, + Name = "Test Group", + AccessAll = true, + ExternalId = groupExternalId, + }, new[] + { + new CollectionAccessSelection + { + Id = collections[0].Id, + HidePasswords = false, + ReadOnly = true, + }, + new CollectionAccessSelection + { + Id = collections[1].Id, + HidePasswords = false, + ReadOnly = false, + }, + }); + + var group = (await groupRepository.GetManyByOrganizationIdAsync(organizationUser.OrganizationId)) + .Single(); + + var (foundGroup, groupCollections) = await groupRepository.GetByIdWithCollectionsAsync(group.Id); + + Assert.NotNull(foundGroup); + Assert.Equal(2, groupCollections.Count); + Assert.Contains(groupCollections, + c => c.Id == collections[0].Id && !c.HidePasswords && c.ReadOnly); + Assert.Contains(groupCollections, + c => c.Id == collections[1].Id && !c.HidePasswords && !c.ReadOnly); + } + + [DatabaseTheory, DatabaseData] + public async Task GetManyCollectionsByOrganizationIdAsync_Works(IGroupRepository groupRepository, + IServiceProvider services) + { + var organizationUser = await services.CreateOrganizationUserAsync(); + + var collections = await services.CreateManyAsync((ICollectionRepository c) => + c.CreateAsync(new Collection + { + OrganizationId = organizationUser.OrganizationId, + Name = "Test Collection", + }), 5); + + var groupExternalIds = new[] + { + Guid.NewGuid().ToString(), + Guid.NewGuid().ToString(), + Guid.NewGuid().ToString(), + }; + + var groups = await services.CreateManyAsync(async (IGroupRepository g, int index) => + { + var externalId = groupExternalIds[index]; + await g.CreateAsync(new Group + { + OrganizationId = organizationUser.OrganizationId, + Name = "Test Group", + AccessAll = true, + ExternalId = externalId, + }, new[] + { + new CollectionAccessSelection + { + Id = collections[index].Id, + HidePasswords = true, + ReadOnly = true, + } + }); + + return (await g.GetManyByOrganizationIdAsync(organizationUser.OrganizationId)) + .Single(g => g.ExternalId == externalId); + }, groupExternalIds.Length); + + var groupsAndCollections = await groupRepository.GetManyWithCollectionsByOrganizationIdAsync(organizationUser.OrganizationId); + + Assert.Equal(3, groupsAndCollections.Count); + + var group1 = groupsAndCollections.FirstOrDefault(t => t.Item1.ExternalId == groupExternalIds[0]); + Assert.NotNull(group1); + var group1Collection = Assert.Single(group1!.Item2); + Assert.True(group1Collection.HidePasswords); + Assert.True(group1Collection.ReadOnly); + + var group2 = groupsAndCollections.FirstOrDefault(t => t.Item1.ExternalId == groupExternalIds[1]); + Assert.NotNull(group2); + Assert.Single(group2!.Item2); + var group2Collection = Assert.Single(group1!.Item2); + Assert.True(group2Collection.HidePasswords); + Assert.True(group2Collection.ReadOnly); + + var group3 = groupsAndCollections.FirstOrDefault(t => t.Item1.ExternalId == groupExternalIds[2]); + Assert.NotNull(group3); + Assert.Single(group3!.Item2); + var group3Collection = Assert.Single(group1!.Item2); + Assert.True(group3Collection.HidePasswords); + Assert.True(group3Collection.ReadOnly); + } + + [DatabaseTheory, DatabaseData] + public async Task ReplaceAsync_Works(IGroupRepository groupRepository, + IUserRepository userRepository, + IServiceProvider services, + ICollectionRepository collectionRepository) + { + var organizationUser = await services.CreateOrganizationUserAsync(); + + var user = await userRepository.GetByIdAsync(organizationUser.UserId!.Value); + + var starterExternalId = Guid.NewGuid().ToString(); + + var starterCollections = await services.CreateManyAsync((ICollectionRepository cr, int index) => + { + return cr.CreateAsync(new Collection + { + ExternalId = starterExternalId + index, + OrganizationId = organizationUser.OrganizationId, + Name = "Test Collection", + }); + }, 4); + + var groupExternalId = Guid.NewGuid().ToString(); + + await groupRepository.CreateAsync(new Group + { + Name = "Test Group", + AccessAll = true, + OrganizationId = organizationUser.OrganizationId, + ExternalId = groupExternalId, + }, starterCollections.Select(c => new CollectionAccessSelection + { + Id = c.Id, + HidePasswords = false, + ReadOnly = true, + })); + + var group = (await groupRepository.GetManyByOrganizationIdAsync(organizationUser.OrganizationId)) + .Single(g => g.ExternalId == groupExternalId); + + var newExternalId = Guid.NewGuid().ToString(); + var newCollections = await services.CreateManyAsync((ICollectionRepository cr, int index) => + cr.CreateAsync(new Collection + { + ExternalId = newExternalId + index, + OrganizationId = organizationUser.OrganizationId, + Name = "Test Collection", + }), 2); + + // Act + await groupRepository.ReplaceAsync(group, + newCollections.Select(c => new CollectionAccessSelection + { + Id = c.Id, + HidePasswords = true, + ReadOnly = true, + })); + + // Assert + var groupCollections = await groupRepository.GetByIdWithCollectionsAsync(group.Id); + Assert.Equal(2, groupCollections.Item2.Count); + Assert.All(groupCollections.Item2, c => + { + Assert.True(c.HidePasswords); + Assert.True(c.ReadOnly); + }); + + var organizationCollections = await collectionRepository.GetManyByOrganizationIdWithAccessAsync(organizationUser.OrganizationId); + var starterCollectionDetails = organizationCollections.Where(c => c.Item1.ExternalId.StartsWith(starterExternalId)); + Assert.Equal(4, starterCollectionDetails.Count()); + Assert.All(starterCollectionDetails, d => + { + var (_, groupsAndUsers) = d; + Assert.Empty(groupsAndUsers.Users); + Assert.Empty(groupsAndUsers.Groups); + }); + + var updatedUser = await userRepository.GetByIdAsync(user.Id); + + Assert.True(user.AccountRevisionDate < updatedUser.AccountRevisionDate); + } +} diff --git a/test/Infrastructure.IntegrationTest/Repositories/InstallationRepositoryTests.cs b/test/Infrastructure.IntegrationTest/Repositories/InstallationRepositoryTests.cs new file mode 100644 index 000000000000..28b22efc3536 --- /dev/null +++ b/test/Infrastructure.IntegrationTest/Repositories/InstallationRepositoryTests.cs @@ -0,0 +1,38 @@ +using Bit.Core.Entities; +using Bit.Core.Repositories; +using Xunit; + +namespace Bit.Infrastructure.IntegrationTest.Repositories; + +public class InstallationRepositoryTests +{ + [DatabaseTheory, DatabaseData] + public async Task CreateAsync_Works(IInstallationRepository installationRepository) + { + var createdInstallation = await installationRepository.CreateAsync(new Installation + { + Email = "test@example.com", + Key = "test_key", + Enabled = true, + }); + + // Assert that we get the Id back right away so that we know we set the Id client side + Assert.NotEqual(createdInstallation.Id, Guid.Empty); + + // Assert that we can find one from the database + var installation = await installationRepository.GetByIdAsync(createdInstallation.Id); + Assert.NotNull(installation); + + // Assert the found item has all the data we expect + Assert.Equal("test@example.com", installation.Email); + Assert.Equal("test_key", installation.Key); + Assert.True(installation.Enabled); + + // Assert the items returned from CreateAsync match what is found by id + Assert.Equal(createdInstallation.Id, installation.Id); + Assert.Equal(createdInstallation.Email, installation.Email); + Assert.Equal(createdInstallation.Key, installation.Key); + Assert.Equal(createdInstallation.Enabled, installation.Enabled); + // TODO: Assert dates + } +} diff --git a/test/Infrastructure.IntegrationTest/Repositories/OrganizationRepositoryTests.cs b/test/Infrastructure.IntegrationTest/Repositories/OrganizationRepositoryTests.cs new file mode 100644 index 000000000000..f71c6e52b33d --- /dev/null +++ b/test/Infrastructure.IntegrationTest/Repositories/OrganizationRepositoryTests.cs @@ -0,0 +1,102 @@ +using Bit.Core.Entities; +using Bit.Core.Enums; +using Bit.Core.Repositories; +using Xunit; + +namespace Bit.Infrastructure.IntegrationTest.Repositories; + +public class OrganizationRepositoryTests +{ + [DatabaseTheory, DatabaseData] + public async Task CreateAsync_Works(IOrganizationRepository organizationRepository) + { + var createdOrganization = await organizationRepository.CreateAsync(new Organization + { + Name = "Test Org", // TODO: EF doesn't enforce this as not null + BillingEmail = "email@test.com", // TODO: EF doesn't enforce this as not null + Plan = "Free", // TODO: EF doesn't enforce this as not null + }); + + var fromDatabaseOrganization = await organizationRepository.GetByIdAsync(createdOrganization.Id); + + Assert.NotNull(fromDatabaseOrganization); + + Assert.Equal("Test Org", fromDatabaseOrganization.Name); + Assert.Equal("email@test.com", fromDatabaseOrganization.BillingEmail); + Assert.Equal("Free", fromDatabaseOrganization.Plan); + } + + [DatabaseTheory, DatabaseData] + public async Task GetByIdentifierAsync_Works(IOrganizationRepository organizationRepository) + { + var nonSearchIdentifier = Guid.NewGuid().ToString(); + var searchIdentifier = Guid.NewGuid().ToString(); + + var org1 = await organizationRepository.CreateAsync(new Organization + { + Name = "Test Org #1", // TODO: EF doesn't enforce this as not null + BillingEmail = "email@test.com", // TODO: EF doesn't enforce this as not null + Plan = "Free", // TODO: EF doesn't enforce this as not null + Identifier = nonSearchIdentifier, // TODO: EF doesn't enforce this as unique + }); + + var org2 = await organizationRepository.CreateAsync(new Organization + { + Name = "Test Org #2", // TODO: EF doesn't enforce this as not null + BillingEmail = "email@test.com", // TODO: EF doesn't enforce this as not null + Plan = "Free", // TODO: EF doesn't enforce this as not null + Identifier = searchIdentifier, + }); + + var retrievedOrganization = await organizationRepository.GetByIdentifierAsync(searchIdentifier); + + Assert.NotNull(retrievedOrganization); + Assert.Equal(org2.Id, retrievedOrganization.Id); + } + + [DatabaseTheory, DatabaseData] + public async Task GetManyByEnabledAsync_Works(IOrganizationRepository organizationRepository) + { + var createdNotEnabledOrg = await organizationRepository.CreateAsync(new Organization + { + Name = "Test Org", // TODO: EF doesn't enforce this as not null + BillingEmail = "email@test.com", // TODO: EF doesn't enforce this as not null + Plan = "Free", // TODO: EF doesn't enforce this as not null + Enabled = false, + }); + + var createdEnabledOrg = await organizationRepository.CreateAsync(new Organization + { + Name = "Test Org", // TODO: EF doesn't enforce this as not null + BillingEmail = "email@test.com", // TODO: EF doesn't enforce this as not null + Plan = "Free", // TODO: EF doesn't enforce this as not null + Enabled = true, + }); + + var enabledOrgs = await organizationRepository.GetManyByEnabledAsync(); + Assert.Contains(enabledOrgs, o => o.Id == createdEnabledOrg.Id); + Assert.DoesNotContain(enabledOrgs, o => o.Id == createdNotEnabledOrg.Id); + } + + [DatabaseTheory, DatabaseData] + public async Task SearchUnassignedToProviderAsync_Works(IOrganizationRepository organizationRepository, + IUserRepository userRepository, + IServiceProvider services) + { + var organizationName = $"test_provider_{Guid.NewGuid()}"; + + var orgUser = await services.CreateOrganizationUserAsync(); + var organization = await services.UpdateOrganizationAsync(orgUser.OrganizationId, organization => + { + organization.Name = organizationName; + organization.PlanType = PlanType.EnterpriseAnnually; + }); + + var user = await userRepository.GetByIdAsync(orgUser.UserId!.Value); + + var matchingOrganizations = await organizationRepository.SearchUnassignedToProviderAsync(organizationName[5..20], + user.Email[..10], 0, 20); + + Assert.Contains(matchingOrganizations, o => o.Name == organizationName); + } +} diff --git a/test/Infrastructure.IntegrationTest/Repositories/PolicyRepositoryTests.cs b/test/Infrastructure.IntegrationTest/Repositories/PolicyRepositoryTests.cs new file mode 100644 index 000000000000..206dbd7b393d --- /dev/null +++ b/test/Infrastructure.IntegrationTest/Repositories/PolicyRepositoryTests.cs @@ -0,0 +1,44 @@ +using Bit.Core.Entities; +using Bit.Core.Enums; +using Bit.Core.Repositories; +using Xunit; + +namespace Bit.Infrastructure.IntegrationTest.Repositories; + +public class PolicyRepositoryTests +{ + [DatabaseTheory, DatabaseData] + public async Task CreateAsync_Works(IPolicyRepository policyRepository, + IServiceProvider services) + { + var organizationUser = await services.CreateOrganizationUserAsync(); + + var createdPolicy = await policyRepository.CreateAsync(new Policy + { + Type = PolicyType.ActivateAutofill, + Enabled = true, + Data = "{}", + OrganizationId = organizationUser.OrganizationId, + }); + + // Assert that we get the Id back right away so that we know we set the Id client side + Assert.NotEqual(createdPolicy.Id, Guid.Empty); + + // Assert that we can find one from the database + var policy = await policyRepository.GetByIdAsync(createdPolicy.Id); + Assert.NotNull(policy); + + // Assert the found item has all the data we expect + Assert.Equal(PolicyType.ActivateAutofill, policy.Type); + Assert.True(policy.Enabled); + Assert.Equal("{}", policy.Data); + Assert.Equal(organizationUser.OrganizationId, policy.OrganizationId); + + // Assert the items returned from CreateAsync match what is found by id + Assert.Equal(createdPolicy.Id, policy.Id); + Assert.Equal(createdPolicy.Type, policy.Type); + Assert.Equal(createdPolicy.Enabled, policy.Enabled); + Assert.Equal(createdPolicy.Data, policy.Data); + Assert.Equal(createdPolicy.OrganizationId, policy.OrganizationId); + } +} diff --git a/test/Infrastructure.IntegrationTest/Repositories/TaxRateRepositoryTests.cs b/test/Infrastructure.IntegrationTest/Repositories/TaxRateRepositoryTests.cs new file mode 100644 index 000000000000..ce5b1f8e4e08 --- /dev/null +++ b/test/Infrastructure.IntegrationTest/Repositories/TaxRateRepositoryTests.cs @@ -0,0 +1,43 @@ +using Bit.Core.Entities; +using Bit.Core.Repositories; +using Xunit; + +namespace Bit.Infrastructure.IntegrationTest.Repositories; + +public class TaxRateRepositoryTests +{ + [DatabaseTheory, DatabaseData] + public async Task CreateAsync_Works(ITaxRateRepository taxRateRepository) + { + var id = Guid.NewGuid().ToString(); + var createdTaxRate = await taxRateRepository.CreateAsync(new TaxRate + { + Id = id, + Country = "US", + Active = true, + PostalCode = "12345", + Rate = 5.1m, + State = "AL", + }); + + // Assert that we can find one from the database + var taxRate = await taxRateRepository.GetByIdAsync(createdTaxRate.Id); + Assert.NotNull(taxRate); + + // Assert the found item has all the data we expect + Assert.Equal(id, taxRate.Id); + Assert.Equal("US", taxRate.Country); + Assert.True(taxRate.Active); + Assert.Equal("12345", taxRate.PostalCode); + Assert.Equal(5.1m, taxRate.Rate); + Assert.Equal("AL", taxRate.State); + + // Assert the items returned from CreateAsync match what is found by id + Assert.Equal(createdTaxRate.Id, taxRate.Id); + Assert.Equal(createdTaxRate.Country, taxRate.Country); + Assert.Equal(createdTaxRate.Active, taxRate.Active); + Assert.Equal(createdTaxRate.PostalCode, taxRate.PostalCode); + Assert.Equal(createdTaxRate.Rate, taxRate.Rate); + Assert.Equal(createdTaxRate.State, taxRate.State); + } +} diff --git a/test/Infrastructure.IntegrationTest/ServiceProviderExtensions.cs b/test/Infrastructure.IntegrationTest/ServiceProviderExtensions.cs new file mode 100644 index 000000000000..a8e7a61d0b92 --- /dev/null +++ b/test/Infrastructure.IntegrationTest/ServiceProviderExtensions.cs @@ -0,0 +1,76 @@ +using Bit.Core.Entities; +using Bit.Core.Enums; +using Bit.Core.Repositories; +using Microsoft.Extensions.DependencyInjection; + +namespace Bit.Infrastructure.IntegrationTest; + +public static class ServiceProviderExtensions +{ + public static async Task CreateUserAsync(this IServiceProvider services) + { + var userRepository = services.GetRequiredService(); + return await userRepository.CreateAsync(new User + { + Name = "Test User", + Email = $"test+{Guid.NewGuid()}@email.com", + ApiKey = "TEST", + SecurityStamp = "stamp", + Key = "something", + PrivateKey = "private_key", + }); + } + + public static async Task CreateOrganizationAsync(this IServiceProvider services) + { + var organizationRepository = services.GetRequiredService(); + return await organizationRepository.CreateAsync(new Organization + { + Name = "Test Org", // TODO: EF doesn't enforce this as not null + BillingEmail = "email@test.com", // TODO: EF doesn't enforce this as not null + Plan = "Free", // TODO: EF doesn't enforce this as not null + }); + } + + public static async Task CreateOrganizationUserAsync(this IServiceProvider services) + { + var organizationUserRepository = services.GetRequiredService(); + + var createdUser = await services.CreateUserAsync(); + var createdOrganization = await services.CreateOrganizationAsync(); + + return await organizationUserRepository.CreateAsync(new OrganizationUser + { + UserId = createdUser.Id, + OrganizationId = createdOrganization.Id, + Type = OrganizationUserType.Owner, + Status = OrganizationUserStatusType.Confirmed, + }); + } + + public static async Task UpdateOrganizationAsync(this IServiceProvider services, Guid organizationId, Action configure) + { + var organizationRepository = services.GetRequiredService(); + var organization = await organizationRepository.GetByIdAsync(organizationId); + configure(organization); + await organizationRepository.ReplaceAsync(organization); + return organization; + } + + public static async Task> CreateManyAsync(this IServiceProvider services, Func> creator, int number) + where TService : class + { + var service = services.GetRequiredService(); + + var items = new T[number]; + for (var i = 0; i < number; i++) + { + items[i] = await creator(service, i); + } + return items; + } + + public static async Task> CreateManyAsync(this IServiceProvider services, Func> creator, int number) + where TService : class + => await services.CreateManyAsync(async (TService service, int _) => await creator(service), number); +} diff --git a/test/Infrastructure.IntegrationTest/restore.sh b/test/Infrastructure.IntegrationTest/restore.sh new file mode 100644 index 000000000000..7d3a4b0907de --- /dev/null +++ b/test/Infrastructure.IntegrationTest/restore.sh @@ -0,0 +1,112 @@ +#!/usr/bin/env bash + +# DIR=$(cd -P "$(dirname "${BASH_SOURCE[0]}")" && pwd) + +API_SECRETS=$(dotnet user-secrets list --project ../../src/Api/Api.csproj) + +get_connection_string() { + echo "$(echo "$API_SECRETS" | grep ^globalSettings:$1:connectionString | sed "s/globalSettings:$1:connectionString = //g")" +} + +get_db() { + # Takes + DB_NAME=$(echo "$1" | perl -nle"print $& while m{$2=\w+}g" | sed "s/$2=//g") + TEST_DB_NAME="${DB_NAME}_int_test" + echo "$1" | sed "s/$DB_NAME/$TEST_DB_NAME/" +} + +drop_and_migrate_ef() { + # $1 should be connection string + # $2 should be name of migrations project + # $3 should be name of configuration section + # $4 should be the index of where to save the + # $5 *optional* Is the exact SupportedDatabaseProviders name of this database + # defaults to $3 if not specified + if [ -z "$5" ]; then + ENUM="$3" + else + ENUM="$5" + fi + + # TODO: Filter out info messages and only show warning/error + dotnet ef database drop --project ../../util/$2/ --startup-project ../../util/$2/ --force --prefix-output -- --GlobalSettings:$3:ConnectionString="$1" > /dev/null + # TODO: Filter out info messages and only show warning/error + dotnet ef database update --project ../../util/$2/ --startup-project ../../util/$2/ --connection "$1" --prefix-output -- --GlobalSettings:$3:ConnectionString="$1" + + dotnet user-secrets set "Databases:$4:Type" "$ENUM" + dotnet user-secrets set "Databases:$4:ConnectionString" "$1" > /dev/null + dotnet user-secrets set "Databases:$4:Enabled" true > /dev/null +} + +# Server=localhost;Database=vault_dev;User Id=SA;Password=secure_password;TrustServerCertificate=true; +SQLSERVER_CONN_STR=$(get_connection_string "sqlServer") + +# Host=localhost;Username=postgres;Password=secure_password;Database=vault_dev +POSTGRES_CONN_STR=$(get_connection_string "postgreSql") + +# server=localhost;uid=root;pwd=secure_password;database=vault_dev +MYSQL_CONN_STR=$(get_connection_string "mySql") + +# Data Source=/home/user/vault_dev.db +SQLITE_CONN_STR=$(get_connection_string "sqlite") + +dotnet tool restore +dotnet user-secrets clear + +INDEX=0 + +increment() { + INDEX=$((INDEX + 1)) +} + +if [ ! -z "$SQLSERVER_CONN_STR" ]; then + echo "You have a SQL Server Connection string." + DB=$(get_db "$SQLSERVER_CONN_STR" "Database") + dotnet run --project ../../util/MsSqlMigratorUtility -- "$DB" + dotnet user-secrets set "Databases:$INDEX:Type" "SqlServer" + dotnet user-secrets set "Databases:$INDEX:ConnectionString" "$DB" > /dev/null + dotnet user-secrets set "Databases:$INDEX:Enabled" true > /dev/null + increment +fi + +echo + +if [ ! -z "$POSTGRES_CONN_STR" ]; then + echo "Making a test database for Postgres: $POSTGRES_CONN_STR" + DB=$(get_db "$POSTGRES_CONN_STR" "Database") + drop_and_migrate_ef "$DB" "PostgresMigrations" "PostgreSql" "$INDEX" "Postgres" + increment +fi + +echo + +if [ ! -z "$MYSQL_CONN_STR" ]; then + echo "Making a test database for MySql: $MYSQL_CONN_STR" + DB=$(get_db "$MYSQL_CONN_STR" "database") + # TODO: MySql needs that special variable, should we assist in that? + drop_and_migrate_ef "$DB" "MySqlMigrations" "MySql" "$INDEX" + increment +fi + +echo + +if [ ! -z "$SQLITE_CONN_STR" ]; then + echo "Making a test database for Sqlite: $SQLITE_CONN_STR" + # this makes the assumption it is a file which I think is likely for local dev + FULL_DB_PATH=$(echo "$SQLITE_CONN_STR" | sed "s/Data Source=//g") + FILE_NAME=$(basename "$FULL_DB_PATH") + FILE_DIR=$(dirname "$FULL_DB_PATH") + FILE_NAME_NO_EXT="${FILE_NAME%.*}" + TEST_FILE_NAME="${FILE_NAME_NO_EXT}_int_test" + TEST_DB_PATH="$FILE_DIR/$TEST_FILE_NAME.db" + TEST_CONN_STR=$(echo "$SQLITE_CONN_STR" | sed "s~$FULL_DB_PATH~$TEST_DB_PATH~") + drop_and_migrate_ef "$TEST_CONN_STR" "SqliteMigrations" "Sqlite" "$INDEX" + increment +else + echo "You don't have a SQLite Connection string, setting one up for you" + TEST_DB_PATH="$(pwd)/test.db" + drop_and_migrate_ef "Data Source=$TEST_DB_PATH" "SqliteMigrations" "Sqlite" "$INDEX" + increment +fi + +echo