mirror of
https://github.com/bitwarden/server.git
synced 2025-07-02 08:32:50 -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:
@ -1,6 +1,14 @@
|
||||
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.Services;
|
||||
using Bit.Core.Test.AutoFixture;
|
||||
using Bit.Core.Test.AutoFixture.Attributes;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
@ -8,34 +16,124 @@ namespace Bit.Core.Test.Services
|
||||
{
|
||||
public class GroupServiceTests
|
||||
{
|
||||
private readonly GroupService _sut;
|
||||
|
||||
private readonly IEventService _eventService;
|
||||
private readonly IOrganizationRepository _organizationRepository;
|
||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||
private readonly IGroupRepository _groupRepository;
|
||||
|
||||
public GroupServiceTests()
|
||||
[Theory, GroupOrganizationAutoData]
|
||||
public async Task SaveAsync_DefaultGroupId_CreatesGroupInRepository(Group group, Organization organization, SutProvider<GroupService> sutProvider)
|
||||
{
|
||||
_eventService = Substitute.For<IEventService>();
|
||||
_organizationRepository = Substitute.For<IOrganizationRepository>();
|
||||
_organizationUserRepository = Substitute.For<IOrganizationUserRepository>();
|
||||
_groupRepository = Substitute.For<IGroupRepository>();
|
||||
group.Id = default(Guid);
|
||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
||||
organization.UseGroups = true;
|
||||
var utcNow = DateTime.UtcNow;
|
||||
|
||||
_sut = new GroupService(
|
||||
_eventService,
|
||||
_organizationRepository,
|
||||
_organizationUserRepository,
|
||||
_groupRepository
|
||||
);
|
||||
await sutProvider.Sut.SaveAsync(group);
|
||||
|
||||
await sutProvider.GetDependency<IGroupRepository>().Received().CreateAsync(group);
|
||||
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));
|
||||
}
|
||||
|
||||
// Remove this test when we add actual tests. It only proves that
|
||||
// we've properly constructed the system under test.
|
||||
[Fact]
|
||||
public void ServiceExists()
|
||||
[Theory, GroupOrganizationAutoData]
|
||||
public async Task SaveAsync_DefaultGroupIdAndCollections_CreatesGroupInRepository(Group group, Organization organization, List<SelectionReadOnly> collections, SutProvider<GroupService> sutProvider)
|
||||
{
|
||||
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.Repositories;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Settings;
|
||||
using Microsoft.AspNetCore.DataProtection;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
@ -16,6 +15,7 @@ using Bit.Core.Enums;
|
||||
using Bit.Core.Test.AutoFixture.Attributes;
|
||||
using Bit.Core.Test.AutoFixture.OrganizationFixtures;
|
||||
using System.Text.Json;
|
||||
using Organization = Bit.Core.Models.Table.Organization;
|
||||
|
||||
namespace Bit.Core.Test.Services
|
||||
{
|
||||
@ -187,7 +187,7 @@ namespace Bit.Core.Test.Services
|
||||
() => sutProvider.Sut.UpgradePlanAsync(organization.Id, upgrade));
|
||||
Assert.Contains("can only upgrade", exception.Message);
|
||||
}
|
||||
|
||||
|
||||
[Theory]
|
||||
[FreeOrganizationUpgradeAutoData]
|
||||
public async Task UpgradePlan_Passes(Organization organization, OrganizationUpgrade upgrade,
|
||||
@ -209,12 +209,12 @@ namespace Bit.Core.Test.Services
|
||||
() => sutProvider.Sut.InviteUserAsync(organization.Id, invitor.UserId, null, invite));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[Theory]
|
||||
[OrganizationInviteAutoData(
|
||||
inviteeUserType: (int)OrganizationUserType.Owner,
|
||||
inviteeUserType: (int)OrganizationUserType.Owner,
|
||||
invitorUserType: (int)OrganizationUserType.Admin
|
||||
)]
|
||||
public async Task InviteUser_NonOwnerConfiguringOwner_Throws(Organization organization, OrganizationUserInvite invite,
|
||||
public async Task InviteUser_NonOwnerConfiguringOwner_Throws(Organization organization, OrganizationUserInvite invite,
|
||||
OrganizationUser invitor, SutProvider<OrganizationService> sutProvider)
|
||||
{
|
||||
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
|
||||
@ -228,12 +228,12 @@ namespace Bit.Core.Test.Services
|
||||
Assert.Contains("only an owner", exception.Message.ToLowerInvariant());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[Theory]
|
||||
[OrganizationInviteAutoData(
|
||||
inviteeUserType: (int)OrganizationUserType.Custom,
|
||||
inviteeUserType: (int)OrganizationUserType.Custom,
|
||||
invitorUserType: (int)OrganizationUserType.Admin
|
||||
)]
|
||||
public async Task InviteUser_NonAdminConfiguringAdmin_Throws(Organization organization, OrganizationUserInvite invite,
|
||||
public async Task InviteUser_NonAdminConfiguringAdmin_Throws(Organization organization, OrganizationUserInvite invite,
|
||||
OrganizationUser invitor, SutProvider<OrganizationService> sutProvider)
|
||||
{
|
||||
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
|
||||
@ -247,15 +247,15 @@ namespace Bit.Core.Test.Services
|
||||
Assert.Contains("only owners and admins", exception.Message.ToLowerInvariant());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[Theory]
|
||||
[OrganizationInviteAutoData(
|
||||
inviteeUserType: (int)OrganizationUserType.Manager,
|
||||
inviteeUserType: (int)OrganizationUserType.Manager,
|
||||
invitorUserType: (int)OrganizationUserType.Custom
|
||||
)]
|
||||
public async Task InviteUser_CustomUserWithoutManageUsersConfiguringUser_Throws(Organization organization, OrganizationUserInvite invite,
|
||||
public async Task InviteUser_CustomUserWithoutManageUsersConfiguringUser_Throws(Organization organization, OrganizationUserInvite invite,
|
||||
OrganizationUser invitor, SutProvider<OrganizationService> sutProvider)
|
||||
{
|
||||
invitor.Permissions = JsonSerializer.Serialize(new Permissions() { ManageUsers = false },
|
||||
invitor.Permissions = JsonSerializer.Serialize(new Permissions() { ManageUsers = false },
|
||||
new JsonSerializerOptions
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||
@ -272,15 +272,15 @@ namespace Bit.Core.Test.Services
|
||||
Assert.Contains("account does not have permission", exception.Message.ToLowerInvariant());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[Theory]
|
||||
[OrganizationInviteAutoData(
|
||||
inviteeUserType: (int)OrganizationUserType.Admin,
|
||||
inviteeUserType: (int)OrganizationUserType.Admin,
|
||||
invitorUserType: (int)OrganizationUserType.Custom
|
||||
)]
|
||||
public async Task InviteUser_CustomUserConfiguringAdmin_Throws(Organization organization, OrganizationUserInvite invite,
|
||||
public async Task InviteUser_CustomUserConfiguringAdmin_Throws(Organization organization, OrganizationUserInvite invite,
|
||||
OrganizationUser invitor, SutProvider<OrganizationService> sutProvider)
|
||||
{
|
||||
invitor.Permissions = JsonSerializer.Serialize(new Permissions() { ManageUsers = true },
|
||||
invitor.Permissions = JsonSerializer.Serialize(new Permissions() { ManageUsers = true },
|
||||
new JsonSerializerOptions
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||
@ -297,12 +297,12 @@ namespace Bit.Core.Test.Services
|
||||
Assert.Contains("can not manage admins", exception.Message.ToLowerInvariant());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[Theory]
|
||||
[OrganizationInviteAutoData(
|
||||
inviteeUserType: (int)OrganizationUserType.User,
|
||||
inviteeUserType: (int)OrganizationUserType.User,
|
||||
invitorUserType: (int)OrganizationUserType.Owner
|
||||
)]
|
||||
public async Task InviteUser_NoPermissionsObject_Passes(Organization organization, OrganizationUserInvite invite,
|
||||
public async Task InviteUser_NoPermissionsObject_Passes(Organization organization, OrganizationUserInvite invite,
|
||||
OrganizationUser invitor, SutProvider<OrganizationService> sutProvider)
|
||||
{
|
||||
invite.Permissions = null;
|
||||
@ -316,15 +316,15 @@ namespace Bit.Core.Test.Services
|
||||
await sutProvider.Sut.InviteUserAsync(organization.Id, invitor.UserId, null, invite);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[Theory]
|
||||
[OrganizationInviteAutoData(
|
||||
inviteeUserType: (int)OrganizationUserType.User,
|
||||
inviteeUserType: (int)OrganizationUserType.User,
|
||||
invitorUserType: (int)OrganizationUserType.Custom
|
||||
)]
|
||||
public async Task InviteUser_Passes(Organization organization, OrganizationUserInvite invite,
|
||||
public async Task InviteUser_Passes(Organization organization, OrganizationUserInvite invite,
|
||||
OrganizationUser invitor, SutProvider<OrganizationService> sutProvider)
|
||||
{
|
||||
invitor.Permissions = JsonSerializer.Serialize(new Permissions() { ManageUsers = true },
|
||||
invitor.Permissions = JsonSerializer.Serialize(new Permissions() { ManageUsers = true },
|
||||
new JsonSerializerOptions
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||
@ -366,7 +366,7 @@ namespace Bit.Core.Test.Services
|
||||
IEnumerable<SelectionReadOnly> collections, OrganizationUser savingUser, SutProvider<OrganizationService> sutProvider)
|
||||
{
|
||||
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
||||
|
||||
|
||||
newUserData.Id = oldUserData.Id;
|
||||
newUserData.UserId = oldUserData.UserId;
|
||||
newUserData.OrganizationId = savingUser.OrganizationId = oldUserData.OrganizationId;
|
||||
|
Reference in New Issue
Block a user