1
0
mirror of https://github.com/bitwarden/server.git synced 2025-07-02 08:32:50 -05:00

Run formatting (#2230)

This commit is contained in:
Justin Baur
2022-08-29 16:06:55 -04:00
committed by GitHub
parent 9b7aef0763
commit 7f5f010e1e
1205 changed files with 73813 additions and 75022 deletions

View File

@ -1,14 +1,13 @@
namespace Bit.Core.Test.AutoFixture.Attributes
namespace Bit.Core.Test.AutoFixture.Attributes;
public sealed class CiSkippedTheory : Xunit.TheoryAttribute
{
public sealed class CiSkippedTheory : Xunit.TheoryAttribute
private static bool IsGithubActions() => Environment.GetEnvironmentVariable("CI") != null;
public CiSkippedTheory()
{
private static bool IsGithubActions() => Environment.GetEnvironmentVariable("CI") != null;
public CiSkippedTheory()
if (IsGithubActions())
{
if (IsGithubActions())
{
Skip = "Ignore during CI builds";
}
Skip = "Ignore during CI builds";
}
}
}

View File

@ -2,32 +2,31 @@
using AutoFixture.Dsl;
using Bit.Core.Models.Data;
namespace Bit.Core.Test.AutoFixture.CipherAttachmentMetaData
namespace Bit.Core.Test.AutoFixture.CipherAttachmentMetaData;
public class MetaData : ICustomization
{
public class MetaData : ICustomization
protected virtual IPostprocessComposer<CipherAttachment.MetaData> ComposerAction(IFixture fixture,
ICustomizationComposer<CipherAttachment.MetaData> composer)
{
protected virtual IPostprocessComposer<CipherAttachment.MetaData> ComposerAction(IFixture fixture,
ICustomizationComposer<CipherAttachment.MetaData> composer)
{
return composer.With(d => d.Size, fixture.Create<long>());
}
public void Customize(IFixture fixture)
{
fixture.Customize<CipherAttachment.MetaData>(composer => ComposerAction(fixture, composer));
}
return composer.With(d => d.Size, fixture.Create<long>());
}
public class MetaDataWithoutContainer : MetaData
public void Customize(IFixture fixture)
{
protected override IPostprocessComposer<CipherAttachment.MetaData> ComposerAction(IFixture fixture,
ICustomizationComposer<CipherAttachment.MetaData> composer) =>
base.ComposerAction(fixture, composer).With(d => d.ContainerName, (string)null);
}
public class MetaDataWithoutKey : MetaDataWithoutContainer
{
protected override IPostprocessComposer<CipherAttachment.MetaData> ComposerAction(IFixture fixture,
ICustomizationComposer<CipherAttachment.MetaData> composer) =>
base.ComposerAction(fixture, composer).Without(d => d.Key);
fixture.Customize<CipherAttachment.MetaData>(composer => ComposerAction(fixture, composer));
}
}
public class MetaDataWithoutContainer : MetaData
{
protected override IPostprocessComposer<CipherAttachment.MetaData> ComposerAction(IFixture fixture,
ICustomizationComposer<CipherAttachment.MetaData> composer) =>
base.ComposerAction(fixture, composer).With(d => d.ContainerName, (string)null);
}
public class MetaDataWithoutKey : MetaDataWithoutContainer
{
protected override IPostprocessComposer<CipherAttachment.MetaData> ComposerAction(IFixture fixture,
ICustomizationComposer<CipherAttachment.MetaData> composer) =>
base.ComposerAction(fixture, composer).Without(d => d.Key);
}

View File

@ -3,67 +3,66 @@ using Bit.Core.Entities;
using Bit.Test.Common.AutoFixture.Attributes;
using Core.Models.Data;
namespace Bit.Core.Test.AutoFixture.CipherFixtures
namespace Bit.Core.Test.AutoFixture.CipherFixtures;
internal class OrganizationCipher : ICustomization
{
internal class OrganizationCipher : ICustomization
public Guid? OrganizationId { get; set; }
public void Customize(IFixture fixture)
{
public Guid? OrganizationId { get; set; }
public void Customize(IFixture fixture)
{
fixture.Customize<Cipher>(composer => composer
.With(c => c.OrganizationId, OrganizationId ?? Guid.NewGuid())
.Without(c => c.UserId));
fixture.Customize<CipherDetails>(composer => composer
.With(c => c.OrganizationId, Guid.NewGuid())
.Without(c => c.UserId));
}
}
internal class UserCipher : ICustomization
{
public Guid? UserId { get; set; }
public void Customize(IFixture fixture)
{
fixture.Customize<Cipher>(composer => composer
.With(c => c.UserId, UserId ?? Guid.NewGuid())
.Without(c => c.OrganizationId));
fixture.Customize<CipherDetails>(composer => composer
.With(c => c.UserId, Guid.NewGuid())
.Without(c => c.OrganizationId));
}
}
internal class UserCipherAutoDataAttribute : CustomAutoDataAttribute
{
public UserCipherAutoDataAttribute(string userId = null) : base(new SutProviderCustomization(),
new UserCipher { UserId = userId == null ? (Guid?)null : new Guid(userId) })
{ }
}
internal class InlineUserCipherAutoDataAttribute : InlineCustomAutoDataAttribute
{
public InlineUserCipherAutoDataAttribute(params object[] values) : base(new[] { typeof(SutProviderCustomization),
typeof(UserCipher) }, values)
{ }
}
internal class InlineKnownUserCipherAutoDataAttribute : InlineCustomAutoDataAttribute
{
public InlineKnownUserCipherAutoDataAttribute(string userId, params object[] values) : base(new ICustomization[]
{ new SutProviderCustomization(), new UserCipher { UserId = new Guid(userId) } }, values)
{ }
}
internal class OrganizationCipherAutoDataAttribute : CustomAutoDataAttribute
{
public OrganizationCipherAutoDataAttribute(string organizationId = null) : base(new SutProviderCustomization(),
new OrganizationCipher { OrganizationId = organizationId == null ? (Guid?)null : new Guid(organizationId) })
{ }
}
internal class InlineOrganizationCipherAutoDataAttribute : InlineCustomAutoDataAttribute
{
public InlineOrganizationCipherAutoDataAttribute(params object[] values) : base(new[] { typeof(SutProviderCustomization),
typeof(OrganizationCipher) }, values)
{ }
fixture.Customize<Cipher>(composer => composer
.With(c => c.OrganizationId, OrganizationId ?? Guid.NewGuid())
.Without(c => c.UserId));
fixture.Customize<CipherDetails>(composer => composer
.With(c => c.OrganizationId, Guid.NewGuid())
.Without(c => c.UserId));
}
}
internal class UserCipher : ICustomization
{
public Guid? UserId { get; set; }
public void Customize(IFixture fixture)
{
fixture.Customize<Cipher>(composer => composer
.With(c => c.UserId, UserId ?? Guid.NewGuid())
.Without(c => c.OrganizationId));
fixture.Customize<CipherDetails>(composer => composer
.With(c => c.UserId, Guid.NewGuid())
.Without(c => c.OrganizationId));
}
}
internal class UserCipherAutoDataAttribute : CustomAutoDataAttribute
{
public UserCipherAutoDataAttribute(string userId = null) : base(new SutProviderCustomization(),
new UserCipher { UserId = userId == null ? (Guid?)null : new Guid(userId) })
{ }
}
internal class InlineUserCipherAutoDataAttribute : InlineCustomAutoDataAttribute
{
public InlineUserCipherAutoDataAttribute(params object[] values) : base(new[] { typeof(SutProviderCustomization),
typeof(UserCipher) }, values)
{ }
}
internal class InlineKnownUserCipherAutoDataAttribute : InlineCustomAutoDataAttribute
{
public InlineKnownUserCipherAutoDataAttribute(string userId, params object[] values) : base(new ICustomization[]
{ new SutProviderCustomization(), new UserCipher { UserId = new Guid(userId) } }, values)
{ }
}
internal class OrganizationCipherAutoDataAttribute : CustomAutoDataAttribute
{
public OrganizationCipherAutoDataAttribute(string organizationId = null) : base(new SutProviderCustomization(),
new OrganizationCipher { OrganizationId = organizationId == null ? (Guid?)null : new Guid(organizationId) })
{ }
}
internal class InlineOrganizationCipherAutoDataAttribute : InlineCustomAutoDataAttribute
{
public InlineOrganizationCipherAutoDataAttribute(params object[] values) : base(new[] { typeof(SutProviderCustomization),
typeof(OrganizationCipher) }, values)
{ }
}

View File

@ -1,11 +1,10 @@
using Bit.Core.Test.AutoFixture.OrganizationFixtures;
using Bit.Test.Common.AutoFixture.Attributes;
namespace Bit.Core.Test.AutoFixture.CollectionFixtures
namespace Bit.Core.Test.AutoFixture.CollectionFixtures;
internal class CollectionAutoDataAttribute : CustomAutoDataAttribute
{
internal class CollectionAutoDataAttribute : CustomAutoDataAttribute
{
public CollectionAutoDataAttribute() : base(new SutProviderCustomization(), new OrganizationCustomization())
{ }
}
public CollectionAutoDataAttribute() : base(new SutProviderCustomization(), new OrganizationCustomization())
{ }
}

View File

@ -3,36 +3,35 @@ using AutoFixture.Kernel;
using Bit.Core.Context;
using Bit.Test.Common.AutoFixture;
namespace Bit.Core.Test.AutoFixture.CurrentContextFixtures
namespace Bit.Core.Test.AutoFixture.CurrentContextFixtures;
internal class CurrentContext : ICustomization
{
internal class CurrentContext : ICustomization
public void Customize(IFixture fixture)
{
public void Customize(IFixture fixture)
{
fixture.Customizations.Add(new CurrentContextBuilder());
}
}
internal class CurrentContextBuilder : ISpecimenBuilder
{
public object Create(object request, ISpecimenContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (!(request is Type typeRequest))
{
return new NoSpecimen();
}
if (typeof(ICurrentContext) != typeRequest)
{
return new NoSpecimen();
}
var obj = new Fixture().WithAutoNSubstitutions().Create<ICurrentContext>();
obj.Organizations = context.Create<List<CurrentContentOrganization>>();
return obj;
}
fixture.Customizations.Add(new CurrentContextBuilder());
}
}
internal class CurrentContextBuilder : ISpecimenBuilder
{
public object Create(object request, ISpecimenContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (!(request is Type typeRequest))
{
return new NoSpecimen();
}
if (typeof(ICurrentContext) != typeRequest)
{
return new NoSpecimen();
}
var obj = new Fixture().WithAutoNSubstitutions().Create<ICurrentContext>();
obj.Organizations = context.Create<List<CurrentContentOrganization>>();
return obj;
}
}

View File

@ -4,29 +4,28 @@ using AutoFixture.Kernel;
using AutoFixture.Xunit2;
using Bit.Core.Test.Helpers.Factories;
namespace Bit.Test.Common.AutoFixture
namespace Bit.Test.Common.AutoFixture;
public class GlobalSettingsBuilder : ISpecimenBuilder
{
public class GlobalSettingsBuilder : ISpecimenBuilder
public object Create(object request, ISpecimenContext context)
{
public object Create(object request, ISpecimenContext context)
if (context == null)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
var pi = request as ParameterInfo;
var fixture = new Fixture();
if (pi == null || pi.ParameterType != typeof(Bit.Core.Settings.GlobalSettings))
return new NoSpecimen();
return GlobalSettingsFactory.GlobalSettings;
throw new ArgumentNullException(nameof(context));
}
}
public class GlobalSettingsCustomizeAttribute : CustomizeAttribute
{
public override ICustomization GetCustomization(ParameterInfo parameter) => new GlobalSettings();
var pi = request as ParameterInfo;
var fixture = new Fixture();
if (pi == null || pi.ParameterType != typeof(Bit.Core.Settings.GlobalSettings))
return new NoSpecimen();
return GlobalSettingsFactory.GlobalSettings;
}
}
public class GlobalSettingsCustomizeAttribute : CustomizeAttribute
{
public override ICustomization GetCustomization(ParameterInfo parameter) => new GlobalSettings();
}

View File

@ -1,19 +1,18 @@
using Bit.Core.Test.AutoFixture.OrganizationFixtures;
using Bit.Test.Common.AutoFixture.Attributes;
namespace Bit.Core.Test.AutoFixture.GroupFixtures
{
internal class GroupOrganizationAutoDataAttribute : CustomAutoDataAttribute
{
public GroupOrganizationAutoDataAttribute() : base(
new SutProviderCustomization(), new OrganizationCustomization { UseGroups = true })
{ }
}
namespace Bit.Core.Test.AutoFixture.GroupFixtures;
internal class GroupOrganizationNotUseGroupsAutoDataAttribute : CustomAutoDataAttribute
{
public GroupOrganizationNotUseGroupsAutoDataAttribute() : base(
new SutProviderCustomization(), new OrganizationCustomization { UseGroups = false })
{ }
}
internal class GroupOrganizationAutoDataAttribute : CustomAutoDataAttribute
{
public GroupOrganizationAutoDataAttribute() : base(
new SutProviderCustomization(), new OrganizationCustomization { UseGroups = true })
{ }
}
internal class GroupOrganizationNotUseGroupsAutoDataAttribute : CustomAutoDataAttribute
{
public GroupOrganizationNotUseGroupsAutoDataAttribute() : base(
new SutProviderCustomization(), new OrganizationCustomization { UseGroups = false })
{ }
}

View File

@ -10,177 +10,176 @@ using Bit.Core.Utilities;
using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
namespace Bit.Core.Test.AutoFixture.OrganizationFixtures
namespace Bit.Core.Test.AutoFixture.OrganizationFixtures;
public class OrganizationCustomization : ICustomization
{
public class OrganizationCustomization : ICustomization
public bool UseGroups { get; set; }
public void Customize(IFixture fixture)
{
public bool UseGroups { get; set; }
var organizationId = Guid.NewGuid();
var maxConnections = (short)new Random().Next(10, short.MaxValue);
public void Customize(IFixture fixture)
{
var organizationId = Guid.NewGuid();
var maxConnections = (short)new Random().Next(10, short.MaxValue);
fixture.Customize<Organization>(composer => composer
.With(o => o.Id, organizationId)
.With(o => o.MaxCollections, maxConnections)
.With(o => o.UseGroups, UseGroups));
fixture.Customize<Organization>(composer => composer
.With(o => o.Id, organizationId)
.With(o => o.MaxCollections, maxConnections)
.With(o => o.UseGroups, UseGroups));
fixture.Customize<Collection>(composer =>
composer
.With(c => c.OrganizationId, organizationId)
.Without(o => o.CreationDate)
.Without(o => o.RevisionDate));
fixture.Customize<Collection>(composer =>
composer
.With(c => c.OrganizationId, organizationId)
.Without(o => o.CreationDate)
.Without(o => o.RevisionDate));
fixture.Customize<Group>(composer => composer.With(g => g.OrganizationId, organizationId));
}
}
internal class OrganizationBuilder : 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(Organization))
{
return new NoSpecimen();
}
var fixture = new Fixture();
var providers = fixture.Create<Dictionary<TwoFactorProviderType, TwoFactorProvider>>();
var organization = new Fixture().WithAutoNSubstitutions().Create<Organization>();
organization.SetTwoFactorProviders(providers);
return organization;
}
}
internal class PaidOrganization : ICustomization
{
public PlanType CheckedPlanType { get; set; }
public void Customize(IFixture fixture)
{
var validUpgradePlans = StaticStore.Plans.Where(p => p.Type != PlanType.Free && !p.Disabled).Select(p => p.Type).ToList();
var lowestActivePaidPlan = validUpgradePlans.First();
CheckedPlanType = CheckedPlanType.Equals(PlanType.Free) ? lowestActivePaidPlan : CheckedPlanType;
validUpgradePlans.Remove(lowestActivePaidPlan);
fixture.Customize<Organization>(composer => composer
.With(o => o.PlanType, CheckedPlanType));
fixture.Customize<OrganizationUpgrade>(composer => composer
.With(ou => ou.Plan, validUpgradePlans.First()));
}
}
internal class FreeOrganization : ICustomization
{
public void Customize(IFixture fixture)
{
fixture.Customize<Organization>(composer => composer
.With(o => o.PlanType, PlanType.Free));
}
}
internal class FreeOrganizationUpgrade : ICustomization
{
public void Customize(IFixture fixture)
{
fixture.Customize<Organization>(composer => composer
.With(o => o.PlanType, PlanType.Free));
var plansToIgnore = new List<PlanType> { PlanType.Free, PlanType.Custom };
var selectedPlan = StaticStore.Plans.Last(p => !plansToIgnore.Contains(p.Type) && !p.Disabled);
fixture.Customize<OrganizationUpgrade>(composer => composer
.With(ou => ou.Plan, selectedPlan.Type)
.With(ou => ou.PremiumAccessAddon, selectedPlan.HasPremiumAccessOption));
fixture.Customize<Organization>(composer => composer
.Without(o => o.GatewaySubscriptionId));
}
}
internal class OrganizationInvite : ICustomization
{
public OrganizationUserType InviteeUserType { get; set; }
public OrganizationUserType InvitorUserType { get; set; }
public string PermissionsBlob { get; set; }
public void Customize(IFixture fixture)
{
var organizationId = new Guid();
PermissionsBlob = PermissionsBlob ?? JsonSerializer.Serialize(new Permissions(), new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
});
fixture.Customize<Organization>(composer => composer
.With(o => o.Id, organizationId)
.With(o => o.Seats, (short)100));
fixture.Customize<OrganizationUser>(composer => composer
.With(ou => ou.OrganizationId, organizationId)
.With(ou => ou.Type, InvitorUserType)
.With(ou => ou.Permissions, PermissionsBlob));
fixture.Customize<OrganizationUserInvite>(composer => composer
.With(oi => oi.Type, InviteeUserType));
}
}
internal class PaidOrganizationAutoDataAttribute : CustomAutoDataAttribute
{
public PaidOrganizationAutoDataAttribute(PlanType planType) : base(new SutProviderCustomization(),
new PaidOrganization { CheckedPlanType = planType })
{ }
public PaidOrganizationAutoDataAttribute(int planType = 0) : this((PlanType)planType) { }
}
internal class InlinePaidOrganizationAutoDataAttribute : InlineCustomAutoDataAttribute
{
public InlinePaidOrganizationAutoDataAttribute(PlanType planType, object[] values) : base(
new ICustomization[] { new SutProviderCustomization(), new PaidOrganization { CheckedPlanType = planType } }, values)
{ }
public InlinePaidOrganizationAutoDataAttribute(params object[] values) : base(new[] { typeof(SutProviderCustomization),
typeof(PaidOrganization) }, values)
{ }
}
internal class InlineFreeOrganizationAutoDataAttribute : InlineCustomAutoDataAttribute
{
public InlineFreeOrganizationAutoDataAttribute(params object[] values) : base(new[] { typeof(SutProviderCustomization),
typeof(FreeOrganization) }, values)
{ }
}
internal class FreeOrganizationUpgradeAutoDataAttribute : CustomAutoDataAttribute
{
public FreeOrganizationUpgradeAutoDataAttribute() : base(new SutProviderCustomization(), new FreeOrganizationUpgrade())
{ }
}
internal class InlineFreeOrganizationUpgradeAutoDataAttribute : InlineCustomAutoDataAttribute
{
public InlineFreeOrganizationUpgradeAutoDataAttribute(params object[] values) : base(new[] { typeof(SutProviderCustomization),
typeof(FreeOrganizationUpgrade) }, values)
{ }
}
internal class OrganizationInviteAutoDataAttribute : CustomAutoDataAttribute
{
public OrganizationInviteAutoDataAttribute(int inviteeUserType = 0, int invitorUserType = 0, string permissionsBlob = null) : base(new SutProviderCustomization(),
new OrganizationInvite
{
InviteeUserType = (OrganizationUserType)inviteeUserType,
InvitorUserType = (OrganizationUserType)invitorUserType,
PermissionsBlob = permissionsBlob,
})
{ }
}
internal class InlineOrganizationInviteAutoDataAttribute : InlineCustomAutoDataAttribute
{
public InlineOrganizationInviteAutoDataAttribute(params object[] values) : base(new[] { typeof(SutProviderCustomization),
typeof(OrganizationInvite) }, values)
{ }
fixture.Customize<Group>(composer => composer.With(g => g.OrganizationId, organizationId));
}
}
internal class OrganizationBuilder : 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(Organization))
{
return new NoSpecimen();
}
var fixture = new Fixture();
var providers = fixture.Create<Dictionary<TwoFactorProviderType, TwoFactorProvider>>();
var organization = new Fixture().WithAutoNSubstitutions().Create<Organization>();
organization.SetTwoFactorProviders(providers);
return organization;
}
}
internal class PaidOrganization : ICustomization
{
public PlanType CheckedPlanType { get; set; }
public void Customize(IFixture fixture)
{
var validUpgradePlans = StaticStore.Plans.Where(p => p.Type != PlanType.Free && !p.Disabled).Select(p => p.Type).ToList();
var lowestActivePaidPlan = validUpgradePlans.First();
CheckedPlanType = CheckedPlanType.Equals(PlanType.Free) ? lowestActivePaidPlan : CheckedPlanType;
validUpgradePlans.Remove(lowestActivePaidPlan);
fixture.Customize<Organization>(composer => composer
.With(o => o.PlanType, CheckedPlanType));
fixture.Customize<OrganizationUpgrade>(composer => composer
.With(ou => ou.Plan, validUpgradePlans.First()));
}
}
internal class FreeOrganization : ICustomization
{
public void Customize(IFixture fixture)
{
fixture.Customize<Organization>(composer => composer
.With(o => o.PlanType, PlanType.Free));
}
}
internal class FreeOrganizationUpgrade : ICustomization
{
public void Customize(IFixture fixture)
{
fixture.Customize<Organization>(composer => composer
.With(o => o.PlanType, PlanType.Free));
var plansToIgnore = new List<PlanType> { PlanType.Free, PlanType.Custom };
var selectedPlan = StaticStore.Plans.Last(p => !plansToIgnore.Contains(p.Type) && !p.Disabled);
fixture.Customize<OrganizationUpgrade>(composer => composer
.With(ou => ou.Plan, selectedPlan.Type)
.With(ou => ou.PremiumAccessAddon, selectedPlan.HasPremiumAccessOption));
fixture.Customize<Organization>(composer => composer
.Without(o => o.GatewaySubscriptionId));
}
}
internal class OrganizationInvite : ICustomization
{
public OrganizationUserType InviteeUserType { get; set; }
public OrganizationUserType InvitorUserType { get; set; }
public string PermissionsBlob { get; set; }
public void Customize(IFixture fixture)
{
var organizationId = new Guid();
PermissionsBlob = PermissionsBlob ?? JsonSerializer.Serialize(new Permissions(), new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
});
fixture.Customize<Organization>(composer => composer
.With(o => o.Id, organizationId)
.With(o => o.Seats, (short)100));
fixture.Customize<OrganizationUser>(composer => composer
.With(ou => ou.OrganizationId, organizationId)
.With(ou => ou.Type, InvitorUserType)
.With(ou => ou.Permissions, PermissionsBlob));
fixture.Customize<OrganizationUserInvite>(composer => composer
.With(oi => oi.Type, InviteeUserType));
}
}
internal class PaidOrganizationAutoDataAttribute : CustomAutoDataAttribute
{
public PaidOrganizationAutoDataAttribute(PlanType planType) : base(new SutProviderCustomization(),
new PaidOrganization { CheckedPlanType = planType })
{ }
public PaidOrganizationAutoDataAttribute(int planType = 0) : this((PlanType)planType) { }
}
internal class InlinePaidOrganizationAutoDataAttribute : InlineCustomAutoDataAttribute
{
public InlinePaidOrganizationAutoDataAttribute(PlanType planType, object[] values) : base(
new ICustomization[] { new SutProviderCustomization(), new PaidOrganization { CheckedPlanType = planType } }, values)
{ }
public InlinePaidOrganizationAutoDataAttribute(params object[] values) : base(new[] { typeof(SutProviderCustomization),
typeof(PaidOrganization) }, values)
{ }
}
internal class InlineFreeOrganizationAutoDataAttribute : InlineCustomAutoDataAttribute
{
public InlineFreeOrganizationAutoDataAttribute(params object[] values) : base(new[] { typeof(SutProviderCustomization),
typeof(FreeOrganization) }, values)
{ }
}
internal class FreeOrganizationUpgradeAutoDataAttribute : CustomAutoDataAttribute
{
public FreeOrganizationUpgradeAutoDataAttribute() : base(new SutProviderCustomization(), new FreeOrganizationUpgrade())
{ }
}
internal class InlineFreeOrganizationUpgradeAutoDataAttribute : InlineCustomAutoDataAttribute
{
public InlineFreeOrganizationUpgradeAutoDataAttribute(params object[] values) : base(new[] { typeof(SutProviderCustomization),
typeof(FreeOrganizationUpgrade) }, values)
{ }
}
internal class OrganizationInviteAutoDataAttribute : CustomAutoDataAttribute
{
public OrganizationInviteAutoDataAttribute(int inviteeUserType = 0, int invitorUserType = 0, string permissionsBlob = null) : base(new SutProviderCustomization(),
new OrganizationInvite
{
InviteeUserType = (OrganizationUserType)inviteeUserType,
InvitorUserType = (OrganizationUserType)invitorUserType,
PermissionsBlob = permissionsBlob,
})
{ }
}
internal class InlineOrganizationInviteAutoDataAttribute : InlineCustomAutoDataAttribute
{
public InlineOrganizationInviteAutoDataAttribute(params object[] values) : base(new[] { typeof(SutProviderCustomization),
typeof(OrganizationInvite) }, values)
{ }
}

View File

@ -2,18 +2,17 @@
using Bit.Core.Models.Business;
using Bit.Test.Common.AutoFixture.Attributes;
namespace Bit.Core.Test.AutoFixture
namespace Bit.Core.Test.AutoFixture;
public class OrganizationLicenseCustomizeAttribute : BitCustomizeAttribute
{
public class OrganizationLicenseCustomizeAttribute : BitCustomizeAttribute
public override ICustomization GetCustomization() => new OrganizationLicenseCustomization();
}
public class OrganizationLicenseCustomization : ICustomization
{
public void Customize(IFixture fixture)
{
public override ICustomization GetCustomization() => new OrganizationLicenseCustomization();
}
public class OrganizationLicenseCustomization : ICustomization
{
public void Customize(IFixture fixture)
{
fixture.Customize<OrganizationLicense>(composer => composer
.With(o => o.Signature, Guid.NewGuid().ToString().Replace('-', '+')));
}
fixture.Customize<OrganizationLicense>(composer => composer
.With(o => o.Signature, Guid.NewGuid().ToString().Replace('-', '+')));
}
}

View File

@ -2,32 +2,31 @@
using Bit.Core.Entities;
using Bit.Test.Common.AutoFixture.Attributes;
namespace Bit.Core.Test.AutoFixture.OrganizationSponsorshipFixtures
namespace Bit.Core.Test.AutoFixture.OrganizationSponsorshipFixtures;
public class OrganizationSponsorshipCustomizeAttribute : BitCustomizeAttribute
{
public class OrganizationSponsorshipCustomizeAttribute : BitCustomizeAttribute
{
public bool ToDelete = false;
public override ICustomization GetCustomization() => ToDelete ?
new ToDeleteOrganizationSponsorship() :
new ValidOrganizationSponsorship();
}
public bool ToDelete = false;
public override ICustomization GetCustomization() => ToDelete ?
new ToDeleteOrganizationSponsorship() :
new ValidOrganizationSponsorship();
}
public class ValidOrganizationSponsorship : ICustomization
public class ValidOrganizationSponsorship : ICustomization
{
public void Customize(IFixture fixture)
{
public void Customize(IFixture fixture)
{
fixture.Customize<OrganizationSponsorship>(composer => composer
.With(s => s.ToDelete, false)
.With(s => s.LastSyncDate, DateTime.UtcNow.AddDays(new Random().Next(-90, 0))));
}
}
public class ToDeleteOrganizationSponsorship : ICustomization
{
public void Customize(IFixture fixture)
{
fixture.Customize<OrganizationSponsorship>(composer => composer
.With(s => s.ToDelete, true));
}
fixture.Customize<OrganizationSponsorship>(composer => composer
.With(s => s.ToDelete, false)
.With(s => s.LastSyncDate, DateTime.UtcNow.AddDays(new Random().Next(-90, 0))));
}
}
public class ToDeleteOrganizationSponsorship : ICustomization
{
public void Customize(IFixture fixture)
{
fixture.Customize<OrganizationSponsorship>(composer => composer
.With(s => s.ToDelete, true));
}
}

View File

@ -4,43 +4,42 @@ using AutoFixture.Xunit2;
using Bit.Core.Entities;
using Bit.Core.Enums;
namespace Bit.Core.Test.AutoFixture.OrganizationUserFixtures
namespace Bit.Core.Test.AutoFixture.OrganizationUserFixtures;
public class OrganizationUserCustomization : ICustomization
{
public class OrganizationUserCustomization : ICustomization
public OrganizationUserStatusType Status { get; set; }
public OrganizationUserType Type { get; set; }
public OrganizationUserCustomization(OrganizationUserStatusType status, OrganizationUserType type)
{
public OrganizationUserStatusType Status { get; set; }
public OrganizationUserType Type { get; set; }
public OrganizationUserCustomization(OrganizationUserStatusType status, OrganizationUserType type)
{
Status = status;
Type = type;
}
public void Customize(IFixture fixture)
{
fixture.Customize<OrganizationUser>(composer => composer
.With(o => o.Type, Type)
.With(o => o.Status, Status));
}
Status = status;
Type = type;
}
public class OrganizationUserAttribute : CustomizeAttribute
public void Customize(IFixture fixture)
{
private readonly OrganizationUserStatusType _status;
private readonly OrganizationUserType _type;
public OrganizationUserAttribute(
OrganizationUserStatusType status = OrganizationUserStatusType.Confirmed,
OrganizationUserType type = OrganizationUserType.User)
{
_status = status;
_type = type;
}
public override ICustomization GetCustomization(ParameterInfo parameter)
{
return new OrganizationUserCustomization(_status, _type);
}
fixture.Customize<OrganizationUser>(composer => composer
.With(o => o.Type, Type)
.With(o => o.Status, Status));
}
}
public class OrganizationUserAttribute : CustomizeAttribute
{
private readonly OrganizationUserStatusType _status;
private readonly OrganizationUserType _type;
public OrganizationUserAttribute(
OrganizationUserStatusType status = OrganizationUserStatusType.Confirmed,
OrganizationUserType type = OrganizationUserType.User)
{
_status = status;
_type = type;
}
public override ICustomization GetCustomization(ParameterInfo parameter)
{
return new OrganizationUserCustomization(_status, _type);
}
}

View File

@ -4,38 +4,37 @@ using AutoFixture.Xunit2;
using Bit.Core.Entities;
using Bit.Core.Enums;
namespace Bit.Core.Test.AutoFixture.PolicyFixtures
namespace Bit.Core.Test.AutoFixture.PolicyFixtures;
internal class PolicyCustomization : ICustomization
{
internal class PolicyCustomization : ICustomization
public PolicyType Type { get; set; }
public PolicyCustomization(PolicyType type)
{
public PolicyType Type { get; set; }
public PolicyCustomization(PolicyType type)
{
Type = type;
}
public void Customize(IFixture fixture)
{
fixture.Customize<Policy>(composer => composer
.With(o => o.OrganizationId, Guid.NewGuid())
.With(o => o.Type, Type)
.With(o => o.Enabled, true));
}
Type = type;
}
public class PolicyAttribute : CustomizeAttribute
public void Customize(IFixture fixture)
{
private readonly PolicyType _type;
public PolicyAttribute(PolicyType type)
{
_type = type;
}
public override ICustomization GetCustomization(ParameterInfo parameter)
{
return new PolicyCustomization(_type);
}
fixture.Customize<Policy>(composer => composer
.With(o => o.OrganizationId, Guid.NewGuid())
.With(o => o.Type, Type)
.With(o => o.Enabled, true));
}
}
public class PolicyAttribute : CustomizeAttribute
{
private readonly PolicyType _type;
public PolicyAttribute(PolicyType type)
{
_type = type;
}
public override ICustomization GetCustomization(ParameterInfo parameter)
{
return new PolicyCustomization(_type);
}
}

View File

@ -2,63 +2,62 @@
using Bit.Core.Entities;
using Bit.Test.Common.AutoFixture.Attributes;
namespace Bit.Core.Test.AutoFixture.SendFixtures
namespace Bit.Core.Test.AutoFixture.SendFixtures;
internal class OrganizationSend : ICustomization
{
internal class OrganizationSend : ICustomization
public Guid? OrganizationId { get; set; }
public void Customize(IFixture fixture)
{
public Guid? OrganizationId { get; set; }
public void Customize(IFixture fixture)
{
fixture.Customize<Send>(composer => composer
.With(s => s.OrganizationId, OrganizationId ?? Guid.NewGuid())
.Without(s => s.UserId));
}
}
internal class UserSend : ICustomization
{
public Guid? UserId { get; set; }
public void Customize(IFixture fixture)
{
fixture.Customize<Send>(composer => composer
.With(s => s.UserId, UserId ?? Guid.NewGuid())
.Without(s => s.OrganizationId));
}
}
internal class UserSendAutoDataAttribute : CustomAutoDataAttribute
{
public UserSendAutoDataAttribute(string userId = null) : base(new SutProviderCustomization(),
new UserSend { UserId = userId == null ? (Guid?)null : new Guid(userId) })
{ }
}
internal class InlineUserSendAutoDataAttribute : InlineCustomAutoDataAttribute
{
public InlineUserSendAutoDataAttribute(params object[] values) : base(new[] { typeof(CurrentContextFixtures.CurrentContext),
typeof(SutProviderCustomization), typeof(UserSend) }, values)
{ }
}
internal class InlineKnownUserSendAutoDataAttribute : InlineCustomAutoDataAttribute
{
public InlineKnownUserSendAutoDataAttribute(string userId, params object[] values) : base(new ICustomization[]
{ new CurrentContextFixtures.CurrentContext(), new SutProviderCustomization(),
new UserSend { UserId = new Guid(userId) } }, values)
{ }
}
internal class OrganizationSendAutoDataAttribute : CustomAutoDataAttribute
{
public OrganizationSendAutoDataAttribute(string organizationId = null) : base(new CurrentContextFixtures.CurrentContext(),
new SutProviderCustomization(),
new OrganizationSend { OrganizationId = organizationId == null ? (Guid?)null : new Guid(organizationId) })
{ }
}
internal class InlineOrganizationSendAutoDataAttribute : InlineCustomAutoDataAttribute
{
public InlineOrganizationSendAutoDataAttribute(params object[] values) : base(new[] { typeof(CurrentContextFixtures.CurrentContext),
typeof(SutProviderCustomization), typeof(OrganizationSend) }, values)
{ }
fixture.Customize<Send>(composer => composer
.With(s => s.OrganizationId, OrganizationId ?? Guid.NewGuid())
.Without(s => s.UserId));
}
}
internal class UserSend : ICustomization
{
public Guid? UserId { get; set; }
public void Customize(IFixture fixture)
{
fixture.Customize<Send>(composer => composer
.With(s => s.UserId, UserId ?? Guid.NewGuid())
.Without(s => s.OrganizationId));
}
}
internal class UserSendAutoDataAttribute : CustomAutoDataAttribute
{
public UserSendAutoDataAttribute(string userId = null) : base(new SutProviderCustomization(),
new UserSend { UserId = userId == null ? (Guid?)null : new Guid(userId) })
{ }
}
internal class InlineUserSendAutoDataAttribute : InlineCustomAutoDataAttribute
{
public InlineUserSendAutoDataAttribute(params object[] values) : base(new[] { typeof(CurrentContextFixtures.CurrentContext),
typeof(SutProviderCustomization), typeof(UserSend) }, values)
{ }
}
internal class InlineKnownUserSendAutoDataAttribute : InlineCustomAutoDataAttribute
{
public InlineKnownUserSendAutoDataAttribute(string userId, params object[] values) : base(new ICustomization[]
{ new CurrentContextFixtures.CurrentContext(), new SutProviderCustomization(),
new UserSend { UserId = new Guid(userId) } }, values)
{ }
}
internal class OrganizationSendAutoDataAttribute : CustomAutoDataAttribute
{
public OrganizationSendAutoDataAttribute(string organizationId = null) : base(new CurrentContextFixtures.CurrentContext(),
new SutProviderCustomization(),
new OrganizationSend { OrganizationId = organizationId == null ? (Guid?)null : new Guid(organizationId) })
{ }
}
internal class InlineOrganizationSendAutoDataAttribute : InlineCustomAutoDataAttribute
{
public InlineOrganizationSendAutoDataAttribute(params object[] values) : base(new[] { typeof(CurrentContextFixtures.CurrentContext),
typeof(SutProviderCustomization), typeof(OrganizationSend) }, values)
{ }
}

View File

@ -6,49 +6,48 @@ using Bit.Core.Models;
using Bit.Core.Test.AutoFixture.OrganizationFixtures;
using Bit.Test.Common.AutoFixture;
namespace Bit.Core.Test.AutoFixture.UserFixtures
namespace Bit.Core.Test.AutoFixture.UserFixtures;
public class UserBuilder : ISpecimenBuilder
{
public class UserBuilder : ISpecimenBuilder
public object Create(object request, ISpecimenContext context)
{
public object Create(object request, ISpecimenContext context)
if (context == null)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
throw new ArgumentNullException(nameof(context));
}
var type = request as Type;
if (type == typeof(User))
var type = request as Type;
if (type == typeof(User))
{
var fixture = new Fixture();
var providers = fixture.Create<Dictionary<TwoFactorProviderType, TwoFactorProvider>>();
var user = fixture.WithAutoNSubstitutions().Create<User>();
user.SetTwoFactorProviders(providers);
return user;
}
else if (type == typeof(List<User>))
{
var fixture = new Fixture();
var users = fixture.WithAutoNSubstitutions().CreateMany<User>(2);
foreach (var user in users)
{
var fixture = new Fixture();
var providers = fixture.Create<Dictionary<TwoFactorProviderType, TwoFactorProvider>>();
var user = fixture.WithAutoNSubstitutions().Create<User>();
user.SetTwoFactorProviders(providers);
return user;
}
else if (type == typeof(List<User>))
{
var fixture = new Fixture();
var users = fixture.WithAutoNSubstitutions().CreateMany<User>(2);
foreach (var user in users)
{
var providers = fixture.Create<Dictionary<TwoFactorProviderType, TwoFactorProvider>>();
user.SetTwoFactorProviders(providers);
}
return users;
}
return new NoSpecimen();
return users;
}
}
public class UserFixture : ICustomization
{
public virtual void Customize(IFixture fixture)
{
fixture.Customizations.Add(new GlobalSettingsBuilder());
fixture.Customizations.Add(new UserBuilder());
fixture.Customizations.Add(new OrganizationBuilder());
}
return new NoSpecimen();
}
}
public class UserFixture : ICustomization
{
public virtual void Customize(IFixture fixture)
{
fixture.Customizations.Add(new GlobalSettingsBuilder());
fixture.Customizations.Add(new UserBuilder());
fixture.Customizations.Add(new OrganizationBuilder());
}
}

View File

@ -5,96 +5,95 @@ using Bit.Core.Models;
using Bit.Test.Common.Helpers;
using Xunit;
namespace Bit.Core.Test.Entities
namespace Bit.Core.Test.Entities;
public class OrganizationTests
{
public class OrganizationTests
private static readonly Dictionary<TwoFactorProviderType, TwoFactorProvider> _testConfig = new Dictionary<TwoFactorProviderType, TwoFactorProvider>()
{
private static readonly Dictionary<TwoFactorProviderType, TwoFactorProvider> _testConfig = new Dictionary<TwoFactorProviderType, TwoFactorProvider>()
[TwoFactorProviderType.OrganizationDuo] = new TwoFactorProvider
{
[TwoFactorProviderType.OrganizationDuo] = new TwoFactorProvider
Enabled = true,
MetaData = new Dictionary<string, object>
{
Enabled = true,
MetaData = new Dictionary<string, object>
{
["IKey"] = "IKey_value",
["SKey"] = "SKey_value",
["Host"] = "Host_value",
},
}
["IKey"] = "IKey_value",
["SKey"] = "SKey_value",
["Host"] = "Host_value",
},
}
};
[Fact]
public void SetTwoFactorProviders_Success()
{
var organization = new Organization();
organization.SetTwoFactorProviders(_testConfig);
using var jsonDocument = JsonDocument.Parse(organization.TwoFactorProviders);
var root = jsonDocument.RootElement;
var duo = AssertHelper.AssertJsonProperty(root, "6", JsonValueKind.Object);
AssertHelper.AssertJsonProperty(duo, "Enabled", JsonValueKind.True);
var duoMetaData = AssertHelper.AssertJsonProperty(duo, "MetaData", JsonValueKind.Object);
var iKey = AssertHelper.AssertJsonProperty(duoMetaData, "IKey", JsonValueKind.String).GetString();
Assert.Equal("IKey_value", iKey);
var sKey = AssertHelper.AssertJsonProperty(duoMetaData, "SKey", JsonValueKind.String).GetString();
Assert.Equal("SKey_value", sKey);
var host = AssertHelper.AssertJsonProperty(duoMetaData, "Host", JsonValueKind.String).GetString();
Assert.Equal("Host_value", host);
}
[Fact]
public void GetTwoFactorProviders_Success()
{
// This is to get rid of the cached dictionary the SetTwoFactorProviders keeps so we can fully test the JSON reading
// It intent is to mimic a storing of the entity in the database and it being read later
var tempOrganization = new Organization();
tempOrganization.SetTwoFactorProviders(_testConfig);
var organization = new Organization
{
TwoFactorProviders = tempOrganization.TwoFactorProviders,
};
var twoFactorProviders = organization.GetTwoFactorProviders();
[Fact]
public void SetTwoFactorProviders_Success()
{
var organization = new Organization();
organization.SetTwoFactorProviders(_testConfig);
var duo = Assert.Contains(TwoFactorProviderType.OrganizationDuo, (IDictionary<TwoFactorProviderType, TwoFactorProvider>)twoFactorProviders);
Assert.True(duo.Enabled);
Assert.NotNull(duo.MetaData);
var iKey = Assert.Contains("IKey", (IDictionary<string, object>)duo.MetaData);
Assert.Equal("IKey_value", iKey);
var sKey = Assert.Contains("SKey", (IDictionary<string, object>)duo.MetaData);
Assert.Equal("SKey_value", sKey);
var host = Assert.Contains("Host", (IDictionary<string, object>)duo.MetaData);
Assert.Equal("Host_value", host);
}
using var jsonDocument = JsonDocument.Parse(organization.TwoFactorProviders);
var root = jsonDocument.RootElement;
[Fact]
public void GetTwoFactorProviders_SavedWithName_Success()
{
var organization = new Organization();
// This should save items with the string name of the enum and we will validate that we can read
// from that just incase some organizations have it saved that way.
organization.TwoFactorProviders = JsonSerializer.Serialize(_testConfig);
var duo = AssertHelper.AssertJsonProperty(root, "6", JsonValueKind.Object);
AssertHelper.AssertJsonProperty(duo, "Enabled", JsonValueKind.True);
var duoMetaData = AssertHelper.AssertJsonProperty(duo, "MetaData", JsonValueKind.Object);
var iKey = AssertHelper.AssertJsonProperty(duoMetaData, "IKey", JsonValueKind.String).GetString();
Assert.Equal("IKey_value", iKey);
var sKey = AssertHelper.AssertJsonProperty(duoMetaData, "SKey", JsonValueKind.String).GetString();
Assert.Equal("SKey_value", sKey);
var host = AssertHelper.AssertJsonProperty(duoMetaData, "Host", JsonValueKind.String).GetString();
Assert.Equal("Host_value", host);
}
// Preliminary Asserts to make sure we are testing what we want to be testing
using var jsonDocument = JsonDocument.Parse(organization.TwoFactorProviders);
var root = jsonDocument.RootElement;
// This means it saved the enum as its string name
AssertHelper.AssertJsonProperty(root, "OrganizationDuo", JsonValueKind.Object);
[Fact]
public void GetTwoFactorProviders_Success()
{
// This is to get rid of the cached dictionary the SetTwoFactorProviders keeps so we can fully test the JSON reading
// It intent is to mimic a storing of the entity in the database and it being read later
var tempOrganization = new Organization();
tempOrganization.SetTwoFactorProviders(_testConfig);
var organization = new Organization
{
TwoFactorProviders = tempOrganization.TwoFactorProviders,
};
// Actual checks
var twoFactorProviders = organization.GetTwoFactorProviders();
var twoFactorProviders = organization.GetTwoFactorProviders();
var duo = Assert.Contains(TwoFactorProviderType.OrganizationDuo, (IDictionary<TwoFactorProviderType, TwoFactorProvider>)twoFactorProviders);
Assert.True(duo.Enabled);
Assert.NotNull(duo.MetaData);
var iKey = Assert.Contains("IKey", (IDictionary<string, object>)duo.MetaData);
Assert.Equal("IKey_value", iKey);
var sKey = Assert.Contains("SKey", (IDictionary<string, object>)duo.MetaData);
Assert.Equal("SKey_value", sKey);
var host = Assert.Contains("Host", (IDictionary<string, object>)duo.MetaData);
Assert.Equal("Host_value", host);
}
[Fact]
public void GetTwoFactorProviders_SavedWithName_Success()
{
var organization = new Organization();
// This should save items with the string name of the enum and we will validate that we can read
// from that just incase some organizations have it saved that way.
organization.TwoFactorProviders = JsonSerializer.Serialize(_testConfig);
// Preliminary Asserts to make sure we are testing what we want to be testing
using var jsonDocument = JsonDocument.Parse(organization.TwoFactorProviders);
var root = jsonDocument.RootElement;
// This means it saved the enum as its string name
AssertHelper.AssertJsonProperty(root, "OrganizationDuo", JsonValueKind.Object);
// Actual checks
var twoFactorProviders = organization.GetTwoFactorProviders();
var duo = Assert.Contains(TwoFactorProviderType.OrganizationDuo, (IDictionary<TwoFactorProviderType, TwoFactorProvider>)twoFactorProviders);
Assert.True(duo.Enabled);
Assert.NotNull(duo.MetaData);
var iKey = Assert.Contains("IKey", (IDictionary<string, object>)duo.MetaData);
Assert.Equal("IKey_value", iKey);
var sKey = Assert.Contains("SKey", (IDictionary<string, object>)duo.MetaData);
Assert.Equal("SKey_value", sKey);
var host = Assert.Contains("Host", (IDictionary<string, object>)duo.MetaData);
Assert.Equal("Host_value", host);
}
var duo = Assert.Contains(TwoFactorProviderType.OrganizationDuo, (IDictionary<TwoFactorProviderType, TwoFactorProvider>)twoFactorProviders);
Assert.True(duo.Enabled);
Assert.NotNull(duo.MetaData);
var iKey = Assert.Contains("IKey", (IDictionary<string, object>)duo.MetaData);
Assert.Equal("IKey_value", iKey);
var sKey = Assert.Contains("SKey", (IDictionary<string, object>)duo.MetaData);
Assert.Equal("SKey_value", sKey);
var host = Assert.Contains("Host", (IDictionary<string, object>)duo.MetaData);
Assert.Equal("Host_value", host);
}
}

View File

@ -5,141 +5,140 @@ using Bit.Core.Models;
using Bit.Test.Common.Helpers;
using Xunit;
namespace Bit.Core.Test.Entities
namespace Bit.Core.Test.Entities;
public class UserTests
{
public class UserTests
// KB MB GB
public const long Multiplier = 1024 * 1024 * 1024;
[Fact]
public void StorageBytesRemaining_HasMax_DoesNotHaveStorage_ReturnsMaxAsBytes()
{
// KB MB GB
public const long Multiplier = 1024 * 1024 * 1024;
short maxStorageGb = 1;
[Fact]
public void StorageBytesRemaining_HasMax_DoesNotHaveStorage_ReturnsMaxAsBytes()
var user = new User
{
short maxStorageGb = 1;
var user = new User
{
MaxStorageGb = maxStorageGb,
Storage = null,
};
var bytesRemaining = user.StorageBytesRemaining();
Assert.Equal(bytesRemaining, maxStorageGb * Multiplier);
}
[Theory]
[InlineData(2, 1 * Multiplier, 1 * Multiplier)]
public void StorageBytesRemaining_HasMax_HasStorage_ReturnRemainingStorage(short maxStorageGb, long storageBytes, long expectedRemainingBytes)
{
var user = new User
{
MaxStorageGb = maxStorageGb,
Storage = storageBytes,
};
var bytesRemaining = user.StorageBytesRemaining();
Assert.Equal(expectedRemainingBytes, bytesRemaining);
}
private static readonly Dictionary<TwoFactorProviderType, TwoFactorProvider> _testTwoFactorConfig = new Dictionary<TwoFactorProviderType, TwoFactorProvider>
{
[TwoFactorProviderType.WebAuthn] = new TwoFactorProvider
{
Enabled = true,
MetaData = new Dictionary<string, object>
{
["Item"] = "thing",
},
},
[TwoFactorProviderType.Email] = new TwoFactorProvider
{
Enabled = false,
MetaData = new Dictionary<string, object>
{
["Email"] = "test@email.com",
},
},
MaxStorageGb = maxStorageGb,
Storage = null,
};
[Fact]
public void SetTwoFactorProviders_Success()
var bytesRemaining = user.StorageBytesRemaining();
Assert.Equal(bytesRemaining, maxStorageGb * Multiplier);
}
[Theory]
[InlineData(2, 1 * Multiplier, 1 * Multiplier)]
public void StorageBytesRemaining_HasMax_HasStorage_ReturnRemainingStorage(short maxStorageGb, long storageBytes, long expectedRemainingBytes)
{
var user = new User
{
var user = new User();
user.SetTwoFactorProviders(_testTwoFactorConfig);
MaxStorageGb = maxStorageGb,
Storage = storageBytes,
};
using var jsonDocument = JsonDocument.Parse(user.TwoFactorProviders);
var root = jsonDocument.RootElement;
var bytesRemaining = user.StorageBytesRemaining();
var webAuthn = AssertHelper.AssertJsonProperty(root, "7", JsonValueKind.Object);
AssertHelper.AssertJsonProperty(webAuthn, "Enabled", JsonValueKind.True);
var webMetaData = AssertHelper.AssertJsonProperty(webAuthn, "MetaData", JsonValueKind.Object);
AssertHelper.AssertJsonProperty(webMetaData, "Item", JsonValueKind.String);
Assert.Equal(expectedRemainingBytes, bytesRemaining);
}
var email = AssertHelper.AssertJsonProperty(root, "1", JsonValueKind.Object);
AssertHelper.AssertJsonProperty(email, "Enabled", JsonValueKind.False);
var emailMetaData = AssertHelper.AssertJsonProperty(email, "MetaData", JsonValueKind.Object);
AssertHelper.AssertJsonProperty(emailMetaData, "Email", JsonValueKind.String);
}
[Fact]
public void GetTwoFactorProviders_Success()
private static readonly Dictionary<TwoFactorProviderType, TwoFactorProvider> _testTwoFactorConfig = new Dictionary<TwoFactorProviderType, TwoFactorProvider>
{
[TwoFactorProviderType.WebAuthn] = new TwoFactorProvider
{
// This is to get rid of the cached dictionary the SetTwoFactorProviders keeps so we can fully test the JSON reading
// It intent is to mimic a storing of the entity in the database and it being read later
var tempUser = new User();
tempUser.SetTwoFactorProviders(_testTwoFactorConfig);
var user = new User
Enabled = true,
MetaData = new Dictionary<string, object>
{
TwoFactorProviders = tempUser.TwoFactorProviders,
};
var twoFactorProviders = user.GetTwoFactorProviders();
var webAuthn = Assert.Contains(TwoFactorProviderType.WebAuthn, (IDictionary<TwoFactorProviderType, TwoFactorProvider>)twoFactorProviders);
Assert.True(webAuthn.Enabled);
Assert.NotNull(webAuthn.MetaData);
var webAuthnMetaDataItem = Assert.Contains("Item", (IDictionary<string, object>)webAuthn.MetaData);
Assert.Equal("thing", webAuthnMetaDataItem);
var email = Assert.Contains(TwoFactorProviderType.Email, (IDictionary<TwoFactorProviderType, TwoFactorProvider>)twoFactorProviders);
Assert.False(email.Enabled);
Assert.NotNull(email.MetaData);
var emailMetaDataEmail = Assert.Contains("Email", (IDictionary<string, object>)email.MetaData);
Assert.Equal("test@email.com", emailMetaDataEmail);
}
[Fact]
public void GetTwoFactorProviders_SavedWithName_Success()
["Item"] = "thing",
},
},
[TwoFactorProviderType.Email] = new TwoFactorProvider
{
var user = new User();
// This should save items with the string name of the enum and we will validate that we can read
// from that just incase some users have it saved that way.
user.TwoFactorProviders = JsonSerializer.Serialize(_testTwoFactorConfig);
Enabled = false,
MetaData = new Dictionary<string, object>
{
["Email"] = "test@email.com",
},
},
};
// Preliminary Asserts to make sure we are testing what we want to be testing
using var jsonDocument = JsonDocument.Parse(user.TwoFactorProviders);
var root = jsonDocument.RootElement;
// This means it saved the enum as its string name
AssertHelper.AssertJsonProperty(root, "WebAuthn", JsonValueKind.Object);
AssertHelper.AssertJsonProperty(root, "Email", JsonValueKind.Object);
[Fact]
public void SetTwoFactorProviders_Success()
{
var user = new User();
user.SetTwoFactorProviders(_testTwoFactorConfig);
// Actual checks
var twoFactorProviders = user.GetTwoFactorProviders();
using var jsonDocument = JsonDocument.Parse(user.TwoFactorProviders);
var root = jsonDocument.RootElement;
var webAuthn = Assert.Contains(TwoFactorProviderType.WebAuthn, (IDictionary<TwoFactorProviderType, TwoFactorProvider>)twoFactorProviders);
Assert.True(webAuthn.Enabled);
Assert.NotNull(webAuthn.MetaData);
var webAuthnMetaDataItem = Assert.Contains("Item", (IDictionary<string, object>)webAuthn.MetaData);
Assert.Equal("thing", webAuthnMetaDataItem);
var webAuthn = AssertHelper.AssertJsonProperty(root, "7", JsonValueKind.Object);
AssertHelper.AssertJsonProperty(webAuthn, "Enabled", JsonValueKind.True);
var webMetaData = AssertHelper.AssertJsonProperty(webAuthn, "MetaData", JsonValueKind.Object);
AssertHelper.AssertJsonProperty(webMetaData, "Item", JsonValueKind.String);
var email = Assert.Contains(TwoFactorProviderType.Email, (IDictionary<TwoFactorProviderType, TwoFactorProvider>)twoFactorProviders);
Assert.False(email.Enabled);
Assert.NotNull(email.MetaData);
var emailMetaDataEmail = Assert.Contains("Email", (IDictionary<string, object>)email.MetaData);
Assert.Equal("test@email.com", emailMetaDataEmail);
}
var email = AssertHelper.AssertJsonProperty(root, "1", JsonValueKind.Object);
AssertHelper.AssertJsonProperty(email, "Enabled", JsonValueKind.False);
var emailMetaData = AssertHelper.AssertJsonProperty(email, "MetaData", JsonValueKind.Object);
AssertHelper.AssertJsonProperty(emailMetaData, "Email", JsonValueKind.String);
}
[Fact]
public void GetTwoFactorProviders_Success()
{
// This is to get rid of the cached dictionary the SetTwoFactorProviders keeps so we can fully test the JSON reading
// It intent is to mimic a storing of the entity in the database and it being read later
var tempUser = new User();
tempUser.SetTwoFactorProviders(_testTwoFactorConfig);
var user = new User
{
TwoFactorProviders = tempUser.TwoFactorProviders,
};
var twoFactorProviders = user.GetTwoFactorProviders();
var webAuthn = Assert.Contains(TwoFactorProviderType.WebAuthn, (IDictionary<TwoFactorProviderType, TwoFactorProvider>)twoFactorProviders);
Assert.True(webAuthn.Enabled);
Assert.NotNull(webAuthn.MetaData);
var webAuthnMetaDataItem = Assert.Contains("Item", (IDictionary<string, object>)webAuthn.MetaData);
Assert.Equal("thing", webAuthnMetaDataItem);
var email = Assert.Contains(TwoFactorProviderType.Email, (IDictionary<TwoFactorProviderType, TwoFactorProvider>)twoFactorProviders);
Assert.False(email.Enabled);
Assert.NotNull(email.MetaData);
var emailMetaDataEmail = Assert.Contains("Email", (IDictionary<string, object>)email.MetaData);
Assert.Equal("test@email.com", emailMetaDataEmail);
}
[Fact]
public void GetTwoFactorProviders_SavedWithName_Success()
{
var user = new User();
// This should save items with the string name of the enum and we will validate that we can read
// from that just incase some users have it saved that way.
user.TwoFactorProviders = JsonSerializer.Serialize(_testTwoFactorConfig);
// Preliminary Asserts to make sure we are testing what we want to be testing
using var jsonDocument = JsonDocument.Parse(user.TwoFactorProviders);
var root = jsonDocument.RootElement;
// This means it saved the enum as its string name
AssertHelper.AssertJsonProperty(root, "WebAuthn", JsonValueKind.Object);
AssertHelper.AssertJsonProperty(root, "Email", JsonValueKind.Object);
// Actual checks
var twoFactorProviders = user.GetTwoFactorProviders();
var webAuthn = Assert.Contains(TwoFactorProviderType.WebAuthn, (IDictionary<TwoFactorProviderType, TwoFactorProvider>)twoFactorProviders);
Assert.True(webAuthn.Enabled);
Assert.NotNull(webAuthn.MetaData);
var webAuthnMetaDataItem = Assert.Contains("Item", (IDictionary<string, object>)webAuthn.MetaData);
Assert.Equal("thing", webAuthnMetaDataItem);
var email = Assert.Contains(TwoFactorProviderType.Email, (IDictionary<TwoFactorProviderType, TwoFactorProvider>)twoFactorProviders);
Assert.False(email.Enabled);
Assert.NotNull(email.MetaData);
var emailMetaDataEmail = Assert.Contains("Email", (IDictionary<string, object>)email.MetaData);
Assert.Equal("test@email.com", emailMetaDataEmail);
}
}

View File

@ -1,16 +1,15 @@
using Bit.Core.Settings;
using Microsoft.Extensions.Configuration;
namespace Bit.Core.Test.Helpers.Factories
namespace Bit.Core.Test.Helpers.Factories;
public static class GlobalSettingsFactory
{
public static class GlobalSettingsFactory
public static GlobalSettings GlobalSettings { get; } = new();
static GlobalSettingsFactory()
{
public static GlobalSettings GlobalSettings { get; } = new();
static GlobalSettingsFactory()
{
var configBuilder = new ConfigurationBuilder().AddUserSecrets("bitwarden-Api");
var Configuration = configBuilder.Build();
ConfigurationBinder.Bind(Configuration.GetSection("GlobalSettings"), GlobalSettings);
}
var configBuilder = new ConfigurationBuilder().AddUserSecrets("bitwarden-Api");
var Configuration = configBuilder.Build();
ConfigurationBinder.Bind(Configuration.GetSection("GlobalSettings"), GlobalSettings);
}
}

View File

@ -5,35 +5,34 @@ using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
using Xunit;
namespace Bit.Core.Test.Identity
namespace Bit.Core.Test.Identity;
public class AuthenticationTokenProviderTests : BaseTokenProviderTests<AuthenticatorTokenProvider>
{
public class AuthenticationTokenProviderTests : BaseTokenProviderTests<AuthenticatorTokenProvider>
public override TwoFactorProviderType TwoFactorProviderType => TwoFactorProviderType.Authenticator;
public static IEnumerable<object[]> CanGenerateTwoFactorTokenAsyncData
=> SetupCanGenerateData(
(
new Dictionary<string, object>
{
["Key"] = "stuff",
},
true
),
(
new Dictionary<string, object>
{
["Key"] = ""
},
false
)
);
[Theory, BitMemberAutoData(nameof(CanGenerateTwoFactorTokenAsyncData))]
public override async Task RunCanGenerateTwoFactorTokenAsync(Dictionary<string, object> metaData, bool expectedResponse,
User user, SutProvider<AuthenticatorTokenProvider> sutProvider)
{
public override TwoFactorProviderType TwoFactorProviderType => TwoFactorProviderType.Authenticator;
public static IEnumerable<object[]> CanGenerateTwoFactorTokenAsyncData
=> SetupCanGenerateData(
(
new Dictionary<string, object>
{
["Key"] = "stuff",
},
true
),
(
new Dictionary<string, object>
{
["Key"] = ""
},
false
)
);
[Theory, BitMemberAutoData(nameof(CanGenerateTwoFactorTokenAsyncData))]
public override async Task RunCanGenerateTwoFactorTokenAsync(Dictionary<string, object> metaData, bool expectedResponse,
User user, SutProvider<AuthenticatorTokenProvider> sutProvider)
{
await base.RunCanGenerateTwoFactorTokenAsync(metaData, expectedResponse, user, sutProvider);
}
await base.RunCanGenerateTwoFactorTokenAsync(metaData, expectedResponse, user, sutProvider);
}
}

View File

@ -11,83 +11,82 @@ using Microsoft.Extensions.Options;
using NSubstitute;
using Xunit;
namespace Bit.Core.Test.Identity
namespace Bit.Core.Test.Identity;
[SutProviderCustomize]
public abstract class BaseTokenProviderTests<T>
where T : IUserTwoFactorTokenProvider<User>
{
[SutProviderCustomize]
public abstract class BaseTokenProviderTests<T>
where T : IUserTwoFactorTokenProvider<User>
public abstract TwoFactorProviderType TwoFactorProviderType { get; }
#region Helpers
protected static IEnumerable<object[]> SetupCanGenerateData(params (Dictionary<string, object> MetaData, bool ExpectedResponse)[] data)
{
public abstract TwoFactorProviderType TwoFactorProviderType { get; }
#region Helpers
protected static IEnumerable<object[]> SetupCanGenerateData(params (Dictionary<string, object> MetaData, bool ExpectedResponse)[] data)
{
return data.Select(d =>
new object[]
{
d.MetaData,
d.ExpectedResponse,
});
}
protected virtual IUserService AdditionalSetup(SutProvider<T> sutProvider, User user)
{
var userService = Substitute.For<IUserService>();
sutProvider.GetDependency<IServiceProvider>()
.GetService(typeof(IUserService))
.Returns(userService);
SetupUserService(userService, user);
return userService;
}
protected virtual void SetupUserService(IUserService userService, User user)
{
userService
.TwoFactorProviderIsEnabledAsync(TwoFactorProviderType, user)
.Returns(true);
}
protected static UserManager<User> SubstituteUserManager()
{
return new UserManager<User>(Substitute.For<IUserStore<User>>(),
Substitute.For<IOptions<IdentityOptions>>(),
Substitute.For<IPasswordHasher<User>>(),
Enumerable.Empty<IUserValidator<User>>(),
Enumerable.Empty<IPasswordValidator<User>>(),
Substitute.For<ILookupNormalizer>(),
Substitute.For<IdentityErrorDescriber>(),
Substitute.For<IServiceProvider>(),
Substitute.For<ILogger<UserManager<User>>>());
}
protected void MockDatabase(User user, Dictionary<string, object> metaData)
{
var providers = new Dictionary<TwoFactorProviderType, TwoFactorProvider>
return data.Select(d =>
new object[]
{
[TwoFactorProviderType] = new TwoFactorProvider
{
Enabled = true,
MetaData = metaData,
},
};
d.MetaData,
d.ExpectedResponse,
});
}
user.TwoFactorProviders = JsonHelpers.LegacySerialize(providers);
}
#endregion
protected virtual IUserService AdditionalSetup(SutProvider<T> sutProvider, User user)
{
var userService = Substitute.For<IUserService>();
public virtual async Task RunCanGenerateTwoFactorTokenAsync(Dictionary<string, object> metaData, bool expectedResponse,
User user, SutProvider<T> sutProvider)
sutProvider.GetDependency<IServiceProvider>()
.GetService(typeof(IUserService))
.Returns(userService);
SetupUserService(userService, user);
return userService;
}
protected virtual void SetupUserService(IUserService userService, User user)
{
userService
.TwoFactorProviderIsEnabledAsync(TwoFactorProviderType, user)
.Returns(true);
}
protected static UserManager<User> SubstituteUserManager()
{
return new UserManager<User>(Substitute.For<IUserStore<User>>(),
Substitute.For<IOptions<IdentityOptions>>(),
Substitute.For<IPasswordHasher<User>>(),
Enumerable.Empty<IUserValidator<User>>(),
Enumerable.Empty<IPasswordValidator<User>>(),
Substitute.For<ILookupNormalizer>(),
Substitute.For<IdentityErrorDescriber>(),
Substitute.For<IServiceProvider>(),
Substitute.For<ILogger<UserManager<User>>>());
}
protected void MockDatabase(User user, Dictionary<string, object> metaData)
{
var providers = new Dictionary<TwoFactorProviderType, TwoFactorProvider>
{
var userManager = SubstituteUserManager();
MockDatabase(user, metaData);
[TwoFactorProviderType] = new TwoFactorProvider
{
Enabled = true,
MetaData = metaData,
},
};
AdditionalSetup(sutProvider, user);
user.TwoFactorProviders = JsonHelpers.LegacySerialize(providers);
}
#endregion
var response = await sutProvider.Sut.CanGenerateTwoFactorTokenAsync(userManager, user);
Assert.Equal(expectedResponse, response);
}
public virtual async Task RunCanGenerateTwoFactorTokenAsync(Dictionary<string, object> metaData, bool expectedResponse,
User user, SutProvider<T> sutProvider)
{
var userManager = SubstituteUserManager();
MockDatabase(user, metaData);
AdditionalSetup(sutProvider, user);
var response = await sutProvider.Sut.CanGenerateTwoFactorTokenAsync(userManager, user);
Assert.Equal(expectedResponse, response);
}
}

View File

@ -5,42 +5,41 @@ using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
using Xunit;
namespace Bit.Core.Test.Identity
namespace Bit.Core.Test.Identity;
public class EmailTokenProviderTests : BaseTokenProviderTests<EmailTokenProvider>
{
public class EmailTokenProviderTests : BaseTokenProviderTests<EmailTokenProvider>
public override TwoFactorProviderType TwoFactorProviderType => TwoFactorProviderType.Email;
public static IEnumerable<object[]> CanGenerateTwoFactorTokenAsyncData
=> SetupCanGenerateData(
(
new Dictionary<string, object>
{
["Email"] = "test@email.com",
},
true
),
(
new Dictionary<string, object>
{
["NotEmail"] = "value",
},
false
),
(
new Dictionary<string, object>
{
["Email"] = "",
},
false
)
);
[Theory, BitMemberAutoData(nameof(CanGenerateTwoFactorTokenAsyncData))]
public override async Task RunCanGenerateTwoFactorTokenAsync(Dictionary<string, object> metaData, bool expectedResponse,
User user, SutProvider<EmailTokenProvider> sutProvider)
{
public override TwoFactorProviderType TwoFactorProviderType => TwoFactorProviderType.Email;
public static IEnumerable<object[]> CanGenerateTwoFactorTokenAsyncData
=> SetupCanGenerateData(
(
new Dictionary<string, object>
{
["Email"] = "test@email.com",
},
true
),
(
new Dictionary<string, object>
{
["NotEmail"] = "value",
},
false
),
(
new Dictionary<string, object>
{
["Email"] = "",
},
false
)
);
[Theory, BitMemberAutoData(nameof(CanGenerateTwoFactorTokenAsyncData))]
public override async Task RunCanGenerateTwoFactorTokenAsync(Dictionary<string, object> metaData, bool expectedResponse,
User user, SutProvider<EmailTokenProvider> sutProvider)
{
await base.RunCanGenerateTwoFactorTokenAsync(metaData, expectedResponse, user, sutProvider);
}
await base.RunCanGenerateTwoFactorTokenAsync(metaData, expectedResponse, user, sutProvider);
}
}

View File

@ -4,91 +4,90 @@ using Microsoft.Extensions.Primitives;
using NSubstitute;
using Xunit;
namespace Bit.Core.Test.IdentityServer
namespace Bit.Core.Test.IdentityServer;
public class TokenRetrievalTests
{
public class TokenRetrievalTests
private readonly Func<HttpRequest, string> _sut = TokenRetrieval.FromAuthorizationHeaderOrQueryString();
[Fact]
public void RetrieveToken_FromHeader_ReturnsToken()
{
private readonly Func<HttpRequest, string> _sut = TokenRetrieval.FromAuthorizationHeaderOrQueryString();
[Fact]
public void RetrieveToken_FromHeader_ReturnsToken()
// Arrange
var headers = new HeaderDictionary
{
// Arrange
var headers = new HeaderDictionary
{
{ "Authorization", "Bearer test_value" },
{ "X-Test-Header", "random_value" }
};
{ "Authorization", "Bearer test_value" },
{ "X-Test-Header", "random_value" }
};
var request = Substitute.For<HttpRequest>();
var request = Substitute.For<HttpRequest>();
request.Headers.Returns(headers);
request.Headers.Returns(headers);
// Act
var token = _sut(request);
// Act
var token = _sut(request);
// Assert
Assert.Equal("test_value", token);
}
// Assert
Assert.Equal("test_value", token);
}
[Fact]
public void RetrieveToken_FromQueryString_ReturnsToken()
[Fact]
public void RetrieveToken_FromQueryString_ReturnsToken()
{
// Arrange
var queryString = new Dictionary<string, StringValues>
{
// Arrange
var queryString = new Dictionary<string, StringValues>
{
{ "access_token", "test_value" },
{ "test-query", "random_value" }
};
{ "access_token", "test_value" },
{ "test-query", "random_value" }
};
var request = Substitute.For<HttpRequest>();
request.Query.Returns(new QueryCollection(queryString));
var request = Substitute.For<HttpRequest>();
request.Query.Returns(new QueryCollection(queryString));
// Act
var token = _sut(request);
// Act
var token = _sut(request);
// Assert
Assert.Equal("test_value", token);
}
// Assert
Assert.Equal("test_value", token);
}
[Fact]
public void RetrieveToken_HasBoth_ReturnsHeaderToken()
[Fact]
public void RetrieveToken_HasBoth_ReturnsHeaderToken()
{
// Arrange
var queryString = new Dictionary<string, StringValues>
{
// Arrange
var queryString = new Dictionary<string, StringValues>
{
{ "access_token", "query_string_token" },
{ "test-query", "random_value" }
};
{ "access_token", "query_string_token" },
{ "test-query", "random_value" }
};
var headers = new HeaderDictionary
{
{ "Authorization", "Bearer header_token" },
{ "X-Test-Header", "random_value" }
};
var request = Substitute.For<HttpRequest>();
request.Headers.Returns(headers);
request.Query.Returns(new QueryCollection(queryString));
// Act
var token = _sut(request);
// Assert
Assert.Equal("header_token", token);
}
[Fact]
public void RetrieveToken_NoToken_ReturnsNull()
var headers = new HeaderDictionary
{
// Arrange
var request = Substitute.For<HttpRequest>();
{ "Authorization", "Bearer header_token" },
{ "X-Test-Header", "random_value" }
};
// Act
var token = _sut(request);
var request = Substitute.For<HttpRequest>();
request.Headers.Returns(headers);
request.Query.Returns(new QueryCollection(queryString));
// Assert
Assert.Null(token);
}
// Act
var token = _sut(request);
// Assert
Assert.Equal("header_token", token);
}
[Fact]
public void RetrieveToken_NoToken_ReturnsNull()
{
// Arrange
var request = Substitute.For<HttpRequest>();
// Act
var token = _sut(request);
// Assert
Assert.Null(token);
}
}

View File

@ -1,23 +1,22 @@
using Bit.Core.Models.Business;
using Xunit;
namespace Bit.Core.Test.Models.Business
namespace Bit.Core.Test.Models.Business;
public class BillingInfoTests
{
public class BillingInfoTests
[Fact]
public void BillingInvoice_Amount_ShouldComeFrom_InvoiceTotal()
{
[Fact]
public void BillingInvoice_Amount_ShouldComeFrom_InvoiceTotal()
var invoice = new Stripe.Invoice
{
var invoice = new Stripe.Invoice
{
AmountDue = 1000,
Total = 2000,
};
AmountDue = 1000,
Total = 2000,
};
var billingInvoice = new BillingInfo.BillingInvoice(invoice);
var billingInvoice = new BillingInfo.BillingInvoice(invoice);
// Should have been set from Total
Assert.Equal(20M, billingInvoice.Amount);
}
// Should have been set from Total
Assert.Equal(20M, billingInvoice.Amount);
}
}

View File

@ -1,115 +1,114 @@
using Bit.Core.Models.Business;
using Xunit;
namespace Bit.Core.Test.Models.Business
namespace Bit.Core.Test.Models.Business;
public class TaxInfoTests
{
public class TaxInfoTests
// PH = Placeholder
[Theory]
[InlineData(null, null, null, null)]
[InlineData("", "", null, null)]
[InlineData("PH", "", null, null)]
[InlineData("", "PH", null, null)]
[InlineData("AE", "PH", null, "ae_trn")]
[InlineData("AU", "PH", null, "au_abn")]
[InlineData("BR", "PH", null, "br_cnpj")]
[InlineData("CA", "PH", "bec", "ca_qst")]
[InlineData("CA", "PH", null, "ca_bn")]
[InlineData("CL", "PH", null, "cl_tin")]
[InlineData("AT", "PH", null, "eu_vat")]
[InlineData("BE", "PH", null, "eu_vat")]
[InlineData("BG", "PH", null, "eu_vat")]
[InlineData("CY", "PH", null, "eu_vat")]
[InlineData("CZ", "PH", null, "eu_vat")]
[InlineData("DE", "PH", null, "eu_vat")]
[InlineData("DK", "PH", null, "eu_vat")]
[InlineData("EE", "PH", null, "eu_vat")]
[InlineData("ES", "PH", null, "eu_vat")]
[InlineData("FI", "PH", null, "eu_vat")]
[InlineData("FR", "PH", null, "eu_vat")]
[InlineData("GB", "PH", null, "eu_vat")]
[InlineData("GR", "PH", null, "eu_vat")]
[InlineData("HR", "PH", null, "eu_vat")]
[InlineData("HU", "PH", null, "eu_vat")]
[InlineData("IE", "PH", null, "eu_vat")]
[InlineData("IT", "PH", null, "eu_vat")]
[InlineData("LT", "PH", null, "eu_vat")]
[InlineData("LU", "PH", null, "eu_vat")]
[InlineData("LV", "PH", null, "eu_vat")]
[InlineData("MT", "PH", null, "eu_vat")]
[InlineData("NL", "PH", null, "eu_vat")]
[InlineData("PL", "PH", null, "eu_vat")]
[InlineData("PT", "PH", null, "eu_vat")]
[InlineData("RO", "PH", null, "eu_vat")]
[InlineData("SE", "PH", null, "eu_vat")]
[InlineData("SI", "PH", null, "eu_vat")]
[InlineData("SK", "PH", null, "eu_vat")]
[InlineData("HK", "PH", null, "hk_br")]
[InlineData("IN", "PH", null, "in_gst")]
[InlineData("JP", "PH", null, "jp_cn")]
[InlineData("KR", "PH", null, "kr_brn")]
[InlineData("LI", "PH", null, "li_uid")]
[InlineData("MX", "PH", null, "mx_rfc")]
[InlineData("MY", "PH", null, "my_sst")]
[InlineData("NO", "PH", null, "no_vat")]
[InlineData("NZ", "PH", null, "nz_gst")]
[InlineData("RU", "PH", null, "ru_inn")]
[InlineData("SA", "PH", null, "sa_vat")]
[InlineData("SG", "PH", null, "sg_gst")]
[InlineData("TH", "PH", null, "th_vat")]
[InlineData("TW", "PH", null, "tw_vat")]
[InlineData("US", "PH", null, "us_ein")]
[InlineData("ZA", "PH", null, "za_vat")]
[InlineData("ABCDEF", "PH", null, null)]
public void GetTaxIdType_Success(string billingAddressCountry,
string taxIdNumber,
string billingAddressState,
string expectedTaxIdType)
{
// PH = Placeholder
[Theory]
[InlineData(null, null, null, null)]
[InlineData("", "", null, null)]
[InlineData("PH", "", null, null)]
[InlineData("", "PH", null, null)]
[InlineData("AE", "PH", null, "ae_trn")]
[InlineData("AU", "PH", null, "au_abn")]
[InlineData("BR", "PH", null, "br_cnpj")]
[InlineData("CA", "PH", "bec", "ca_qst")]
[InlineData("CA", "PH", null, "ca_bn")]
[InlineData("CL", "PH", null, "cl_tin")]
[InlineData("AT", "PH", null, "eu_vat")]
[InlineData("BE", "PH", null, "eu_vat")]
[InlineData("BG", "PH", null, "eu_vat")]
[InlineData("CY", "PH", null, "eu_vat")]
[InlineData("CZ", "PH", null, "eu_vat")]
[InlineData("DE", "PH", null, "eu_vat")]
[InlineData("DK", "PH", null, "eu_vat")]
[InlineData("EE", "PH", null, "eu_vat")]
[InlineData("ES", "PH", null, "eu_vat")]
[InlineData("FI", "PH", null, "eu_vat")]
[InlineData("FR", "PH", null, "eu_vat")]
[InlineData("GB", "PH", null, "eu_vat")]
[InlineData("GR", "PH", null, "eu_vat")]
[InlineData("HR", "PH", null, "eu_vat")]
[InlineData("HU", "PH", null, "eu_vat")]
[InlineData("IE", "PH", null, "eu_vat")]
[InlineData("IT", "PH", null, "eu_vat")]
[InlineData("LT", "PH", null, "eu_vat")]
[InlineData("LU", "PH", null, "eu_vat")]
[InlineData("LV", "PH", null, "eu_vat")]
[InlineData("MT", "PH", null, "eu_vat")]
[InlineData("NL", "PH", null, "eu_vat")]
[InlineData("PL", "PH", null, "eu_vat")]
[InlineData("PT", "PH", null, "eu_vat")]
[InlineData("RO", "PH", null, "eu_vat")]
[InlineData("SE", "PH", null, "eu_vat")]
[InlineData("SI", "PH", null, "eu_vat")]
[InlineData("SK", "PH", null, "eu_vat")]
[InlineData("HK", "PH", null, "hk_br")]
[InlineData("IN", "PH", null, "in_gst")]
[InlineData("JP", "PH", null, "jp_cn")]
[InlineData("KR", "PH", null, "kr_brn")]
[InlineData("LI", "PH", null, "li_uid")]
[InlineData("MX", "PH", null, "mx_rfc")]
[InlineData("MY", "PH", null, "my_sst")]
[InlineData("NO", "PH", null, "no_vat")]
[InlineData("NZ", "PH", null, "nz_gst")]
[InlineData("RU", "PH", null, "ru_inn")]
[InlineData("SA", "PH", null, "sa_vat")]
[InlineData("SG", "PH", null, "sg_gst")]
[InlineData("TH", "PH", null, "th_vat")]
[InlineData("TW", "PH", null, "tw_vat")]
[InlineData("US", "PH", null, "us_ein")]
[InlineData("ZA", "PH", null, "za_vat")]
[InlineData("ABCDEF", "PH", null, null)]
public void GetTaxIdType_Success(string billingAddressCountry,
string taxIdNumber,
string billingAddressState,
string expectedTaxIdType)
var taxInfo = new TaxInfo
{
var taxInfo = new TaxInfo
{
BillingAddressCountry = billingAddressCountry,
TaxIdNumber = taxIdNumber,
BillingAddressState = billingAddressState,
};
BillingAddressCountry = billingAddressCountry,
TaxIdNumber = taxIdNumber,
BillingAddressState = billingAddressState,
};
Assert.Equal(expectedTaxIdType, taxInfo.TaxIdType);
}
Assert.Equal(expectedTaxIdType, taxInfo.TaxIdType);
}
[Fact]
public void GetTaxIdType_CreateOnce_ReturnCacheSecondTime()
[Fact]
public void GetTaxIdType_CreateOnce_ReturnCacheSecondTime()
{
var taxInfo = new TaxInfo
{
var taxInfo = new TaxInfo
{
BillingAddressCountry = "US",
TaxIdNumber = "PH",
BillingAddressState = null,
};
BillingAddressCountry = "US",
TaxIdNumber = "PH",
BillingAddressState = null,
};
Assert.Equal("us_ein", taxInfo.TaxIdType);
Assert.Equal("us_ein", taxInfo.TaxIdType);
// Per the current spec even if the values change to something other than null it
// will return the cached version of TaxIdType.
taxInfo.BillingAddressCountry = "ZA";
// Per the current spec even if the values change to something other than null it
// will return the cached version of TaxIdType.
taxInfo.BillingAddressCountry = "ZA";
Assert.Equal("us_ein", taxInfo.TaxIdType);
}
Assert.Equal("us_ein", taxInfo.TaxIdType);
}
[Theory]
[InlineData(null, null, false)]
[InlineData("123", "US", true)]
[InlineData("123", "ZQ12", false)]
[InlineData(" ", "US", false)]
public void HasTaxId_ReturnsExpected(string taxIdNumber, string billingAddressCountry, bool expected)
[Theory]
[InlineData(null, null, false)]
[InlineData("123", "US", true)]
[InlineData("123", "ZQ12", false)]
[InlineData(" ", "US", false)]
public void HasTaxId_ReturnsExpected(string taxIdNumber, string billingAddressCountry, bool expected)
{
var taxInfo = new TaxInfo
{
var taxInfo = new TaxInfo
{
TaxIdNumber = taxIdNumber,
BillingAddressCountry = billingAddressCountry,
};
TaxIdNumber = taxIdNumber,
BillingAddressCountry = billingAddressCountry,
};
Assert.Equal(expected, taxInfo.HasTaxId);
}
Assert.Equal(expected, taxInfo.HasTaxId);
}
}

View File

@ -4,30 +4,29 @@ using Bit.Core.Models.Business.Tokenables;
using Bit.Core.Tokens;
using Xunit;
namespace Bit.Core.Test.Models.Business.Tokenables
namespace Bit.Core.Test.Models.Business.Tokenables;
public class EmergencyAccessInviteTokenableTests
{
public class EmergencyAccessInviteTokenableTests
[Theory, AutoData]
public void SerializationSetsCorrectDateTime(EmergencyAccess emergencyAccess)
{
[Theory, AutoData]
public void SerializationSetsCorrectDateTime(EmergencyAccess emergencyAccess)
{
var token = new EmergencyAccessInviteTokenable(emergencyAccess, 2);
Assert.Equal(Tokenable.FromToken<EmergencyAccessInviteTokenable>(token.ToToken().ToString()).ExpirationDate,
token.ExpirationDate,
TimeSpan.FromMilliseconds(10));
}
var token = new EmergencyAccessInviteTokenable(emergencyAccess, 2);
Assert.Equal(Tokenable.FromToken<EmergencyAccessInviteTokenable>(token.ToToken().ToString()).ExpirationDate,
token.ExpirationDate,
TimeSpan.FromMilliseconds(10));
}
[Fact]
public void IsInvalidIfIdentifierIsWrong()
[Fact]
public void IsInvalidIfIdentifierIsWrong()
{
var token = new EmergencyAccessInviteTokenable(DateTime.MaxValue)
{
var token = new EmergencyAccessInviteTokenable(DateTime.MaxValue)
{
Email = "email",
Id = Guid.NewGuid(),
Identifier = "not correct"
};
Email = "email",
Id = Guid.NewGuid(),
Identifier = "not correct"
};
Assert.False(token.Valid);
}
Assert.False(token.Valid);
}
}

View File

@ -5,84 +5,83 @@ using Bit.Core.Tokens;
using Bit.Test.Common.AutoFixture.Attributes;
using Xunit;
namespace Bit.Core.Test.Models.Business.Tokenables
namespace Bit.Core.Test.Models.Business.Tokenables;
public class HCaptchaTokenableTests
{
public class HCaptchaTokenableTests
[Fact]
public void CanHandleNullUser()
{
[Fact]
public void CanHandleNullUser()
var token = new HCaptchaTokenable(null);
Assert.Equal(default, token.Id);
Assert.Equal(default, token.Email);
}
[Fact]
public void TokenWithNullUserIsInvalid()
{
var token = new HCaptchaTokenable(null)
{
var token = new HCaptchaTokenable(null);
ExpirationDate = DateTime.UtcNow + TimeSpan.FromDays(1)
};
Assert.Equal(default, token.Id);
Assert.Equal(default, token.Email);
}
Assert.False(token.Valid);
}
[Fact]
public void TokenWithNullUserIsInvalid()
[Theory, BitAutoData]
public void TokenValidityCheckNullUserIdIsInvalid(User user)
{
var token = new HCaptchaTokenable(user)
{
var token = new HCaptchaTokenable(null)
{
ExpirationDate = DateTime.UtcNow + TimeSpan.FromDays(1)
};
ExpirationDate = DateTime.UtcNow + TimeSpan.FromDays(1)
};
Assert.False(token.Valid);
}
Assert.False(token.TokenIsValid(null));
}
[Theory, BitAutoData]
public void TokenValidityCheckNullUserIdIsInvalid(User user)
[Theory, AutoData]
public void CanUpdateExpirationToNonStandard(User user)
{
var token = new HCaptchaTokenable(user)
{
var token = new HCaptchaTokenable(user)
{
ExpirationDate = DateTime.UtcNow + TimeSpan.FromDays(1)
};
ExpirationDate = DateTime.MinValue
};
Assert.False(token.TokenIsValid(null));
}
Assert.Equal(DateTime.MinValue, token.ExpirationDate, TimeSpan.FromMilliseconds(10));
}
[Theory, AutoData]
public void CanUpdateExpirationToNonStandard(User user)
[Theory, AutoData]
public void SetsDataFromUser(User user)
{
var token = new HCaptchaTokenable(user);
Assert.Equal(user.Id, token.Id);
Assert.Equal(user.Email, token.Email);
}
[Theory, AutoData]
public void SerializationSetsCorrectDateTime(User user)
{
var expectedDateTime = DateTime.UtcNow.AddHours(-5);
var token = new HCaptchaTokenable(user)
{
var token = new HCaptchaTokenable(user)
{
ExpirationDate = DateTime.MinValue
};
ExpirationDate = expectedDateTime
};
Assert.Equal(DateTime.MinValue, token.ExpirationDate, TimeSpan.FromMilliseconds(10));
}
var result = Tokenable.FromToken<HCaptchaTokenable>(token.ToToken());
[Theory, AutoData]
public void SetsDataFromUser(User user)
Assert.Equal(expectedDateTime, result.ExpirationDate, TimeSpan.FromMilliseconds(10));
}
[Theory, AutoData]
public void IsInvalidIfIdentifierIsWrong(User user)
{
var token = new HCaptchaTokenable(user)
{
var token = new HCaptchaTokenable(user);
Identifier = "not correct"
};
Assert.Equal(user.Id, token.Id);
Assert.Equal(user.Email, token.Email);
}
[Theory, AutoData]
public void SerializationSetsCorrectDateTime(User user)
{
var expectedDateTime = DateTime.UtcNow.AddHours(-5);
var token = new HCaptchaTokenable(user)
{
ExpirationDate = expectedDateTime
};
var result = Tokenable.FromToken<HCaptchaTokenable>(token.ToToken());
Assert.Equal(expectedDateTime, result.ExpirationDate, TimeSpan.FromMilliseconds(10));
}
[Theory, AutoData]
public void IsInvalidIfIdentifierIsWrong(User user)
{
var token = new HCaptchaTokenable(user)
{
Identifier = "not correct"
};
Assert.False(token.Valid);
}
Assert.False(token.Valid);
}
}

View File

@ -4,153 +4,152 @@ using Bit.Core.Models.Business.Tokenables;
using Bit.Test.Common.AutoFixture.Attributes;
using Xunit;
namespace Bit.Core.Test.Models.Business.Tokenables
namespace Bit.Core.Test.Models.Business.Tokenables;
public class OrganizationSponsorshipOfferTokenableTests
{
public class OrganizationSponsorshipOfferTokenableTests
public static IEnumerable<object[]> PlanSponsorshipTypes() => Enum.GetValues<PlanSponsorshipType>().Select(x => new object[] { x });
[Fact]
public void IsInvalidIfIdentifierIsWrong()
{
public static IEnumerable<object[]> PlanSponsorshipTypes() => Enum.GetValues<PlanSponsorshipType>().Select(x => new object[] { x });
[Fact]
public void IsInvalidIfIdentifierIsWrong()
var token = new OrganizationSponsorshipOfferTokenable()
{
var token = new OrganizationSponsorshipOfferTokenable()
{
Email = "email",
Id = Guid.NewGuid(),
Identifier = "not correct",
SponsorshipType = PlanSponsorshipType.FamiliesForEnterprise,
};
Email = "email",
Id = Guid.NewGuid(),
Identifier = "not correct",
SponsorshipType = PlanSponsorshipType.FamiliesForEnterprise,
};
Assert.False(token.Valid);
}
Assert.False(token.Valid);
}
[Fact]
public void IsInvalidIfIdIsDefault()
[Fact]
public void IsInvalidIfIdIsDefault()
{
var token = new OrganizationSponsorshipOfferTokenable()
{
var token = new OrganizationSponsorshipOfferTokenable()
{
Email = "email",
Id = default,
SponsorshipType = PlanSponsorshipType.FamiliesForEnterprise,
};
Email = "email",
Id = default,
SponsorshipType = PlanSponsorshipType.FamiliesForEnterprise,
};
Assert.False(token.Valid);
}
Assert.False(token.Valid);
}
[Fact]
public void IsInvalidIfEmailIsEmpty()
[Fact]
public void IsInvalidIfEmailIsEmpty()
{
var token = new OrganizationSponsorshipOfferTokenable()
{
var token = new OrganizationSponsorshipOfferTokenable()
{
Email = "",
Id = Guid.NewGuid(),
SponsorshipType = PlanSponsorshipType.FamiliesForEnterprise,
};
Email = "",
Id = Guid.NewGuid(),
SponsorshipType = PlanSponsorshipType.FamiliesForEnterprise,
};
Assert.False(token.Valid);
}
Assert.False(token.Valid);
}
[Theory, BitAutoData]
public void IsValid_Success(OrganizationSponsorship sponsorship)
{
var token = new OrganizationSponsorshipOfferTokenable(sponsorship);
[Theory, BitAutoData]
public void IsValid_Success(OrganizationSponsorship sponsorship)
{
var token = new OrganizationSponsorshipOfferTokenable(sponsorship);
Assert.True(token.IsValid(sponsorship, sponsorship.OfferedToEmail));
}
Assert.True(token.IsValid(sponsorship, sponsorship.OfferedToEmail));
}
[Theory, BitAutoData]
public void IsValid_RequiresNonNullSponsorship(OrganizationSponsorship sponsorship)
{
var token = new OrganizationSponsorshipOfferTokenable(sponsorship);
[Theory, BitAutoData]
public void IsValid_RequiresNonNullSponsorship(OrganizationSponsorship sponsorship)
{
var token = new OrganizationSponsorshipOfferTokenable(sponsorship);
Assert.False(token.IsValid(null, sponsorship.OfferedToEmail));
}
Assert.False(token.IsValid(null, sponsorship.OfferedToEmail));
}
[Theory, BitAutoData]
public void IsValid_RequiresCurrentEmailToBeSameAsOfferedToEmail(OrganizationSponsorship sponsorship, string currentEmail)
{
var token = new OrganizationSponsorshipOfferTokenable(sponsorship);
[Theory, BitAutoData]
public void IsValid_RequiresCurrentEmailToBeSameAsOfferedToEmail(OrganizationSponsorship sponsorship, string currentEmail)
{
var token = new OrganizationSponsorshipOfferTokenable(sponsorship);
Assert.False(token.IsValid(sponsorship, currentEmail));
}
Assert.False(token.IsValid(sponsorship, currentEmail));
}
[Theory, BitAutoData]
public void IsValid_RequiresSameSponsorshipId(OrganizationSponsorship sponsorship1, OrganizationSponsorship sponsorship2)
{
sponsorship1.Id = sponsorship2.Id;
[Theory, BitAutoData]
public void IsValid_RequiresSameSponsorshipId(OrganizationSponsorship sponsorship1, OrganizationSponsorship sponsorship2)
{
sponsorship1.Id = sponsorship2.Id;
var token = new OrganizationSponsorshipOfferTokenable(sponsorship1);
var token = new OrganizationSponsorshipOfferTokenable(sponsorship1);
Assert.False(token.IsValid(sponsorship2, sponsorship1.OfferedToEmail));
}
Assert.False(token.IsValid(sponsorship2, sponsorship1.OfferedToEmail));
}
[Theory, BitAutoData]
public void IsValid_RequiresSameEmail(OrganizationSponsorship sponsorship1, OrganizationSponsorship sponsorship2)
{
sponsorship1.OfferedToEmail = sponsorship2.OfferedToEmail;
[Theory, BitAutoData]
public void IsValid_RequiresSameEmail(OrganizationSponsorship sponsorship1, OrganizationSponsorship sponsorship2)
{
sponsorship1.OfferedToEmail = sponsorship2.OfferedToEmail;
var token = new OrganizationSponsorshipOfferTokenable(sponsorship1);
var token = new OrganizationSponsorshipOfferTokenable(sponsorship1);
Assert.False(token.IsValid(sponsorship2, sponsorship1.OfferedToEmail));
}
Assert.False(token.IsValid(sponsorship2, sponsorship1.OfferedToEmail));
}
[Theory, BitAutoData]
public void Constructor_GrabsIdFromSponsorship(OrganizationSponsorship sponsorship)
{
var token = new OrganizationSponsorshipOfferTokenable(sponsorship);
[Theory, BitAutoData]
public void Constructor_GrabsIdFromSponsorship(OrganizationSponsorship sponsorship)
{
var token = new OrganizationSponsorshipOfferTokenable(sponsorship);
Assert.Equal(sponsorship.Id, token.Id);
}
Assert.Equal(sponsorship.Id, token.Id);
}
[Theory, BitAutoData]
public void Constructor_GrabsEmailFromSponsorshipOfferedToEmail(OrganizationSponsorship sponsorship)
{
var token = new OrganizationSponsorshipOfferTokenable(sponsorship);
[Theory, BitAutoData]
public void Constructor_GrabsEmailFromSponsorshipOfferedToEmail(OrganizationSponsorship sponsorship)
{
var token = new OrganizationSponsorshipOfferTokenable(sponsorship);
Assert.Equal(sponsorship.OfferedToEmail, token.Email);
}
Assert.Equal(sponsorship.OfferedToEmail, token.Email);
}
[Theory, BitMemberAutoData(nameof(PlanSponsorshipTypes))]
public void Constructor_GrabsSponsorshipType(PlanSponsorshipType planSponsorshipType,
OrganizationSponsorship sponsorship)
{
sponsorship.PlanSponsorshipType = planSponsorshipType;
var token = new OrganizationSponsorshipOfferTokenable(sponsorship);
[Theory, BitMemberAutoData(nameof(PlanSponsorshipTypes))]
public void Constructor_GrabsSponsorshipType(PlanSponsorshipType planSponsorshipType,
OrganizationSponsorship sponsorship)
{
sponsorship.PlanSponsorshipType = planSponsorshipType;
var token = new OrganizationSponsorshipOfferTokenable(sponsorship);
Assert.Equal(sponsorship.PlanSponsorshipType, token.SponsorshipType);
}
Assert.Equal(sponsorship.PlanSponsorshipType, token.SponsorshipType);
}
[Theory, BitAutoData]
public void Constructor_DefaultId_Throws(OrganizationSponsorship sponsorship)
{
sponsorship.Id = default;
[Theory, BitAutoData]
public void Constructor_DefaultId_Throws(OrganizationSponsorship sponsorship)
{
sponsorship.Id = default;
Assert.Throws<ArgumentException>(() => new OrganizationSponsorshipOfferTokenable(sponsorship));
}
Assert.Throws<ArgumentException>(() => new OrganizationSponsorshipOfferTokenable(sponsorship));
}
[Theory, BitAutoData]
public void Constructor_NoOfferedToEmail_Throws(OrganizationSponsorship sponsorship)
{
sponsorship.OfferedToEmail = null;
[Theory, BitAutoData]
public void Constructor_NoOfferedToEmail_Throws(OrganizationSponsorship sponsorship)
{
sponsorship.OfferedToEmail = null;
Assert.Throws<ArgumentException>(() => new OrganizationSponsorshipOfferTokenable(sponsorship));
}
Assert.Throws<ArgumentException>(() => new OrganizationSponsorshipOfferTokenable(sponsorship));
}
[Theory, BitAutoData]
public void Constructor_EmptyOfferedToEmail_Throws(OrganizationSponsorship sponsorship)
{
sponsorship.OfferedToEmail = "";
[Theory, BitAutoData]
public void Constructor_EmptyOfferedToEmail_Throws(OrganizationSponsorship sponsorship)
{
sponsorship.OfferedToEmail = "";
Assert.Throws<ArgumentException>(() => new OrganizationSponsorshipOfferTokenable(sponsorship));
}
Assert.Throws<ArgumentException>(() => new OrganizationSponsorshipOfferTokenable(sponsorship));
}
[Theory, BitAutoData]
public void Constructor_NoPlanSponsorshipType_Throws(OrganizationSponsorship sponsorship)
{
sponsorship.PlanSponsorshipType = null;
[Theory, BitAutoData]
public void Constructor_NoPlanSponsorshipType_Throws(OrganizationSponsorship sponsorship)
{
sponsorship.PlanSponsorshipType = null;
Assert.Throws<ArgumentException>(() => new OrganizationSponsorshipOfferTokenable(sponsorship));
}
Assert.Throws<ArgumentException>(() => new OrganizationSponsorshipOfferTokenable(sponsorship));
}
}

View File

@ -5,85 +5,84 @@ using Bit.Core.Tokens;
using Bit.Test.Common.AutoFixture.Attributes;
using Xunit;
namespace Bit.Core.Test.Models.Business.Tokenables
namespace Bit.Core.Test.Models.Business.Tokenables;
public class SsoTokenableTests
{
public class SsoTokenableTests
[Fact]
public void CanHandleNullOrganization()
{
[Fact]
public void CanHandleNullOrganization()
var token = new SsoTokenable(null, default);
Assert.Equal(default, token.OrganizationId);
Assert.Equal(default, token.DomainHint);
}
[Fact]
public void TokenWithNullOrganizationIsInvalid()
{
var token = new SsoTokenable(null, 500)
{
var token = new SsoTokenable(null, default);
ExpirationDate = DateTime.UtcNow + TimeSpan.FromDays(1)
};
Assert.Equal(default, token.OrganizationId);
Assert.Equal(default, token.DomainHint);
}
Assert.False(token.Valid);
}
[Fact]
public void TokenWithNullOrganizationIsInvalid()
[Theory, BitAutoData]
public void TokenValidityCheckNullOrganizationIsInvalid(Organization organization)
{
var token = new SsoTokenable(organization, 500)
{
var token = new SsoTokenable(null, 500)
{
ExpirationDate = DateTime.UtcNow + TimeSpan.FromDays(1)
};
ExpirationDate = DateTime.UtcNow + TimeSpan.FromDays(1)
};
Assert.False(token.Valid);
}
Assert.False(token.TokenIsValid(null));
}
[Theory, BitAutoData]
public void TokenValidityCheckNullOrganizationIsInvalid(Organization organization)
[Theory, AutoData]
public void SetsDataFromOrganization(Organization organization)
{
var token = new SsoTokenable(organization, default);
Assert.Equal(organization.Id, token.OrganizationId);
Assert.Equal(organization.Identifier, token.DomainHint);
}
[Fact]
public void SetsExpirationFromConstructor()
{
var expectedDateTime = DateTime.UtcNow.AddSeconds(500);
var token = new SsoTokenable(null, 500);
Assert.Equal(expectedDateTime, token.ExpirationDate, TimeSpan.FromMilliseconds(10));
}
[Theory, AutoData]
public void SerializationSetsCorrectDateTime(Organization organization)
{
var expectedDateTime = DateTime.UtcNow.AddHours(-5);
var token = new SsoTokenable(organization, default)
{
var token = new SsoTokenable(organization, 500)
{
ExpirationDate = DateTime.UtcNow + TimeSpan.FromDays(1)
};
ExpirationDate = expectedDateTime
};
Assert.False(token.TokenIsValid(null));
}
var result = Tokenable.FromToken<HCaptchaTokenable>(token.ToToken());
[Theory, AutoData]
public void SetsDataFromOrganization(Organization organization)
Assert.Equal(expectedDateTime, result.ExpirationDate, TimeSpan.FromMilliseconds(10));
}
[Theory, AutoData]
public void TokenIsValidFailsWhenExpired(Organization organization)
{
var expectedDateTime = DateTime.UtcNow.AddHours(-5);
var token = new SsoTokenable(organization, default)
{
var token = new SsoTokenable(organization, default);
ExpirationDate = expectedDateTime
};
Assert.Equal(organization.Id, token.OrganizationId);
Assert.Equal(organization.Identifier, token.DomainHint);
}
var result = token.TokenIsValid(organization);
[Fact]
public void SetsExpirationFromConstructor()
{
var expectedDateTime = DateTime.UtcNow.AddSeconds(500);
var token = new SsoTokenable(null, 500);
Assert.Equal(expectedDateTime, token.ExpirationDate, TimeSpan.FromMilliseconds(10));
}
[Theory, AutoData]
public void SerializationSetsCorrectDateTime(Organization organization)
{
var expectedDateTime = DateTime.UtcNow.AddHours(-5);
var token = new SsoTokenable(organization, default)
{
ExpirationDate = expectedDateTime
};
var result = Tokenable.FromToken<HCaptchaTokenable>(token.ToToken());
Assert.Equal(expectedDateTime, result.ExpirationDate, TimeSpan.FromMilliseconds(10));
}
[Theory, AutoData]
public void TokenIsValidFailsWhenExpired(Organization organization)
{
var expectedDateTime = DateTime.UtcNow.AddHours(-5);
var token = new SsoTokenable(organization, default)
{
ExpirationDate = expectedDateTime
};
var result = token.TokenIsValid(organization);
Assert.False(result);
}
Assert.False(result);
}
}

View File

@ -3,16 +3,15 @@ using Bit.Core.Entities;
using Bit.Core.Test.AutoFixture.CipherFixtures;
using Xunit;
namespace Bit.Core.Test.Models
namespace Bit.Core.Test.Models;
public class CipherTests
{
public class CipherTests
[Theory]
[InlineUserCipherAutoData]
[InlineOrganizationCipherAutoData]
public void Clone_CreatesExactCopy(Cipher cipher)
{
[Theory]
[InlineUserCipherAutoData]
[InlineOrganizationCipherAutoData]
public void Clone_CreatesExactCopy(Cipher cipher)
{
Assert.Equal(JsonSerializer.Serialize(cipher), JsonSerializer.Serialize(cipher.Clone()));
}
Assert.Equal(JsonSerializer.Serialize(cipher), JsonSerializer.Serialize(cipher.Clone()));
}
}

View File

@ -3,26 +3,25 @@ using Bit.Core.Models.Data;
using Bit.Test.Common.Helpers;
using Xunit;
namespace Bit.Core.Test.Models.Data
{
public class SendFileDataTests
{
[Fact]
public void Serialize_Success()
{
var sut = new SendFileData
{
Id = "test",
Size = 100,
FileName = "thing.pdf",
Validated = true,
};
namespace Bit.Core.Test.Models.Data;
var json = JsonSerializer.Serialize(sut);
var document = JsonDocument.Parse(json);
var root = document.RootElement;
AssertHelper.AssertJsonProperty(root, "Size", JsonValueKind.String);
Assert.False(root.TryGetProperty("SizeString", out _));
}
public class SendFileDataTests
{
[Fact]
public void Serialize_Success()
{
var sut = new SendFileData
{
Id = "test",
Size = 100,
FileName = "thing.pdf",
Validated = true,
};
var json = JsonSerializer.Serialize(sut);
var document = JsonDocument.Parse(json);
var root = document.RootElement;
AssertHelper.AssertJsonProperty(root, "Size", JsonValueKind.String);
Assert.False(root.TryGetProperty("SizeString", out _));
}
}

View File

@ -3,59 +3,58 @@ using Bit.Core.Models.Data;
using Bit.Core.Utilities;
using Xunit;
namespace Bit.Core.Test.Models
namespace Bit.Core.Test.Models;
public class PermissionsTests
{
public class PermissionsTests
private static readonly string _exampleSerializedPermissions = string.Concat(
"{",
"\"accessEventLogs\": false,",
"\"accessImportExport\": false,",
"\"accessReports\": false,",
"\"manageAllCollections\": true,", // exists for backwards compatibility
"\"createNewCollections\": true,",
"\"editAnyCollection\": true,",
"\"deleteAnyCollection\": true,",
"\"manageAssignedCollections\": false,", // exists for backwards compatibility
"\"editAssignedCollections\": false,",
"\"deleteAssignedCollections\": false,",
"\"manageGroups\": false,",
"\"managePolicies\": false,",
"\"manageSso\": false,",
"\"manageUsers\": false,",
"\"manageResetPassword\": false,",
"\"manageScim\": false",
"}");
[Fact]
public void Serialization_Success()
{
private static readonly string _exampleSerializedPermissions = string.Concat(
"{",
"\"accessEventLogs\": false,",
"\"accessImportExport\": false,",
"\"accessReports\": false,",
"\"manageAllCollections\": true,", // exists for backwards compatibility
"\"createNewCollections\": true,",
"\"editAnyCollection\": true,",
"\"deleteAnyCollection\": true,",
"\"manageAssignedCollections\": false,", // exists for backwards compatibility
"\"editAssignedCollections\": false,",
"\"deleteAssignedCollections\": false,",
"\"manageGroups\": false,",
"\"managePolicies\": false,",
"\"manageSso\": false,",
"\"manageUsers\": false,",
"\"manageResetPassword\": false,",
"\"manageScim\": false",
"}");
[Fact]
public void Serialization_Success()
var permissions = new Permissions
{
var permissions = new Permissions
{
AccessEventLogs = false,
AccessImportExport = false,
AccessReports = false,
CreateNewCollections = true,
EditAnyCollection = true,
DeleteAnyCollection = true,
EditAssignedCollections = false,
DeleteAssignedCollections = false,
ManageGroups = false,
ManagePolicies = false,
ManageSso = false,
ManageUsers = false,
ManageResetPassword = false,
ManageScim = false,
};
AccessEventLogs = false,
AccessImportExport = false,
AccessReports = false,
CreateNewCollections = true,
EditAnyCollection = true,
DeleteAnyCollection = true,
EditAssignedCollections = false,
DeleteAssignedCollections = false,
ManageGroups = false,
ManagePolicies = false,
ManageSso = false,
ManageUsers = false,
ManageResetPassword = false,
ManageScim = false,
};
// minify expected json
var expected = JsonSerializer.Serialize(permissions, JsonHelpers.CamelCase);
// minify expected json
var expected = JsonSerializer.Serialize(permissions, JsonHelpers.CamelCase);
var actual = JsonSerializer.Serialize(
JsonHelpers.DeserializeOrNew<Permissions>(_exampleSerializedPermissions, JsonHelpers.CamelCase),
JsonHelpers.CamelCase);
var actual = JsonSerializer.Serialize(
JsonHelpers.DeserializeOrNew<Permissions>(_exampleSerializedPermissions, JsonHelpers.CamelCase),
JsonHelpers.CamelCase);
Assert.Equal(expected, actual);
}
Assert.Equal(expected, actual);
}
}

View File

@ -7,94 +7,93 @@ using Bit.Test.Common.AutoFixture.Attributes;
using NSubstitute;
using Xunit;
namespace Bit.Core.Test.OrganizationFeatures.OrganizationApiKeys
namespace Bit.Core.Test.OrganizationFeatures.OrganizationApiKeys;
[SutProviderCustomize]
public class GetOrganizationApiKeyCommandTests
{
[SutProviderCustomize]
public class GetOrganizationApiKeyCommandTests
[Theory]
[BitAutoData]
public async Task GetOrganizationApiKey_HasOne_Returns(SutProvider<GetOrganizationApiKeyCommand> sutProvider,
Guid id, Guid organizationId, OrganizationApiKeyType keyType)
{
[Theory]
[BitAutoData]
public async Task GetOrganizationApiKey_HasOne_Returns(SutProvider<GetOrganizationApiKeyCommand> sutProvider,
Guid id, Guid organizationId, OrganizationApiKeyType keyType)
{
sutProvider.GetDependency<IOrganizationApiKeyRepository>()
.GetManyByOrganizationIdTypeAsync(organizationId, keyType)
.Returns(new List<OrganizationApiKey>
sutProvider.GetDependency<IOrganizationApiKeyRepository>()
.GetManyByOrganizationIdTypeAsync(organizationId, keyType)
.Returns(new List<OrganizationApiKey>
{
new OrganizationApiKey
{
new OrganizationApiKey
{
Id = id,
OrganizationId = organizationId,
ApiKey = "test",
Type = keyType,
RevisionDate = DateTime.Now.AddDays(-1),
},
});
Id = id,
OrganizationId = organizationId,
ApiKey = "test",
Type = keyType,
RevisionDate = DateTime.Now.AddDays(-1),
},
});
var apiKey = await sutProvider.Sut.GetOrganizationApiKeyAsync(organizationId, keyType);
Assert.NotNull(apiKey);
Assert.Equal(id, apiKey.Id);
}
var apiKey = await sutProvider.Sut.GetOrganizationApiKeyAsync(organizationId, keyType);
Assert.NotNull(apiKey);
Assert.Equal(id, apiKey.Id);
}
[Theory]
[BitAutoData]
public async Task GetOrganizationApiKey_HasTwo_Throws(SutProvider<GetOrganizationApiKeyCommand> sutProvider,
Guid organizationId, OrganizationApiKeyType keyType)
{
sutProvider.GetDependency<IOrganizationApiKeyRepository>()
.GetManyByOrganizationIdTypeAsync(organizationId, keyType)
.Returns(new List<OrganizationApiKey>
[Theory]
[BitAutoData]
public async Task GetOrganizationApiKey_HasTwo_Throws(SutProvider<GetOrganizationApiKeyCommand> sutProvider,
Guid organizationId, OrganizationApiKeyType keyType)
{
sutProvider.GetDependency<IOrganizationApiKeyRepository>()
.GetManyByOrganizationIdTypeAsync(organizationId, keyType)
.Returns(new List<OrganizationApiKey>
{
new OrganizationApiKey
{
new OrganizationApiKey
{
Id = Guid.NewGuid(),
OrganizationId = organizationId,
ApiKey = "test",
Type = keyType,
RevisionDate = DateTime.Now.AddDays(-1),
},
new OrganizationApiKey
{
Id = Guid.NewGuid(),
OrganizationId = organizationId,
ApiKey = "test_other",
Type = keyType,
RevisionDate = DateTime.Now.AddDays(-1),
},
});
Id = Guid.NewGuid(),
OrganizationId = organizationId,
ApiKey = "test",
Type = keyType,
RevisionDate = DateTime.Now.AddDays(-1),
},
new OrganizationApiKey
{
Id = Guid.NewGuid(),
OrganizationId = organizationId,
ApiKey = "test_other",
Type = keyType,
RevisionDate = DateTime.Now.AddDays(-1),
},
});
await Assert.ThrowsAsync<InvalidOperationException>(
async () => await sutProvider.Sut.GetOrganizationApiKeyAsync(organizationId, keyType));
}
await Assert.ThrowsAsync<InvalidOperationException>(
async () => await sutProvider.Sut.GetOrganizationApiKeyAsync(organizationId, keyType));
}
[Theory]
[BitAutoData]
public async Task GetOrganizationApiKey_HasNone_CreatesAndReturns(SutProvider<GetOrganizationApiKeyCommand> sutProvider,
Guid organizationId, OrganizationApiKeyType keyType)
{
sutProvider.GetDependency<IOrganizationApiKeyRepository>()
.GetManyByOrganizationIdTypeAsync(organizationId, keyType)
.Returns(Enumerable.Empty<OrganizationApiKey>());
[Theory]
[BitAutoData]
public async Task GetOrganizationApiKey_HasNone_CreatesAndReturns(SutProvider<GetOrganizationApiKeyCommand> sutProvider,
Guid organizationId, OrganizationApiKeyType keyType)
{
sutProvider.GetDependency<IOrganizationApiKeyRepository>()
.GetManyByOrganizationIdTypeAsync(organizationId, keyType)
.Returns(Enumerable.Empty<OrganizationApiKey>());
var apiKey = await sutProvider.Sut.GetOrganizationApiKeyAsync(organizationId, keyType);
var apiKey = await sutProvider.Sut.GetOrganizationApiKeyAsync(organizationId, keyType);
Assert.NotNull(apiKey);
Assert.Equal(organizationId, apiKey.OrganizationId);
Assert.Equal(keyType, apiKey.Type);
await sutProvider.GetDependency<IOrganizationApiKeyRepository>()
.Received(1)
.CreateAsync(Arg.Any<OrganizationApiKey>());
}
Assert.NotNull(apiKey);
Assert.Equal(organizationId, apiKey.OrganizationId);
Assert.Equal(keyType, apiKey.Type);
await sutProvider.GetDependency<IOrganizationApiKeyRepository>()
.Received(1)
.CreateAsync(Arg.Any<OrganizationApiKey>());
}
[Theory]
[BitAutoData]
public async Task GetOrganizationApiKey_BadType_Throws(SutProvider<GetOrganizationApiKeyCommand> sutProvider,
Guid organizationId, OrganizationApiKeyType keyType)
{
keyType = (OrganizationApiKeyType)byte.MaxValue;
[Theory]
[BitAutoData]
public async Task GetOrganizationApiKey_BadType_Throws(SutProvider<GetOrganizationApiKeyCommand> sutProvider,
Guid organizationId, OrganizationApiKeyType keyType)
{
keyType = (OrganizationApiKeyType)byte.MaxValue;
await Assert.ThrowsAsync<ArgumentOutOfRangeException>(
async () => await sutProvider.Sut.GetOrganizationApiKeyAsync(organizationId, keyType));
}
await Assert.ThrowsAsync<ArgumentOutOfRangeException>(
async () => await sutProvider.Sut.GetOrganizationApiKeyAsync(organizationId, keyType));
}
}

View File

@ -5,19 +5,18 @@ using Bit.Test.Common.AutoFixture.Attributes;
using Bit.Test.Common.Helpers;
using Xunit;
namespace Bit.Core.Test.OrganizationFeatures.OrganizationApiKeys
namespace Bit.Core.Test.OrganizationFeatures.OrganizationApiKeys;
[SutProviderCustomize]
public class RotateOrganizationApiKeyCommandTests
{
[SutProviderCustomize]
public class RotateOrganizationApiKeyCommandTests
[Theory, BitAutoData]
public async Task RotateApiKeyAsync_RotatesKey(SutProvider<RotateOrganizationApiKeyCommand> sutProvider,
OrganizationApiKey organizationApiKey)
{
[Theory, BitAutoData]
public async Task RotateApiKeyAsync_RotatesKey(SutProvider<RotateOrganizationApiKeyCommand> sutProvider,
OrganizationApiKey organizationApiKey)
{
var existingKey = organizationApiKey.ApiKey;
organizationApiKey = await sutProvider.Sut.RotateApiKeyAsync(organizationApiKey);
Assert.NotEqual(existingKey, organizationApiKey.ApiKey);
AssertHelper.AssertRecent(organizationApiKey.RevisionDate);
}
var existingKey = organizationApiKey.ApiKey;
organizationApiKey = await sutProvider.Sut.RotateApiKeyAsync(organizationApiKey);
Assert.NotEqual(existingKey, organizationApiKey.ApiKey);
AssertHelper.AssertRecent(organizationApiKey.RevisionDate);
}
}

View File

@ -8,20 +8,19 @@ using Bit.Test.Common.Helpers;
using NSubstitute;
using Xunit;
namespace Bit.Core.Test.OrganizationFeatures.OrganizationConnections
{
[SutProviderCustomize]
public class CreateOrganizationConnectionCommandTests
{
[Theory]
[BitAutoData]
public async Task CreateAsync_CallsCreate(OrganizationConnectionData<BillingSyncConfig> data,
SutProvider<CreateOrganizationConnectionCommand> sutProvider)
{
await sutProvider.Sut.CreateAsync(data);
namespace Bit.Core.Test.OrganizationFeatures.OrganizationConnections;
await sutProvider.GetDependency<IOrganizationConnectionRepository>().Received(1)
.CreateAsync(Arg.Is(AssertHelper.AssertPropertyEqual(data.ToEntity())));
}
[SutProviderCustomize]
public class CreateOrganizationConnectionCommandTests
{
[Theory]
[BitAutoData]
public async Task CreateAsync_CallsCreate(OrganizationConnectionData<BillingSyncConfig> data,
SutProvider<CreateOrganizationConnectionCommand> sutProvider)
{
await sutProvider.Sut.CreateAsync(data);
await sutProvider.GetDependency<IOrganizationConnectionRepository>().Received(1)
.CreateAsync(Arg.Is(AssertHelper.AssertPropertyEqual(data.ToEntity())));
}
}

View File

@ -6,20 +6,19 @@ using Bit.Test.Common.AutoFixture.Attributes;
using NSubstitute;
using Xunit;
namespace Bit.Core.Test.OrganizationFeatures.OrganizationConnections
{
[SutProviderCustomize]
public class DeleteOrganizationConnectionCommandTests
{
[Theory]
[BitAutoData]
public async Task DeleteAsync_CallsDelete(OrganizationConnection connection,
SutProvider<DeleteOrganizationConnectionCommand> sutProvider)
{
await sutProvider.Sut.DeleteAsync(connection);
namespace Bit.Core.Test.OrganizationFeatures.OrganizationConnections;
await sutProvider.GetDependency<IOrganizationConnectionRepository>().Received(1)
.DeleteAsync(connection);
}
[SutProviderCustomize]
public class DeleteOrganizationConnectionCommandTests
{
[Theory]
[BitAutoData]
public async Task DeleteAsync_CallsDelete(OrganizationConnection connection,
SutProvider<DeleteOrganizationConnectionCommand> sutProvider)
{
await sutProvider.Sut.DeleteAsync(connection);
await sutProvider.GetDependency<IOrganizationConnectionRepository>().Received(1)
.DeleteAsync(connection);
}
}

View File

@ -10,50 +10,49 @@ using Bit.Test.Common.Helpers;
using NSubstitute;
using Xunit;
namespace Bit.Core.Test.OrganizationFeatures.OrganizationConnections
namespace Bit.Core.Test.OrganizationFeatures.OrganizationConnections;
[SutProviderCustomize]
public class UpdateOrganizationConnectionCommandTests
{
[SutProviderCustomize]
public class UpdateOrganizationConnectionCommandTests
[Theory]
[BitAutoData]
public async Task UpdateAsync_NoId_Fails(OrganizationConnectionData<BillingSyncConfig> data,
SutProvider<UpdateOrganizationConnectionCommand> sutProvider)
{
[Theory]
[BitAutoData]
public async Task UpdateAsync_NoId_Fails(OrganizationConnectionData<BillingSyncConfig> data,
SutProvider<UpdateOrganizationConnectionCommand> sutProvider)
{
data.Id = null;
data.Id = null;
var exception = await Assert.ThrowsAsync<Exception>(() => sutProvider.Sut.UpdateAsync(data));
var exception = await Assert.ThrowsAsync<Exception>(() => sutProvider.Sut.UpdateAsync(data));
Assert.Contains("Cannot update connection, Connection does not exist.", exception.Message);
await sutProvider.GetDependency<IOrganizationConnectionRepository>().DidNotReceiveWithAnyArgs()
.UpsertAsync(default);
}
Assert.Contains("Cannot update connection, Connection does not exist.", exception.Message);
await sutProvider.GetDependency<IOrganizationConnectionRepository>().DidNotReceiveWithAnyArgs()
.UpsertAsync(default);
}
[Theory]
[BitAutoData]
public async Task UpdateAsync_ConnectionDoesNotExist_ThrowsNotFound(
OrganizationConnectionData<BillingSyncConfig> data,
SutProvider<UpdateOrganizationConnectionCommand> sutProvider)
{
var exception = await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.UpdateAsync(data));
[Theory]
[BitAutoData]
public async Task UpdateAsync_ConnectionDoesNotExist_ThrowsNotFound(
OrganizationConnectionData<BillingSyncConfig> data,
SutProvider<UpdateOrganizationConnectionCommand> sutProvider)
{
var exception = await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.UpdateAsync(data));
await sutProvider.GetDependency<IOrganizationConnectionRepository>().DidNotReceiveWithAnyArgs()
.UpsertAsync(default);
}
await sutProvider.GetDependency<IOrganizationConnectionRepository>().DidNotReceiveWithAnyArgs()
.UpsertAsync(default);
}
[Theory]
[BitAutoData]
public async Task UpdateAsync_CallsUpsert(OrganizationConnectionData<BillingSyncConfig> data,
OrganizationConnection existing,
SutProvider<UpdateOrganizationConnectionCommand> sutProvider)
{
data.Id = existing.Id;
[Theory]
[BitAutoData]
public async Task UpdateAsync_CallsUpsert(OrganizationConnectionData<BillingSyncConfig> data,
OrganizationConnection existing,
SutProvider<UpdateOrganizationConnectionCommand> sutProvider)
{
data.Id = existing.Id;
sutProvider.GetDependency<IOrganizationConnectionRepository>().GetByIdAsync(data.Id.Value).Returns(existing);
await sutProvider.Sut.UpdateAsync(data);
sutProvider.GetDependency<IOrganizationConnectionRepository>().GetByIdAsync(data.Id.Value).Returns(existing);
await sutProvider.Sut.UpdateAsync(data);
await sutProvider.GetDependency<IOrganizationConnectionRepository>().Received(1)
.UpsertAsync(Arg.Is(AssertHelper.AssertPropertyEqual(data.ToEntity())));
}
await sutProvider.GetDependency<IOrganizationConnectionRepository>().Received(1)
.UpsertAsync(Arg.Is(AssertHelper.AssertPropertyEqual(data.ToEntity())));
}
}

View File

@ -4,71 +4,70 @@ using Bit.Core.Services;
using Bit.Test.Common.AutoFixture;
using NSubstitute;
namespace Bit.Core.Test.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise
namespace Bit.Core.Test.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise;
public abstract class CancelSponsorshipCommandTestsBase : FamiliesForEnterpriseTestsBase
{
public abstract class CancelSponsorshipCommandTestsBase : FamiliesForEnterpriseTestsBase
protected async Task AssertRemovedSponsoredPaymentAsync<T>(Organization sponsoredOrg,
OrganizationSponsorship sponsorship, SutProvider<T> sutProvider)
{
protected async Task AssertRemovedSponsoredPaymentAsync<T>(Organization sponsoredOrg,
OrganizationSponsorship sponsorship, SutProvider<T> sutProvider)
await sutProvider.GetDependency<IPaymentService>().Received(1)
.RemoveOrganizationSponsorshipAsync(sponsoredOrg, sponsorship);
await sutProvider.GetDependency<IOrganizationRepository>().Received(1).UpsertAsync(sponsoredOrg);
if (sponsorship != null)
{
await sutProvider.GetDependency<IPaymentService>().Received(1)
.RemoveOrganizationSponsorshipAsync(sponsoredOrg, sponsorship);
await sutProvider.GetDependency<IOrganizationRepository>().Received(1).UpsertAsync(sponsoredOrg);
if (sponsorship != null)
{
await sutProvider.GetDependency<IMailService>().Received(1)
.SendFamiliesForEnterpriseSponsorshipRevertingEmailAsync(sponsoredOrg.BillingEmailAddress(), sponsorship.ValidUntil.GetValueOrDefault());
}
}
protected async Task AssertDeletedSponsorshipAsync<T>(OrganizationSponsorship sponsorship,
SutProvider<T> sutProvider)
{
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>().Received(1)
.DeleteAsync(sponsorship);
}
protected static async Task AssertDidNotRemoveSponsorshipAsync<T>(SutProvider<T> sutProvider)
{
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>().DidNotReceiveWithAnyArgs()
.DeleteAsync(default);
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>().DidNotReceiveWithAnyArgs()
.UpsertAsync(default);
}
protected async Task AssertRemovedSponsorshipAsync<T>(OrganizationSponsorship sponsorship,
SutProvider<T> sutProvider)
{
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>().Received(1)
.DeleteAsync(sponsorship);
}
protected static async Task AssertDidNotRemoveSponsoredPaymentAsync<T>(SutProvider<T> sutProvider)
{
await sutProvider.GetDependency<IPaymentService>().DidNotReceiveWithAnyArgs()
.RemoveOrganizationSponsorshipAsync(default, default);
await sutProvider.GetDependency<IOrganizationRepository>().DidNotReceiveWithAnyArgs()
.UpsertAsync(default);
await sutProvider.GetDependency<IMailService>().DidNotReceiveWithAnyArgs()
.SendFamiliesForEnterpriseSponsorshipRevertingEmailAsync(default, default);
}
protected static async Task AssertDidNotDeleteSponsorshipAsync<T>(SutProvider<T> sutProvider)
{
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>().DidNotReceiveWithAnyArgs()
.DeleteAsync(default);
}
protected static async Task AssertDidNotUpdateSponsorshipAsync<T>(SutProvider<T> sutProvider)
{
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>().DidNotReceiveWithAnyArgs()
.UpsertAsync(default);
}
protected static async Task AssertUpdatedSponsorshipAsync<T>(OrganizationSponsorship sponsorship,
SutProvider<T> sutProvider)
{
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>().Received(1).UpsertAsync(sponsorship);
await sutProvider.GetDependency<IMailService>().Received(1)
.SendFamiliesForEnterpriseSponsorshipRevertingEmailAsync(sponsoredOrg.BillingEmailAddress(), sponsorship.ValidUntil.GetValueOrDefault());
}
}
protected async Task AssertDeletedSponsorshipAsync<T>(OrganizationSponsorship sponsorship,
SutProvider<T> sutProvider)
{
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>().Received(1)
.DeleteAsync(sponsorship);
}
protected static async Task AssertDidNotRemoveSponsorshipAsync<T>(SutProvider<T> sutProvider)
{
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>().DidNotReceiveWithAnyArgs()
.DeleteAsync(default);
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>().DidNotReceiveWithAnyArgs()
.UpsertAsync(default);
}
protected async Task AssertRemovedSponsorshipAsync<T>(OrganizationSponsorship sponsorship,
SutProvider<T> sutProvider)
{
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>().Received(1)
.DeleteAsync(sponsorship);
}
protected static async Task AssertDidNotRemoveSponsoredPaymentAsync<T>(SutProvider<T> sutProvider)
{
await sutProvider.GetDependency<IPaymentService>().DidNotReceiveWithAnyArgs()
.RemoveOrganizationSponsorshipAsync(default, default);
await sutProvider.GetDependency<IOrganizationRepository>().DidNotReceiveWithAnyArgs()
.UpsertAsync(default);
await sutProvider.GetDependency<IMailService>().DidNotReceiveWithAnyArgs()
.SendFamiliesForEnterpriseSponsorshipRevertingEmailAsync(default, default);
}
protected static async Task AssertDidNotDeleteSponsorshipAsync<T>(SutProvider<T> sutProvider)
{
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>().DidNotReceiveWithAnyArgs()
.DeleteAsync(default);
}
protected static async Task AssertDidNotUpdateSponsorshipAsync<T>(SutProvider<T> sutProvider)
{
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>().DidNotReceiveWithAnyArgs()
.UpsertAsync(default);
}
protected static async Task AssertUpdatedSponsorshipAsync<T>(OrganizationSponsorship sponsorship,
SutProvider<T> sutProvider)
{
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>().Received(1).UpsertAsync(sponsorship);
}
}

View File

@ -6,46 +6,45 @@ using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
using Xunit;
namespace Bit.Core.Test.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Cloud
namespace Bit.Core.Test.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Cloud;
[SutProviderCustomize]
[OrganizationSponsorshipCustomize]
public class CloudRevokeSponsorshipCommandTests : CancelSponsorshipCommandTestsBase
{
[SutProviderCustomize]
[OrganizationSponsorshipCustomize]
public class CloudRevokeSponsorshipCommandTests : CancelSponsorshipCommandTestsBase
[Theory]
[BitAutoData]
public async Task RevokeSponsorship_NoExistingSponsorship_ThrowsBadRequest(
SutProvider<CloudRevokeSponsorshipCommand> sutProvider)
{
[Theory]
[BitAutoData]
public async Task RevokeSponsorship_NoExistingSponsorship_ThrowsBadRequest(
SutProvider<CloudRevokeSponsorshipCommand> sutProvider)
{
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
sutProvider.Sut.RevokeSponsorshipAsync(null));
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
sutProvider.Sut.RevokeSponsorshipAsync(null));
Assert.Contains("You are not currently sponsoring an organization.", exception.Message);
await AssertDidNotDeleteSponsorshipAsync(sutProvider);
await AssertDidNotUpdateSponsorshipAsync(sutProvider);
}
Assert.Contains("You are not currently sponsoring an organization.", exception.Message);
await AssertDidNotDeleteSponsorshipAsync(sutProvider);
await AssertDidNotUpdateSponsorshipAsync(sutProvider);
}
[Theory]
[BitAutoData]
public async Task RevokeSponsorship_SponsorshipNotRedeemed_DeletesSponsorship(OrganizationSponsorship sponsorship,
SutProvider<CloudRevokeSponsorshipCommand> sutProvider)
{
sponsorship.SponsoredOrganizationId = null;
[Theory]
[BitAutoData]
public async Task RevokeSponsorship_SponsorshipNotRedeemed_DeletesSponsorship(OrganizationSponsorship sponsorship,
SutProvider<CloudRevokeSponsorshipCommand> sutProvider)
{
sponsorship.SponsoredOrganizationId = null;
await sutProvider.Sut.RevokeSponsorshipAsync(sponsorship);
await AssertDeletedSponsorshipAsync(sponsorship, sutProvider);
}
await sutProvider.Sut.RevokeSponsorshipAsync(sponsorship);
await AssertDeletedSponsorshipAsync(sponsorship, sutProvider);
}
[Theory]
[BitAutoData]
public async Task RevokeSponsorship_SponsorshipRedeemed_MarksForDelete(OrganizationSponsorship sponsorship,
SutProvider<CloudRevokeSponsorshipCommand> sutProvider)
{
await sutProvider.Sut.RevokeSponsorshipAsync(sponsorship);
[Theory]
[BitAutoData]
public async Task RevokeSponsorship_SponsorshipRedeemed_MarksForDelete(OrganizationSponsorship sponsorship,
SutProvider<CloudRevokeSponsorshipCommand> sutProvider)
{
await sutProvider.Sut.RevokeSponsorshipAsync(sponsorship);
Assert.True(sponsorship.ToDelete);
await AssertUpdatedSponsorshipAsync(sponsorship, sutProvider);
await AssertDidNotDeleteSponsorshipAsync(sutProvider);
}
Assert.True(sponsorship.ToDelete);
await AssertUpdatedSponsorshipAsync(sponsorship, sutProvider);
await AssertDidNotDeleteSponsorshipAsync(sutProvider);
}
}

View File

@ -10,218 +10,216 @@ using Bit.Test.Common.AutoFixture.Attributes;
using NSubstitute;
using Xunit;
namespace Bit.Core.Test.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Cloud
namespace Bit.Core.Test.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Cloud;
[SutProviderCustomize]
public class CloudSyncSponsorshipsCommandTests : FamiliesForEnterpriseTestsBase
{
[SutProviderCustomize]
public class CloudSyncSponsorshipsCommandTests : FamiliesForEnterpriseTestsBase
[Theory]
[BitAutoData]
public async Task SyncOrganization_SponsoringOrgNotFound_ThrowsBadRequest(
IEnumerable<OrganizationSponsorshipData> sponsorshipsData,
SutProvider<CloudSyncSponsorshipsCommand> sutProvider)
{
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
sutProvider.Sut.SyncOrganization(null, sponsorshipsData));
[Theory]
[BitAutoData]
public async Task SyncOrganization_SponsoringOrgNotFound_ThrowsBadRequest(
IEnumerable<OrganizationSponsorshipData> sponsorshipsData,
SutProvider<CloudSyncSponsorshipsCommand> sutProvider)
{
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
sutProvider.Sut.SyncOrganization(null, sponsorshipsData));
Assert.Contains("Failed to sync sponsorship - missing organization.", exception.Message);
Assert.Contains("Failed to sync sponsorship - missing organization.", exception.Message);
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.DidNotReceiveWithAnyArgs()
.UpsertManyAsync(default);
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.DidNotReceiveWithAnyArgs()
.DeleteManyAsync(default);
}
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.DidNotReceiveWithAnyArgs()
.UpsertManyAsync(default);
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.DidNotReceiveWithAnyArgs()
.DeleteManyAsync(default);
}
[Theory]
[BitAutoData]
public async Task SyncOrganization_NoSponsorships_EarlyReturn(
Organization organization,
SutProvider<CloudSyncSponsorshipsCommand> sutProvider)
{
var result = await sutProvider.Sut.SyncOrganization(organization, Enumerable.Empty<OrganizationSponsorshipData>());
[Theory]
[BitAutoData]
public async Task SyncOrganization_NoSponsorships_EarlyReturn(
Organization organization,
SutProvider<CloudSyncSponsorshipsCommand> sutProvider)
{
var result = await sutProvider.Sut.SyncOrganization(organization, Enumerable.Empty<OrganizationSponsorshipData>());
Assert.Empty(result.Item1.SponsorshipsBatch);
Assert.Empty(result.Item2);
Assert.Empty(result.Item1.SponsorshipsBatch);
Assert.Empty(result.Item2);
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.DidNotReceiveWithAnyArgs()
.UpsertManyAsync(default);
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.DidNotReceiveWithAnyArgs()
.DeleteManyAsync(default);
}
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.DidNotReceiveWithAnyArgs()
.UpsertManyAsync(default);
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.DidNotReceiveWithAnyArgs()
.DeleteManyAsync(default);
}
[Theory]
[BitMemberAutoData(nameof(NonEnterprisePlanTypes))]
public async Task SyncOrganization_BadSponsoringOrgPlan_NoSync(
PlanType planType,
Organization organization, IEnumerable<OrganizationSponsorshipData> sponsorshipsData,
SutProvider<CloudSyncSponsorshipsCommand> sutProvider)
{
organization.PlanType = planType;
[Theory]
[BitMemberAutoData(nameof(NonEnterprisePlanTypes))]
public async Task SyncOrganization_BadSponsoringOrgPlan_NoSync(
PlanType planType,
Organization organization, IEnumerable<OrganizationSponsorshipData> sponsorshipsData,
SutProvider<CloudSyncSponsorshipsCommand> sutProvider)
{
organization.PlanType = planType;
await sutProvider.Sut.SyncOrganization(organization, sponsorshipsData);
await sutProvider.Sut.SyncOrganization(organization, sponsorshipsData);
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.DidNotReceiveWithAnyArgs()
.UpsertManyAsync(default);
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.DidNotReceiveWithAnyArgs()
.DeleteManyAsync(default);
}
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.DidNotReceiveWithAnyArgs()
.UpsertManyAsync(default);
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.DidNotReceiveWithAnyArgs()
.DeleteManyAsync(default);
}
[Theory]
[BitAutoData]
public async Task SyncOrganization_Success_RecordsEvent(Organization organization,
SutProvider<CloudSyncSponsorshipsCommand> sutProvider)
{
await sutProvider.Sut.SyncOrganization(organization, Array.Empty<OrganizationSponsorshipData>());
[Theory]
[BitAutoData]
public async Task SyncOrganization_Success_RecordsEvent(Organization organization,
SutProvider<CloudSyncSponsorshipsCommand> sutProvider)
{
await sutProvider.Sut.SyncOrganization(organization, Array.Empty<OrganizationSponsorshipData>());
await sutProvider.GetDependency<IEventService>().Received(1).LogOrganizationEventAsync(organization, EventType.Organization_SponsorshipsSynced, Arg.Any<DateTime?>());
}
await sutProvider.GetDependency<IEventService>().Received(1).LogOrganizationEventAsync(organization, EventType.Organization_SponsorshipsSynced, Arg.Any<DateTime?>());
}
[Theory]
[BitAutoData]
public async Task SyncOrganization_OneExisting_OneNew_Success(SutProvider<CloudSyncSponsorshipsCommand> sutProvider,
Organization sponsoringOrganization, OrganizationSponsorship existingSponsorship, OrganizationSponsorship newSponsorship)
{
// Arrange
sponsoringOrganization.Enabled = true;
sponsoringOrganization.PlanType = PlanType.EnterpriseAnnually;
[Theory]
[BitAutoData]
public async Task SyncOrganization_OneExisting_OneNew_Success(SutProvider<CloudSyncSponsorshipsCommand> sutProvider,
Organization sponsoringOrganization, OrganizationSponsorship existingSponsorship, OrganizationSponsorship newSponsorship)
{
// Arrange
sponsoringOrganization.Enabled = true;
sponsoringOrganization.PlanType = PlanType.EnterpriseAnnually;
existingSponsorship.ToDelete = false;
newSponsorship.ToDelete = false;
existingSponsorship.ToDelete = false;
newSponsorship.ToDelete = false;
sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.GetManyBySponsoringOrganizationAsync(sponsoringOrganization.Id)
.Returns(new List<OrganizationSponsorship>
{
existingSponsorship,
});
// Act
var (syncData, toEmailSponsorships) = await sutProvider.Sut.SyncOrganization(sponsoringOrganization, new[]
sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.GetManyBySponsoringOrganizationAsync(sponsoringOrganization.Id)
.Returns(new List<OrganizationSponsorship>
{
new OrganizationSponsorshipData(existingSponsorship),
new OrganizationSponsorshipData(newSponsorship),
existingSponsorship,
});
// Assert
// Should have updated the cloud copy for each item given
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.Received(1)
.UpsertManyAsync(Arg.Is<IEnumerable<OrganizationSponsorship>>(sponsorships => sponsorships.Count() == 2));
// Neither were marked as delete, should not have deleted
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.DidNotReceiveWithAnyArgs()
.DeleteManyAsync(default);
// Only one sponsorship was new so it should only send one
Assert.Single(toEmailSponsorships);
}
[Theory]
[BitAutoData]
public async Task SyncOrganization_TwoToDelete_OneCanDelete_Success(SutProvider<CloudSyncSponsorshipsCommand> sutProvider,
Organization sponsoringOrganization, OrganizationSponsorship canDeleteSponsorship, OrganizationSponsorship cannotDeleteSponsorship)
// Act
var (syncData, toEmailSponsorships) = await sutProvider.Sut.SyncOrganization(sponsoringOrganization, new[]
{
// Arrange
sponsoringOrganization.PlanType = PlanType.EnterpriseAnnually;
new OrganizationSponsorshipData(existingSponsorship),
new OrganizationSponsorshipData(newSponsorship),
});
canDeleteSponsorship.ToDelete = true;
canDeleteSponsorship.SponsoredOrganizationId = null;
// Assert
// Should have updated the cloud copy for each item given
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.Received(1)
.UpsertManyAsync(Arg.Is<IEnumerable<OrganizationSponsorship>>(sponsorships => sponsorships.Count() == 2));
cannotDeleteSponsorship.ToDelete = true;
cannotDeleteSponsorship.SponsoredOrganizationId = Guid.NewGuid();
// Neither were marked as delete, should not have deleted
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.DidNotReceiveWithAnyArgs()
.DeleteManyAsync(default);
sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.GetManyBySponsoringOrganizationAsync(sponsoringOrganization.Id)
.Returns(new List<OrganizationSponsorship>
{
canDeleteSponsorship,
cannotDeleteSponsorship,
});
// Only one sponsorship was new so it should only send one
Assert.Single(toEmailSponsorships);
}
// Act
var (syncData, toEmailSponsorships) = await sutProvider.Sut.SyncOrganization(sponsoringOrganization, new[]
[Theory]
[BitAutoData]
public async Task SyncOrganization_TwoToDelete_OneCanDelete_Success(SutProvider<CloudSyncSponsorshipsCommand> sutProvider,
Organization sponsoringOrganization, OrganizationSponsorship canDeleteSponsorship, OrganizationSponsorship cannotDeleteSponsorship)
{
// Arrange
sponsoringOrganization.PlanType = PlanType.EnterpriseAnnually;
canDeleteSponsorship.ToDelete = true;
canDeleteSponsorship.SponsoredOrganizationId = null;
cannotDeleteSponsorship.ToDelete = true;
cannotDeleteSponsorship.SponsoredOrganizationId = Guid.NewGuid();
sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.GetManyBySponsoringOrganizationAsync(sponsoringOrganization.Id)
.Returns(new List<OrganizationSponsorship>
{
new OrganizationSponsorshipData(canDeleteSponsorship),
new OrganizationSponsorshipData(cannotDeleteSponsorship),
canDeleteSponsorship,
cannotDeleteSponsorship,
});
// Assert
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.Received(1)
.UpsertManyAsync(Arg.Is<IEnumerable<OrganizationSponsorship>>(sponsorships => sponsorships.Count() == 2));
// Deletes the sponsorship that had delete requested and is not sponsoring an org
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.Received(1)
.DeleteManyAsync(Arg.Is<IEnumerable<Guid>>(toDeleteIds =>
toDeleteIds.Count() == 1 && toDeleteIds.ElementAt(0) == canDeleteSponsorship.Id));
}
[Theory]
[BitAutoData]
public async Task SyncOrganization_BadData_DoesNotSave(SutProvider<CloudSyncSponsorshipsCommand> sutProvider,
Organization sponsoringOrganization, OrganizationSponsorship badOrganizationSponsorship)
// Act
var (syncData, toEmailSponsorships) = await sutProvider.Sut.SyncOrganization(sponsoringOrganization, new[]
{
sponsoringOrganization.PlanType = PlanType.EnterpriseAnnually;
new OrganizationSponsorshipData(canDeleteSponsorship),
new OrganizationSponsorshipData(cannotDeleteSponsorship),
});
badOrganizationSponsorship.ToDelete = true;
badOrganizationSponsorship.LastSyncDate = null;
// Assert
sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.GetManyBySponsoringOrganizationAsync(sponsoringOrganization.Id)
.Returns(new List<OrganizationSponsorship>());
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.Received(1)
.UpsertManyAsync(Arg.Is<IEnumerable<OrganizationSponsorship>>(sponsorships => sponsorships.Count() == 2));
var (syncData, toEmailSponsorships) = await sutProvider.Sut.SyncOrganization(sponsoringOrganization, new[]
{
new OrganizationSponsorshipData(badOrganizationSponsorship),
});
// Deletes the sponsorship that had delete requested and is not sponsoring an org
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.Received(1)
.DeleteManyAsync(Arg.Is<IEnumerable<Guid>>(toDeleteIds =>
toDeleteIds.Count() == 1 && toDeleteIds.ElementAt(0) == canDeleteSponsorship.Id));
}
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.DidNotReceiveWithAnyArgs()
.UpsertManyAsync(default);
[Theory]
[BitAutoData]
public async Task SyncOrganization_BadData_DoesNotSave(SutProvider<CloudSyncSponsorshipsCommand> sutProvider,
Organization sponsoringOrganization, OrganizationSponsorship badOrganizationSponsorship)
{
sponsoringOrganization.PlanType = PlanType.EnterpriseAnnually;
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.DidNotReceiveWithAnyArgs()
.DeleteManyAsync(default);
}
badOrganizationSponsorship.ToDelete = true;
badOrganizationSponsorship.LastSyncDate = null;
[Theory]
[BitAutoData]
public async Task SyncOrganization_OrgDisabledForFourMonths_DoesNotSave(SutProvider<CloudSyncSponsorshipsCommand> sutProvider,
Organization sponsoringOrganization, OrganizationSponsorship organizationSponsorship)
sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.GetManyBySponsoringOrganizationAsync(sponsoringOrganization.Id)
.Returns(new List<OrganizationSponsorship>());
var (syncData, toEmailSponsorships) = await sutProvider.Sut.SyncOrganization(sponsoringOrganization, new[]
{
sponsoringOrganization.PlanType = PlanType.EnterpriseAnnually;
sponsoringOrganization.Enabled = false;
sponsoringOrganization.ExpirationDate = DateTime.UtcNow.AddDays(-120);
new OrganizationSponsorshipData(badOrganizationSponsorship),
});
organizationSponsorship.ToDelete = false;
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.DidNotReceiveWithAnyArgs()
.UpsertManyAsync(default);
sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.GetManyBySponsoringOrganizationAsync(sponsoringOrganization.Id)
.Returns(new List<OrganizationSponsorship>());
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.DidNotReceiveWithAnyArgs()
.DeleteManyAsync(default);
}
var (syncData, toEmailSponsorships) = await sutProvider.Sut.SyncOrganization(sponsoringOrganization, new[]
{
new OrganizationSponsorshipData(organizationSponsorship),
});
[Theory]
[BitAutoData]
public async Task SyncOrganization_OrgDisabledForFourMonths_DoesNotSave(SutProvider<CloudSyncSponsorshipsCommand> sutProvider,
Organization sponsoringOrganization, OrganizationSponsorship organizationSponsorship)
{
sponsoringOrganization.PlanType = PlanType.EnterpriseAnnually;
sponsoringOrganization.Enabled = false;
sponsoringOrganization.ExpirationDate = DateTime.UtcNow.AddDays(-120);
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.DidNotReceiveWithAnyArgs()
.UpsertManyAsync(default);
organizationSponsorship.ToDelete = false;
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.DidNotReceiveWithAnyArgs()
.DeleteManyAsync(default);
}
sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.GetManyBySponsoringOrganizationAsync(sponsoringOrganization.Id)
.Returns(new List<OrganizationSponsorship>());
var (syncData, toEmailSponsorships) = await sutProvider.Sut.SyncOrganization(sponsoringOrganization, new[]
{
new OrganizationSponsorshipData(organizationSponsorship),
});
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.DidNotReceiveWithAnyArgs()
.UpsertManyAsync(default);
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.DidNotReceiveWithAnyArgs()
.DeleteManyAsync(default);
}
}

View File

@ -6,22 +6,21 @@ using Bit.Test.Common.AutoFixture.Attributes;
using NSubstitute;
using Xunit;
namespace Bit.Core.Test.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Cloud
namespace Bit.Core.Test.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Cloud;
[SutProviderCustomize]
public class OrganizationSponsorshipRenewCommandTests
{
[SutProviderCustomize]
public class OrganizationSponsorshipRenewCommandTests
[Theory]
[BitAutoData]
public async Task UpdateExpirationDate_UpdatesValidUntil(OrganizationSponsorship sponsorship, DateTime expireDate,
SutProvider<OrganizationSponsorshipRenewCommand> sutProvider)
{
[Theory]
[BitAutoData]
public async Task UpdateExpirationDate_UpdatesValidUntil(OrganizationSponsorship sponsorship, DateTime expireDate,
SutProvider<OrganizationSponsorshipRenewCommand> sutProvider)
{
sutProvider.GetDependency<IOrganizationSponsorshipRepository>().GetBySponsoredOrganizationIdAsync(sponsorship.SponsoredOrganizationId.Value).Returns(sponsorship);
sutProvider.GetDependency<IOrganizationSponsorshipRepository>().GetBySponsoredOrganizationIdAsync(sponsorship.SponsoredOrganizationId.Value).Returns(sponsorship);
await sutProvider.Sut.UpdateExpirationDateAsync(sponsorship.SponsoredOrganizationId.Value, expireDate);
await sutProvider.Sut.UpdateExpirationDateAsync(sponsorship.SponsoredOrganizationId.Value, expireDate);
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>().Received(1)
.UpsertAsync(sponsorship);
}
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>().Received(1)
.UpsertAsync(sponsorship);
}
}

View File

@ -6,38 +6,37 @@ using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
using Xunit;
namespace Bit.Core.Test.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise
namespace Bit.Core.Test.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise;
[SutProviderCustomize]
[OrganizationSponsorshipCustomize]
public class RemoveSponsorshipCommandTests : CancelSponsorshipCommandTestsBase
{
[SutProviderCustomize]
[OrganizationSponsorshipCustomize]
public class RemoveSponsorshipCommandTests : CancelSponsorshipCommandTestsBase
[Theory]
[BitAutoData]
public async Task RemoveSponsorship_SponsoredOrgNull_ThrowsBadRequest(OrganizationSponsorship sponsorship,
SutProvider<RemoveSponsorshipCommand> sutProvider)
{
[Theory]
[BitAutoData]
public async Task RemoveSponsorship_SponsoredOrgNull_ThrowsBadRequest(OrganizationSponsorship sponsorship,
SutProvider<RemoveSponsorshipCommand> sutProvider)
{
sponsorship.SponsoredOrganizationId = null;
sponsorship.SponsoredOrganizationId = null;
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
sutProvider.Sut.RemoveSponsorshipAsync(sponsorship));
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
sutProvider.Sut.RemoveSponsorshipAsync(sponsorship));
Assert.Contains("The requested organization is not currently being sponsored.", exception.Message);
Assert.False(sponsorship.ToDelete);
await AssertDidNotDeleteSponsorshipAsync(sutProvider);
await AssertDidNotUpdateSponsorshipAsync(sutProvider);
}
Assert.Contains("The requested organization is not currently being sponsored.", exception.Message);
Assert.False(sponsorship.ToDelete);
await AssertDidNotDeleteSponsorshipAsync(sutProvider);
await AssertDidNotUpdateSponsorshipAsync(sutProvider);
}
[Theory]
[BitAutoData]
public async Task RemoveSponsorship_SponsorshipNotFound_ThrowsBadRequest(SutProvider<RemoveSponsorshipCommand> sutProvider)
{
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
sutProvider.Sut.RemoveSponsorshipAsync(null));
[Theory]
[BitAutoData]
public async Task RemoveSponsorship_SponsorshipNotFound_ThrowsBadRequest(SutProvider<RemoveSponsorshipCommand> sutProvider)
{
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
sutProvider.Sut.RemoveSponsorshipAsync(null));
Assert.Contains("The requested organization is not currently being sponsored.", exception.Message);
await AssertDidNotDeleteSponsorshipAsync(sutProvider);
await AssertDidNotUpdateSponsorshipAsync(sutProvider);
}
Assert.Contains("The requested organization is not currently being sponsored.", exception.Message);
await AssertDidNotDeleteSponsorshipAsync(sutProvider);
await AssertDidNotUpdateSponsorshipAsync(sutProvider);
}
}

View File

@ -10,115 +10,114 @@ using Bit.Test.Common.AutoFixture.Attributes;
using NSubstitute;
using Xunit;
namespace Bit.Core.Test.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise
namespace Bit.Core.Test.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise;
[SutProviderCustomize]
[OrganizationSponsorshipCustomize]
public class SendSponsorshipOfferCommandTests : FamiliesForEnterpriseTestsBase
{
[SutProviderCustomize]
[OrganizationSponsorshipCustomize]
public class SendSponsorshipOfferCommandTests : FamiliesForEnterpriseTestsBase
[Theory]
[BitAutoData]
public async Task SendSponsorshipOffer_SendSponsorshipOfferAsync_ExistingAccount_Success(OrganizationSponsorship sponsorship, string sponsoringOrgName, User user, SutProvider<SendSponsorshipOfferCommand> sutProvider)
{
[Theory]
[BitAutoData]
public async Task SendSponsorshipOffer_SendSponsorshipOfferAsync_ExistingAccount_Success(OrganizationSponsorship sponsorship, string sponsoringOrgName, User user, SutProvider<SendSponsorshipOfferCommand> sutProvider)
{
sutProvider.GetDependency<IUserRepository>().GetByEmailAsync(sponsorship.OfferedToEmail).Returns(user);
sutProvider.GetDependency<IUserRepository>().GetByEmailAsync(sponsorship.OfferedToEmail).Returns(user);
await sutProvider.Sut.SendSponsorshipOfferAsync(sponsorship, sponsoringOrgName);
await sutProvider.Sut.SendSponsorshipOfferAsync(sponsorship, sponsoringOrgName);
await sutProvider.GetDependency<IMailService>().Received(1).SendFamiliesForEnterpriseOfferEmailAsync(sponsoringOrgName, sponsorship.OfferedToEmail, true, Arg.Any<string>());
}
await sutProvider.GetDependency<IMailService>().Received(1).SendFamiliesForEnterpriseOfferEmailAsync(sponsoringOrgName, sponsorship.OfferedToEmail, true, Arg.Any<string>());
}
[Theory]
[BitAutoData]
public async Task SendSponsorshipOffer_SendSponsorshipOfferAsync_NewAccount_Success(OrganizationSponsorship sponsorship, string sponsoringOrgName, SutProvider<SendSponsorshipOfferCommand> sutProvider)
{
sutProvider.GetDependency<IUserRepository>().GetByEmailAsync(sponsorship.OfferedToEmail).Returns((User)null);
[Theory]
[BitAutoData]
public async Task SendSponsorshipOffer_SendSponsorshipOfferAsync_NewAccount_Success(OrganizationSponsorship sponsorship, string sponsoringOrgName, SutProvider<SendSponsorshipOfferCommand> sutProvider)
{
sutProvider.GetDependency<IUserRepository>().GetByEmailAsync(sponsorship.OfferedToEmail).Returns((User)null);
await sutProvider.Sut.SendSponsorshipOfferAsync(sponsorship, sponsoringOrgName);
await sutProvider.Sut.SendSponsorshipOfferAsync(sponsorship, sponsoringOrgName);
await sutProvider.GetDependency<IMailService>().Received(1).SendFamiliesForEnterpriseOfferEmailAsync(sponsoringOrgName, sponsorship.OfferedToEmail, false, Arg.Any<string>());
}
await sutProvider.GetDependency<IMailService>().Received(1).SendFamiliesForEnterpriseOfferEmailAsync(sponsoringOrgName, sponsorship.OfferedToEmail, false, Arg.Any<string>());
}
[Theory]
[BitAutoData]
public async Task ResendSponsorshipOffer_SponsoringOrgNotFound_ThrowsBadRequest(
OrganizationUser orgUser, OrganizationSponsorship sponsorship,
SutProvider<SendSponsorshipOfferCommand> sutProvider)
{
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
sutProvider.Sut.SendSponsorshipOfferAsync(null, orgUser, sponsorship));
[Theory]
[BitAutoData]
public async Task ResendSponsorshipOffer_SponsoringOrgNotFound_ThrowsBadRequest(
OrganizationUser orgUser, OrganizationSponsorship sponsorship,
SutProvider<SendSponsorshipOfferCommand> sutProvider)
{
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
sutProvider.Sut.SendSponsorshipOfferAsync(null, orgUser, sponsorship));
Assert.Contains("Cannot find the requested sponsoring organization.", exception.Message);
await sutProvider.GetDependency<IMailService>()
.DidNotReceiveWithAnyArgs()
.SendFamiliesForEnterpriseOfferEmailAsync(default, default, default, default);
}
Assert.Contains("Cannot find the requested sponsoring organization.", exception.Message);
await sutProvider.GetDependency<IMailService>()
.DidNotReceiveWithAnyArgs()
.SendFamiliesForEnterpriseOfferEmailAsync(default, default, default, default);
}
[Theory]
[BitAutoData]
public async Task ResendSponsorshipOffer_SponsoringOrgUserNotFound_ThrowsBadRequest(Organization org,
OrganizationSponsorship sponsorship, SutProvider<SendSponsorshipOfferCommand> sutProvider)
{
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
sutProvider.Sut.SendSponsorshipOfferAsync(org, null, sponsorship));
[Theory]
[BitAutoData]
public async Task ResendSponsorshipOffer_SponsoringOrgUserNotFound_ThrowsBadRequest(Organization org,
OrganizationSponsorship sponsorship, SutProvider<SendSponsorshipOfferCommand> sutProvider)
{
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
sutProvider.Sut.SendSponsorshipOfferAsync(org, null, sponsorship));
Assert.Contains("Only confirmed users can sponsor other organizations.", exception.Message);
await sutProvider.GetDependency<IMailService>()
.DidNotReceiveWithAnyArgs()
.SendFamiliesForEnterpriseOfferEmailAsync(default, default, default, default);
}
Assert.Contains("Only confirmed users can sponsor other organizations.", exception.Message);
await sutProvider.GetDependency<IMailService>()
.DidNotReceiveWithAnyArgs()
.SendFamiliesForEnterpriseOfferEmailAsync(default, default, default, default);
}
[Theory]
[BitAutoData]
[BitMemberAutoData(nameof(NonConfirmedOrganizationUsersStatuses))]
public async Task ResendSponsorshipOffer_SponsoringOrgUserNotConfirmed_ThrowsBadRequest(OrganizationUserStatusType status,
Organization org, OrganizationUser orgUser, OrganizationSponsorship sponsorship,
SutProvider<SendSponsorshipOfferCommand> sutProvider)
{
orgUser.Status = status;
[Theory]
[BitAutoData]
[BitMemberAutoData(nameof(NonConfirmedOrganizationUsersStatuses))]
public async Task ResendSponsorshipOffer_SponsoringOrgUserNotConfirmed_ThrowsBadRequest(OrganizationUserStatusType status,
Organization org, OrganizationUser orgUser, OrganizationSponsorship sponsorship,
SutProvider<SendSponsorshipOfferCommand> sutProvider)
{
orgUser.Status = status;
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
sutProvider.Sut.SendSponsorshipOfferAsync(org, orgUser, sponsorship));
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
sutProvider.Sut.SendSponsorshipOfferAsync(org, orgUser, sponsorship));
Assert.Contains("Only confirmed users can sponsor other organizations.", exception.Message);
await sutProvider.GetDependency<IMailService>()
.DidNotReceiveWithAnyArgs()
.SendFamiliesForEnterpriseOfferEmailAsync(default, default, default, default);
}
Assert.Contains("Only confirmed users can sponsor other organizations.", exception.Message);
await sutProvider.GetDependency<IMailService>()
.DidNotReceiveWithAnyArgs()
.SendFamiliesForEnterpriseOfferEmailAsync(default, default, default, default);
}
[Theory]
[BitAutoData]
public async Task ResendSponsorshipOffer_SponsorshipNotFound_ThrowsBadRequest(Organization org,
OrganizationUser orgUser,
SutProvider<SendSponsorshipOfferCommand> sutProvider)
{
orgUser.Status = OrganizationUserStatusType.Confirmed;
[Theory]
[BitAutoData]
public async Task ResendSponsorshipOffer_SponsorshipNotFound_ThrowsBadRequest(Organization org,
OrganizationUser orgUser,
SutProvider<SendSponsorshipOfferCommand> sutProvider)
{
orgUser.Status = OrganizationUserStatusType.Confirmed;
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
sutProvider.Sut.SendSponsorshipOfferAsync(org, orgUser, null));
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
sutProvider.Sut.SendSponsorshipOfferAsync(org, orgUser, null));
Assert.Contains("Cannot find an outstanding sponsorship offer for this organization.", exception.Message);
await sutProvider.GetDependency<IMailService>()
.DidNotReceiveWithAnyArgs()
.SendFamiliesForEnterpriseOfferEmailAsync(default, default, default, default);
}
Assert.Contains("Cannot find an outstanding sponsorship offer for this organization.", exception.Message);
await sutProvider.GetDependency<IMailService>()
.DidNotReceiveWithAnyArgs()
.SendFamiliesForEnterpriseOfferEmailAsync(default, default, default, default);
}
[Theory]
[BitAutoData]
public async Task ResendSponsorshipOffer_NoOfferToEmail_ThrowsBadRequest(Organization org,
OrganizationUser orgUser, OrganizationSponsorship sponsorship,
SutProvider<SendSponsorshipOfferCommand> sutProvider)
{
orgUser.Status = OrganizationUserStatusType.Confirmed;
sponsorship.OfferedToEmail = null;
[Theory]
[BitAutoData]
public async Task ResendSponsorshipOffer_NoOfferToEmail_ThrowsBadRequest(Organization org,
OrganizationUser orgUser, OrganizationSponsorship sponsorship,
SutProvider<SendSponsorshipOfferCommand> sutProvider)
{
orgUser.Status = OrganizationUserStatusType.Confirmed;
sponsorship.OfferedToEmail = null;
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
sutProvider.Sut.SendSponsorshipOfferAsync(org, orgUser, sponsorship));
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
sutProvider.Sut.SendSponsorshipOfferAsync(org, orgUser, sponsorship));
Assert.Contains("Cannot find an outstanding sponsorship offer for this organization.", exception.Message);
await sutProvider.GetDependency<IMailService>()
.DidNotReceiveWithAnyArgs()
.SendFamiliesForEnterpriseOfferEmailAsync(default, default, default, default);
}
Assert.Contains("Cannot find an outstanding sponsorship offer for this organization.", exception.Message);
await sutProvider.GetDependency<IMailService>()
.DidNotReceiveWithAnyArgs()
.SendFamiliesForEnterpriseOfferEmailAsync(default, default, default, default);
}
}

View File

@ -10,86 +10,85 @@ using Bit.Test.Common.AutoFixture.Attributes;
using NSubstitute;
using Xunit;
namespace Bit.Core.Test.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Cloud
namespace Bit.Core.Test.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Cloud;
[SutProviderCustomize]
[OrganizationSponsorshipCustomize]
public class SetUpSponsorshipCommandTests : FamiliesForEnterpriseTestsBase
{
[SutProviderCustomize]
[OrganizationSponsorshipCustomize]
public class SetUpSponsorshipCommandTests : FamiliesForEnterpriseTestsBase
[Theory]
[BitAutoData]
public async Task SetUpSponsorship_SponsorshipNotFound_ThrowsBadRequest(Organization org,
SutProvider<SetUpSponsorshipCommand> sutProvider)
{
[Theory]
[BitAutoData]
public async Task SetUpSponsorship_SponsorshipNotFound_ThrowsBadRequest(Organization org,
SutProvider<SetUpSponsorshipCommand> sutProvider)
{
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
sutProvider.Sut.SetUpSponsorshipAsync(null, org));
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
sutProvider.Sut.SetUpSponsorshipAsync(null, org));
Assert.Contains("No unredeemed sponsorship offer exists for you.", exception.Message);
await AssertDidNotSetUpAsync(sutProvider);
}
Assert.Contains("No unredeemed sponsorship offer exists for you.", exception.Message);
await AssertDidNotSetUpAsync(sutProvider);
}
[Theory]
[BitAutoData]
public async Task SetUpSponsorship_OrgAlreadySponsored_ThrowsBadRequest(Organization org,
OrganizationSponsorship sponsorship, OrganizationSponsorship existingSponsorship,
SutProvider<SetUpSponsorshipCommand> sutProvider)
{
sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.GetBySponsoredOrganizationIdAsync(org.Id).Returns(existingSponsorship);
[Theory]
[BitAutoData]
public async Task SetUpSponsorship_OrgAlreadySponsored_ThrowsBadRequest(Organization org,
OrganizationSponsorship sponsorship, OrganizationSponsorship existingSponsorship,
SutProvider<SetUpSponsorshipCommand> sutProvider)
{
sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.GetBySponsoredOrganizationIdAsync(org.Id).Returns(existingSponsorship);
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
sutProvider.Sut.SetUpSponsorshipAsync(sponsorship, org));
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
sutProvider.Sut.SetUpSponsorshipAsync(sponsorship, org));
Assert.Contains("Cannot redeem a sponsorship offer for an organization that is already sponsored. Revoke existing sponsorship first.", exception.Message);
await AssertDidNotSetUpAsync(sutProvider);
}
Assert.Contains("Cannot redeem a sponsorship offer for an organization that is already sponsored. Revoke existing sponsorship first.", exception.Message);
await AssertDidNotSetUpAsync(sutProvider);
}
[Theory]
[BitMemberAutoData(nameof(FamiliesPlanTypes))]
public async Task SetUpSponsorship_TooLongSinceLastSync_ThrowsBadRequest(PlanType planType, Organization org,
OrganizationSponsorship sponsorship,
SutProvider<SetUpSponsorshipCommand> sutProvider)
{
org.PlanType = planType;
sponsorship.LastSyncDate = DateTime.UtcNow.AddDays(-365);
[Theory]
[BitMemberAutoData(nameof(FamiliesPlanTypes))]
public async Task SetUpSponsorship_TooLongSinceLastSync_ThrowsBadRequest(PlanType planType, Organization org,
OrganizationSponsorship sponsorship,
SutProvider<SetUpSponsorshipCommand> sutProvider)
{
org.PlanType = planType;
sponsorship.LastSyncDate = DateTime.UtcNow.AddDays(-365);
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
sutProvider.Sut.SetUpSponsorshipAsync(sponsorship, org));
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
sutProvider.Sut.SetUpSponsorshipAsync(sponsorship, org));
Assert.Contains("This sponsorship offer is more than 6 months old and has expired.", exception.Message);
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.Received(1)
.DeleteAsync(sponsorship);
await AssertDidNotSetUpAsync(sutProvider);
}
Assert.Contains("This sponsorship offer is more than 6 months old and has expired.", exception.Message);
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.Received(1)
.DeleteAsync(sponsorship);
await AssertDidNotSetUpAsync(sutProvider);
}
[Theory]
[BitMemberAutoData(nameof(NonFamiliesPlanTypes))]
public async Task SetUpSponsorship_OrgNotFamiles_ThrowsBadRequest(PlanType planType,
OrganizationSponsorship sponsorship, Organization org,
SutProvider<SetUpSponsorshipCommand> sutProvider)
{
org.PlanType = planType;
sponsorship.LastSyncDate = DateTime.UtcNow;
[Theory]
[BitMemberAutoData(nameof(NonFamiliesPlanTypes))]
public async Task SetUpSponsorship_OrgNotFamiles_ThrowsBadRequest(PlanType planType,
OrganizationSponsorship sponsorship, Organization org,
SutProvider<SetUpSponsorshipCommand> sutProvider)
{
org.PlanType = planType;
sponsorship.LastSyncDate = DateTime.UtcNow;
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
sutProvider.Sut.SetUpSponsorshipAsync(sponsorship, org));
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
sutProvider.Sut.SetUpSponsorshipAsync(sponsorship, org));
Assert.Contains("Can only redeem sponsorship offer on families organizations.", exception.Message);
await AssertDidNotSetUpAsync(sutProvider);
}
Assert.Contains("Can only redeem sponsorship offer on families organizations.", exception.Message);
await AssertDidNotSetUpAsync(sutProvider);
}
private static async Task AssertDidNotSetUpAsync(SutProvider<SetUpSponsorshipCommand> sutProvider)
{
await sutProvider.GetDependency<IPaymentService>()
.DidNotReceiveWithAnyArgs()
.SponsorOrganizationAsync(default, default);
await sutProvider.GetDependency<IOrganizationRepository>()
.DidNotReceiveWithAnyArgs()
.UpsertAsync(default);
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.DidNotReceiveWithAnyArgs()
.UpsertAsync(default);
}
private static async Task AssertDidNotSetUpAsync(SutProvider<SetUpSponsorshipCommand> sutProvider)
{
await sutProvider.GetDependency<IPaymentService>()
.DidNotReceiveWithAnyArgs()
.SponsorOrganizationAsync(default, default);
await sutProvider.GetDependency<IOrganizationRepository>()
.DidNotReceiveWithAnyArgs()
.UpsertAsync(default);
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.DidNotReceiveWithAnyArgs()
.UpsertAsync(default);
}
}

View File

@ -8,51 +8,50 @@ using Bit.Test.Common.AutoFixture.Attributes;
using NSubstitute;
using Xunit;
namespace Bit.Core.Test.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Cloud
namespace Bit.Core.Test.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Cloud;
[SutProviderCustomize]
public class ValidateBillingSyncKeyCommandTests
{
[SutProviderCustomize]
public class ValidateBillingSyncKeyCommandTests
[Theory]
[BitAutoData]
public async Task ValidateBillingSyncKeyAsync_NullOrganization_Throws(SutProvider<ValidateBillingSyncKeyCommand> sutProvider)
{
[Theory]
[BitAutoData]
public async Task ValidateBillingSyncKeyAsync_NullOrganization_Throws(SutProvider<ValidateBillingSyncKeyCommand> sutProvider)
{
await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.ValidateBillingSyncKeyAsync(null, null));
}
await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.ValidateBillingSyncKeyAsync(null, null));
}
[Theory]
[BitAutoData((string)null)]
[BitAutoData("")]
[BitAutoData(" ")]
public async Task ValidateBillingSyncKeyAsync_BadString_ReturnsFalse(string billingSyncKey, SutProvider<ValidateBillingSyncKeyCommand> sutProvider)
{
Assert.False(await sutProvider.Sut.ValidateBillingSyncKeyAsync(new Organization(), billingSyncKey));
}
[Theory]
[BitAutoData((string)null)]
[BitAutoData("")]
[BitAutoData(" ")]
public async Task ValidateBillingSyncKeyAsync_BadString_ReturnsFalse(string billingSyncKey, SutProvider<ValidateBillingSyncKeyCommand> sutProvider)
{
Assert.False(await sutProvider.Sut.ValidateBillingSyncKeyAsync(new Organization(), billingSyncKey));
}
[Theory]
[BitAutoData]
public async Task ValidateBillingSyncKeyAsync_KeyEquals_ReturnsTrue(SutProvider<ValidateBillingSyncKeyCommand> sutProvider,
Organization organization, OrganizationApiKey orgApiKey, string billingSyncKey)
{
orgApiKey.ApiKey = billingSyncKey;
[Theory]
[BitAutoData]
public async Task ValidateBillingSyncKeyAsync_KeyEquals_ReturnsTrue(SutProvider<ValidateBillingSyncKeyCommand> sutProvider,
Organization organization, OrganizationApiKey orgApiKey, string billingSyncKey)
{
orgApiKey.ApiKey = billingSyncKey;
sutProvider.GetDependency<IOrganizationApiKeyRepository>()
.GetManyByOrganizationIdTypeAsync(organization.Id, OrganizationApiKeyType.BillingSync)
.Returns(new[] { orgApiKey });
sutProvider.GetDependency<IOrganizationApiKeyRepository>()
.GetManyByOrganizationIdTypeAsync(organization.Id, OrganizationApiKeyType.BillingSync)
.Returns(new[] { orgApiKey });
Assert.True(await sutProvider.Sut.ValidateBillingSyncKeyAsync(organization, billingSyncKey));
}
Assert.True(await sutProvider.Sut.ValidateBillingSyncKeyAsync(organization, billingSyncKey));
}
[Theory]
[BitAutoData]
public async Task ValidateBillingSyncKeyAsync_KeyDoesNotEqual_ReturnsFalse(SutProvider<ValidateBillingSyncKeyCommand> sutProvider,
Organization organization, OrganizationApiKey orgApiKey, string billingSyncKey)
{
sutProvider.GetDependency<IOrganizationApiKeyRepository>()
.GetManyByOrganizationIdTypeAsync(organization.Id, OrganizationApiKeyType.BillingSync)
.Returns(new[] { orgApiKey });
[Theory]
[BitAutoData]
public async Task ValidateBillingSyncKeyAsync_KeyDoesNotEqual_ReturnsFalse(SutProvider<ValidateBillingSyncKeyCommand> sutProvider,
Organization organization, OrganizationApiKey orgApiKey, string billingSyncKey)
{
sutProvider.GetDependency<IOrganizationApiKeyRepository>()
.GetManyByOrganizationIdTypeAsync(organization.Id, OrganizationApiKeyType.BillingSync)
.Returns(new[] { orgApiKey });
Assert.False(await sutProvider.Sut.ValidateBillingSyncKeyAsync(organization, billingSyncKey));
}
Assert.False(await sutProvider.Sut.ValidateBillingSyncKeyAsync(organization, billingSyncKey));
}
}

View File

@ -9,79 +9,78 @@ using Bit.Test.Common.AutoFixture.Attributes;
using NSubstitute;
using Xunit;
namespace Bit.Core.Test.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Cloud
namespace Bit.Core.Test.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Cloud;
[SutProviderCustomize]
public class ValidateRedemptionTokenCommandTests
{
[SutProviderCustomize]
public class ValidateRedemptionTokenCommandTests
[Theory]
[BitAutoData]
public async Task ValidateRedemptionTokenAsync_CannotUnprotect_ReturnsFalse(SutProvider<ValidateRedemptionTokenCommand> sutProvider,
string encryptedString)
{
[Theory]
[BitAutoData]
public async Task ValidateRedemptionTokenAsync_CannotUnprotect_ReturnsFalse(SutProvider<ValidateRedemptionTokenCommand> sutProvider,
string encryptedString)
{
sutProvider
.GetDependency<IDataProtectorTokenFactory<OrganizationSponsorshipOfferTokenable>>()
.TryUnprotect(encryptedString, out _)
.Returns(call =>
{
call[1] = null;
return false;
});
sutProvider
.GetDependency<IDataProtectorTokenFactory<OrganizationSponsorshipOfferTokenable>>()
.TryUnprotect(encryptedString, out _)
.Returns(call =>
{
call[1] = null;
return false;
});
var (valid, sponsorship) = await sutProvider.Sut.ValidateRedemptionTokenAsync(encryptedString, null);
Assert.False(valid);
Assert.Null(sponsorship);
}
var (valid, sponsorship) = await sutProvider.Sut.ValidateRedemptionTokenAsync(encryptedString, null);
Assert.False(valid);
Assert.Null(sponsorship);
}
[Theory]
[BitAutoData]
public async Task ValidateRedemptionTokenAsync_NoSponsorship_ReturnsFalse(SutProvider<ValidateRedemptionTokenCommand> sutProvider,
string encryptedString, OrganizationSponsorshipOfferTokenable tokenable)
{
sutProvider
.GetDependency<IDataProtectorTokenFactory<OrganizationSponsorshipOfferTokenable>>()
.TryUnprotect(encryptedString, out _)
.Returns(call =>
{
call[1] = tokenable;
return true;
});
[Theory]
[BitAutoData]
public async Task ValidateRedemptionTokenAsync_NoSponsorship_ReturnsFalse(SutProvider<ValidateRedemptionTokenCommand> sutProvider,
string encryptedString, OrganizationSponsorshipOfferTokenable tokenable)
{
sutProvider
.GetDependency<IDataProtectorTokenFactory<OrganizationSponsorshipOfferTokenable>>()
.TryUnprotect(encryptedString, out _)
.Returns(call =>
{
call[1] = tokenable;
return true;
});
var (valid, sponsorship) = await sutProvider.Sut.ValidateRedemptionTokenAsync(encryptedString, "test@email.com");
Assert.False(valid);
Assert.Null(sponsorship);
}
var (valid, sponsorship) = await sutProvider.Sut.ValidateRedemptionTokenAsync(encryptedString, "test@email.com");
Assert.False(valid);
Assert.Null(sponsorship);
}
[Theory]
[BitAutoData]
public async Task ValidateRedemptionTokenAsync_ValidSponsorship_ReturnsFalse(SutProvider<ValidateRedemptionTokenCommand> sutProvider,
string encryptedString, string email, OrganizationSponsorshipOfferTokenable tokenable)
{
tokenable.Email = email;
[Theory]
[BitAutoData]
public async Task ValidateRedemptionTokenAsync_ValidSponsorship_ReturnsFalse(SutProvider<ValidateRedemptionTokenCommand> sutProvider,
string encryptedString, string email, OrganizationSponsorshipOfferTokenable tokenable)
{
tokenable.Email = email;
sutProvider
.GetDependency<IDataProtectorTokenFactory<OrganizationSponsorshipOfferTokenable>>()
.TryUnprotect(encryptedString, out _)
.Returns(call =>
{
call[1] = tokenable;
return true;
});
sutProvider
.GetDependency<IDataProtectorTokenFactory<OrganizationSponsorshipOfferTokenable>>()
.TryUnprotect(encryptedString, out _)
.Returns(call =>
{
call[1] = tokenable;
return true;
});
sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.GetByIdAsync(tokenable.Id)
.Returns(new OrganizationSponsorship
{
Id = tokenable.Id,
PlanSponsorshipType = PlanSponsorshipType.FamiliesForEnterprise,
OfferedToEmail = email
});
sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.GetByIdAsync(tokenable.Id)
.Returns(new OrganizationSponsorship
{
Id = tokenable.Id,
PlanSponsorshipType = PlanSponsorshipType.FamiliesForEnterprise,
OfferedToEmail = email
});
var (valid, sponsorship) = await sutProvider.Sut
.ValidateRedemptionTokenAsync(encryptedString, email);
var (valid, sponsorship) = await sutProvider.Sut
.ValidateRedemptionTokenAsync(encryptedString, email);
Assert.True(valid);
Assert.NotNull(sponsorship);
}
Assert.True(valid);
Assert.NotNull(sponsorship);
}
}

View File

@ -8,247 +8,246 @@ using Bit.Test.Common.AutoFixture.Attributes;
using NSubstitute;
using Xunit;
namespace Bit.Core.Test.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Cloud
namespace Bit.Core.Test.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Cloud;
[SutProviderCustomize]
[OrganizationSponsorshipCustomize]
public class ValidateSponsorshipCommandTests : CancelSponsorshipCommandTestsBase
{
[SutProviderCustomize]
[OrganizationSponsorshipCustomize]
public class ValidateSponsorshipCommandTests : CancelSponsorshipCommandTestsBase
[Theory]
[BitAutoData]
public async Task ValidateSponsorshipAsync_NoSponsoredOrg_EarlyReturn(Guid sponsoredOrgId,
SutProvider<ValidateSponsorshipCommand> sutProvider)
{
[Theory]
[BitAutoData]
public async Task ValidateSponsorshipAsync_NoSponsoredOrg_EarlyReturn(Guid sponsoredOrgId,
SutProvider<ValidateSponsorshipCommand> sutProvider)
{
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(sponsoredOrgId).Returns((Organization)null);
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(sponsoredOrgId).Returns((Organization)null);
var result = await sutProvider.Sut.ValidateSponsorshipAsync(sponsoredOrgId);
var result = await sutProvider.Sut.ValidateSponsorshipAsync(sponsoredOrgId);
Assert.False(result);
await AssertDidNotRemoveSponsoredPaymentAsync(sutProvider);
await AssertDidNotDeleteSponsorshipAsync(sutProvider);
}
Assert.False(result);
await AssertDidNotRemoveSponsoredPaymentAsync(sutProvider);
await AssertDidNotDeleteSponsorshipAsync(sutProvider);
}
[Theory]
[BitAutoData]
public async Task ValidateSponsorshipAsync_NoExistingSponsorship_UpdatesStripePlan(Organization sponsoredOrg,
SutProvider<ValidateSponsorshipCommand> sutProvider)
{
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(sponsoredOrg.Id).Returns(sponsoredOrg);
[Theory]
[BitAutoData]
public async Task ValidateSponsorshipAsync_NoExistingSponsorship_UpdatesStripePlan(Organization sponsoredOrg,
SutProvider<ValidateSponsorshipCommand> sutProvider)
{
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(sponsoredOrg.Id).Returns(sponsoredOrg);
var result = await sutProvider.Sut.ValidateSponsorshipAsync(sponsoredOrg.Id);
var result = await sutProvider.Sut.ValidateSponsorshipAsync(sponsoredOrg.Id);
Assert.False(result);
await AssertRemovedSponsoredPaymentAsync(sponsoredOrg, null, sutProvider);
}
Assert.False(result);
await AssertRemovedSponsoredPaymentAsync(sponsoredOrg, null, sutProvider);
}
[Theory]
[BitAutoData]
public async Task ValidateSponsorshipAsync_SponsoringOrgDefault_UpdatesStripePlan(Organization sponsoredOrg,
OrganizationSponsorship existingSponsorship, SutProvider<ValidateSponsorshipCommand> sutProvider)
{
existingSponsorship.SponsoringOrganizationId = default;
[Theory]
[BitAutoData]
public async Task ValidateSponsorshipAsync_SponsoringOrgDefault_UpdatesStripePlan(Organization sponsoredOrg,
OrganizationSponsorship existingSponsorship, SutProvider<ValidateSponsorshipCommand> sutProvider)
{
existingSponsorship.SponsoringOrganizationId = default;
sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.GetBySponsoredOrganizationIdAsync(sponsoredOrg.Id).Returns(existingSponsorship);
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(sponsoredOrg.Id).Returns(sponsoredOrg);
sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.GetBySponsoredOrganizationIdAsync(sponsoredOrg.Id).Returns(existingSponsorship);
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(sponsoredOrg.Id).Returns(sponsoredOrg);
var result = await sutProvider.Sut.ValidateSponsorshipAsync(sponsoredOrg.Id);
var result = await sutProvider.Sut.ValidateSponsorshipAsync(sponsoredOrg.Id);
Assert.False(result);
await AssertRemovedSponsoredPaymentAsync(sponsoredOrg, existingSponsorship, sutProvider);
await AssertDeletedSponsorshipAsync(existingSponsorship, sutProvider);
}
Assert.False(result);
await AssertRemovedSponsoredPaymentAsync(sponsoredOrg, existingSponsorship, sutProvider);
await AssertDeletedSponsorshipAsync(existingSponsorship, sutProvider);
}
[Theory]
[BitAutoData]
public async Task ValidateSponsorshipAsync_SponsoringOrgUserDefault_UpdatesStripePlan(Organization sponsoredOrg,
OrganizationSponsorship existingSponsorship, SutProvider<ValidateSponsorshipCommand> sutProvider)
{
existingSponsorship.SponsoringOrganizationUserId = default;
[Theory]
[BitAutoData]
public async Task ValidateSponsorshipAsync_SponsoringOrgUserDefault_UpdatesStripePlan(Organization sponsoredOrg,
OrganizationSponsorship existingSponsorship, SutProvider<ValidateSponsorshipCommand> sutProvider)
{
existingSponsorship.SponsoringOrganizationUserId = default;
sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.GetBySponsoredOrganizationIdAsync(sponsoredOrg.Id).Returns(existingSponsorship);
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(sponsoredOrg.Id).Returns(sponsoredOrg);
sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.GetBySponsoredOrganizationIdAsync(sponsoredOrg.Id).Returns(existingSponsorship);
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(sponsoredOrg.Id).Returns(sponsoredOrg);
var result = await sutProvider.Sut.ValidateSponsorshipAsync(sponsoredOrg.Id);
var result = await sutProvider.Sut.ValidateSponsorshipAsync(sponsoredOrg.Id);
Assert.False(result);
await AssertRemovedSponsoredPaymentAsync(sponsoredOrg, existingSponsorship, sutProvider);
await AssertDeletedSponsorshipAsync(existingSponsorship, sutProvider);
}
Assert.False(result);
await AssertRemovedSponsoredPaymentAsync(sponsoredOrg, existingSponsorship, sutProvider);
await AssertDeletedSponsorshipAsync(existingSponsorship, sutProvider);
}
[Theory]
[BitAutoData]
public async Task ValidateSponsorshipAsync_SponsorshipTypeNull_UpdatesStripePlan(Organization sponsoredOrg,
OrganizationSponsorship existingSponsorship, SutProvider<ValidateSponsorshipCommand> sutProvider)
{
existingSponsorship.PlanSponsorshipType = null;
[Theory]
[BitAutoData]
public async Task ValidateSponsorshipAsync_SponsorshipTypeNull_UpdatesStripePlan(Organization sponsoredOrg,
OrganizationSponsorship existingSponsorship, SutProvider<ValidateSponsorshipCommand> sutProvider)
{
existingSponsorship.PlanSponsorshipType = null;
sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.GetBySponsoredOrganizationIdAsync(sponsoredOrg.Id).Returns(existingSponsorship);
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(sponsoredOrg.Id).Returns(sponsoredOrg);
sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.GetBySponsoredOrganizationIdAsync(sponsoredOrg.Id).Returns(existingSponsorship);
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(sponsoredOrg.Id).Returns(sponsoredOrg);
var result = await sutProvider.Sut.ValidateSponsorshipAsync(sponsoredOrg.Id);
var result = await sutProvider.Sut.ValidateSponsorshipAsync(sponsoredOrg.Id);
Assert.False(result);
await AssertRemovedSponsoredPaymentAsync(sponsoredOrg, existingSponsorship, sutProvider);
await AssertDeletedSponsorshipAsync(existingSponsorship, sutProvider);
}
Assert.False(result);
await AssertRemovedSponsoredPaymentAsync(sponsoredOrg, existingSponsorship, sutProvider);
await AssertDeletedSponsorshipAsync(existingSponsorship, sutProvider);
}
[Theory]
[BitAutoData]
public async Task ValidateSponsorshipAsync_SponsoringOrgNotFound_UpdatesStripePlan(Organization sponsoredOrg,
OrganizationSponsorship existingSponsorship, SutProvider<ValidateSponsorshipCommand> sutProvider)
{
sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.GetBySponsoredOrganizationIdAsync(sponsoredOrg.Id).Returns(existingSponsorship);
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(sponsoredOrg.Id).Returns(sponsoredOrg);
[Theory]
[BitAutoData]
public async Task ValidateSponsorshipAsync_SponsoringOrgNotFound_UpdatesStripePlan(Organization sponsoredOrg,
OrganizationSponsorship existingSponsorship, SutProvider<ValidateSponsorshipCommand> sutProvider)
{
sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.GetBySponsoredOrganizationIdAsync(sponsoredOrg.Id).Returns(existingSponsorship);
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(sponsoredOrg.Id).Returns(sponsoredOrg);
var result = await sutProvider.Sut.ValidateSponsorshipAsync(sponsoredOrg.Id);
var result = await sutProvider.Sut.ValidateSponsorshipAsync(sponsoredOrg.Id);
Assert.False(result);
await AssertRemovedSponsoredPaymentAsync(sponsoredOrg, existingSponsorship, sutProvider);
await AssertDeletedSponsorshipAsync(existingSponsorship, sutProvider);
}
Assert.False(result);
await AssertRemovedSponsoredPaymentAsync(sponsoredOrg, existingSponsorship, sutProvider);
await AssertDeletedSponsorshipAsync(existingSponsorship, sutProvider);
}
[Theory]
[BitMemberAutoData(nameof(NonEnterprisePlanTypes))]
public async Task ValidateSponsorshipAsync_SponsoringOrgNotEnterprise_UpdatesStripePlan(PlanType planType,
Organization sponsoredOrg, OrganizationSponsorship existingSponsorship, Organization sponsoringOrg,
SutProvider<ValidateSponsorshipCommand> sutProvider)
{
sponsoringOrg.PlanType = planType;
existingSponsorship.SponsoringOrganizationId = sponsoringOrg.Id;
[Theory]
[BitMemberAutoData(nameof(NonEnterprisePlanTypes))]
public async Task ValidateSponsorshipAsync_SponsoringOrgNotEnterprise_UpdatesStripePlan(PlanType planType,
Organization sponsoredOrg, OrganizationSponsorship existingSponsorship, Organization sponsoringOrg,
SutProvider<ValidateSponsorshipCommand> sutProvider)
{
sponsoringOrg.PlanType = planType;
existingSponsorship.SponsoringOrganizationId = sponsoringOrg.Id;
sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.GetBySponsoredOrganizationIdAsync(sponsoredOrg.Id).Returns(existingSponsorship);
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(sponsoredOrg.Id).Returns(sponsoredOrg);
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(sponsoringOrg.Id).Returns(sponsoringOrg);
sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.GetBySponsoredOrganizationIdAsync(sponsoredOrg.Id).Returns(existingSponsorship);
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(sponsoredOrg.Id).Returns(sponsoredOrg);
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(sponsoringOrg.Id).Returns(sponsoringOrg);
var result = await sutProvider.Sut.ValidateSponsorshipAsync(sponsoredOrg.Id);
var result = await sutProvider.Sut.ValidateSponsorshipAsync(sponsoredOrg.Id);
Assert.False(result);
await AssertRemovedSponsoredPaymentAsync(sponsoredOrg, existingSponsorship, sutProvider);
await AssertDeletedSponsorshipAsync(existingSponsorship, sutProvider);
}
Assert.False(result);
await AssertRemovedSponsoredPaymentAsync(sponsoredOrg, existingSponsorship, sutProvider);
await AssertDeletedSponsorshipAsync(existingSponsorship, sutProvider);
}
[Theory]
[BitMemberAutoData(nameof(EnterprisePlanTypes))]
public async Task ValidateSponsorshipAsync_SponsoringOrgDisabledLongerThanGrace_UpdatesStripePlan(PlanType planType,
Organization sponsoredOrg, OrganizationSponsorship existingSponsorship, Organization sponsoringOrg,
SutProvider<ValidateSponsorshipCommand> sutProvider)
{
sponsoringOrg.PlanType = planType;
sponsoringOrg.Enabled = false;
sponsoringOrg.ExpirationDate = DateTime.UtcNow.AddDays(-100);
existingSponsorship.SponsoringOrganizationId = sponsoringOrg.Id;
[Theory]
[BitMemberAutoData(nameof(EnterprisePlanTypes))]
public async Task ValidateSponsorshipAsync_SponsoringOrgDisabledLongerThanGrace_UpdatesStripePlan(PlanType planType,
Organization sponsoredOrg, OrganizationSponsorship existingSponsorship, Organization sponsoringOrg,
SutProvider<ValidateSponsorshipCommand> sutProvider)
{
sponsoringOrg.PlanType = planType;
sponsoringOrg.Enabled = false;
sponsoringOrg.ExpirationDate = DateTime.UtcNow.AddDays(-100);
existingSponsorship.SponsoringOrganizationId = sponsoringOrg.Id;
sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.GetBySponsoredOrganizationIdAsync(sponsoredOrg.Id).Returns(existingSponsorship);
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(sponsoredOrg.Id).Returns(sponsoredOrg);
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(sponsoringOrg.Id).Returns(sponsoringOrg);
sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.GetBySponsoredOrganizationIdAsync(sponsoredOrg.Id).Returns(existingSponsorship);
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(sponsoredOrg.Id).Returns(sponsoredOrg);
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(sponsoringOrg.Id).Returns(sponsoringOrg);
var result = await sutProvider.Sut.ValidateSponsorshipAsync(sponsoredOrg.Id);
var result = await sutProvider.Sut.ValidateSponsorshipAsync(sponsoredOrg.Id);
Assert.False(result);
await AssertRemovedSponsoredPaymentAsync(sponsoredOrg, existingSponsorship, sutProvider);
await AssertDeletedSponsorshipAsync(existingSponsorship, sutProvider);
}
Assert.False(result);
await AssertRemovedSponsoredPaymentAsync(sponsoredOrg, existingSponsorship, sutProvider);
await AssertDeletedSponsorshipAsync(existingSponsorship, sutProvider);
}
[Theory]
[OrganizationSponsorshipCustomize(ToDelete = true)]
[BitMemberAutoData(nameof(EnterprisePlanTypes))]
public async Task ValidateSponsorshipAsync_ToDeleteSponsorship_IsInvalid(PlanType planType,
Organization sponsoredOrg, OrganizationSponsorship sponsorship, Organization sponsoringOrg,
SutProvider<ValidateSponsorshipCommand> sutProvider)
{
sponsoringOrg.PlanType = planType;
sponsoringOrg.Enabled = true;
sponsorship.SponsoringOrganizationId = sponsoringOrg.Id;
[Theory]
[OrganizationSponsorshipCustomize(ToDelete = true)]
[BitMemberAutoData(nameof(EnterprisePlanTypes))]
public async Task ValidateSponsorshipAsync_ToDeleteSponsorship_IsInvalid(PlanType planType,
Organization sponsoredOrg, OrganizationSponsorship sponsorship, Organization sponsoringOrg,
SutProvider<ValidateSponsorshipCommand> sutProvider)
{
sponsoringOrg.PlanType = planType;
sponsoringOrg.Enabled = true;
sponsorship.SponsoringOrganizationId = sponsoringOrg.Id;
sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.GetBySponsoredOrganizationIdAsync(sponsoredOrg.Id).Returns(sponsorship);
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(sponsoredOrg.Id).Returns(sponsoredOrg);
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(sponsoringOrg.Id).Returns(sponsoringOrg);
sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.GetBySponsoredOrganizationIdAsync(sponsoredOrg.Id).Returns(sponsorship);
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(sponsoredOrg.Id).Returns(sponsoredOrg);
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(sponsoringOrg.Id).Returns(sponsoringOrg);
var result = await sutProvider.Sut.ValidateSponsorshipAsync(sponsoredOrg.Id);
var result = await sutProvider.Sut.ValidateSponsorshipAsync(sponsoredOrg.Id);
Assert.False(result);
Assert.False(result);
await AssertRemovedSponsoredPaymentAsync(sponsoredOrg, sponsorship, sutProvider);
await AssertDeletedSponsorshipAsync(sponsorship, sutProvider);
}
await AssertRemovedSponsoredPaymentAsync(sponsoredOrg, sponsorship, sutProvider);
await AssertDeletedSponsorshipAsync(sponsorship, sutProvider);
}
[Theory]
[BitMemberAutoData(nameof(EnterprisePlanTypes))]
public async Task ValidateSponsorshipAsync_SponsoringOrgDisabledUnknownTime_UpdatesStripePlan(PlanType planType,
Organization sponsoredOrg, OrganizationSponsorship existingSponsorship, Organization sponsoringOrg,
SutProvider<ValidateSponsorshipCommand> sutProvider)
{
sponsoringOrg.PlanType = planType;
sponsoringOrg.Enabled = false;
sponsoringOrg.ExpirationDate = null;
existingSponsorship.SponsoringOrganizationId = sponsoringOrg.Id;
[Theory]
[BitMemberAutoData(nameof(EnterprisePlanTypes))]
public async Task ValidateSponsorshipAsync_SponsoringOrgDisabledUnknownTime_UpdatesStripePlan(PlanType planType,
Organization sponsoredOrg, OrganizationSponsorship existingSponsorship, Organization sponsoringOrg,
SutProvider<ValidateSponsorshipCommand> sutProvider)
{
sponsoringOrg.PlanType = planType;
sponsoringOrg.Enabled = false;
sponsoringOrg.ExpirationDate = null;
existingSponsorship.SponsoringOrganizationId = sponsoringOrg.Id;
sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.GetBySponsoredOrganizationIdAsync(sponsoredOrg.Id).Returns(existingSponsorship);
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(sponsoredOrg.Id).Returns(sponsoredOrg);
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(sponsoringOrg.Id).Returns(sponsoringOrg);
sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.GetBySponsoredOrganizationIdAsync(sponsoredOrg.Id).Returns(existingSponsorship);
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(sponsoredOrg.Id).Returns(sponsoredOrg);
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(sponsoringOrg.Id).Returns(sponsoringOrg);
var result = await sutProvider.Sut.ValidateSponsorshipAsync(sponsoredOrg.Id);
var result = await sutProvider.Sut.ValidateSponsorshipAsync(sponsoredOrg.Id);
Assert.False(result);
await AssertRemovedSponsoredPaymentAsync(sponsoredOrg, existingSponsorship, sutProvider);
await AssertRemovedSponsorshipAsync(existingSponsorship, sutProvider);
}
Assert.False(result);
await AssertRemovedSponsoredPaymentAsync(sponsoredOrg, existingSponsorship, sutProvider);
await AssertRemovedSponsorshipAsync(existingSponsorship, sutProvider);
}
[Theory]
[BitMemberAutoData(nameof(EnterprisePlanTypes))]
public async Task ValidateSponsorshipAsync_SponsoringOrgDisabledLessThanGrace_Valid(PlanType planType,
Organization sponsoredOrg, OrganizationSponsorship existingSponsorship, Organization sponsoringOrg,
SutProvider<ValidateSponsorshipCommand> sutProvider)
{
sponsoringOrg.PlanType = planType;
sponsoringOrg.Enabled = true;
sponsoringOrg.ExpirationDate = DateTime.UtcNow.AddDays(-1);
existingSponsorship.SponsoringOrganizationId = sponsoringOrg.Id;
[Theory]
[BitMemberAutoData(nameof(EnterprisePlanTypes))]
public async Task ValidateSponsorshipAsync_SponsoringOrgDisabledLessThanGrace_Valid(PlanType planType,
Organization sponsoredOrg, OrganizationSponsorship existingSponsorship, Organization sponsoringOrg,
SutProvider<ValidateSponsorshipCommand> sutProvider)
{
sponsoringOrg.PlanType = planType;
sponsoringOrg.Enabled = true;
sponsoringOrg.ExpirationDate = DateTime.UtcNow.AddDays(-1);
existingSponsorship.SponsoringOrganizationId = sponsoringOrg.Id;
sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.GetBySponsoredOrganizationIdAsync(sponsoredOrg.Id).Returns(existingSponsorship);
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(sponsoredOrg.Id).Returns(sponsoredOrg);
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(sponsoringOrg.Id).Returns(sponsoringOrg);
sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.GetBySponsoredOrganizationIdAsync(sponsoredOrg.Id).Returns(existingSponsorship);
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(sponsoredOrg.Id).Returns(sponsoredOrg);
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(sponsoringOrg.Id).Returns(sponsoringOrg);
var result = await sutProvider.Sut.ValidateSponsorshipAsync(sponsoredOrg.Id);
var result = await sutProvider.Sut.ValidateSponsorshipAsync(sponsoredOrg.Id);
Assert.True(result);
Assert.True(result);
await AssertDidNotRemoveSponsoredPaymentAsync(sutProvider);
await AssertDidNotRemoveSponsorshipAsync(sutProvider);
}
await AssertDidNotRemoveSponsoredPaymentAsync(sutProvider);
await AssertDidNotRemoveSponsorshipAsync(sutProvider);
}
[Theory]
[BitMemberAutoData(nameof(EnterprisePlanTypes))]
public async Task ValidateSponsorshipAsync_Valid(PlanType planType,
Organization sponsoredOrg, OrganizationSponsorship existingSponsorship, Organization sponsoringOrg,
SutProvider<ValidateSponsorshipCommand> sutProvider)
{
sponsoringOrg.PlanType = planType;
sponsoringOrg.Enabled = true;
existingSponsorship.SponsoringOrganizationId = sponsoringOrg.Id;
[Theory]
[BitMemberAutoData(nameof(EnterprisePlanTypes))]
public async Task ValidateSponsorshipAsync_Valid(PlanType planType,
Organization sponsoredOrg, OrganizationSponsorship existingSponsorship, Organization sponsoringOrg,
SutProvider<ValidateSponsorshipCommand> sutProvider)
{
sponsoringOrg.PlanType = planType;
sponsoringOrg.Enabled = true;
existingSponsorship.SponsoringOrganizationId = sponsoringOrg.Id;
sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.GetBySponsoredOrganizationIdAsync(sponsoredOrg.Id).Returns(existingSponsorship);
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(sponsoredOrg.Id).Returns(sponsoredOrg);
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(sponsoringOrg.Id).Returns(sponsoringOrg);
sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.GetBySponsoredOrganizationIdAsync(sponsoredOrg.Id).Returns(existingSponsorship);
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(sponsoredOrg.Id).Returns(sponsoredOrg);
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(sponsoringOrg.Id).Returns(sponsoringOrg);
var result = await sutProvider.Sut.ValidateSponsorshipAsync(sponsoredOrg.Id);
var result = await sutProvider.Sut.ValidateSponsorshipAsync(sponsoredOrg.Id);
Assert.True(result);
Assert.True(result);
await AssertDidNotRemoveSponsoredPaymentAsync(sutProvider);
await AssertDidNotDeleteSponsorshipAsync(sutProvider);
}
await AssertDidNotRemoveSponsoredPaymentAsync(sutProvider);
await AssertDidNotDeleteSponsorshipAsync(sutProvider);
}
}

View File

@ -13,167 +13,166 @@ using NSubstitute.ExceptionExtensions;
using NSubstitute.ReturnsExtensions;
using Xunit;
namespace Bit.Core.Test.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise
namespace Bit.Core.Test.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise;
[SutProviderCustomize]
public class CreateSponsorshipCommandTests : FamiliesForEnterpriseTestsBase
{
[SutProviderCustomize]
public class CreateSponsorshipCommandTests : FamiliesForEnterpriseTestsBase
private bool SponsorshipValidator(OrganizationSponsorship sponsorship, OrganizationSponsorship expectedSponsorship)
{
private bool SponsorshipValidator(OrganizationSponsorship sponsorship, OrganizationSponsorship expectedSponsorship)
try
{
try
{
AssertHelper.AssertPropertyEqual(sponsorship, expectedSponsorship, nameof(OrganizationSponsorship.Id));
return true;
}
catch
{
return false;
}
AssertHelper.AssertPropertyEqual(sponsorship, expectedSponsorship, nameof(OrganizationSponsorship.Id));
return true;
}
[Theory, BitAutoData]
public async Task CreateSponsorship_OfferedToNotFound_ThrowsBadRequest(OrganizationUser orgUser, SutProvider<CreateSponsorshipCommand> sutProvider)
catch
{
sutProvider.GetDependency<IUserService>().GetUserByIdAsync(orgUser.UserId.Value).ReturnsNull();
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
sutProvider.Sut.CreateSponsorshipAsync(null, orgUser, PlanSponsorshipType.FamiliesForEnterprise, default, default));
Assert.Contains("Cannot offer a Families Organization Sponsorship to yourself. Choose a different email.", exception.Message);
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>().DidNotReceiveWithAnyArgs()
.CreateAsync(default);
}
[Theory, BitAutoData]
public async Task CreateSponsorship_OfferedToSelf_ThrowsBadRequest(OrganizationUser orgUser, string sponsoredEmail, User user, SutProvider<CreateSponsorshipCommand> sutProvider)
{
user.Email = sponsoredEmail;
sutProvider.GetDependency<IUserService>().GetUserByIdAsync(orgUser.UserId.Value).Returns(user);
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
sutProvider.Sut.CreateSponsorshipAsync(null, orgUser, PlanSponsorshipType.FamiliesForEnterprise, sponsoredEmail, default));
Assert.Contains("Cannot offer a Families Organization Sponsorship to yourself. Choose a different email.", exception.Message);
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>().DidNotReceiveWithAnyArgs()
.CreateAsync(default);
}
[Theory, BitMemberAutoData(nameof(NonEnterprisePlanTypes))]
public async Task CreateSponsorship_BadSponsoringOrgPlan_ThrowsBadRequest(PlanType sponsoringOrgPlan,
Organization org, OrganizationUser orgUser, User user, SutProvider<CreateSponsorshipCommand> sutProvider)
{
org.PlanType = sponsoringOrgPlan;
orgUser.Status = OrganizationUserStatusType.Confirmed;
sutProvider.GetDependency<IUserService>().GetUserByIdAsync(orgUser.UserId.Value).Returns(user);
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
sutProvider.Sut.CreateSponsorshipAsync(org, orgUser, PlanSponsorshipType.FamiliesForEnterprise, default, default));
Assert.Contains("Specified Organization cannot sponsor other organizations.", exception.Message);
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>().DidNotReceiveWithAnyArgs()
.CreateAsync(default);
}
[Theory]
[BitMemberAutoData(nameof(NonConfirmedOrganizationUsersStatuses))]
public async Task CreateSponsorship_BadSponsoringUserStatus_ThrowsBadRequest(
OrganizationUserStatusType statusType, Organization org, OrganizationUser orgUser, User user,
SutProvider<CreateSponsorshipCommand> sutProvider)
{
org.PlanType = PlanType.EnterpriseAnnually;
orgUser.Status = statusType;
sutProvider.GetDependency<IUserService>().GetUserByIdAsync(orgUser.UserId.Value).Returns(user);
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
sutProvider.Sut.CreateSponsorshipAsync(org, orgUser, PlanSponsorshipType.FamiliesForEnterprise, default, default));
Assert.Contains("Only confirmed users can sponsor other organizations.", exception.Message);
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>().DidNotReceiveWithAnyArgs()
.CreateAsync(default);
}
[Theory]
[OrganizationSponsorshipCustomize]
[BitAutoData]
public async Task CreateSponsorship_AlreadySponsoring_Throws(Organization org,
OrganizationUser orgUser, User user, OrganizationSponsorship sponsorship,
SutProvider<CreateSponsorshipCommand> sutProvider)
{
org.PlanType = PlanType.EnterpriseAnnually;
orgUser.Status = OrganizationUserStatusType.Confirmed;
sutProvider.GetDependency<IUserService>().GetUserByIdAsync(orgUser.UserId.Value).Returns(user);
sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.GetBySponsoringOrganizationUserIdAsync(orgUser.Id).Returns(sponsorship);
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
sutProvider.Sut.CreateSponsorshipAsync(org, orgUser, sponsorship.PlanSponsorshipType.Value, default, default));
Assert.Contains("Can only sponsor one organization per Organization User.", exception.Message);
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>().DidNotReceiveWithAnyArgs()
.CreateAsync(default);
}
[Theory]
[BitAutoData]
public async Task CreateSponsorship_CreatesSponsorship(Organization sponsoringOrg, OrganizationUser sponsoringOrgUser, User user,
string sponsoredEmail, string friendlyName, Guid sponsorshipId, SutProvider<CreateSponsorshipCommand> sutProvider)
{
sponsoringOrg.PlanType = PlanType.EnterpriseAnnually;
sponsoringOrgUser.Status = OrganizationUserStatusType.Confirmed;
sutProvider.GetDependency<IUserService>().GetUserByIdAsync(sponsoringOrgUser.UserId.Value).Returns(user);
sutProvider.GetDependency<IOrganizationSponsorshipRepository>().WhenForAnyArgs(x => x.UpsertAsync(default)).Do(callInfo =>
{
var sponsorship = callInfo.Arg<OrganizationSponsorship>();
sponsorship.Id = sponsorshipId;
});
await sutProvider.Sut.CreateSponsorshipAsync(sponsoringOrg, sponsoringOrgUser,
PlanSponsorshipType.FamiliesForEnterprise, sponsoredEmail, friendlyName);
var expectedSponsorship = new OrganizationSponsorship
{
Id = sponsorshipId,
SponsoringOrganizationId = sponsoringOrg.Id,
SponsoringOrganizationUserId = sponsoringOrgUser.Id,
FriendlyName = friendlyName,
OfferedToEmail = sponsoredEmail,
PlanSponsorshipType = PlanSponsorshipType.FamiliesForEnterprise,
};
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>().Received(1)
.UpsertAsync(Arg.Is<OrganizationSponsorship>(s => SponsorshipValidator(s, expectedSponsorship)));
}
[Theory]
[BitAutoData]
public async Task CreateSponsorship_CreateSponsorshipThrows_RevertsDatabase(Organization sponsoringOrg, OrganizationUser sponsoringOrgUser, User user,
string sponsoredEmail, string friendlyName, SutProvider<CreateSponsorshipCommand> sutProvider)
{
sponsoringOrg.PlanType = PlanType.EnterpriseAnnually;
sponsoringOrgUser.Status = OrganizationUserStatusType.Confirmed;
var expectedException = new Exception();
OrganizationSponsorship createdSponsorship = null;
sutProvider.GetDependency<IUserService>().GetUserByIdAsync(sponsoringOrgUser.UserId.Value).Returns(user);
sutProvider.GetDependency<IOrganizationSponsorshipRepository>().UpsertAsync(default).ThrowsForAnyArgs(callInfo =>
{
createdSponsorship = callInfo.ArgAt<OrganizationSponsorship>(0);
createdSponsorship.Id = Guid.NewGuid();
return expectedException;
});
var actualException = await Assert.ThrowsAsync<Exception>(() =>
sutProvider.Sut.CreateSponsorshipAsync(sponsoringOrg, sponsoringOrgUser,
PlanSponsorshipType.FamiliesForEnterprise, sponsoredEmail, friendlyName));
Assert.Same(expectedException, actualException);
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>().Received(1)
.DeleteAsync(createdSponsorship);
return false;
}
}
[Theory, BitAutoData]
public async Task CreateSponsorship_OfferedToNotFound_ThrowsBadRequest(OrganizationUser orgUser, SutProvider<CreateSponsorshipCommand> sutProvider)
{
sutProvider.GetDependency<IUserService>().GetUserByIdAsync(orgUser.UserId.Value).ReturnsNull();
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
sutProvider.Sut.CreateSponsorshipAsync(null, orgUser, PlanSponsorshipType.FamiliesForEnterprise, default, default));
Assert.Contains("Cannot offer a Families Organization Sponsorship to yourself. Choose a different email.", exception.Message);
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>().DidNotReceiveWithAnyArgs()
.CreateAsync(default);
}
[Theory, BitAutoData]
public async Task CreateSponsorship_OfferedToSelf_ThrowsBadRequest(OrganizationUser orgUser, string sponsoredEmail, User user, SutProvider<CreateSponsorshipCommand> sutProvider)
{
user.Email = sponsoredEmail;
sutProvider.GetDependency<IUserService>().GetUserByIdAsync(orgUser.UserId.Value).Returns(user);
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
sutProvider.Sut.CreateSponsorshipAsync(null, orgUser, PlanSponsorshipType.FamiliesForEnterprise, sponsoredEmail, default));
Assert.Contains("Cannot offer a Families Organization Sponsorship to yourself. Choose a different email.", exception.Message);
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>().DidNotReceiveWithAnyArgs()
.CreateAsync(default);
}
[Theory, BitMemberAutoData(nameof(NonEnterprisePlanTypes))]
public async Task CreateSponsorship_BadSponsoringOrgPlan_ThrowsBadRequest(PlanType sponsoringOrgPlan,
Organization org, OrganizationUser orgUser, User user, SutProvider<CreateSponsorshipCommand> sutProvider)
{
org.PlanType = sponsoringOrgPlan;
orgUser.Status = OrganizationUserStatusType.Confirmed;
sutProvider.GetDependency<IUserService>().GetUserByIdAsync(orgUser.UserId.Value).Returns(user);
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
sutProvider.Sut.CreateSponsorshipAsync(org, orgUser, PlanSponsorshipType.FamiliesForEnterprise, default, default));
Assert.Contains("Specified Organization cannot sponsor other organizations.", exception.Message);
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>().DidNotReceiveWithAnyArgs()
.CreateAsync(default);
}
[Theory]
[BitMemberAutoData(nameof(NonConfirmedOrganizationUsersStatuses))]
public async Task CreateSponsorship_BadSponsoringUserStatus_ThrowsBadRequest(
OrganizationUserStatusType statusType, Organization org, OrganizationUser orgUser, User user,
SutProvider<CreateSponsorshipCommand> sutProvider)
{
org.PlanType = PlanType.EnterpriseAnnually;
orgUser.Status = statusType;
sutProvider.GetDependency<IUserService>().GetUserByIdAsync(orgUser.UserId.Value).Returns(user);
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
sutProvider.Sut.CreateSponsorshipAsync(org, orgUser, PlanSponsorshipType.FamiliesForEnterprise, default, default));
Assert.Contains("Only confirmed users can sponsor other organizations.", exception.Message);
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>().DidNotReceiveWithAnyArgs()
.CreateAsync(default);
}
[Theory]
[OrganizationSponsorshipCustomize]
[BitAutoData]
public async Task CreateSponsorship_AlreadySponsoring_Throws(Organization org,
OrganizationUser orgUser, User user, OrganizationSponsorship sponsorship,
SutProvider<CreateSponsorshipCommand> sutProvider)
{
org.PlanType = PlanType.EnterpriseAnnually;
orgUser.Status = OrganizationUserStatusType.Confirmed;
sutProvider.GetDependency<IUserService>().GetUserByIdAsync(orgUser.UserId.Value).Returns(user);
sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.GetBySponsoringOrganizationUserIdAsync(orgUser.Id).Returns(sponsorship);
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
sutProvider.Sut.CreateSponsorshipAsync(org, orgUser, sponsorship.PlanSponsorshipType.Value, default, default));
Assert.Contains("Can only sponsor one organization per Organization User.", exception.Message);
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>().DidNotReceiveWithAnyArgs()
.CreateAsync(default);
}
[Theory]
[BitAutoData]
public async Task CreateSponsorship_CreatesSponsorship(Organization sponsoringOrg, OrganizationUser sponsoringOrgUser, User user,
string sponsoredEmail, string friendlyName, Guid sponsorshipId, SutProvider<CreateSponsorshipCommand> sutProvider)
{
sponsoringOrg.PlanType = PlanType.EnterpriseAnnually;
sponsoringOrgUser.Status = OrganizationUserStatusType.Confirmed;
sutProvider.GetDependency<IUserService>().GetUserByIdAsync(sponsoringOrgUser.UserId.Value).Returns(user);
sutProvider.GetDependency<IOrganizationSponsorshipRepository>().WhenForAnyArgs(x => x.UpsertAsync(default)).Do(callInfo =>
{
var sponsorship = callInfo.Arg<OrganizationSponsorship>();
sponsorship.Id = sponsorshipId;
});
await sutProvider.Sut.CreateSponsorshipAsync(sponsoringOrg, sponsoringOrgUser,
PlanSponsorshipType.FamiliesForEnterprise, sponsoredEmail, friendlyName);
var expectedSponsorship = new OrganizationSponsorship
{
Id = sponsorshipId,
SponsoringOrganizationId = sponsoringOrg.Id,
SponsoringOrganizationUserId = sponsoringOrgUser.Id,
FriendlyName = friendlyName,
OfferedToEmail = sponsoredEmail,
PlanSponsorshipType = PlanSponsorshipType.FamiliesForEnterprise,
};
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>().Received(1)
.UpsertAsync(Arg.Is<OrganizationSponsorship>(s => SponsorshipValidator(s, expectedSponsorship)));
}
[Theory]
[BitAutoData]
public async Task CreateSponsorship_CreateSponsorshipThrows_RevertsDatabase(Organization sponsoringOrg, OrganizationUser sponsoringOrgUser, User user,
string sponsoredEmail, string friendlyName, SutProvider<CreateSponsorshipCommand> sutProvider)
{
sponsoringOrg.PlanType = PlanType.EnterpriseAnnually;
sponsoringOrgUser.Status = OrganizationUserStatusType.Confirmed;
var expectedException = new Exception();
OrganizationSponsorship createdSponsorship = null;
sutProvider.GetDependency<IUserService>().GetUserByIdAsync(sponsoringOrgUser.UserId.Value).Returns(user);
sutProvider.GetDependency<IOrganizationSponsorshipRepository>().UpsertAsync(default).ThrowsForAnyArgs(callInfo =>
{
createdSponsorship = callInfo.ArgAt<OrganizationSponsorship>(0);
createdSponsorship.Id = Guid.NewGuid();
return expectedException;
});
var actualException = await Assert.ThrowsAsync<Exception>(() =>
sutProvider.Sut.CreateSponsorshipAsync(sponsoringOrg, sponsoringOrgUser,
PlanSponsorshipType.FamiliesForEnterprise, sponsoredEmail, friendlyName));
Assert.Same(expectedException, actualException);
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>().Received(1)
.DeleteAsync(createdSponsorship);
}
}

View File

@ -1,25 +1,24 @@
using Bit.Core.Enums;
using Bit.Core.Utilities;
namespace Bit.Core.Test.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise
namespace Bit.Core.Test.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise;
public abstract class FamiliesForEnterpriseTestsBase
{
public abstract class FamiliesForEnterpriseTestsBase
{
public static IEnumerable<object[]> EnterprisePlanTypes =>
Enum.GetValues<PlanType>().Where(p => StaticStore.GetPlan(p).Product == ProductType.Enterprise).Select(p => new object[] { p });
public static IEnumerable<object[]> EnterprisePlanTypes =>
Enum.GetValues<PlanType>().Where(p => StaticStore.GetPlan(p).Product == ProductType.Enterprise).Select(p => new object[] { p });
public static IEnumerable<object[]> NonEnterprisePlanTypes =>
Enum.GetValues<PlanType>().Where(p => StaticStore.GetPlan(p).Product != ProductType.Enterprise).Select(p => new object[] { p });
public static IEnumerable<object[]> NonEnterprisePlanTypes =>
Enum.GetValues<PlanType>().Where(p => StaticStore.GetPlan(p).Product != ProductType.Enterprise).Select(p => new object[] { p });
public static IEnumerable<object[]> FamiliesPlanTypes =>
Enum.GetValues<PlanType>().Where(p => StaticStore.GetPlan(p).Product == ProductType.Families).Select(p => new object[] { p });
public static IEnumerable<object[]> FamiliesPlanTypes =>
Enum.GetValues<PlanType>().Where(p => StaticStore.GetPlan(p).Product == ProductType.Families).Select(p => new object[] { p });
public static IEnumerable<object[]> NonFamiliesPlanTypes =>
Enum.GetValues<PlanType>().Where(p => StaticStore.GetPlan(p).Product != ProductType.Families).Select(p => new object[] { p });
public static IEnumerable<object[]> NonFamiliesPlanTypes =>
Enum.GetValues<PlanType>().Where(p => StaticStore.GetPlan(p).Product != ProductType.Families).Select(p => new object[] { p });
public static IEnumerable<object[]> NonConfirmedOrganizationUsersStatuses =>
Enum.GetValues<OrganizationUserStatusType>()
.Where(s => s != OrganizationUserStatusType.Confirmed)
.Select(s => new object[] { s });
}
public static IEnumerable<object[]> NonConfirmedOrganizationUsersStatuses =>
Enum.GetValues<OrganizationUserStatusType>()
.Where(s => s != OrganizationUserStatusType.Confirmed)
.Select(s => new object[] { s });
}

View File

@ -6,48 +6,47 @@ using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
using Xunit;
namespace Bit.Core.Test.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.SelfHosted
namespace Bit.Core.Test.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.SelfHosted;
[SutProviderCustomize]
[OrganizationSponsorshipCustomize]
public class SelfHostedRevokeSponsorshipCommandTests : CancelSponsorshipCommandTestsBase
{
[SutProviderCustomize]
[OrganizationSponsorshipCustomize]
public class SelfHostedRevokeSponsorshipCommandTests : CancelSponsorshipCommandTestsBase
[Theory]
[BitAutoData]
public async Task RevokeSponsorship_NoExistingSponsorship_ThrowsBadRequest(
SutProvider<SelfHostedRevokeSponsorshipCommand> sutProvider)
{
[Theory]
[BitAutoData]
public async Task RevokeSponsorship_NoExistingSponsorship_ThrowsBadRequest(
SutProvider<SelfHostedRevokeSponsorshipCommand> sutProvider)
{
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
sutProvider.Sut.RevokeSponsorshipAsync(null));
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
sutProvider.Sut.RevokeSponsorshipAsync(null));
Assert.Contains("You are not currently sponsoring an organization.", exception.Message);
await AssertDidNotDeleteSponsorshipAsync(sutProvider);
await AssertDidNotUpdateSponsorshipAsync(sutProvider);
}
Assert.Contains("You are not currently sponsoring an organization.", exception.Message);
await AssertDidNotDeleteSponsorshipAsync(sutProvider);
await AssertDidNotUpdateSponsorshipAsync(sutProvider);
}
[Theory]
[BitAutoData]
public async Task RevokeSponsorship_SponsorshipNotSynced_DeletesSponsorship(OrganizationSponsorship sponsorship,
SutProvider<SelfHostedRevokeSponsorshipCommand> sutProvider)
{
sponsorship.LastSyncDate = null;
[Theory]
[BitAutoData]
public async Task RevokeSponsorship_SponsorshipNotSynced_DeletesSponsorship(OrganizationSponsorship sponsorship,
SutProvider<SelfHostedRevokeSponsorshipCommand> sutProvider)
{
sponsorship.LastSyncDate = null;
await sutProvider.Sut.RevokeSponsorshipAsync(sponsorship);
await AssertDeletedSponsorshipAsync(sponsorship, sutProvider);
}
await sutProvider.Sut.RevokeSponsorshipAsync(sponsorship);
await AssertDeletedSponsorshipAsync(sponsorship, sutProvider);
}
[Theory]
[BitAutoData]
public async Task RevokeSponsorship_SponsorshipSynced_MarksForDeletion(OrganizationSponsorship sponsorship,
SutProvider<SelfHostedRevokeSponsorshipCommand> sutProvider)
{
sponsorship.LastSyncDate = DateTime.UtcNow;
[Theory]
[BitAutoData]
public async Task RevokeSponsorship_SponsorshipSynced_MarksForDeletion(OrganizationSponsorship sponsorship,
SutProvider<SelfHostedRevokeSponsorshipCommand> sutProvider)
{
sponsorship.LastSyncDate = DateTime.UtcNow;
await sutProvider.Sut.RevokeSponsorshipAsync(sponsorship);
await sutProvider.Sut.RevokeSponsorshipAsync(sponsorship);
Assert.True(sponsorship.ToDelete);
await AssertUpdatedSponsorshipAsync(sponsorship, sutProvider);
await AssertDidNotDeleteSponsorshipAsync(sutProvider);
}
Assert.True(sponsorship.ToDelete);
await AssertUpdatedSponsorshipAsync(sponsorship, sutProvider);
await AssertDidNotDeleteSponsorshipAsync(sutProvider);
}
}

View File

@ -15,174 +15,172 @@ using NSubstitute;
using RichardSzalay.MockHttp;
using Xunit;
namespace Bit.Core.Test.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.SelfHosted
namespace Bit.Core.Test.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.SelfHosted;
public class SelfHostedSyncSponsorshipsCommandTests : FamiliesForEnterpriseTestsBase
{
public class SelfHostedSyncSponsorshipsCommandTests : FamiliesForEnterpriseTestsBase
public static SutProvider<SelfHostedSyncSponsorshipsCommand> GetSutProvider(bool enableCloudCommunication = true, string identityResponse = null, string apiResponse = null)
{
var fixture = new Fixture().WithAutoNSubstitutionsAutoPopulatedProperties();
fixture.AddMockHttp();
public static SutProvider<SelfHostedSyncSponsorshipsCommand> GetSutProvider(bool enableCloudCommunication = true, string identityResponse = null, string apiResponse = null)
var settings = fixture.Create<IGlobalSettings>();
settings.SelfHosted = true;
settings.EnableCloudCommunication = enableCloudCommunication;
var apiUri = fixture.Create<Uri>();
var identityUri = fixture.Create<Uri>();
settings.Installation.ApiUri.Returns(apiUri.ToString());
settings.Installation.IdentityUri.Returns(identityUri.ToString());
var apiHandler = new MockHttpMessageHandler();
var identityHandler = new MockHttpMessageHandler();
var syncUri = string.Concat(apiUri, "organization/sponsorship/sync");
var tokenUri = string.Concat(identityUri, "connect/token");
apiHandler.When(HttpMethod.Post, syncUri)
.Respond("application/json", apiResponse);
identityHandler.When(HttpMethod.Post, tokenUri)
.Respond("application/json", identityResponse ?? "{\"access_token\":\"string\",\"expires_in\":3600,\"token_type\":\"Bearer\",\"scope\":\"string\"}");
var apiHttp = apiHandler.ToHttpClient();
var identityHttp = identityHandler.ToHttpClient();
var mockHttpClientFactory = Substitute.For<IHttpClientFactory>();
mockHttpClientFactory.CreateClient(Arg.Is("client")).Returns(apiHttp);
mockHttpClientFactory.CreateClient(Arg.Is("identity")).Returns(identityHttp);
return new SutProvider<SelfHostedSyncSponsorshipsCommand>(fixture)
.SetDependency(settings)
.SetDependency(mockHttpClientFactory)
.Create();
}
[Theory]
[BitAutoData]
public async Task SyncOrganization_BillingSyncKeyDisabled_ThrowsBadRequest(
Guid cloudOrganizationId, OrganizationConnection billingSyncConnection)
{
var sutProvider = GetSutProvider();
billingSyncConnection.Enabled = false;
billingSyncConnection.SetConfig(new BillingSyncConfig
{
var fixture = new Fixture().WithAutoNSubstitutionsAutoPopulatedProperties();
fixture.AddMockHttp();
BillingSyncKey = "okslkcslkjf"
});
var settings = fixture.Create<IGlobalSettings>();
settings.SelfHosted = true;
settings.EnableCloudCommunication = enableCloudCommunication;
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
sutProvider.Sut.SyncOrganization(billingSyncConnection.OrganizationId, cloudOrganizationId, billingSyncConnection));
var apiUri = fixture.Create<Uri>();
var identityUri = fixture.Create<Uri>();
settings.Installation.ApiUri.Returns(apiUri.ToString());
settings.Installation.IdentityUri.Returns(identityUri.ToString());
Assert.Contains($"Billing Sync Key disabled", exception.Message);
var apiHandler = new MockHttpMessageHandler();
var identityHandler = new MockHttpMessageHandler();
var syncUri = string.Concat(apiUri, "organization/sponsorship/sync");
var tokenUri = string.Concat(identityUri, "connect/token");
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.DidNotReceiveWithAnyArgs()
.DeleteManyAsync(default);
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.DidNotReceiveWithAnyArgs()
.UpsertManyAsync(default);
}
apiHandler.When(HttpMethod.Post, syncUri)
.Respond("application/json", apiResponse);
identityHandler.When(HttpMethod.Post, tokenUri)
.Respond("application/json", identityResponse ?? "{\"access_token\":\"string\",\"expires_in\":3600,\"token_type\":\"Bearer\",\"scope\":\"string\"}");
[Theory]
[BitAutoData]
public async Task SyncOrganization_BillingSyncKeyEmpty_ThrowsBadRequest(
Guid cloudOrganizationId, OrganizationConnection billingSyncConnection)
{
var sutProvider = GetSutProvider();
billingSyncConnection.Config = "";
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
sutProvider.Sut.SyncOrganization(billingSyncConnection.OrganizationId, cloudOrganizationId, billingSyncConnection));
var apiHttp = apiHandler.ToHttpClient();
var identityHttp = identityHandler.ToHttpClient();
Assert.Contains($"No Billing Sync Key known", exception.Message);
var mockHttpClientFactory = Substitute.For<IHttpClientFactory>();
mockHttpClientFactory.CreateClient(Arg.Is("client")).Returns(apiHttp);
mockHttpClientFactory.CreateClient(Arg.Is("identity")).Returns(identityHttp);
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.DidNotReceiveWithAnyArgs()
.DeleteManyAsync(default);
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.DidNotReceiveWithAnyArgs()
.UpsertManyAsync(default);
}
return new SutProvider<SelfHostedSyncSponsorshipsCommand>(fixture)
.SetDependency(settings)
.SetDependency(mockHttpClientFactory)
.Create();
}
[Theory]
[BitAutoData]
public async Task SyncOrganization_CloudCommunicationDisabled_EarlyReturn(
Guid cloudOrganizationId, OrganizationConnection billingSyncConnection)
{
var sutProvider = GetSutProvider(false);
[Theory]
[BitAutoData]
public async Task SyncOrganization_BillingSyncKeyDisabled_ThrowsBadRequest(
Guid cloudOrganizationId, OrganizationConnection billingSyncConnection)
{
var sutProvider = GetSutProvider();
billingSyncConnection.Enabled = false;
billingSyncConnection.SetConfig(new BillingSyncConfig
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
sutProvider.Sut.SyncOrganization(billingSyncConnection.OrganizationId, cloudOrganizationId, billingSyncConnection));
Assert.Contains($"Cloud communication is disabled", exception.Message);
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.DidNotReceiveWithAnyArgs()
.DeleteManyAsync(default);
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.DidNotReceiveWithAnyArgs()
.UpsertManyAsync(default);
}
[Theory]
[OrganizationSponsorshipCustomize]
[BitAutoData]
public async Task SyncOrganization_SyncsSponsorships(
Guid cloudOrganizationId, OrganizationConnection billingSyncConnection, IEnumerable<OrganizationSponsorship> sponsorships)
{
var syncJsonResponse = JsonSerializer.Serialize(new OrganizationSponsorshipSyncResponseModel(
new OrganizationSponsorshipSyncData
{
BillingSyncKey = "okslkcslkjf"
});
SponsorshipsBatch = sponsorships.Select(o => new OrganizationSponsorshipData(o))
}));
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
sutProvider.Sut.SyncOrganization(billingSyncConnection.OrganizationId, cloudOrganizationId, billingSyncConnection));
Assert.Contains($"Billing Sync Key disabled", exception.Message);
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.DidNotReceiveWithAnyArgs()
.DeleteManyAsync(default);
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.DidNotReceiveWithAnyArgs()
.UpsertManyAsync(default);
}
[Theory]
[BitAutoData]
public async Task SyncOrganization_BillingSyncKeyEmpty_ThrowsBadRequest(
Guid cloudOrganizationId, OrganizationConnection billingSyncConnection)
var sutProvider = GetSutProvider(apiResponse: syncJsonResponse);
billingSyncConnection.SetConfig(new BillingSyncConfig
{
var sutProvider = GetSutProvider();
billingSyncConnection.Config = "";
BillingSyncKey = "okslkcslkjf"
});
sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.GetManyBySponsoringOrganizationAsync(Arg.Any<Guid>()).Returns(sponsorships.ToList());
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
sutProvider.Sut.SyncOrganization(billingSyncConnection.OrganizationId, cloudOrganizationId, billingSyncConnection));
await sutProvider.Sut.SyncOrganization(billingSyncConnection.OrganizationId, cloudOrganizationId, billingSyncConnection);
Assert.Contains($"No Billing Sync Key known", exception.Message);
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.DidNotReceiveWithAnyArgs()
.DeleteManyAsync(default);
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.Received(1)
.UpsertManyAsync(Arg.Any<IEnumerable<OrganizationSponsorship>>());
}
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.DidNotReceiveWithAnyArgs()
.DeleteManyAsync(default);
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.DidNotReceiveWithAnyArgs()
.UpsertManyAsync(default);
}
[Theory]
[BitAutoData]
public async Task SyncOrganization_CloudCommunicationDisabled_EarlyReturn(
Guid cloudOrganizationId, OrganizationConnection billingSyncConnection)
{
var sutProvider = GetSutProvider(false);
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
sutProvider.Sut.SyncOrganization(billingSyncConnection.OrganizationId, cloudOrganizationId, billingSyncConnection));
Assert.Contains($"Cloud communication is disabled", exception.Message);
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.DidNotReceiveWithAnyArgs()
.DeleteManyAsync(default);
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.DidNotReceiveWithAnyArgs()
.UpsertManyAsync(default);
}
[Theory]
[OrganizationSponsorshipCustomize]
[BitAutoData]
public async Task SyncOrganization_SyncsSponsorships(
Guid cloudOrganizationId, OrganizationConnection billingSyncConnection, IEnumerable<OrganizationSponsorship> sponsorships)
{
var syncJsonResponse = JsonSerializer.Serialize(new OrganizationSponsorshipSyncResponseModel(
new OrganizationSponsorshipSyncData
{
SponsorshipsBatch = sponsorships.Select(o => new OrganizationSponsorshipData(o))
}));
var sutProvider = GetSutProvider(apiResponse: syncJsonResponse);
billingSyncConnection.SetConfig(new BillingSyncConfig
[Theory]
[OrganizationSponsorshipCustomize(ToDelete = true)]
[BitAutoData]
public async Task SyncOrganization_DeletesSponsorships(
Guid cloudOrganizationId, OrganizationConnection billingSyncConnection, IEnumerable<OrganizationSponsorship> sponsorships)
{
var syncJsonResponse = JsonSerializer.Serialize(new OrganizationSponsorshipSyncResponseModel(
new OrganizationSponsorshipSyncData
{
BillingSyncKey = "okslkcslkjf"
});
sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.GetManyBySponsoringOrganizationAsync(Arg.Any<Guid>()).Returns(sponsorships.ToList());
SponsorshipsBatch = sponsorships.Select(o => new OrganizationSponsorshipData(o) { CloudSponsorshipRemoved = true })
}));
await sutProvider.Sut.SyncOrganization(billingSyncConnection.OrganizationId, cloudOrganizationId, billingSyncConnection);
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.DidNotReceiveWithAnyArgs()
.DeleteManyAsync(default);
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.Received(1)
.UpsertManyAsync(Arg.Any<IEnumerable<OrganizationSponsorship>>());
}
[Theory]
[OrganizationSponsorshipCustomize(ToDelete = true)]
[BitAutoData]
public async Task SyncOrganization_DeletesSponsorships(
Guid cloudOrganizationId, OrganizationConnection billingSyncConnection, IEnumerable<OrganizationSponsorship> sponsorships)
var sutProvider = GetSutProvider(apiResponse: syncJsonResponse);
billingSyncConnection.SetConfig(new BillingSyncConfig
{
var syncJsonResponse = JsonSerializer.Serialize(new OrganizationSponsorshipSyncResponseModel(
new OrganizationSponsorshipSyncData
{
SponsorshipsBatch = sponsorships.Select(o => new OrganizationSponsorshipData(o) { CloudSponsorshipRemoved = true })
}));
BillingSyncKey = "okslkcslkjf"
});
sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.GetManyBySponsoringOrganizationAsync(Arg.Any<Guid>()).Returns(sponsorships.ToList());
var sutProvider = GetSutProvider(apiResponse: syncJsonResponse);
billingSyncConnection.SetConfig(new BillingSyncConfig
{
BillingSyncKey = "okslkcslkjf"
});
sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.GetManyBySponsoringOrganizationAsync(Arg.Any<Guid>()).Returns(sponsorships.ToList());
await sutProvider.Sut.SyncOrganization(billingSyncConnection.OrganizationId, cloudOrganizationId, billingSyncConnection);
await sutProvider.Sut.SyncOrganization(billingSyncConnection.OrganizationId, cloudOrganizationId, billingSyncConnection);
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.Received(1)
.DeleteManyAsync(Arg.Any<IEnumerable<Guid>>());
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.DidNotReceiveWithAnyArgs()
.UpsertManyAsync(default);
}
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.Received(1)
.DeleteManyAsync(Arg.Any<IEnumerable<Guid>>());
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
.DidNotReceiveWithAnyArgs()
.UpsertManyAsync(default);
}
}

View File

@ -1,28 +1,27 @@
using Bit.Core.Utilities;
using Xunit;
namespace Bit.Core.Test.Resources
namespace Bit.Core.Test.Resources;
public class VerifyResources
{
public class VerifyResources
[Theory]
[MemberData(nameof(GetResources))]
public void Resource_FoundAndReadable(string resourceName)
{
[Theory]
[MemberData(nameof(GetResources))]
public void Resource_FoundAndReadable(string resourceName)
{
var assembly = typeof(CoreHelpers).Assembly;
var assembly = typeof(CoreHelpers).Assembly;
using (var resource = assembly.GetManifestResourceStream(resourceName))
{
Assert.NotNull(resource);
Assert.True(resource.CanRead);
}
}
public static IEnumerable<object[]> GetResources()
using (var resource = assembly.GetManifestResourceStream(resourceName))
{
yield return new[] { "Bit.Core.licensing.cer" };
yield return new[] { "Bit.Core.MailTemplates.Handlebars.AddedCredit.html.hbs" };
yield return new[] { "Bit.Core.MailTemplates.Handlebars.Layouts.Basic.html.hbs" };
Assert.NotNull(resource);
Assert.True(resource.CanRead);
}
}
public static IEnumerable<object[]> GetResources()
{
yield return new[] { "Bit.Core.licensing.cer" };
yield return new[] { "Bit.Core.MailTemplates.Handlebars.AddedCredit.html.hbs" };
yield return new[] { "Bit.Core.MailTemplates.Handlebars.Layouts.Basic.html.hbs" };
}
}

View File

@ -8,80 +8,79 @@ using Microsoft.Extensions.Logging;
using NSubstitute;
using Xunit;
namespace Bit.Core.Test.Services
namespace Bit.Core.Test.Services;
public class AmazonSesMailDeliveryServiceTests : IDisposable
{
public class AmazonSesMailDeliveryServiceTests : IDisposable
private readonly AmazonSesMailDeliveryService _sut;
private readonly GlobalSettings _globalSettings;
private readonly IWebHostEnvironment _hostingEnvironment;
private readonly ILogger<AmazonSesMailDeliveryService> _logger;
private readonly IAmazonSimpleEmailService _amazonSimpleEmailService;
public AmazonSesMailDeliveryServiceTests()
{
private readonly AmazonSesMailDeliveryService _sut;
private readonly GlobalSettings _globalSettings;
private readonly IWebHostEnvironment _hostingEnvironment;
private readonly ILogger<AmazonSesMailDeliveryService> _logger;
private readonly IAmazonSimpleEmailService _amazonSimpleEmailService;
public AmazonSesMailDeliveryServiceTests()
_globalSettings = new GlobalSettings
{
_globalSettings = new GlobalSettings
Amazon =
{
AccessKeyId = "AccessKeyId-AmazonSesMailDeliveryServiceTests",
AccessKeySecret = "AccessKeySecret-AmazonSesMailDeliveryServiceTests",
Region = "Region-AmazonSesMailDeliveryServiceTests"
}
};
_hostingEnvironment = Substitute.For<IWebHostEnvironment>();
_logger = Substitute.For<ILogger<AmazonSesMailDeliveryService>>();
_amazonSimpleEmailService = Substitute.For<IAmazonSimpleEmailService>();
_sut = new AmazonSesMailDeliveryService(
_globalSettings,
_hostingEnvironment,
_logger,
_amazonSimpleEmailService
);
}
public void Dispose()
{
_sut?.Dispose();
}
[Fact]
public async Task SendEmailAsync_CallsSendEmailAsync_WhenMessageIsValid()
{
var mailMessage = new MailMessage
{
ToEmails = new List<string> { "ToEmails" },
BccEmails = new List<string> { "BccEmails" },
Subject = "Subject",
HtmlContent = "HtmlContent",
TextContent = "TextContent",
Category = "Category"
};
await _sut.SendEmailAsync(mailMessage);
await _amazonSimpleEmailService.Received(1).SendEmailAsync(
Arg.Do<SendEmailRequest>(request =>
{
Amazon =
{
AccessKeyId = "AccessKeyId-AmazonSesMailDeliveryServiceTests",
AccessKeySecret = "AccessKeySecret-AmazonSesMailDeliveryServiceTests",
Region = "Region-AmazonSesMailDeliveryServiceTests"
}
};
Assert.False(string.IsNullOrEmpty(request.Source));
_hostingEnvironment = Substitute.For<IWebHostEnvironment>();
_logger = Substitute.For<ILogger<AmazonSesMailDeliveryService>>();
_amazonSimpleEmailService = Substitute.For<IAmazonSimpleEmailService>();
Assert.Single(request.Destination.ToAddresses);
Assert.Equal(mailMessage.ToEmails.First(), request.Destination.ToAddresses.First());
_sut = new AmazonSesMailDeliveryService(
_globalSettings,
_hostingEnvironment,
_logger,
_amazonSimpleEmailService
);
}
Assert.Equal(mailMessage.Subject, request.Message.Subject.Data);
Assert.Equal(mailMessage.HtmlContent, request.Message.Body.Html.Data);
Assert.Equal(mailMessage.TextContent, request.Message.Body.Text.Data);
public void Dispose()
{
_sut?.Dispose();
}
Assert.Single(request.Destination.BccAddresses);
Assert.Equal(mailMessage.BccEmails.First(), request.Destination.BccAddresses.First());
[Fact]
public async Task SendEmailAsync_CallsSendEmailAsync_WhenMessageIsValid()
{
var mailMessage = new MailMessage
{
ToEmails = new List<string> { "ToEmails" },
BccEmails = new List<string> { "BccEmails" },
Subject = "Subject",
HtmlContent = "HtmlContent",
TextContent = "TextContent",
Category = "Category"
};
await _sut.SendEmailAsync(mailMessage);
await _amazonSimpleEmailService.Received(1).SendEmailAsync(
Arg.Do<SendEmailRequest>(request =>
{
Assert.False(string.IsNullOrEmpty(request.Source));
Assert.Single(request.Destination.ToAddresses);
Assert.Equal(mailMessage.ToEmails.First(), request.Destination.ToAddresses.First());
Assert.Equal(mailMessage.Subject, request.Message.Subject.Data);
Assert.Equal(mailMessage.HtmlContent, request.Message.Body.Html.Data);
Assert.Equal(mailMessage.TextContent, request.Message.Body.Text.Data);
Assert.Single(request.Destination.BccAddresses);
Assert.Equal(mailMessage.BccEmails.First(), request.Destination.BccAddresses.First());
Assert.Contains(request.Tags, x => x.Name == "Environment");
Assert.Contains(request.Tags, x => x.Name == "Sender");
Assert.Contains(request.Tags, x => x.Name == "Category");
}));
}
Assert.Contains(request.Tags, x => x.Name == "Environment");
Assert.Contains(request.Tags, x => x.Name == "Sender");
Assert.Contains(request.Tags, x => x.Name == "Category");
}));
}
}

View File

@ -4,74 +4,73 @@ using Bit.Core.Settings;
using NSubstitute;
using Xunit;
namespace Bit.Core.Test.Services
namespace Bit.Core.Test.Services;
public class AmazonSqsBlockIpServiceTests : IDisposable
{
public class AmazonSqsBlockIpServiceTests : IDisposable
private readonly AmazonSqsBlockIpService _sut;
private readonly GlobalSettings _globalSettings;
private readonly IAmazonSQS _amazonSqs;
public AmazonSqsBlockIpServiceTests()
{
private readonly AmazonSqsBlockIpService _sut;
private readonly GlobalSettings _globalSettings;
private readonly IAmazonSQS _amazonSqs;
public AmazonSqsBlockIpServiceTests()
_globalSettings = new GlobalSettings
{
_globalSettings = new GlobalSettings
Amazon =
{
Amazon =
{
AccessKeyId = "AccessKeyId-AmazonSesMailDeliveryServiceTests",
AccessKeySecret = "AccessKeySecret-AmazonSesMailDeliveryServiceTests",
Region = "Region-AmazonSesMailDeliveryServiceTests"
}
};
AccessKeyId = "AccessKeyId-AmazonSesMailDeliveryServiceTests",
AccessKeySecret = "AccessKeySecret-AmazonSesMailDeliveryServiceTests",
Region = "Region-AmazonSesMailDeliveryServiceTests"
}
};
_amazonSqs = Substitute.For<IAmazonSQS>();
_amazonSqs = Substitute.For<IAmazonSQS>();
_sut = new AmazonSqsBlockIpService(_globalSettings, _amazonSqs);
}
_sut = new AmazonSqsBlockIpService(_globalSettings, _amazonSqs);
}
public void Dispose()
{
_sut?.Dispose();
}
public void Dispose()
{
_sut?.Dispose();
}
[Fact]
public async Task BlockIpAsync_UnblockCalled_WhenNotPermanent()
{
const string expectedIp = "ip";
[Fact]
public async Task BlockIpAsync_UnblockCalled_WhenNotPermanent()
{
const string expectedIp = "ip";
await _sut.BlockIpAsync(expectedIp, false);
await _sut.BlockIpAsync(expectedIp, false);
await _amazonSqs.Received(2).SendMessageAsync(
Arg.Any<string>(),
Arg.Is(expectedIp));
}
await _amazonSqs.Received(2).SendMessageAsync(
Arg.Any<string>(),
Arg.Is(expectedIp));
}
[Fact]
public async Task BlockIpAsync_UnblockNotCalled_WhenPermanent()
{
const string expectedIp = "ip";
[Fact]
public async Task BlockIpAsync_UnblockNotCalled_WhenPermanent()
{
const string expectedIp = "ip";
await _sut.BlockIpAsync(expectedIp, true);
await _sut.BlockIpAsync(expectedIp, true);
await _amazonSqs.Received(1).SendMessageAsync(
Arg.Any<string>(),
Arg.Is(expectedIp));
}
await _amazonSqs.Received(1).SendMessageAsync(
Arg.Any<string>(),
Arg.Is(expectedIp));
}
[Fact]
public async Task BlockIpAsync_NotBlocked_WhenAlreadyBlockedRecently()
{
const string expectedIp = "ip";
[Fact]
public async Task BlockIpAsync_NotBlocked_WhenAlreadyBlockedRecently()
{
const string expectedIp = "ip";
await _sut.BlockIpAsync(expectedIp, true);
await _sut.BlockIpAsync(expectedIp, true);
// The second call should hit the already blocked guard clause
await _sut.BlockIpAsync(expectedIp, true);
// The second call should hit the already blocked guard clause
await _sut.BlockIpAsync(expectedIp, true);
await _amazonSqs.Received(1).SendMessageAsync(
Arg.Any<string>(),
Arg.Is(expectedIp));
}
await _amazonSqs.Received(1).SendMessageAsync(
Arg.Any<string>(),
Arg.Is(expectedIp));
}
}

View File

@ -6,36 +6,35 @@ using NSubstitute;
using NSubstitute.Core;
using Xunit;
namespace Bit.Core.Test.Services
namespace Bit.Core.Test.Services;
[SutProviderCustomize]
public class AppleIapServiceTests
{
[SutProviderCustomize]
public class AppleIapServiceTests
[Theory, BitAutoData]
public async Task GetReceiptStatusAsync_MoreThanFourAttempts_Throws(SutProvider<AppleIapService> sutProvider)
{
[Theory, BitAutoData]
public async Task GetReceiptStatusAsync_MoreThanFourAttempts_Throws(SutProvider<AppleIapService> sutProvider)
var result = await sutProvider.Sut.GetReceiptStatusAsync("test", false, 5, null);
Assert.Null(result);
var errorLog = sutProvider.GetDependency<ILogger<AppleIapService>>()
.ReceivedCalls()
.SingleOrDefault(LogOneWarning);
Assert.True(errorLog != null, "Must contain one error log of warning level containing 'null'");
static bool LogOneWarning(ICall call)
{
var result = await sutProvider.Sut.GetReceiptStatusAsync("test", false, 5, null);
Assert.Null(result);
var errorLog = sutProvider.GetDependency<ILogger<AppleIapService>>()
.ReceivedCalls()
.SingleOrDefault(LogOneWarning);
Assert.True(errorLog != null, "Must contain one error log of warning level containing 'null'");
static bool LogOneWarning(ICall call)
if (call.GetMethodInfo().Name != "Log")
{
if (call.GetMethodInfo().Name != "Log")
{
return false;
}
var args = call.GetArguments();
var logLevel = (LogLevel)args[0];
var exception = (Exception)args[3];
return logLevel == LogLevel.Warning && exception.Message.Contains("null");
return false;
}
var args = call.GetArguments();
var logLevel = (LogLevel)args[0];
var exception = (Exception)args[3];
return logLevel == LogLevel.Warning && exception.Message.Contains("null");
}
}
}

View File

@ -4,29 +4,28 @@ using Microsoft.Extensions.Logging;
using NSubstitute;
using Xunit;
namespace Bit.Core.Test.Services
namespace Bit.Core.Test.Services;
public class AzureAttachmentStorageServiceTests
{
public class AzureAttachmentStorageServiceTests
private readonly AzureAttachmentStorageService _sut;
private readonly GlobalSettings _globalSettings;
private readonly ILogger<AzureAttachmentStorageService> _logger;
public AzureAttachmentStorageServiceTests()
{
private readonly AzureAttachmentStorageService _sut;
_globalSettings = new GlobalSettings();
_logger = Substitute.For<ILogger<AzureAttachmentStorageService>>();
private readonly GlobalSettings _globalSettings;
private readonly ILogger<AzureAttachmentStorageService> _logger;
_sut = new AzureAttachmentStorageService(_globalSettings, _logger);
}
public AzureAttachmentStorageServiceTests()
{
_globalSettings = new GlobalSettings();
_logger = Substitute.For<ILogger<AzureAttachmentStorageService>>();
_sut = new AzureAttachmentStorageService(_globalSettings, _logger);
}
// Remove this test when we add actual tests. It only proves that
// we've properly constructed the system under test.
[Fact(Skip = "Needs additional work")]
public void ServiceExists()
{
Assert.NotNull(_sut);
}
// Remove this test when we add actual tests. It only proves that
// we've properly constructed the system under test.
[Fact(Skip = "Needs additional work")]
public void ServiceExists()
{
Assert.NotNull(_sut);
}
}

View File

@ -2,27 +2,26 @@
using Bit.Core.Settings;
using Xunit;
namespace Bit.Core.Test.Services
namespace Bit.Core.Test.Services;
public class AzureQueueBlockIpServiceTests
{
public class AzureQueueBlockIpServiceTests
private readonly AzureQueueBlockIpService _sut;
private readonly GlobalSettings _globalSettings;
public AzureQueueBlockIpServiceTests()
{
private readonly AzureQueueBlockIpService _sut;
_globalSettings = new GlobalSettings();
private readonly GlobalSettings _globalSettings;
_sut = new AzureQueueBlockIpService(_globalSettings);
}
public AzureQueueBlockIpServiceTests()
{
_globalSettings = new GlobalSettings();
_sut = new AzureQueueBlockIpService(_globalSettings);
}
// Remove this test when we add actual tests. It only proves that
// we've properly constructed the system under test.
[Fact(Skip = "Needs additional work")]
public void ServiceExists()
{
Assert.NotNull(_sut);
}
// Remove this test when we add actual tests. It only proves that
// we've properly constructed the system under test.
[Fact(Skip = "Needs additional work")]
public void ServiceExists()
{
Assert.NotNull(_sut);
}
}

View File

@ -4,31 +4,30 @@ using Bit.Core.Settings;
using NSubstitute;
using Xunit;
namespace Bit.Core.Test.Services
namespace Bit.Core.Test.Services;
public class AzureQueueEventWriteServiceTests
{
public class AzureQueueEventWriteServiceTests
private readonly AzureQueueEventWriteService _sut;
private readonly GlobalSettings _globalSettings;
private readonly IEventRepository _eventRepository;
public AzureQueueEventWriteServiceTests()
{
private readonly AzureQueueEventWriteService _sut;
_globalSettings = new GlobalSettings();
_eventRepository = Substitute.For<IEventRepository>();
private readonly GlobalSettings _globalSettings;
private readonly IEventRepository _eventRepository;
_sut = new AzureQueueEventWriteService(
_globalSettings
);
}
public AzureQueueEventWriteServiceTests()
{
_globalSettings = new GlobalSettings();
_eventRepository = Substitute.For<IEventRepository>();
_sut = new AzureQueueEventWriteService(
_globalSettings
);
}
// Remove this test when we add actual tests. It only proves that
// we've properly constructed the system under test.
[Fact(Skip = "Needs additional work")]
public void ServiceExists()
{
Assert.NotNull(_sut);
}
// Remove this test when we add actual tests. It only proves that
// we've properly constructed the system under test.
[Fact(Skip = "Needs additional work")]
public void ServiceExists()
{
Assert.NotNull(_sut);
}
}

View File

@ -4,32 +4,31 @@ using Microsoft.AspNetCore.Http;
using NSubstitute;
using Xunit;
namespace Bit.Core.Test.Services
namespace Bit.Core.Test.Services;
public class AzureQueuePushNotificationServiceTests
{
public class AzureQueuePushNotificationServiceTests
private readonly AzureQueuePushNotificationService _sut;
private readonly GlobalSettings _globalSettings;
private readonly IHttpContextAccessor _httpContextAccessor;
public AzureQueuePushNotificationServiceTests()
{
private readonly AzureQueuePushNotificationService _sut;
_globalSettings = new GlobalSettings();
_httpContextAccessor = Substitute.For<IHttpContextAccessor>();
private readonly GlobalSettings _globalSettings;
private readonly IHttpContextAccessor _httpContextAccessor;
_sut = new AzureQueuePushNotificationService(
_globalSettings,
_httpContextAccessor
);
}
public AzureQueuePushNotificationServiceTests()
{
_globalSettings = new GlobalSettings();
_httpContextAccessor = Substitute.For<IHttpContextAccessor>();
_sut = new AzureQueuePushNotificationService(
_globalSettings,
_httpContextAccessor
);
}
// Remove this test when we add actual tests. It only proves that
// we've properly constructed the system under test.
[Fact(Skip = "Needs additional work")]
public void ServiceExists()
{
Assert.NotNull(_sut);
}
// Remove this test when we add actual tests. It only proves that
// we've properly constructed the system under test.
[Fact(Skip = "Needs additional work")]
public void ServiceExists()
{
Assert.NotNull(_sut);
}
}

View File

@ -9,209 +9,208 @@ using Core.Models.Data;
using NSubstitute;
using Xunit;
namespace Bit.Core.Test.Services
namespace Bit.Core.Test.Services;
public class CipherServiceTests
{
public class CipherServiceTests
[Theory, UserCipherAutoData]
public async Task SaveAsync_WrongRevisionDate_Throws(SutProvider<CipherService> sutProvider, Cipher cipher)
{
[Theory, UserCipherAutoData]
public async Task SaveAsync_WrongRevisionDate_Throws(SutProvider<CipherService> sutProvider, Cipher cipher)
{
var lastKnownRevisionDate = cipher.RevisionDate.AddDays(-1);
var lastKnownRevisionDate = cipher.RevisionDate.AddDays(-1);
var exception = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.SaveAsync(cipher, cipher.UserId.Value, lastKnownRevisionDate));
Assert.Contains("out of date", exception.Message);
}
var exception = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.SaveAsync(cipher, cipher.UserId.Value, lastKnownRevisionDate));
Assert.Contains("out of date", exception.Message);
}
[Theory, UserCipherAutoData]
public async Task SaveDetailsAsync_WrongRevisionDate_Throws(SutProvider<CipherService> sutProvider,
CipherDetails cipherDetails)
{
var lastKnownRevisionDate = cipherDetails.RevisionDate.AddDays(-1);
[Theory, UserCipherAutoData]
public async Task SaveDetailsAsync_WrongRevisionDate_Throws(SutProvider<CipherService> sutProvider,
CipherDetails cipherDetails)
{
var lastKnownRevisionDate = cipherDetails.RevisionDate.AddDays(-1);
var exception = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.SaveDetailsAsync(cipherDetails, cipherDetails.UserId.Value, lastKnownRevisionDate));
Assert.Contains("out of date", exception.Message);
}
var exception = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.SaveDetailsAsync(cipherDetails, cipherDetails.UserId.Value, lastKnownRevisionDate));
Assert.Contains("out of date", exception.Message);
}
[Theory, UserCipherAutoData]
public async Task ShareAsync_WrongRevisionDate_Throws(SutProvider<CipherService> sutProvider, Cipher cipher,
Organization organization, List<Guid> collectionIds)
{
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
var lastKnownRevisionDate = cipher.RevisionDate.AddDays(-1);
[Theory, UserCipherAutoData]
public async Task ShareAsync_WrongRevisionDate_Throws(SutProvider<CipherService> sutProvider, Cipher cipher,
Organization organization, List<Guid> collectionIds)
{
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
var lastKnownRevisionDate = cipher.RevisionDate.AddDays(-1);
var exception = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.ShareAsync(cipher, cipher, organization.Id, collectionIds, cipher.UserId.Value,
lastKnownRevisionDate));
Assert.Contains("out of date", exception.Message);
}
var exception = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.ShareAsync(cipher, cipher, organization.Id, collectionIds, cipher.UserId.Value,
lastKnownRevisionDate));
Assert.Contains("out of date", exception.Message);
}
[Theory, UserCipherAutoData("99ab4f6c-44f8-4ff5-be7a-75c37c33c69e")]
public async Task ShareManyAsync_WrongRevisionDate_Throws(SutProvider<CipherService> sutProvider,
IEnumerable<Cipher> ciphers, Guid organizationId, List<Guid> collectionIds)
{
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organizationId)
.Returns(new Organization
{
PlanType = Enums.PlanType.EnterpriseAnnually,
MaxStorageGb = 100
});
var cipherInfos = ciphers.Select(c => (c, (DateTime?)c.RevisionDate.AddDays(-1)));
var exception = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.ShareManyAsync(cipherInfos, organizationId, collectionIds, ciphers.First().UserId.Value));
Assert.Contains("out of date", exception.Message);
}
[Theory]
[InlineUserCipherAutoData("")]
[InlineUserCipherAutoData("Correct Time")]
public async Task SaveAsync_CorrectRevisionDate_Passes(string revisionDateString,
SutProvider<CipherService> sutProvider, Cipher cipher)
{
var lastKnownRevisionDate = string.IsNullOrEmpty(revisionDateString) ? (DateTime?)null : cipher.RevisionDate;
await sutProvider.Sut.SaveAsync(cipher, cipher.UserId.Value, lastKnownRevisionDate);
await sutProvider.GetDependency<ICipherRepository>().Received(1).ReplaceAsync(cipher);
}
[Theory]
[InlineUserCipherAutoData("")]
[InlineUserCipherAutoData("Correct Time")]
public async Task SaveDetailsAsync_CorrectRevisionDate_Passes(string revisionDateString,
SutProvider<CipherService> sutProvider, CipherDetails cipherDetails)
{
var lastKnownRevisionDate = string.IsNullOrEmpty(revisionDateString) ? (DateTime?)null : cipherDetails.RevisionDate;
await sutProvider.Sut.SaveDetailsAsync(cipherDetails, cipherDetails.UserId.Value, lastKnownRevisionDate);
await sutProvider.GetDependency<ICipherRepository>().Received(1).ReplaceAsync(cipherDetails);
}
[Theory]
[InlineUserCipherAutoData("")]
[InlineUserCipherAutoData("Correct Time")]
public async Task ShareAsync_CorrectRevisionDate_Passes(string revisionDateString,
SutProvider<CipherService> sutProvider, Cipher cipher, Organization organization, List<Guid> collectionIds)
{
var lastKnownRevisionDate = string.IsNullOrEmpty(revisionDateString) ? (DateTime?)null : cipher.RevisionDate;
var cipherRepository = sutProvider.GetDependency<ICipherRepository>();
cipherRepository.ReplaceAsync(cipher, collectionIds).Returns(true);
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
await sutProvider.Sut.ShareAsync(cipher, cipher, organization.Id, collectionIds, cipher.UserId.Value,
lastKnownRevisionDate);
await cipherRepository.Received(1).ReplaceAsync(cipher, collectionIds);
}
[Theory]
[InlineKnownUserCipherAutoData(userId: "99ab4f6c-44f8-4ff5-be7a-75c37c33c69e", "")]
[InlineKnownUserCipherAutoData(userId: "99ab4f6c-44f8-4ff5-be7a-75c37c33c69e", "CorrectTime")]
public async Task ShareManyAsync_CorrectRevisionDate_Passes(string revisionDateString,
SutProvider<CipherService> sutProvider, IEnumerable<Cipher> ciphers, Organization organization, List<Guid> collectionIds)
{
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id)
.Returns(new Organization
{
PlanType = Enums.PlanType.EnterpriseAnnually,
MaxStorageGb = 100
});
var cipherInfos = ciphers.Select(c => (c,
string.IsNullOrEmpty(revisionDateString) ? null : (DateTime?)c.RevisionDate));
var sharingUserId = ciphers.First().UserId.Value;
await sutProvider.Sut.ShareManyAsync(cipherInfos, organization.Id, collectionIds, sharingUserId);
await sutProvider.GetDependency<ICipherRepository>().Received(1).UpdateCiphersAsync(sharingUserId,
Arg.Is<IEnumerable<Cipher>>(arg => arg.Except(ciphers).IsNullOrEmpty()));
}
[Theory]
[InlineKnownUserCipherAutoData("c64d8a15-606e-41d6-9c7e-174d4d8f3b2e", "c64d8a15-606e-41d6-9c7e-174d4d8f3b2e")]
[InlineOrganizationCipherAutoData("c64d8a15-606e-41d6-9c7e-174d4d8f3b2e")]
public async Task RestoreAsync_UpdatesCipher(Guid restoringUserId, Cipher cipher, SutProvider<CipherService> sutProvider)
{
sutProvider.GetDependency<ICipherRepository>().GetCanEditByIdAsync(restoringUserId, cipher.Id).Returns(true);
var initialRevisionDate = new DateTime(1970, 1, 1, 0, 0, 0);
cipher.DeletedDate = initialRevisionDate;
cipher.RevisionDate = initialRevisionDate;
await sutProvider.Sut.RestoreAsync(cipher, restoringUserId, cipher.OrganizationId.HasValue);
Assert.Null(cipher.DeletedDate);
Assert.NotEqual(initialRevisionDate, cipher.RevisionDate);
}
[Theory]
[InlineKnownUserCipherAutoData("c64d8a15-606e-41d6-9c7e-174d4d8f3b2e", "c64d8a15-606e-41d6-9c7e-174d4d8f3b2e")]
public async Task RestoreManyAsync_UpdatesCiphers(Guid restoringUserId, IEnumerable<CipherDetails> ciphers,
SutProvider<CipherService> sutProvider)
{
var previousRevisionDate = DateTime.UtcNow;
foreach (var cipher in ciphers)
[Theory, UserCipherAutoData("99ab4f6c-44f8-4ff5-be7a-75c37c33c69e")]
public async Task ShareManyAsync_WrongRevisionDate_Throws(SutProvider<CipherService> sutProvider,
IEnumerable<Cipher> ciphers, Guid organizationId, List<Guid> collectionIds)
{
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organizationId)
.Returns(new Organization
{
cipher.RevisionDate = previousRevisionDate;
}
var revisionDate = previousRevisionDate + TimeSpan.FromMinutes(1);
sutProvider.GetDependency<ICipherRepository>().RestoreAsync(Arg.Any<IEnumerable<Guid>>(), restoringUserId)
.Returns(revisionDate);
await sutProvider.Sut.RestoreManyAsync(ciphers, restoringUserId);
foreach (var cipher in ciphers)
{
Assert.Null(cipher.DeletedDate);
Assert.Equal(revisionDate, cipher.RevisionDate);
}
}
[Theory]
[InlineUserCipherAutoData]
public async Task ShareManyAsync_FreeOrgWithAttachment_Throws(SutProvider<CipherService> sutProvider,
IEnumerable<Cipher> ciphers, Guid organizationId, List<Guid> collectionIds)
{
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organizationId).Returns(new Organization
{
PlanType = Enums.PlanType.Free
PlanType = Enums.PlanType.EnterpriseAnnually,
MaxStorageGb = 100
});
ciphers.FirstOrDefault().Attachments =
"{\"attachment1\":{\"Size\":\"250\",\"FileName\":\"superCoolFile\","
+ "\"Key\":\"superCoolFile\",\"ContainerName\":\"testContainer\",\"Validated\":false}}";
var cipherInfos = ciphers.Select(c => (c,
(DateTime?)c.RevisionDate));
var sharingUserId = ciphers.First().UserId.Value;
var cipherInfos = ciphers.Select(c => (c, (DateTime?)c.RevisionDate.AddDays(-1)));
var exception = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.ShareManyAsync(cipherInfos, organizationId, collectionIds, sharingUserId));
Assert.Contains("This organization cannot use attachments", exception.Message);
var exception = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.ShareManyAsync(cipherInfos, organizationId, collectionIds, ciphers.First().UserId.Value));
Assert.Contains("out of date", exception.Message);
}
[Theory]
[InlineUserCipherAutoData("")]
[InlineUserCipherAutoData("Correct Time")]
public async Task SaveAsync_CorrectRevisionDate_Passes(string revisionDateString,
SutProvider<CipherService> sutProvider, Cipher cipher)
{
var lastKnownRevisionDate = string.IsNullOrEmpty(revisionDateString) ? (DateTime?)null : cipher.RevisionDate;
await sutProvider.Sut.SaveAsync(cipher, cipher.UserId.Value, lastKnownRevisionDate);
await sutProvider.GetDependency<ICipherRepository>().Received(1).ReplaceAsync(cipher);
}
[Theory]
[InlineUserCipherAutoData("")]
[InlineUserCipherAutoData("Correct Time")]
public async Task SaveDetailsAsync_CorrectRevisionDate_Passes(string revisionDateString,
SutProvider<CipherService> sutProvider, CipherDetails cipherDetails)
{
var lastKnownRevisionDate = string.IsNullOrEmpty(revisionDateString) ? (DateTime?)null : cipherDetails.RevisionDate;
await sutProvider.Sut.SaveDetailsAsync(cipherDetails, cipherDetails.UserId.Value, lastKnownRevisionDate);
await sutProvider.GetDependency<ICipherRepository>().Received(1).ReplaceAsync(cipherDetails);
}
[Theory]
[InlineUserCipherAutoData("")]
[InlineUserCipherAutoData("Correct Time")]
public async Task ShareAsync_CorrectRevisionDate_Passes(string revisionDateString,
SutProvider<CipherService> sutProvider, Cipher cipher, Organization organization, List<Guid> collectionIds)
{
var lastKnownRevisionDate = string.IsNullOrEmpty(revisionDateString) ? (DateTime?)null : cipher.RevisionDate;
var cipherRepository = sutProvider.GetDependency<ICipherRepository>();
cipherRepository.ReplaceAsync(cipher, collectionIds).Returns(true);
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
await sutProvider.Sut.ShareAsync(cipher, cipher, organization.Id, collectionIds, cipher.UserId.Value,
lastKnownRevisionDate);
await cipherRepository.Received(1).ReplaceAsync(cipher, collectionIds);
}
[Theory]
[InlineKnownUserCipherAutoData(userId: "99ab4f6c-44f8-4ff5-be7a-75c37c33c69e", "")]
[InlineKnownUserCipherAutoData(userId: "99ab4f6c-44f8-4ff5-be7a-75c37c33c69e", "CorrectTime")]
public async Task ShareManyAsync_CorrectRevisionDate_Passes(string revisionDateString,
SutProvider<CipherService> sutProvider, IEnumerable<Cipher> ciphers, Organization organization, List<Guid> collectionIds)
{
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id)
.Returns(new Organization
{
PlanType = Enums.PlanType.EnterpriseAnnually,
MaxStorageGb = 100
});
var cipherInfos = ciphers.Select(c => (c,
string.IsNullOrEmpty(revisionDateString) ? null : (DateTime?)c.RevisionDate));
var sharingUserId = ciphers.First().UserId.Value;
await sutProvider.Sut.ShareManyAsync(cipherInfos, organization.Id, collectionIds, sharingUserId);
await sutProvider.GetDependency<ICipherRepository>().Received(1).UpdateCiphersAsync(sharingUserId,
Arg.Is<IEnumerable<Cipher>>(arg => arg.Except(ciphers).IsNullOrEmpty()));
}
[Theory]
[InlineKnownUserCipherAutoData("c64d8a15-606e-41d6-9c7e-174d4d8f3b2e", "c64d8a15-606e-41d6-9c7e-174d4d8f3b2e")]
[InlineOrganizationCipherAutoData("c64d8a15-606e-41d6-9c7e-174d4d8f3b2e")]
public async Task RestoreAsync_UpdatesCipher(Guid restoringUserId, Cipher cipher, SutProvider<CipherService> sutProvider)
{
sutProvider.GetDependency<ICipherRepository>().GetCanEditByIdAsync(restoringUserId, cipher.Id).Returns(true);
var initialRevisionDate = new DateTime(1970, 1, 1, 0, 0, 0);
cipher.DeletedDate = initialRevisionDate;
cipher.RevisionDate = initialRevisionDate;
await sutProvider.Sut.RestoreAsync(cipher, restoringUserId, cipher.OrganizationId.HasValue);
Assert.Null(cipher.DeletedDate);
Assert.NotEqual(initialRevisionDate, cipher.RevisionDate);
}
[Theory]
[InlineKnownUserCipherAutoData("c64d8a15-606e-41d6-9c7e-174d4d8f3b2e", "c64d8a15-606e-41d6-9c7e-174d4d8f3b2e")]
public async Task RestoreManyAsync_UpdatesCiphers(Guid restoringUserId, IEnumerable<CipherDetails> ciphers,
SutProvider<CipherService> sutProvider)
{
var previousRevisionDate = DateTime.UtcNow;
foreach (var cipher in ciphers)
{
cipher.RevisionDate = previousRevisionDate;
}
[Theory]
[InlineUserCipherAutoData]
public async Task ShareManyAsync_PaidOrgWithAttachment_Passes(SutProvider<CipherService> sutProvider,
IEnumerable<Cipher> ciphers, Guid organizationId, List<Guid> collectionIds)
var revisionDate = previousRevisionDate + TimeSpan.FromMinutes(1);
sutProvider.GetDependency<ICipherRepository>().RestoreAsync(Arg.Any<IEnumerable<Guid>>(), restoringUserId)
.Returns(revisionDate);
await sutProvider.Sut.RestoreManyAsync(ciphers, restoringUserId);
foreach (var cipher in ciphers)
{
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organizationId)
.Returns(new Organization
{
PlanType = Enums.PlanType.EnterpriseAnnually,
MaxStorageGb = 100
});
ciphers.FirstOrDefault().Attachments =
"{\"attachment1\":{\"Size\":\"250\",\"FileName\":\"superCoolFile\","
+ "\"Key\":\"superCoolFile\",\"ContainerName\":\"testContainer\",\"Validated\":false}}";
var cipherInfos = ciphers.Select(c => (c,
(DateTime?)c.RevisionDate));
var sharingUserId = ciphers.First().UserId.Value;
await sutProvider.Sut.ShareManyAsync(cipherInfos, organizationId, collectionIds, sharingUserId);
await sutProvider.GetDependency<ICipherRepository>().Received(1).UpdateCiphersAsync(sharingUserId,
Arg.Is<IEnumerable<Cipher>>(arg => arg.Except(ciphers).IsNullOrEmpty()));
Assert.Null(cipher.DeletedDate);
Assert.Equal(revisionDate, cipher.RevisionDate);
}
}
[Theory]
[InlineUserCipherAutoData]
public async Task ShareManyAsync_FreeOrgWithAttachment_Throws(SutProvider<CipherService> sutProvider,
IEnumerable<Cipher> ciphers, Guid organizationId, List<Guid> collectionIds)
{
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organizationId).Returns(new Organization
{
PlanType = Enums.PlanType.Free
});
ciphers.FirstOrDefault().Attachments =
"{\"attachment1\":{\"Size\":\"250\",\"FileName\":\"superCoolFile\","
+ "\"Key\":\"superCoolFile\",\"ContainerName\":\"testContainer\",\"Validated\":false}}";
var cipherInfos = ciphers.Select(c => (c,
(DateTime?)c.RevisionDate));
var sharingUserId = ciphers.First().UserId.Value;
var exception = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.ShareManyAsync(cipherInfos, organizationId, collectionIds, sharingUserId));
Assert.Contains("This organization cannot use attachments", exception.Message);
}
[Theory]
[InlineUserCipherAutoData]
public async Task ShareManyAsync_PaidOrgWithAttachment_Passes(SutProvider<CipherService> sutProvider,
IEnumerable<Cipher> ciphers, Guid organizationId, List<Guid> collectionIds)
{
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organizationId)
.Returns(new Organization
{
PlanType = Enums.PlanType.EnterpriseAnnually,
MaxStorageGb = 100
});
ciphers.FirstOrDefault().Attachments =
"{\"attachment1\":{\"Size\":\"250\",\"FileName\":\"superCoolFile\","
+ "\"Key\":\"superCoolFile\",\"ContainerName\":\"testContainer\",\"Validated\":false}}";
var cipherInfos = ciphers.Select(c => (c,
(DateTime?)c.RevisionDate));
var sharingUserId = ciphers.First().UserId.Value;
await sutProvider.Sut.ShareManyAsync(cipherInfos, organizationId, collectionIds, sharingUserId);
await sutProvider.GetDependency<ICipherRepository>().Received(1).UpdateCiphersAsync(sharingUserId,
Arg.Is<IEnumerable<Cipher>>(arg => arg.Except(ciphers).IsNullOrEmpty()));
}
}

View File

@ -10,161 +10,160 @@ using Bit.Test.Common.AutoFixture.Attributes;
using NSubstitute;
using Xunit;
namespace Bit.Core.Test.Services
namespace Bit.Core.Test.Services;
public class CollectionServiceTest
{
public class CollectionServiceTest
[Theory, CollectionAutoData]
public async Task SaveAsync_DefaultId_CreatesCollectionInTheRepository(Collection collection, Organization organization, SutProvider<CollectionService> sutProvider)
{
[Theory, CollectionAutoData]
public async Task SaveAsync_DefaultId_CreatesCollectionInTheRepository(Collection collection, Organization organization, SutProvider<CollectionService> sutProvider)
{
collection.Id = default;
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
var utcNow = DateTime.UtcNow;
collection.Id = default;
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
var utcNow = DateTime.UtcNow;
await sutProvider.Sut.SaveAsync(collection);
await sutProvider.Sut.SaveAsync(collection);
await sutProvider.GetDependency<ICollectionRepository>().Received().CreateAsync(collection);
await sutProvider.GetDependency<IEventService>().Received()
.LogCollectionEventAsync(collection, EventType.Collection_Created);
Assert.True(collection.CreationDate - utcNow < TimeSpan.FromSeconds(1));
Assert.True(collection.RevisionDate - utcNow < TimeSpan.FromSeconds(1));
}
await sutProvider.GetDependency<ICollectionRepository>().Received().CreateAsync(collection);
await sutProvider.GetDependency<IEventService>().Received()
.LogCollectionEventAsync(collection, EventType.Collection_Created);
Assert.True(collection.CreationDate - utcNow < TimeSpan.FromSeconds(1));
Assert.True(collection.RevisionDate - utcNow < TimeSpan.FromSeconds(1));
}
[Theory, CollectionAutoData]
public async Task SaveAsync_DefaultIdWithGroups_CreateCollectionWithGroupsInRepository(Collection collection,
IEnumerable<SelectionReadOnly> groups, Organization organization, SutProvider<CollectionService> sutProvider)
{
collection.Id = default;
organization.UseGroups = true;
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
var utcNow = DateTime.UtcNow;
[Theory, CollectionAutoData]
public async Task SaveAsync_DefaultIdWithGroups_CreateCollectionWithGroupsInRepository(Collection collection,
IEnumerable<SelectionReadOnly> groups, Organization organization, SutProvider<CollectionService> sutProvider)
{
collection.Id = default;
organization.UseGroups = true;
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
var utcNow = DateTime.UtcNow;
await sutProvider.Sut.SaveAsync(collection, groups);
await sutProvider.Sut.SaveAsync(collection, groups);
await sutProvider.GetDependency<ICollectionRepository>().Received().CreateAsync(collection, groups);
await sutProvider.GetDependency<IEventService>().Received()
.LogCollectionEventAsync(collection, EventType.Collection_Created);
Assert.True(collection.CreationDate - utcNow < TimeSpan.FromSeconds(1));
Assert.True(collection.RevisionDate - utcNow < TimeSpan.FromSeconds(1));
}
await sutProvider.GetDependency<ICollectionRepository>().Received().CreateAsync(collection, groups);
await sutProvider.GetDependency<IEventService>().Received()
.LogCollectionEventAsync(collection, EventType.Collection_Created);
Assert.True(collection.CreationDate - utcNow < TimeSpan.FromSeconds(1));
Assert.True(collection.RevisionDate - utcNow < TimeSpan.FromSeconds(1));
}
[Theory, CollectionAutoData]
public async Task SaveAsync_NonDefaultId_ReplacesCollectionInRepository(Collection collection, Organization organization, SutProvider<CollectionService> sutProvider)
{
var creationDate = collection.CreationDate;
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
var utcNow = DateTime.UtcNow;
[Theory, CollectionAutoData]
public async Task SaveAsync_NonDefaultId_ReplacesCollectionInRepository(Collection collection, Organization organization, SutProvider<CollectionService> sutProvider)
{
var creationDate = collection.CreationDate;
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
var utcNow = DateTime.UtcNow;
await sutProvider.Sut.SaveAsync(collection);
await sutProvider.Sut.SaveAsync(collection);
await sutProvider.GetDependency<ICollectionRepository>().Received().ReplaceAsync(collection);
await sutProvider.GetDependency<IEventService>().Received()
.LogCollectionEventAsync(collection, EventType.Collection_Updated);
Assert.Equal(collection.CreationDate, creationDate);
Assert.True(collection.RevisionDate - utcNow < TimeSpan.FromSeconds(1));
}
await sutProvider.GetDependency<ICollectionRepository>().Received().ReplaceAsync(collection);
await sutProvider.GetDependency<IEventService>().Received()
.LogCollectionEventAsync(collection, EventType.Collection_Updated);
Assert.Equal(collection.CreationDate, creationDate);
Assert.True(collection.RevisionDate - utcNow < TimeSpan.FromSeconds(1));
}
[Theory, CollectionAutoData]
public async Task SaveAsync_OrganizationNotUseGroup_CreateCollectionWithoutGroupsInRepository(Collection collection, IEnumerable<SelectionReadOnly> groups,
Organization organization, SutProvider<CollectionService> sutProvider)
{
collection.Id = default;
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
var utcNow = DateTime.UtcNow;
[Theory, CollectionAutoData]
public async Task SaveAsync_OrganizationNotUseGroup_CreateCollectionWithoutGroupsInRepository(Collection collection, IEnumerable<SelectionReadOnly> groups,
Organization organization, SutProvider<CollectionService> sutProvider)
{
collection.Id = default;
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
var utcNow = DateTime.UtcNow;
await sutProvider.Sut.SaveAsync(collection, groups);
await sutProvider.Sut.SaveAsync(collection, groups);
await sutProvider.GetDependency<ICollectionRepository>().Received().CreateAsync(collection);
await sutProvider.GetDependency<IEventService>().Received()
.LogCollectionEventAsync(collection, EventType.Collection_Created);
Assert.True(collection.CreationDate - utcNow < TimeSpan.FromSeconds(1));
Assert.True(collection.RevisionDate - utcNow < TimeSpan.FromSeconds(1));
}
await sutProvider.GetDependency<ICollectionRepository>().Received().CreateAsync(collection);
await sutProvider.GetDependency<IEventService>().Received()
.LogCollectionEventAsync(collection, EventType.Collection_Created);
Assert.True(collection.CreationDate - utcNow < TimeSpan.FromSeconds(1));
Assert.True(collection.RevisionDate - utcNow < TimeSpan.FromSeconds(1));
}
[Theory, CollectionAutoData]
public async Task SaveAsync_DefaultIdWithUserId_UpdateUserInCollectionRepository(Collection collection,
Organization organization, OrganizationUser organizationUser, SutProvider<CollectionService> sutProvider)
{
collection.Id = default;
organizationUser.Status = OrganizationUserStatusType.Confirmed;
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
sutProvider.GetDependency<IOrganizationUserRepository>().GetByOrganizationAsync(organization.Id, organizationUser.Id)
.Returns(organizationUser);
var utcNow = DateTime.UtcNow;
[Theory, CollectionAutoData]
public async Task SaveAsync_DefaultIdWithUserId_UpdateUserInCollectionRepository(Collection collection,
Organization organization, OrganizationUser organizationUser, SutProvider<CollectionService> sutProvider)
{
collection.Id = default;
organizationUser.Status = OrganizationUserStatusType.Confirmed;
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
sutProvider.GetDependency<IOrganizationUserRepository>().GetByOrganizationAsync(organization.Id, organizationUser.Id)
.Returns(organizationUser);
var utcNow = DateTime.UtcNow;
await sutProvider.Sut.SaveAsync(collection, null, organizationUser.Id);
await sutProvider.Sut.SaveAsync(collection, null, organizationUser.Id);
await sutProvider.GetDependency<ICollectionRepository>().Received().CreateAsync(collection);
await sutProvider.GetDependency<IOrganizationUserRepository>().Received()
.GetByOrganizationAsync(organization.Id, organizationUser.Id);
await sutProvider.GetDependency<ICollectionRepository>().Received().UpdateUsersAsync(collection.Id, Arg.Any<List<SelectionReadOnly>>());
await sutProvider.GetDependency<IEventService>().Received()
.LogCollectionEventAsync(collection, EventType.Collection_Created);
Assert.True(collection.CreationDate - utcNow < TimeSpan.FromSeconds(1));
Assert.True(collection.RevisionDate - utcNow < TimeSpan.FromSeconds(1));
}
await sutProvider.GetDependency<ICollectionRepository>().Received().CreateAsync(collection);
await sutProvider.GetDependency<IOrganizationUserRepository>().Received()
.GetByOrganizationAsync(organization.Id, organizationUser.Id);
await sutProvider.GetDependency<ICollectionRepository>().Received().UpdateUsersAsync(collection.Id, Arg.Any<List<SelectionReadOnly>>());
await sutProvider.GetDependency<IEventService>().Received()
.LogCollectionEventAsync(collection, EventType.Collection_Created);
Assert.True(collection.CreationDate - utcNow < TimeSpan.FromSeconds(1));
Assert.True(collection.RevisionDate - utcNow < TimeSpan.FromSeconds(1));
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task SaveAsync_NonExistingOrganizationId_ThrowsBadRequest(Collection collection, SutProvider<CollectionService> sutProvider)
{
var ex = await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.SaveAsync(collection));
Assert.Contains("Organization not found", ex.Message);
await sutProvider.GetDependency<ICollectionRepository>().DidNotReceiveWithAnyArgs().CreateAsync(default);
await sutProvider.GetDependency<ICollectionRepository>().DidNotReceiveWithAnyArgs().CreateAsync(default, default);
await sutProvider.GetDependency<ICollectionRepository>().DidNotReceiveWithAnyArgs().ReplaceAsync(default);
await sutProvider.GetDependency<IEventService>().DidNotReceiveWithAnyArgs().LogCollectionEventAsync(default, default);
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task SaveAsync_NonExistingOrganizationId_ThrowsBadRequest(Collection collection, SutProvider<CollectionService> sutProvider)
{
var ex = await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.SaveAsync(collection));
Assert.Contains("Organization not found", ex.Message);
await sutProvider.GetDependency<ICollectionRepository>().DidNotReceiveWithAnyArgs().CreateAsync(default);
await sutProvider.GetDependency<ICollectionRepository>().DidNotReceiveWithAnyArgs().CreateAsync(default, default);
await sutProvider.GetDependency<ICollectionRepository>().DidNotReceiveWithAnyArgs().ReplaceAsync(default);
await sutProvider.GetDependency<IEventService>().DidNotReceiveWithAnyArgs().LogCollectionEventAsync(default, default);
}
[Theory, CollectionAutoData]
public async Task SaveAsync_ExceedsOrganizationMaxCollections_ThrowsBadRequest(Collection collection, Organization organization, SutProvider<CollectionService> sutProvider)
{
collection.Id = default;
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
sutProvider.GetDependency<ICollectionRepository>().GetCountByOrganizationIdAsync(organization.Id)
.Returns(organization.MaxCollections.Value);
[Theory, CollectionAutoData]
public async Task SaveAsync_ExceedsOrganizationMaxCollections_ThrowsBadRequest(Collection collection, Organization organization, SutProvider<CollectionService> sutProvider)
{
collection.Id = default;
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
sutProvider.GetDependency<ICollectionRepository>().GetCountByOrganizationIdAsync(organization.Id)
.Returns(organization.MaxCollections.Value);
var ex = await Assert.ThrowsAsync<BadRequestException>(() => 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<ICollectionRepository>().DidNotReceiveWithAnyArgs().CreateAsync(default);
await sutProvider.GetDependency<ICollectionRepository>().DidNotReceiveWithAnyArgs().CreateAsync(default, default);
await sutProvider.GetDependency<ICollectionRepository>().DidNotReceiveWithAnyArgs().ReplaceAsync(default);
await sutProvider.GetDependency<IEventService>().DidNotReceiveWithAnyArgs().LogCollectionEventAsync(default, default);
}
var ex = await Assert.ThrowsAsync<BadRequestException>(() => 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<ICollectionRepository>().DidNotReceiveWithAnyArgs().CreateAsync(default);
await sutProvider.GetDependency<ICollectionRepository>().DidNotReceiveWithAnyArgs().CreateAsync(default, default);
await sutProvider.GetDependency<ICollectionRepository>().DidNotReceiveWithAnyArgs().ReplaceAsync(default);
await sutProvider.GetDependency<IEventService>().DidNotReceiveWithAnyArgs().LogCollectionEventAsync(default, default);
}
[Theory, CollectionAutoData]
public async Task DeleteUserAsync_DeletesValidUserWhoBelongsToCollection(Collection collection,
Organization organization, OrganizationUser organizationUser, SutProvider<CollectionService> sutProvider)
{
collection.OrganizationId = organization.Id;
organizationUser.OrganizationId = organization.Id;
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
sutProvider.GetDependency<IOrganizationUserRepository>().GetByIdAsync(organizationUser.Id)
.Returns(organizationUser);
[Theory, CollectionAutoData]
public async Task DeleteUserAsync_DeletesValidUserWhoBelongsToCollection(Collection collection,
Organization organization, OrganizationUser organizationUser, SutProvider<CollectionService> sutProvider)
{
collection.OrganizationId = organization.Id;
organizationUser.OrganizationId = organization.Id;
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
sutProvider.GetDependency<IOrganizationUserRepository>().GetByIdAsync(organizationUser.Id)
.Returns(organizationUser);
await sutProvider.Sut.DeleteUserAsync(collection, organizationUser.Id);
await sutProvider.Sut.DeleteUserAsync(collection, organizationUser.Id);
await sutProvider.GetDependency<ICollectionRepository>().Received()
.DeleteUserAsync(collection.Id, organizationUser.Id);
await sutProvider.GetDependency<IEventService>().Received().LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Updated);
}
await sutProvider.GetDependency<ICollectionRepository>().Received()
.DeleteUserAsync(collection.Id, organizationUser.Id);
await sutProvider.GetDependency<IEventService>().Received().LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Updated);
}
[Theory, CollectionAutoData]
public async Task DeleteUserAsync_InvalidUser_ThrowsNotFound(Collection collection, Organization organization,
OrganizationUser organizationUser, SutProvider<CollectionService> sutProvider)
{
collection.OrganizationId = organization.Id;
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
sutProvider.GetDependency<IOrganizationUserRepository>().GetByIdAsync(organizationUser.Id)
.Returns(organizationUser);
[Theory, CollectionAutoData]
public async Task DeleteUserAsync_InvalidUser_ThrowsNotFound(Collection collection, Organization organization,
OrganizationUser organizationUser, SutProvider<CollectionService> sutProvider)
{
collection.OrganizationId = organization.Id;
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
sutProvider.GetDependency<IOrganizationUserRepository>().GetByIdAsync(organizationUser.Id)
.Returns(organizationUser);
// user not in organization
await Assert.ThrowsAsync<NotFoundException>(() =>
sutProvider.Sut.DeleteUserAsync(collection, organizationUser.Id));
// invalid user
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.DeleteUserAsync(collection, Guid.NewGuid()));
await sutProvider.GetDependency<ICollectionRepository>().DidNotReceiveWithAnyArgs().DeleteUserAsync(default, default);
await sutProvider.GetDependency<IEventService>().DidNotReceiveWithAnyArgs()
.LogOrganizationUserEventAsync(default, default);
}
// user not in organization
await Assert.ThrowsAsync<NotFoundException>(() =>
sutProvider.Sut.DeleteUserAsync(collection, organizationUser.Id));
// invalid user
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.DeleteUserAsync(collection, Guid.NewGuid()));
await sutProvider.GetDependency<ICollectionRepository>().DidNotReceiveWithAnyArgs().DeleteUserAsync(default, default);
await sutProvider.GetDependency<IEventService>().DidNotReceiveWithAnyArgs()
.LogOrganizationUserEventAsync(default, default);
}
}

View File

@ -5,33 +5,32 @@ using Bit.Core.Services;
using NSubstitute;
using Xunit;
namespace Bit.Core.Test.Services
namespace Bit.Core.Test.Services;
public class DeviceServiceTests
{
public class DeviceServiceTests
[Fact]
public async Task DeviceSaveShouldUpdateRevisionDateAndPushRegistration()
{
[Fact]
public async Task DeviceSaveShouldUpdateRevisionDateAndPushRegistration()
var deviceRepo = Substitute.For<IDeviceRepository>();
var pushRepo = Substitute.For<IPushRegistrationService>();
var deviceService = new DeviceService(deviceRepo, pushRepo);
var id = Guid.NewGuid();
var userId = Guid.NewGuid();
var device = new Device
{
var deviceRepo = Substitute.For<IDeviceRepository>();
var pushRepo = Substitute.For<IPushRegistrationService>();
var deviceService = new DeviceService(deviceRepo, pushRepo);
Id = id,
Name = "test device",
Type = DeviceType.Android,
UserId = userId,
PushToken = "testtoken",
Identifier = "testid"
};
await deviceService.SaveAsync(device);
var id = Guid.NewGuid();
var userId = Guid.NewGuid();
var device = new Device
{
Id = id,
Name = "test device",
Type = DeviceType.Android,
UserId = userId,
PushToken = "testtoken",
Identifier = "testid"
};
await deviceService.SaveAsync(device);
Assert.True(device.RevisionDate - DateTime.UtcNow < TimeSpan.FromSeconds(1));
await pushRepo.Received().CreateOrUpdateRegistrationAsync("testtoken", id.ToString(),
userId.ToString(), "testid", DeviceType.Android);
}
Assert.True(device.RevisionDate - DateTime.UtcNow < TimeSpan.FromSeconds(1));
await pushRepo.Received().CreateOrUpdateRegistrationAsync("testtoken", id.ToString(),
userId.ToString(), "testid", DeviceType.Android);
}
}

View File

@ -9,163 +9,162 @@ using Bit.Test.Common.AutoFixture.Attributes;
using NSubstitute;
using Xunit;
namespace Bit.Core.Test.Services
namespace Bit.Core.Test.Services;
public class EmergencyAccessServiceTests
{
public class EmergencyAccessServiceTests
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task SaveAsync_PremiumCannotUpdate(
SutProvider<EmergencyAccessService> sutProvider, User savingUser)
{
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task SaveAsync_PremiumCannotUpdate(
SutProvider<EmergencyAccessService> sutProvider, User savingUser)
savingUser.Premium = false;
var emergencyAccess = new EmergencyAccess
{
savingUser.Premium = false;
var emergencyAccess = new EmergencyAccess
{
Type = Enums.EmergencyAccessType.Takeover,
GrantorId = savingUser.Id,
};
Type = Enums.EmergencyAccessType.Takeover,
GrantorId = savingUser.Id,
};
sutProvider.GetDependency<IUserService>().GetUserByIdAsync(savingUser.Id).Returns(savingUser);
sutProvider.GetDependency<IUserService>().GetUserByIdAsync(savingUser.Id).Returns(savingUser);
var exception = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.SaveAsync(emergencyAccess, savingUser));
var exception = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.SaveAsync(emergencyAccess, savingUser));
Assert.Contains("Not a premium user.", exception.Message);
await sutProvider.GetDependency<IEmergencyAccessRepository>().DidNotReceiveWithAnyArgs().ReplaceAsync(default);
}
Assert.Contains("Not a premium user.", exception.Message);
await sutProvider.GetDependency<IEmergencyAccessRepository>().DidNotReceiveWithAnyArgs().ReplaceAsync(default);
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task InviteAsync_UserWithKeyConnectorCannotUseTakeover(
SutProvider<EmergencyAccessService> sutProvider, User invitingUser, string email, int waitTime)
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task InviteAsync_UserWithKeyConnectorCannotUseTakeover(
SutProvider<EmergencyAccessService> sutProvider, User invitingUser, string email, int waitTime)
{
invitingUser.UsesKeyConnector = true;
sutProvider.GetDependency<IUserService>().CanAccessPremium(invitingUser).Returns(true);
var exception = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.InviteAsync(invitingUser, email, Enums.EmergencyAccessType.Takeover, waitTime));
Assert.Contains("You cannot use Emergency Access Takeover because you are using Key Connector", exception.Message);
await sutProvider.GetDependency<IEmergencyAccessRepository>().DidNotReceiveWithAnyArgs().CreateAsync(default);
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task ConfirmUserAsync_UserWithKeyConnectorCannotUseTakeover(
SutProvider<EmergencyAccessService> sutProvider, User confirmingUser, string key)
{
confirmingUser.UsesKeyConnector = true;
var emergencyAccess = new EmergencyAccess
{
invitingUser.UsesKeyConnector = true;
sutProvider.GetDependency<IUserService>().CanAccessPremium(invitingUser).Returns(true);
Status = Enums.EmergencyAccessStatusType.Accepted,
GrantorId = confirmingUser.Id,
Type = Enums.EmergencyAccessType.Takeover,
};
var exception = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.InviteAsync(invitingUser, email, Enums.EmergencyAccessType.Takeover, waitTime));
sutProvider.GetDependency<IUserRepository>().GetByIdAsync(confirmingUser.Id).Returns(confirmingUser);
sutProvider.GetDependency<IEmergencyAccessRepository>().GetByIdAsync(Arg.Any<Guid>()).Returns(emergencyAccess);
Assert.Contains("You cannot use Emergency Access Takeover because you are using Key Connector", exception.Message);
await sutProvider.GetDependency<IEmergencyAccessRepository>().DidNotReceiveWithAnyArgs().CreateAsync(default);
}
var exception = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.ConfirmUserAsync(new Guid(), key, confirmingUser.Id));
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task ConfirmUserAsync_UserWithKeyConnectorCannotUseTakeover(
SutProvider<EmergencyAccessService> sutProvider, User confirmingUser, string key)
Assert.Contains("You cannot use Emergency Access Takeover because you are using Key Connector", exception.Message);
await sutProvider.GetDependency<IEmergencyAccessRepository>().DidNotReceiveWithAnyArgs().ReplaceAsync(default);
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task SaveAsync_UserWithKeyConnectorCannotUseTakeover(
SutProvider<EmergencyAccessService> sutProvider, User savingUser)
{
savingUser.UsesKeyConnector = true;
var emergencyAccess = new EmergencyAccess
{
confirmingUser.UsesKeyConnector = true;
var emergencyAccess = new EmergencyAccess
{
Status = Enums.EmergencyAccessStatusType.Accepted,
GrantorId = confirmingUser.Id,
Type = Enums.EmergencyAccessType.Takeover,
};
Type = Enums.EmergencyAccessType.Takeover,
GrantorId = savingUser.Id,
};
sutProvider.GetDependency<IUserRepository>().GetByIdAsync(confirmingUser.Id).Returns(confirmingUser);
sutProvider.GetDependency<IEmergencyAccessRepository>().GetByIdAsync(Arg.Any<Guid>()).Returns(emergencyAccess);
var userService = sutProvider.GetDependency<IUserService>();
userService.GetUserByIdAsync(savingUser.Id).Returns(savingUser);
userService.CanAccessPremium(savingUser).Returns(true);
var exception = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.ConfirmUserAsync(new Guid(), key, confirmingUser.Id));
var exception = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.SaveAsync(emergencyAccess, savingUser));
Assert.Contains("You cannot use Emergency Access Takeover because you are using Key Connector", exception.Message);
await sutProvider.GetDependency<IEmergencyAccessRepository>().DidNotReceiveWithAnyArgs().ReplaceAsync(default);
}
Assert.Contains("You cannot use Emergency Access Takeover because you are using Key Connector", exception.Message);
await sutProvider.GetDependency<IEmergencyAccessRepository>().DidNotReceiveWithAnyArgs().ReplaceAsync(default);
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task SaveAsync_UserWithKeyConnectorCannotUseTakeover(
SutProvider<EmergencyAccessService> sutProvider, User savingUser)
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task InitiateAsync_UserWithKeyConnectorCannotUseTakeover(
SutProvider<EmergencyAccessService> sutProvider, User initiatingUser, User grantor)
{
grantor.UsesKeyConnector = true;
var emergencyAccess = new EmergencyAccess
{
savingUser.UsesKeyConnector = true;
var emergencyAccess = new EmergencyAccess
{
Type = Enums.EmergencyAccessType.Takeover,
GrantorId = savingUser.Id,
};
Status = Enums.EmergencyAccessStatusType.Confirmed,
GranteeId = initiatingUser.Id,
GrantorId = grantor.Id,
Type = Enums.EmergencyAccessType.Takeover,
};
var userService = sutProvider.GetDependency<IUserService>();
userService.GetUserByIdAsync(savingUser.Id).Returns(savingUser);
userService.CanAccessPremium(savingUser).Returns(true);
sutProvider.GetDependency<IEmergencyAccessRepository>().GetByIdAsync(Arg.Any<Guid>()).Returns(emergencyAccess);
sutProvider.GetDependency<IUserRepository>().GetByIdAsync(grantor.Id).Returns(grantor);
var exception = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.SaveAsync(emergencyAccess, savingUser));
var exception = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.InitiateAsync(new Guid(), initiatingUser));
Assert.Contains("You cannot use Emergency Access Takeover because you are using Key Connector", exception.Message);
await sutProvider.GetDependency<IEmergencyAccessRepository>().DidNotReceiveWithAnyArgs().ReplaceAsync(default);
}
Assert.Contains("You cannot takeover an account that is using Key Connector", exception.Message);
await sutProvider.GetDependency<IEmergencyAccessRepository>().DidNotReceiveWithAnyArgs().ReplaceAsync(default);
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task InitiateAsync_UserWithKeyConnectorCannotUseTakeover(
SutProvider<EmergencyAccessService> sutProvider, User initiatingUser, User grantor)
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task TakeoverAsync_UserWithKeyConnectorCannotUseTakeover(
SutProvider<EmergencyAccessService> sutProvider, User requestingUser, User grantor)
{
grantor.UsesKeyConnector = true;
var emergencyAccess = new EmergencyAccess
{
grantor.UsesKeyConnector = true;
var emergencyAccess = new EmergencyAccess
{
Status = Enums.EmergencyAccessStatusType.Confirmed,
GranteeId = initiatingUser.Id,
GrantorId = grantor.Id,
Type = Enums.EmergencyAccessType.Takeover,
};
GrantorId = grantor.Id,
GranteeId = requestingUser.Id,
Status = Enums.EmergencyAccessStatusType.RecoveryApproved,
Type = Enums.EmergencyAccessType.Takeover,
};
sutProvider.GetDependency<IEmergencyAccessRepository>().GetByIdAsync(Arg.Any<Guid>()).Returns(emergencyAccess);
sutProvider.GetDependency<IUserRepository>().GetByIdAsync(grantor.Id).Returns(grantor);
sutProvider.GetDependency<IEmergencyAccessRepository>().GetByIdAsync(Arg.Any<Guid>()).Returns(emergencyAccess);
sutProvider.GetDependency<IUserRepository>().GetByIdAsync(grantor.Id).Returns(grantor);
var exception = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.InitiateAsync(new Guid(), initiatingUser));
var exception = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.TakeoverAsync(new Guid(), requestingUser));
Assert.Contains("You cannot takeover an account that is using Key Connector", exception.Message);
await sutProvider.GetDependency<IEmergencyAccessRepository>().DidNotReceiveWithAnyArgs().ReplaceAsync(default);
}
Assert.Contains("You cannot takeover an account that is using Key Connector", exception.Message);
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task TakeoverAsync_UserWithKeyConnectorCannotUseTakeover(
SutProvider<EmergencyAccessService> sutProvider, User requestingUser, User grantor)
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task PasswordAsync_Disables_2FA_Providers_And_Unknown_Device_Verification_On_The_Grantor(
SutProvider<EmergencyAccessService> sutProvider, User requestingUser, User grantor)
{
grantor.UsesKeyConnector = true;
grantor.UnknownDeviceVerificationEnabled = true;
grantor.SetTwoFactorProviders(new Dictionary<TwoFactorProviderType, TwoFactorProvider>
{
grantor.UsesKeyConnector = true;
var emergencyAccess = new EmergencyAccess
[TwoFactorProviderType.Email] = new TwoFactorProvider
{
GrantorId = grantor.Id,
GranteeId = requestingUser.Id,
Status = Enums.EmergencyAccessStatusType.RecoveryApproved,
Type = Enums.EmergencyAccessType.Takeover,
};
sutProvider.GetDependency<IEmergencyAccessRepository>().GetByIdAsync(Arg.Any<Guid>()).Returns(emergencyAccess);
sutProvider.GetDependency<IUserRepository>().GetByIdAsync(grantor.Id).Returns(grantor);
var exception = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.TakeoverAsync(new Guid(), requestingUser));
Assert.Contains("You cannot takeover an account that is using Key Connector", exception.Message);
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task PasswordAsync_Disables_2FA_Providers_And_Unknown_Device_Verification_On_The_Grantor(
SutProvider<EmergencyAccessService> sutProvider, User requestingUser, User grantor)
MetaData = new Dictionary<string, object> { ["Email"] = "asdfasf" },
Enabled = true
}
});
var emergencyAccess = new EmergencyAccess
{
grantor.UsesKeyConnector = true;
grantor.UnknownDeviceVerificationEnabled = true;
grantor.SetTwoFactorProviders(new Dictionary<TwoFactorProviderType, TwoFactorProvider>
{
[TwoFactorProviderType.Email] = new TwoFactorProvider
{
MetaData = new Dictionary<string, object> { ["Email"] = "asdfasf" },
Enabled = true
}
});
var emergencyAccess = new EmergencyAccess
{
GrantorId = grantor.Id,
GranteeId = requestingUser.Id,
Status = Enums.EmergencyAccessStatusType.RecoveryApproved,
Type = Enums.EmergencyAccessType.Takeover,
};
GrantorId = grantor.Id,
GranteeId = requestingUser.Id,
Status = Enums.EmergencyAccessStatusType.RecoveryApproved,
Type = Enums.EmergencyAccessType.Takeover,
};
sutProvider.GetDependency<IEmergencyAccessRepository>().GetByIdAsync(Arg.Any<Guid>()).Returns(emergencyAccess);
sutProvider.GetDependency<IUserRepository>().GetByIdAsync(grantor.Id).Returns(grantor);
sutProvider.GetDependency<IEmergencyAccessRepository>().GetByIdAsync(Arg.Any<Guid>()).Returns(emergencyAccess);
sutProvider.GetDependency<IUserRepository>().GetByIdAsync(grantor.Id).Returns(grantor);
await sutProvider.Sut.PasswordAsync(Guid.NewGuid(), requestingUser, "blablahash", "blablakey");
await sutProvider.Sut.PasswordAsync(Guid.NewGuid(), requestingUser, "blablahash", "blablakey");
Assert.False(grantor.UnknownDeviceVerificationEnabled);
Assert.Empty(grantor.GetTwoFactorProviders());
await sutProvider.GetDependency<IUserRepository>().Received().ReplaceAsync(grantor);
}
Assert.False(grantor.UnknownDeviceVerificationEnabled);
Assert.Empty(grantor.GetTwoFactorProviders());
await sutProvider.GetDependency<IUserRepository>().Received().ReplaceAsync(grantor);
}
}

View File

@ -11,99 +11,98 @@ using Bit.Test.Common.Helpers;
using NSubstitute;
using Xunit;
namespace Bit.Core.Test.Services
namespace Bit.Core.Test.Services;
[SutProviderCustomize]
public class EventServiceTests
{
[SutProviderCustomize]
public class EventServiceTests
public static IEnumerable<object[]> InstallationIdTestCases => TestCaseHelper.GetCombinationsOfMultipleLists(
new object[] { Guid.NewGuid(), null },
Enum.GetValues<EventType>().Select(e => (object)e)
).Select(p => p.ToArray());
[Theory]
[BitMemberAutoData(nameof(InstallationIdTestCases))]
public async Task LogOrganizationEvent_ProvidesInstallationId(Guid? installationId, EventType eventType,
Organization organization, SutProvider<EventService> sutProvider)
{
public static IEnumerable<object[]> InstallationIdTestCases => TestCaseHelper.GetCombinationsOfMultipleLists(
new object[] { Guid.NewGuid(), null },
Enum.GetValues<EventType>().Select(e => (object)e)
).Select(p => p.ToArray());
organization.Enabled = true;
organization.UseEvents = true;
[Theory]
[BitMemberAutoData(nameof(InstallationIdTestCases))]
public async Task LogOrganizationEvent_ProvidesInstallationId(Guid? installationId, EventType eventType,
Organization organization, SutProvider<EventService> sutProvider)
sutProvider.GetDependency<ICurrentContext>().InstallationId.Returns(installationId);
await sutProvider.Sut.LogOrganizationEventAsync(organization, eventType);
await sutProvider.GetDependency<IEventWriteService>().Received(1).CreateAsync(Arg.Is<IEvent>(e =>
e.OrganizationId == organization.Id &&
e.Type == eventType &&
e.InstallationId == installationId));
}
[Theory, BitAutoData]
public async Task LogOrganizationUserEvent_LogsRequiredInfo(OrganizationUser orgUser, EventType eventType, DateTime date,
Guid actingUserId, Guid providerId, string ipAddress, DeviceType deviceType, SutProvider<EventService> sutProvider)
{
var orgAbilities = new Dictionary<Guid, OrganizationAbility>()
{
organization.Enabled = true;
organization.UseEvents = true;
{orgUser.OrganizationId, new OrganizationAbility() { UseEvents = true, Enabled = true } }
};
sutProvider.GetDependency<IApplicationCacheService>().GetOrganizationAbilitiesAsync().Returns(orgAbilities);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().IpAddress.Returns(ipAddress);
sutProvider.GetDependency<ICurrentContext>().DeviceType.Returns(deviceType);
sutProvider.GetDependency<ICurrentContext>().ProviderIdForOrg(Arg.Any<Guid>()).Returns(providerId);
sutProvider.GetDependency<ICurrentContext>().InstallationId.Returns(installationId);
await sutProvider.Sut.LogOrganizationUserEventAsync(orgUser, eventType, date);
await sutProvider.Sut.LogOrganizationEventAsync(organization, eventType);
await sutProvider.GetDependency<IEventWriteService>().Received(1).CreateAsync(Arg.Is<IEvent>(e =>
e.OrganizationId == organization.Id &&
e.Type == eventType &&
e.InstallationId == installationId));
}
[Theory, BitAutoData]
public async Task LogOrganizationUserEvent_LogsRequiredInfo(OrganizationUser orgUser, EventType eventType, DateTime date,
Guid actingUserId, Guid providerId, string ipAddress, DeviceType deviceType, SutProvider<EventService> sutProvider)
{
var orgAbilities = new Dictionary<Guid, OrganizationAbility>()
var expected = new List<IEvent>() {
new EventMessage()
{
{orgUser.OrganizationId, new OrganizationAbility() { UseEvents = true, Enabled = true } }
};
sutProvider.GetDependency<IApplicationCacheService>().GetOrganizationAbilitiesAsync().Returns(orgAbilities);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().IpAddress.Returns(ipAddress);
sutProvider.GetDependency<ICurrentContext>().DeviceType.Returns(deviceType);
sutProvider.GetDependency<ICurrentContext>().ProviderIdForOrg(Arg.Any<Guid>()).Returns(providerId);
IpAddress = ipAddress,
DeviceType = deviceType,
OrganizationId = orgUser.OrganizationId,
UserId = orgUser.UserId,
OrganizationUserId = orgUser.Id,
ProviderId = providerId,
Type = eventType,
ActingUserId = actingUserId,
Date = date
}
};
await sutProvider.Sut.LogOrganizationUserEventAsync(orgUser, eventType, date);
await sutProvider.GetDependency<IEventWriteService>().Received(1).CreateManyAsync(Arg.Is(AssertHelper.AssertPropertyEqual<IEvent>(expected, new[] { "IdempotencyId" })));
}
var expected = new List<IEvent>() {
new EventMessage()
{
IpAddress = ipAddress,
DeviceType = deviceType,
OrganizationId = orgUser.OrganizationId,
UserId = orgUser.UserId,
OrganizationUserId = orgUser.Id,
ProviderId = providerId,
Type = eventType,
ActingUserId = actingUserId,
Date = date
}
};
await sutProvider.GetDependency<IEventWriteService>().Received(1).CreateManyAsync(Arg.Is(AssertHelper.AssertPropertyEqual<IEvent>(expected, new[] { "IdempotencyId" })));
}
[Theory, BitAutoData]
public async Task LogProviderUserEvent_LogsRequiredInfo(ProviderUser providerUser, EventType eventType, DateTime date,
Guid actingUserId, Guid providerId, string ipAddress, DeviceType deviceType, SutProvider<EventService> sutProvider)
[Theory, BitAutoData]
public async Task LogProviderUserEvent_LogsRequiredInfo(ProviderUser providerUser, EventType eventType, DateTime date,
Guid actingUserId, Guid providerId, string ipAddress, DeviceType deviceType, SutProvider<EventService> sutProvider)
{
var providerAbilities = new Dictionary<Guid, ProviderAbility>()
{
var providerAbilities = new Dictionary<Guid, ProviderAbility>()
{providerUser.ProviderId, new ProviderAbility() { UseEvents = true, Enabled = true } }
};
sutProvider.GetDependency<IApplicationCacheService>().GetProviderAbilitiesAsync().Returns(providerAbilities);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().IpAddress.Returns(ipAddress);
sutProvider.GetDependency<ICurrentContext>().DeviceType.Returns(deviceType);
sutProvider.GetDependency<ICurrentContext>().ProviderIdForOrg(Arg.Any<Guid>()).Returns(providerId);
await sutProvider.Sut.LogProviderUserEventAsync(providerUser, eventType, date);
var expected = new List<IEvent>() {
new EventMessage()
{
{providerUser.ProviderId, new ProviderAbility() { UseEvents = true, Enabled = true } }
};
sutProvider.GetDependency<IApplicationCacheService>().GetProviderAbilitiesAsync().Returns(providerAbilities);
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
sutProvider.GetDependency<ICurrentContext>().IpAddress.Returns(ipAddress);
sutProvider.GetDependency<ICurrentContext>().DeviceType.Returns(deviceType);
sutProvider.GetDependency<ICurrentContext>().ProviderIdForOrg(Arg.Any<Guid>()).Returns(providerId);
IpAddress = ipAddress,
DeviceType = deviceType,
ProviderId = providerUser.ProviderId,
UserId = providerUser.UserId,
ProviderUserId = providerUser.Id,
Type = eventType,
ActingUserId = actingUserId,
Date = date
}
};
await sutProvider.Sut.LogProviderUserEventAsync(providerUser, eventType, date);
var expected = new List<IEvent>() {
new EventMessage()
{
IpAddress = ipAddress,
DeviceType = deviceType,
ProviderId = providerUser.ProviderId,
UserId = providerUser.UserId,
ProviderUserId = providerUser.Id,
Type = eventType,
ActingUserId = actingUserId,
Date = date
}
};
await sutProvider.GetDependency<IEventWriteService>().Received(1).CreateManyAsync(Arg.Is(AssertHelper.AssertPropertyEqual<IEvent>(expected, new[] { "IdempotencyId" })));
}
await sutProvider.GetDependency<IEventWriteService>().Received(1).CreateManyAsync(Arg.Is(AssertHelper.AssertPropertyEqual<IEvent>(expected, new[] { "IdempotencyId" })));
}
}

View File

@ -10,128 +10,127 @@ using Bit.Test.Common.AutoFixture.Attributes;
using NSubstitute;
using Xunit;
namespace Bit.Core.Test.Services
namespace Bit.Core.Test.Services;
public class GroupServiceTests
{
public class GroupServiceTests
[Theory, GroupOrganizationAutoData]
public async Task SaveAsync_DefaultGroupId_CreatesGroupInRepository(Group group, Organization organization, SutProvider<GroupService> sutProvider)
{
[Theory, GroupOrganizationAutoData]
public async Task SaveAsync_DefaultGroupId_CreatesGroupInRepository(Group group, Organization organization, SutProvider<GroupService> sutProvider)
{
group.Id = default(Guid);
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
organization.UseGroups = true;
var utcNow = DateTime.UtcNow;
group.Id = default(Guid);
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
organization.UseGroups = true;
var utcNow = DateTime.UtcNow;
await sutProvider.Sut.SaveAsync(group);
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));
}
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));
}
[Theory, GroupOrganizationAutoData]
public async Task SaveAsync_DefaultGroupIdAndCollections_CreatesGroupInRepository(Group group, Organization organization, List<SelectionReadOnly> collections, SutProvider<GroupService> sutProvider)
{
group.Id = default(Guid);
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
organization.UseGroups = true;
var utcNow = DateTime.UtcNow;
[Theory, GroupOrganizationAutoData]
public async Task SaveAsync_DefaultGroupIdAndCollections_CreatesGroupInRepository(Group group, Organization organization, List<SelectionReadOnly> collections, SutProvider<GroupService> sutProvider)
{
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.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));
}
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);
[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.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));
}
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, 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, CustomAutoData(typeof(SutProviderCustomization))]
public async Task SaveAsync_NonExistingOrganizationId_ThrowsBadRequest(Group group, 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);
[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));
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);
}
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);
[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);
}
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);
[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.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);
}
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);
[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);
}
// 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);
}
}

View File

@ -8,169 +8,168 @@ using Microsoft.Extensions.Logging;
using NSubstitute;
using Xunit;
namespace Bit.Core.Test.Services
namespace Bit.Core.Test.Services;
public class HandlebarsMailServiceTests
{
public class HandlebarsMailServiceTests
private readonly HandlebarsMailService _sut;
private readonly GlobalSettings _globalSettings;
private readonly IMailDeliveryService _mailDeliveryService;
private readonly IMailEnqueuingService _mailEnqueuingService;
public HandlebarsMailServiceTests()
{
private readonly HandlebarsMailService _sut;
_globalSettings = new GlobalSettings();
_mailDeliveryService = Substitute.For<IMailDeliveryService>();
_mailEnqueuingService = Substitute.For<IMailEnqueuingService>();
private readonly GlobalSettings _globalSettings;
private readonly IMailDeliveryService _mailDeliveryService;
private readonly IMailEnqueuingService _mailEnqueuingService;
_sut = new HandlebarsMailService(
_globalSettings,
_mailDeliveryService,
_mailEnqueuingService
);
}
public HandlebarsMailServiceTests()
[Fact(Skip = "For local development")]
public async Task SendAllEmails()
{
// This test is only opt in and is more for development purposes.
// This will send all emails to the test email address so that they can be viewed.
var namedParameters = new Dictionary<(string, Type), object>
{
_globalSettings = new GlobalSettings();
_mailDeliveryService = Substitute.For<IMailDeliveryService>();
_mailEnqueuingService = Substitute.For<IMailEnqueuingService>();
// TODO: Swith to use env variable
{ ("email", typeof(string)), "test@bitwarden.com" },
{ ("user", typeof(User)), new User
{
Id = Guid.NewGuid(),
Email = "test@bitwarden.com",
}},
{ ("userId", typeof(Guid)), Guid.NewGuid() },
{ ("token", typeof(string)), "test_token" },
{ ("fromEmail", typeof(string)), "test@bitwarden.com" },
{ ("toEmail", typeof(string)), "test@bitwarden.com" },
{ ("newEmailAddress", typeof(string)), "test@bitwarden.com" },
{ ("hint", typeof(string)), "Test Hint" },
{ ("organizationName", typeof(string)), "Test Organization Name" },
{ ("orgUser", typeof(OrganizationUser)), new OrganizationUser
{
Id = Guid.NewGuid(),
Email = "test@bitwarden.com",
OrganizationId = Guid.NewGuid(),
_sut = new HandlebarsMailService(
_globalSettings,
_mailDeliveryService,
_mailEnqueuingService
);
}},
{ ("token", typeof(ExpiringToken)), new ExpiringToken("test_token", DateTime.UtcNow.AddDays(1))},
{ ("organization", typeof(Organization)), new Organization
{
Id = Guid.NewGuid(),
Name = "Test Organization Name",
Seats = 5
}},
{ ("initialSeatCount", typeof(int)), 5},
{ ("ownerEmails", typeof(IEnumerable<string>)), new [] { "test@bitwarden.com" }},
{ ("maxSeatCount", typeof(int)), 5 },
{ ("userIdentifier", typeof(string)), "test_user" },
{ ("adminEmails", typeof(IEnumerable<string>)), new [] { "test@bitwarden.com" }},
{ ("returnUrl", typeof(string)), "https://bitwarden.com/" },
{ ("amount", typeof(decimal)), 1.00M },
{ ("dueDate", typeof(DateTime)), DateTime.UtcNow.AddDays(1) },
{ ("items", typeof(List<string>)), new List<string> { "test@bitwarden.com" }},
{ ("mentionInvoices", typeof(bool)), true },
{ ("emails", typeof(IEnumerable<string>)), new [] { "test@bitwarden.com" }},
{ ("deviceType", typeof(string)), "Mobile" },
{ ("timestamp", typeof(DateTime)), DateTime.UtcNow.AddDays(1)},
{ ("ip", typeof(string)), "127.0.0.1" },
{ ("emergencyAccess", typeof(EmergencyAccess)), new EmergencyAccess
{
Id = Guid.NewGuid(),
Email = "test@bitwarden.com",
}},
{ ("granteeEmail", typeof(string)), "test@bitwarden.com" },
{ ("grantorName", typeof(string)), "Test User" },
{ ("initiatingName", typeof(string)), "Test" },
{ ("approvingName", typeof(string)), "Test Name" },
{ ("rejectingName", typeof(string)), "Test Name" },
{ ("provider", typeof(Provider)), new Provider
{
Id = Guid.NewGuid(),
}},
{ ("name", typeof(string)), "Test Name" },
{ ("ea", typeof(EmergencyAccess)), new EmergencyAccess
{
Id = Guid.NewGuid(),
Email = "test@bitwarden.com",
}},
{ ("userName", typeof(string)), "testUser" },
{ ("orgName", typeof(string)), "Test Org Name" },
{ ("providerName", typeof(string)), "testProvider" },
{ ("providerUser", typeof(ProviderUser)), new ProviderUser
{
ProviderId = Guid.NewGuid(),
Id = Guid.NewGuid(),
}},
{ ("familyUserEmail", typeof(string)), "test@bitwarden.com" },
{ ("sponsorEmail", typeof(string)), "test@bitwarden.com" },
{ ("familyOrgName", typeof(string)), "Test Org Name" },
// Swap existingAccount to true or false to generate different versions of the SendFamiliesForEnterpriseOfferEmailAsync emails.
{ ("existingAccount", typeof(bool)), false },
{ ("sponsorshipEndDate", typeof(DateTime)), DateTime.UtcNow.AddDays(1)},
{ ("sponsorOrgName", typeof(string)), "Sponsor Test Org Name" },
{ ("expirationDate", typeof(DateTime)), DateTime.Now.AddDays(3) },
{ ("utcNow", typeof(DateTime)), DateTime.UtcNow },
};
var globalSettings = new GlobalSettings
{
Mail = new GlobalSettings.MailSettings
{
Smtp = new GlobalSettings.MailSettings.SmtpSettings
{
Host = "localhost",
TrustServer = true,
Port = 10250,
},
ReplyToEmail = "noreply@bitwarden.com",
},
SiteName = "Bitwarden",
};
var mailDeliveryService = new MailKitSmtpMailDeliveryService(globalSettings, Substitute.For<ILogger<MailKitSmtpMailDeliveryService>>());
var handlebarsService = new HandlebarsMailService(globalSettings, mailDeliveryService, new BlockingMailEnqueuingService());
var sendMethods = typeof(IMailService).GetMethods(BindingFlags.Public | BindingFlags.Instance)
.Where(m => m.Name.StartsWith("Send") && m.Name != "SendEnqueuedMailMessageAsync");
foreach (var sendMethod in sendMethods)
{
await InvokeMethod(sendMethod);
}
[Fact(Skip = "For local development")]
public async Task SendAllEmails()
async Task InvokeMethod(MethodInfo method)
{
// This test is only opt in and is more for development purposes.
// This will send all emails to the test email address so that they can be viewed.
var namedParameters = new Dictionary<(string, Type), object>
var parameters = method.GetParameters();
var args = new object[parameters.Length];
for (var i = 0; i < parameters.Length; i++)
{
// TODO: Swith to use env variable
{ ("email", typeof(string)), "test@bitwarden.com" },
{ ("user", typeof(User)), new User
if (!namedParameters.TryGetValue((parameters[i].Name, parameters[i].ParameterType), out var value))
{
Id = Guid.NewGuid(),
Email = "test@bitwarden.com",
}},
{ ("userId", typeof(Guid)), Guid.NewGuid() },
{ ("token", typeof(string)), "test_token" },
{ ("fromEmail", typeof(string)), "test@bitwarden.com" },
{ ("toEmail", typeof(string)), "test@bitwarden.com" },
{ ("newEmailAddress", typeof(string)), "test@bitwarden.com" },
{ ("hint", typeof(string)), "Test Hint" },
{ ("organizationName", typeof(string)), "Test Organization Name" },
{ ("orgUser", typeof(OrganizationUser)), new OrganizationUser
{
Id = Guid.NewGuid(),
Email = "test@bitwarden.com",
OrganizationId = Guid.NewGuid(),
}},
{ ("token", typeof(ExpiringToken)), new ExpiringToken("test_token", DateTime.UtcNow.AddDays(1))},
{ ("organization", typeof(Organization)), new Organization
{
Id = Guid.NewGuid(),
Name = "Test Organization Name",
Seats = 5
}},
{ ("initialSeatCount", typeof(int)), 5},
{ ("ownerEmails", typeof(IEnumerable<string>)), new [] { "test@bitwarden.com" }},
{ ("maxSeatCount", typeof(int)), 5 },
{ ("userIdentifier", typeof(string)), "test_user" },
{ ("adminEmails", typeof(IEnumerable<string>)), new [] { "test@bitwarden.com" }},
{ ("returnUrl", typeof(string)), "https://bitwarden.com/" },
{ ("amount", typeof(decimal)), 1.00M },
{ ("dueDate", typeof(DateTime)), DateTime.UtcNow.AddDays(1) },
{ ("items", typeof(List<string>)), new List<string> { "test@bitwarden.com" }},
{ ("mentionInvoices", typeof(bool)), true },
{ ("emails", typeof(IEnumerable<string>)), new [] { "test@bitwarden.com" }},
{ ("deviceType", typeof(string)), "Mobile" },
{ ("timestamp", typeof(DateTime)), DateTime.UtcNow.AddDays(1)},
{ ("ip", typeof(string)), "127.0.0.1" },
{ ("emergencyAccess", typeof(EmergencyAccess)), new EmergencyAccess
{
Id = Guid.NewGuid(),
Email = "test@bitwarden.com",
}},
{ ("granteeEmail", typeof(string)), "test@bitwarden.com" },
{ ("grantorName", typeof(string)), "Test User" },
{ ("initiatingName", typeof(string)), "Test" },
{ ("approvingName", typeof(string)), "Test Name" },
{ ("rejectingName", typeof(string)), "Test Name" },
{ ("provider", typeof(Provider)), new Provider
{
Id = Guid.NewGuid(),
}},
{ ("name", typeof(string)), "Test Name" },
{ ("ea", typeof(EmergencyAccess)), new EmergencyAccess
{
Id = Guid.NewGuid(),
Email = "test@bitwarden.com",
}},
{ ("userName", typeof(string)), "testUser" },
{ ("orgName", typeof(string)), "Test Org Name" },
{ ("providerName", typeof(string)), "testProvider" },
{ ("providerUser", typeof(ProviderUser)), new ProviderUser
{
ProviderId = Guid.NewGuid(),
Id = Guid.NewGuid(),
}},
{ ("familyUserEmail", typeof(string)), "test@bitwarden.com" },
{ ("sponsorEmail", typeof(string)), "test@bitwarden.com" },
{ ("familyOrgName", typeof(string)), "Test Org Name" },
// Swap existingAccount to true or false to generate different versions of the SendFamiliesForEnterpriseOfferEmailAsync emails.
{ ("existingAccount", typeof(bool)), false },
{ ("sponsorshipEndDate", typeof(DateTime)), DateTime.UtcNow.AddDays(1)},
{ ("sponsorOrgName", typeof(string)), "Sponsor Test Org Name" },
{ ("expirationDate", typeof(DateTime)), DateTime.Now.AddDays(3) },
{ ("utcNow", typeof(DateTime)), DateTime.UtcNow },
};
var globalSettings = new GlobalSettings
{
Mail = new GlobalSettings.MailSettings
{
Smtp = new GlobalSettings.MailSettings.SmtpSettings
{
Host = "localhost",
TrustServer = true,
Port = 10250,
},
ReplyToEmail = "noreply@bitwarden.com",
},
SiteName = "Bitwarden",
};
var mailDeliveryService = new MailKitSmtpMailDeliveryService(globalSettings, Substitute.For<ILogger<MailKitSmtpMailDeliveryService>>());
var handlebarsService = new HandlebarsMailService(globalSettings, mailDeliveryService, new BlockingMailEnqueuingService());
var sendMethods = typeof(IMailService).GetMethods(BindingFlags.Public | BindingFlags.Instance)
.Where(m => m.Name.StartsWith("Send") && m.Name != "SendEnqueuedMailMessageAsync");
foreach (var sendMethod in sendMethods)
{
await InvokeMethod(sendMethod);
}
async Task InvokeMethod(MethodInfo method)
{
var parameters = method.GetParameters();
var args = new object[parameters.Length];
for (var i = 0; i < parameters.Length; i++)
{
if (!namedParameters.TryGetValue((parameters[i].Name, parameters[i].ParameterType), out var value))
{
throw new InvalidOperationException($"Couldn't find a parameter for name '{parameters[i].Name}' and type '{parameters[i].ParameterType.FullName}'");
}
args[i] = value;
throw new InvalidOperationException($"Couldn't find a parameter for name '{parameters[i].Name}' and type '{parameters[i].ParameterType.FullName}'");
}
await (Task)method.Invoke(handlebarsService, args);
args[i] = value;
}
}
// Remove this test when we add actual tests. It only proves that
// we've properly constructed the system under test.
[Fact]
public void ServiceExists()
{
Assert.NotNull(_sut);
await (Task)method.Invoke(handlebarsService, args);
}
}
// Remove this test when we add actual tests. It only proves that
// we've properly constructed the system under test.
[Fact]
public void ServiceExists()
{
Assert.NotNull(_sut);
}
}

View File

@ -3,29 +3,28 @@ using Bit.Core.Services;
using NSubstitute;
using Xunit;
namespace Bit.Core.Test.Services
namespace Bit.Core.Test.Services;
public class InMemoryApplicationCacheServiceTests
{
public class InMemoryApplicationCacheServiceTests
private readonly InMemoryApplicationCacheService _sut;
private readonly IOrganizationRepository _organizationRepository;
private readonly IProviderRepository _providerRepository;
public InMemoryApplicationCacheServiceTests()
{
private readonly InMemoryApplicationCacheService _sut;
_organizationRepository = Substitute.For<IOrganizationRepository>();
_providerRepository = Substitute.For<IProviderRepository>();
private readonly IOrganizationRepository _organizationRepository;
private readonly IProviderRepository _providerRepository;
_sut = new InMemoryApplicationCacheService(_organizationRepository, _providerRepository);
}
public InMemoryApplicationCacheServiceTests()
{
_organizationRepository = Substitute.For<IOrganizationRepository>();
_providerRepository = Substitute.For<IProviderRepository>();
_sut = new InMemoryApplicationCacheService(_organizationRepository, _providerRepository);
}
// Remove this test when we add actual tests. It only proves that
// we've properly constructed the system under test.
[Fact]
public void ServiceExists()
{
Assert.NotNull(_sut);
}
// Remove this test when we add actual tests. It only proves that
// we've properly constructed the system under test.
[Fact]
public void ServiceExists()
{
Assert.NotNull(_sut);
}
}

View File

@ -4,35 +4,34 @@ using Bit.Core.Settings;
using NSubstitute;
using Xunit;
namespace Bit.Core.Test.Services
namespace Bit.Core.Test.Services;
public class InMemoryServiceBusApplicationCacheServiceTests
{
public class InMemoryServiceBusApplicationCacheServiceTests
private readonly InMemoryServiceBusApplicationCacheService _sut;
private readonly IOrganizationRepository _organizationRepository;
private readonly IProviderRepository _providerRepository;
private readonly GlobalSettings _globalSettings;
public InMemoryServiceBusApplicationCacheServiceTests()
{
private readonly InMemoryServiceBusApplicationCacheService _sut;
_organizationRepository = Substitute.For<IOrganizationRepository>();
_providerRepository = Substitute.For<IProviderRepository>();
_globalSettings = new GlobalSettings();
private readonly IOrganizationRepository _organizationRepository;
private readonly IProviderRepository _providerRepository;
private readonly GlobalSettings _globalSettings;
_sut = new InMemoryServiceBusApplicationCacheService(
_organizationRepository,
_providerRepository,
_globalSettings
);
}
public InMemoryServiceBusApplicationCacheServiceTests()
{
_organizationRepository = Substitute.For<IOrganizationRepository>();
_providerRepository = Substitute.For<IProviderRepository>();
_globalSettings = new GlobalSettings();
_sut = new InMemoryServiceBusApplicationCacheService(
_organizationRepository,
_providerRepository,
_globalSettings
);
}
// Remove this test when we add actual tests. It only proves that
// we've properly constructed the system under test.
[Fact(Skip = "Needs additional work")]
public void ServiceExists()
{
Assert.NotNull(_sut);
}
// Remove this test when we add actual tests. It only proves that
// we've properly constructed the system under test.
[Fact(Skip = "Needs additional work")]
public void ServiceExists()
{
Assert.NotNull(_sut);
}
}

View File

@ -9,53 +9,52 @@ using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
using Xunit;
namespace Bit.Core.Test.Services
namespace Bit.Core.Test.Services;
[SutProviderCustomize]
public class LicensingServiceTests
{
[SutProviderCustomize]
public class LicensingServiceTests
private static string licenseFilePath(Guid orgId) =>
Path.Combine(OrganizationLicenseDirectory.Value, $"{orgId}.json");
private static string LicenseDirectory => Path.GetDirectoryName(OrganizationLicenseDirectory.Value);
private static Lazy<string> OrganizationLicenseDirectory => new(() =>
{
private static string licenseFilePath(Guid orgId) =>
Path.Combine(OrganizationLicenseDirectory.Value, $"{orgId}.json");
private static string LicenseDirectory => Path.GetDirectoryName(OrganizationLicenseDirectory.Value);
private static Lazy<string> OrganizationLicenseDirectory => new(() =>
var directory = Path.Combine(Path.GetTempPath(), "organization");
if (!Directory.Exists(directory))
{
var directory = Path.Combine(Path.GetTempPath(), "organization");
if (!Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
return directory;
});
public static SutProvider<LicensingService> GetSutProvider()
{
var fixture = new Fixture().WithAutoNSubstitutions();
var settings = fixture.Create<IGlobalSettings>();
settings.LicenseDirectory = LicenseDirectory;
settings.SelfHosted = true;
return new SutProvider<LicensingService>(fixture)
.SetDependency(settings)
.Create();
Directory.CreateDirectory(directory);
}
return directory;
});
[Theory, BitAutoData, OrganizationLicenseCustomize]
public async Task ReadOrganizationLicense(Organization organization, OrganizationLicense license)
public static SutProvider<LicensingService> GetSutProvider()
{
var fixture = new Fixture().WithAutoNSubstitutions();
var settings = fixture.Create<IGlobalSettings>();
settings.LicenseDirectory = LicenseDirectory;
settings.SelfHosted = true;
return new SutProvider<LicensingService>(fixture)
.SetDependency(settings)
.Create();
}
[Theory, BitAutoData, OrganizationLicenseCustomize]
public async Task ReadOrganizationLicense(Organization organization, OrganizationLicense license)
{
var sutProvider = GetSutProvider();
File.WriteAllText(licenseFilePath(organization.Id), JsonSerializer.Serialize(license));
var actual = await sutProvider.Sut.ReadOrganizationLicenseAsync(organization);
try
{
var sutProvider = GetSutProvider();
File.WriteAllText(licenseFilePath(organization.Id), JsonSerializer.Serialize(license));
var actual = await sutProvider.Sut.ReadOrganizationLicenseAsync(organization);
try
{
Assert.Equal(JsonSerializer.Serialize(license), JsonSerializer.Serialize(actual));
}
finally
{
Directory.Delete(OrganizationLicenseDirectory.Value, true);
}
Assert.Equal(JsonSerializer.Serialize(license), JsonSerializer.Serialize(actual));
}
finally
{
Directory.Delete(OrganizationLicenseDirectory.Value, true);
}
}
}

View File

@ -11,194 +11,75 @@ using Bit.Test.Common.AutoFixture.Attributes;
using NSubstitute;
using Xunit;
namespace Bit.Core.Test.Services
namespace Bit.Core.Test.Services;
public class LocalAttachmentStorageServiceTests
{
public class LocalAttachmentStorageServiceTests
private void AssertFileCreation(string expectedPath, string expectedFileContents)
{
Assert.True(File.Exists(expectedPath));
Assert.Equal(expectedFileContents, File.ReadAllText(expectedPath));
}
private void AssertFileCreation(string expectedPath, string expectedFileContents)
[Theory]
[InlineCustomAutoData(new[] { typeof(UserCipher), typeof(MetaData) })]
[InlineCustomAutoData(new[] { typeof(UserCipher), typeof(MetaDataWithoutContainer) })]
[InlineCustomAutoData(new[] { typeof(UserCipher), typeof(MetaDataWithoutKey) })]
public async Task UploadNewAttachmentAsync_Success(string stream, Cipher cipher, CipherAttachment.MetaData attachmentData)
{
using (var tempDirectory = new TempDirectory())
{
Assert.True(File.Exists(expectedPath));
Assert.Equal(expectedFileContents, File.ReadAllText(expectedPath));
}
var sutProvider = GetSutProvider(tempDirectory);
[Theory]
[InlineCustomAutoData(new[] { typeof(UserCipher), typeof(MetaData) })]
[InlineCustomAutoData(new[] { typeof(UserCipher), typeof(MetaDataWithoutContainer) })]
[InlineCustomAutoData(new[] { typeof(UserCipher), typeof(MetaDataWithoutKey) })]
public async Task UploadNewAttachmentAsync_Success(string stream, Cipher cipher, CipherAttachment.MetaData attachmentData)
await sutProvider.Sut.UploadNewAttachmentAsync(new MemoryStream(Encoding.UTF8.GetBytes(stream)),
cipher, attachmentData);
AssertFileCreation($"{tempDirectory}/{cipher.Id}/{attachmentData.AttachmentId}", stream);
}
}
[Theory]
[InlineCustomAutoData(new[] { typeof(OrganizationCipher), typeof(MetaData) })]
[InlineCustomAutoData(new[] { typeof(OrganizationCipher), typeof(MetaDataWithoutContainer) })]
[InlineCustomAutoData(new[] { typeof(OrganizationCipher), typeof(MetaDataWithoutKey) })]
public async Task UploadShareAttachmentAsync_Success(string stream, Cipher cipher, CipherAttachment.MetaData attachmentData)
{
using (var tempDirectory = new TempDirectory())
{
using (var tempDirectory = new TempDirectory())
{
var sutProvider = GetSutProvider(tempDirectory);
var sutProvider = GetSutProvider(tempDirectory);
await sutProvider.Sut.UploadNewAttachmentAsync(new MemoryStream(Encoding.UTF8.GetBytes(stream)),
cipher, attachmentData);
await sutProvider.Sut.UploadShareAttachmentAsync(new MemoryStream(Encoding.UTF8.GetBytes(stream)),
cipher.Id, cipher.OrganizationId.Value, attachmentData);
AssertFileCreation($"{tempDirectory}/{cipher.Id}/{attachmentData.AttachmentId}", stream);
}
AssertFileCreation($"{tempDirectory}/temp/{cipher.Id}/{cipher.OrganizationId}/{attachmentData.AttachmentId}", stream);
}
}
[Theory]
[InlineCustomAutoData(new[] { typeof(OrganizationCipher), typeof(MetaData) })]
[InlineCustomAutoData(new[] { typeof(OrganizationCipher), typeof(MetaDataWithoutContainer) })]
[InlineCustomAutoData(new[] { typeof(OrganizationCipher), typeof(MetaDataWithoutKey) })]
public async Task UploadShareAttachmentAsync_Success(string stream, Cipher cipher, CipherAttachment.MetaData attachmentData)
[Theory]
[InlineCustomAutoData(new[] { typeof(OrganizationCipher), typeof(MetaData) })]
[InlineCustomAutoData(new[] { typeof(OrganizationCipher), typeof(MetaDataWithoutContainer) })]
[InlineCustomAutoData(new[] { typeof(OrganizationCipher), typeof(MetaDataWithoutKey) })]
public async Task StartShareAttachmentAsync_NoSource_NoWork(Cipher cipher, CipherAttachment.MetaData attachmentData)
{
using (var tempDirectory = new TempDirectory())
{
using (var tempDirectory = new TempDirectory())
{
var sutProvider = GetSutProvider(tempDirectory);
var sutProvider = GetSutProvider(tempDirectory);
await sutProvider.Sut.UploadShareAttachmentAsync(new MemoryStream(Encoding.UTF8.GetBytes(stream)),
cipher.Id, cipher.OrganizationId.Value, attachmentData);
await sutProvider.Sut.StartShareAttachmentAsync(cipher.Id, cipher.OrganizationId.Value, attachmentData);
AssertFileCreation($"{tempDirectory}/temp/{cipher.Id}/{cipher.OrganizationId}/{attachmentData.AttachmentId}", stream);
}
Assert.False(File.Exists($"{tempDirectory}/{cipher.Id}/{attachmentData.AttachmentId}"));
Assert.False(File.Exists($"{tempDirectory}/{cipher.Id}/{attachmentData.AttachmentId}"));
}
}
[Theory]
[InlineCustomAutoData(new[] { typeof(OrganizationCipher), typeof(MetaData) })]
[InlineCustomAutoData(new[] { typeof(OrganizationCipher), typeof(MetaDataWithoutContainer) })]
[InlineCustomAutoData(new[] { typeof(OrganizationCipher), typeof(MetaDataWithoutKey) })]
public async Task StartShareAttachmentAsync_NoSource_NoWork(Cipher cipher, CipherAttachment.MetaData attachmentData)
{
using (var tempDirectory = new TempDirectory())
{
var sutProvider = GetSutProvider(tempDirectory);
await sutProvider.Sut.StartShareAttachmentAsync(cipher.Id, cipher.OrganizationId.Value, attachmentData);
Assert.False(File.Exists($"{tempDirectory}/{cipher.Id}/{attachmentData.AttachmentId}"));
Assert.False(File.Exists($"{tempDirectory}/{cipher.Id}/{attachmentData.AttachmentId}"));
}
}
[Theory]
[InlineCustomAutoData(new[] { typeof(OrganizationCipher), typeof(MetaData) })]
[InlineCustomAutoData(new[] { typeof(OrganizationCipher), typeof(MetaDataWithoutContainer) })]
[InlineCustomAutoData(new[] { typeof(OrganizationCipher), typeof(MetaDataWithoutKey) })]
public async Task StartShareAttachmentAsync_NoDest_NoWork(string source, Cipher cipher, CipherAttachment.MetaData attachmentData)
{
using (var tempDirectory = new TempDirectory())
{
var sutProvider = GetSutProvider(tempDirectory);
var sourcePath = $"{tempDirectory}/temp/{cipher.Id}/{cipher.OrganizationId}/{attachmentData.AttachmentId}";
var destPath = $"{tempDirectory}/{cipher.Id}/{attachmentData.AttachmentId}";
var rollBackPath = $"{tempDirectory}/temp/{cipher.Id}/{attachmentData.AttachmentId}";
Directory.CreateDirectory(Path.GetDirectoryName(sourcePath));
File.WriteAllText(sourcePath, source);
await sutProvider.Sut.StartShareAttachmentAsync(cipher.Id, cipher.OrganizationId.Value, attachmentData);
Assert.True(File.Exists(sourcePath));
Assert.Equal(source, File.ReadAllText(sourcePath));
Assert.False(File.Exists(destPath));
Assert.False(File.Exists(rollBackPath));
}
}
[Theory]
[InlineCustomAutoData(new[] { typeof(OrganizationCipher), typeof(MetaData) })]
[InlineCustomAutoData(new[] { typeof(OrganizationCipher), typeof(MetaDataWithoutContainer) })]
[InlineCustomAutoData(new[] { typeof(OrganizationCipher), typeof(MetaDataWithoutKey) })]
public async Task StartShareAttachmentAsync_Success(string source, string destOriginal, Cipher cipher, CipherAttachment.MetaData attachmentData)
{
using (var tempDirectory = new TempDirectory())
{
await StartShareAttachmentAsync(source, destOriginal, cipher, attachmentData, tempDirectory);
}
}
[Theory]
[InlineCustomAutoData(new[] { typeof(OrganizationCipher), typeof(MetaData) })]
[InlineCustomAutoData(new[] { typeof(OrganizationCipher), typeof(MetaDataWithoutContainer) })]
[InlineCustomAutoData(new[] { typeof(OrganizationCipher), typeof(MetaDataWithoutKey) })]
public async Task RollbackShareAttachmentAsync_Success(string source, string destOriginal, Cipher cipher, CipherAttachment.MetaData attachmentData)
{
using (var tempDirectory = new TempDirectory())
{
var sutProvider = GetSutProvider(tempDirectory);
var sourcePath = $"{tempDirectory}/temp/{cipher.Id}/{cipher.OrganizationId}/{attachmentData.AttachmentId}";
var destPath = $"{tempDirectory}/{cipher.Id}/{attachmentData.AttachmentId}";
var rollBackPath = $"{tempDirectory}/temp/{cipher.Id}/{attachmentData.AttachmentId}";
await StartShareAttachmentAsync(source, destOriginal, cipher, attachmentData, tempDirectory);
await sutProvider.Sut.RollbackShareAttachmentAsync(cipher.Id, cipher.OrganizationId.Value, attachmentData, "Not Used Here");
Assert.True(File.Exists(destPath));
Assert.Equal(destOriginal, File.ReadAllText(destPath));
Assert.False(File.Exists(sourcePath));
Assert.False(File.Exists(rollBackPath));
}
}
[Theory]
[InlineCustomAutoData(new[] { typeof(UserCipher), typeof(MetaData) })]
[InlineCustomAutoData(new[] { typeof(UserCipher), typeof(MetaDataWithoutContainer) })]
[InlineCustomAutoData(new[] { typeof(UserCipher), typeof(MetaDataWithoutKey) })]
public async Task DeleteAttachmentAsync_Success(Cipher cipher, CipherAttachment.MetaData attachmentData)
{
using (var tempDirectory = new TempDirectory())
{
var sutProvider = GetSutProvider(tempDirectory);
var expectedPath = $"{tempDirectory}/{cipher.Id}/{attachmentData.AttachmentId}";
Directory.CreateDirectory(Path.GetDirectoryName(expectedPath));
File.Create(expectedPath).Close();
await sutProvider.Sut.DeleteAttachmentAsync(cipher.Id, attachmentData);
Assert.False(File.Exists(expectedPath));
}
}
[Theory]
[InlineUserCipherAutoData]
[InlineOrganizationCipherAutoData]
public async Task CleanupAsync_Succes(Cipher cipher)
{
using (var tempDirectory = new TempDirectory())
{
var sutProvider = GetSutProvider(tempDirectory);
var tempPath = $"{tempDirectory}/temp/{cipher.Id}";
var permPath = $"{tempDirectory}/{cipher.Id}";
Directory.CreateDirectory(tempPath);
Directory.CreateDirectory(permPath);
await sutProvider.Sut.CleanupAsync(cipher.Id);
Assert.False(Directory.Exists(tempPath));
Assert.True(Directory.Exists(permPath));
}
}
[Theory]
[InlineUserCipherAutoData]
[InlineOrganizationCipherAutoData]
public async Task DeleteAttachmentsForCipherAsync_Succes(Cipher cipher)
{
using (var tempDirectory = new TempDirectory())
{
var sutProvider = GetSutProvider(tempDirectory);
var tempPath = $"{tempDirectory}/temp/{cipher.Id}";
var permPath = $"{tempDirectory}/{cipher.Id}";
Directory.CreateDirectory(tempPath);
Directory.CreateDirectory(permPath);
await sutProvider.Sut.DeleteAttachmentsForCipherAsync(cipher.Id);
Assert.True(Directory.Exists(tempPath));
Assert.False(Directory.Exists(permPath));
}
}
private async Task StartShareAttachmentAsync(string source, string destOriginal, Cipher cipher,
CipherAttachment.MetaData attachmentData, TempDirectory tempDirectory)
[Theory]
[InlineCustomAutoData(new[] { typeof(OrganizationCipher), typeof(MetaData) })]
[InlineCustomAutoData(new[] { typeof(OrganizationCipher), typeof(MetaDataWithoutContainer) })]
[InlineCustomAutoData(new[] { typeof(OrganizationCipher), typeof(MetaDataWithoutKey) })]
public async Task StartShareAttachmentAsync_NoDest_NoWork(string source, Cipher cipher, CipherAttachment.MetaData attachmentData)
{
using (var tempDirectory = new TempDirectory())
{
var sutProvider = GetSutProvider(tempDirectory);
@ -206,26 +87,144 @@ namespace Bit.Core.Test.Services
var destPath = $"{tempDirectory}/{cipher.Id}/{attachmentData.AttachmentId}";
var rollBackPath = $"{tempDirectory}/temp/{cipher.Id}/{attachmentData.AttachmentId}";
Directory.CreateDirectory(Path.GetDirectoryName(sourcePath));
Directory.CreateDirectory(Path.GetDirectoryName(destPath));
File.WriteAllText(sourcePath, source);
File.WriteAllText(destPath, destOriginal);
await sutProvider.Sut.StartShareAttachmentAsync(cipher.Id, cipher.OrganizationId.Value, attachmentData);
Assert.False(File.Exists(sourcePath));
Assert.True(File.Exists(destPath));
Assert.Equal(source, File.ReadAllText(destPath));
Assert.True(File.Exists(rollBackPath));
Assert.Equal(destOriginal, File.ReadAllText(rollBackPath));
}
private SutProvider<LocalAttachmentStorageService> GetSutProvider(TempDirectory tempDirectory)
{
var fixture = new Fixture().WithAutoNSubstitutions();
fixture.Freeze<IGlobalSettings>().Attachment.BaseDirectory.Returns(tempDirectory.Directory);
fixture.Freeze<IGlobalSettings>().Attachment.BaseUrl.Returns(Guid.NewGuid().ToString());
return new SutProvider<LocalAttachmentStorageService>(fixture).Create();
Assert.True(File.Exists(sourcePath));
Assert.Equal(source, File.ReadAllText(sourcePath));
Assert.False(File.Exists(destPath));
Assert.False(File.Exists(rollBackPath));
}
}
[Theory]
[InlineCustomAutoData(new[] { typeof(OrganizationCipher), typeof(MetaData) })]
[InlineCustomAutoData(new[] { typeof(OrganizationCipher), typeof(MetaDataWithoutContainer) })]
[InlineCustomAutoData(new[] { typeof(OrganizationCipher), typeof(MetaDataWithoutKey) })]
public async Task StartShareAttachmentAsync_Success(string source, string destOriginal, Cipher cipher, CipherAttachment.MetaData attachmentData)
{
using (var tempDirectory = new TempDirectory())
{
await StartShareAttachmentAsync(source, destOriginal, cipher, attachmentData, tempDirectory);
}
}
[Theory]
[InlineCustomAutoData(new[] { typeof(OrganizationCipher), typeof(MetaData) })]
[InlineCustomAutoData(new[] { typeof(OrganizationCipher), typeof(MetaDataWithoutContainer) })]
[InlineCustomAutoData(new[] { typeof(OrganizationCipher), typeof(MetaDataWithoutKey) })]
public async Task RollbackShareAttachmentAsync_Success(string source, string destOriginal, Cipher cipher, CipherAttachment.MetaData attachmentData)
{
using (var tempDirectory = new TempDirectory())
{
var sutProvider = GetSutProvider(tempDirectory);
var sourcePath = $"{tempDirectory}/temp/{cipher.Id}/{cipher.OrganizationId}/{attachmentData.AttachmentId}";
var destPath = $"{tempDirectory}/{cipher.Id}/{attachmentData.AttachmentId}";
var rollBackPath = $"{tempDirectory}/temp/{cipher.Id}/{attachmentData.AttachmentId}";
await StartShareAttachmentAsync(source, destOriginal, cipher, attachmentData, tempDirectory);
await sutProvider.Sut.RollbackShareAttachmentAsync(cipher.Id, cipher.OrganizationId.Value, attachmentData, "Not Used Here");
Assert.True(File.Exists(destPath));
Assert.Equal(destOriginal, File.ReadAllText(destPath));
Assert.False(File.Exists(sourcePath));
Assert.False(File.Exists(rollBackPath));
}
}
[Theory]
[InlineCustomAutoData(new[] { typeof(UserCipher), typeof(MetaData) })]
[InlineCustomAutoData(new[] { typeof(UserCipher), typeof(MetaDataWithoutContainer) })]
[InlineCustomAutoData(new[] { typeof(UserCipher), typeof(MetaDataWithoutKey) })]
public async Task DeleteAttachmentAsync_Success(Cipher cipher, CipherAttachment.MetaData attachmentData)
{
using (var tempDirectory = new TempDirectory())
{
var sutProvider = GetSutProvider(tempDirectory);
var expectedPath = $"{tempDirectory}/{cipher.Id}/{attachmentData.AttachmentId}";
Directory.CreateDirectory(Path.GetDirectoryName(expectedPath));
File.Create(expectedPath).Close();
await sutProvider.Sut.DeleteAttachmentAsync(cipher.Id, attachmentData);
Assert.False(File.Exists(expectedPath));
}
}
[Theory]
[InlineUserCipherAutoData]
[InlineOrganizationCipherAutoData]
public async Task CleanupAsync_Succes(Cipher cipher)
{
using (var tempDirectory = new TempDirectory())
{
var sutProvider = GetSutProvider(tempDirectory);
var tempPath = $"{tempDirectory}/temp/{cipher.Id}";
var permPath = $"{tempDirectory}/{cipher.Id}";
Directory.CreateDirectory(tempPath);
Directory.CreateDirectory(permPath);
await sutProvider.Sut.CleanupAsync(cipher.Id);
Assert.False(Directory.Exists(tempPath));
Assert.True(Directory.Exists(permPath));
}
}
[Theory]
[InlineUserCipherAutoData]
[InlineOrganizationCipherAutoData]
public async Task DeleteAttachmentsForCipherAsync_Succes(Cipher cipher)
{
using (var tempDirectory = new TempDirectory())
{
var sutProvider = GetSutProvider(tempDirectory);
var tempPath = $"{tempDirectory}/temp/{cipher.Id}";
var permPath = $"{tempDirectory}/{cipher.Id}";
Directory.CreateDirectory(tempPath);
Directory.CreateDirectory(permPath);
await sutProvider.Sut.DeleteAttachmentsForCipherAsync(cipher.Id);
Assert.True(Directory.Exists(tempPath));
Assert.False(Directory.Exists(permPath));
}
}
private async Task StartShareAttachmentAsync(string source, string destOriginal, Cipher cipher,
CipherAttachment.MetaData attachmentData, TempDirectory tempDirectory)
{
var sutProvider = GetSutProvider(tempDirectory);
var sourcePath = $"{tempDirectory}/temp/{cipher.Id}/{cipher.OrganizationId}/{attachmentData.AttachmentId}";
var destPath = $"{tempDirectory}/{cipher.Id}/{attachmentData.AttachmentId}";
var rollBackPath = $"{tempDirectory}/temp/{cipher.Id}/{attachmentData.AttachmentId}";
Directory.CreateDirectory(Path.GetDirectoryName(sourcePath));
Directory.CreateDirectory(Path.GetDirectoryName(destPath));
File.WriteAllText(sourcePath, source);
File.WriteAllText(destPath, destOriginal);
await sutProvider.Sut.StartShareAttachmentAsync(cipher.Id, cipher.OrganizationId.Value, attachmentData);
Assert.False(File.Exists(sourcePath));
Assert.True(File.Exists(destPath));
Assert.Equal(source, File.ReadAllText(destPath));
Assert.True(File.Exists(rollBackPath));
Assert.Equal(destOriginal, File.ReadAllText(rollBackPath));
}
private SutProvider<LocalAttachmentStorageService> GetSutProvider(TempDirectory tempDirectory)
{
var fixture = new Fixture().WithAutoNSubstitutions();
fixture.Freeze<IGlobalSettings>().Attachment.BaseDirectory.Returns(tempDirectory.Directory);
fixture.Freeze<IGlobalSettings>().Attachment.BaseUrl.Returns(Guid.NewGuid().ToString());
return new SutProvider<LocalAttachmentStorageService>(fixture).Create();
}
}

View File

@ -4,35 +4,34 @@ using Microsoft.Extensions.Logging;
using NSubstitute;
using Xunit;
namespace Bit.Core.Test.Services
namespace Bit.Core.Test.Services;
public class MailKitSmtpMailDeliveryServiceTests
{
public class MailKitSmtpMailDeliveryServiceTests
private readonly MailKitSmtpMailDeliveryService _sut;
private readonly GlobalSettings _globalSettings;
private readonly ILogger<MailKitSmtpMailDeliveryService> _logger;
public MailKitSmtpMailDeliveryServiceTests()
{
private readonly MailKitSmtpMailDeliveryService _sut;
_globalSettings = new GlobalSettings();
_logger = Substitute.For<ILogger<MailKitSmtpMailDeliveryService>>();
private readonly GlobalSettings _globalSettings;
private readonly ILogger<MailKitSmtpMailDeliveryService> _logger;
_globalSettings.Mail.Smtp.Host = "unittests.example.com";
_globalSettings.Mail.ReplyToEmail = "noreply@unittests.example.com";
public MailKitSmtpMailDeliveryServiceTests()
{
_globalSettings = new GlobalSettings();
_logger = Substitute.For<ILogger<MailKitSmtpMailDeliveryService>>();
_sut = new MailKitSmtpMailDeliveryService(
_globalSettings,
_logger
);
}
_globalSettings.Mail.Smtp.Host = "unittests.example.com";
_globalSettings.Mail.ReplyToEmail = "noreply@unittests.example.com";
_sut = new MailKitSmtpMailDeliveryService(
_globalSettings,
_logger
);
}
// Remove this test when we add actual tests. It only proves that
// we've properly constructed the system under test.
[Fact]
public void ServiceExists()
{
Assert.NotNull(_sut);
}
// Remove this test when we add actual tests. It only proves that
// we've properly constructed the system under test.
[Fact]
public void ServiceExists()
{
Assert.NotNull(_sut);
}
}

View File

@ -6,50 +6,49 @@ using Microsoft.Extensions.Logging;
using NSubstitute;
using Xunit;
namespace Bit.Core.Test.Services
namespace Bit.Core.Test.Services;
public class MultiServicePushNotificationServiceTests
{
public class MultiServicePushNotificationServiceTests
private readonly MultiServicePushNotificationService _sut;
private readonly IHttpClientFactory _httpFactory;
private readonly IDeviceRepository _deviceRepository;
private readonly IInstallationDeviceRepository _installationDeviceRepository;
private readonly GlobalSettings _globalSettings;
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly ILogger<MultiServicePushNotificationService> _logger;
private readonly ILogger<RelayPushNotificationService> _relayLogger;
private readonly ILogger<NotificationsApiPushNotificationService> _hubLogger;
public MultiServicePushNotificationServiceTests()
{
private readonly MultiServicePushNotificationService _sut;
_httpFactory = Substitute.For<IHttpClientFactory>();
_deviceRepository = Substitute.For<IDeviceRepository>();
_installationDeviceRepository = Substitute.For<IInstallationDeviceRepository>();
_globalSettings = new GlobalSettings();
_httpContextAccessor = Substitute.For<IHttpContextAccessor>();
_logger = Substitute.For<ILogger<MultiServicePushNotificationService>>();
_relayLogger = Substitute.For<ILogger<RelayPushNotificationService>>();
_hubLogger = Substitute.For<ILogger<NotificationsApiPushNotificationService>>();
private readonly IHttpClientFactory _httpFactory;
private readonly IDeviceRepository _deviceRepository;
private readonly IInstallationDeviceRepository _installationDeviceRepository;
private readonly GlobalSettings _globalSettings;
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly ILogger<MultiServicePushNotificationService> _logger;
private readonly ILogger<RelayPushNotificationService> _relayLogger;
private readonly ILogger<NotificationsApiPushNotificationService> _hubLogger;
_sut = new MultiServicePushNotificationService(
_httpFactory,
_deviceRepository,
_installationDeviceRepository,
_globalSettings,
_httpContextAccessor,
_logger,
_relayLogger,
_hubLogger
);
}
public MultiServicePushNotificationServiceTests()
{
_httpFactory = Substitute.For<IHttpClientFactory>();
_deviceRepository = Substitute.For<IDeviceRepository>();
_installationDeviceRepository = Substitute.For<IInstallationDeviceRepository>();
_globalSettings = new GlobalSettings();
_httpContextAccessor = Substitute.For<IHttpContextAccessor>();
_logger = Substitute.For<ILogger<MultiServicePushNotificationService>>();
_relayLogger = Substitute.For<ILogger<RelayPushNotificationService>>();
_hubLogger = Substitute.For<ILogger<NotificationsApiPushNotificationService>>();
_sut = new MultiServicePushNotificationService(
_httpFactory,
_deviceRepository,
_installationDeviceRepository,
_globalSettings,
_httpContextAccessor,
_logger,
_relayLogger,
_hubLogger
);
}
// Remove this test when we add actual tests. It only proves that
// we've properly constructed the system under test.
[Fact]
public void ServiceExists()
{
Assert.NotNull(_sut);
}
// Remove this test when we add actual tests. It only proves that
// we've properly constructed the system under test.
[Fact]
public void ServiceExists()
{
Assert.NotNull(_sut);
}
}

View File

@ -5,35 +5,34 @@ using Microsoft.AspNetCore.Http;
using NSubstitute;
using Xunit;
namespace Bit.Core.Test.Services
namespace Bit.Core.Test.Services;
public class NotificationHubPushNotificationServiceTests
{
public class NotificationHubPushNotificationServiceTests
private readonly NotificationHubPushNotificationService _sut;
private readonly IInstallationDeviceRepository _installationDeviceRepository;
private readonly GlobalSettings _globalSettings;
private readonly IHttpContextAccessor _httpContextAccessor;
public NotificationHubPushNotificationServiceTests()
{
private readonly NotificationHubPushNotificationService _sut;
_installationDeviceRepository = Substitute.For<IInstallationDeviceRepository>();
_globalSettings = new GlobalSettings();
_httpContextAccessor = Substitute.For<IHttpContextAccessor>();
private readonly IInstallationDeviceRepository _installationDeviceRepository;
private readonly GlobalSettings _globalSettings;
private readonly IHttpContextAccessor _httpContextAccessor;
_sut = new NotificationHubPushNotificationService(
_installationDeviceRepository,
_globalSettings,
_httpContextAccessor
);
}
public NotificationHubPushNotificationServiceTests()
{
_installationDeviceRepository = Substitute.For<IInstallationDeviceRepository>();
_globalSettings = new GlobalSettings();
_httpContextAccessor = Substitute.For<IHttpContextAccessor>();
_sut = new NotificationHubPushNotificationService(
_installationDeviceRepository,
_globalSettings,
_httpContextAccessor
);
}
// Remove this test when we add actual tests. It only proves that
// we've properly constructed the system under test.
[Fact(Skip = "Needs additional work")]
public void ServiceExists()
{
Assert.NotNull(_sut);
}
// Remove this test when we add actual tests. It only proves that
// we've properly constructed the system under test.
[Fact(Skip = "Needs additional work")]
public void ServiceExists()
{
Assert.NotNull(_sut);
}
}

View File

@ -4,32 +4,31 @@ using Bit.Core.Settings;
using NSubstitute;
using Xunit;
namespace Bit.Core.Test.Services
namespace Bit.Core.Test.Services;
public class NotificationHubPushRegistrationServiceTests
{
public class NotificationHubPushRegistrationServiceTests
private readonly NotificationHubPushRegistrationService _sut;
private readonly IInstallationDeviceRepository _installationDeviceRepository;
private readonly GlobalSettings _globalSettings;
public NotificationHubPushRegistrationServiceTests()
{
private readonly NotificationHubPushRegistrationService _sut;
_installationDeviceRepository = Substitute.For<IInstallationDeviceRepository>();
_globalSettings = new GlobalSettings();
private readonly IInstallationDeviceRepository _installationDeviceRepository;
private readonly GlobalSettings _globalSettings;
_sut = new NotificationHubPushRegistrationService(
_installationDeviceRepository,
_globalSettings
);
}
public NotificationHubPushRegistrationServiceTests()
{
_installationDeviceRepository = Substitute.For<IInstallationDeviceRepository>();
_globalSettings = new GlobalSettings();
_sut = new NotificationHubPushRegistrationService(
_installationDeviceRepository,
_globalSettings
);
}
// Remove this test when we add actual tests. It only proves that
// we've properly constructed the system under test.
[Fact(Skip = "Needs additional work")]
public void ServiceExists()
{
Assert.NotNull(_sut);
}
// Remove this test when we add actual tests. It only proves that
// we've properly constructed the system under test.
[Fact(Skip = "Needs additional work")]
public void ServiceExists()
{
Assert.NotNull(_sut);
}
}

View File

@ -5,38 +5,37 @@ using Microsoft.Extensions.Logging;
using NSubstitute;
using Xunit;
namespace Bit.Core.Test.Services
namespace Bit.Core.Test.Services;
public class NotificationsApiPushNotificationServiceTests
{
public class NotificationsApiPushNotificationServiceTests
private readonly NotificationsApiPushNotificationService _sut;
private readonly IHttpClientFactory _httpFactory;
private readonly GlobalSettings _globalSettings;
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly ILogger<NotificationsApiPushNotificationService> _logger;
public NotificationsApiPushNotificationServiceTests()
{
private readonly NotificationsApiPushNotificationService _sut;
_httpFactory = Substitute.For<IHttpClientFactory>();
_globalSettings = new GlobalSettings();
_httpContextAccessor = Substitute.For<IHttpContextAccessor>();
_logger = Substitute.For<ILogger<NotificationsApiPushNotificationService>>();
private readonly IHttpClientFactory _httpFactory;
private readonly GlobalSettings _globalSettings;
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly ILogger<NotificationsApiPushNotificationService> _logger;
_sut = new NotificationsApiPushNotificationService(
_httpFactory,
_globalSettings,
_httpContextAccessor,
_logger
);
}
public NotificationsApiPushNotificationServiceTests()
{
_httpFactory = Substitute.For<IHttpClientFactory>();
_globalSettings = new GlobalSettings();
_httpContextAccessor = Substitute.For<IHttpContextAccessor>();
_logger = Substitute.For<ILogger<NotificationsApiPushNotificationService>>();
_sut = new NotificationsApiPushNotificationService(
_httpFactory,
_globalSettings,
_httpContextAccessor,
_logger
);
}
// Remove this test when we add actual tests. It only proves that
// we've properly constructed the system under test.
[Fact(Skip = "Needs additional work")]
public void ServiceExists()
{
Assert.NotNull(_sut);
}
// Remove this test when we add actual tests. It only proves that
// we've properly constructed the system under test.
[Fact(Skip = "Needs additional work")]
public void ServiceExists()
{
Assert.NotNull(_sut);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -10,392 +10,391 @@ using NSubstitute;
using Xunit;
using PolicyFixtures = Bit.Core.Test.AutoFixture.PolicyFixtures;
namespace Bit.Core.Test.Services
namespace Bit.Core.Test.Services;
[SutProviderCustomize]
public class PolicyServiceTests
{
[SutProviderCustomize]
public class PolicyServiceTests
[Theory, BitAutoData]
public async Task SaveAsync_OrganizationDoesNotExist_ThrowsBadRequest(
[PolicyFixtures.Policy(PolicyType.DisableSend)] Policy policy, SutProvider<PolicyService> sutProvider)
{
[Theory, BitAutoData]
public async Task SaveAsync_OrganizationDoesNotExist_ThrowsBadRequest(
[PolicyFixtures.Policy(PolicyType.DisableSend)] Policy policy, SutProvider<PolicyService> sutProvider)
SetupOrg(sutProvider, policy.OrganizationId, null);
var badRequestException = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.SaveAsync(policy,
Substitute.For<IUserService>(),
Substitute.For<IOrganizationService>(),
Guid.NewGuid()));
Assert.Contains("Organization not found", badRequestException.Message, StringComparison.OrdinalIgnoreCase);
await sutProvider.GetDependency<IPolicyRepository>()
.DidNotReceiveWithAnyArgs()
.UpsertAsync(default);
await sutProvider.GetDependency<IEventService>()
.DidNotReceiveWithAnyArgs()
.LogPolicyEventAsync(default, default, default);
}
[Theory, BitAutoData]
public async Task SaveAsync_OrganizationCannotUsePolicies_ThrowsBadRequest(
[PolicyFixtures.Policy(PolicyType.DisableSend)] Policy policy, SutProvider<PolicyService> sutProvider)
{
var orgId = Guid.NewGuid();
SetupOrg(sutProvider, policy.OrganizationId, new Organization
{
SetupOrg(sutProvider, policy.OrganizationId, null);
UsePolicies = false,
});
var badRequestException = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.SaveAsync(policy,
Substitute.For<IUserService>(),
Substitute.For<IOrganizationService>(),
Guid.NewGuid()));
var badRequestException = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.SaveAsync(policy,
Substitute.For<IUserService>(),
Substitute.For<IOrganizationService>(),
Guid.NewGuid()));
Assert.Contains("Organization not found", badRequestException.Message, StringComparison.OrdinalIgnoreCase);
Assert.Contains("cannot use policies", badRequestException.Message, StringComparison.OrdinalIgnoreCase);
await sutProvider.GetDependency<IPolicyRepository>()
.DidNotReceiveWithAnyArgs()
.UpsertAsync(default);
await sutProvider.GetDependency<IPolicyRepository>()
.DidNotReceiveWithAnyArgs()
.UpsertAsync(default);
await sutProvider.GetDependency<IEventService>()
.DidNotReceiveWithAnyArgs()
.LogPolicyEventAsync(default, default, default);
}
await sutProvider.GetDependency<IEventService>()
.DidNotReceiveWithAnyArgs()
.LogPolicyEventAsync(default, default, default);
}
[Theory, BitAutoData]
public async Task SaveAsync_OrganizationCannotUsePolicies_ThrowsBadRequest(
[PolicyFixtures.Policy(PolicyType.DisableSend)] Policy policy, SutProvider<PolicyService> sutProvider)
[Theory, BitAutoData]
public async Task SaveAsync_SingleOrg_RequireSsoEnabled_ThrowsBadRequest(
[PolicyFixtures.Policy(PolicyType.SingleOrg)] Policy policy, SutProvider<PolicyService> sutProvider)
{
policy.Enabled = false;
SetupOrg(sutProvider, policy.OrganizationId, new Organization
{
var orgId = Guid.NewGuid();
Id = policy.OrganizationId,
UsePolicies = true,
});
SetupOrg(sutProvider, policy.OrganizationId, new Organization
sutProvider.GetDependency<IPolicyRepository>()
.GetByOrganizationIdTypeAsync(policy.OrganizationId, PolicyType.RequireSso)
.Returns(Task.FromResult(new Policy { Enabled = true }));
var badRequestException = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.SaveAsync(policy,
Substitute.For<IUserService>(),
Substitute.For<IOrganizationService>(),
Guid.NewGuid()));
Assert.Contains("Single Sign-On Authentication policy is enabled.", badRequestException.Message, StringComparison.OrdinalIgnoreCase);
await sutProvider.GetDependency<IPolicyRepository>()
.DidNotReceiveWithAnyArgs()
.UpsertAsync(default);
await sutProvider.GetDependency<IEventService>()
.DidNotReceiveWithAnyArgs()
.LogPolicyEventAsync(default, default, default);
}
[Theory, BitAutoData]
public async Task SaveAsync_SingleOrg_VaultTimeoutEnabled_ThrowsBadRequest([PolicyFixtures.Policy(Enums.PolicyType.SingleOrg)] Policy policy, SutProvider<PolicyService> sutProvider)
{
policy.Enabled = false;
SetupOrg(sutProvider, policy.OrganizationId, new Organization
{
Id = policy.OrganizationId,
UsePolicies = true,
});
sutProvider.GetDependency<IPolicyRepository>()
.GetByOrganizationIdTypeAsync(policy.OrganizationId, Enums.PolicyType.MaximumVaultTimeout)
.Returns(new Policy { Enabled = true });
var badRequestException = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.SaveAsync(policy,
Substitute.For<IUserService>(),
Substitute.For<IOrganizationService>(),
Guid.NewGuid()));
Assert.Contains("Maximum Vault Timeout policy is enabled.", badRequestException.Message, StringComparison.OrdinalIgnoreCase);
await sutProvider.GetDependency<IPolicyRepository>()
.DidNotReceiveWithAnyArgs()
.UpsertAsync(default);
}
[Theory]
[BitAutoData(PolicyType.SingleOrg)]
[BitAutoData(PolicyType.RequireSso)]
public async Task SaveAsync_PolicyRequiredByKeyConnector_DisablePolicy_ThrowsBadRequest(
Enums.PolicyType policyType,
Policy policy,
SutProvider<PolicyService> sutProvider)
{
policy.Enabled = false;
policy.Type = policyType;
SetupOrg(sutProvider, policy.OrganizationId, new Organization
{
Id = policy.OrganizationId,
UsePolicies = true,
});
var ssoConfig = new SsoConfig { Enabled = true };
var data = new SsoConfigurationData { KeyConnectorEnabled = true };
ssoConfig.SetData(data);
sutProvider.GetDependency<ISsoConfigRepository>()
.GetByOrganizationIdAsync(policy.OrganizationId)
.Returns(ssoConfig);
var badRequestException = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.SaveAsync(policy,
Substitute.For<IUserService>(),
Substitute.For<IOrganizationService>(),
Guid.NewGuid()));
Assert.Contains("Key Connector is enabled.", badRequestException.Message, StringComparison.OrdinalIgnoreCase);
await sutProvider.GetDependency<IPolicyRepository>()
.DidNotReceiveWithAnyArgs()
.UpsertAsync(default);
}
[Theory, BitAutoData]
public async Task SaveAsync_RequireSsoPolicy_NotEnabled_ThrowsBadRequestAsync(
[PolicyFixtures.Policy(Enums.PolicyType.RequireSso)] Policy policy, SutProvider<PolicyService> sutProvider)
{
policy.Enabled = true;
SetupOrg(sutProvider, policy.OrganizationId, new Organization
{
Id = policy.OrganizationId,
UsePolicies = true,
});
sutProvider.GetDependency<IPolicyRepository>()
.GetByOrganizationIdTypeAsync(policy.OrganizationId, PolicyType.SingleOrg)
.Returns(Task.FromResult(new Policy { Enabled = false }));
var badRequestException = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.SaveAsync(policy,
Substitute.For<IUserService>(),
Substitute.For<IOrganizationService>(),
Guid.NewGuid()));
Assert.Contains("Single Organization policy not enabled.", badRequestException.Message, StringComparison.OrdinalIgnoreCase);
await sutProvider.GetDependency<IPolicyRepository>()
.DidNotReceiveWithAnyArgs()
.UpsertAsync(default);
await sutProvider.GetDependency<IEventService>()
.DidNotReceiveWithAnyArgs()
.LogPolicyEventAsync(default, default, default);
}
[Theory, BitAutoData]
public async Task SaveAsync_NewPolicy_Created(
[PolicyFixtures.Policy(PolicyType.ResetPassword)] Policy policy, SutProvider<PolicyService> sutProvider)
{
policy.Id = default;
SetupOrg(sutProvider, policy.OrganizationId, new Organization
{
Id = policy.OrganizationId,
UsePolicies = true,
});
var utcNow = DateTime.UtcNow;
await sutProvider.Sut.SaveAsync(policy, Substitute.For<IUserService>(), Substitute.For<IOrganizationService>(), Guid.NewGuid());
await sutProvider.GetDependency<IEventService>().Received()
.LogPolicyEventAsync(policy, EventType.Policy_Updated);
await sutProvider.GetDependency<IPolicyRepository>().Received()
.UpsertAsync(policy);
Assert.True(policy.CreationDate - utcNow < TimeSpan.FromSeconds(1));
Assert.True(policy.RevisionDate - utcNow < TimeSpan.FromSeconds(1));
}
[Theory, BitAutoData]
public async Task SaveAsync_VaultTimeoutPolicy_NotEnabled_ThrowsBadRequestAsync(
[PolicyFixtures.Policy(PolicyType.MaximumVaultTimeout)] Policy policy, SutProvider<PolicyService> sutProvider)
{
policy.Enabled = true;
SetupOrg(sutProvider, policy.OrganizationId, new Organization
{
Id = policy.OrganizationId,
UsePolicies = true,
});
sutProvider.GetDependency<IPolicyRepository>()
.GetByOrganizationIdTypeAsync(policy.OrganizationId, Enums.PolicyType.SingleOrg)
.Returns(Task.FromResult(new Policy { Enabled = false }));
var badRequestException = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.SaveAsync(policy,
Substitute.For<IUserService>(),
Substitute.For<IOrganizationService>(),
Guid.NewGuid()));
Assert.Contains("Single Organization policy not enabled.", badRequestException.Message, StringComparison.OrdinalIgnoreCase);
await sutProvider.GetDependency<IPolicyRepository>()
.DidNotReceiveWithAnyArgs()
.UpsertAsync(default);
await sutProvider.GetDependency<IEventService>()
.DidNotReceiveWithAnyArgs()
.LogPolicyEventAsync(default, default, default);
}
[Theory, BitAutoData]
public async Task SaveAsync_ExistingPolicy_UpdateTwoFactor(
[PolicyFixtures.Policy(PolicyType.TwoFactorAuthentication)] Policy policy, SutProvider<PolicyService> sutProvider)
{
// If the policy that this is updating isn't enabled then do some work now that the current one is enabled
var org = new Organization
{
Id = policy.OrganizationId,
UsePolicies = true,
Name = "TEST",
};
SetupOrg(sutProvider, policy.OrganizationId, org);
sutProvider.GetDependency<IPolicyRepository>()
.GetByIdAsync(policy.Id)
.Returns(new Policy
{
UsePolicies = false,
Id = policy.Id,
Type = PolicyType.TwoFactorAuthentication,
Enabled = false,
});
var badRequestException = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.SaveAsync(policy,
Substitute.For<IUserService>(),
Substitute.For<IOrganizationService>(),
Guid.NewGuid()));
Assert.Contains("cannot use policies", badRequestException.Message, StringComparison.OrdinalIgnoreCase);
await sutProvider.GetDependency<IPolicyRepository>()
.DidNotReceiveWithAnyArgs()
.UpsertAsync(default);
await sutProvider.GetDependency<IEventService>()
.DidNotReceiveWithAnyArgs()
.LogPolicyEventAsync(default, default, default);
}
[Theory, BitAutoData]
public async Task SaveAsync_SingleOrg_RequireSsoEnabled_ThrowsBadRequest(
[PolicyFixtures.Policy(PolicyType.SingleOrg)] Policy policy, SutProvider<PolicyService> sutProvider)
var orgUserDetail = new Core.Models.Data.Organizations.OrganizationUsers.OrganizationUserUserDetails
{
policy.Enabled = false;
Id = Guid.NewGuid(),
Status = OrganizationUserStatusType.Accepted,
Type = OrganizationUserType.User,
// Needs to be different from what is passed in as the savingUserId to Sut.SaveAsync
Email = "test@bitwarden.com",
Name = "TEST",
UserId = Guid.NewGuid(),
};
SetupOrg(sutProvider, policy.OrganizationId, new Organization
sutProvider.GetDependency<IOrganizationUserRepository>()
.GetManyDetailsByOrganizationAsync(policy.OrganizationId)
.Returns(new List<Core.Models.Data.Organizations.OrganizationUsers.OrganizationUserUserDetails>
{
Id = policy.OrganizationId,
UsePolicies = true,
orgUserDetail,
});
sutProvider.GetDependency<IPolicyRepository>()
.GetByOrganizationIdTypeAsync(policy.OrganizationId, PolicyType.RequireSso)
.Returns(Task.FromResult(new Policy { Enabled = true }));
var userService = Substitute.For<IUserService>();
var organizationService = Substitute.For<IOrganizationService>();
var badRequestException = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.SaveAsync(policy,
Substitute.For<IUserService>(),
Substitute.For<IOrganizationService>(),
Guid.NewGuid()));
userService.TwoFactorIsEnabledAsync(orgUserDetail)
.Returns(false);
Assert.Contains("Single Sign-On Authentication policy is enabled.", badRequestException.Message, StringComparison.OrdinalIgnoreCase);
var utcNow = DateTime.UtcNow;
await sutProvider.GetDependency<IPolicyRepository>()
.DidNotReceiveWithAnyArgs()
.UpsertAsync(default);
var savingUserId = Guid.NewGuid();
await sutProvider.GetDependency<IEventService>()
.DidNotReceiveWithAnyArgs()
.LogPolicyEventAsync(default, default, default);
}
await sutProvider.Sut.SaveAsync(policy, userService, organizationService, savingUserId);
[Theory, BitAutoData]
public async Task SaveAsync_SingleOrg_VaultTimeoutEnabled_ThrowsBadRequest([PolicyFixtures.Policy(Enums.PolicyType.SingleOrg)] Policy policy, SutProvider<PolicyService> sutProvider)
await organizationService.Received()
.DeleteUserAsync(policy.OrganizationId, orgUserDetail.Id, savingUserId);
await sutProvider.GetDependency<IMailService>().Received()
.SendOrganizationUserRemovedForPolicyTwoStepEmailAsync(org.Name, orgUserDetail.Email);
await sutProvider.GetDependency<IEventService>().Received()
.LogPolicyEventAsync(policy, EventType.Policy_Updated);
await sutProvider.GetDependency<IPolicyRepository>().Received()
.UpsertAsync(policy);
Assert.True(policy.CreationDate - utcNow < TimeSpan.FromSeconds(1));
Assert.True(policy.RevisionDate - utcNow < TimeSpan.FromSeconds(1));
}
[Theory, BitAutoData]
public async Task SaveAsync_ExistingPolicy_UpdateSingleOrg(
[PolicyFixtures.Policy(PolicyType.TwoFactorAuthentication)] Policy policy, SutProvider<PolicyService> sutProvider)
{
// If the policy that this is updating isn't enabled then do some work now that the current one is enabled
var org = new Organization
{
policy.Enabled = false;
Id = policy.OrganizationId,
UsePolicies = true,
Name = "TEST",
};
SetupOrg(sutProvider, policy.OrganizationId, new Organization
SetupOrg(sutProvider, policy.OrganizationId, org);
sutProvider.GetDependency<IPolicyRepository>()
.GetByIdAsync(policy.Id)
.Returns(new Policy
{
Id = policy.OrganizationId,
UsePolicies = true,
Id = policy.Id,
Type = PolicyType.SingleOrg,
Enabled = false,
});
sutProvider.GetDependency<IPolicyRepository>()
.GetByOrganizationIdTypeAsync(policy.OrganizationId, Enums.PolicyType.MaximumVaultTimeout)
.Returns(new Policy { Enabled = true });
var badRequestException = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.SaveAsync(policy,
Substitute.For<IUserService>(),
Substitute.For<IOrganizationService>(),
Guid.NewGuid()));
Assert.Contains("Maximum Vault Timeout policy is enabled.", badRequestException.Message, StringComparison.OrdinalIgnoreCase);
await sutProvider.GetDependency<IPolicyRepository>()
.DidNotReceiveWithAnyArgs()
.UpsertAsync(default);
}
[Theory]
[BitAutoData(PolicyType.SingleOrg)]
[BitAutoData(PolicyType.RequireSso)]
public async Task SaveAsync_PolicyRequiredByKeyConnector_DisablePolicy_ThrowsBadRequest(
Enums.PolicyType policyType,
Policy policy,
SutProvider<PolicyService> sutProvider)
var orgUserDetail = new Core.Models.Data.Organizations.OrganizationUsers.OrganizationUserUserDetails
{
policy.Enabled = false;
policy.Type = policyType;
Id = Guid.NewGuid(),
Status = OrganizationUserStatusType.Accepted,
Type = OrganizationUserType.User,
// Needs to be different from what is passed in as the savingUserId to Sut.SaveAsync
Email = "test@bitwarden.com",
Name = "TEST",
UserId = Guid.NewGuid(),
};
SetupOrg(sutProvider, policy.OrganizationId, new Organization
sutProvider.GetDependency<IOrganizationUserRepository>()
.GetManyDetailsByOrganizationAsync(policy.OrganizationId)
.Returns(new List<Core.Models.Data.Organizations.OrganizationUsers.OrganizationUserUserDetails>
{
Id = policy.OrganizationId,
UsePolicies = true,
orgUserDetail,
});
var ssoConfig = new SsoConfig { Enabled = true };
var data = new SsoConfigurationData { KeyConnectorEnabled = true };
ssoConfig.SetData(data);
var userService = Substitute.For<IUserService>();
var organizationService = Substitute.For<IOrganizationService>();
sutProvider.GetDependency<ISsoConfigRepository>()
.GetByOrganizationIdAsync(policy.OrganizationId)
.Returns(ssoConfig);
userService.TwoFactorIsEnabledAsync(orgUserDetail)
.Returns(false);
var badRequestException = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.SaveAsync(policy,
Substitute.For<IUserService>(),
Substitute.For<IOrganizationService>(),
Guid.NewGuid()));
var utcNow = DateTime.UtcNow;
Assert.Contains("Key Connector is enabled.", badRequestException.Message, StringComparison.OrdinalIgnoreCase);
var savingUserId = Guid.NewGuid();
await sutProvider.GetDependency<IPolicyRepository>()
.DidNotReceiveWithAnyArgs()
.UpsertAsync(default);
}
await sutProvider.Sut.SaveAsync(policy, userService, organizationService, savingUserId);
[Theory, BitAutoData]
public async Task SaveAsync_RequireSsoPolicy_NotEnabled_ThrowsBadRequestAsync(
[PolicyFixtures.Policy(Enums.PolicyType.RequireSso)] Policy policy, SutProvider<PolicyService> sutProvider)
{
policy.Enabled = true;
await sutProvider.GetDependency<IEventService>().Received()
.LogPolicyEventAsync(policy, EventType.Policy_Updated);
SetupOrg(sutProvider, policy.OrganizationId, new Organization
{
Id = policy.OrganizationId,
UsePolicies = true,
});
await sutProvider.GetDependency<IPolicyRepository>().Received()
.UpsertAsync(policy);
sutProvider.GetDependency<IPolicyRepository>()
.GetByOrganizationIdTypeAsync(policy.OrganizationId, PolicyType.SingleOrg)
.Returns(Task.FromResult(new Policy { Enabled = false }));
Assert.True(policy.CreationDate - utcNow < TimeSpan.FromSeconds(1));
Assert.True(policy.RevisionDate - utcNow < TimeSpan.FromSeconds(1));
}
var badRequestException = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.SaveAsync(policy,
Substitute.For<IUserService>(),
Substitute.For<IOrganizationService>(),
Guid.NewGuid()));
Assert.Contains("Single Organization policy not enabled.", badRequestException.Message, StringComparison.OrdinalIgnoreCase);
await sutProvider.GetDependency<IPolicyRepository>()
.DidNotReceiveWithAnyArgs()
.UpsertAsync(default);
await sutProvider.GetDependency<IEventService>()
.DidNotReceiveWithAnyArgs()
.LogPolicyEventAsync(default, default, default);
}
[Theory, BitAutoData]
public async Task SaveAsync_NewPolicy_Created(
[PolicyFixtures.Policy(PolicyType.ResetPassword)] Policy policy, SutProvider<PolicyService> sutProvider)
{
policy.Id = default;
SetupOrg(sutProvider, policy.OrganizationId, new Organization
{
Id = policy.OrganizationId,
UsePolicies = true,
});
var utcNow = DateTime.UtcNow;
await sutProvider.Sut.SaveAsync(policy, Substitute.For<IUserService>(), Substitute.For<IOrganizationService>(), Guid.NewGuid());
await sutProvider.GetDependency<IEventService>().Received()
.LogPolicyEventAsync(policy, EventType.Policy_Updated);
await sutProvider.GetDependency<IPolicyRepository>().Received()
.UpsertAsync(policy);
Assert.True(policy.CreationDate - utcNow < TimeSpan.FromSeconds(1));
Assert.True(policy.RevisionDate - utcNow < TimeSpan.FromSeconds(1));
}
[Theory, BitAutoData]
public async Task SaveAsync_VaultTimeoutPolicy_NotEnabled_ThrowsBadRequestAsync(
[PolicyFixtures.Policy(PolicyType.MaximumVaultTimeout)] Policy policy, SutProvider<PolicyService> sutProvider)
{
policy.Enabled = true;
SetupOrg(sutProvider, policy.OrganizationId, new Organization
{
Id = policy.OrganizationId,
UsePolicies = true,
});
sutProvider.GetDependency<IPolicyRepository>()
.GetByOrganizationIdTypeAsync(policy.OrganizationId, Enums.PolicyType.SingleOrg)
.Returns(Task.FromResult(new Policy { Enabled = false }));
var badRequestException = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.SaveAsync(policy,
Substitute.For<IUserService>(),
Substitute.For<IOrganizationService>(),
Guid.NewGuid()));
Assert.Contains("Single Organization policy not enabled.", badRequestException.Message, StringComparison.OrdinalIgnoreCase);
await sutProvider.GetDependency<IPolicyRepository>()
.DidNotReceiveWithAnyArgs()
.UpsertAsync(default);
await sutProvider.GetDependency<IEventService>()
.DidNotReceiveWithAnyArgs()
.LogPolicyEventAsync(default, default, default);
}
[Theory, BitAutoData]
public async Task SaveAsync_ExistingPolicy_UpdateTwoFactor(
[PolicyFixtures.Policy(PolicyType.TwoFactorAuthentication)] Policy policy, SutProvider<PolicyService> sutProvider)
{
// If the policy that this is updating isn't enabled then do some work now that the current one is enabled
var org = new Organization
{
Id = policy.OrganizationId,
UsePolicies = true,
Name = "TEST",
};
SetupOrg(sutProvider, policy.OrganizationId, org);
sutProvider.GetDependency<IPolicyRepository>()
.GetByIdAsync(policy.Id)
.Returns(new Policy
{
Id = policy.Id,
Type = PolicyType.TwoFactorAuthentication,
Enabled = false,
});
var orgUserDetail = new Core.Models.Data.Organizations.OrganizationUsers.OrganizationUserUserDetails
{
Id = Guid.NewGuid(),
Status = OrganizationUserStatusType.Accepted,
Type = OrganizationUserType.User,
// Needs to be different from what is passed in as the savingUserId to Sut.SaveAsync
Email = "test@bitwarden.com",
Name = "TEST",
UserId = Guid.NewGuid(),
};
sutProvider.GetDependency<IOrganizationUserRepository>()
.GetManyDetailsByOrganizationAsync(policy.OrganizationId)
.Returns(new List<Core.Models.Data.Organizations.OrganizationUsers.OrganizationUserUserDetails>
{
orgUserDetail,
});
var userService = Substitute.For<IUserService>();
var organizationService = Substitute.For<IOrganizationService>();
userService.TwoFactorIsEnabledAsync(orgUserDetail)
.Returns(false);
var utcNow = DateTime.UtcNow;
var savingUserId = Guid.NewGuid();
await sutProvider.Sut.SaveAsync(policy, userService, organizationService, savingUserId);
await organizationService.Received()
.DeleteUserAsync(policy.OrganizationId, orgUserDetail.Id, savingUserId);
await sutProvider.GetDependency<IMailService>().Received()
.SendOrganizationUserRemovedForPolicyTwoStepEmailAsync(org.Name, orgUserDetail.Email);
await sutProvider.GetDependency<IEventService>().Received()
.LogPolicyEventAsync(policy, EventType.Policy_Updated);
await sutProvider.GetDependency<IPolicyRepository>().Received()
.UpsertAsync(policy);
Assert.True(policy.CreationDate - utcNow < TimeSpan.FromSeconds(1));
Assert.True(policy.RevisionDate - utcNow < TimeSpan.FromSeconds(1));
}
[Theory, BitAutoData]
public async Task SaveAsync_ExistingPolicy_UpdateSingleOrg(
[PolicyFixtures.Policy(PolicyType.TwoFactorAuthentication)] Policy policy, SutProvider<PolicyService> sutProvider)
{
// If the policy that this is updating isn't enabled then do some work now that the current one is enabled
var org = new Organization
{
Id = policy.OrganizationId,
UsePolicies = true,
Name = "TEST",
};
SetupOrg(sutProvider, policy.OrganizationId, org);
sutProvider.GetDependency<IPolicyRepository>()
.GetByIdAsync(policy.Id)
.Returns(new Policy
{
Id = policy.Id,
Type = PolicyType.SingleOrg,
Enabled = false,
});
var orgUserDetail = new Core.Models.Data.Organizations.OrganizationUsers.OrganizationUserUserDetails
{
Id = Guid.NewGuid(),
Status = OrganizationUserStatusType.Accepted,
Type = OrganizationUserType.User,
// Needs to be different from what is passed in as the savingUserId to Sut.SaveAsync
Email = "test@bitwarden.com",
Name = "TEST",
UserId = Guid.NewGuid(),
};
sutProvider.GetDependency<IOrganizationUserRepository>()
.GetManyDetailsByOrganizationAsync(policy.OrganizationId)
.Returns(new List<Core.Models.Data.Organizations.OrganizationUsers.OrganizationUserUserDetails>
{
orgUserDetail,
});
var userService = Substitute.For<IUserService>();
var organizationService = Substitute.For<IOrganizationService>();
userService.TwoFactorIsEnabledAsync(orgUserDetail)
.Returns(false);
var utcNow = DateTime.UtcNow;
var savingUserId = Guid.NewGuid();
await sutProvider.Sut.SaveAsync(policy, userService, organizationService, savingUserId);
await sutProvider.GetDependency<IEventService>().Received()
.LogPolicyEventAsync(policy, EventType.Policy_Updated);
await sutProvider.GetDependency<IPolicyRepository>().Received()
.UpsertAsync(policy);
Assert.True(policy.CreationDate - utcNow < TimeSpan.FromSeconds(1));
Assert.True(policy.RevisionDate - utcNow < TimeSpan.FromSeconds(1));
}
private static void SetupOrg(SutProvider<PolicyService> sutProvider, Guid organizationId, Organization organization)
{
sutProvider.GetDependency<IOrganizationRepository>()
.GetByIdAsync(organizationId)
.Returns(Task.FromResult(organization));
}
private static void SetupOrg(SutProvider<PolicyService> sutProvider, Guid organizationId, Organization organization)
{
sutProvider.GetDependency<IOrganizationRepository>()
.GetByIdAsync(organizationId)
.Returns(Task.FromResult(organization));
}
}

View File

@ -6,41 +6,40 @@ using Microsoft.Extensions.Logging;
using NSubstitute;
using Xunit;
namespace Bit.Core.Test.Services
namespace Bit.Core.Test.Services;
public class RelayPushNotificationServiceTests
{
public class RelayPushNotificationServiceTests
private readonly RelayPushNotificationService _sut;
private readonly IHttpClientFactory _httpFactory;
private readonly IDeviceRepository _deviceRepository;
private readonly GlobalSettings _globalSettings;
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly ILogger<RelayPushNotificationService> _logger;
public RelayPushNotificationServiceTests()
{
private readonly RelayPushNotificationService _sut;
_httpFactory = Substitute.For<IHttpClientFactory>();
_deviceRepository = Substitute.For<IDeviceRepository>();
_globalSettings = new GlobalSettings();
_httpContextAccessor = Substitute.For<IHttpContextAccessor>();
_logger = Substitute.For<ILogger<RelayPushNotificationService>>();
private readonly IHttpClientFactory _httpFactory;
private readonly IDeviceRepository _deviceRepository;
private readonly GlobalSettings _globalSettings;
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly ILogger<RelayPushNotificationService> _logger;
_sut = new RelayPushNotificationService(
_httpFactory,
_deviceRepository,
_globalSettings,
_httpContextAccessor,
_logger
);
}
public RelayPushNotificationServiceTests()
{
_httpFactory = Substitute.For<IHttpClientFactory>();
_deviceRepository = Substitute.For<IDeviceRepository>();
_globalSettings = new GlobalSettings();
_httpContextAccessor = Substitute.For<IHttpContextAccessor>();
_logger = Substitute.For<ILogger<RelayPushNotificationService>>();
_sut = new RelayPushNotificationService(
_httpFactory,
_deviceRepository,
_globalSettings,
_httpContextAccessor,
_logger
);
}
// Remove this test when we add actual tests. It only proves that
// we've properly constructed the system under test.
[Fact(Skip = "Needs additional work")]
public void ServiceExists()
{
Assert.NotNull(_sut);
}
// Remove this test when we add actual tests. It only proves that
// we've properly constructed the system under test.
[Fact(Skip = "Needs additional work")]
public void ServiceExists()
{
Assert.NotNull(_sut);
}
}

View File

@ -4,35 +4,34 @@ using Microsoft.Extensions.Logging;
using NSubstitute;
using Xunit;
namespace Bit.Core.Test.Services
namespace Bit.Core.Test.Services;
public class RelayPushRegistrationServiceTests
{
public class RelayPushRegistrationServiceTests
private readonly RelayPushRegistrationService _sut;
private readonly IHttpClientFactory _httpFactory;
private readonly GlobalSettings _globalSettings;
private readonly ILogger<RelayPushRegistrationService> _logger;
public RelayPushRegistrationServiceTests()
{
private readonly RelayPushRegistrationService _sut;
_globalSettings = new GlobalSettings();
_httpFactory = Substitute.For<IHttpClientFactory>();
_logger = Substitute.For<ILogger<RelayPushRegistrationService>>();
private readonly IHttpClientFactory _httpFactory;
private readonly GlobalSettings _globalSettings;
private readonly ILogger<RelayPushRegistrationService> _logger;
_sut = new RelayPushRegistrationService(
_httpFactory,
_globalSettings,
_logger
);
}
public RelayPushRegistrationServiceTests()
{
_globalSettings = new GlobalSettings();
_httpFactory = Substitute.For<IHttpClientFactory>();
_logger = Substitute.For<ILogger<RelayPushRegistrationService>>();
_sut = new RelayPushRegistrationService(
_httpFactory,
_globalSettings,
_logger
);
}
// Remove this test when we add actual tests. It only proves that
// we've properly constructed the system under test.
[Fact(Skip = "Needs additional work")]
public void ServiceExists()
{
Assert.NotNull(_sut);
}
// Remove this test when we add actual tests. It only proves that
// we've properly constructed the system under test.
[Fact(Skip = "Needs additional work")]
public void ServiceExists()
{
Assert.NotNull(_sut);
}
}

View File

@ -3,27 +3,26 @@ using Bit.Core.Services;
using NSubstitute;
using Xunit;
namespace Bit.Core.Test.Services
namespace Bit.Core.Test.Services;
public class RepositoryEventWriteServiceTests
{
public class RepositoryEventWriteServiceTests
private readonly RepositoryEventWriteService _sut;
private readonly IEventRepository _eventRepository;
public RepositoryEventWriteServiceTests()
{
private readonly RepositoryEventWriteService _sut;
_eventRepository = Substitute.For<IEventRepository>();
private readonly IEventRepository _eventRepository;
_sut = new RepositoryEventWriteService(_eventRepository);
}
public RepositoryEventWriteServiceTests()
{
_eventRepository = Substitute.For<IEventRepository>();
_sut = new RepositoryEventWriteService(_eventRepository);
}
// Remove this test when we add actual tests. It only proves that
// we've properly constructed the system under test.
[Fact]
public void ServiceExists()
{
Assert.NotNull(_sut);
}
// Remove this test when we add actual tests. It only proves that
// we've properly constructed the system under test.
[Fact]
public void ServiceExists()
{
Assert.NotNull(_sut);
}
}

View File

@ -8,78 +8,77 @@ using SendGrid;
using SendGrid.Helpers.Mail;
using Xunit;
namespace Bit.Core.Test.Services
namespace Bit.Core.Test.Services;
public class SendGridMailDeliveryServiceTests : IDisposable
{
public class SendGridMailDeliveryServiceTests : IDisposable
private readonly SendGridMailDeliveryService _sut;
private readonly GlobalSettings _globalSettings;
private readonly IWebHostEnvironment _hostingEnvironment;
private readonly ILogger<SendGridMailDeliveryService> _logger;
private readonly ISendGridClient _sendGridClient;
public SendGridMailDeliveryServiceTests()
{
private readonly SendGridMailDeliveryService _sut;
private readonly GlobalSettings _globalSettings;
private readonly IWebHostEnvironment _hostingEnvironment;
private readonly ILogger<SendGridMailDeliveryService> _logger;
private readonly ISendGridClient _sendGridClient;
public SendGridMailDeliveryServiceTests()
_globalSettings = new GlobalSettings
{
_globalSettings = new GlobalSettings
Mail =
{
Mail =
{
SendGridApiKey = "SendGridApiKey"
}
};
SendGridApiKey = "SendGridApiKey"
}
};
_hostingEnvironment = Substitute.For<IWebHostEnvironment>();
_logger = Substitute.For<ILogger<SendGridMailDeliveryService>>();
_sendGridClient = Substitute.For<ISendGridClient>();
_hostingEnvironment = Substitute.For<IWebHostEnvironment>();
_logger = Substitute.For<ILogger<SendGridMailDeliveryService>>();
_sendGridClient = Substitute.For<ISendGridClient>();
_sut = new SendGridMailDeliveryService(
_sendGridClient,
_globalSettings,
_hostingEnvironment,
_logger
);
}
_sut = new SendGridMailDeliveryService(
_sendGridClient,
_globalSettings,
_hostingEnvironment,
_logger
);
}
public void Dispose()
public void Dispose()
{
_sut?.Dispose();
}
[Fact]
public async Task SendEmailAsync_CallsSendEmailAsync_WhenMessageIsValid()
{
var mailMessage = new MailMessage
{
_sut?.Dispose();
}
ToEmails = new List<string> { "ToEmails" },
BccEmails = new List<string> { "BccEmails" },
Subject = "Subject",
HtmlContent = "HtmlContent",
TextContent = "TextContent",
Category = "Category"
};
[Fact]
public async Task SendEmailAsync_CallsSendEmailAsync_WhenMessageIsValid()
{
var mailMessage = new MailMessage
_sendGridClient.SendEmailAsync(Arg.Any<SendGridMessage>()).Returns(
new Response(System.Net.HttpStatusCode.OK, null, null));
await _sut.SendEmailAsync(mailMessage);
await _sendGridClient.Received(1).SendEmailAsync(
Arg.Do<SendGridMessage>(msg =>
{
ToEmails = new List<string> { "ToEmails" },
BccEmails = new List<string> { "BccEmails" },
Subject = "Subject",
HtmlContent = "HtmlContent",
TextContent = "TextContent",
Category = "Category"
};
msg.Received(1).AddTos(new List<EmailAddress> { new EmailAddress(mailMessage.ToEmails.First()) });
msg.Received(1).AddBccs(new List<EmailAddress> { new EmailAddress(mailMessage.ToEmails.First()) });
_sendGridClient.SendEmailAsync(Arg.Any<SendGridMessage>()).Returns(
new Response(System.Net.HttpStatusCode.OK, null, null));
await _sut.SendEmailAsync(mailMessage);
Assert.Equal(mailMessage.Subject, msg.Subject);
Assert.Equal(mailMessage.HtmlContent, msg.HtmlContent);
Assert.Equal(mailMessage.TextContent, msg.PlainTextContent);
await _sendGridClient.Received(1).SendEmailAsync(
Arg.Do<SendGridMessage>(msg =>
{
msg.Received(1).AddTos(new List<EmailAddress> { new EmailAddress(mailMessage.ToEmails.First()) });
msg.Received(1).AddBccs(new List<EmailAddress> { new EmailAddress(mailMessage.ToEmails.First()) });
Assert.Contains("type:Cateogry", msg.Categories);
Assert.Contains(msg.Categories, x => x.StartsWith("env:"));
Assert.Contains(msg.Categories, x => x.StartsWith("sender:"));
Assert.Equal(mailMessage.Subject, msg.Subject);
Assert.Equal(mailMessage.HtmlContent, msg.HtmlContent);
Assert.Equal(mailMessage.TextContent, msg.PlainTextContent);
Assert.Contains("type:Cateogry", msg.Categories);
Assert.Contains(msg.Categories, x => x.StartsWith("env:"));
Assert.Contains(msg.Categories, x => x.StartsWith("sender:"));
msg.Received(1).SetClickTracking(false, false);
msg.Received(1).SetOpenTracking(false);
}));
}
msg.Received(1).SetClickTracking(false, false);
msg.Received(1).SetOpenTracking(false);
}));
}
}

File diff suppressed because it is too large Load Diff

View File

@ -9,309 +9,308 @@ using Bit.Test.Common.AutoFixture.Attributes;
using NSubstitute;
using Xunit;
namespace Bit.Core.Test.Services
namespace Bit.Core.Test.Services;
public class SsoConfigServiceTests
{
public class SsoConfigServiceTests
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task SaveAsync_ExistingItem_UpdatesRevisionDateOnly(SutProvider<SsoConfigService> sutProvider,
Organization organization)
{
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task SaveAsync_ExistingItem_UpdatesRevisionDateOnly(SutProvider<SsoConfigService> sutProvider,
Organization organization)
var utcNow = DateTime.UtcNow;
var ssoConfig = new SsoConfig
{
var utcNow = DateTime.UtcNow;
Id = 1,
Data = "{}",
Enabled = true,
OrganizationId = organization.Id,
CreationDate = utcNow.AddDays(-10),
RevisionDate = utcNow.AddDays(-10),
};
var ssoConfig = new SsoConfig
{
Id = 1,
Data = "{}",
Enabled = true,
OrganizationId = organization.Id,
CreationDate = utcNow.AddDays(-10),
RevisionDate = utcNow.AddDays(-10),
};
sutProvider.GetDependency<ISsoConfigRepository>()
.UpsertAsync(ssoConfig).Returns(Task.CompletedTask);
sutProvider.GetDependency<ISsoConfigRepository>()
.UpsertAsync(ssoConfig).Returns(Task.CompletedTask);
await sutProvider.Sut.SaveAsync(ssoConfig, organization);
await sutProvider.Sut.SaveAsync(ssoConfig, organization);
await sutProvider.GetDependency<ISsoConfigRepository>().Received()
.UpsertAsync(ssoConfig);
await sutProvider.GetDependency<ISsoConfigRepository>().Received()
.UpsertAsync(ssoConfig);
Assert.Equal(utcNow.AddDays(-10), ssoConfig.CreationDate);
Assert.True(ssoConfig.RevisionDate - utcNow < TimeSpan.FromSeconds(1));
}
Assert.Equal(utcNow.AddDays(-10), ssoConfig.CreationDate);
Assert.True(ssoConfig.RevisionDate - utcNow < TimeSpan.FromSeconds(1));
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task SaveAsync_NewItem_UpdatesCreationAndRevisionDate(SutProvider<SsoConfigService> sutProvider,
Organization organization)
{
var utcNow = DateTime.UtcNow;
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task SaveAsync_NewItem_UpdatesCreationAndRevisionDate(SutProvider<SsoConfigService> sutProvider,
Organization organization)
var ssoConfig = new SsoConfig
{
var utcNow = DateTime.UtcNow;
Id = default,
Data = "{}",
Enabled = true,
OrganizationId = organization.Id,
CreationDate = utcNow.AddDays(-10),
RevisionDate = utcNow.AddDays(-10),
};
var ssoConfig = new SsoConfig
{
Id = default,
Data = "{}",
Enabled = true,
OrganizationId = organization.Id,
CreationDate = utcNow.AddDays(-10),
RevisionDate = utcNow.AddDays(-10),
};
sutProvider.GetDependency<ISsoConfigRepository>()
.UpsertAsync(ssoConfig).Returns(Task.CompletedTask);
sutProvider.GetDependency<ISsoConfigRepository>()
.UpsertAsync(ssoConfig).Returns(Task.CompletedTask);
await sutProvider.Sut.SaveAsync(ssoConfig, organization);
await sutProvider.Sut.SaveAsync(ssoConfig, organization);
await sutProvider.GetDependency<ISsoConfigRepository>().Received()
.UpsertAsync(ssoConfig);
await sutProvider.GetDependency<ISsoConfigRepository>().Received()
.UpsertAsync(ssoConfig);
Assert.True(ssoConfig.CreationDate - utcNow < TimeSpan.FromSeconds(1));
Assert.True(ssoConfig.RevisionDate - utcNow < TimeSpan.FromSeconds(1));
}
Assert.True(ssoConfig.CreationDate - utcNow < TimeSpan.FromSeconds(1));
Assert.True(ssoConfig.RevisionDate - utcNow < TimeSpan.FromSeconds(1));
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task SaveAsync_PreventDisablingKeyConnector(SutProvider<SsoConfigService> sutProvider,
Organization organization)
{
var utcNow = DateTime.UtcNow;
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task SaveAsync_PreventDisablingKeyConnector(SutProvider<SsoConfigService> sutProvider,
Organization organization)
var oldSsoConfig = new SsoConfig
{
var utcNow = DateTime.UtcNow;
var oldSsoConfig = new SsoConfig
Id = 1,
Data = new SsoConfigurationData
{
Id = 1,
Data = new SsoConfigurationData
{
KeyConnectorEnabled = true,
}.Serialize(),
Enabled = true,
OrganizationId = organization.Id,
CreationDate = utcNow.AddDays(-10),
RevisionDate = utcNow.AddDays(-10),
};
KeyConnectorEnabled = true,
}.Serialize(),
Enabled = true,
OrganizationId = organization.Id,
CreationDate = utcNow.AddDays(-10),
RevisionDate = utcNow.AddDays(-10),
};
var newSsoConfig = new SsoConfig
{
Id = 1,
Data = "{}",
Enabled = true,
OrganizationId = organization.Id,
CreationDate = utcNow.AddDays(-10),
RevisionDate = utcNow,
};
var ssoConfigRepository = sutProvider.GetDependency<ISsoConfigRepository>();
ssoConfigRepository.GetByOrganizationIdAsync(organization.Id).Returns(oldSsoConfig);
ssoConfigRepository.UpsertAsync(newSsoConfig).Returns(Task.CompletedTask);
sutProvider.GetDependency<IOrganizationUserRepository>().GetManyDetailsByOrganizationAsync(organization.Id)
.Returns(new[] { new OrganizationUserUserDetails { UsesKeyConnector = true } });
var exception = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.SaveAsync(newSsoConfig, organization));
Assert.Contains("Key Connector cannot be disabled at this moment.", exception.Message);
await sutProvider.GetDependency<ISsoConfigRepository>().DidNotReceiveWithAnyArgs()
.UpsertAsync(default);
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task SaveAsync_AllowDisablingKeyConnectorWhenNoUserIsUsingIt(
SutProvider<SsoConfigService> sutProvider, Organization organization)
var newSsoConfig = new SsoConfig
{
var utcNow = DateTime.UtcNow;
Id = 1,
Data = "{}",
Enabled = true,
OrganizationId = organization.Id,
CreationDate = utcNow.AddDays(-10),
RevisionDate = utcNow,
};
var oldSsoConfig = new SsoConfig
{
Id = 1,
Data = new SsoConfigurationData
{
KeyConnectorEnabled = true,
}.Serialize(),
Enabled = true,
OrganizationId = organization.Id,
CreationDate = utcNow.AddDays(-10),
RevisionDate = utcNow.AddDays(-10),
};
var ssoConfigRepository = sutProvider.GetDependency<ISsoConfigRepository>();
ssoConfigRepository.GetByOrganizationIdAsync(organization.Id).Returns(oldSsoConfig);
ssoConfigRepository.UpsertAsync(newSsoConfig).Returns(Task.CompletedTask);
sutProvider.GetDependency<IOrganizationUserRepository>().GetManyDetailsByOrganizationAsync(organization.Id)
.Returns(new[] { new OrganizationUserUserDetails { UsesKeyConnector = true } });
var newSsoConfig = new SsoConfig
{
Id = 1,
Data = "{}",
Enabled = true,
OrganizationId = organization.Id,
CreationDate = utcNow.AddDays(-10),
RevisionDate = utcNow,
};
var exception = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.SaveAsync(newSsoConfig, organization));
var ssoConfigRepository = sutProvider.GetDependency<ISsoConfigRepository>();
ssoConfigRepository.GetByOrganizationIdAsync(organization.Id).Returns(oldSsoConfig);
ssoConfigRepository.UpsertAsync(newSsoConfig).Returns(Task.CompletedTask);
sutProvider.GetDependency<IOrganizationUserRepository>().GetManyDetailsByOrganizationAsync(organization.Id)
.Returns(new[] { new OrganizationUserUserDetails { UsesKeyConnector = false } });
Assert.Contains("Key Connector cannot be disabled at this moment.", exception.Message);
await sutProvider.Sut.SaveAsync(newSsoConfig, organization);
}
await sutProvider.GetDependency<ISsoConfigRepository>().DidNotReceiveWithAnyArgs()
.UpsertAsync(default);
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task SaveAsync_KeyConnector_SingleOrgNotEnabled_Throws(SutProvider<SsoConfigService> sutProvider,
Organization organization)
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task SaveAsync_AllowDisablingKeyConnectorWhenNoUserIsUsingIt(
SutProvider<SsoConfigService> sutProvider, Organization organization)
{
var utcNow = DateTime.UtcNow;
var oldSsoConfig = new SsoConfig
{
var utcNow = DateTime.UtcNow;
var ssoConfig = new SsoConfig
Id = 1,
Data = new SsoConfigurationData
{
Id = default,
Data = new SsoConfigurationData
{
KeyConnectorEnabled = true,
}.Serialize(),
Enabled = true,
OrganizationId = organization.Id,
CreationDate = utcNow.AddDays(-10),
RevisionDate = utcNow.AddDays(-10),
};
KeyConnectorEnabled = true,
}.Serialize(),
Enabled = true,
OrganizationId = organization.Id,
CreationDate = utcNow.AddDays(-10),
RevisionDate = utcNow.AddDays(-10),
};
var exception = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.SaveAsync(ssoConfig, organization));
Assert.Contains("Key Connector requires the Single Organization policy to be enabled.", exception.Message);
await sutProvider.GetDependency<ISsoConfigRepository>().DidNotReceiveWithAnyArgs()
.UpsertAsync(default);
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task SaveAsync_KeyConnector_SsoPolicyNotEnabled_Throws(SutProvider<SsoConfigService> sutProvider,
Organization organization)
var newSsoConfig = new SsoConfig
{
var utcNow = DateTime.UtcNow;
Id = 1,
Data = "{}",
Enabled = true,
OrganizationId = organization.Id,
CreationDate = utcNow.AddDays(-10),
RevisionDate = utcNow,
};
var ssoConfig = new SsoConfig
{
Id = default,
Data = new SsoConfigurationData
{
KeyConnectorEnabled = true,
}.Serialize(),
Enabled = true,
OrganizationId = organization.Id,
CreationDate = utcNow.AddDays(-10),
RevisionDate = utcNow.AddDays(-10),
};
var ssoConfigRepository = sutProvider.GetDependency<ISsoConfigRepository>();
ssoConfigRepository.GetByOrganizationIdAsync(organization.Id).Returns(oldSsoConfig);
ssoConfigRepository.UpsertAsync(newSsoConfig).Returns(Task.CompletedTask);
sutProvider.GetDependency<IOrganizationUserRepository>().GetManyDetailsByOrganizationAsync(organization.Id)
.Returns(new[] { new OrganizationUserUserDetails { UsesKeyConnector = false } });
sutProvider.GetDependency<IPolicyRepository>().GetByOrganizationIdTypeAsync(
Arg.Any<Guid>(), Enums.PolicyType.SingleOrg).Returns(new Policy
{
Enabled = true
});
await sutProvider.Sut.SaveAsync(newSsoConfig, organization);
}
var exception = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.SaveAsync(ssoConfig, organization));
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task SaveAsync_KeyConnector_SingleOrgNotEnabled_Throws(SutProvider<SsoConfigService> sutProvider,
Organization organization)
{
var utcNow = DateTime.UtcNow;
Assert.Contains("Key Connector requires the Single Sign-On Authentication policy to be enabled.", exception.Message);
await sutProvider.GetDependency<ISsoConfigRepository>().DidNotReceiveWithAnyArgs()
.UpsertAsync(default);
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task SaveAsync_KeyConnector_SsoConfigNotEnabled_Throws(SutProvider<SsoConfigService> sutProvider,
Organization organization)
var ssoConfig = new SsoConfig
{
var utcNow = DateTime.UtcNow;
var ssoConfig = new SsoConfig
Id = default,
Data = new SsoConfigurationData
{
Id = default,
Data = new SsoConfigurationData
{
KeyConnectorEnabled = true,
}.Serialize(),
Enabled = false,
OrganizationId = organization.Id,
CreationDate = utcNow.AddDays(-10),
RevisionDate = utcNow.AddDays(-10),
};
KeyConnectorEnabled = true,
}.Serialize(),
Enabled = true,
OrganizationId = organization.Id,
CreationDate = utcNow.AddDays(-10),
RevisionDate = utcNow.AddDays(-10),
};
sutProvider.GetDependency<IPolicyRepository>().GetByOrganizationIdTypeAsync(
Arg.Any<Guid>(), Arg.Any<Enums.PolicyType>()).Returns(new Policy
{
Enabled = true
});
var exception = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.SaveAsync(ssoConfig, organization));
var exception = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.SaveAsync(ssoConfig, organization));
Assert.Contains("Key Connector requires the Single Organization policy to be enabled.", exception.Message);
Assert.Contains("You must enable SSO to use Key Connector.", exception.Message);
await sutProvider.GetDependency<ISsoConfigRepository>().DidNotReceiveWithAnyArgs()
.UpsertAsync(default);
}
await sutProvider.GetDependency<ISsoConfigRepository>().DidNotReceiveWithAnyArgs()
.UpsertAsync(default);
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task SaveAsync_KeyConnector_SsoPolicyNotEnabled_Throws(SutProvider<SsoConfigService> sutProvider,
Organization organization)
{
var utcNow = DateTime.UtcNow;
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task SaveAsync_KeyConnector_KeyConnectorAbilityNotEnabled_Throws(SutProvider<SsoConfigService> sutProvider,
Organization organization)
var ssoConfig = new SsoConfig
{
var utcNow = DateTime.UtcNow;
organization.UseKeyConnector = false;
var ssoConfig = new SsoConfig
Id = default,
Data = new SsoConfigurationData
{
Id = default,
Data = new SsoConfigurationData
{
KeyConnectorEnabled = true,
}.Serialize(),
Enabled = true,
OrganizationId = organization.Id,
CreationDate = utcNow.AddDays(-10),
RevisionDate = utcNow.AddDays(-10),
};
KeyConnectorEnabled = true,
}.Serialize(),
Enabled = true,
OrganizationId = organization.Id,
CreationDate = utcNow.AddDays(-10),
RevisionDate = utcNow.AddDays(-10),
};
sutProvider.GetDependency<IPolicyRepository>().GetByOrganizationIdTypeAsync(
Arg.Any<Guid>(), Arg.Any<Enums.PolicyType>()).Returns(new Policy
{
Enabled = true,
});
sutProvider.GetDependency<IPolicyRepository>().GetByOrganizationIdTypeAsync(
Arg.Any<Guid>(), Enums.PolicyType.SingleOrg).Returns(new Policy
{
Enabled = true
});
var exception = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.SaveAsync(ssoConfig, organization));
var exception = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.SaveAsync(ssoConfig, organization));
Assert.Contains("Organization cannot use Key Connector.", exception.Message);
Assert.Contains("Key Connector requires the Single Sign-On Authentication policy to be enabled.", exception.Message);
await sutProvider.GetDependency<ISsoConfigRepository>().DidNotReceiveWithAnyArgs()
.UpsertAsync(default);
}
await sutProvider.GetDependency<ISsoConfigRepository>().DidNotReceiveWithAnyArgs()
.UpsertAsync(default);
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task SaveAsync_KeyConnector_Success(SutProvider<SsoConfigService> sutProvider,
Organization organization)
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task SaveAsync_KeyConnector_SsoConfigNotEnabled_Throws(SutProvider<SsoConfigService> sutProvider,
Organization organization)
{
var utcNow = DateTime.UtcNow;
var ssoConfig = new SsoConfig
{
var utcNow = DateTime.UtcNow;
organization.UseKeyConnector = true;
var ssoConfig = new SsoConfig
Id = default,
Data = new SsoConfigurationData
{
KeyConnectorEnabled = true,
}.Serialize(),
Enabled = false,
OrganizationId = organization.Id,
CreationDate = utcNow.AddDays(-10),
RevisionDate = utcNow.AddDays(-10),
};
sutProvider.GetDependency<IPolicyRepository>().GetByOrganizationIdTypeAsync(
Arg.Any<Guid>(), Arg.Any<Enums.PolicyType>()).Returns(new Policy
{
Enabled = true
});
var exception = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.SaveAsync(ssoConfig, organization));
Assert.Contains("You must enable SSO to use Key Connector.", exception.Message);
await sutProvider.GetDependency<ISsoConfigRepository>().DidNotReceiveWithAnyArgs()
.UpsertAsync(default);
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task SaveAsync_KeyConnector_KeyConnectorAbilityNotEnabled_Throws(SutProvider<SsoConfigService> sutProvider,
Organization organization)
{
var utcNow = DateTime.UtcNow;
organization.UseKeyConnector = false;
var ssoConfig = new SsoConfig
{
Id = default,
Data = new SsoConfigurationData
{
KeyConnectorEnabled = true,
}.Serialize(),
Enabled = true,
OrganizationId = organization.Id,
CreationDate = utcNow.AddDays(-10),
RevisionDate = utcNow.AddDays(-10),
};
sutProvider.GetDependency<IPolicyRepository>().GetByOrganizationIdTypeAsync(
Arg.Any<Guid>(), Arg.Any<Enums.PolicyType>()).Returns(new Policy
{
Id = default,
Data = new SsoConfigurationData
{
KeyConnectorEnabled = true,
}.Serialize(),
Enabled = true,
OrganizationId = organization.Id,
CreationDate = utcNow.AddDays(-10),
RevisionDate = utcNow.AddDays(-10),
};
});
sutProvider.GetDependency<IPolicyRepository>().GetByOrganizationIdTypeAsync(
Arg.Any<Guid>(), Arg.Any<Enums.PolicyType>()).Returns(new Policy
{
Enabled = true,
});
var exception = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.SaveAsync(ssoConfig, organization));
await sutProvider.Sut.SaveAsync(ssoConfig, organization);
Assert.Contains("Organization cannot use Key Connector.", exception.Message);
await sutProvider.GetDependency<ISsoConfigRepository>().ReceivedWithAnyArgs()
.UpsertAsync(default);
}
await sutProvider.GetDependency<ISsoConfigRepository>().DidNotReceiveWithAnyArgs()
.UpsertAsync(default);
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task SaveAsync_KeyConnector_Success(SutProvider<SsoConfigService> sutProvider,
Organization organization)
{
var utcNow = DateTime.UtcNow;
organization.UseKeyConnector = true;
var ssoConfig = new SsoConfig
{
Id = default,
Data = new SsoConfigurationData
{
KeyConnectorEnabled = true,
}.Serialize(),
Enabled = true,
OrganizationId = organization.Id,
CreationDate = utcNow.AddDays(-10),
RevisionDate = utcNow.AddDays(-10),
};
sutProvider.GetDependency<IPolicyRepository>().GetByOrganizationIdTypeAsync(
Arg.Any<Guid>(), Arg.Any<Enums.PolicyType>()).Returns(new Policy
{
Enabled = true,
});
await sutProvider.Sut.SaveAsync(ssoConfig, organization);
await sutProvider.GetDependency<ISsoConfigRepository>().ReceivedWithAnyArgs()
.UpsertAsync(default);
}
}

View File

@ -12,363 +12,362 @@ using NSubstitute;
using Xunit;
using PaymentMethodType = Bit.Core.Enums.PaymentMethodType;
namespace Bit.Core.Test.Services
namespace Bit.Core.Test.Services;
public class StripePaymentServiceTests
{
public class StripePaymentServiceTests
[Theory]
[InlineCustomAutoData(new[] { typeof(SutProviderCustomization) }, PaymentMethodType.BitPay)]
[InlineCustomAutoData(new[] { typeof(SutProviderCustomization) }, PaymentMethodType.BitPay)]
[InlineCustomAutoData(new[] { typeof(SutProviderCustomization) }, PaymentMethodType.Credit)]
[InlineCustomAutoData(new[] { typeof(SutProviderCustomization) }, PaymentMethodType.WireTransfer)]
[InlineCustomAutoData(new[] { typeof(SutProviderCustomization) }, PaymentMethodType.AppleInApp)]
[InlineCustomAutoData(new[] { typeof(SutProviderCustomization) }, PaymentMethodType.GoogleInApp)]
[InlineCustomAutoData(new[] { typeof(SutProviderCustomization) }, PaymentMethodType.Check)]
public async void PurchaseOrganizationAsync_Invalid(PaymentMethodType paymentMethodType, SutProvider<StripePaymentService> sutProvider)
{
[Theory]
[InlineCustomAutoData(new[] { typeof(SutProviderCustomization) }, PaymentMethodType.BitPay)]
[InlineCustomAutoData(new[] { typeof(SutProviderCustomization) }, PaymentMethodType.BitPay)]
[InlineCustomAutoData(new[] { typeof(SutProviderCustomization) }, PaymentMethodType.Credit)]
[InlineCustomAutoData(new[] { typeof(SutProviderCustomization) }, PaymentMethodType.WireTransfer)]
[InlineCustomAutoData(new[] { typeof(SutProviderCustomization) }, PaymentMethodType.AppleInApp)]
[InlineCustomAutoData(new[] { typeof(SutProviderCustomization) }, PaymentMethodType.GoogleInApp)]
[InlineCustomAutoData(new[] { typeof(SutProviderCustomization) }, PaymentMethodType.Check)]
public async void PurchaseOrganizationAsync_Invalid(PaymentMethodType paymentMethodType, SutProvider<StripePaymentService> sutProvider)
var exception = await Assert.ThrowsAsync<GatewayException>(
() => sutProvider.Sut.PurchaseOrganizationAsync(null, paymentMethodType, null, null, 0, 0, false, null));
Assert.Equal("Payment method is not supported at this time.", exception.Message);
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async void PurchaseOrganizationAsync_Stripe(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
{
var plan = StaticStore.Plans.First(p => p.Type == PlanType.EnterpriseAnnually);
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer
{
var exception = await Assert.ThrowsAsync<GatewayException>(
() => sutProvider.Sut.PurchaseOrganizationAsync(null, paymentMethodType, null, null, 0, 0, false, null));
Assert.Equal("Payment method is not supported at this time.", exception.Message);
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async void PurchaseOrganizationAsync_Stripe(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
Id = "C-1",
});
stripeAdapter.SubscriptionCreateAsync(default).ReturnsForAnyArgs(new Stripe.Subscription
{
var plan = StaticStore.Plans.First(p => p.Type == PlanType.EnterpriseAnnually);
Id = "S-1",
CurrentPeriodEnd = DateTime.Today.AddDays(10),
});
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer
{
Id = "C-1",
});
stripeAdapter.SubscriptionCreateAsync(default).ReturnsForAnyArgs(new Stripe.Subscription
{
Id = "S-1",
CurrentPeriodEnd = DateTime.Today.AddDays(10),
});
var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.Card, paymentToken, plan, 0, 0, false, taxInfo);
var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.Card, paymentToken, plan, 0, 0, false, taxInfo);
Assert.Null(result);
Assert.Equal(GatewayType.Stripe, organization.Gateway);
Assert.Equal("C-1", organization.GatewayCustomerId);
Assert.Equal("S-1", organization.GatewaySubscriptionId);
Assert.True(organization.Enabled);
Assert.Equal(DateTime.Today.AddDays(10), organization.ExpirationDate);
Assert.Null(result);
Assert.Equal(GatewayType.Stripe, organization.Gateway);
Assert.Equal("C-1", organization.GatewayCustomerId);
Assert.Equal("S-1", organization.GatewaySubscriptionId);
Assert.True(organization.Enabled);
Assert.Equal(DateTime.Today.AddDays(10), organization.ExpirationDate);
await stripeAdapter.Received().CustomerCreateAsync(Arg.Is<Stripe.CustomerCreateOptions>(c =>
c.Description == organization.BusinessName &&
c.Email == organization.BillingEmail &&
c.Source == paymentToken &&
c.PaymentMethod == null &&
!c.Metadata.Any() &&
c.InvoiceSettings.DefaultPaymentMethod == null &&
c.Address.Country == taxInfo.BillingAddressCountry &&
c.Address.PostalCode == taxInfo.BillingAddressPostalCode &&
c.Address.Line1 == taxInfo.BillingAddressLine1 &&
c.Address.Line2 == taxInfo.BillingAddressLine2 &&
c.Address.City == taxInfo.BillingAddressCity &&
c.Address.State == taxInfo.BillingAddressState &&
c.TaxIdData == null
));
await stripeAdapter.Received().CustomerCreateAsync(Arg.Is<Stripe.CustomerCreateOptions>(c =>
c.Description == organization.BusinessName &&
c.Email == organization.BillingEmail &&
c.Source == paymentToken &&
c.PaymentMethod == null &&
!c.Metadata.Any() &&
c.InvoiceSettings.DefaultPaymentMethod == null &&
c.Address.Country == taxInfo.BillingAddressCountry &&
c.Address.PostalCode == taxInfo.BillingAddressPostalCode &&
c.Address.Line1 == taxInfo.BillingAddressLine1 &&
c.Address.Line2 == taxInfo.BillingAddressLine2 &&
c.Address.City == taxInfo.BillingAddressCity &&
c.Address.State == taxInfo.BillingAddressState &&
c.TaxIdData == null
));
await stripeAdapter.Received().SubscriptionCreateAsync(Arg.Is<Stripe.SubscriptionCreateOptions>(s =>
s.Customer == "C-1" &&
s.Expand[0] == "latest_invoice.payment_intent" &&
s.Metadata[organization.GatewayIdField()] == organization.Id.ToString() &&
s.Items.Count == 0
));
}
await stripeAdapter.Received().SubscriptionCreateAsync(Arg.Is<Stripe.SubscriptionCreateOptions>(s =>
s.Customer == "C-1" &&
s.Expand[0] == "latest_invoice.payment_intent" &&
s.Metadata[organization.GatewayIdField()] == organization.Id.ToString() &&
s.Items.Count == 0
));
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async void PurchaseOrganizationAsync_Stripe_PM(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
{
var plan = StaticStore.Plans.First(p => p.Type == PlanType.EnterpriseAnnually);
paymentToken = "pm_" + paymentToken;
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async void PurchaseOrganizationAsync_Stripe_PM(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer
{
var plan = StaticStore.Plans.First(p => p.Type == PlanType.EnterpriseAnnually);
paymentToken = "pm_" + paymentToken;
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer
{
Id = "C-1",
});
stripeAdapter.SubscriptionCreateAsync(default).ReturnsForAnyArgs(new Stripe.Subscription
{
Id = "S-1",
CurrentPeriodEnd = DateTime.Today.AddDays(10),
});
var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.Card, paymentToken, plan, 0, 0, false, taxInfo);
Assert.Null(result);
Assert.Equal(GatewayType.Stripe, organization.Gateway);
Assert.Equal("C-1", organization.GatewayCustomerId);
Assert.Equal("S-1", organization.GatewaySubscriptionId);
Assert.True(organization.Enabled);
Assert.Equal(DateTime.Today.AddDays(10), organization.ExpirationDate);
await stripeAdapter.Received().CustomerCreateAsync(Arg.Is<Stripe.CustomerCreateOptions>(c =>
c.Description == organization.BusinessName &&
c.Email == organization.BillingEmail &&
c.Source == null &&
c.PaymentMethod == paymentToken &&
!c.Metadata.Any() &&
c.InvoiceSettings.DefaultPaymentMethod == paymentToken &&
c.Address.Country == taxInfo.BillingAddressCountry &&
c.Address.PostalCode == taxInfo.BillingAddressPostalCode &&
c.Address.Line1 == taxInfo.BillingAddressLine1 &&
c.Address.Line2 == taxInfo.BillingAddressLine2 &&
c.Address.City == taxInfo.BillingAddressCity &&
c.Address.State == taxInfo.BillingAddressState &&
c.TaxIdData == null
));
await stripeAdapter.Received().SubscriptionCreateAsync(Arg.Is<Stripe.SubscriptionCreateOptions>(s =>
s.Customer == "C-1" &&
s.Expand[0] == "latest_invoice.payment_intent" &&
s.Metadata[organization.GatewayIdField()] == organization.Id.ToString() &&
s.Items.Count == 0
));
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async void PurchaseOrganizationAsync_Stripe_TaxRate(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
Id = "C-1",
});
stripeAdapter.SubscriptionCreateAsync(default).ReturnsForAnyArgs(new Stripe.Subscription
{
var plan = StaticStore.Plans.First(p => p.Type == PlanType.EnterpriseAnnually);
Id = "S-1",
CurrentPeriodEnd = DateTime.Today.AddDays(10),
});
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer
{
Id = "C-1",
});
stripeAdapter.SubscriptionCreateAsync(default).ReturnsForAnyArgs(new Stripe.Subscription
{
Id = "S-1",
CurrentPeriodEnd = DateTime.Today.AddDays(10),
});
sutProvider.GetDependency<ITaxRateRepository>().GetByLocationAsync(Arg.Is<TaxRate>(t =>
t.Country == taxInfo.BillingAddressCountry && t.PostalCode == taxInfo.BillingAddressPostalCode))
.Returns(new List<TaxRate> { new() { Id = "T-1" } });
var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.Card, paymentToken, plan, 0, 0, false, taxInfo);
var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.Card, paymentToken, plan, 0, 0, false, taxInfo);
Assert.Null(result);
Assert.Equal(GatewayType.Stripe, organization.Gateway);
Assert.Equal("C-1", organization.GatewayCustomerId);
Assert.Equal("S-1", organization.GatewaySubscriptionId);
Assert.True(organization.Enabled);
Assert.Equal(DateTime.Today.AddDays(10), organization.ExpirationDate);
Assert.Null(result);
await stripeAdapter.Received().CustomerCreateAsync(Arg.Is<Stripe.CustomerCreateOptions>(c =>
c.Description == organization.BusinessName &&
c.Email == organization.BillingEmail &&
c.Source == null &&
c.PaymentMethod == paymentToken &&
!c.Metadata.Any() &&
c.InvoiceSettings.DefaultPaymentMethod == paymentToken &&
c.Address.Country == taxInfo.BillingAddressCountry &&
c.Address.PostalCode == taxInfo.BillingAddressPostalCode &&
c.Address.Line1 == taxInfo.BillingAddressLine1 &&
c.Address.Line2 == taxInfo.BillingAddressLine2 &&
c.Address.City == taxInfo.BillingAddressCity &&
c.Address.State == taxInfo.BillingAddressState &&
c.TaxIdData == null
));
await stripeAdapter.Received().SubscriptionCreateAsync(Arg.Is<Stripe.SubscriptionCreateOptions>(s =>
s.DefaultTaxRates.Count == 1 &&
s.DefaultTaxRates[0] == "T-1"
));
}
await stripeAdapter.Received().SubscriptionCreateAsync(Arg.Is<Stripe.SubscriptionCreateOptions>(s =>
s.Customer == "C-1" &&
s.Expand[0] == "latest_invoice.payment_intent" &&
s.Metadata[organization.GatewayIdField()] == organization.Id.ToString() &&
s.Items.Count == 0
));
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async void PurchaseOrganizationAsync_Stripe_Declined(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async void PurchaseOrganizationAsync_Stripe_TaxRate(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
{
var plan = StaticStore.Plans.First(p => p.Type == PlanType.EnterpriseAnnually);
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer
{
var plan = StaticStore.Plans.First(p => p.Type == PlanType.EnterpriseAnnually);
paymentToken = "pm_" + paymentToken;
Id = "C-1",
});
stripeAdapter.SubscriptionCreateAsync(default).ReturnsForAnyArgs(new Stripe.Subscription
{
Id = "S-1",
CurrentPeriodEnd = DateTime.Today.AddDays(10),
});
sutProvider.GetDependency<ITaxRateRepository>().GetByLocationAsync(Arg.Is<TaxRate>(t =>
t.Country == taxInfo.BillingAddressCountry && t.PostalCode == taxInfo.BillingAddressPostalCode))
.Returns(new List<TaxRate> { new() { Id = "T-1" } });
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer
var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.Card, paymentToken, plan, 0, 0, false, taxInfo);
Assert.Null(result);
await stripeAdapter.Received().SubscriptionCreateAsync(Arg.Is<Stripe.SubscriptionCreateOptions>(s =>
s.DefaultTaxRates.Count == 1 &&
s.DefaultTaxRates[0] == "T-1"
));
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async void PurchaseOrganizationAsync_Stripe_Declined(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
{
var plan = StaticStore.Plans.First(p => p.Type == PlanType.EnterpriseAnnually);
paymentToken = "pm_" + paymentToken;
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer
{
Id = "C-1",
});
stripeAdapter.SubscriptionCreateAsync(default).ReturnsForAnyArgs(new Stripe.Subscription
{
Id = "S-1",
CurrentPeriodEnd = DateTime.Today.AddDays(10),
Status = "incomplete",
LatestInvoice = new Stripe.Invoice
{
Id = "C-1",
});
stripeAdapter.SubscriptionCreateAsync(default).ReturnsForAnyArgs(new Stripe.Subscription
{
Id = "S-1",
CurrentPeriodEnd = DateTime.Today.AddDays(10),
Status = "incomplete",
LatestInvoice = new Stripe.Invoice
PaymentIntent = new Stripe.PaymentIntent
{
PaymentIntent = new Stripe.PaymentIntent
{
Status = "requires_payment_method",
},
Status = "requires_payment_method",
},
});
},
});
var exception = await Assert.ThrowsAsync<GatewayException>(
() => sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.Card, paymentToken, plan, 0, 0, false, taxInfo));
var exception = await Assert.ThrowsAsync<GatewayException>(
() => sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.Card, paymentToken, plan, 0, 0, false, taxInfo));
Assert.Equal("Payment method was declined.", exception.Message);
Assert.Equal("Payment method was declined.", exception.Message);
await stripeAdapter.Received(1).CustomerDeleteAsync("C-1");
}
await stripeAdapter.Received(1).CustomerDeleteAsync("C-1");
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async void PurchaseOrganizationAsync_Stripe_RequiresAction(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async void PurchaseOrganizationAsync_Stripe_RequiresAction(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
{
var plan = StaticStore.Plans.First(p => p.Type == PlanType.EnterpriseAnnually);
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer
{
var plan = StaticStore.Plans.First(p => p.Type == PlanType.EnterpriseAnnually);
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer
Id = "C-1",
});
stripeAdapter.SubscriptionCreateAsync(default).ReturnsForAnyArgs(new Stripe.Subscription
{
Id = "S-1",
CurrentPeriodEnd = DateTime.Today.AddDays(10),
Status = "incomplete",
LatestInvoice = new Stripe.Invoice
{
Id = "C-1",
});
stripeAdapter.SubscriptionCreateAsync(default).ReturnsForAnyArgs(new Stripe.Subscription
{
Id = "S-1",
CurrentPeriodEnd = DateTime.Today.AddDays(10),
Status = "incomplete",
LatestInvoice = new Stripe.Invoice
PaymentIntent = new Stripe.PaymentIntent
{
PaymentIntent = new Stripe.PaymentIntent
{
Status = "requires_action",
ClientSecret = "clientSecret",
},
Status = "requires_action",
ClientSecret = "clientSecret",
},
});
},
});
var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.Card, paymentToken, plan, 0, 0, false, taxInfo);
var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.Card, paymentToken, plan, 0, 0, false, taxInfo);
Assert.Equal("clientSecret", result);
Assert.False(organization.Enabled);
}
Assert.Equal("clientSecret", result);
Assert.False(organization.Enabled);
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async void PurchaseOrganizationAsync_Paypal(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async void PurchaseOrganizationAsync_Paypal(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
{
var plan = StaticStore.Plans.First(p => p.Type == PlanType.EnterpriseAnnually);
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer
{
var plan = StaticStore.Plans.First(p => p.Type == PlanType.EnterpriseAnnually);
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer
{
Id = "C-1",
});
stripeAdapter.SubscriptionCreateAsync(default).ReturnsForAnyArgs(new Stripe.Subscription
{
Id = "S-1",
CurrentPeriodEnd = DateTime.Today.AddDays(10),
});
var customer = Substitute.For<Customer>();
customer.Id.ReturnsForAnyArgs("Braintree-Id");
customer.PaymentMethods.ReturnsForAnyArgs(new[] { Substitute.For<PaymentMethod>() });
var customerResult = Substitute.For<Result<Customer>>();
customerResult.IsSuccess().Returns(true);
customerResult.Target.ReturnsForAnyArgs(customer);
var braintreeGateway = sutProvider.GetDependency<IBraintreeGateway>();
braintreeGateway.Customer.CreateAsync(default).ReturnsForAnyArgs(customerResult);
var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.PayPal, paymentToken, plan, 0, 0, false, taxInfo);
Assert.Null(result);
Assert.Equal(GatewayType.Stripe, organization.Gateway);
Assert.Equal("C-1", organization.GatewayCustomerId);
Assert.Equal("S-1", organization.GatewaySubscriptionId);
Assert.True(organization.Enabled);
Assert.Equal(DateTime.Today.AddDays(10), organization.ExpirationDate);
await stripeAdapter.Received().CustomerCreateAsync(Arg.Is<Stripe.CustomerCreateOptions>(c =>
c.Description == organization.BusinessName &&
c.Email == organization.BillingEmail &&
c.PaymentMethod == null &&
c.Metadata.Count == 1 &&
c.Metadata["btCustomerId"] == "Braintree-Id" &&
c.InvoiceSettings.DefaultPaymentMethod == null &&
c.Address.Country == taxInfo.BillingAddressCountry &&
c.Address.PostalCode == taxInfo.BillingAddressPostalCode &&
c.Address.Line1 == taxInfo.BillingAddressLine1 &&
c.Address.Line2 == taxInfo.BillingAddressLine2 &&
c.Address.City == taxInfo.BillingAddressCity &&
c.Address.State == taxInfo.BillingAddressState &&
c.TaxIdData == null
));
await stripeAdapter.Received().SubscriptionCreateAsync(Arg.Is<Stripe.SubscriptionCreateOptions>(s =>
s.Customer == "C-1" &&
s.Expand[0] == "latest_invoice.payment_intent" &&
s.Metadata[organization.GatewayIdField()] == organization.Id.ToString() &&
s.Items.Count == 0
));
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async void PurchaseOrganizationAsync_Paypal_FailedCreate(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
Id = "C-1",
});
stripeAdapter.SubscriptionCreateAsync(default).ReturnsForAnyArgs(new Stripe.Subscription
{
var plan = StaticStore.Plans.First(p => p.Type == PlanType.EnterpriseAnnually);
Id = "S-1",
CurrentPeriodEnd = DateTime.Today.AddDays(10),
});
var customerResult = Substitute.For<Result<Customer>>();
customerResult.IsSuccess().Returns(false);
var customer = Substitute.For<Customer>();
customer.Id.ReturnsForAnyArgs("Braintree-Id");
customer.PaymentMethods.ReturnsForAnyArgs(new[] { Substitute.For<PaymentMethod>() });
var customerResult = Substitute.For<Result<Customer>>();
customerResult.IsSuccess().Returns(true);
customerResult.Target.ReturnsForAnyArgs(customer);
var braintreeGateway = sutProvider.GetDependency<IBraintreeGateway>();
braintreeGateway.Customer.CreateAsync(default).ReturnsForAnyArgs(customerResult);
var braintreeGateway = sutProvider.GetDependency<IBraintreeGateway>();
braintreeGateway.Customer.CreateAsync(default).ReturnsForAnyArgs(customerResult);
var exception = await Assert.ThrowsAsync<GatewayException>(
() => sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.PayPal, paymentToken, plan, 0, 0, false, taxInfo));
var result = await sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.PayPal, paymentToken, plan, 0, 0, false, taxInfo);
Assert.Equal("Failed to create PayPal customer record.", exception.Message);
}
Assert.Null(result);
Assert.Equal(GatewayType.Stripe, organization.Gateway);
Assert.Equal("C-1", organization.GatewayCustomerId);
Assert.Equal("S-1", organization.GatewaySubscriptionId);
Assert.True(organization.Enabled);
Assert.Equal(DateTime.Today.AddDays(10), organization.ExpirationDate);
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async void PurchaseOrganizationAsync_PayPal_Declined(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
await stripeAdapter.Received().CustomerCreateAsync(Arg.Is<Stripe.CustomerCreateOptions>(c =>
c.Description == organization.BusinessName &&
c.Email == organization.BillingEmail &&
c.PaymentMethod == null &&
c.Metadata.Count == 1 &&
c.Metadata["btCustomerId"] == "Braintree-Id" &&
c.InvoiceSettings.DefaultPaymentMethod == null &&
c.Address.Country == taxInfo.BillingAddressCountry &&
c.Address.PostalCode == taxInfo.BillingAddressPostalCode &&
c.Address.Line1 == taxInfo.BillingAddressLine1 &&
c.Address.Line2 == taxInfo.BillingAddressLine2 &&
c.Address.City == taxInfo.BillingAddressCity &&
c.Address.State == taxInfo.BillingAddressState &&
c.TaxIdData == null
));
await stripeAdapter.Received().SubscriptionCreateAsync(Arg.Is<Stripe.SubscriptionCreateOptions>(s =>
s.Customer == "C-1" &&
s.Expand[0] == "latest_invoice.payment_intent" &&
s.Metadata[organization.GatewayIdField()] == organization.Id.ToString() &&
s.Items.Count == 0
));
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async void PurchaseOrganizationAsync_Paypal_FailedCreate(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
{
var plan = StaticStore.Plans.First(p => p.Type == PlanType.EnterpriseAnnually);
var customerResult = Substitute.For<Result<Customer>>();
customerResult.IsSuccess().Returns(false);
var braintreeGateway = sutProvider.GetDependency<IBraintreeGateway>();
braintreeGateway.Customer.CreateAsync(default).ReturnsForAnyArgs(customerResult);
var exception = await Assert.ThrowsAsync<GatewayException>(
() => sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.PayPal, paymentToken, plan, 0, 0, false, taxInfo));
Assert.Equal("Failed to create PayPal customer record.", exception.Message);
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async void PurchaseOrganizationAsync_PayPal_Declined(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
{
var plan = StaticStore.Plans.First(p => p.Type == PlanType.EnterpriseAnnually);
paymentToken = "pm_" + paymentToken;
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer
{
var plan = StaticStore.Plans.First(p => p.Type == PlanType.EnterpriseAnnually);
paymentToken = "pm_" + paymentToken;
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer
Id = "C-1",
});
stripeAdapter.SubscriptionCreateAsync(default).ReturnsForAnyArgs(new Stripe.Subscription
{
Id = "S-1",
CurrentPeriodEnd = DateTime.Today.AddDays(10),
Status = "incomplete",
LatestInvoice = new Stripe.Invoice
{
Id = "C-1",
});
stripeAdapter.SubscriptionCreateAsync(default).ReturnsForAnyArgs(new Stripe.Subscription
{
Id = "S-1",
CurrentPeriodEnd = DateTime.Today.AddDays(10),
Status = "incomplete",
LatestInvoice = new Stripe.Invoice
PaymentIntent = new Stripe.PaymentIntent
{
PaymentIntent = new Stripe.PaymentIntent
{
Status = "requires_payment_method",
},
Status = "requires_payment_method",
},
});
},
});
var customer = Substitute.For<Customer>();
customer.Id.ReturnsForAnyArgs("Braintree-Id");
customer.PaymentMethods.ReturnsForAnyArgs(new[] { Substitute.For<PaymentMethod>() });
var customerResult = Substitute.For<Result<Customer>>();
customerResult.IsSuccess().Returns(true);
customerResult.Target.ReturnsForAnyArgs(customer);
var customer = Substitute.For<Customer>();
customer.Id.ReturnsForAnyArgs("Braintree-Id");
customer.PaymentMethods.ReturnsForAnyArgs(new[] { Substitute.For<PaymentMethod>() });
var customerResult = Substitute.For<Result<Customer>>();
customerResult.IsSuccess().Returns(true);
customerResult.Target.ReturnsForAnyArgs(customer);
var braintreeGateway = sutProvider.GetDependency<IBraintreeGateway>();
braintreeGateway.Customer.CreateAsync(default).ReturnsForAnyArgs(customerResult);
var braintreeGateway = sutProvider.GetDependency<IBraintreeGateway>();
braintreeGateway.Customer.CreateAsync(default).ReturnsForAnyArgs(customerResult);
var exception = await Assert.ThrowsAsync<GatewayException>(
() => sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.PayPal, paymentToken, plan, 0, 0, false, taxInfo));
var exception = await Assert.ThrowsAsync<GatewayException>(
() => sutProvider.Sut.PurchaseOrganizationAsync(organization, PaymentMethodType.PayPal, paymentToken, plan, 0, 0, false, taxInfo));
Assert.Equal("Payment method was declined.", exception.Message);
Assert.Equal("Payment method was declined.", exception.Message);
await stripeAdapter.Received(1).CustomerDeleteAsync("C-1");
await braintreeGateway.Customer.Received(1).DeleteAsync("Braintree-Id");
}
await stripeAdapter.Received(1).CustomerDeleteAsync("C-1");
await braintreeGateway.Customer.Received(1).DeleteAsync("Braintree-Id");
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async void UpgradeFreeOrganizationAsync_Success(SutProvider<StripePaymentService> sutProvider,
Organization organization, TaxInfo taxInfo)
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async void UpgradeFreeOrganizationAsync_Success(SutProvider<StripePaymentService> sutProvider,
Organization organization, TaxInfo taxInfo)
{
organization.GatewaySubscriptionId = null;
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
stripeAdapter.CustomerGetAsync(default).ReturnsForAnyArgs(new Stripe.Customer
{
organization.GatewaySubscriptionId = null;
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
stripeAdapter.CustomerGetAsync(default).ReturnsForAnyArgs(new Stripe.Customer
Id = "C-1",
Metadata = new Dictionary<string, string>
{
Id = "C-1",
Metadata = new Dictionary<string, string>
{
{ "btCustomerId", "B-123" },
}
});
stripeAdapter.InvoiceUpcomingAsync(default).ReturnsForAnyArgs(new Stripe.Invoice
{
PaymentIntent = new Stripe.PaymentIntent { Status = "requires_payment_method", },
AmountDue = 0
});
stripeAdapter.SubscriptionCreateAsync(default).ReturnsForAnyArgs(new Stripe.Subscription { });
{ "btCustomerId", "B-123" },
}
});
stripeAdapter.InvoiceUpcomingAsync(default).ReturnsForAnyArgs(new Stripe.Invoice
{
PaymentIntent = new Stripe.PaymentIntent { Status = "requires_payment_method", },
AmountDue = 0
});
stripeAdapter.SubscriptionCreateAsync(default).ReturnsForAnyArgs(new Stripe.Subscription { });
var plan = StaticStore.Plans.First(p => p.Type == PlanType.EnterpriseAnnually);
var result = await sutProvider.Sut.UpgradeFreeOrganizationAsync(organization, plan, 0, 0, false, taxInfo);
var plan = StaticStore.Plans.First(p => p.Type == PlanType.EnterpriseAnnually);
var result = await sutProvider.Sut.UpgradeFreeOrganizationAsync(organization, plan, 0, 0, false, taxInfo);
Assert.Null(result);
}
Assert.Null(result);
}
}

View File

@ -14,376 +14,375 @@ using NSubstitute;
using NSubstitute.ReceivedExtensions;
using Xunit;
namespace Bit.Core.Test.Services
namespace Bit.Core.Test.Services;
public class UserServiceTests
{
public class UserServiceTests
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task UpdateLicenseAsync_Success(SutProvider<UserService> sutProvider,
User user, UserLicense userLicense)
{
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task UpdateLicenseAsync_Success(SutProvider<UserService> sutProvider,
User user, UserLicense userLicense)
using var tempDir = new TempDirectory();
var now = DateTime.UtcNow;
userLicense.Issued = now.AddDays(-10);
userLicense.Expires = now.AddDays(10);
userLicense.Version = 1;
userLicense.Premium = true;
user.EmailVerified = true;
user.Email = userLicense.Email;
sutProvider.GetDependency<Settings.IGlobalSettings>().SelfHosted = true;
sutProvider.GetDependency<Settings.IGlobalSettings>().LicenseDirectory = tempDir.Directory;
sutProvider.GetDependency<ILicensingService>()
.VerifyLicense(userLicense)
.Returns(true);
await sutProvider.Sut.UpdateLicenseAsync(user, userLicense);
var filePath = Path.Combine(tempDir.Directory, "user", $"{user.Id}.json");
Assert.True(File.Exists(filePath));
var document = JsonDocument.Parse(File.OpenRead(filePath));
var root = document.RootElement;
Assert.Equal(JsonValueKind.Object, root.ValueKind);
// Sort of a lazy way to test that it is indented but not sure of a better way
Assert.Contains('\n', root.GetRawText());
AssertHelper.AssertJsonProperty(root, "LicenseKey", JsonValueKind.String);
AssertHelper.AssertJsonProperty(root, "Id", JsonValueKind.String);
AssertHelper.AssertJsonProperty(root, "Premium", JsonValueKind.True);
var versionProp = AssertHelper.AssertJsonProperty(root, "Version", JsonValueKind.Number);
Assert.Equal(1, versionProp.GetInt32());
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task SendTwoFactorEmailAsync_Success(SutProvider<UserService> sutProvider, User user)
{
var email = user.Email.ToLowerInvariant();
var token = "thisisatokentocompare";
var userTwoFactorTokenProvider = Substitute.For<IUserTwoFactorTokenProvider<User>>();
userTwoFactorTokenProvider
.CanGenerateTwoFactorTokenAsync(Arg.Any<UserManager<User>>(), user)
.Returns(Task.FromResult(true));
userTwoFactorTokenProvider
.GenerateAsync("2faEmail:" + email, Arg.Any<UserManager<User>>(), user)
.Returns(Task.FromResult(token));
sutProvider.Sut.RegisterTokenProvider("Email", userTwoFactorTokenProvider);
user.SetTwoFactorProviders(new Dictionary<TwoFactorProviderType, TwoFactorProvider>
{
using var tempDir = new TempDirectory();
var now = DateTime.UtcNow;
userLicense.Issued = now.AddDays(-10);
userLicense.Expires = now.AddDays(10);
userLicense.Version = 1;
userLicense.Premium = true;
user.EmailVerified = true;
user.Email = userLicense.Email;
sutProvider.GetDependency<Settings.IGlobalSettings>().SelfHosted = true;
sutProvider.GetDependency<Settings.IGlobalSettings>().LicenseDirectory = tempDir.Directory;
sutProvider.GetDependency<ILicensingService>()
.VerifyLicense(userLicense)
.Returns(true);
await sutProvider.Sut.UpdateLicenseAsync(user, userLicense);
var filePath = Path.Combine(tempDir.Directory, "user", $"{user.Id}.json");
Assert.True(File.Exists(filePath));
var document = JsonDocument.Parse(File.OpenRead(filePath));
var root = document.RootElement;
Assert.Equal(JsonValueKind.Object, root.ValueKind);
// Sort of a lazy way to test that it is indented but not sure of a better way
Assert.Contains('\n', root.GetRawText());
AssertHelper.AssertJsonProperty(root, "LicenseKey", JsonValueKind.String);
AssertHelper.AssertJsonProperty(root, "Id", JsonValueKind.String);
AssertHelper.AssertJsonProperty(root, "Premium", JsonValueKind.True);
var versionProp = AssertHelper.AssertJsonProperty(root, "Version", JsonValueKind.Number);
Assert.Equal(1, versionProp.GetInt32());
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task SendTwoFactorEmailAsync_Success(SutProvider<UserService> sutProvider, User user)
{
var email = user.Email.ToLowerInvariant();
var token = "thisisatokentocompare";
var userTwoFactorTokenProvider = Substitute.For<IUserTwoFactorTokenProvider<User>>();
userTwoFactorTokenProvider
.CanGenerateTwoFactorTokenAsync(Arg.Any<UserManager<User>>(), user)
.Returns(Task.FromResult(true));
userTwoFactorTokenProvider
.GenerateAsync("2faEmail:" + email, Arg.Any<UserManager<User>>(), user)
.Returns(Task.FromResult(token));
sutProvider.Sut.RegisterTokenProvider("Email", userTwoFactorTokenProvider);
user.SetTwoFactorProviders(new Dictionary<TwoFactorProviderType, TwoFactorProvider>
[TwoFactorProviderType.Email] = new TwoFactorProvider
{
[TwoFactorProviderType.Email] = new TwoFactorProvider
{
MetaData = new Dictionary<string, object> { ["Email"] = email },
Enabled = true
}
});
await sutProvider.Sut.SendTwoFactorEmailAsync(user);
MetaData = new Dictionary<string, object> { ["Email"] = email },
Enabled = true
}
});
await sutProvider.Sut.SendTwoFactorEmailAsync(user);
await sutProvider.GetDependency<IMailService>()
.Received(1)
.SendTwoFactorEmailAsync(email, token);
}
await sutProvider.GetDependency<IMailService>()
.Received(1)
.SendTwoFactorEmailAsync(email, token);
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task SendTwoFactorEmailBecauseNewDeviceLoginAsync_Success(SutProvider<UserService> sutProvider, User user)
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task SendTwoFactorEmailBecauseNewDeviceLoginAsync_Success(SutProvider<UserService> sutProvider, User user)
{
var email = user.Email.ToLowerInvariant();
var token = "thisisatokentocompare";
var userTwoFactorTokenProvider = Substitute.For<IUserTwoFactorTokenProvider<User>>();
userTwoFactorTokenProvider
.CanGenerateTwoFactorTokenAsync(Arg.Any<UserManager<User>>(), user)
.Returns(Task.FromResult(true));
userTwoFactorTokenProvider
.GenerateAsync("2faEmail:" + email, Arg.Any<UserManager<User>>(), user)
.Returns(Task.FromResult(token));
sutProvider.Sut.RegisterTokenProvider("Email", userTwoFactorTokenProvider);
user.SetTwoFactorProviders(new Dictionary<TwoFactorProviderType, TwoFactorProvider>
{
var email = user.Email.ToLowerInvariant();
var token = "thisisatokentocompare";
var userTwoFactorTokenProvider = Substitute.For<IUserTwoFactorTokenProvider<User>>();
userTwoFactorTokenProvider
.CanGenerateTwoFactorTokenAsync(Arg.Any<UserManager<User>>(), user)
.Returns(Task.FromResult(true));
userTwoFactorTokenProvider
.GenerateAsync("2faEmail:" + email, Arg.Any<UserManager<User>>(), user)
.Returns(Task.FromResult(token));
sutProvider.Sut.RegisterTokenProvider("Email", userTwoFactorTokenProvider);
user.SetTwoFactorProviders(new Dictionary<TwoFactorProviderType, TwoFactorProvider>
[TwoFactorProviderType.Email] = new TwoFactorProvider
{
[TwoFactorProviderType.Email] = new TwoFactorProvider
{
MetaData = new Dictionary<string, object> { ["Email"] = email },
Enabled = true
}
});
await sutProvider.Sut.SendTwoFactorEmailAsync(user, true);
MetaData = new Dictionary<string, object> { ["Email"] = email },
Enabled = true
}
});
await sutProvider.Sut.SendTwoFactorEmailAsync(user, true);
await sutProvider.GetDependency<IMailService>()
.Received(1)
.SendNewDeviceLoginTwoFactorEmailAsync(email, token);
}
await sutProvider.GetDependency<IMailService>()
.Received(1)
.SendNewDeviceLoginTwoFactorEmailAsync(email, token);
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task SendTwoFactorEmailAsync_ExceptionBecauseNoProviderOnUser(SutProvider<UserService> sutProvider, User user)
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task SendTwoFactorEmailAsync_ExceptionBecauseNoProviderOnUser(SutProvider<UserService> sutProvider, User user)
{
user.TwoFactorProviders = null;
await Assert.ThrowsAsync<ArgumentNullException>("No email.", () => sutProvider.Sut.SendTwoFactorEmailAsync(user));
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task SendTwoFactorEmailAsync_ExceptionBecauseNoProviderMetadataOnUser(SutProvider<UserService> sutProvider, User user)
{
user.SetTwoFactorProviders(new Dictionary<TwoFactorProviderType, TwoFactorProvider>
{
user.TwoFactorProviders = null;
await Assert.ThrowsAsync<ArgumentNullException>("No email.", () => sutProvider.Sut.SendTwoFactorEmailAsync(user));
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task SendTwoFactorEmailAsync_ExceptionBecauseNoProviderMetadataOnUser(SutProvider<UserService> sutProvider, User user)
{
user.SetTwoFactorProviders(new Dictionary<TwoFactorProviderType, TwoFactorProvider>
[TwoFactorProviderType.Email] = new TwoFactorProvider
{
[TwoFactorProviderType.Email] = new TwoFactorProvider
{
MetaData = null,
Enabled = true
}
});
MetaData = null,
Enabled = true
}
});
await Assert.ThrowsAsync<ArgumentNullException>("No email.", () => sutProvider.Sut.SendTwoFactorEmailAsync(user));
}
await Assert.ThrowsAsync<ArgumentNullException>("No email.", () => sutProvider.Sut.SendTwoFactorEmailAsync(user));
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task SendTwoFactorEmailAsync_ExceptionBecauseNoProviderEmailMetadataOnUser(SutProvider<UserService> sutProvider, User user)
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task SendTwoFactorEmailAsync_ExceptionBecauseNoProviderEmailMetadataOnUser(SutProvider<UserService> sutProvider, User user)
{
user.SetTwoFactorProviders(new Dictionary<TwoFactorProviderType, TwoFactorProvider>
{
user.SetTwoFactorProviders(new Dictionary<TwoFactorProviderType, TwoFactorProvider>
[TwoFactorProviderType.Email] = new TwoFactorProvider
{
[TwoFactorProviderType.Email] = new TwoFactorProvider
{
MetaData = new Dictionary<string, object> { ["qweqwe"] = user.Email.ToLowerInvariant() },
Enabled = true
}
});
MetaData = new Dictionary<string, object> { ["qweqwe"] = user.Email.ToLowerInvariant() },
Enabled = true
}
});
await Assert.ThrowsAsync<ArgumentNullException>("No email.", () => sutProvider.Sut.SendTwoFactorEmailAsync(user));
}
await Assert.ThrowsAsync<ArgumentNullException>("No email.", () => sutProvider.Sut.SendTwoFactorEmailAsync(user));
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task Needs2FABecauseNewDeviceAsync_ReturnsTrue(SutProvider<UserService> sutProvider, User user)
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task Needs2FABecauseNewDeviceAsync_ReturnsTrue(SutProvider<UserService> sutProvider, User user)
{
user.EmailVerified = true;
user.TwoFactorProviders = null;
user.UnknownDeviceVerificationEnabled = true;
const string deviceIdToCheck = "7b01b586-b210-499f-8d52-0c3fdaa646fc";
const string deviceIdInRepo = "ea29126c-91b7-4cc4-8ce6-00105b37f64a";
sutProvider.GetDependency<IDeviceRepository>()
.GetManyByUserIdAsync(user.Id)
.Returns(Task.FromResult<ICollection<Device>>(new List<Device>
{
new Device { Identifier = deviceIdInRepo }
}));
sutProvider.GetDependency<Settings.IGlobalSettings>().TwoFactorAuth.EmailOnNewDeviceLogin.Returns(true);
Assert.True(await sutProvider.Sut.Needs2FABecauseNewDeviceAsync(user, deviceIdToCheck, "password"));
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task Needs2FABecauseNewDeviceAsync_ReturnsFalse_When_GranType_Is_AuthorizationCode(SutProvider<UserService> sutProvider, User user)
{
user.EmailVerified = true;
user.TwoFactorProviders = null;
const string deviceIdToCheck = "7b01b586-b210-499f-8d52-0c3fdaa646fc";
const string deviceIdInRepo = "ea29126c-91b7-4cc4-8ce6-00105b37f64a";
sutProvider.GetDependency<IDeviceRepository>()
.GetManyByUserIdAsync(user.Id)
.Returns(Task.FromResult<ICollection<Device>>(new List<Device>
{
new Device { Identifier = deviceIdInRepo }
}));
Assert.False(await sutProvider.Sut.Needs2FABecauseNewDeviceAsync(user, deviceIdToCheck, "authorization_code"));
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task Needs2FABecauseNewDeviceAsync_ReturnsFalse_When_Email_Is_Not_Verified(SutProvider<UserService> sutProvider, User user)
{
user.EmailVerified = false;
user.TwoFactorProviders = null;
const string deviceIdToCheck = "7b01b586-b210-499f-8d52-0c3fdaa646fc";
const string deviceIdInRepo = "ea29126c-91b7-4cc4-8ce6-00105b37f64a";
sutProvider.GetDependency<IDeviceRepository>()
.GetManyByUserIdAsync(user.Id)
.Returns(Task.FromResult<ICollection<Device>>(new List<Device>
{
new Device { Identifier = deviceIdInRepo }
}));
Assert.False(await sutProvider.Sut.Needs2FABecauseNewDeviceAsync(user, deviceIdToCheck, "password"));
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task Needs2FABecauseNewDeviceAsync_ReturnsFalse_When_Is_The_First_Device(SutProvider<UserService> sutProvider, User user)
{
user.EmailVerified = true;
user.TwoFactorProviders = null;
const string deviceIdToCheck = "7b01b586-b210-499f-8d52-0c3fdaa646fc";
sutProvider.GetDependency<IDeviceRepository>()
.GetManyByUserIdAsync(user.Id)
.Returns(Task.FromResult<ICollection<Device>>(new List<Device>()));
Assert.False(await sutProvider.Sut.Needs2FABecauseNewDeviceAsync(user, deviceIdToCheck, "password"));
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task Needs2FABecauseNewDeviceAsync_ReturnsFalse_When_DeviceId_Is_Already_In_Repo(SutProvider<UserService> sutProvider, User user)
{
user.EmailVerified = true;
user.TwoFactorProviders = null;
const string deviceIdToCheck = "7b01b586-b210-499f-8d52-0c3fdaa646fc";
sutProvider.GetDependency<IDeviceRepository>()
.GetManyByUserIdAsync(user.Id)
.Returns(Task.FromResult<ICollection<Device>>(new List<Device>
{
new Device { Identifier = deviceIdToCheck }
}));
Assert.False(await sutProvider.Sut.Needs2FABecauseNewDeviceAsync(user, deviceIdToCheck, "password"));
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task Needs2FABecauseNewDeviceAsync_ReturnsFalse_When_GlobalSettings_2FA_EmailOnNewDeviceLogin_Is_Disabled(SutProvider<UserService> sutProvider, User user)
{
user.EmailVerified = true;
user.TwoFactorProviders = null;
const string deviceIdToCheck = "7b01b586-b210-499f-8d52-0c3fdaa646fc";
const string deviceIdInRepo = "ea29126c-91b7-4cc4-8ce6-00105b37f64a";
sutProvider.GetDependency<IDeviceRepository>()
.GetManyByUserIdAsync(user.Id)
.Returns(Task.FromResult<ICollection<Device>>(new List<Device>
{
new Device { Identifier = deviceIdInRepo }
}));
sutProvider.GetDependency<Settings.IGlobalSettings>().TwoFactorAuth.EmailOnNewDeviceLogin.Returns(false);
Assert.False(await sutProvider.Sut.Needs2FABecauseNewDeviceAsync(user, deviceIdToCheck, "password"));
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task Needs2FABecauseNewDeviceAsync_ReturnsFalse_When_UnknownDeviceVerification_Is_Disabled(SutProvider<UserService> sutProvider, User user)
{
user.EmailVerified = true;
user.TwoFactorProviders = null;
user.UnknownDeviceVerificationEnabled = false;
const string deviceIdToCheck = "7b01b586-b210-499f-8d52-0c3fdaa646fc";
const string deviceIdInRepo = "ea29126c-91b7-4cc4-8ce6-00105b37f64a";
sutProvider.GetDependency<IDeviceRepository>()
.GetManyByUserIdAsync(user.Id)
.Returns(Task.FromResult<ICollection<Device>>(new List<Device>
{
new Device { Identifier = deviceIdInRepo }
}));
sutProvider.GetDependency<Settings.IGlobalSettings>().TwoFactorAuth.EmailOnNewDeviceLogin.Returns(true);
Assert.False(await sutProvider.Sut.Needs2FABecauseNewDeviceAsync(user, deviceIdToCheck, "password"));
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public void CanEditDeviceVerificationSettings_ReturnsTrue(SutProvider<UserService> sutProvider, User user)
{
user.EmailVerified = true;
user.TwoFactorProviders = null;
sutProvider.GetDependency<Settings.IGlobalSettings>().TwoFactorAuth.EmailOnNewDeviceLogin.Returns(true);
Assert.True(sutProvider.Sut.CanEditDeviceVerificationSettings(user));
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public void CanEditDeviceVerificationSettings_ReturnsFalse_When_GlobalSettings_2FA_EmailOnNewDeviceLogin_Is_Disabled(SutProvider<UserService> sutProvider, User user)
{
user.EmailVerified = true;
user.TwoFactorProviders = null;
sutProvider.GetDependency<Settings.IGlobalSettings>().TwoFactorAuth.EmailOnNewDeviceLogin.Returns(false);
Assert.False(sutProvider.Sut.CanEditDeviceVerificationSettings(user));
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public void CanEditDeviceVerificationSettings_ReturnsFalse_When_Email_Is_Not_Verified(SutProvider<UserService> sutProvider, User user)
{
user.EmailVerified = false;
user.TwoFactorProviders = null;
sutProvider.GetDependency<Settings.IGlobalSettings>().TwoFactorAuth.EmailOnNewDeviceLogin.Returns(true);
Assert.False(sutProvider.Sut.CanEditDeviceVerificationSettings(user));
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public void CanEditDeviceVerificationSettings_ReturnsFalse_When_User_Uses_Key_Connector(SutProvider<UserService> sutProvider, User user)
{
user.EmailVerified = true;
user.TwoFactorProviders = null;
user.UsesKeyConnector = true;
sutProvider.GetDependency<Settings.IGlobalSettings>().TwoFactorAuth.EmailOnNewDeviceLogin.Returns(true);
Assert.False(sutProvider.Sut.CanEditDeviceVerificationSettings(user));
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public void CanEditDeviceVerificationSettings_ReturnsFalse_When_User_Has_A_2FA_Already_Set_Up(SutProvider<UserService> sutProvider, User user)
{
user.EmailVerified = true;
user.SetTwoFactorProviders(new Dictionary<TwoFactorProviderType, TwoFactorProvider>
{
user.EmailVerified = true;
user.TwoFactorProviders = null;
user.UnknownDeviceVerificationEnabled = true;
const string deviceIdToCheck = "7b01b586-b210-499f-8d52-0c3fdaa646fc";
const string deviceIdInRepo = "ea29126c-91b7-4cc4-8ce6-00105b37f64a";
sutProvider.GetDependency<IDeviceRepository>()
.GetManyByUserIdAsync(user.Id)
.Returns(Task.FromResult<ICollection<Device>>(new List<Device>
{
new Device { Identifier = deviceIdInRepo }
}));
sutProvider.GetDependency<Settings.IGlobalSettings>().TwoFactorAuth.EmailOnNewDeviceLogin.Returns(true);
Assert.True(await sutProvider.Sut.Needs2FABecauseNewDeviceAsync(user, deviceIdToCheck, "password"));
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task Needs2FABecauseNewDeviceAsync_ReturnsFalse_When_GranType_Is_AuthorizationCode(SutProvider<UserService> sutProvider, User user)
{
user.EmailVerified = true;
user.TwoFactorProviders = null;
const string deviceIdToCheck = "7b01b586-b210-499f-8d52-0c3fdaa646fc";
const string deviceIdInRepo = "ea29126c-91b7-4cc4-8ce6-00105b37f64a";
sutProvider.GetDependency<IDeviceRepository>()
.GetManyByUserIdAsync(user.Id)
.Returns(Task.FromResult<ICollection<Device>>(new List<Device>
{
new Device { Identifier = deviceIdInRepo }
}));
Assert.False(await sutProvider.Sut.Needs2FABecauseNewDeviceAsync(user, deviceIdToCheck, "authorization_code"));
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task Needs2FABecauseNewDeviceAsync_ReturnsFalse_When_Email_Is_Not_Verified(SutProvider<UserService> sutProvider, User user)
{
user.EmailVerified = false;
user.TwoFactorProviders = null;
const string deviceIdToCheck = "7b01b586-b210-499f-8d52-0c3fdaa646fc";
const string deviceIdInRepo = "ea29126c-91b7-4cc4-8ce6-00105b37f64a";
sutProvider.GetDependency<IDeviceRepository>()
.GetManyByUserIdAsync(user.Id)
.Returns(Task.FromResult<ICollection<Device>>(new List<Device>
{
new Device { Identifier = deviceIdInRepo }
}));
Assert.False(await sutProvider.Sut.Needs2FABecauseNewDeviceAsync(user, deviceIdToCheck, "password"));
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task Needs2FABecauseNewDeviceAsync_ReturnsFalse_When_Is_The_First_Device(SutProvider<UserService> sutProvider, User user)
{
user.EmailVerified = true;
user.TwoFactorProviders = null;
const string deviceIdToCheck = "7b01b586-b210-499f-8d52-0c3fdaa646fc";
sutProvider.GetDependency<IDeviceRepository>()
.GetManyByUserIdAsync(user.Id)
.Returns(Task.FromResult<ICollection<Device>>(new List<Device>()));
Assert.False(await sutProvider.Sut.Needs2FABecauseNewDeviceAsync(user, deviceIdToCheck, "password"));
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task Needs2FABecauseNewDeviceAsync_ReturnsFalse_When_DeviceId_Is_Already_In_Repo(SutProvider<UserService> sutProvider, User user)
{
user.EmailVerified = true;
user.TwoFactorProviders = null;
const string deviceIdToCheck = "7b01b586-b210-499f-8d52-0c3fdaa646fc";
sutProvider.GetDependency<IDeviceRepository>()
.GetManyByUserIdAsync(user.Id)
.Returns(Task.FromResult<ICollection<Device>>(new List<Device>
{
new Device { Identifier = deviceIdToCheck }
}));
Assert.False(await sutProvider.Sut.Needs2FABecauseNewDeviceAsync(user, deviceIdToCheck, "password"));
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task Needs2FABecauseNewDeviceAsync_ReturnsFalse_When_GlobalSettings_2FA_EmailOnNewDeviceLogin_Is_Disabled(SutProvider<UserService> sutProvider, User user)
{
user.EmailVerified = true;
user.TwoFactorProviders = null;
const string deviceIdToCheck = "7b01b586-b210-499f-8d52-0c3fdaa646fc";
const string deviceIdInRepo = "ea29126c-91b7-4cc4-8ce6-00105b37f64a";
sutProvider.GetDependency<IDeviceRepository>()
.GetManyByUserIdAsync(user.Id)
.Returns(Task.FromResult<ICollection<Device>>(new List<Device>
{
new Device { Identifier = deviceIdInRepo }
}));
sutProvider.GetDependency<Settings.IGlobalSettings>().TwoFactorAuth.EmailOnNewDeviceLogin.Returns(false);
Assert.False(await sutProvider.Sut.Needs2FABecauseNewDeviceAsync(user, deviceIdToCheck, "password"));
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async Task Needs2FABecauseNewDeviceAsync_ReturnsFalse_When_UnknownDeviceVerification_Is_Disabled(SutProvider<UserService> sutProvider, User user)
{
user.EmailVerified = true;
user.TwoFactorProviders = null;
user.UnknownDeviceVerificationEnabled = false;
const string deviceIdToCheck = "7b01b586-b210-499f-8d52-0c3fdaa646fc";
const string deviceIdInRepo = "ea29126c-91b7-4cc4-8ce6-00105b37f64a";
sutProvider.GetDependency<IDeviceRepository>()
.GetManyByUserIdAsync(user.Id)
.Returns(Task.FromResult<ICollection<Device>>(new List<Device>
{
new Device { Identifier = deviceIdInRepo }
}));
sutProvider.GetDependency<Settings.IGlobalSettings>().TwoFactorAuth.EmailOnNewDeviceLogin.Returns(true);
Assert.False(await sutProvider.Sut.Needs2FABecauseNewDeviceAsync(user, deviceIdToCheck, "password"));
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public void CanEditDeviceVerificationSettings_ReturnsTrue(SutProvider<UserService> sutProvider, User user)
{
user.EmailVerified = true;
user.TwoFactorProviders = null;
sutProvider.GetDependency<Settings.IGlobalSettings>().TwoFactorAuth.EmailOnNewDeviceLogin.Returns(true);
Assert.True(sutProvider.Sut.CanEditDeviceVerificationSettings(user));
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public void CanEditDeviceVerificationSettings_ReturnsFalse_When_GlobalSettings_2FA_EmailOnNewDeviceLogin_Is_Disabled(SutProvider<UserService> sutProvider, User user)
{
user.EmailVerified = true;
user.TwoFactorProviders = null;
sutProvider.GetDependency<Settings.IGlobalSettings>().TwoFactorAuth.EmailOnNewDeviceLogin.Returns(false);
Assert.False(sutProvider.Sut.CanEditDeviceVerificationSettings(user));
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public void CanEditDeviceVerificationSettings_ReturnsFalse_When_Email_Is_Not_Verified(SutProvider<UserService> sutProvider, User user)
{
user.EmailVerified = false;
user.TwoFactorProviders = null;
sutProvider.GetDependency<Settings.IGlobalSettings>().TwoFactorAuth.EmailOnNewDeviceLogin.Returns(true);
Assert.False(sutProvider.Sut.CanEditDeviceVerificationSettings(user));
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public void CanEditDeviceVerificationSettings_ReturnsFalse_When_User_Uses_Key_Connector(SutProvider<UserService> sutProvider, User user)
{
user.EmailVerified = true;
user.TwoFactorProviders = null;
user.UsesKeyConnector = true;
sutProvider.GetDependency<Settings.IGlobalSettings>().TwoFactorAuth.EmailOnNewDeviceLogin.Returns(true);
Assert.False(sutProvider.Sut.CanEditDeviceVerificationSettings(user));
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public void CanEditDeviceVerificationSettings_ReturnsFalse_When_User_Has_A_2FA_Already_Set_Up(SutProvider<UserService> sutProvider, User user)
{
user.EmailVerified = true;
user.SetTwoFactorProviders(new Dictionary<TwoFactorProviderType, TwoFactorProvider>
[TwoFactorProviderType.Email] = new TwoFactorProvider
{
[TwoFactorProviderType.Email] = new TwoFactorProvider
{
MetaData = new Dictionary<string, object> { ["Email"] = "asdfasf" },
Enabled = true
}
});
MetaData = new Dictionary<string, object> { ["Email"] = "asdfasf" },
Enabled = true
}
});
sutProvider.GetDependency<Settings.IGlobalSettings>().TwoFactorAuth.EmailOnNewDeviceLogin.Returns(true);
sutProvider.GetDependency<Settings.IGlobalSettings>().TwoFactorAuth.EmailOnNewDeviceLogin.Returns(true);
Assert.False(sutProvider.Sut.CanEditDeviceVerificationSettings(user));
}
Assert.False(sutProvider.Sut.CanEditDeviceVerificationSettings(user));
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async void HasPremiumFromOrganization_Returns_False_If_No_Orgs(SutProvider<UserService> sutProvider, User user)
{
sutProvider.GetDependency<IOrganizationUserRepository>().GetManyByUserAsync(user.Id).Returns(new List<OrganizationUser>());
Assert.False(await sutProvider.Sut.HasPremiumFromOrganization(user));
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async void HasPremiumFromOrganization_Returns_False_If_No_Orgs(SutProvider<UserService> sutProvider, User user)
{
sutProvider.GetDependency<IOrganizationUserRepository>().GetManyByUserAsync(user.Id).Returns(new List<OrganizationUser>());
Assert.False(await sutProvider.Sut.HasPremiumFromOrganization(user));
}
}
[Theory]
[InlineCustomAutoData(new[] { typeof(SutProviderCustomization) }, false, true)]
[InlineCustomAutoData(new[] { typeof(SutProviderCustomization) }, true, false)]
public async void HasPremiumFromOrganization_Returns_False_If_Org_Not_Eligible(bool orgEnabled, bool orgUsersGetPremium, SutProvider<UserService> sutProvider, User user, OrganizationUser orgUser, Organization organization)
{
orgUser.OrganizationId = organization.Id;
organization.Enabled = orgEnabled;
organization.UsersGetPremium = orgUsersGetPremium;
var orgAbilities = new Dictionary<Guid, OrganizationAbility>() { { organization.Id, new OrganizationAbility(organization) } };
[Theory]
[InlineCustomAutoData(new[] { typeof(SutProviderCustomization) }, false, true)]
[InlineCustomAutoData(new[] { typeof(SutProviderCustomization) }, true, false)]
public async void HasPremiumFromOrganization_Returns_False_If_Org_Not_Eligible(bool orgEnabled, bool orgUsersGetPremium, SutProvider<UserService> sutProvider, User user, OrganizationUser orgUser, Organization organization)
{
orgUser.OrganizationId = organization.Id;
organization.Enabled = orgEnabled;
organization.UsersGetPremium = orgUsersGetPremium;
var orgAbilities = new Dictionary<Guid, OrganizationAbility>() { { organization.Id, new OrganizationAbility(organization) } };
sutProvider.GetDependency<IOrganizationUserRepository>().GetManyByUserAsync(user.Id).Returns(new List<OrganizationUser>() { orgUser });
sutProvider.GetDependency<IApplicationCacheService>().GetOrganizationAbilitiesAsync().Returns(orgAbilities);
sutProvider.GetDependency<IOrganizationUserRepository>().GetManyByUserAsync(user.Id).Returns(new List<OrganizationUser>() { orgUser });
sutProvider.GetDependency<IApplicationCacheService>().GetOrganizationAbilitiesAsync().Returns(orgAbilities);
Assert.False(await sutProvider.Sut.HasPremiumFromOrganization(user));
}
Assert.False(await sutProvider.Sut.HasPremiumFromOrganization(user));
}
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async void HasPremiumFromOrganization_Returns_True_If_Org_Eligible(SutProvider<UserService> sutProvider, User user, OrganizationUser orgUser, Organization organization)
{
orgUser.OrganizationId = organization.Id;
organization.Enabled = true;
organization.UsersGetPremium = true;
var orgAbilities = new Dictionary<Guid, OrganizationAbility>() { { organization.Id, new OrganizationAbility(organization) } };
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
public async void HasPremiumFromOrganization_Returns_True_If_Org_Eligible(SutProvider<UserService> sutProvider, User user, OrganizationUser orgUser, Organization organization)
{
orgUser.OrganizationId = organization.Id;
organization.Enabled = true;
organization.UsersGetPremium = true;
var orgAbilities = new Dictionary<Guid, OrganizationAbility>() { { organization.Id, new OrganizationAbility(organization) } };
sutProvider.GetDependency<IOrganizationUserRepository>().GetManyByUserAsync(user.Id).Returns(new List<OrganizationUser>() { orgUser });
sutProvider.GetDependency<IApplicationCacheService>().GetOrganizationAbilitiesAsync().Returns(orgAbilities);
sutProvider.GetDependency<IOrganizationUserRepository>().GetManyByUserAsync(user.Id).Returns(new List<OrganizationUser>() { orgUser });
sutProvider.GetDependency<IApplicationCacheService>().GetOrganizationAbilitiesAsync().Returns(orgAbilities);
Assert.True(await sutProvider.Sut.HasPremiumFromOrganization(user));
}
Assert.True(await sutProvider.Sut.HasPremiumFromOrganization(user));
}
}

View File

@ -1,39 +1,38 @@
namespace Bit.Core.Test
namespace Bit.Core.Test;
public class TempDirectory : IDisposable
{
public class TempDirectory : IDisposable
public string Directory { get; private set; }
public TempDirectory()
{
public string Directory { get; private set; }
public TempDirectory()
{
Directory = Path.Combine(Path.GetTempPath(), $"bitwarden_{Guid.NewGuid().ToString().Replace("-", "")}");
}
public override string ToString() => Directory;
#region IDisposable implementation
~TempDirectory()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public void Dispose(bool disposing)
{
if (disposing)
{
try
{
System.IO.Directory.Delete(Directory, true);
}
catch { }
}
}
# endregion
Directory = Path.Combine(Path.GetTempPath(), $"bitwarden_{Guid.NewGuid().ToString().Replace("-", "")}");
}
public override string ToString() => Directory;
#region IDisposable implementation
~TempDirectory()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public void Dispose(bool disposing)
{
if (disposing)
{
try
{
System.IO.Directory.Delete(Directory, true);
}
catch { }
}
}
# endregion
}

View File

@ -7,122 +7,121 @@ using Bit.Test.Common.Helpers;
using Microsoft.AspNetCore.DataProtection;
using Xunit;
namespace Bit.Core.Test.Tokens
namespace Bit.Core.Test.Tokens;
[SutProviderCustomize]
public class DataProtectorTokenFactoryTests
{
[SutProviderCustomize]
public class DataProtectorTokenFactoryTests
public static SutProvider<DataProtectorTokenFactory<TestTokenable>> GetSutProvider()
{
public static SutProvider<DataProtectorTokenFactory<TestTokenable>> GetSutProvider()
{
var fixture = new Fixture();
return new SutProvider<DataProtectorTokenFactory<TestTokenable>>(fixture)
.SetDependency<IDataProtectionProvider>(fixture.Create<EphemeralDataProtectionProvider>())
.Create();
}
[Theory, BitAutoData]
public void CanRoundTripTokenables(TestTokenable tokenable)
{
var sutProvider = GetSutProvider();
var token = sutProvider.Sut.Protect(tokenable);
var recoveredTokenable = sutProvider.Sut.Unprotect(token);
AssertHelper.AssertPropertyEqual(tokenable, recoveredTokenable);
}
[Theory, BitAutoData]
public void PrependsClearText(TestTokenable tokenable)
{
var sutProvider = GetSutProvider();
var token = sutProvider.Sut.Protect(tokenable);
Assert.StartsWith(sutProvider.GetDependency<string>("clearTextPrefix"), token);
}
[Theory, BitAutoData]
public void EncryptsToken(TestTokenable tokenable)
{
var sutProvider = GetSutProvider();
var prefix = sutProvider.GetDependency<string>("clearTextPrefix");
var token = sutProvider.Sut.Protect(tokenable);
Assert.NotEqual(new Token(token).RemovePrefix(prefix), tokenable.ToToken());
}
[Theory, BitAutoData]
public void ThrowsIfUnprotectFails(TestTokenable tokenable)
{
var sutProvider = GetSutProvider();
var token = sutProvider.Sut.Protect(tokenable);
token += "stuff to make sure decryption fails";
Assert.Throws<CryptographicException>(() => sutProvider.Sut.Unprotect(token));
}
[Theory, BitAutoData]
public void TryUnprotect_FalseIfUnprotectFails(TestTokenable tokenable)
{
var sutProvider = GetSutProvider();
var token = sutProvider.Sut.Protect(tokenable) + "fail decryption";
var result = sutProvider.Sut.TryUnprotect(token, out var data);
Assert.False(result);
Assert.Null(data);
}
[Theory, BitAutoData]
public void TokenValid_FalseIfUnprotectFails(TestTokenable tokenable)
{
var sutProvider = GetSutProvider();
var token = sutProvider.Sut.Protect(tokenable) + "fail decryption";
var result = sutProvider.Sut.TokenValid(token);
Assert.False(result);
}
[Theory, BitAutoData]
public void TokenValid_FalseIfTokenInvalid(TestTokenable tokenable)
{
var sutProvider = GetSutProvider();
tokenable.ForceInvalid = true;
var token = sutProvider.Sut.Protect(tokenable);
var result = sutProvider.Sut.TokenValid(token);
Assert.False(result);
}
[Theory, BitAutoData]
public void TryUnprotect_TrueIfSuccess(TestTokenable tokenable)
{
var sutProvider = GetSutProvider();
var token = sutProvider.Sut.Protect(tokenable);
var result = sutProvider.Sut.TryUnprotect(token, out var data);
Assert.True(result);
AssertHelper.AssertPropertyEqual(tokenable, data);
}
[Theory, BitAutoData]
public void TokenValid_TrueIfSuccess(TestTokenable tokenable)
{
tokenable.ForceInvalid = false;
var sutProvider = GetSutProvider();
var token = sutProvider.Sut.Protect(tokenable);
var result = sutProvider.Sut.TokenValid(token);
Assert.True(result);
}
var fixture = new Fixture();
return new SutProvider<DataProtectorTokenFactory<TestTokenable>>(fixture)
.SetDependency<IDataProtectionProvider>(fixture.Create<EphemeralDataProtectionProvider>())
.Create();
}
[Theory, BitAutoData]
public void CanRoundTripTokenables(TestTokenable tokenable)
{
var sutProvider = GetSutProvider();
var token = sutProvider.Sut.Protect(tokenable);
var recoveredTokenable = sutProvider.Sut.Unprotect(token);
AssertHelper.AssertPropertyEqual(tokenable, recoveredTokenable);
}
[Theory, BitAutoData]
public void PrependsClearText(TestTokenable tokenable)
{
var sutProvider = GetSutProvider();
var token = sutProvider.Sut.Protect(tokenable);
Assert.StartsWith(sutProvider.GetDependency<string>("clearTextPrefix"), token);
}
[Theory, BitAutoData]
public void EncryptsToken(TestTokenable tokenable)
{
var sutProvider = GetSutProvider();
var prefix = sutProvider.GetDependency<string>("clearTextPrefix");
var token = sutProvider.Sut.Protect(tokenable);
Assert.NotEqual(new Token(token).RemovePrefix(prefix), tokenable.ToToken());
}
[Theory, BitAutoData]
public void ThrowsIfUnprotectFails(TestTokenable tokenable)
{
var sutProvider = GetSutProvider();
var token = sutProvider.Sut.Protect(tokenable);
token += "stuff to make sure decryption fails";
Assert.Throws<CryptographicException>(() => sutProvider.Sut.Unprotect(token));
}
[Theory, BitAutoData]
public void TryUnprotect_FalseIfUnprotectFails(TestTokenable tokenable)
{
var sutProvider = GetSutProvider();
var token = sutProvider.Sut.Protect(tokenable) + "fail decryption";
var result = sutProvider.Sut.TryUnprotect(token, out var data);
Assert.False(result);
Assert.Null(data);
}
[Theory, BitAutoData]
public void TokenValid_FalseIfUnprotectFails(TestTokenable tokenable)
{
var sutProvider = GetSutProvider();
var token = sutProvider.Sut.Protect(tokenable) + "fail decryption";
var result = sutProvider.Sut.TokenValid(token);
Assert.False(result);
}
[Theory, BitAutoData]
public void TokenValid_FalseIfTokenInvalid(TestTokenable tokenable)
{
var sutProvider = GetSutProvider();
tokenable.ForceInvalid = true;
var token = sutProvider.Sut.Protect(tokenable);
var result = sutProvider.Sut.TokenValid(token);
Assert.False(result);
}
[Theory, BitAutoData]
public void TryUnprotect_TrueIfSuccess(TestTokenable tokenable)
{
var sutProvider = GetSutProvider();
var token = sutProvider.Sut.Protect(tokenable);
var result = sutProvider.Sut.TryUnprotect(token, out var data);
Assert.True(result);
AssertHelper.AssertPropertyEqual(tokenable, data);
}
[Theory, BitAutoData]
public void TokenValid_TrueIfSuccess(TestTokenable tokenable)
{
tokenable.ForceInvalid = false;
var sutProvider = GetSutProvider();
var token = sutProvider.Sut.Protect(tokenable);
var result = sutProvider.Sut.TokenValid(token);
Assert.True(result);
}
}

View File

@ -3,69 +3,68 @@ using AutoFixture.Xunit2;
using Bit.Core.Utilities;
using Xunit;
namespace Bit.Core.Test.Tokens
namespace Bit.Core.Test.Tokens;
public class ExpiringTokenTests
{
public class ExpiringTokenTests
[Theory, AutoData]
public void ExpirationSerializesToEpochMilliseconds(DateTime expirationDate)
{
[Theory, AutoData]
public void ExpirationSerializesToEpochMilliseconds(DateTime expirationDate)
var sut = new TestExpiringTokenable
{
var sut = new TestExpiringTokenable
{
ExpirationDate = expirationDate
};
ExpirationDate = expirationDate
};
var result = JsonSerializer.Serialize(sut);
var expectedDate = CoreHelpers.ToEpocMilliseconds(expirationDate);
var result = JsonSerializer.Serialize(sut);
var expectedDate = CoreHelpers.ToEpocMilliseconds(expirationDate);
Assert.Contains($"\"ExpirationDate\":{expectedDate}", result);
}
Assert.Contains($"\"ExpirationDate\":{expectedDate}", result);
}
[Theory, AutoData]
public void ExpirationSerializationRoundTrip(DateTime expirationDate)
[Theory, AutoData]
public void ExpirationSerializationRoundTrip(DateTime expirationDate)
{
var sut = new TestExpiringTokenable
{
var sut = new TestExpiringTokenable
{
ExpirationDate = expirationDate
};
ExpirationDate = expirationDate
};
var intermediate = JsonSerializer.Serialize(sut);
var result = JsonSerializer.Deserialize<TestExpiringTokenable>(intermediate);
var intermediate = JsonSerializer.Serialize(sut);
var result = JsonSerializer.Deserialize<TestExpiringTokenable>(intermediate);
Assert.Equal(sut.ExpirationDate, result.ExpirationDate, TimeSpan.FromMilliseconds(100));
}
Assert.Equal(sut.ExpirationDate, result.ExpirationDate, TimeSpan.FromMilliseconds(100));
}
[Fact]
public void InvalidIfPastExpiryDate()
[Fact]
public void InvalidIfPastExpiryDate()
{
var sut = new TestExpiringTokenable
{
var sut = new TestExpiringTokenable
{
ExpirationDate = DateTime.UtcNow.AddHours(-1)
};
ExpirationDate = DateTime.UtcNow.AddHours(-1)
};
Assert.False(sut.Valid);
}
Assert.False(sut.Valid);
}
[Fact]
public void ValidIfWithinExpirationAndTokenReportsValid()
[Fact]
public void ValidIfWithinExpirationAndTokenReportsValid()
{
var sut = new TestExpiringTokenable
{
var sut = new TestExpiringTokenable
{
ExpirationDate = DateTime.UtcNow.AddHours(1)
};
ExpirationDate = DateTime.UtcNow.AddHours(1)
};
Assert.True(sut.Valid);
}
Assert.True(sut.Valid);
}
[Fact]
public void HonorsTokenIsValidAbstractMember()
[Fact]
public void HonorsTokenIsValidAbstractMember()
{
var sut = new TestExpiringTokenable(forceInvalid: true)
{
var sut = new TestExpiringTokenable(forceInvalid: true)
{
ExpirationDate = DateTime.UtcNow.AddHours(1)
};
ExpirationDate = DateTime.UtcNow.AddHours(1)
};
Assert.False(sut.Valid);
}
Assert.False(sut.Valid);
}
}

View File

@ -1,26 +1,25 @@
using System.Text.Json.Serialization;
using Bit.Core.Tokens;
namespace Bit.Core.Test.Tokens
namespace Bit.Core.Test.Tokens;
public class TestTokenable : Tokenable
{
public class TestTokenable : Tokenable
{
public bool ForceInvalid { get; set; } = false;
public bool ForceInvalid { get; set; } = false;
[JsonIgnore]
public override bool Valid => !ForceInvalid;
}
public class TestExpiringTokenable : ExpiringTokenable
{
private bool _forceInvalid;
public TestExpiringTokenable() : this(false) { }
public TestExpiringTokenable(bool forceInvalid)
{
_forceInvalid = forceInvalid;
}
protected override bool TokenIsValid() => !_forceInvalid;
}
[JsonIgnore]
public override bool Valid => !ForceInvalid;
}
public class TestExpiringTokenable : ExpiringTokenable
{
private bool _forceInvalid;
public TestExpiringTokenable() : this(false) { }
public TestExpiringTokenable(bool forceInvalid)
{
_forceInvalid = forceInvalid;
}
protected override bool TokenIsValid() => !_forceInvalid;
}

View File

@ -2,38 +2,37 @@
using Bit.Core.Tokens;
using Xunit;
namespace Bit.Core.Test.Tokens
namespace Bit.Core.Test.Tokens;
public class TokenTests
{
public class TokenTests
[Theory, AutoData]
public void InitializeWithString_ReturnsString(string initString)
{
[Theory, AutoData]
public void InitializeWithString_ReturnsString(string initString)
{
var token = new Token(initString);
var token = new Token(initString);
Assert.Equal(initString, token.ToString());
}
Assert.Equal(initString, token.ToString());
}
[Theory, AutoData]
public void AddsPrefix(Token token, string prefix)
{
Assert.Equal($"{prefix}{token.ToString()}", token.WithPrefix(prefix).ToString());
}
[Theory, AutoData]
public void AddsPrefix(Token token, string prefix)
{
Assert.Equal($"{prefix}{token.ToString()}", token.WithPrefix(prefix).ToString());
}
[Theory, AutoData]
public void RemovePrefix_WithPrefix_RemovesPrefix(string initString, string prefix)
{
var token = new Token(initString).WithPrefix(prefix);
[Theory, AutoData]
public void RemovePrefix_WithPrefix_RemovesPrefix(string initString, string prefix)
{
var token = new Token(initString).WithPrefix(prefix);
Assert.Equal(initString, token.RemovePrefix(prefix).ToString());
}
Assert.Equal(initString, token.RemovePrefix(prefix).ToString());
}
[Theory, AutoData]
public void RemovePrefix_WithoutPrefix_Throws(Token token, string prefix)
{
var exception = Assert.Throws<BadTokenException>(() => token.RemovePrefix(prefix));
[Theory, AutoData]
public void RemovePrefix_WithoutPrefix_Throws(Token token, string prefix)
{
var exception = Assert.Throws<BadTokenException>(() => token.RemovePrefix(prefix));
Assert.Equal($"Expected prefix, {prefix}, was not present.", exception.Message);
}
Assert.Equal($"Expected prefix, {prefix}, was not present.", exception.Message);
}
}

View File

@ -2,36 +2,35 @@
using Bit.Core.Utilities;
using Xunit;
namespace Bit.Core.Test.Utilities
namespace Bit.Core.Test.Utilities;
public class ClaimsExtensionsTests
{
public class ClaimsExtensionsTests
[Fact]
public void HasSSOIdP_Returns_True_When_The_Claims_Has_One_Of_Type_IdP_And_Value_Sso()
{
[Fact]
public void HasSSOIdP_Returns_True_When_The_Claims_Has_One_Of_Type_IdP_And_Value_Sso()
{
var claims = new List<Claim> { new Claim("idp", "sso") };
Assert.True(claims.HasSsoIdP());
}
var claims = new List<Claim> { new Claim("idp", "sso") };
Assert.True(claims.HasSsoIdP());
}
[Fact]
public void HasSSOIdP_Returns_False_When_The_Claims_Has_One_Of_Type_IdP_And_Value_Is_Not_Sso()
{
var claims = new List<Claim> { new Claim("idp", "asdfasfd") };
Assert.False(claims.HasSsoIdP());
}
[Fact]
public void HasSSOIdP_Returns_False_When_The_Claims_Has_One_Of_Type_IdP_And_Value_Is_Not_Sso()
{
var claims = new List<Claim> { new Claim("idp", "asdfasfd") };
Assert.False(claims.HasSsoIdP());
}
[Fact]
public void HasSSOIdP_Returns_False_When_The_Claims_Has_No_One_Of_Type_IdP()
{
var claims = new List<Claim> { new Claim("qweqweq", "sso") };
Assert.False(claims.HasSsoIdP());
}
[Fact]
public void HasSSOIdP_Returns_False_When_The_Claims_Has_No_One_Of_Type_IdP()
{
var claims = new List<Claim> { new Claim("qweqweq", "sso") };
Assert.False(claims.HasSsoIdP());
}
[Fact]
public void HasSSOIdP_Returns_False_When_The_Claims_Are_Empty()
{
var claims = new List<Claim>();
Assert.False(claims.HasSsoIdP());
}
[Fact]
public void HasSSOIdP_Returns_False_When_The_Claims_Are_Empty()
{
var claims = new List<Claim>();
Assert.False(claims.HasSsoIdP());
}
}

View File

@ -12,434 +12,433 @@ using IdentityModel;
using Microsoft.AspNetCore.DataProtection;
using Xunit;
namespace Bit.Core.Test.Utilities
namespace Bit.Core.Test.Utilities;
public class CoreHelpersTests
{
public class CoreHelpersTests
public static IEnumerable<object[]> _epochTestCases = new[]
{
public static IEnumerable<object[]> _epochTestCases = new[]
{
new object[] {new DateTime(2020, 12, 30, 11, 49, 12, DateTimeKind.Utc), 1609328952000L},
};
new object[] {new DateTime(2020, 12, 30, 11, 49, 12, DateTimeKind.Utc), 1609328952000L},
};
[Fact]
public void GenerateComb_Success()
{
// Arrange & Act
var comb = CoreHelpers.GenerateComb();
[Fact]
public void GenerateComb_Success()
{
// Arrange & Act
var comb = CoreHelpers.GenerateComb();
// Assert
Assert.NotEqual(Guid.Empty, comb);
// TODO: Add more asserts to make sure important aspects of
// the comb are working properly
// Assert
Assert.NotEqual(Guid.Empty, comb);
// TODO: Add more asserts to make sure important aspects of
// the comb are working properly
}
public static IEnumerable<object[]> GenerateCombCases = new[]
{
new object[]
{
Guid.Parse("a58db474-43d8-42f1-b4ee-0c17647cd0c0"), // Input Guid
new DateTime(2022, 3, 12, 12, 12, 0, DateTimeKind.Utc), // Input Time
Guid.Parse("a58db474-43d8-42f1-b4ee-ae5600c90cc1"), // Expected Comb
},
new object[]
{
Guid.Parse("f776e6ee-511f-4352-bb28-88513002bdeb"),
new DateTime(2021, 5, 10, 10, 52, 0, DateTimeKind.Utc),
Guid.Parse("f776e6ee-511f-4352-bb28-ad2400b313c1"),
},
new object[]
{
Guid.Parse("51a25fc7-3cad-497d-8e2f-8d77011648a1"),
new DateTime(1999, 2, 26, 16, 53, 13, DateTimeKind.Utc),
Guid.Parse("51a25fc7-3cad-497d-8e2f-8d77011649cd"),
},
new object[]
{
Guid.Parse("bfb8f353-3b32-4a9e-bef6-24fe0b54bfb0"),
new DateTime(2024, 10, 20, 1, 32, 16, DateTimeKind.Utc),
Guid.Parse("bfb8f353-3b32-4a9e-bef6-b20f00195780"),
}
};
[Theory]
[MemberData(nameof(GenerateCombCases))]
public void GenerateComb_WithInputs_Success(Guid inputGuid, DateTime inputTime, Guid expectedComb)
{
var comb = CoreHelpers.GenerateComb(inputGuid, inputTime);
Assert.Equal(expectedComb, comb);
}
[Theory]
[InlineData(2, 5, new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 })]
[InlineData(2, 3, new[] { 1, 2, 3, 4, 5 })]
[InlineData(2, 1, new[] { 1, 2 })]
[InlineData(1, 1, new[] { 1 })]
[InlineData(2, 2, new[] { 1, 2, 3 })]
public void Batch_Success(int batchSize, int totalBatches, int[] collection)
{
// Arrange
var remainder = collection.Length % batchSize;
// Act
var batches = collection.Batch(batchSize);
// Assert
Assert.Equal(totalBatches, batches.Count());
foreach (var batch in batches.Take(totalBatches - 1))
{
Assert.Equal(batchSize, batch.Count());
}
public static IEnumerable<object[]> GenerateCombCases = new[]
{
new object[]
Assert.Equal(batches.Last().Count(), remainder == 0 ? batchSize : remainder);
}
/*
[Fact]
public void ToGuidIdArrayTVP_Success()
{
// Arrange
var item0 = Guid.NewGuid();
var item1 = Guid.NewGuid();
var ids = new[] { item0, item1 };
// Act
var dt = ids.ToGuidIdArrayTVP();
// Assert
Assert.Single(dt.Columns);
Assert.Equal("GuidId", dt.Columns[0].ColumnName);
Assert.Equal(2, dt.Rows.Count);
Assert.Equal(item0, dt.Rows[0][0]);
Assert.Equal(item1, dt.Rows[1][0]);
}
*/
// TODO: Test the other ToArrayTVP Methods
[Theory]
[InlineData("12345&6789", "123456789")]
[InlineData("abcdef", "ABCDEF")]
[InlineData("1!@#$%&*()_+", "1")]
[InlineData("\u00C6123abc\u00C7", "123ABC")]
[InlineData("123\u00C6ABC", "123ABC")]
[InlineData("\r\nHello", "E")]
[InlineData("\tdef", "DEF")]
[InlineData("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUV1234567890", "ABCDEFABCDEF1234567890")]
public void CleanCertificateThumbprint_Success(string input, string output)
{
// Arrange & Act
var sanitizedInput = CoreHelpers.CleanCertificateThumbprint(input);
// Assert
Assert.Equal(output, sanitizedInput);
}
// TODO: Add more tests
[Theory]
[MemberData(nameof(_epochTestCases))]
public void ToEpocMilliseconds_Success(DateTime date, long milliseconds)
{
// Act & Assert
Assert.Equal(milliseconds, CoreHelpers.ToEpocMilliseconds(date));
}
[Theory]
[MemberData(nameof(_epochTestCases))]
public void FromEpocMilliseconds(DateTime date, long milliseconds)
{
// Act & Assert
Assert.Equal(date, CoreHelpers.FromEpocMilliseconds(milliseconds));
}
[Fact]
public void SecureRandomString_Success()
{
// Arrange & Act
var @string = CoreHelpers.SecureRandomString(8);
// Assert
// TODO: Should probably add more Asserts down the line
Assert.Equal(8, @string.Length);
}
[Theory]
[InlineData(1, "1 Bytes")]
[InlineData(-5L, "-5 Bytes")]
[InlineData(1023L, "1023 Bytes")]
[InlineData(1024L, "1 KB")]
[InlineData(1025L, "1 KB")]
[InlineData(-1023L, "-1023 Bytes")]
[InlineData(-1024L, "-1 KB")]
[InlineData(-1025L, "-1 KB")]
[InlineData(1048575L, "1024 KB")]
[InlineData(1048576L, "1 MB")]
[InlineData(1048577L, "1 MB")]
[InlineData(-1048575L, "-1024 KB")]
[InlineData(-1048576L, "-1 MB")]
[InlineData(-1048577L, "-1 MB")]
[InlineData(1073741823L, "1024 MB")]
[InlineData(1073741824L, "1 GB")]
[InlineData(1073741825L, "1 GB")]
[InlineData(-1073741823L, "-1024 MB")]
[InlineData(-1073741824L, "-1 GB")]
[InlineData(-1073741825L, "-1 GB")]
[InlineData(long.MaxValue, "8589934592 GB")]
public void ReadableBytesSize_Success(long size, string readable)
{
// Act & Assert
Assert.Equal(readable, CoreHelpers.ReadableBytesSize(size));
}
[Fact]
public void CloneObject_Success()
{
var original = new { Message = "Message" };
var copy = CoreHelpers.CloneObject(original);
Assert.Equal(original.Message, copy.Message);
}
[Fact]
public void ExtendQuery_AddNewParameter_Success()
{
// Arrange
var uri = new Uri("https://bitwarden.com/?param1=value1");
// Act
var newUri = CoreHelpers.ExtendQuery(uri,
new Dictionary<string, string> { { "param2", "value2" } });
// Assert
Assert.Equal("https://bitwarden.com/?param1=value1&param2=value2", newUri.ToString());
}
[Fact]
public void ExtendQuery_AddTwoNewParameters_Success()
{
// Arrange
var uri = new Uri("https://bitwarden.com/?param1=value1");
// Act
var newUri = CoreHelpers.ExtendQuery(uri,
new Dictionary<string, string>
{
Guid.Parse("a58db474-43d8-42f1-b4ee-0c17647cd0c0"), // Input Guid
new DateTime(2022, 3, 12, 12, 12, 0, DateTimeKind.Utc), // Input Time
Guid.Parse("a58db474-43d8-42f1-b4ee-ae5600c90cc1"), // Expected Comb
},
new object[]
{
Guid.Parse("f776e6ee-511f-4352-bb28-88513002bdeb"),
new DateTime(2021, 5, 10, 10, 52, 0, DateTimeKind.Utc),
Guid.Parse("f776e6ee-511f-4352-bb28-ad2400b313c1"),
},
new object[]
{
Guid.Parse("51a25fc7-3cad-497d-8e2f-8d77011648a1"),
new DateTime(1999, 2, 26, 16, 53, 13, DateTimeKind.Utc),
Guid.Parse("51a25fc7-3cad-497d-8e2f-8d77011649cd"),
},
new object[]
{
Guid.Parse("bfb8f353-3b32-4a9e-bef6-24fe0b54bfb0"),
new DateTime(2024, 10, 20, 1, 32, 16, DateTimeKind.Utc),
Guid.Parse("bfb8f353-3b32-4a9e-bef6-b20f00195780"),
}
};
{ "param2", "value2" },
{ "param3", "value3" }
});
[Theory]
[MemberData(nameof(GenerateCombCases))]
public void GenerateComb_WithInputs_Success(Guid inputGuid, DateTime inputTime, Guid expectedComb)
// Assert
Assert.Equal("https://bitwarden.com/?param1=value1&param2=value2&param3=value3", newUri.ToString());
}
[Fact]
public void ExtendQuery_AddExistingParameter_Success()
{
// Arrange
var uri = new Uri("https://bitwarden.com/?param1=value1&param2=value2");
// Act
var newUri = CoreHelpers.ExtendQuery(uri,
new Dictionary<string, string> { { "param1", "test_value" } });
// Assert
Assert.Equal("https://bitwarden.com/?param1=test_value&param2=value2", newUri.ToString());
}
[Fact]
public void ExtendQuery_AddNoParameters_Success()
{
// Arrange
const string startingUri = "https://bitwarden.com/?param1=value1";
var uri = new Uri(startingUri);
// Act
var newUri = CoreHelpers.ExtendQuery(uri, new Dictionary<string, string>());
// Assert
Assert.Equal(startingUri, newUri.ToString());
}
[Theory]
[InlineData("bücher.com", "xn--bcher-kva.com")]
[InlineData("bücher.cömé", "xn--bcher-kva.xn--cm-cja4c")]
[InlineData("hello@bücher.com", "hello@xn--bcher-kva.com")]
[InlineData("hello@world.cömé", "hello@world.xn--cm-cja4c")]
[InlineData("hello@bücher.cömé", "hello@xn--bcher-kva.xn--cm-cja4c")]
[InlineData("ascii.com", "ascii.com")]
[InlineData("", "")]
[InlineData(null, null)]
public void PunyEncode_Success(string text, string expected)
{
var actual = CoreHelpers.PunyEncode(text);
Assert.Equal(expected, actual);
}
[Fact]
public void GetEmbeddedResourceContentsAsync_Success()
{
var fileContents = CoreHelpers.GetEmbeddedResourceContentsAsync("data.embeddedResource.txt");
Assert.Equal("Contents of embeddedResource.txt\n", fileContents.Replace("\r\n", "\n"));
}
[Theory, CustomAutoData(typeof(UserFixture))]
public void BuildIdentityClaims_BaseClaims_Success(User user, bool isPremium)
{
var expected = new Dictionary<string, string>
{
var comb = CoreHelpers.GenerateComb(inputGuid, inputTime);
{ "premium", isPremium ? "true" : "false" },
{ JwtClaimTypes.Email, user.Email },
{ JwtClaimTypes.EmailVerified, user.EmailVerified ? "true" : "false" },
{ JwtClaimTypes.Name, user.Name },
{ "sstamp", user.SecurityStamp },
}.ToList();
Assert.Equal(expectedComb, comb);
var actual = CoreHelpers.BuildIdentityClaims(user, Array.Empty<CurrentContentOrganization>(),
Array.Empty<CurrentContentProvider>(), isPremium);
foreach (var claim in expected)
{
Assert.Contains(claim, actual);
}
Assert.Equal(expected.Count, actual.Count);
}
[Theory]
[InlineData(2, 5, new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 })]
[InlineData(2, 3, new[] { 1, 2, 3, 4, 5 })]
[InlineData(2, 1, new[] { 1, 2 })]
[InlineData(1, 1, new[] { 1 })]
[InlineData(2, 2, new[] { 1, 2, 3 })]
public void Batch_Success(int batchSize, int totalBatches, int[] collection)
[Theory, CustomAutoData(typeof(UserFixture))]
public void BuildIdentityClaims_NonCustomOrganizationUserType_Success(User user)
{
var fixture = new Fixture().WithAutoNSubstitutions();
foreach (var organizationUserType in Enum.GetValues<OrganizationUserType>().Except(new[] { OrganizationUserType.Custom }))
{
// Arrange
var remainder = collection.Length % batchSize;
// Act
var batches = collection.Batch(batchSize);
// Assert
Assert.Equal(totalBatches, batches.Count());
foreach (var batch in batches.Take(totalBatches - 1))
{
Assert.Equal(batchSize, batch.Count());
}
Assert.Equal(batches.Last().Count(), remainder == 0 ? batchSize : remainder);
}
/*
[Fact]
public void ToGuidIdArrayTVP_Success()
{
// Arrange
var item0 = Guid.NewGuid();
var item1 = Guid.NewGuid();
var ids = new[] { item0, item1 };
// Act
var dt = ids.ToGuidIdArrayTVP();
// Assert
Assert.Single(dt.Columns);
Assert.Equal("GuidId", dt.Columns[0].ColumnName);
Assert.Equal(2, dt.Rows.Count);
Assert.Equal(item0, dt.Rows[0][0]);
Assert.Equal(item1, dt.Rows[1][0]);
}
*/
// TODO: Test the other ToArrayTVP Methods
[Theory]
[InlineData("12345&6789", "123456789")]
[InlineData("abcdef", "ABCDEF")]
[InlineData("1!@#$%&*()_+", "1")]
[InlineData("\u00C6123abc\u00C7", "123ABC")]
[InlineData("123\u00C6ABC", "123ABC")]
[InlineData("\r\nHello", "E")]
[InlineData("\tdef", "DEF")]
[InlineData("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUV1234567890", "ABCDEFABCDEF1234567890")]
public void CleanCertificateThumbprint_Success(string input, string output)
{
// Arrange & Act
var sanitizedInput = CoreHelpers.CleanCertificateThumbprint(input);
// Assert
Assert.Equal(output, sanitizedInput);
}
// TODO: Add more tests
[Theory]
[MemberData(nameof(_epochTestCases))]
public void ToEpocMilliseconds_Success(DateTime date, long milliseconds)
{
// Act & Assert
Assert.Equal(milliseconds, CoreHelpers.ToEpocMilliseconds(date));
}
[Theory]
[MemberData(nameof(_epochTestCases))]
public void FromEpocMilliseconds(DateTime date, long milliseconds)
{
// Act & Assert
Assert.Equal(date, CoreHelpers.FromEpocMilliseconds(milliseconds));
}
[Fact]
public void SecureRandomString_Success()
{
// Arrange & Act
var @string = CoreHelpers.SecureRandomString(8);
// Assert
// TODO: Should probably add more Asserts down the line
Assert.Equal(8, @string.Length);
}
[Theory]
[InlineData(1, "1 Bytes")]
[InlineData(-5L, "-5 Bytes")]
[InlineData(1023L, "1023 Bytes")]
[InlineData(1024L, "1 KB")]
[InlineData(1025L, "1 KB")]
[InlineData(-1023L, "-1023 Bytes")]
[InlineData(-1024L, "-1 KB")]
[InlineData(-1025L, "-1 KB")]
[InlineData(1048575L, "1024 KB")]
[InlineData(1048576L, "1 MB")]
[InlineData(1048577L, "1 MB")]
[InlineData(-1048575L, "-1024 KB")]
[InlineData(-1048576L, "-1 MB")]
[InlineData(-1048577L, "-1 MB")]
[InlineData(1073741823L, "1024 MB")]
[InlineData(1073741824L, "1 GB")]
[InlineData(1073741825L, "1 GB")]
[InlineData(-1073741823L, "-1024 MB")]
[InlineData(-1073741824L, "-1 GB")]
[InlineData(-1073741825L, "-1 GB")]
[InlineData(long.MaxValue, "8589934592 GB")]
public void ReadableBytesSize_Success(long size, string readable)
{
// Act & Assert
Assert.Equal(readable, CoreHelpers.ReadableBytesSize(size));
}
[Fact]
public void CloneObject_Success()
{
var original = new { Message = "Message" };
var copy = CoreHelpers.CloneObject(original);
Assert.Equal(original.Message, copy.Message);
}
[Fact]
public void ExtendQuery_AddNewParameter_Success()
{
// Arrange
var uri = new Uri("https://bitwarden.com/?param1=value1");
// Act
var newUri = CoreHelpers.ExtendQuery(uri,
new Dictionary<string, string> { { "param2", "value2" } });
// Assert
Assert.Equal("https://bitwarden.com/?param1=value1&param2=value2", newUri.ToString());
}
[Fact]
public void ExtendQuery_AddTwoNewParameters_Success()
{
// Arrange
var uri = new Uri("https://bitwarden.com/?param1=value1");
// Act
var newUri = CoreHelpers.ExtendQuery(uri,
new Dictionary<string, string>
{
{ "param2", "value2" },
{ "param3", "value3" }
});
// Assert
Assert.Equal("https://bitwarden.com/?param1=value1&param2=value2&param3=value3", newUri.ToString());
}
[Fact]
public void ExtendQuery_AddExistingParameter_Success()
{
// Arrange
var uri = new Uri("https://bitwarden.com/?param1=value1&param2=value2");
// Act
var newUri = CoreHelpers.ExtendQuery(uri,
new Dictionary<string, string> { { "param1", "test_value" } });
// Assert
Assert.Equal("https://bitwarden.com/?param1=test_value&param2=value2", newUri.ToString());
}
[Fact]
public void ExtendQuery_AddNoParameters_Success()
{
// Arrange
const string startingUri = "https://bitwarden.com/?param1=value1";
var uri = new Uri(startingUri);
// Act
var newUri = CoreHelpers.ExtendQuery(uri, new Dictionary<string, string>());
// Assert
Assert.Equal(startingUri, newUri.ToString());
}
[Theory]
[InlineData("bücher.com", "xn--bcher-kva.com")]
[InlineData("bücher.cömé", "xn--bcher-kva.xn--cm-cja4c")]
[InlineData("hello@bücher.com", "hello@xn--bcher-kva.com")]
[InlineData("hello@world.cömé", "hello@world.xn--cm-cja4c")]
[InlineData("hello@bücher.cömé", "hello@xn--bcher-kva.xn--cm-cja4c")]
[InlineData("ascii.com", "ascii.com")]
[InlineData("", "")]
[InlineData(null, null)]
public void PunyEncode_Success(string text, string expected)
{
var actual = CoreHelpers.PunyEncode(text);
Assert.Equal(expected, actual);
}
[Fact]
public void GetEmbeddedResourceContentsAsync_Success()
{
var fileContents = CoreHelpers.GetEmbeddedResourceContentsAsync("data.embeddedResource.txt");
Assert.Equal("Contents of embeddedResource.txt\n", fileContents.Replace("\r\n", "\n"));
}
[Theory, CustomAutoData(typeof(UserFixture))]
public void BuildIdentityClaims_BaseClaims_Success(User user, bool isPremium)
{
var expected = new Dictionary<string, string>
{
{ "premium", isPremium ? "true" : "false" },
{ JwtClaimTypes.Email, user.Email },
{ JwtClaimTypes.EmailVerified, user.EmailVerified ? "true" : "false" },
{ JwtClaimTypes.Name, user.Name },
{ "sstamp", user.SecurityStamp },
}.ToList();
var actual = CoreHelpers.BuildIdentityClaims(user, Array.Empty<CurrentContentOrganization>(),
Array.Empty<CurrentContentProvider>(), isPremium);
foreach (var claim in expected)
{
Assert.Contains(claim, actual);
}
Assert.Equal(expected.Count, actual.Count);
}
[Theory, CustomAutoData(typeof(UserFixture))]
public void BuildIdentityClaims_NonCustomOrganizationUserType_Success(User user)
{
var fixture = new Fixture().WithAutoNSubstitutions();
foreach (var organizationUserType in Enum.GetValues<OrganizationUserType>().Except(new[] { OrganizationUserType.Custom }))
{
var org = fixture.Create<CurrentContentOrganization>();
org.Type = organizationUserType;
var expected = new KeyValuePair<string, string>($"org{organizationUserType.ToString().ToLower()}", org.Id.ToString());
var actual = CoreHelpers.BuildIdentityClaims(user, new[] { org }, Array.Empty<CurrentContentProvider>(), false);
Assert.Contains(expected, actual);
}
}
[Theory, CustomAutoData(typeof(UserFixture))]
public void BuildIdentityClaims_CustomOrganizationUserClaims_Success(User user, CurrentContentOrganization org)
{
var fixture = new Fixture().WithAutoNSubstitutions();
org.Type = OrganizationUserType.Custom;
var org = fixture.Create<CurrentContentOrganization>();
org.Type = organizationUserType;
var expected = new KeyValuePair<string, string>($"org{organizationUserType.ToString().ToLower()}", org.Id.ToString());
var actual = CoreHelpers.BuildIdentityClaims(user, new[] { org }, Array.Empty<CurrentContentProvider>(), false);
foreach (var (permitted, claimName) in org.Permissions.ClaimsMap)
{
var claim = new KeyValuePair<string, string>(claimName, org.Id.ToString());
if (permitted)
{
Assert.Contains(claim, actual);
}
else
{
Assert.DoesNotContain(claim, actual);
}
}
}
[Theory, CustomAutoData(typeof(UserFixture))]
public void BuildIdentityClaims_ProviderClaims_Success(User user)
{
var fixture = new Fixture().WithAutoNSubstitutions();
var providers = new List<CurrentContentProvider>();
foreach (var providerUserType in Enum.GetValues<ProviderUserType>())
{
var provider = fixture.Create<CurrentContentProvider>();
provider.Type = providerUserType;
providers.Add(provider);
}
var claims = new List<KeyValuePair<string, string>>();
if (providers.Any())
{
foreach (var group in providers.GroupBy(o => o.Type))
{
switch (group.Key)
{
case ProviderUserType.ProviderAdmin:
foreach (var provider in group)
{
claims.Add(new KeyValuePair<string, string>("providerprovideradmin", provider.Id.ToString()));
}
break;
case ProviderUserType.ServiceUser:
foreach (var provider in group)
{
claims.Add(new KeyValuePair<string, string>("providerserviceuser", provider.Id.ToString()));
}
break;
}
}
}
var actual = CoreHelpers.BuildIdentityClaims(user, Array.Empty<CurrentContentOrganization>(), providers, false);
foreach (var claim in claims)
{
Assert.Contains(claim, actual);
}
}
public static IEnumerable<object[]> TokenIsValidData()
{
return new[]
{
new object[]
{
"first_part 476669d4-9642-4af8-9b29-9366efad4ed3 test@email.com {0}", // unprotectedTokenTemplate
"first_part", // firstPart
"test@email.com", // email
Guid.Parse("476669d4-9642-4af8-9b29-9366efad4ed3"), // id
DateTime.UtcNow.AddHours(-1), // creationTime
12, // expirationInHours
true, // isValid
}
};
}
[Theory]
[MemberData(nameof(TokenIsValidData))]
public void TokenIsValid_Success(string unprotectedTokenTemplate, string firstPart, string userEmail, Guid id, DateTime creationTime, double expirationInHours, bool isValid)
{
var protector = new TestDataProtector(string.Format(unprotectedTokenTemplate, CoreHelpers.ToEpocMilliseconds(creationTime)));
Assert.Equal(isValid, CoreHelpers.TokenIsValid(firstPart, protector, "protected_token", userEmail, id, expirationInHours));
}
private class TestDataProtector : IDataProtector
{
private readonly string _token;
public TestDataProtector(string token)
{
_token = token;
}
public IDataProtector CreateProtector(string purpose) => throw new NotImplementedException();
public byte[] Protect(byte[] plaintext) => throw new NotImplementedException();
public byte[] Unprotect(byte[] protectedData)
{
return Encoding.UTF8.GetBytes(_token);
}
}
[Theory]
[InlineData("hi@email.com", "hi@email.com")] // Short email with no room to obfuscate
[InlineData("name@email.com", "na**@email.com")] // Can obfuscate
[InlineData("reallylongnamethatnooneshouldhave@email", "re*******************************@email")] // Really long email and no .com, .net, etc
[InlineData("name@", "name@")] // @ symbol but no domain
[InlineData("", "")] // Empty string
[InlineData(null, null)] // null
public void ObfuscateEmail_Success(string input, string expected)
{
Assert.Equal(expected, CoreHelpers.ObfuscateEmail(input));
Assert.Contains(expected, actual);
}
}
[Theory, CustomAutoData(typeof(UserFixture))]
public void BuildIdentityClaims_CustomOrganizationUserClaims_Success(User user, CurrentContentOrganization org)
{
var fixture = new Fixture().WithAutoNSubstitutions();
org.Type = OrganizationUserType.Custom;
var actual = CoreHelpers.BuildIdentityClaims(user, new[] { org }, Array.Empty<CurrentContentProvider>(), false);
foreach (var (permitted, claimName) in org.Permissions.ClaimsMap)
{
var claim = new KeyValuePair<string, string>(claimName, org.Id.ToString());
if (permitted)
{
Assert.Contains(claim, actual);
}
else
{
Assert.DoesNotContain(claim, actual);
}
}
}
[Theory, CustomAutoData(typeof(UserFixture))]
public void BuildIdentityClaims_ProviderClaims_Success(User user)
{
var fixture = new Fixture().WithAutoNSubstitutions();
var providers = new List<CurrentContentProvider>();
foreach (var providerUserType in Enum.GetValues<ProviderUserType>())
{
var provider = fixture.Create<CurrentContentProvider>();
provider.Type = providerUserType;
providers.Add(provider);
}
var claims = new List<KeyValuePair<string, string>>();
if (providers.Any())
{
foreach (var group in providers.GroupBy(o => o.Type))
{
switch (group.Key)
{
case ProviderUserType.ProviderAdmin:
foreach (var provider in group)
{
claims.Add(new KeyValuePair<string, string>("providerprovideradmin", provider.Id.ToString()));
}
break;
case ProviderUserType.ServiceUser:
foreach (var provider in group)
{
claims.Add(new KeyValuePair<string, string>("providerserviceuser", provider.Id.ToString()));
}
break;
}
}
}
var actual = CoreHelpers.BuildIdentityClaims(user, Array.Empty<CurrentContentOrganization>(), providers, false);
foreach (var claim in claims)
{
Assert.Contains(claim, actual);
}
}
public static IEnumerable<object[]> TokenIsValidData()
{
return new[]
{
new object[]
{
"first_part 476669d4-9642-4af8-9b29-9366efad4ed3 test@email.com {0}", // unprotectedTokenTemplate
"first_part", // firstPart
"test@email.com", // email
Guid.Parse("476669d4-9642-4af8-9b29-9366efad4ed3"), // id
DateTime.UtcNow.AddHours(-1), // creationTime
12, // expirationInHours
true, // isValid
}
};
}
[Theory]
[MemberData(nameof(TokenIsValidData))]
public void TokenIsValid_Success(string unprotectedTokenTemplate, string firstPart, string userEmail, Guid id, DateTime creationTime, double expirationInHours, bool isValid)
{
var protector = new TestDataProtector(string.Format(unprotectedTokenTemplate, CoreHelpers.ToEpocMilliseconds(creationTime)));
Assert.Equal(isValid, CoreHelpers.TokenIsValid(firstPart, protector, "protected_token", userEmail, id, expirationInHours));
}
private class TestDataProtector : IDataProtector
{
private readonly string _token;
public TestDataProtector(string token)
{
_token = token;
}
public IDataProtector CreateProtector(string purpose) => throw new NotImplementedException();
public byte[] Protect(byte[] plaintext) => throw new NotImplementedException();
public byte[] Unprotect(byte[] protectedData)
{
return Encoding.UTF8.GetBytes(_token);
}
}
[Theory]
[InlineData("hi@email.com", "hi@email.com")] // Short email with no room to obfuscate
[InlineData("name@email.com", "na**@email.com")] // Can obfuscate
[InlineData("reallylongnamethatnooneshouldhave@email", "re*******************************@email")] // Really long email and no .com, .net, etc
[InlineData("name@", "name@")] // @ symbol but no domain
[InlineData("", "")] // Empty string
[InlineData(null, null)] // null
public void ObfuscateEmail_Success(string input, string expected)
{
Assert.Equal(expected, CoreHelpers.ObfuscateEmail(input));
}
}

View File

@ -1,43 +1,42 @@
using Bit.Core.Utilities;
using Xunit;
namespace Bit.Core.Test.Utilities
namespace Bit.Core.Test.Utilities;
public class EncryptedStringAttributeTests
{
public class EncryptedStringAttributeTests
[Theory]
[InlineData(null)]
[InlineData("aXY=|Y3Q=")] // Valid AesCbc256_B64
[InlineData("aXY=|Y3Q=|cnNhQ3Q=")] // Valid AesCbc128_HmacSha256_B64
[InlineData("Rsa2048_OaepSha256_B64.cnNhQ3Q=")]
public void IsValid_ReturnsTrue_WhenValid(string input)
{
[Theory]
[InlineData(null)]
[InlineData("aXY=|Y3Q=")] // Valid AesCbc256_B64
[InlineData("aXY=|Y3Q=|cnNhQ3Q=")] // Valid AesCbc128_HmacSha256_B64
[InlineData("Rsa2048_OaepSha256_B64.cnNhQ3Q=")]
public void IsValid_ReturnsTrue_WhenValid(string input)
{
var sut = new EncryptedStringAttribute();
var sut = new EncryptedStringAttribute();
var actual = sut.IsValid(input);
var actual = sut.IsValid(input);
Assert.True(actual);
}
Assert.True(actual);
}
[Theory]
[InlineData("")]
[InlineData(".")]
[InlineData("|")]
[InlineData("!|!")] // Invalid base 64
[InlineData("Rsa2048_OaepSha1_HmacSha256_B64.1")] // Invalid length
[InlineData("Rsa2048_OaepSha1_HmacSha256_B64.|")] // Empty iv & ct
[InlineData("AesCbc128_HmacSha256_B64.1")] // Invalid length
[InlineData("AesCbc128_HmacSha256_B64.aXY=|Y3Q=|")] // Empty mac
[InlineData("Rsa2048_OaepSha1_HmacSha256_B64.aXY=|Y3Q=|")] // Empty mac
[InlineData("Rsa2048_OaepSha256_B64.1|2")] // Invalid length
[InlineData("Rsa2048_OaepSha1_HmacSha256_B64.aXY=|")] // Empty mac
public void IsValid_ReturnsFalse_WhenInvalid(string input)
{
var sut = new EncryptedStringAttribute();
[Theory]
[InlineData("")]
[InlineData(".")]
[InlineData("|")]
[InlineData("!|!")] // Invalid base 64
[InlineData("Rsa2048_OaepSha1_HmacSha256_B64.1")] // Invalid length
[InlineData("Rsa2048_OaepSha1_HmacSha256_B64.|")] // Empty iv & ct
[InlineData("AesCbc128_HmacSha256_B64.1")] // Invalid length
[InlineData("AesCbc128_HmacSha256_B64.aXY=|Y3Q=|")] // Empty mac
[InlineData("Rsa2048_OaepSha1_HmacSha256_B64.aXY=|Y3Q=|")] // Empty mac
[InlineData("Rsa2048_OaepSha256_B64.1|2")] // Invalid length
[InlineData("Rsa2048_OaepSha1_HmacSha256_B64.aXY=|")] // Empty mac
public void IsValid_ReturnsFalse_WhenInvalid(string input)
{
var sut = new EncryptedStringAttribute();
var actual = sut.IsValid(input);
var actual = sut.IsValid(input);
Assert.False(actual);
}
Assert.False(actual);
}
}

View File

@ -2,65 +2,64 @@
using Bit.Core.Utilities;
using Xunit;
namespace Bit.Core.Test.Helpers
namespace Bit.Core.Test.Helpers;
public class JsonHelpersTests
{
public class JsonHelpersTests
private static void CompareJson<T>(T value, JsonSerializerOptions options, Newtonsoft.Json.JsonSerializerSettings settings)
{
private static void CompareJson<T>(T value, JsonSerializerOptions options, Newtonsoft.Json.JsonSerializerSettings settings)
{
var stgJson = JsonSerializer.Serialize(value, options);
var nsJson = Newtonsoft.Json.JsonConvert.SerializeObject(value, settings);
var stgJson = JsonSerializer.Serialize(value, options);
var nsJson = Newtonsoft.Json.JsonConvert.SerializeObject(value, settings);
Assert.Equal(stgJson, nsJson);
}
[Fact]
public void DefaultJsonOptions()
{
var testObject = new SimpleTestObject
{
Id = 0,
Name = "Test",
};
CompareJson(testObject, JsonHelpers.Default, new Newtonsoft.Json.JsonSerializerSettings());
}
[Fact]
public void IndentedJsonOptions()
{
var testObject = new SimpleTestObject
{
Id = 10,
Name = "Test Name"
};
CompareJson(testObject, JsonHelpers.Indented, new Newtonsoft.Json.JsonSerializerSettings
{
Formatting = Newtonsoft.Json.Formatting.Indented,
});
}
[Fact]
public void NullValueHandlingJsonOptions()
{
var testObject = new SimpleTestObject
{
Id = 14,
Name = null,
};
CompareJson(testObject, JsonHelpers.IgnoreWritingNull, new Newtonsoft.Json.JsonSerializerSettings
{
NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore,
});
}
Assert.Equal(stgJson, nsJson);
}
public class SimpleTestObject
[Fact]
public void DefaultJsonOptions()
{
public int Id { get; set; }
public string Name { get; set; }
var testObject = new SimpleTestObject
{
Id = 0,
Name = "Test",
};
CompareJson(testObject, JsonHelpers.Default, new Newtonsoft.Json.JsonSerializerSettings());
}
[Fact]
public void IndentedJsonOptions()
{
var testObject = new SimpleTestObject
{
Id = 10,
Name = "Test Name"
};
CompareJson(testObject, JsonHelpers.Indented, new Newtonsoft.Json.JsonSerializerSettings
{
Formatting = Newtonsoft.Json.Formatting.Indented,
});
}
[Fact]
public void NullValueHandlingJsonOptions()
{
var testObject = new SimpleTestObject
{
Id = 14,
Name = null,
};
CompareJson(testObject, JsonHelpers.IgnoreWritingNull, new Newtonsoft.Json.JsonSerializerSettings
{
NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore,
});
}
}
public class SimpleTestObject
{
public int Id { get; set; }
public string Name { get; set; }
}

View File

@ -5,165 +5,164 @@ using Bit.Core.Utilities;
using Bit.Test.Common.Helpers;
using Xunit;
namespace Bit.Core.Test.Utilities
namespace Bit.Core.Test.Utilities;
public class PermissiveStringConverterTests
{
public class PermissiveStringConverterTests
private const string numberJson = "{ \"StringProp\": 1, \"EnumerableStringProp\": [ 2, 3 ]}";
private const string stringJson = "{ \"StringProp\": \"1\", \"EnumerableStringProp\": [ \"2\", \"3\" ]}";
private const string nullAndEmptyJson = "{ \"StringProp\": null, \"EnumerableStringProp\": [] }";
private const string singleValueJson = "{ \"StringProp\": 1, \"EnumerableStringProp\": \"Hello!\" }";
private const string nullJson = "{ \"StringProp\": null, \"EnumerableStringProp\": null }";
private const string boolJson = "{ \"StringProp\": true, \"EnumerableStringProp\": [ false, 1.2]}";
private const string objectJsonOne = "{ \"StringProp\": { \"Message\": \"Hi\"}, \"EnumerableStringProp\": []}";
private const string objectJsonTwo = "{ \"StringProp\": \"Hi\", \"EnumerableStringProp\": {}}";
private readonly string bigNumbersJson =
"{ \"StringProp\":" + decimal.MinValue + ", \"EnumerableStringProp\": [" + ulong.MaxValue + ", " + long.MinValue + "]}";
[Theory]
[InlineData(numberJson)]
[InlineData(stringJson)]
public void Read_Success(string json)
{
private const string numberJson = "{ \"StringProp\": 1, \"EnumerableStringProp\": [ 2, 3 ]}";
private const string stringJson = "{ \"StringProp\": \"1\", \"EnumerableStringProp\": [ \"2\", \"3\" ]}";
private const string nullAndEmptyJson = "{ \"StringProp\": null, \"EnumerableStringProp\": [] }";
private const string singleValueJson = "{ \"StringProp\": 1, \"EnumerableStringProp\": \"Hello!\" }";
private const string nullJson = "{ \"StringProp\": null, \"EnumerableStringProp\": null }";
private const string boolJson = "{ \"StringProp\": true, \"EnumerableStringProp\": [ false, 1.2]}";
private const string objectJsonOne = "{ \"StringProp\": { \"Message\": \"Hi\"}, \"EnumerableStringProp\": []}";
private const string objectJsonTwo = "{ \"StringProp\": \"Hi\", \"EnumerableStringProp\": {}}";
private readonly string bigNumbersJson =
"{ \"StringProp\":" + decimal.MinValue + ", \"EnumerableStringProp\": [" + ulong.MaxValue + ", " + long.MinValue + "]}";
[Theory]
[InlineData(numberJson)]
[InlineData(stringJson)]
public void Read_Success(string json)
{
var obj = JsonSerializer.Deserialize<TestObject>(json);
Assert.Equal("1", obj.StringProp);
Assert.Equal(2, obj.EnumerableStringProp.Count());
Assert.Equal("2", obj.EnumerableStringProp.ElementAt(0));
Assert.Equal("3", obj.EnumerableStringProp.ElementAt(1));
}
[Fact]
public void Read_Boolean_Success()
{
var obj = JsonSerializer.Deserialize<TestObject>(boolJson);
Assert.Equal("True", obj.StringProp);
Assert.Equal(2, obj.EnumerableStringProp.Count());
Assert.Equal("False", obj.EnumerableStringProp.ElementAt(0));
Assert.Equal("1.2", obj.EnumerableStringProp.ElementAt(1));
}
[Fact]
public void Read_Float_Success_Culture()
{
var ci = new CultureInfo("sv-SE");
Thread.CurrentThread.CurrentCulture = ci;
Thread.CurrentThread.CurrentUICulture = ci;
var obj = JsonSerializer.Deserialize<TestObject>(boolJson);
Assert.Equal("1.2", obj.EnumerableStringProp.ElementAt(1));
}
[Fact]
public void Read_BigNumbers_Success()
{
var obj = JsonSerializer.Deserialize<TestObject>(bigNumbersJson);
Assert.Equal(decimal.MinValue.ToString(), obj.StringProp);
Assert.Equal(2, obj.EnumerableStringProp.Count());
Assert.Equal(ulong.MaxValue.ToString(), obj.EnumerableStringProp.ElementAt(0));
Assert.Equal(long.MinValue.ToString(), obj.EnumerableStringProp.ElementAt(1));
}
[Fact]
public void Read_SingleValue_Success()
{
var obj = JsonSerializer.Deserialize<TestObject>(singleValueJson);
Assert.Equal("1", obj.StringProp);
Assert.Single(obj.EnumerableStringProp);
Assert.Equal("Hello!", obj.EnumerableStringProp.ElementAt(0));
}
[Fact]
public void Read_NullAndEmptyJson_Success()
{
var obj = JsonSerializer.Deserialize<TestObject>(nullAndEmptyJson);
Assert.Null(obj.StringProp);
Assert.Empty(obj.EnumerableStringProp);
}
[Fact]
public void Read_Null_Success()
{
var obj = JsonSerializer.Deserialize<TestObject>(nullJson);
Assert.Null(obj.StringProp);
Assert.Null(obj.EnumerableStringProp);
}
[Theory]
[InlineData(objectJsonOne)]
[InlineData(objectJsonTwo)]
public void Read_Object_Throws(string json)
{
var exception = Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<TestObject>(json));
}
[Fact]
public void Write_Success()
{
var json = JsonSerializer.Serialize(new TestObject
{
StringProp = "1",
EnumerableStringProp = new List<string>
{
"2",
"3",
},
});
var jsonElement = JsonDocument.Parse(json).RootElement;
var stringProp = AssertHelper.AssertJsonProperty(jsonElement, "StringProp", JsonValueKind.String);
Assert.Equal("1", stringProp.GetString());
var list = AssertHelper.AssertJsonProperty(jsonElement, "EnumerableStringProp", JsonValueKind.Array);
Assert.Equal(2, list.GetArrayLength());
var firstElement = list[0];
Assert.Equal(JsonValueKind.String, firstElement.ValueKind);
Assert.Equal("2", firstElement.GetString());
var secondElement = list[1];
Assert.Equal(JsonValueKind.String, secondElement.ValueKind);
Assert.Equal("3", secondElement.GetString());
}
[Fact]
public void Write_Null()
{
// When the values are null the converters aren't actually ran and it automatically serializes null
var json = JsonSerializer.Serialize(new TestObject
{
StringProp = null,
EnumerableStringProp = null,
});
var jsonElement = JsonDocument.Parse(json).RootElement;
AssertHelper.AssertJsonProperty(jsonElement, "StringProp", JsonValueKind.Null);
AssertHelper.AssertJsonProperty(jsonElement, "EnumerableStringProp", JsonValueKind.Null);
}
[Fact]
public void Write_Empty()
{
// When the values are null the converters aren't actually ran and it automatically serializes null
var json = JsonSerializer.Serialize(new TestObject
{
StringProp = "",
EnumerableStringProp = Enumerable.Empty<string>(),
});
var jsonElement = JsonDocument.Parse(json).RootElement;
var stringVal = AssertHelper.AssertJsonProperty(jsonElement, "StringProp", JsonValueKind.String).GetString();
Assert.Equal("", stringVal);
var array = AssertHelper.AssertJsonProperty(jsonElement, "EnumerableStringProp", JsonValueKind.Array);
Assert.Equal(0, array.GetArrayLength());
}
var obj = JsonSerializer.Deserialize<TestObject>(json);
Assert.Equal("1", obj.StringProp);
Assert.Equal(2, obj.EnumerableStringProp.Count());
Assert.Equal("2", obj.EnumerableStringProp.ElementAt(0));
Assert.Equal("3", obj.EnumerableStringProp.ElementAt(1));
}
public class TestObject
[Fact]
public void Read_Boolean_Success()
{
[JsonConverter(typeof(PermissiveStringConverter))]
public string StringProp { get; set; }
var obj = JsonSerializer.Deserialize<TestObject>(boolJson);
Assert.Equal("True", obj.StringProp);
Assert.Equal(2, obj.EnumerableStringProp.Count());
Assert.Equal("False", obj.EnumerableStringProp.ElementAt(0));
Assert.Equal("1.2", obj.EnumerableStringProp.ElementAt(1));
}
[JsonConverter(typeof(PermissiveStringEnumerableConverter))]
public IEnumerable<string> EnumerableStringProp { get; set; }
[Fact]
public void Read_Float_Success_Culture()
{
var ci = new CultureInfo("sv-SE");
Thread.CurrentThread.CurrentCulture = ci;
Thread.CurrentThread.CurrentUICulture = ci;
var obj = JsonSerializer.Deserialize<TestObject>(boolJson);
Assert.Equal("1.2", obj.EnumerableStringProp.ElementAt(1));
}
[Fact]
public void Read_BigNumbers_Success()
{
var obj = JsonSerializer.Deserialize<TestObject>(bigNumbersJson);
Assert.Equal(decimal.MinValue.ToString(), obj.StringProp);
Assert.Equal(2, obj.EnumerableStringProp.Count());
Assert.Equal(ulong.MaxValue.ToString(), obj.EnumerableStringProp.ElementAt(0));
Assert.Equal(long.MinValue.ToString(), obj.EnumerableStringProp.ElementAt(1));
}
[Fact]
public void Read_SingleValue_Success()
{
var obj = JsonSerializer.Deserialize<TestObject>(singleValueJson);
Assert.Equal("1", obj.StringProp);
Assert.Single(obj.EnumerableStringProp);
Assert.Equal("Hello!", obj.EnumerableStringProp.ElementAt(0));
}
[Fact]
public void Read_NullAndEmptyJson_Success()
{
var obj = JsonSerializer.Deserialize<TestObject>(nullAndEmptyJson);
Assert.Null(obj.StringProp);
Assert.Empty(obj.EnumerableStringProp);
}
[Fact]
public void Read_Null_Success()
{
var obj = JsonSerializer.Deserialize<TestObject>(nullJson);
Assert.Null(obj.StringProp);
Assert.Null(obj.EnumerableStringProp);
}
[Theory]
[InlineData(objectJsonOne)]
[InlineData(objectJsonTwo)]
public void Read_Object_Throws(string json)
{
var exception = Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<TestObject>(json));
}
[Fact]
public void Write_Success()
{
var json = JsonSerializer.Serialize(new TestObject
{
StringProp = "1",
EnumerableStringProp = new List<string>
{
"2",
"3",
},
});
var jsonElement = JsonDocument.Parse(json).RootElement;
var stringProp = AssertHelper.AssertJsonProperty(jsonElement, "StringProp", JsonValueKind.String);
Assert.Equal("1", stringProp.GetString());
var list = AssertHelper.AssertJsonProperty(jsonElement, "EnumerableStringProp", JsonValueKind.Array);
Assert.Equal(2, list.GetArrayLength());
var firstElement = list[0];
Assert.Equal(JsonValueKind.String, firstElement.ValueKind);
Assert.Equal("2", firstElement.GetString());
var secondElement = list[1];
Assert.Equal(JsonValueKind.String, secondElement.ValueKind);
Assert.Equal("3", secondElement.GetString());
}
[Fact]
public void Write_Null()
{
// When the values are null the converters aren't actually ran and it automatically serializes null
var json = JsonSerializer.Serialize(new TestObject
{
StringProp = null,
EnumerableStringProp = null,
});
var jsonElement = JsonDocument.Parse(json).RootElement;
AssertHelper.AssertJsonProperty(jsonElement, "StringProp", JsonValueKind.Null);
AssertHelper.AssertJsonProperty(jsonElement, "EnumerableStringProp", JsonValueKind.Null);
}
[Fact]
public void Write_Empty()
{
// When the values are null the converters aren't actually ran and it automatically serializes null
var json = JsonSerializer.Serialize(new TestObject
{
StringProp = "",
EnumerableStringProp = Enumerable.Empty<string>(),
});
var jsonElement = JsonDocument.Parse(json).RootElement;
var stringVal = AssertHelper.AssertJsonProperty(jsonElement, "StringProp", JsonValueKind.String).GetString();
Assert.Equal("", stringVal);
var array = AssertHelper.AssertJsonProperty(jsonElement, "EnumerableStringProp", JsonValueKind.Array);
Assert.Equal(0, array.GetArrayLength());
}
}
public class TestObject
{
[JsonConverter(typeof(PermissiveStringConverter))]
public string StringProp { get; set; }
[JsonConverter(typeof(PermissiveStringEnumerableConverter))]
public IEnumerable<string> EnumerableStringProp { get; set; }
}

View File

@ -10,83 +10,82 @@ using Microsoft.Extensions.DependencyInjection;
using NSubstitute;
using Xunit;
namespace Bit.Core.Test.Utilities
namespace Bit.Core.Test.Utilities;
public class SelfHostedAttributeTests
{
public class SelfHostedAttributeTests
[Fact]
public void NotSelfHosted_Throws_When_SelfHosted()
{
[Fact]
public void NotSelfHosted_Throws_When_SelfHosted()
// Arrange
var sha = new SelfHostedAttribute { NotSelfHostedOnly = true };
// Act & Assert
Assert.Throws<BadRequestException>(() => sha.OnActionExecuting(GetContext(selfHosted: true)));
}
[Fact]
public void NotSelfHosted_Success_When_NotSelfHosted()
{
// Arrange
var sha = new SelfHostedAttribute { NotSelfHostedOnly = true };
// Act
sha.OnActionExecuting(GetContext(selfHosted: false));
// Assert
// The Assert here is just NOT throwing an exception
}
[Fact]
public void SelfHosted_Success_When_SelfHosted()
{
// Arrange
var sha = new SelfHostedAttribute { SelfHostedOnly = true };
// Act
sha.OnActionExecuting(GetContext(selfHosted: true));
// Assert
// The Assert here is just NOT throwing an exception
}
[Fact]
public void SelfHosted_Throws_When_NotSelfHosted()
{
// Arrange
var sha = new SelfHostedAttribute { SelfHostedOnly = true };
// Act & Assert
Assert.Throws<BadRequestException>(() => sha.OnActionExecuting(GetContext(selfHosted: false)));
}
// This generates a ActionExecutingContext with the needed injected
// service with the given value.
private ActionExecutingContext GetContext(bool selfHosted)
{
IServiceCollection services = new ServiceCollection();
var globalSettings = new GlobalSettings
{
// Arrange
var sha = new SelfHostedAttribute { NotSelfHostedOnly = true };
SelfHosted = selfHosted
};
// Act & Assert
Assert.Throws<BadRequestException>(() => sha.OnActionExecuting(GetContext(selfHosted: true)));
}
services.AddSingleton(globalSettings);
[Fact]
public void NotSelfHosted_Success_When_NotSelfHosted()
{
// Arrange
var sha = new SelfHostedAttribute { NotSelfHostedOnly = true };
var httpContext = new DefaultHttpContext();
httpContext.RequestServices = services.BuildServiceProvider();
// Act
sha.OnActionExecuting(GetContext(selfHosted: false));
var context = Substitute.For<ActionExecutingContext>(
Substitute.For<ActionContext>(httpContext,
new RouteData(),
Substitute.For<ActionDescriptor>()),
new List<IFilterMetadata>(),
new Dictionary<string, object>(),
Substitute.For<Controller>());
// Assert
// The Assert here is just NOT throwing an exception
}
[Fact]
public void SelfHosted_Success_When_SelfHosted()
{
// Arrange
var sha = new SelfHostedAttribute { SelfHostedOnly = true };
// Act
sha.OnActionExecuting(GetContext(selfHosted: true));
// Assert
// The Assert here is just NOT throwing an exception
}
[Fact]
public void SelfHosted_Throws_When_NotSelfHosted()
{
// Arrange
var sha = new SelfHostedAttribute { SelfHostedOnly = true };
// Act & Assert
Assert.Throws<BadRequestException>(() => sha.OnActionExecuting(GetContext(selfHosted: false)));
}
// This generates a ActionExecutingContext with the needed injected
// service with the given value.
private ActionExecutingContext GetContext(bool selfHosted)
{
IServiceCollection services = new ServiceCollection();
var globalSettings = new GlobalSettings
{
SelfHosted = selfHosted
};
services.AddSingleton(globalSettings);
var httpContext = new DefaultHttpContext();
httpContext.RequestServices = services.BuildServiceProvider();
var context = Substitute.For<ActionExecutingContext>(
Substitute.For<ActionContext>(httpContext,
new RouteData(),
Substitute.For<ActionDescriptor>()),
new List<IFilterMetadata>(),
new Dictionary<string, object>(),
Substitute.For<Controller>());
return context;
}
return context;
}
}

View File

@ -1,59 +1,58 @@
using Bit.Core.Utilities;
using Xunit;
namespace Bit.Core.Test.Utilities
namespace Bit.Core.Test.Utilities;
public class StrictEmailAttributeTests
{
public class StrictEmailAttributeTests
[Theory]
[InlineData("hello@world.com")] // regular email address
[InlineData("hello@world.planet.com")] // subdomain
[InlineData("hello+1@world.com")] // alias
[InlineData("hello.there@world.com")] // period in local-part
[InlineData("hello@wörldé.com")] // unicode domain
[InlineData("hello@world.cömé")] // unicode top-level domain
public void IsValid_ReturnsTrueWhenValid(string email)
{
[Theory]
[InlineData("hello@world.com")] // regular email address
[InlineData("hello@world.planet.com")] // subdomain
[InlineData("hello+1@world.com")] // alias
[InlineData("hello.there@world.com")] // period in local-part
[InlineData("hello@wörldé.com")] // unicode domain
[InlineData("hello@world.cömé")] // unicode top-level domain
public void IsValid_ReturnsTrueWhenValid(string email)
{
var sut = new StrictEmailAddressAttribute();
var sut = new StrictEmailAddressAttribute();
var actual = sut.IsValid(email);
var actual = sut.IsValid(email);
Assert.True(actual);
}
Assert.True(actual);
}
[Theory]
[InlineData(null)] // null
[InlineData("hello@world.com\t")] // trailing tab char
[InlineData("\thello@world.com")] // leading tab char
[InlineData("hel\tlo@world.com")] // local-part tab char
[InlineData("hello@world.com\b")] // trailing backspace char
[InlineData("\" \"hello@world.com")] // leading spaces in quotes
[InlineData("hello@world.com\" \"")] // trailing spaces in quotes
[InlineData("hel\" \"lo@world.com")] // local-part spaces in quotes
[InlineData("hello there@world.com")] // unescaped unquoted spaces
[InlineData("Hello <hello@world.com>")] // friendly from
[InlineData("<hello@world.com>")] // wrapped angle brackets
[InlineData("hello(com)there@world.com")] // comment
[InlineData("hello@world.com.")] // trailing period
[InlineData(".hello@world.com")] // leading period
[InlineData("hello@world.com;")] // trailing semicolon
[InlineData(";hello@world.com")] // leading semicolon
[InlineData("hello@world.com; hello@world.com")] // semicolon separated list
[InlineData("hello@world.com, hello@world.com")] // comma separated list
[InlineData("hellothere@worldcom")] // dotless domain
[InlineData("hello.there@worldcom")] // dotless domain
[InlineData("hellothere@.worldcom")] // domain beginning with dot
[InlineData("hellothere@worldcom.")] // domain ending in dot
[InlineData("hellothere@world.com-")] // domain ending in hyphen
[InlineData("hellö@world.com")] // unicode at end of local-part
[InlineData("héllo@world.com")] // unicode in middle of local-part
public void IsValid_ReturnsFalseWhenInvalid(string email)
{
var sut = new StrictEmailAddressAttribute();
[Theory]
[InlineData(null)] // null
[InlineData("hello@world.com\t")] // trailing tab char
[InlineData("\thello@world.com")] // leading tab char
[InlineData("hel\tlo@world.com")] // local-part tab char
[InlineData("hello@world.com\b")] // trailing backspace char
[InlineData("\" \"hello@world.com")] // leading spaces in quotes
[InlineData("hello@world.com\" \"")] // trailing spaces in quotes
[InlineData("hel\" \"lo@world.com")] // local-part spaces in quotes
[InlineData("hello there@world.com")] // unescaped unquoted spaces
[InlineData("Hello <hello@world.com>")] // friendly from
[InlineData("<hello@world.com>")] // wrapped angle brackets
[InlineData("hello(com)there@world.com")] // comment
[InlineData("hello@world.com.")] // trailing period
[InlineData(".hello@world.com")] // leading period
[InlineData("hello@world.com;")] // trailing semicolon
[InlineData(";hello@world.com")] // leading semicolon
[InlineData("hello@world.com; hello@world.com")] // semicolon separated list
[InlineData("hello@world.com, hello@world.com")] // comma separated list
[InlineData("hellothere@worldcom")] // dotless domain
[InlineData("hello.there@worldcom")] // dotless domain
[InlineData("hellothere@.worldcom")] // domain beginning with dot
[InlineData("hellothere@worldcom.")] // domain ending in dot
[InlineData("hellothere@world.com-")] // domain ending in hyphen
[InlineData("hellö@world.com")] // unicode at end of local-part
[InlineData("héllo@world.com")] // unicode in middle of local-part
public void IsValid_ReturnsFalseWhenInvalid(string email)
{
var sut = new StrictEmailAddressAttribute();
var actual = sut.IsValid(email);
var actual = sut.IsValid(email);
Assert.False(actual);
}
Assert.False(actual);
}
}

View File

@ -1,54 +1,53 @@
using Bit.Core.Utilities;
using Xunit;
namespace Bit.Core.Test.Utilities
namespace Bit.Core.Test.Utilities;
public class StrictEmailAddressListAttributeTests
{
public class StrictEmailAddressListAttributeTests
public static List<object[]> EmailList => new()
{
public static List<object[]> EmailList => new()
{
new object[] { new List<string> { "test@domain.com", "test@sub.domain.com", "hello@world.planet.com" }, true },
new object[] { new List<string> { "/hello@world.com", "hello@##world.pla net.com", "''thello@world.com" }, false },
new object[] { new List<string> { "/hello.com", "test@domain.com", "''thello@world.com" }, false },
new object[] { new List<string> { "héllö@world.com", "hello@world.planet.com", "hello@world.planet.com" }, false },
new object[] { new List<string> { }, false },
new object[] { new List<string>
{
"test1@domain.com", "test2@domain.com", "test3@domain.com", "test4@domain.com", "test5@domain.com",
"test6@domain.com", "test7@domain.com", "test8@domain.com", "test9@domain.com", "test10@domain.com",
"test11@domain.com", "test12@domain.com", "test13@domain.com", "test14@domain.com", "test15@domain.com",
"test16@domain.com", "test17@domain.com", "test18@domain.com", "test19@domain.com", "test20@domain.com",
"test21@domain.com", "test22@domain.com", "test23@domain.com", "test24@domain.com", "test25@domain.com",
}, false },
new object[] { new List<string>
{
"test1domaincomtest2domaincomtest3domaincomtest4domaincomtest5domaincomtest6domaincomtest7domaincomtest8domaincomtest9domaincomtest10domaincomtest1domaincomtest2domaincomtest3domaincomtest4domaincomtest5domaincomtest6domaincomtest7domaincomtest8domaincomtest9domaincomtest10domaincom@test.com",
"test@domain.com"
}, false } // > 256 character email
new object[] { new List<string> { "test@domain.com", "test@sub.domain.com", "hello@world.planet.com" }, true },
new object[] { new List<string> { "/hello@world.com", "hello@##world.pla net.com", "''thello@world.com" }, false },
new object[] { new List<string> { "/hello.com", "test@domain.com", "''thello@world.com" }, false },
new object[] { new List<string> { "llö@world.com", "hello@world.planet.com", "hello@world.planet.com" }, false },
new object[] { new List<string> { }, false },
new object[] { new List<string>
{
"test1@domain.com", "test2@domain.com", "test3@domain.com", "test4@domain.com", "test5@domain.com",
"test6@domain.com", "test7@domain.com", "test8@domain.com", "test9@domain.com", "test10@domain.com",
"test11@domain.com", "test12@domain.com", "test13@domain.com", "test14@domain.com", "test15@domain.com",
"test16@domain.com", "test17@domain.com", "test18@domain.com", "test19@domain.com", "test20@domain.com",
"test21@domain.com", "test22@domain.com", "test23@domain.com", "test24@domain.com", "test25@domain.com",
}, false },
new object[] { new List<string>
{
"test1domaincomtest2domaincomtest3domaincomtest4domaincomtest5domaincomtest6domaincomtest7domaincomtest8domaincomtest9domaincomtest10domaincomtest1domaincomtest2domaincomtest3domaincomtest4domaincomtest5domaincomtest6domaincomtest7domaincomtest8domaincomtest9domaincomtest10domaincom@test.com",
"test@domain.com"
}, false } // > 256 character email
};
};
[Theory]
[MemberData(nameof(EmailList))]
public void IsListValid_ReturnsTrue_WhenValid(List<string> emailList, bool valid)
{
var sut = new StrictEmailAddressListAttribute();
[Theory]
[MemberData(nameof(EmailList))]
public void IsListValid_ReturnsTrue_WhenValid(List<string> emailList, bool valid)
{
var sut = new StrictEmailAddressListAttribute();
var actual = sut.IsValid(emailList);
var actual = sut.IsValid(emailList);
Assert.Equal(actual, valid);
}
Assert.Equal(actual, valid);
}
[Theory]
[InlineData("single@email.com", false)]
[InlineData(null, false)]
public void IsValid_ReturnsTrue_WhenValid(string email, bool valid)
{
var sut = new StrictEmailAddressListAttribute();
[Theory]
[InlineData("single@email.com", false)]
[InlineData(null, false)]
public void IsValid_ReturnsTrue_WhenValid(string email, bool valid)
{
var sut = new StrictEmailAddressListAttribute();
var actual = sut.IsValid(email);
var actual = sut.IsValid(email);
Assert.Equal(actual, valid);
}
Assert.Equal(actual, valid);
}
}