mirror of
https://github.com/bitwarden/server.git
synced 2025-04-06 21:48:12 -05:00
Write GroupService unit tests (#1267)
* Write GroupService tests * Rewrite with AutoFixture, improve tests * Resolve PR comments * Rename OrganizationCustomization Co-authored-by: Matt Gibson <mgibson@bitwarden.com>
This commit is contained in:
parent
5a7b00c037
commit
69c2673f1f
19
test/Core.Test/AutoFixture/GroupFixtures.cs
Normal file
19
test/Core.Test/AutoFixture/GroupFixtures.cs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
using Bit.Core.Test.AutoFixture.Attributes;
|
||||||
|
using Bit.Core.Test.AutoFixture.OrganizationFixtures;
|
||||||
|
|
||||||
|
namespace Bit.Core.Test.AutoFixture
|
||||||
|
{
|
||||||
|
internal class GroupOrganizationAutoDataAttribute : CustomAutoDataAttribute
|
||||||
|
{
|
||||||
|
public GroupOrganizationAutoDataAttribute() : base(
|
||||||
|
new SutProviderCustomization(), new Organization { UseGroups = true })
|
||||||
|
{ }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class GroupOrganizationNotUseGroupsAutoDataAttribute : CustomAutoDataAttribute
|
||||||
|
{
|
||||||
|
public GroupOrganizationNotUseGroupsAutoDataAttribute() : base(
|
||||||
|
new SutProviderCustomization(), new Organization { UseGroups = false })
|
||||||
|
{ }
|
||||||
|
}
|
||||||
|
}
|
@ -12,6 +12,22 @@ using Bit.Core.Utilities;
|
|||||||
|
|
||||||
namespace Bit.Core.Test.AutoFixture.OrganizationFixtures
|
namespace Bit.Core.Test.AutoFixture.OrganizationFixtures
|
||||||
{
|
{
|
||||||
|
public class Organization : ICustomization
|
||||||
|
{
|
||||||
|
public bool UseGroups { get; set; }
|
||||||
|
|
||||||
|
public void Customize(IFixture fixture)
|
||||||
|
{
|
||||||
|
var organizationId = Guid.NewGuid();
|
||||||
|
|
||||||
|
fixture.Customize<Models.Table.Organization>(composer => composer
|
||||||
|
.With(o => o.Id, organizationId)
|
||||||
|
.With(o => o.UseGroups, UseGroups));
|
||||||
|
|
||||||
|
fixture.Customize<Group>(composer => composer.With(g => g.OrganizationId, organizationId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal class PaidOrganization : ICustomization
|
internal class PaidOrganization : ICustomization
|
||||||
{
|
{
|
||||||
public PlanType CheckedPlanType { get; set; }
|
public PlanType CheckedPlanType { get; set; }
|
||||||
@ -21,7 +37,7 @@ namespace Bit.Core.Test.AutoFixture.OrganizationFixtures
|
|||||||
var lowestActivePaidPlan = validUpgradePlans.First();
|
var lowestActivePaidPlan = validUpgradePlans.First();
|
||||||
CheckedPlanType = CheckedPlanType.Equals(Enums.PlanType.Free) ? lowestActivePaidPlan : CheckedPlanType;
|
CheckedPlanType = CheckedPlanType.Equals(Enums.PlanType.Free) ? lowestActivePaidPlan : CheckedPlanType;
|
||||||
validUpgradePlans.Remove(lowestActivePaidPlan);
|
validUpgradePlans.Remove(lowestActivePaidPlan);
|
||||||
fixture.Customize<Organization>(composer => composer
|
fixture.Customize<Models.Table.Organization>(composer => composer
|
||||||
.With(o => o.PlanType, CheckedPlanType));
|
.With(o => o.PlanType, CheckedPlanType));
|
||||||
fixture.Customize<OrganizationUpgrade>(composer => composer
|
fixture.Customize<OrganizationUpgrade>(composer => composer
|
||||||
.With(ou => ou.Plan, validUpgradePlans.First()));
|
.With(ou => ou.Plan, validUpgradePlans.First()));
|
||||||
@ -32,7 +48,7 @@ namespace Bit.Core.Test.AutoFixture.OrganizationFixtures
|
|||||||
{
|
{
|
||||||
public void Customize(IFixture fixture)
|
public void Customize(IFixture fixture)
|
||||||
{
|
{
|
||||||
fixture.Customize<Organization>(composer => composer
|
fixture.Customize<Models.Table.Organization>(composer => composer
|
||||||
.With(o => o.PlanType, PlanType.Free));
|
.With(o => o.PlanType, PlanType.Free));
|
||||||
|
|
||||||
var plansToIgnore = new List<PlanType> { PlanType.Free, PlanType.Custom };
|
var plansToIgnore = new List<PlanType> { PlanType.Free, PlanType.Custom };
|
||||||
@ -57,7 +73,7 @@ namespace Bit.Core.Test.AutoFixture.OrganizationFixtures
|
|||||||
{
|
{
|
||||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||||
});
|
});
|
||||||
fixture.Customize<Organization>(composer => composer
|
fixture.Customize<Models.Table.Organization>(composer => composer
|
||||||
.With(o => o.Id, organizationId)
|
.With(o => o.Id, organizationId)
|
||||||
.With(o => o.Seats, (short)100));
|
.With(o => o.Seats, (short)100));
|
||||||
fixture.Customize<OrganizationUser>(composer => composer
|
fixture.Customize<OrganizationUser>(composer => composer
|
||||||
|
@ -1,6 +1,14 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
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.Repositories;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
|
using Bit.Core.Test.AutoFixture;
|
||||||
|
using Bit.Core.Test.AutoFixture.Attributes;
|
||||||
using NSubstitute;
|
using NSubstitute;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
@ -8,34 +16,124 @@ namespace Bit.Core.Test.Services
|
|||||||
{
|
{
|
||||||
public class GroupServiceTests
|
public class GroupServiceTests
|
||||||
{
|
{
|
||||||
private readonly GroupService _sut;
|
[Theory, GroupOrganizationAutoData]
|
||||||
|
public async Task SaveAsync_DefaultGroupId_CreatesGroupInRepository(Group group, Organization organization, SutProvider<GroupService> sutProvider)
|
||||||
private readonly IEventService _eventService;
|
|
||||||
private readonly IOrganizationRepository _organizationRepository;
|
|
||||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
|
||||||
private readonly IGroupRepository _groupRepository;
|
|
||||||
|
|
||||||
public GroupServiceTests()
|
|
||||||
{
|
{
|
||||||
_eventService = Substitute.For<IEventService>();
|
group.Id = default(Guid);
|
||||||
_organizationRepository = Substitute.For<IOrganizationRepository>();
|
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
||||||
_organizationUserRepository = Substitute.For<IOrganizationUserRepository>();
|
organization.UseGroups = true;
|
||||||
_groupRepository = Substitute.For<IGroupRepository>();
|
var utcNow = DateTime.UtcNow;
|
||||||
|
|
||||||
_sut = new GroupService(
|
await sutProvider.Sut.SaveAsync(group);
|
||||||
_eventService,
|
|
||||||
_organizationRepository,
|
await sutProvider.GetDependency<IGroupRepository>().Received().CreateAsync(group);
|
||||||
_organizationUserRepository,
|
await sutProvider.GetDependency<IEventService>().Received()
|
||||||
_groupRepository
|
.LogGroupEventAsync(group, EventType.Group_Created);
|
||||||
);
|
Assert.True(group.CreationDate - utcNow < TimeSpan.FromSeconds(1));
|
||||||
|
Assert.True(group.RevisionDate - utcNow < TimeSpan.FromSeconds(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove this test when we add actual tests. It only proves that
|
[Theory, GroupOrganizationAutoData]
|
||||||
// we've properly constructed the system under test.
|
public async Task SaveAsync_DefaultGroupIdAndCollections_CreatesGroupInRepository(Group group, Organization organization, List<SelectionReadOnly> collections, SutProvider<GroupService> sutProvider)
|
||||||
[Fact]
|
|
||||||
public void ServiceExists()
|
|
||||||
{
|
{
|
||||||
Assert.NotNull(_sut);
|
group.Id = default(Guid);
|
||||||
|
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
||||||
|
organization.UseGroups = true;
|
||||||
|
var utcNow = DateTime.UtcNow;
|
||||||
|
|
||||||
|
await sutProvider.Sut.SaveAsync(group, collections);
|
||||||
|
|
||||||
|
await sutProvider.GetDependency<IGroupRepository>().Received().CreateAsync(group, collections);
|
||||||
|
await sutProvider.GetDependency<IEventService>().Received()
|
||||||
|
.LogGroupEventAsync(group, EventType.Group_Created);
|
||||||
|
Assert.True(group.CreationDate - utcNow < TimeSpan.FromSeconds(1));
|
||||||
|
Assert.True(group.RevisionDate - utcNow < TimeSpan.FromSeconds(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, GroupOrganizationAutoData]
|
||||||
|
public async Task SaveAsync_NonDefaultGroupId_ReplaceGroupInRepository(Group group, Organization organization, List<SelectionReadOnly> collections, SutProvider<GroupService> sutProvider)
|
||||||
|
{
|
||||||
|
organization.UseGroups = true;
|
||||||
|
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
||||||
|
|
||||||
|
await sutProvider.Sut.SaveAsync(group, collections);
|
||||||
|
|
||||||
|
await sutProvider.GetDependency<IGroupRepository>().Received().ReplaceAsync(group, collections);
|
||||||
|
await sutProvider.GetDependency<IEventService>().Received()
|
||||||
|
.LogGroupEventAsync(group, EventType.Group_Updated);
|
||||||
|
Assert.True(group.RevisionDate - DateTime.UtcNow < TimeSpan.FromSeconds(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
|
||||||
|
public async Task SaveAsync_NonExistingOrganizationId_ThrowsBadRequest(Group group, Organization organization, SutProvider<GroupService> sutProvider)
|
||||||
|
{
|
||||||
|
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
||||||
|
() => sutProvider.Sut.SaveAsync(group));
|
||||||
|
Assert.Contains("Organization not found", exception.Message);
|
||||||
|
await sutProvider.GetDependency<IGroupRepository>().DidNotReceiveWithAnyArgs().CreateAsync(default);
|
||||||
|
await sutProvider.GetDependency<IGroupRepository>().DidNotReceiveWithAnyArgs().ReplaceAsync(default);
|
||||||
|
await sutProvider.GetDependency<IEventService>().DidNotReceiveWithAnyArgs().LogGroupEventAsync(default, default, default);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, GroupOrganizationNotUseGroupsAutoData]
|
||||||
|
public async Task SaveAsync_OrganizationDoesNotUseGroups_ThrowsBadRequest(Group group, Organization organization, SutProvider<GroupService> sutProvider)
|
||||||
|
{
|
||||||
|
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
||||||
|
|
||||||
|
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
||||||
|
() => sutProvider.Sut.SaveAsync(group));
|
||||||
|
|
||||||
|
Assert.Contains("This organization cannot use groups", exception.Message);
|
||||||
|
await sutProvider.GetDependency<IGroupRepository>().DidNotReceiveWithAnyArgs().CreateAsync(default);
|
||||||
|
await sutProvider.GetDependency<IGroupRepository>().DidNotReceiveWithAnyArgs().ReplaceAsync(default);
|
||||||
|
await sutProvider.GetDependency<IEventService>().DidNotReceiveWithAnyArgs().LogGroupEventAsync(default, default, default);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
|
||||||
|
public async Task DeleteAsync_ValidData_DeletesGroup(Group group, SutProvider<GroupService> sutProvider)
|
||||||
|
{
|
||||||
|
await sutProvider.Sut.DeleteAsync(group);
|
||||||
|
|
||||||
|
await sutProvider.GetDependency<IGroupRepository>().Received().DeleteAsync(group);
|
||||||
|
await sutProvider.GetDependency<IEventService>().Received()
|
||||||
|
.LogGroupEventAsync(group, EventType.Group_Deleted);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
|
||||||
|
public async Task DeleteUserAsync_ValidData_DeletesUserInGroupRepository(Group group, Organization organization, OrganizationUser organizationUser, SutProvider<GroupService> sutProvider)
|
||||||
|
{
|
||||||
|
group.OrganizationId = organization.Id;
|
||||||
|
organization.UseGroups = true;
|
||||||
|
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
||||||
|
organizationUser.OrganizationId = organization.Id;
|
||||||
|
sutProvider.GetDependency<IOrganizationUserRepository>().GetByIdAsync(organizationUser.Id)
|
||||||
|
.Returns(organizationUser);
|
||||||
|
|
||||||
|
await sutProvider.Sut.DeleteUserAsync(group, organizationUser.Id);
|
||||||
|
|
||||||
|
await sutProvider.GetDependency<IGroupRepository>().Received().DeleteUserAsync(group.Id, organizationUser.Id);
|
||||||
|
await sutProvider.GetDependency<IEventService>().Received()
|
||||||
|
.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_UpdatedGroups);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
|
||||||
|
public async Task DeleteUserAsync_InvalidUser_ThrowsNotFound(Group group, Organization organization, OrganizationUser organizationUser, SutProvider<GroupService> sutProvider)
|
||||||
|
{
|
||||||
|
group.OrganizationId = organization.Id;
|
||||||
|
organization.UseGroups = true;
|
||||||
|
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
||||||
|
// organizationUser.OrganizationId = organization.Id;
|
||||||
|
sutProvider.GetDependency<IOrganizationUserRepository>().GetByIdAsync(organizationUser.Id)
|
||||||
|
.Returns(organizationUser);
|
||||||
|
|
||||||
|
// user not in organization
|
||||||
|
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.DeleteUserAsync(group, organizationUser.Id));
|
||||||
|
// invalid user
|
||||||
|
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.DeleteUserAsync(group, Guid.NewGuid()));
|
||||||
|
await sutProvider.GetDependency<IGroupRepository>().DidNotReceiveWithAnyArgs()
|
||||||
|
.DeleteUserAsync(default, default);
|
||||||
|
await sutProvider.GetDependency<IEventService>().DidNotReceiveWithAnyArgs()
|
||||||
|
.LogOrganizationUserEventAsync(default, default);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@ using Bit.Core.Models.Table;
|
|||||||
using Bit.Core.Models.Business;
|
using Bit.Core.Models.Business;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
using Bit.Core.Settings;
|
|
||||||
using Microsoft.AspNetCore.DataProtection;
|
using Microsoft.AspNetCore.DataProtection;
|
||||||
using NSubstitute;
|
using NSubstitute;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
@ -16,6 +15,7 @@ using Bit.Core.Enums;
|
|||||||
using Bit.Core.Test.AutoFixture.Attributes;
|
using Bit.Core.Test.AutoFixture.Attributes;
|
||||||
using Bit.Core.Test.AutoFixture.OrganizationFixtures;
|
using Bit.Core.Test.AutoFixture.OrganizationFixtures;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
using Organization = Bit.Core.Models.Table.Organization;
|
||||||
|
|
||||||
namespace Bit.Core.Test.Services
|
namespace Bit.Core.Test.Services
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user