From 4f3d1587e2b09836e2c12a254c030311cb337d98 Mon Sep 17 00:00:00 2001 From: Sang <11912225+hantatsang@users.noreply.github.com> Date: Wed, 2 Jun 2021 01:13:08 +1000 Subject: [PATCH] Re-write CollectionService unit tests with AutoFixtures (#1330) * Add CollectionService unit tests * Add missing CollectionFixtures * Resolve pr comments * Resolve PR comments --- .../AutoFixture/CollectionFixtures.cs | 12 + .../AutoFixture/OrganizationFixtures.cs | 8 +- .../Services/CollectionServiceTests.cs | 275 +++++++++--------- 3 files changed, 151 insertions(+), 144 deletions(-) create mode 100644 test/Core.Test/AutoFixture/CollectionFixtures.cs diff --git a/test/Core.Test/AutoFixture/CollectionFixtures.cs b/test/Core.Test/AutoFixture/CollectionFixtures.cs new file mode 100644 index 0000000000..22acefef4d --- /dev/null +++ b/test/Core.Test/AutoFixture/CollectionFixtures.cs @@ -0,0 +1,12 @@ +using Bit.Core.Test.AutoFixture.Attributes; +using Bit.Core.Test.AutoFixture.OrganizationFixtures; + +namespace Bit.Core.Test.AutoFixture +{ + + internal class CollectionAutoDataAttribute : CustomAutoDataAttribute + { + public CollectionAutoDataAttribute() : base(new SutProviderCustomization(), new Organization()) + { } + } +} diff --git a/test/Core.Test/AutoFixture/OrganizationFixtures.cs b/test/Core.Test/AutoFixture/OrganizationFixtures.cs index 482de7e2d0..5fd953270a 100644 --- a/test/Core.Test/AutoFixture/OrganizationFixtures.cs +++ b/test/Core.Test/AutoFixture/OrganizationFixtures.cs @@ -19,11 +19,17 @@ namespace Bit.Core.Test.AutoFixture.OrganizationFixtures public void Customize(IFixture fixture) { var organizationId = Guid.NewGuid(); + var maxConnections = (short)new Random().Next(10, short.MaxValue); fixture.Customize(composer => composer .With(o => o.Id, organizationId) + .With(o => o.MaxCollections, maxConnections) .With(o => o.UseGroups, UseGroups)); + fixture.Customize(composer => + composer + .With(c => c.OrganizationId, organizationId)); + fixture.Customize(composer => composer.With(g => g.OrganizationId, organizationId)); } } @@ -53,7 +59,7 @@ namespace Bit.Core.Test.AutoFixture.OrganizationFixtures var plansToIgnore = new List { PlanType.Free, PlanType.Custom }; var selectedPlan = StaticStore.Plans.Last(p => !plansToIgnore.Contains(p.Type) && !p.Disabled); - + fixture.Customize(composer => composer .With(ou => ou.Plan, selectedPlan.Type) .With(ou => ou.PremiumAccessAddon, selectedPlan.HasPremiumAccessOption)); diff --git a/test/Core.Test/Services/CollectionServiceTests.cs b/test/Core.Test/Services/CollectionServiceTests.cs index bfecc70167..d41dcb030f 100644 --- a/test/Core.Test/Services/CollectionServiceTests.cs +++ b/test/Core.Test/Services/CollectionServiceTests.cs @@ -1,184 +1,173 @@ using System; +using System.Collections.Generic; using System.Threading.Tasks; -using Xunit; +using Bit.Core.Enums; +using Bit.Core.Exceptions; +using Bit.Core.Models.Data; +using Bit.Core.Models.Table; using Bit.Core.Repositories; using Bit.Core.Services; +using Bit.Core.Test.AutoFixture; +using Bit.Core.Test.AutoFixture.Attributes; using NSubstitute; -using Bit.Core.Exceptions; +using Xunit; namespace Bit.Core.Test.Services { public class CollectionServiceTest { - private readonly IEventService _eventService; - private readonly IOrganizationRepository _organizationRepository; - private readonly IOrganizationUserRepository _organizationUserRepository; - private readonly ICollectionRepository _collectionRepository; - private readonly IUserRepository _userRepository; - private readonly IMailService _mailService; - - public CollectionServiceTest() + [Theory, CollectionAutoData] + public async Task SaveAsync_DefaultId_CreatesCollectionInTheRepository(Collection collection, Organization organization, SutProvider sutProvider) { - _eventService = Substitute.For(); - _organizationRepository = Substitute.For(); - _organizationUserRepository = Substitute.For(); - _collectionRepository = Substitute.For(); - _userRepository = Substitute.For(); - _mailService = Substitute.For(); + collection.Id = default; + sutProvider.GetDependency().GetByIdAsync(organization.Id).Returns(organization); + var utcNow = DateTime.UtcNow; + + await sutProvider.Sut.SaveAsync(collection); + + await sutProvider.GetDependency().Received().CreateAsync(collection); + await sutProvider.GetDependency().Received() + .LogCollectionEventAsync(collection, EventType.Collection_Created); + Assert.True(collection.CreationDate - utcNow < TimeSpan.FromSeconds(1)); + Assert.True(collection.RevisionDate - utcNow < TimeSpan.FromSeconds(1)); } - [Fact] - public async Task SaveAsync_CollectionNotFound() + [Theory, CollectionAutoData] + public async Task SaveAsync_DefaultIdWithGroups_CreateCollectionWithGroupsInRepository(Collection collection, + IEnumerable groups, Organization organization, OrganizationUser organizationUser, + SutProvider sutProvider) { - var collectionService = new CollectionService( - _eventService, - _organizationRepository, - _organizationUserRepository, - _collectionRepository, - _userRepository, - _mailService); + collection.Id = default; + organization.UseGroups = true; + sutProvider.GetDependency().GetByIdAsync(organization.Id).Returns(organization); + var utcNow = DateTime.UtcNow; - var id = Guid.NewGuid(); + await sutProvider.Sut.SaveAsync(collection, groups); - var collection = new Core.Models.Table.Collection - { - Id = id, - }; - - var ex = await Assert.ThrowsAsync(() => collectionService.SaveAsync(collection)); - - Assert.Equal("Organization not found", ex.Message); + await sutProvider.GetDependency().Received().CreateAsync(collection, groups); + await sutProvider.GetDependency().Received() + .LogCollectionEventAsync(collection, EventType.Collection_Created); + Assert.True(collection.CreationDate - utcNow < TimeSpan.FromSeconds(1)); + Assert.True(collection.RevisionDate - utcNow < TimeSpan.FromSeconds(1)); } - [Fact] - public async Task SaveAsync_DefaultCollectionId_CreatesCollectionInTheRepository() + [Theory, CollectionAutoData] + public async Task SaveAsync_NonDefaultId_ReplacesCollectionInRepository(Collection collection, Organization organization, SutProvider sutProvider) { - // prepare the organization - var testOrganizationId = Guid.NewGuid(); - var testOrganization = new Core.Models.Table.Organization - { - Id = testOrganizationId, - }; - _organizationRepository.GetByIdAsync(testOrganizationId).Returns(testOrganization); + var creationDate = collection.CreationDate; + sutProvider.GetDependency().GetByIdAsync(organization.Id).Returns(organization); + var utcNow = DateTime.UtcNow; - var collectionService = new CollectionService( - _eventService, - _organizationRepository, - _organizationUserRepository, - _collectionRepository, - _userRepository, - _mailService); + await sutProvider.Sut.SaveAsync(collection); - // execute - var testCollection = new Core.Models.Table.Collection - { - OrganizationId = testOrganizationId, - }; - await collectionService.SaveAsync(testCollection); - - // verify - await _collectionRepository.Received().CreateAsync(testCollection); + await sutProvider.GetDependency().Received().ReplaceAsync(collection); + await sutProvider.GetDependency().Received() + .LogCollectionEventAsync(collection, EventType.Collection_Updated); + Assert.Equal(collection.CreationDate, creationDate); + Assert.True(collection.RevisionDate - utcNow < TimeSpan.FromSeconds(1)); } - [Fact] - public async Task SaveAsync_RespectsMaxNumberOfCollectionsPerOrganization() + [Theory, CollectionAutoData] + public async Task SaveAsync_OrganizationNotUseGroup_CreateCollectionWithoutGroupsInRepository(Collection collection, IEnumerable groups, + Organization organization, OrganizationUser organizationUser, SutProvider sutProvider) { - // prepare the organization - var testOrganizationId = Guid.NewGuid(); - var testOrganization = new Core.Models.Table.Organization - { - Id = testOrganizationId, - MaxCollections = 2, - }; - _organizationRepository.GetByIdAsync(testOrganizationId).Returns(testOrganization); - _collectionRepository.GetCountByOrganizationIdAsync(testOrganizationId).Returns(2); + collection.Id = default; + sutProvider.GetDependency().GetByIdAsync(organization.Id).Returns(organization); + var utcNow = DateTime.UtcNow; - // execute - var collectionService = new CollectionService( - _eventService, - _organizationRepository, - _organizationUserRepository, - _collectionRepository, - _userRepository, - _mailService); + await sutProvider.Sut.SaveAsync(collection, groups); - var testCollection = new Core.Models.Table.Collection { OrganizationId = testOrganizationId }; - - // verify & expect exception to be thrown - var ex = await Assert.ThrowsAsync(() => collectionService.SaveAsync(testCollection)); - - Assert.Equal("You have reached the maximum number of collections (2) for this organization.", - ex.Message); + await sutProvider.GetDependency().Received().CreateAsync(collection); + await sutProvider.GetDependency().Received() + .LogCollectionEventAsync(collection, EventType.Collection_Created); + Assert.True(collection.CreationDate - utcNow < TimeSpan.FromSeconds(1)); + Assert.True(collection.RevisionDate - utcNow < TimeSpan.FromSeconds(1)); } - [Fact] - public async Task DeleteUserAsync_DeletesValidUserWhoBelongsToCollection() + [Theory, CollectionAutoData] + public async Task SaveAsync_DefaultIdWithUserId_UpdateUserInCollectionRepository(Collection collection, + Organization organization, OrganizationUser organizationUser, SutProvider sutProvider) { - // prepare the organization - var testOrganizationId = Guid.NewGuid(); - var testOrganization = new Core.Models.Table.Organization - { - Id = testOrganizationId, - }; - var testUserId = Guid.NewGuid(); - var organizationUser = new Core.Models.Table.OrganizationUser - { - Id = testUserId, - OrganizationId = testOrganizationId, - }; - _organizationUserRepository.GetByIdAsync(testUserId).Returns(organizationUser); + collection.Id = default; + organizationUser.Status = OrganizationUserStatusType.Confirmed; + sutProvider.GetDependency().GetByIdAsync(organization.Id).Returns(organization); + sutProvider.GetDependency().GetByOrganizationAsync(organization.Id, organizationUser.Id) + .Returns(organizationUser); + var utcNow = DateTime.UtcNow; - // execute - var collectionService = new CollectionService( - _eventService, - _organizationRepository, - _organizationUserRepository, - _collectionRepository, - _userRepository, - _mailService); + await sutProvider.Sut.SaveAsync(collection, null, organizationUser.Id); - var testCollection = new Core.Models.Table.Collection { OrganizationId = testOrganizationId }; - await collectionService.DeleteUserAsync(testCollection, organizationUser.Id); - - // verify - await _collectionRepository.Received().DeleteUserAsync(testCollection.Id, organizationUser.Id); + await sutProvider.GetDependency().Received().CreateAsync(collection); + await sutProvider.GetDependency().Received() + .GetByOrganizationAsync(organization.Id, organizationUser.Id); + await sutProvider.GetDependency().Received().UpdateUsersAsync(collection.Id, Arg.Any>()); + await sutProvider.GetDependency().Received() + .LogCollectionEventAsync(collection, EventType.Collection_Created); + Assert.True(collection.CreationDate - utcNow < TimeSpan.FromSeconds(1)); + Assert.True(collection.RevisionDate - utcNow < TimeSpan.FromSeconds(1)); } - [Fact] - public async Task DeleteUserAsync_ThrowsIfUserIsInvalid() + [Theory, CustomAutoData(typeof(SutProviderCustomization))] + public async Task SaveAsync_NonExistingOrganizationId_ThrowsBadRequest(Collection collection, SutProvider sutProvider) { - // prepare the organization - var testOrganizationId = Guid.NewGuid(); - var testOrganization = new Core.Models.Table.Organization - { - Id = testOrganizationId, - }; - var testUserId = Guid.NewGuid(); - var nonOrganizationUser = new Core.Models.Table.OrganizationUser - { - Id = testUserId, - OrganizationId = Guid.NewGuid(), - }; - _organizationUserRepository.GetByIdAsync(testUserId).Returns(nonOrganizationUser); + var ex = await Assert.ThrowsAsync(() => sutProvider.Sut.SaveAsync(collection)); + Assert.Contains("Organization not found", ex.Message); + await sutProvider.GetDependency().DidNotReceiveWithAnyArgs().CreateAsync(default); + await sutProvider.GetDependency().DidNotReceiveWithAnyArgs().CreateAsync(default, default); + await sutProvider.GetDependency().DidNotReceiveWithAnyArgs().ReplaceAsync(default); + await sutProvider.GetDependency().DidNotReceiveWithAnyArgs().LogCollectionEventAsync(default, default); + } - // execute - var collectionService = new CollectionService( - _eventService, - _organizationRepository, - _organizationUserRepository, - _collectionRepository, - _userRepository, - _mailService); + [Theory, CollectionAutoData] + public async Task SaveAsync_ExceedsOrganizationMaxCollections_ThrowsBadRequest(Collection collection, Collection collection1, Collection collection2, Organization organization, SutProvider sutProvider) + { + collection.Id = default; + sutProvider.GetDependency().GetByIdAsync(organization.Id).Returns(organization); + sutProvider.GetDependency().GetCountByOrganizationIdAsync(organization.Id) + .Returns(organization.MaxCollections.Value); - var testCollection = new Core.Models.Table.Collection { OrganizationId = testOrganizationId }; + var ex = await Assert.ThrowsAsync(() => sutProvider.Sut.SaveAsync(collection)); + Assert.Equal($@"You have reached the maximum number of collections ({organization.MaxCollections.Value}) for this organization.", ex.Message); + await sutProvider.GetDependency().DidNotReceiveWithAnyArgs().CreateAsync(default); + await sutProvider.GetDependency().DidNotReceiveWithAnyArgs().CreateAsync(default, default); + await sutProvider.GetDependency().DidNotReceiveWithAnyArgs().ReplaceAsync(default); + await sutProvider.GetDependency().DidNotReceiveWithAnyArgs().LogCollectionEventAsync(default, default); + } - // verify + [Theory, CollectionAutoData] + public async Task DeleteUserAsync_DeletesValidUserWhoBelongsToCollection(Collection collection, + Organization organization, OrganizationUser organizationUser, SutProvider sutProvider) + { + collection.OrganizationId = organization.Id; + organizationUser.OrganizationId = organization.Id; + sutProvider.GetDependency().GetByIdAsync(organization.Id).Returns(organization); + sutProvider.GetDependency().GetByIdAsync(organizationUser.Id) + .Returns(organizationUser); + + await sutProvider.Sut.DeleteUserAsync(collection, organizationUser.Id); + + await sutProvider.GetDependency().Received() + .DeleteUserAsync(collection.Id, organizationUser.Id); + await sutProvider.GetDependency().Received().LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Updated); + } + + [Theory, CollectionAutoData] + public async Task DeleteUserAsync_InvalidUser_ThrowsNotFound(Collection collection, Organization organization, + OrganizationUser organizationUser, SutProvider sutProvider) + { + collection.OrganizationId = organization.Id; + sutProvider.GetDependency().GetByIdAsync(organization.Id).Returns(organization); + sutProvider.GetDependency().GetByIdAsync(organizationUser.Id) + .Returns(organizationUser); + + // user not in organization + await Assert.ThrowsAsync(() => + sutProvider.Sut.DeleteUserAsync(collection, organizationUser.Id)); // invalid user - await Assert.ThrowsAsync(() => - collectionService.DeleteUserAsync(testCollection, Guid.NewGuid())); - // user from other organization - await Assert.ThrowsAsync(() => - collectionService.DeleteUserAsync(testCollection, testUserId)); + await Assert.ThrowsAsync(() => sutProvider.Sut.DeleteUserAsync(collection, Guid.NewGuid())); + await sutProvider.GetDependency().DidNotReceiveWithAnyArgs().DeleteUserAsync(default, default); + await sutProvider.GetDependency().DidNotReceiveWithAnyArgs() + .LogOrganizationUserEventAsync(default, default); } } }