mirror of
https://github.com/bitwarden/server
synced 2025-12-24 04:03:25 +00:00
Revert filescoped (#2227)
* Revert "Add git blame entry (#2226)" This reverts commit239286737d. * Revert "Turn on file scoped namespaces (#2225)" This reverts commit34fb4cca2a.
This commit is contained in:
@@ -1,13 +1,14 @@
|
||||
namespace Bit.Core.Test.AutoFixture.Attributes;
|
||||
|
||||
public sealed class CiSkippedTheory : Xunit.TheoryAttribute
|
||||
namespace Bit.Core.Test.AutoFixture.Attributes
|
||||
{
|
||||
private static bool IsGithubActions() => Environment.GetEnvironmentVariable("CI") != null;
|
||||
public CiSkippedTheory()
|
||||
public sealed class CiSkippedTheory : Xunit.TheoryAttribute
|
||||
{
|
||||
if (IsGithubActions())
|
||||
private static bool IsGithubActions() => Environment.GetEnvironmentVariable("CI") != null;
|
||||
public CiSkippedTheory()
|
||||
{
|
||||
Skip = "Ignore during CI builds";
|
||||
if (IsGithubActions())
|
||||
{
|
||||
Skip = "Ignore during CI builds";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,31 +2,32 @@
|
||||
using AutoFixture.Dsl;
|
||||
using Bit.Core.Models.Data;
|
||||
|
||||
namespace Bit.Core.Test.AutoFixture.CipherAttachmentMetaData;
|
||||
|
||||
public class MetaData : ICustomization
|
||||
namespace Bit.Core.Test.AutoFixture.CipherAttachmentMetaData
|
||||
{
|
||||
protected virtual IPostprocessComposer<CipherAttachment.MetaData> ComposerAction(IFixture fixture,
|
||||
ICustomizationComposer<CipherAttachment.MetaData> composer)
|
||||
public class MetaData : ICustomization
|
||||
{
|
||||
return composer.With(d => d.Size, fixture.Create<long>());
|
||||
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));
|
||||
}
|
||||
}
|
||||
public void Customize(IFixture fixture)
|
||||
|
||||
public class MetaDataWithoutContainer : MetaData
|
||||
{
|
||||
fixture.Customize<CipherAttachment.MetaData>(composer => ComposerAction(fixture, composer));
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -3,66 +3,67 @@ using Bit.Core.Entities;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using Core.Models.Data;
|
||||
|
||||
namespace Bit.Core.Test.AutoFixture.CipherFixtures;
|
||||
|
||||
internal class OrganizationCipher : ICustomization
|
||||
namespace Bit.Core.Test.AutoFixture.CipherFixtures
|
||||
{
|
||||
public Guid? OrganizationId { get; set; }
|
||||
public void Customize(IFixture fixture)
|
||||
internal class OrganizationCipher : ICustomization
|
||||
{
|
||||
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));
|
||||
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)
|
||||
{ }
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{ }
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
using Bit.Core.Test.AutoFixture.OrganizationFixtures;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
|
||||
namespace Bit.Core.Test.AutoFixture.CollectionFixtures;
|
||||
|
||||
internal class CollectionAutoDataAttribute : CustomAutoDataAttribute
|
||||
namespace Bit.Core.Test.AutoFixture.CollectionFixtures
|
||||
{
|
||||
public CollectionAutoDataAttribute() : base(new SutProviderCustomization(), new OrganizationCustomization())
|
||||
{ }
|
||||
internal class CollectionAutoDataAttribute : CustomAutoDataAttribute
|
||||
{
|
||||
public CollectionAutoDataAttribute() : base(new SutProviderCustomization(), new OrganizationCustomization())
|
||||
{ }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,35 +3,36 @@ using AutoFixture.Kernel;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Test.Common.AutoFixture;
|
||||
|
||||
namespace Bit.Core.Test.AutoFixture.CurrentContextFixtures;
|
||||
|
||||
internal class CurrentContext : ICustomization
|
||||
namespace Bit.Core.Test.AutoFixture.CurrentContextFixtures
|
||||
{
|
||||
public void Customize(IFixture fixture)
|
||||
internal class CurrentContext : ICustomization
|
||||
{
|
||||
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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,28 +4,29 @@ using AutoFixture.Kernel;
|
||||
using AutoFixture.Xunit2;
|
||||
using Bit.Core.Test.Helpers.Factories;
|
||||
|
||||
namespace Bit.Test.Common.AutoFixture;
|
||||
|
||||
public class GlobalSettingsBuilder : ISpecimenBuilder
|
||||
namespace Bit.Test.Common.AutoFixture
|
||||
{
|
||||
public object Create(object request, ISpecimenContext context)
|
||||
public class GlobalSettingsBuilder : ISpecimenBuilder
|
||||
{
|
||||
if (context == null)
|
||||
public object Create(object request, ISpecimenContext context)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
public class GlobalSettingsCustomizeAttribute : CustomizeAttribute
|
||||
{
|
||||
public override ICustomization GetCustomization(ParameterInfo parameter) => new GlobalSettings();
|
||||
}
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
using Bit.Core.Test.AutoFixture.OrganizationFixtures;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
|
||||
namespace Bit.Core.Test.AutoFixture.GroupFixtures;
|
||||
|
||||
internal class GroupOrganizationAutoDataAttribute : CustomAutoDataAttribute
|
||||
namespace Bit.Core.Test.AutoFixture.GroupFixtures
|
||||
{
|
||||
public GroupOrganizationAutoDataAttribute() : base(
|
||||
new SutProviderCustomization(), new OrganizationCustomization { UseGroups = true })
|
||||
{ }
|
||||
}
|
||||
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 })
|
||||
{ }
|
||||
internal class GroupOrganizationNotUseGroupsAutoDataAttribute : CustomAutoDataAttribute
|
||||
{
|
||||
public GroupOrganizationNotUseGroupsAutoDataAttribute() : base(
|
||||
new SutProviderCustomization(), new OrganizationCustomization { UseGroups = false })
|
||||
{ }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,176 +10,177 @@ using Bit.Core.Utilities;
|
||||
using Bit.Test.Common.AutoFixture;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
|
||||
namespace Bit.Core.Test.AutoFixture.OrganizationFixtures;
|
||||
|
||||
public class OrganizationCustomization : ICustomization
|
||||
namespace Bit.Core.Test.AutoFixture.OrganizationFixtures
|
||||
{
|
||||
public bool UseGroups { get; set; }
|
||||
|
||||
public void Customize(IFixture fixture)
|
||||
public class OrganizationCustomization : ICustomization
|
||||
{
|
||||
var organizationId = Guid.NewGuid();
|
||||
var maxConnections = (short)new Random().Next(10, short.MaxValue);
|
||||
public bool UseGroups { get; set; }
|
||||
|
||||
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<Group>(composer => composer.With(g => g.OrganizationId, organizationId));
|
||||
}
|
||||
}
|
||||
|
||||
internal class OrganizationBuilder : ISpecimenBuilder
|
||||
{
|
||||
public object Create(object request, ISpecimenContext context)
|
||||
{
|
||||
if (context == null)
|
||||
public void Customize(IFixture fixture)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
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<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));
|
||||
}
|
||||
}
|
||||
|
||||
var type = request as Type;
|
||||
if (type == null || type != typeof(Organization))
|
||||
internal class OrganizationBuilder : ISpecimenBuilder
|
||||
{
|
||||
public object Create(object request, ISpecimenContext context)
|
||||
{
|
||||
return new NoSpecimen();
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
internal class PaidOrganization : ICustomization
|
||||
{
|
||||
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
|
||||
public PlanType CheckedPlanType { get; set; }
|
||||
public void Customize(IFixture fixture)
|
||||
{
|
||||
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));
|
||||
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)
|
||||
{ }
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{ }
|
||||
}
|
||||
|
||||
@@ -2,17 +2,18 @@
|
||||
using Bit.Core.Models.Business;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
|
||||
namespace Bit.Core.Test.AutoFixture;
|
||||
|
||||
public class OrganizationLicenseCustomizeAttribute : BitCustomizeAttribute
|
||||
namespace Bit.Core.Test.AutoFixture
|
||||
{
|
||||
public override ICustomization GetCustomization() => new OrganizationLicenseCustomization();
|
||||
}
|
||||
public class OrganizationLicenseCustomization : ICustomization
|
||||
{
|
||||
public void Customize(IFixture fixture)
|
||||
public class OrganizationLicenseCustomizeAttribute : BitCustomizeAttribute
|
||||
{
|
||||
fixture.Customize<OrganizationLicense>(composer => composer
|
||||
.With(o => o.Signature, Guid.NewGuid().ToString().Replace('-', '+')));
|
||||
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('-', '+')));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,31 +2,32 @@
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
|
||||
namespace Bit.Core.Test.AutoFixture.OrganizationSponsorshipFixtures;
|
||||
|
||||
public class OrganizationSponsorshipCustomizeAttribute : BitCustomizeAttribute
|
||||
namespace Bit.Core.Test.AutoFixture.OrganizationSponsorshipFixtures
|
||||
{
|
||||
public bool ToDelete = false;
|
||||
public override ICustomization GetCustomization() => ToDelete ?
|
||||
new ToDeleteOrganizationSponsorship() :
|
||||
new ValidOrganizationSponsorship();
|
||||
}
|
||||
|
||||
public class ValidOrganizationSponsorship : ICustomization
|
||||
{
|
||||
public void Customize(IFixture fixture)
|
||||
public class OrganizationSponsorshipCustomizeAttribute : BitCustomizeAttribute
|
||||
{
|
||||
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));
|
||||
public bool ToDelete = false;
|
||||
public override ICustomization GetCustomization() => ToDelete ?
|
||||
new ToDeleteOrganizationSponsorship() :
|
||||
new ValidOrganizationSponsorship();
|
||||
}
|
||||
|
||||
public class ValidOrganizationSponsorship : ICustomization
|
||||
{
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,42 +4,43 @@ using AutoFixture.Xunit2;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
|
||||
namespace Bit.Core.Test.AutoFixture.OrganizationUserFixtures;
|
||||
|
||||
public class OrganizationUserCustomization : ICustomization
|
||||
namespace Bit.Core.Test.AutoFixture.OrganizationUserFixtures
|
||||
{
|
||||
public OrganizationUserStatusType Status { get; set; }
|
||||
public OrganizationUserType Type { get; set; }
|
||||
|
||||
public OrganizationUserCustomization(OrganizationUserStatusType status, OrganizationUserType type)
|
||||
public class OrganizationUserCustomization : ICustomization
|
||||
{
|
||||
Status = status;
|
||||
Type = 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));
|
||||
}
|
||||
}
|
||||
|
||||
public void Customize(IFixture fixture)
|
||||
public class OrganizationUserAttribute : CustomizeAttribute
|
||||
{
|
||||
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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,37 +4,38 @@ using AutoFixture.Xunit2;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
|
||||
namespace Bit.Core.Test.AutoFixture.PolicyFixtures;
|
||||
|
||||
internal class PolicyCustomization : ICustomization
|
||||
namespace Bit.Core.Test.AutoFixture.PolicyFixtures
|
||||
{
|
||||
public PolicyType Type { get; set; }
|
||||
|
||||
public PolicyCustomization(PolicyType type)
|
||||
internal class PolicyCustomization : ICustomization
|
||||
{
|
||||
Type = 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));
|
||||
}
|
||||
}
|
||||
|
||||
public void Customize(IFixture fixture)
|
||||
public class PolicyAttribute : CustomizeAttribute
|
||||
{
|
||||
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);
|
||||
private readonly PolicyType _type;
|
||||
|
||||
public PolicyAttribute(PolicyType type)
|
||||
{
|
||||
_type = type;
|
||||
}
|
||||
|
||||
public override ICustomization GetCustomization(ParameterInfo parameter)
|
||||
{
|
||||
return new PolicyCustomization(_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,62 +2,63 @@
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
|
||||
namespace Bit.Core.Test.AutoFixture.SendFixtures;
|
||||
|
||||
internal class OrganizationSend : ICustomization
|
||||
namespace Bit.Core.Test.AutoFixture.SendFixtures
|
||||
{
|
||||
public Guid? OrganizationId { get; set; }
|
||||
public void Customize(IFixture fixture)
|
||||
internal class OrganizationSend : ICustomization
|
||||
{
|
||||
fixture.Customize<Send>(composer => composer
|
||||
.With(s => s.OrganizationId, OrganizationId ?? Guid.NewGuid())
|
||||
.Without(s => s.UserId));
|
||||
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)
|
||||
{ }
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{ }
|
||||
}
|
||||
|
||||
@@ -6,48 +6,49 @@ using Bit.Core.Models;
|
||||
using Bit.Core.Test.AutoFixture.OrganizationFixtures;
|
||||
using Bit.Test.Common.AutoFixture;
|
||||
|
||||
namespace Bit.Core.Test.AutoFixture.UserFixtures;
|
||||
|
||||
public class UserBuilder : ISpecimenBuilder
|
||||
namespace Bit.Core.Test.AutoFixture.UserFixtures
|
||||
{
|
||||
public object Create(object request, ISpecimenContext context)
|
||||
public class UserBuilder : ISpecimenBuilder
|
||||
{
|
||||
if (context == null)
|
||||
public object Create(object request, ISpecimenContext context)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
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)
|
||||
if (context == null)
|
||||
{
|
||||
var providers = fixture.Create<Dictionary<TwoFactorProviderType, TwoFactorProvider>>();
|
||||
user.SetTwoFactorProviders(providers);
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
return users;
|
||||
|
||||
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 providers = fixture.Create<Dictionary<TwoFactorProviderType, TwoFactorProvider>>();
|
||||
user.SetTwoFactorProviders(providers);
|
||||
}
|
||||
return users;
|
||||
}
|
||||
|
||||
return new NoSpecimen();
|
||||
}
|
||||
|
||||
return new NoSpecimen();
|
||||
}
|
||||
}
|
||||
|
||||
public class UserFixture : ICustomization
|
||||
{
|
||||
public virtual void Customize(IFixture fixture)
|
||||
public class UserFixture : ICustomization
|
||||
{
|
||||
fixture.Customizations.Add(new GlobalSettingsBuilder());
|
||||
fixture.Customizations.Add(new UserBuilder());
|
||||
fixture.Customizations.Add(new OrganizationBuilder());
|
||||
public virtual void Customize(IFixture fixture)
|
||||
{
|
||||
fixture.Customizations.Add(new GlobalSettingsBuilder());
|
||||
fixture.Customizations.Add(new UserBuilder());
|
||||
fixture.Customizations.Add(new OrganizationBuilder());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,95 +5,96 @@ using Bit.Core.Models;
|
||||
using Bit.Test.Common.Helpers;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Entities;
|
||||
|
||||
public class OrganizationTests
|
||||
namespace Bit.Core.Test.Entities
|
||||
{
|
||||
private static readonly Dictionary<TwoFactorProviderType, TwoFactorProvider> _testConfig = new Dictionary<TwoFactorProviderType, TwoFactorProvider>()
|
||||
public class OrganizationTests
|
||||
{
|
||||
[TwoFactorProviderType.OrganizationDuo] = new TwoFactorProvider
|
||||
private static readonly Dictionary<TwoFactorProviderType, TwoFactorProvider> _testConfig = new Dictionary<TwoFactorProviderType, TwoFactorProvider>()
|
||||
{
|
||||
Enabled = true,
|
||||
MetaData = new Dictionary<string, object>
|
||||
[TwoFactorProviderType.OrganizationDuo] = new TwoFactorProvider
|
||||
{
|
||||
["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,
|
||||
Enabled = true,
|
||||
MetaData = new Dictionary<string, object>
|
||||
{
|
||||
["IKey"] = "IKey_value",
|
||||
["SKey"] = "SKey_value",
|
||||
["Host"] = "Host_value",
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
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 SetTwoFactorProviders_Success()
|
||||
{
|
||||
var organization = new Organization();
|
||||
organization.SetTwoFactorProviders(_testConfig);
|
||||
|
||||
[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);
|
||||
using var jsonDocument = JsonDocument.Parse(organization.TwoFactorProviders);
|
||||
var root = jsonDocument.RootElement;
|
||||
|
||||
// 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);
|
||||
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);
|
||||
}
|
||||
|
||||
// Actual checks
|
||||
var twoFactorProviders = organization.GetTwoFactorProviders();
|
||||
[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 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 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,140 +5,141 @@ using Bit.Core.Models;
|
||||
using Bit.Test.Common.Helpers;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Entities;
|
||||
|
||||
public class UserTests
|
||||
namespace Bit.Core.Test.Entities
|
||||
{
|
||||
// KB MB GB
|
||||
public const long Multiplier = 1024 * 1024 * 1024;
|
||||
|
||||
[Fact]
|
||||
public void StorageBytesRemaining_HasMax_DoesNotHaveStorage_ReturnsMaxAsBytes()
|
||||
public class UserTests
|
||||
{
|
||||
short maxStorageGb = 1;
|
||||
// KB MB GB
|
||||
public const long Multiplier = 1024 * 1024 * 1024;
|
||||
|
||||
var user = new User
|
||||
[Fact]
|
||||
public void StorageBytesRemaining_HasMax_DoesNotHaveStorage_ReturnsMaxAsBytes()
|
||||
{
|
||||
MaxStorageGb = maxStorageGb,
|
||||
Storage = null,
|
||||
};
|
||||
short maxStorageGb = 1;
|
||||
|
||||
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>
|
||||
var user = new User
|
||||
{
|
||||
["Item"] = "thing",
|
||||
},
|
||||
},
|
||||
[TwoFactorProviderType.Email] = new TwoFactorProvider
|
||||
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)
|
||||
{
|
||||
Enabled = false,
|
||||
MetaData = new Dictionary<string, object>
|
||||
var user = new User
|
||||
{
|
||||
["Email"] = "test@email.com",
|
||||
},
|
||||
},
|
||||
};
|
||||
MaxStorageGb = maxStorageGb,
|
||||
Storage = storageBytes,
|
||||
};
|
||||
|
||||
[Fact]
|
||||
public void SetTwoFactorProviders_Success()
|
||||
{
|
||||
var user = new User();
|
||||
user.SetTwoFactorProviders(_testTwoFactorConfig);
|
||||
var bytesRemaining = user.StorageBytesRemaining();
|
||||
|
||||
using var jsonDocument = JsonDocument.Parse(user.TwoFactorProviders);
|
||||
var root = jsonDocument.RootElement;
|
||||
Assert.Equal(expectedRemainingBytes, bytesRemaining);
|
||||
}
|
||||
|
||||
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 = 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
|
||||
private static readonly Dictionary<TwoFactorProviderType, TwoFactorProvider> _testTwoFactorConfig = new Dictionary<TwoFactorProviderType, TwoFactorProvider>
|
||||
{
|
||||
TwoFactorProviders = tempUser.TwoFactorProviders,
|
||||
[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",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
var twoFactorProviders = user.GetTwoFactorProviders();
|
||||
[Fact]
|
||||
public void SetTwoFactorProviders_Success()
|
||||
{
|
||||
var user = new User();
|
||||
user.SetTwoFactorProviders(_testTwoFactorConfig);
|
||||
|
||||
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);
|
||||
using var jsonDocument = JsonDocument.Parse(user.TwoFactorProviders);
|
||||
var root = jsonDocument.RootElement;
|
||||
|
||||
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 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);
|
||||
|
||||
[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);
|
||||
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);
|
||||
}
|
||||
|
||||
// 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 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,
|
||||
};
|
||||
|
||||
// Actual checks
|
||||
var twoFactorProviders = user.GetTwoFactorProviders();
|
||||
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 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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
using Bit.Core.Settings;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace Bit.Core.Test.Helpers.Factories;
|
||||
|
||||
public static class GlobalSettingsFactory
|
||||
namespace Bit.Core.Test.Helpers.Factories
|
||||
{
|
||||
public static GlobalSettings GlobalSettings { get; } = new();
|
||||
static GlobalSettingsFactory()
|
||||
public static class GlobalSettingsFactory
|
||||
{
|
||||
var configBuilder = new ConfigurationBuilder().AddUserSecrets("bitwarden-Api");
|
||||
var Configuration = configBuilder.Build();
|
||||
ConfigurationBinder.Bind(Configuration.GetSection("GlobalSettings"), GlobalSettings);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,34 +5,35 @@ using Bit.Test.Common.AutoFixture;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Identity;
|
||||
|
||||
public class AuthenticationTokenProviderTests : BaseTokenProviderTests<AuthenticatorTokenProvider>
|
||||
namespace Bit.Core.Test.Identity
|
||||
{
|
||||
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 class AuthenticationTokenProviderTests : BaseTokenProviderTests<AuthenticatorTokenProvider>
|
||||
{
|
||||
await base.RunCanGenerateTwoFactorTokenAsync(metaData, expectedResponse, user, 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,82 +11,83 @@ using Microsoft.Extensions.Options;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Identity;
|
||||
|
||||
[SutProviderCustomize]
|
||||
public abstract class BaseTokenProviderTests<T>
|
||||
where T : IUserTwoFactorTokenProvider<User>
|
||||
namespace Bit.Core.Test.Identity
|
||||
{
|
||||
public abstract TwoFactorProviderType TwoFactorProviderType { get; }
|
||||
|
||||
#region Helpers
|
||||
protected static IEnumerable<object[]> SetupCanGenerateData(params (Dictionary<string, object> MetaData, bool ExpectedResponse)[] data)
|
||||
[SutProviderCustomize]
|
||||
public abstract class BaseTokenProviderTests<T>
|
||||
where T : IUserTwoFactorTokenProvider<User>
|
||||
{
|
||||
return data.Select(d =>
|
||||
new object[]
|
||||
{
|
||||
d.MetaData,
|
||||
d.ExpectedResponse,
|
||||
});
|
||||
}
|
||||
public abstract TwoFactorProviderType TwoFactorProviderType { get; }
|
||||
|
||||
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>
|
||||
#region Helpers
|
||||
protected static IEnumerable<object[]> SetupCanGenerateData(params (Dictionary<string, object> MetaData, bool ExpectedResponse)[] data)
|
||||
{
|
||||
[TwoFactorProviderType] = new TwoFactorProvider
|
||||
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>
|
||||
{
|
||||
Enabled = true,
|
||||
MetaData = metaData,
|
||||
},
|
||||
};
|
||||
[TwoFactorProviderType] = new TwoFactorProvider
|
||||
{
|
||||
Enabled = true,
|
||||
MetaData = metaData,
|
||||
},
|
||||
};
|
||||
|
||||
user.TwoFactorProviders = JsonHelpers.LegacySerialize(providers);
|
||||
}
|
||||
#endregion
|
||||
user.TwoFactorProviders = JsonHelpers.LegacySerialize(providers);
|
||||
}
|
||||
#endregion
|
||||
|
||||
public virtual async Task RunCanGenerateTwoFactorTokenAsync(Dictionary<string, object> metaData, bool expectedResponse,
|
||||
User user, SutProvider<T> sutProvider)
|
||||
{
|
||||
var userManager = SubstituteUserManager();
|
||||
MockDatabase(user, metaData);
|
||||
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);
|
||||
AdditionalSetup(sutProvider, user);
|
||||
|
||||
var response = await sutProvider.Sut.CanGenerateTwoFactorTokenAsync(userManager, user);
|
||||
Assert.Equal(expectedResponse, response);
|
||||
var response = await sutProvider.Sut.CanGenerateTwoFactorTokenAsync(userManager, user);
|
||||
Assert.Equal(expectedResponse, response);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,41 +5,42 @@ using Bit.Test.Common.AutoFixture;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Identity;
|
||||
|
||||
public class EmailTokenProviderTests : BaseTokenProviderTests<EmailTokenProvider>
|
||||
namespace Bit.Core.Test.Identity
|
||||
{
|
||||
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 class EmailTokenProviderTests : BaseTokenProviderTests<EmailTokenProvider>
|
||||
{
|
||||
await base.RunCanGenerateTwoFactorTokenAsync(metaData, expectedResponse, user, 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,90 +4,91 @@ using Microsoft.Extensions.Primitives;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.IdentityServer;
|
||||
|
||||
public class TokenRetrievalTests
|
||||
namespace Bit.Core.Test.IdentityServer
|
||||
{
|
||||
private readonly Func<HttpRequest, string> _sut = TokenRetrieval.FromAuthorizationHeaderOrQueryString();
|
||||
|
||||
[Fact]
|
||||
public void RetrieveToken_FromHeader_ReturnsToken()
|
||||
public class TokenRetrievalTests
|
||||
{
|
||||
// Arrange
|
||||
var headers = new HeaderDictionary
|
||||
private readonly Func<HttpRequest, string> _sut = TokenRetrieval.FromAuthorizationHeaderOrQueryString();
|
||||
|
||||
[Fact]
|
||||
public void RetrieveToken_FromHeader_ReturnsToken()
|
||||
{
|
||||
{ "Authorization", "Bearer test_value" },
|
||||
{ "X-Test-Header", "random_value" }
|
||||
};
|
||||
// Arrange
|
||||
var headers = new HeaderDictionary
|
||||
{
|
||||
{ "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()
|
||||
{
|
||||
// Arrange
|
||||
var queryString = new Dictionary<string, StringValues>
|
||||
[Fact]
|
||||
public void RetrieveToken_FromQueryString_ReturnsToken()
|
||||
{
|
||||
{ "access_token", "test_value" },
|
||||
{ "test-query", "random_value" }
|
||||
};
|
||||
// Arrange
|
||||
var queryString = new Dictionary<string, StringValues>
|
||||
{
|
||||
{ "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()
|
||||
{
|
||||
// Arrange
|
||||
var queryString = new Dictionary<string, StringValues>
|
||||
[Fact]
|
||||
public void RetrieveToken_HasBoth_ReturnsHeaderToken()
|
||||
{
|
||||
{ "access_token", "query_string_token" },
|
||||
{ "test-query", "random_value" }
|
||||
};
|
||||
// Arrange
|
||||
var queryString = new Dictionary<string, StringValues>
|
||||
{
|
||||
{ "access_token", "query_string_token" },
|
||||
{ "test-query", "random_value" }
|
||||
};
|
||||
|
||||
var headers = new HeaderDictionary
|
||||
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()
|
||||
{
|
||||
{ "Authorization", "Bearer header_token" },
|
||||
{ "X-Test-Header", "random_value" }
|
||||
};
|
||||
// Arrange
|
||||
var request = Substitute.For<HttpRequest>();
|
||||
|
||||
var request = Substitute.For<HttpRequest>();
|
||||
request.Headers.Returns(headers);
|
||||
request.Query.Returns(new QueryCollection(queryString));
|
||||
// Act
|
||||
var token = _sut(request);
|
||||
|
||||
// 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);
|
||||
// Assert
|
||||
Assert.Null(token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,23 @@
|
||||
using Bit.Core.Models.Business;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Models.Business;
|
||||
|
||||
public class BillingInfoTests
|
||||
namespace Bit.Core.Test.Models.Business
|
||||
{
|
||||
[Fact]
|
||||
public void BillingInvoice_Amount_ShouldComeFrom_InvoiceTotal()
|
||||
public class BillingInfoTests
|
||||
{
|
||||
var invoice = new Stripe.Invoice
|
||||
[Fact]
|
||||
public void BillingInvoice_Amount_ShouldComeFrom_InvoiceTotal()
|
||||
{
|
||||
AmountDue = 1000,
|
||||
Total = 2000,
|
||||
};
|
||||
var invoice = new Stripe.Invoice
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,114 +1,115 @@
|
||||
using Bit.Core.Models.Business;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Models.Business;
|
||||
|
||||
public class TaxInfoTests
|
||||
namespace Bit.Core.Test.Models.Business
|
||||
{
|
||||
// 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)
|
||||
public class TaxInfoTests
|
||||
{
|
||||
var taxInfo = new TaxInfo
|
||||
// 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)
|
||||
{
|
||||
BillingAddressCountry = billingAddressCountry,
|
||||
TaxIdNumber = taxIdNumber,
|
||||
BillingAddressState = billingAddressState,
|
||||
};
|
||||
var taxInfo = new TaxInfo
|
||||
{
|
||||
BillingAddressCountry = billingAddressCountry,
|
||||
TaxIdNumber = taxIdNumber,
|
||||
BillingAddressState = billingAddressState,
|
||||
};
|
||||
|
||||
Assert.Equal(expectedTaxIdType, taxInfo.TaxIdType);
|
||||
}
|
||||
Assert.Equal(expectedTaxIdType, taxInfo.TaxIdType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetTaxIdType_CreateOnce_ReturnCacheSecondTime()
|
||||
{
|
||||
var taxInfo = new TaxInfo
|
||||
[Fact]
|
||||
public void GetTaxIdType_CreateOnce_ReturnCacheSecondTime()
|
||||
{
|
||||
BillingAddressCountry = "US",
|
||||
TaxIdNumber = "PH",
|
||||
BillingAddressState = null,
|
||||
};
|
||||
var taxInfo = new TaxInfo
|
||||
{
|
||||
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)
|
||||
{
|
||||
var taxInfo = new TaxInfo
|
||||
[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)
|
||||
{
|
||||
TaxIdNumber = taxIdNumber,
|
||||
BillingAddressCountry = billingAddressCountry,
|
||||
};
|
||||
var taxInfo = new TaxInfo
|
||||
{
|
||||
TaxIdNumber = taxIdNumber,
|
||||
BillingAddressCountry = billingAddressCountry,
|
||||
};
|
||||
|
||||
Assert.Equal(expected, taxInfo.HasTaxId);
|
||||
Assert.Equal(expected, taxInfo.HasTaxId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,29 +4,30 @@ using Bit.Core.Models.Business.Tokenables;
|
||||
using Bit.Core.Tokens;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Models.Business.Tokenables;
|
||||
|
||||
public class EmergencyAccessInviteTokenableTests
|
||||
namespace Bit.Core.Test.Models.Business.Tokenables
|
||||
{
|
||||
[Theory, AutoData]
|
||||
public void SerializationSetsCorrectDateTime(EmergencyAccess emergencyAccess)
|
||||
public class EmergencyAccessInviteTokenableTests
|
||||
{
|
||||
var token = new EmergencyAccessInviteTokenable(emergencyAccess, 2);
|
||||
Assert.Equal(Tokenable.FromToken<EmergencyAccessInviteTokenable>(token.ToToken().ToString()).ExpirationDate,
|
||||
token.ExpirationDate,
|
||||
TimeSpan.FromMilliseconds(10));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsInvalidIfIdentifierIsWrong()
|
||||
{
|
||||
var token = new EmergencyAccessInviteTokenable(DateTime.MaxValue)
|
||||
[Theory, AutoData]
|
||||
public void SerializationSetsCorrectDateTime(EmergencyAccess emergencyAccess)
|
||||
{
|
||||
Email = "email",
|
||||
Id = Guid.NewGuid(),
|
||||
Identifier = "not correct"
|
||||
};
|
||||
var token = new EmergencyAccessInviteTokenable(emergencyAccess, 2);
|
||||
Assert.Equal(Tokenable.FromToken<EmergencyAccessInviteTokenable>(token.ToToken().ToString()).ExpirationDate,
|
||||
token.ExpirationDate,
|
||||
TimeSpan.FromMilliseconds(10));
|
||||
}
|
||||
|
||||
Assert.False(token.Valid);
|
||||
[Fact]
|
||||
public void IsInvalidIfIdentifierIsWrong()
|
||||
{
|
||||
var token = new EmergencyAccessInviteTokenable(DateTime.MaxValue)
|
||||
{
|
||||
Email = "email",
|
||||
Id = Guid.NewGuid(),
|
||||
Identifier = "not correct"
|
||||
};
|
||||
|
||||
Assert.False(token.Valid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,83 +5,84 @@ using Bit.Core.Tokens;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Models.Business.Tokenables;
|
||||
|
||||
public class HCaptchaTokenableTests
|
||||
namespace Bit.Core.Test.Models.Business.Tokenables
|
||||
{
|
||||
[Fact]
|
||||
public void CanHandleNullUser()
|
||||
public class HCaptchaTokenableTests
|
||||
{
|
||||
var token = new HCaptchaTokenable(null);
|
||||
|
||||
Assert.Equal(default, token.Id);
|
||||
Assert.Equal(default, token.Email);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TokenWithNullUserIsInvalid()
|
||||
{
|
||||
var token = new HCaptchaTokenable(null)
|
||||
[Fact]
|
||||
public void CanHandleNullUser()
|
||||
{
|
||||
ExpirationDate = DateTime.UtcNow + TimeSpan.FromDays(1)
|
||||
};
|
||||
var token = new HCaptchaTokenable(null);
|
||||
|
||||
Assert.False(token.Valid);
|
||||
}
|
||||
Assert.Equal(default, token.Id);
|
||||
Assert.Equal(default, token.Email);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public void TokenValidityCheckNullUserIdIsInvalid(User user)
|
||||
{
|
||||
var token = new HCaptchaTokenable(user)
|
||||
[Fact]
|
||||
public void TokenWithNullUserIsInvalid()
|
||||
{
|
||||
ExpirationDate = DateTime.UtcNow + TimeSpan.FromDays(1)
|
||||
};
|
||||
var token = new HCaptchaTokenable(null)
|
||||
{
|
||||
ExpirationDate = DateTime.UtcNow + TimeSpan.FromDays(1)
|
||||
};
|
||||
|
||||
Assert.False(token.TokenIsValid(null));
|
||||
}
|
||||
Assert.False(token.Valid);
|
||||
}
|
||||
|
||||
[Theory, AutoData]
|
||||
public void CanUpdateExpirationToNonStandard(User user)
|
||||
{
|
||||
var token = new HCaptchaTokenable(user)
|
||||
[Theory, BitAutoData]
|
||||
public void TokenValidityCheckNullUserIdIsInvalid(User user)
|
||||
{
|
||||
ExpirationDate = DateTime.MinValue
|
||||
};
|
||||
var token = new HCaptchaTokenable(user)
|
||||
{
|
||||
ExpirationDate = DateTime.UtcNow + TimeSpan.FromDays(1)
|
||||
};
|
||||
|
||||
Assert.Equal(DateTime.MinValue, token.ExpirationDate, TimeSpan.FromMilliseconds(10));
|
||||
}
|
||||
Assert.False(token.TokenIsValid(null));
|
||||
}
|
||||
|
||||
[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)
|
||||
[Theory, AutoData]
|
||||
public void CanUpdateExpirationToNonStandard(User user)
|
||||
{
|
||||
ExpirationDate = expectedDateTime
|
||||
};
|
||||
var token = new HCaptchaTokenable(user)
|
||||
{
|
||||
ExpirationDate = DateTime.MinValue
|
||||
};
|
||||
|
||||
var result = Tokenable.FromToken<HCaptchaTokenable>(token.ToToken());
|
||||
Assert.Equal(DateTime.MinValue, token.ExpirationDate, TimeSpan.FromMilliseconds(10));
|
||||
}
|
||||
|
||||
Assert.Equal(expectedDateTime, result.ExpirationDate, TimeSpan.FromMilliseconds(10));
|
||||
}
|
||||
|
||||
[Theory, AutoData]
|
||||
public void IsInvalidIfIdentifierIsWrong(User user)
|
||||
{
|
||||
var token = new HCaptchaTokenable(user)
|
||||
[Theory, AutoData]
|
||||
public void SetsDataFromUser(User user)
|
||||
{
|
||||
Identifier = "not correct"
|
||||
};
|
||||
var token = new HCaptchaTokenable(user);
|
||||
|
||||
Assert.False(token.Valid);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,152 +4,153 @@ using Bit.Core.Models.Business.Tokenables;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Models.Business.Tokenables;
|
||||
|
||||
public class OrganizationSponsorshipOfferTokenableTests
|
||||
namespace Bit.Core.Test.Models.Business.Tokenables
|
||||
{
|
||||
public static IEnumerable<object[]> PlanSponsorshipTypes() => Enum.GetValues<PlanSponsorshipType>().Select(x => new object[] { x });
|
||||
|
||||
[Fact]
|
||||
public void IsInvalidIfIdentifierIsWrong()
|
||||
public class OrganizationSponsorshipOfferTokenableTests
|
||||
{
|
||||
var token = new OrganizationSponsorshipOfferTokenable()
|
||||
public static IEnumerable<object[]> PlanSponsorshipTypes() => Enum.GetValues<PlanSponsorshipType>().Select(x => new object[] { x });
|
||||
|
||||
[Fact]
|
||||
public void IsInvalidIfIdentifierIsWrong()
|
||||
{
|
||||
Email = "email",
|
||||
Id = Guid.NewGuid(),
|
||||
Identifier = "not correct",
|
||||
SponsorshipType = PlanSponsorshipType.FamiliesForEnterprise,
|
||||
};
|
||||
var token = new OrganizationSponsorshipOfferTokenable()
|
||||
{
|
||||
Email = "email",
|
||||
Id = Guid.NewGuid(),
|
||||
Identifier = "not correct",
|
||||
SponsorshipType = PlanSponsorshipType.FamiliesForEnterprise,
|
||||
};
|
||||
|
||||
Assert.False(token.Valid);
|
||||
}
|
||||
Assert.False(token.Valid);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsInvalidIfIdIsDefault()
|
||||
{
|
||||
var token = new OrganizationSponsorshipOfferTokenable()
|
||||
[Fact]
|
||||
public void IsInvalidIfIdIsDefault()
|
||||
{
|
||||
Email = "email",
|
||||
Id = default,
|
||||
SponsorshipType = PlanSponsorshipType.FamiliesForEnterprise,
|
||||
};
|
||||
var token = new OrganizationSponsorshipOfferTokenable()
|
||||
{
|
||||
Email = "email",
|
||||
Id = default,
|
||||
SponsorshipType = PlanSponsorshipType.FamiliesForEnterprise,
|
||||
};
|
||||
|
||||
Assert.False(token.Valid);
|
||||
}
|
||||
Assert.False(token.Valid);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void IsInvalidIfEmailIsEmpty()
|
||||
{
|
||||
var token = new OrganizationSponsorshipOfferTokenable()
|
||||
[Fact]
|
||||
public void IsInvalidIfEmailIsEmpty()
|
||||
{
|
||||
Email = "",
|
||||
Id = Guid.NewGuid(),
|
||||
SponsorshipType = PlanSponsorshipType.FamiliesForEnterprise,
|
||||
};
|
||||
var token = new OrganizationSponsorshipOfferTokenable()
|
||||
{
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,84 +5,85 @@ using Bit.Core.Tokens;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Models.Business.Tokenables;
|
||||
|
||||
public class SsoTokenableTests
|
||||
namespace Bit.Core.Test.Models.Business.Tokenables
|
||||
{
|
||||
[Fact]
|
||||
public void CanHandleNullOrganization()
|
||||
public class SsoTokenableTests
|
||||
{
|
||||
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)
|
||||
[Fact]
|
||||
public void CanHandleNullOrganization()
|
||||
{
|
||||
ExpirationDate = DateTime.UtcNow + TimeSpan.FromDays(1)
|
||||
};
|
||||
var token = new SsoTokenable(null, default);
|
||||
|
||||
Assert.False(token.Valid);
|
||||
}
|
||||
Assert.Equal(default, token.OrganizationId);
|
||||
Assert.Equal(default, token.DomainHint);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public void TokenValidityCheckNullOrganizationIsInvalid(Organization organization)
|
||||
{
|
||||
var token = new SsoTokenable(organization, 500)
|
||||
[Fact]
|
||||
public void TokenWithNullOrganizationIsInvalid()
|
||||
{
|
||||
ExpirationDate = DateTime.UtcNow + TimeSpan.FromDays(1)
|
||||
};
|
||||
var token = new SsoTokenable(null, 500)
|
||||
{
|
||||
ExpirationDate = DateTime.UtcNow + TimeSpan.FromDays(1)
|
||||
};
|
||||
|
||||
Assert.False(token.TokenIsValid(null));
|
||||
}
|
||||
Assert.False(token.Valid);
|
||||
}
|
||||
|
||||
[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)
|
||||
[Theory, BitAutoData]
|
||||
public void TokenValidityCheckNullOrganizationIsInvalid(Organization organization)
|
||||
{
|
||||
ExpirationDate = expectedDateTime
|
||||
};
|
||||
var token = new SsoTokenable(organization, 500)
|
||||
{
|
||||
ExpirationDate = DateTime.UtcNow + TimeSpan.FromDays(1)
|
||||
};
|
||||
|
||||
var result = Tokenable.FromToken<HCaptchaTokenable>(token.ToToken());
|
||||
Assert.False(token.TokenIsValid(null));
|
||||
}
|
||||
|
||||
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)
|
||||
[Theory, AutoData]
|
||||
public void SetsDataFromOrganization(Organization organization)
|
||||
{
|
||||
ExpirationDate = expectedDateTime
|
||||
};
|
||||
var token = new SsoTokenable(organization, default);
|
||||
|
||||
var result = token.TokenIsValid(organization);
|
||||
Assert.Equal(organization.Id, token.OrganizationId);
|
||||
Assert.Equal(organization.Identifier, token.DomainHint);
|
||||
}
|
||||
|
||||
Assert.False(result);
|
||||
[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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,15 +3,16 @@ using Bit.Core.Entities;
|
||||
using Bit.Core.Test.AutoFixture.CipherFixtures;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Models;
|
||||
|
||||
public class CipherTests
|
||||
namespace Bit.Core.Test.Models
|
||||
{
|
||||
[Theory]
|
||||
[InlineUserCipherAutoData]
|
||||
[InlineOrganizationCipherAutoData]
|
||||
public void Clone_CreatesExactCopy(Cipher cipher)
|
||||
public class CipherTests
|
||||
{
|
||||
Assert.Equal(JsonSerializer.Serialize(cipher), JsonSerializer.Serialize(cipher.Clone()));
|
||||
[Theory]
|
||||
[InlineUserCipherAutoData]
|
||||
[InlineOrganizationCipherAutoData]
|
||||
public void Clone_CreatesExactCopy(Cipher cipher)
|
||||
{
|
||||
Assert.Equal(JsonSerializer.Serialize(cipher), JsonSerializer.Serialize(cipher.Clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,25 +3,26 @@ using Bit.Core.Models.Data;
|
||||
using Bit.Test.Common.Helpers;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Models.Data;
|
||||
|
||||
public class SendFileDataTests
|
||||
namespace Bit.Core.Test.Models.Data
|
||||
{
|
||||
[Fact]
|
||||
public void Serialize_Success()
|
||||
public class SendFileDataTests
|
||||
{
|
||||
var sut = new SendFileData
|
||||
[Fact]
|
||||
public void Serialize_Success()
|
||||
{
|
||||
Id = "test",
|
||||
Size = 100,
|
||||
FileName = "thing.pdf",
|
||||
Validated = true,
|
||||
};
|
||||
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 _));
|
||||
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 _));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,58 +3,59 @@ using Bit.Core.Models.Data;
|
||||
using Bit.Core.Utilities;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Models;
|
||||
|
||||
public class PermissionsTests
|
||||
namespace Bit.Core.Test.Models
|
||||
{
|
||||
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()
|
||||
public class PermissionsTests
|
||||
{
|
||||
var permissions = new Permissions
|
||||
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()
|
||||
{
|
||||
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,
|
||||
};
|
||||
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,
|
||||
};
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,93 +7,94 @@ using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.OrganizationFeatures.OrganizationApiKeys;
|
||||
|
||||
[SutProviderCustomize]
|
||||
public class GetOrganizationApiKeyCommandTests
|
||||
namespace Bit.Core.Test.OrganizationFeatures.OrganizationApiKeys
|
||||
{
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task GetOrganizationApiKey_HasOne_Returns(SutProvider<GetOrganizationApiKeyCommand> sutProvider,
|
||||
Guid id, Guid organizationId, OrganizationApiKeyType keyType)
|
||||
[SutProviderCustomize]
|
||||
public class GetOrganizationApiKeyCommandTests
|
||||
{
|
||||
sutProvider.GetDependency<IOrganizationApiKeyRepository>()
|
||||
.GetManyByOrganizationIdTypeAsync(organizationId, keyType)
|
||||
.Returns(new List<OrganizationApiKey>
|
||||
{
|
||||
new OrganizationApiKey
|
||||
[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>
|
||||
{
|
||||
Id = id,
|
||||
OrganizationId = organizationId,
|
||||
ApiKey = "test",
|
||||
Type = keyType,
|
||||
RevisionDate = DateTime.Now.AddDays(-1),
|
||||
},
|
||||
});
|
||||
new OrganizationApiKey
|
||||
{
|
||||
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>
|
||||
{
|
||||
new 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>
|
||||
{
|
||||
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),
|
||||
},
|
||||
});
|
||||
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),
|
||||
},
|
||||
});
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,18 +5,19 @@ using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using Bit.Test.Common.Helpers;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.OrganizationFeatures.OrganizationApiKeys;
|
||||
|
||||
[SutProviderCustomize]
|
||||
public class RotateOrganizationApiKeyCommandTests
|
||||
namespace Bit.Core.Test.OrganizationFeatures.OrganizationApiKeys
|
||||
{
|
||||
[Theory, BitAutoData]
|
||||
public async Task RotateApiKeyAsync_RotatesKey(SutProvider<RotateOrganizationApiKeyCommand> sutProvider,
|
||||
OrganizationApiKey organizationApiKey)
|
||||
[SutProviderCustomize]
|
||||
public class RotateOrganizationApiKeyCommandTests
|
||||
{
|
||||
var existingKey = organizationApiKey.ApiKey;
|
||||
organizationApiKey = await sutProvider.Sut.RotateApiKeyAsync(organizationApiKey);
|
||||
Assert.NotEqual(existingKey, organizationApiKey.ApiKey);
|
||||
AssertHelper.AssertRecent(organizationApiKey.RevisionDate);
|
||||
[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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,19 +8,20 @@ using Bit.Test.Common.Helpers;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.OrganizationFeatures.OrganizationConnections;
|
||||
|
||||
[SutProviderCustomize]
|
||||
public class CreateOrganizationConnectionCommandTests
|
||||
namespace Bit.Core.Test.OrganizationFeatures.OrganizationConnections
|
||||
{
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task CreateAsync_CallsCreate(OrganizationConnectionData<BillingSyncConfig> data,
|
||||
SutProvider<CreateOrganizationConnectionCommand> sutProvider)
|
||||
[SutProviderCustomize]
|
||||
public class CreateOrganizationConnectionCommandTests
|
||||
{
|
||||
await sutProvider.Sut.CreateAsync(data);
|
||||
[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())));
|
||||
await sutProvider.GetDependency<IOrganizationConnectionRepository>().Received(1)
|
||||
.CreateAsync(Arg.Is(AssertHelper.AssertPropertyEqual(data.ToEntity())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,19 +6,20 @@ using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.OrganizationFeatures.OrganizationConnections;
|
||||
|
||||
[SutProviderCustomize]
|
||||
public class DeleteOrganizationConnectionCommandTests
|
||||
namespace Bit.Core.Test.OrganizationFeatures.OrganizationConnections
|
||||
{
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task DeleteAsync_CallsDelete(OrganizationConnection connection,
|
||||
SutProvider<DeleteOrganizationConnectionCommand> sutProvider)
|
||||
[SutProviderCustomize]
|
||||
public class DeleteOrganizationConnectionCommandTests
|
||||
{
|
||||
await sutProvider.Sut.DeleteAsync(connection);
|
||||
[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);
|
||||
await sutProvider.GetDependency<IOrganizationConnectionRepository>().Received(1)
|
||||
.DeleteAsync(connection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,49 +10,50 @@ using Bit.Test.Common.Helpers;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.OrganizationFeatures.OrganizationConnections;
|
||||
|
||||
[SutProviderCustomize]
|
||||
public class UpdateOrganizationConnectionCommandTests
|
||||
namespace Bit.Core.Test.OrganizationFeatures.OrganizationConnections
|
||||
{
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task UpdateAsync_NoId_Fails(OrganizationConnectionData<BillingSyncConfig> data,
|
||||
SutProvider<UpdateOrganizationConnectionCommand> sutProvider)
|
||||
[SutProviderCustomize]
|
||||
public class UpdateOrganizationConnectionCommandTests
|
||||
{
|
||||
data.Id = null;
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task UpdateAsync_NoId_Fails(OrganizationConnectionData<BillingSyncConfig> data,
|
||||
SutProvider<UpdateOrganizationConnectionCommand> sutProvider)
|
||||
{
|
||||
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())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,70 +4,71 @@ using Bit.Core.Services;
|
||||
using Bit.Test.Common.AutoFixture;
|
||||
using NSubstitute;
|
||||
|
||||
namespace Bit.Core.Test.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise;
|
||||
|
||||
public abstract class CancelSponsorshipCommandTestsBase : FamiliesForEnterpriseTestsBase
|
||||
namespace Bit.Core.Test.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise
|
||||
{
|
||||
protected async Task AssertRemovedSponsoredPaymentAsync<T>(Organization sponsoredOrg,
|
||||
OrganizationSponsorship sponsorship, SutProvider<T> sutProvider)
|
||||
public abstract class CancelSponsorshipCommandTestsBase : FamiliesForEnterpriseTestsBase
|
||||
{
|
||||
await sutProvider.GetDependency<IPaymentService>().Received(1)
|
||||
.RemoveOrganizationSponsorshipAsync(sponsoredOrg, sponsorship);
|
||||
await sutProvider.GetDependency<IOrganizationRepository>().Received(1).UpsertAsync(sponsoredOrg);
|
||||
if (sponsorship != null)
|
||||
protected async Task AssertRemovedSponsoredPaymentAsync<T>(Organization sponsoredOrg,
|
||||
OrganizationSponsorship sponsorship, SutProvider<T> sutProvider)
|
||||
{
|
||||
await sutProvider.GetDependency<IMailService>().Received(1)
|
||||
.SendFamiliesForEnterpriseSponsorshipRevertingEmailAsync(sponsoredOrg.BillingEmailAddress(), sponsorship.ValidUntil.GetValueOrDefault());
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,45 +6,46 @@ using Bit.Test.Common.AutoFixture;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Cloud;
|
||||
|
||||
[SutProviderCustomize]
|
||||
[OrganizationSponsorshipCustomize]
|
||||
public class CloudRevokeSponsorshipCommandTests : CancelSponsorshipCommandTestsBase
|
||||
namespace Bit.Core.Test.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Cloud
|
||||
{
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task RevokeSponsorship_NoExistingSponsorship_ThrowsBadRequest(
|
||||
SutProvider<CloudRevokeSponsorshipCommand> sutProvider)
|
||||
[SutProviderCustomize]
|
||||
[OrganizationSponsorshipCustomize]
|
||||
public class CloudRevokeSponsorshipCommandTests : CancelSponsorshipCommandTestsBase
|
||||
{
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
|
||||
sutProvider.Sut.RevokeSponsorshipAsync(null));
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task RevokeSponsorship_NoExistingSponsorship_ThrowsBadRequest(
|
||||
SutProvider<CloudRevokeSponsorshipCommand> sutProvider)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,216 +10,218 @@ using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Cloud;
|
||||
|
||||
[SutProviderCustomize]
|
||||
public class CloudSyncSponsorshipsCommandTests : FamiliesForEnterpriseTestsBase
|
||||
namespace Bit.Core.Test.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Cloud
|
||||
{
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task SyncOrganization_SponsoringOrgNotFound_ThrowsBadRequest(
|
||||
IEnumerable<OrganizationSponsorshipData> sponsorshipsData,
|
||||
SutProvider<CloudSyncSponsorshipsCommand> sutProvider)
|
||||
[SutProviderCustomize]
|
||||
public class CloudSyncSponsorshipsCommandTests : FamiliesForEnterpriseTestsBase
|
||||
{
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
|
||||
sutProvider.Sut.SyncOrganization(null, sponsorshipsData));
|
||||
|
||||
Assert.Contains("Failed to sync sponsorship - missing organization.", exception.Message);
|
||||
[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));
|
||||
|
||||
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
|
||||
.DidNotReceiveWithAnyArgs()
|
||||
.UpsertManyAsync(default);
|
||||
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
|
||||
.DidNotReceiveWithAnyArgs()
|
||||
.DeleteManyAsync(default);
|
||||
}
|
||||
Assert.Contains("Failed to sync sponsorship - missing organization.", exception.Message);
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task SyncOrganization_NoSponsorships_EarlyReturn(
|
||||
Organization organization,
|
||||
SutProvider<CloudSyncSponsorshipsCommand> sutProvider)
|
||||
{
|
||||
var result = await sutProvider.Sut.SyncOrganization(organization, Enumerable.Empty<OrganizationSponsorshipData>());
|
||||
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
|
||||
.DidNotReceiveWithAnyArgs()
|
||||
.UpsertManyAsync(default);
|
||||
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
|
||||
.DidNotReceiveWithAnyArgs()
|
||||
.DeleteManyAsync(default);
|
||||
}
|
||||
|
||||
Assert.Empty(result.Item1.SponsorshipsBatch);
|
||||
Assert.Empty(result.Item2);
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task SyncOrganization_NoSponsorships_EarlyReturn(
|
||||
Organization organization,
|
||||
SutProvider<CloudSyncSponsorshipsCommand> sutProvider)
|
||||
{
|
||||
var result = await sutProvider.Sut.SyncOrganization(organization, Enumerable.Empty<OrganizationSponsorshipData>());
|
||||
|
||||
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
|
||||
.DidNotReceiveWithAnyArgs()
|
||||
.UpsertManyAsync(default);
|
||||
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
|
||||
.DidNotReceiveWithAnyArgs()
|
||||
.DeleteManyAsync(default);
|
||||
}
|
||||
Assert.Empty(result.Item1.SponsorshipsBatch);
|
||||
Assert.Empty(result.Item2);
|
||||
|
||||
[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.GetDependency<IOrganizationSponsorshipRepository>()
|
||||
.DidNotReceiveWithAnyArgs()
|
||||
.UpsertManyAsync(default);
|
||||
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
|
||||
.DidNotReceiveWithAnyArgs()
|
||||
.DeleteManyAsync(default);
|
||||
}
|
||||
|
||||
await sutProvider.Sut.SyncOrganization(organization, sponsorshipsData);
|
||||
[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.GetDependency<IOrganizationSponsorshipRepository>()
|
||||
.DidNotReceiveWithAnyArgs()
|
||||
.UpsertManyAsync(default);
|
||||
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
|
||||
.DidNotReceiveWithAnyArgs()
|
||||
.DeleteManyAsync(default);
|
||||
}
|
||||
await sutProvider.Sut.SyncOrganization(organization, sponsorshipsData);
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task SyncOrganization_Success_RecordsEvent(Organization organization,
|
||||
SutProvider<CloudSyncSponsorshipsCommand> sutProvider)
|
||||
{
|
||||
await sutProvider.Sut.SyncOrganization(organization, Array.Empty<OrganizationSponsorshipData>());
|
||||
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
|
||||
.DidNotReceiveWithAnyArgs()
|
||||
.UpsertManyAsync(default);
|
||||
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
|
||||
.DidNotReceiveWithAnyArgs()
|
||||
.DeleteManyAsync(default);
|
||||
}
|
||||
|
||||
await sutProvider.GetDependency<IEventService>().Received(1).LogOrganizationEventAsync(organization, EventType.Organization_SponsorshipsSynced, Arg.Any<DateTime?>());
|
||||
}
|
||||
[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_OneExisting_OneNew_Success(SutProvider<CloudSyncSponsorshipsCommand> sutProvider,
|
||||
Organization sponsoringOrganization, OrganizationSponsorship existingSponsorship, OrganizationSponsorship newSponsorship)
|
||||
{
|
||||
// Arrange
|
||||
sponsoringOrganization.Enabled = true;
|
||||
sponsoringOrganization.PlanType = PlanType.EnterpriseAnnually;
|
||||
await sutProvider.GetDependency<IEventService>().Received(1).LogOrganizationEventAsync(organization, EventType.Organization_SponsorshipsSynced, Arg.Any<DateTime?>());
|
||||
}
|
||||
|
||||
existingSponsorship.ToDelete = false;
|
||||
newSponsorship.ToDelete = false;
|
||||
[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;
|
||||
|
||||
sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
|
||||
.GetManyBySponsoringOrganizationAsync(sponsoringOrganization.Id)
|
||||
.Returns(new List<OrganizationSponsorship>
|
||||
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[]
|
||||
{
|
||||
existingSponsorship,
|
||||
new OrganizationSponsorshipData(existingSponsorship),
|
||||
new OrganizationSponsorshipData(newSponsorship),
|
||||
});
|
||||
|
||||
// Act
|
||||
var (syncData, toEmailSponsorships) = await sutProvider.Sut.SyncOrganization(sponsoringOrganization, new[]
|
||||
// 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)
|
||||
{
|
||||
new OrganizationSponsorshipData(existingSponsorship),
|
||||
new OrganizationSponsorshipData(newSponsorship),
|
||||
});
|
||||
// Arrange
|
||||
sponsoringOrganization.PlanType = PlanType.EnterpriseAnnually;
|
||||
|
||||
// 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));
|
||||
canDeleteSponsorship.ToDelete = true;
|
||||
canDeleteSponsorship.SponsoredOrganizationId = null;
|
||||
|
||||
// Neither were marked as delete, should not have deleted
|
||||
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
|
||||
.DidNotReceiveWithAnyArgs()
|
||||
.DeleteManyAsync(default);
|
||||
cannotDeleteSponsorship.ToDelete = true;
|
||||
cannotDeleteSponsorship.SponsoredOrganizationId = Guid.NewGuid();
|
||||
|
||||
// Only one sponsorship was new so it should only send one
|
||||
Assert.Single(toEmailSponsorships);
|
||||
}
|
||||
sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
|
||||
.GetManyBySponsoringOrganizationAsync(sponsoringOrganization.Id)
|
||||
.Returns(new List<OrganizationSponsorship>
|
||||
{
|
||||
canDeleteSponsorship,
|
||||
cannotDeleteSponsorship,
|
||||
});
|
||||
|
||||
[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>
|
||||
// Act
|
||||
var (syncData, toEmailSponsorships) = await sutProvider.Sut.SyncOrganization(sponsoringOrganization, new[]
|
||||
{
|
||||
canDeleteSponsorship,
|
||||
cannotDeleteSponsorship,
|
||||
new OrganizationSponsorshipData(canDeleteSponsorship),
|
||||
new OrganizationSponsorshipData(cannotDeleteSponsorship),
|
||||
});
|
||||
|
||||
// Act
|
||||
var (syncData, toEmailSponsorships) = await sutProvider.Sut.SyncOrganization(sponsoringOrganization, new[]
|
||||
// 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)
|
||||
{
|
||||
new OrganizationSponsorshipData(canDeleteSponsorship),
|
||||
new OrganizationSponsorshipData(cannotDeleteSponsorship),
|
||||
});
|
||||
sponsoringOrganization.PlanType = PlanType.EnterpriseAnnually;
|
||||
|
||||
// Assert
|
||||
badOrganizationSponsorship.ToDelete = true;
|
||||
badOrganizationSponsorship.LastSyncDate = null;
|
||||
|
||||
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
|
||||
.Received(1)
|
||||
.UpsertManyAsync(Arg.Is<IEnumerable<OrganizationSponsorship>>(sponsorships => sponsorships.Count() == 2));
|
||||
sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
|
||||
.GetManyBySponsoringOrganizationAsync(sponsoringOrganization.Id)
|
||||
.Returns(new List<OrganizationSponsorship>());
|
||||
|
||||
// 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));
|
||||
}
|
||||
var (syncData, toEmailSponsorships) = await sutProvider.Sut.SyncOrganization(sponsoringOrganization, new[]
|
||||
{
|
||||
new OrganizationSponsorshipData(badOrganizationSponsorship),
|
||||
});
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task SyncOrganization_BadData_DoesNotSave(SutProvider<CloudSyncSponsorshipsCommand> sutProvider,
|
||||
Organization sponsoringOrganization, OrganizationSponsorship badOrganizationSponsorship)
|
||||
{
|
||||
sponsoringOrganization.PlanType = PlanType.EnterpriseAnnually;
|
||||
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
|
||||
.DidNotReceiveWithAnyArgs()
|
||||
.UpsertManyAsync(default);
|
||||
|
||||
badOrganizationSponsorship.ToDelete = true;
|
||||
badOrganizationSponsorship.LastSyncDate = null;
|
||||
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[]
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task SyncOrganization_OrgDisabledForFourMonths_DoesNotSave(SutProvider<CloudSyncSponsorshipsCommand> sutProvider,
|
||||
Organization sponsoringOrganization, OrganizationSponsorship organizationSponsorship)
|
||||
{
|
||||
new OrganizationSponsorshipData(badOrganizationSponsorship),
|
||||
});
|
||||
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>());
|
||||
|
||||
[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);
|
||||
var (syncData, toEmailSponsorships) = await sutProvider.Sut.SyncOrganization(sponsoringOrganization, new[]
|
||||
{
|
||||
new OrganizationSponsorshipData(organizationSponsorship),
|
||||
});
|
||||
|
||||
organizationSponsorship.ToDelete = false;
|
||||
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
|
||||
.DidNotReceiveWithAnyArgs()
|
||||
.UpsertManyAsync(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);
|
||||
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
|
||||
.DidNotReceiveWithAnyArgs()
|
||||
.DeleteManyAsync(default);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,21 +6,22 @@ using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Cloud;
|
||||
|
||||
[SutProviderCustomize]
|
||||
public class OrganizationSponsorshipRenewCommandTests
|
||||
namespace Bit.Core.Test.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Cloud
|
||||
{
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task UpdateExpirationDate_UpdatesValidUntil(OrganizationSponsorship sponsorship, DateTime expireDate,
|
||||
SutProvider<OrganizationSponsorshipRenewCommand> sutProvider)
|
||||
[SutProviderCustomize]
|
||||
public class OrganizationSponsorshipRenewCommandTests
|
||||
{
|
||||
sutProvider.GetDependency<IOrganizationSponsorshipRepository>().GetBySponsoredOrganizationIdAsync(sponsorship.SponsoredOrganizationId.Value).Returns(sponsorship);
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task UpdateExpirationDate_UpdatesValidUntil(OrganizationSponsorship sponsorship, DateTime expireDate,
|
||||
SutProvider<OrganizationSponsorshipRenewCommand> sutProvider)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,37 +6,38 @@ using Bit.Test.Common.AutoFixture;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise;
|
||||
|
||||
[SutProviderCustomize]
|
||||
[OrganizationSponsorshipCustomize]
|
||||
public class RemoveSponsorshipCommandTests : CancelSponsorshipCommandTestsBase
|
||||
namespace Bit.Core.Test.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise
|
||||
{
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task RemoveSponsorship_SponsoredOrgNull_ThrowsBadRequest(OrganizationSponsorship sponsorship,
|
||||
SutProvider<RemoveSponsorshipCommand> sutProvider)
|
||||
[SutProviderCustomize]
|
||||
[OrganizationSponsorshipCustomize]
|
||||
public class RemoveSponsorshipCommandTests : CancelSponsorshipCommandTestsBase
|
||||
{
|
||||
sponsorship.SponsoredOrganizationId = null;
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task RemoveSponsorship_SponsoredOrgNull_ThrowsBadRequest(OrganizationSponsorship sponsorship,
|
||||
SutProvider<RemoveSponsorshipCommand> sutProvider)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,114 +10,115 @@ using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise;
|
||||
|
||||
[SutProviderCustomize]
|
||||
[OrganizationSponsorshipCustomize]
|
||||
public class SendSponsorshipOfferCommandTests : FamiliesForEnterpriseTestsBase
|
||||
namespace Bit.Core.Test.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise
|
||||
{
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task SendSponsorshipOffer_SendSponsorshipOfferAsync_ExistingAccount_Success(OrganizationSponsorship sponsorship, string sponsoringOrgName, User user, SutProvider<SendSponsorshipOfferCommand> sutProvider)
|
||||
[SutProviderCustomize]
|
||||
[OrganizationSponsorshipCustomize]
|
||||
public class SendSponsorshipOfferCommandTests : FamiliesForEnterpriseTestsBase
|
||||
{
|
||||
sutProvider.GetDependency<IUserRepository>().GetByEmailAsync(sponsorship.OfferedToEmail).Returns(user);
|
||||
[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);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,85 +10,86 @@ using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Cloud;
|
||||
|
||||
[SutProviderCustomize]
|
||||
[OrganizationSponsorshipCustomize]
|
||||
public class SetUpSponsorshipCommandTests : FamiliesForEnterpriseTestsBase
|
||||
namespace Bit.Core.Test.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Cloud
|
||||
{
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task SetUpSponsorship_SponsorshipNotFound_ThrowsBadRequest(Organization org,
|
||||
SutProvider<SetUpSponsorshipCommand> sutProvider)
|
||||
[SutProviderCustomize]
|
||||
[OrganizationSponsorshipCustomize]
|
||||
public class SetUpSponsorshipCommandTests : FamiliesForEnterpriseTestsBase
|
||||
{
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
|
||||
sutProvider.Sut.SetUpSponsorshipAsync(null, org));
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task SetUpSponsorship_SponsorshipNotFound_ThrowsBadRequest(Organization org,
|
||||
SutProvider<SetUpSponsorshipCommand> sutProvider)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,50 +8,51 @@ using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Cloud;
|
||||
|
||||
[SutProviderCustomize]
|
||||
public class ValidateBillingSyncKeyCommandTests
|
||||
namespace Bit.Core.Test.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Cloud
|
||||
{
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task ValidateBillingSyncKeyAsync_NullOrganization_Throws(SutProvider<ValidateBillingSyncKeyCommand> sutProvider)
|
||||
[SutProviderCustomize]
|
||||
public class ValidateBillingSyncKeyCommandTests
|
||||
{
|
||||
await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.ValidateBillingSyncKeyAsync(null, null));
|
||||
}
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task ValidateBillingSyncKeyAsync_NullOrganization_Throws(SutProvider<ValidateBillingSyncKeyCommand> sutProvider)
|
||||
{
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,78 +9,79 @@ using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Cloud;
|
||||
|
||||
[SutProviderCustomize]
|
||||
public class ValidateRedemptionTokenCommandTests
|
||||
namespace Bit.Core.Test.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Cloud
|
||||
{
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task ValidateRedemptionTokenAsync_CannotUnprotect_ReturnsFalse(SutProvider<ValidateRedemptionTokenCommand> sutProvider,
|
||||
string encryptedString)
|
||||
[SutProviderCustomize]
|
||||
public class ValidateRedemptionTokenCommandTests
|
||||
{
|
||||
sutProvider
|
||||
.GetDependency<IDataProtectorTokenFactory<OrganizationSponsorshipOfferTokenable>>()
|
||||
.TryUnprotect(encryptedString, out _)
|
||||
.Returns(call =>
|
||||
{
|
||||
call[1] = null;
|
||||
return false;
|
||||
});
|
||||
[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;
|
||||
});
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,246 +8,247 @@ using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Cloud;
|
||||
|
||||
[SutProviderCustomize]
|
||||
[OrganizationSponsorshipCustomize]
|
||||
public class ValidateSponsorshipCommandTests : CancelSponsorshipCommandTestsBase
|
||||
namespace Bit.Core.Test.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Cloud
|
||||
{
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task ValidateSponsorshipAsync_NoSponsoredOrg_EarlyReturn(Guid sponsoredOrgId,
|
||||
SutProvider<ValidateSponsorshipCommand> sutProvider)
|
||||
[SutProviderCustomize]
|
||||
[OrganizationSponsorshipCustomize]
|
||||
public class ValidateSponsorshipCommandTests : CancelSponsorshipCommandTestsBase
|
||||
{
|
||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(sponsoredOrgId).Returns((Organization)null);
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task ValidateSponsorshipAsync_NoSponsoredOrg_EarlyReturn(Guid sponsoredOrgId,
|
||||
SutProvider<ValidateSponsorshipCommand> sutProvider)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,166 +13,167 @@ using NSubstitute.ExceptionExtensions;
|
||||
using NSubstitute.ReturnsExtensions;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise;
|
||||
|
||||
[SutProviderCustomize]
|
||||
public class CreateSponsorshipCommandTests : FamiliesForEnterpriseTestsBase
|
||||
namespace Bit.Core.Test.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise
|
||||
{
|
||||
private bool SponsorshipValidator(OrganizationSponsorship sponsorship, OrganizationSponsorship expectedSponsorship)
|
||||
[SutProviderCustomize]
|
||||
public class CreateSponsorshipCommandTests : FamiliesForEnterpriseTestsBase
|
||||
{
|
||||
try
|
||||
private bool SponsorshipValidator(OrganizationSponsorship sponsorship, OrganizationSponsorship expectedSponsorship)
|
||||
{
|
||||
AssertHelper.AssertPropertyEqual(sponsorship, expectedSponsorship, nameof(OrganizationSponsorship.Id));
|
||||
return true;
|
||||
try
|
||||
{
|
||||
AssertHelper.AssertPropertyEqual(sponsorship, expectedSponsorship, nameof(OrganizationSponsorship.Id));
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task CreateSponsorship_OfferedToNotFound_ThrowsBadRequest(OrganizationUser orgUser, SutProvider<CreateSponsorshipCommand> sutProvider)
|
||||
{
|
||||
return false;
|
||||
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_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 =>
|
||||
[Theory, BitAutoData]
|
||||
public async Task CreateSponsorship_OfferedToSelf_ThrowsBadRequest(OrganizationUser orgUser, string sponsoredEmail, User user, SutProvider<CreateSponsorshipCommand> sutProvider)
|
||||
{
|
||||
var sponsorship = callInfo.Arg<OrganizationSponsorship>();
|
||||
sponsorship.Id = sponsorshipId;
|
||||
});
|
||||
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));
|
||||
|
||||
await sutProvider.Sut.CreateSponsorshipAsync(sponsoringOrg, sponsoringOrgUser,
|
||||
PlanSponsorshipType.FamiliesForEnterprise, sponsoredEmail, friendlyName);
|
||||
Assert.Contains("Cannot offer a Families Organization Sponsorship to yourself. Choose a different email.", exception.Message);
|
||||
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>().DidNotReceiveWithAnyArgs()
|
||||
.CreateAsync(default);
|
||||
}
|
||||
|
||||
var expectedSponsorship = new OrganizationSponsorship
|
||||
[Theory, BitMemberAutoData(nameof(NonEnterprisePlanTypes))]
|
||||
public async Task CreateSponsorship_BadSponsoringOrgPlan_ThrowsBadRequest(PlanType sponsoringOrgPlan,
|
||||
Organization org, OrganizationUser orgUser, User user, SutProvider<CreateSponsorshipCommand> sutProvider)
|
||||
{
|
||||
Id = sponsorshipId,
|
||||
SponsoringOrganizationId = sponsoringOrg.Id,
|
||||
SponsoringOrganizationUserId = sponsoringOrgUser.Id,
|
||||
FriendlyName = friendlyName,
|
||||
OfferedToEmail = sponsoredEmail,
|
||||
PlanSponsorshipType = PlanSponsorshipType.FamiliesForEnterprise,
|
||||
};
|
||||
org.PlanType = sponsoringOrgPlan;
|
||||
orgUser.Status = OrganizationUserStatusType.Confirmed;
|
||||
|
||||
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>().Received(1)
|
||||
.UpsertAsync(Arg.Is<OrganizationSponsorship>(s => SponsorshipValidator(s, expectedSponsorship)));
|
||||
}
|
||||
sutProvider.GetDependency<IUserService>().GetUserByIdAsync(orgUser.UserId.Value).Returns(user);
|
||||
|
||||
[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 exception = await Assert.ThrowsAsync<BadRequestException>(() =>
|
||||
sutProvider.Sut.CreateSponsorshipAsync(org, orgUser, PlanSponsorshipType.FamiliesForEnterprise, default, default));
|
||||
|
||||
var expectedException = new Exception();
|
||||
OrganizationSponsorship createdSponsorship = null;
|
||||
sutProvider.GetDependency<IUserService>().GetUserByIdAsync(sponsoringOrgUser.UserId.Value).Returns(user);
|
||||
sutProvider.GetDependency<IOrganizationSponsorshipRepository>().UpsertAsync(default).ThrowsForAnyArgs(callInfo =>
|
||||
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)
|
||||
{
|
||||
createdSponsorship = callInfo.ArgAt<OrganizationSponsorship>(0);
|
||||
createdSponsorship.Id = Guid.NewGuid();
|
||||
return expectedException;
|
||||
});
|
||||
org.PlanType = PlanType.EnterpriseAnnually;
|
||||
orgUser.Status = statusType;
|
||||
|
||||
var actualException = await Assert.ThrowsAsync<Exception>(() =>
|
||||
sutProvider.Sut.CreateSponsorshipAsync(sponsoringOrg, sponsoringOrgUser,
|
||||
PlanSponsorshipType.FamiliesForEnterprise, sponsoredEmail, friendlyName));
|
||||
Assert.Same(expectedException, actualException);
|
||||
sutProvider.GetDependency<IUserService>().GetUserByIdAsync(orgUser.UserId.Value).Returns(user);
|
||||
|
||||
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>().Received(1)
|
||||
.DeleteAsync(createdSponsorship);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,25 @@
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
namespace Bit.Core.Test.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise;
|
||||
|
||||
public abstract class FamiliesForEnterpriseTestsBase
|
||||
namespace Bit.Core.Test.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise
|
||||
{
|
||||
public static IEnumerable<object[]> EnterprisePlanTypes =>
|
||||
Enum.GetValues<PlanType>().Where(p => StaticStore.GetPlan(p).Product == ProductType.Enterprise).Select(p => new object[] { p });
|
||||
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[]> 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 });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,47 +6,48 @@ using Bit.Test.Common.AutoFixture;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.SelfHosted;
|
||||
|
||||
[SutProviderCustomize]
|
||||
[OrganizationSponsorshipCustomize]
|
||||
public class SelfHostedRevokeSponsorshipCommandTests : CancelSponsorshipCommandTestsBase
|
||||
namespace Bit.Core.Test.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.SelfHosted
|
||||
{
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task RevokeSponsorship_NoExistingSponsorship_ThrowsBadRequest(
|
||||
SutProvider<SelfHostedRevokeSponsorshipCommand> sutProvider)
|
||||
[SutProviderCustomize]
|
||||
[OrganizationSponsorshipCustomize]
|
||||
public class SelfHostedRevokeSponsorshipCommandTests : CancelSponsorshipCommandTestsBase
|
||||
{
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
|
||||
sutProvider.Sut.RevokeSponsorshipAsync(null));
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task RevokeSponsorship_NoExistingSponsorship_ThrowsBadRequest(
|
||||
SutProvider<SelfHostedRevokeSponsorshipCommand> sutProvider)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,172 +15,174 @@ using NSubstitute;
|
||||
using RichardSzalay.MockHttp;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.SelfHosted;
|
||||
|
||||
public class SelfHostedSyncSponsorshipsCommandTests : FamiliesForEnterpriseTestsBase
|
||||
namespace Bit.Core.Test.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.SelfHosted
|
||||
{
|
||||
|
||||
public static SutProvider<SelfHostedSyncSponsorshipsCommand> GetSutProvider(bool enableCloudCommunication = true, string identityResponse = null, string apiResponse = null)
|
||||
public class SelfHostedSyncSponsorshipsCommandTests : FamiliesForEnterpriseTestsBase
|
||||
{
|
||||
var fixture = new Fixture().WithAutoNSubstitutionsAutoPopulatedProperties();
|
||||
fixture.AddMockHttp();
|
||||
|
||||
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
|
||||
public static SutProvider<SelfHostedSyncSponsorshipsCommand> GetSutProvider(bool enableCloudCommunication = true, string identityResponse = null, string apiResponse = null)
|
||||
{
|
||||
BillingSyncKey = "okslkcslkjf"
|
||||
});
|
||||
var fixture = new Fixture().WithAutoNSubstitutionsAutoPopulatedProperties();
|
||||
fixture.AddMockHttp();
|
||||
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
|
||||
sutProvider.Sut.SyncOrganization(billingSyncConnection.OrganizationId, cloudOrganizationId, billingSyncConnection));
|
||||
var settings = fixture.Create<IGlobalSettings>();
|
||||
settings.SelfHosted = true;
|
||||
settings.EnableCloudCommunication = enableCloudCommunication;
|
||||
|
||||
Assert.Contains($"Billing Sync Key disabled", exception.Message);
|
||||
var apiUri = fixture.Create<Uri>();
|
||||
var identityUri = fixture.Create<Uri>();
|
||||
settings.Installation.ApiUri.Returns(apiUri.ToString());
|
||||
settings.Installation.IdentityUri.Returns(identityUri.ToString());
|
||||
|
||||
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
|
||||
.DidNotReceiveWithAnyArgs()
|
||||
.DeleteManyAsync(default);
|
||||
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
|
||||
.DidNotReceiveWithAnyArgs()
|
||||
.UpsertManyAsync(default);
|
||||
}
|
||||
var apiHandler = new MockHttpMessageHandler();
|
||||
var identityHandler = new MockHttpMessageHandler();
|
||||
var syncUri = string.Concat(apiUri, "organization/sponsorship/sync");
|
||||
var tokenUri = string.Concat(identityUri, "connect/token");
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task SyncOrganization_BillingSyncKeyEmpty_ThrowsBadRequest(
|
||||
Guid cloudOrganizationId, OrganizationConnection billingSyncConnection)
|
||||
{
|
||||
var sutProvider = GetSutProvider();
|
||||
billingSyncConnection.Config = "";
|
||||
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 exception = await Assert.ThrowsAsync<BadRequestException>(() =>
|
||||
sutProvider.Sut.SyncOrganization(billingSyncConnection.OrganizationId, cloudOrganizationId, billingSyncConnection));
|
||||
|
||||
Assert.Contains($"No Billing Sync Key known", exception.Message);
|
||||
var apiHttp = apiHandler.ToHttpClient();
|
||||
var identityHttp = identityHandler.ToHttpClient();
|
||||
|
||||
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
|
||||
.DidNotReceiveWithAnyArgs()
|
||||
.DeleteManyAsync(default);
|
||||
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
|
||||
.DidNotReceiveWithAnyArgs()
|
||||
.UpsertManyAsync(default);
|
||||
}
|
||||
var mockHttpClientFactory = Substitute.For<IHttpClientFactory>();
|
||||
mockHttpClientFactory.CreateClient(Arg.Is("client")).Returns(apiHttp);
|
||||
mockHttpClientFactory.CreateClient(Arg.Is("identity")).Returns(identityHttp);
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task SyncOrganization_CloudCommunicationDisabled_EarlyReturn(
|
||||
Guid cloudOrganizationId, OrganizationConnection billingSyncConnection)
|
||||
{
|
||||
var sutProvider = GetSutProvider(false);
|
||||
return new SutProvider<SelfHostedSyncSponsorshipsCommand>(fixture)
|
||||
.SetDependency(settings)
|
||||
.SetDependency(mockHttpClientFactory)
|
||||
.Create();
|
||||
}
|
||||
|
||||
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
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task SyncOrganization_BillingSyncKeyDisabled_ThrowsBadRequest(
|
||||
Guid cloudOrganizationId, OrganizationConnection billingSyncConnection)
|
||||
{
|
||||
var sutProvider = GetSutProvider();
|
||||
billingSyncConnection.Enabled = false;
|
||||
billingSyncConnection.SetConfig(new BillingSyncConfig
|
||||
{
|
||||
SponsorshipsBatch = sponsorships.Select(o => new OrganizationSponsorshipData(o))
|
||||
}));
|
||||
BillingSyncKey = "okslkcslkjf"
|
||||
});
|
||||
|
||||
var sutProvider = GetSutProvider(apiResponse: syncJsonResponse);
|
||||
billingSyncConnection.SetConfig(new BillingSyncConfig
|
||||
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)
|
||||
{
|
||||
BillingSyncKey = "okslkcslkjf"
|
||||
});
|
||||
sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
|
||||
.GetManyBySponsoringOrganizationAsync(Arg.Any<Guid>()).Returns(sponsorships.ToList());
|
||||
var sutProvider = GetSutProvider();
|
||||
billingSyncConnection.Config = "";
|
||||
|
||||
await sutProvider.Sut.SyncOrganization(billingSyncConnection.OrganizationId, cloudOrganizationId, billingSyncConnection);
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(() =>
|
||||
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>>());
|
||||
}
|
||||
Assert.Contains($"No Billing Sync Key known", exception.Message);
|
||||
|
||||
[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
|
||||
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
|
||||
{
|
||||
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
|
||||
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)
|
||||
{
|
||||
BillingSyncKey = "okslkcslkjf"
|
||||
});
|
||||
sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
|
||||
.GetManyBySponsoringOrganizationAsync(Arg.Any<Guid>()).Returns(sponsorships.ToList());
|
||||
var syncJsonResponse = JsonSerializer.Serialize(new OrganizationSponsorshipSyncResponseModel(
|
||||
new OrganizationSponsorshipSyncData
|
||||
{
|
||||
SponsorshipsBatch = sponsorships.Select(o => new OrganizationSponsorshipData(o) { CloudSponsorshipRemoved = true })
|
||||
}));
|
||||
|
||||
await sutProvider.Sut.SyncOrganization(billingSyncConnection.OrganizationId, cloudOrganizationId, billingSyncConnection);
|
||||
var sutProvider = GetSutProvider(apiResponse: syncJsonResponse);
|
||||
billingSyncConnection.SetConfig(new BillingSyncConfig
|
||||
{
|
||||
BillingSyncKey = "okslkcslkjf"
|
||||
});
|
||||
sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
|
||||
.GetManyBySponsoringOrganizationAsync(Arg.Any<Guid>()).Returns(sponsorships.ToList());
|
||||
|
||||
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
|
||||
.Received(1)
|
||||
.DeleteManyAsync(Arg.Any<IEnumerable<Guid>>());
|
||||
await sutProvider.GetDependency<IOrganizationSponsorshipRepository>()
|
||||
.DidNotReceiveWithAnyArgs()
|
||||
.UpsertManyAsync(default);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,27 +1,28 @@
|
||||
using Bit.Core.Utilities;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Resources;
|
||||
|
||||
public class VerifyResources
|
||||
namespace Bit.Core.Test.Resources
|
||||
{
|
||||
[Theory]
|
||||
[MemberData(nameof(GetResources))]
|
||||
public void Resource_FoundAndReadable(string resourceName)
|
||||
public class VerifyResources
|
||||
{
|
||||
var assembly = typeof(CoreHelpers).Assembly;
|
||||
|
||||
using (var resource = assembly.GetManifestResourceStream(resourceName))
|
||||
[Theory]
|
||||
[MemberData(nameof(GetResources))]
|
||||
public void Resource_FoundAndReadable(string resourceName)
|
||||
{
|
||||
Assert.NotNull(resource);
|
||||
Assert.True(resource.CanRead);
|
||||
var assembly = typeof(CoreHelpers).Assembly;
|
||||
|
||||
using (var resource = assembly.GetManifestResourceStream(resourceName))
|
||||
{
|
||||
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" };
|
||||
}
|
||||
}
|
||||
|
||||
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" };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,79 +8,80 @@ using Microsoft.Extensions.Logging;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Services;
|
||||
|
||||
public class AmazonSesMailDeliveryServiceTests : IDisposable
|
||||
namespace Bit.Core.Test.Services
|
||||
{
|
||||
private readonly AmazonSesMailDeliveryService _sut;
|
||||
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly IWebHostEnvironment _hostingEnvironment;
|
||||
private readonly ILogger<AmazonSesMailDeliveryService> _logger;
|
||||
private readonly IAmazonSimpleEmailService _amazonSimpleEmailService;
|
||||
|
||||
public AmazonSesMailDeliveryServiceTests()
|
||||
public class AmazonSesMailDeliveryServiceTests : IDisposable
|
||||
{
|
||||
_globalSettings = new GlobalSettings
|
||||
private readonly AmazonSesMailDeliveryService _sut;
|
||||
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly IWebHostEnvironment _hostingEnvironment;
|
||||
private readonly ILogger<AmazonSesMailDeliveryService> _logger;
|
||||
private readonly IAmazonSimpleEmailService _amazonSimpleEmailService;
|
||||
|
||||
public AmazonSesMailDeliveryServiceTests()
|
||||
{
|
||||
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 =>
|
||||
_globalSettings = new GlobalSettings
|
||||
{
|
||||
Assert.False(string.IsNullOrEmpty(request.Source));
|
||||
Amazon =
|
||||
{
|
||||
AccessKeyId = "AccessKeyId-AmazonSesMailDeliveryServiceTests",
|
||||
AccessKeySecret = "AccessKeySecret-AmazonSesMailDeliveryServiceTests",
|
||||
Region = "Region-AmazonSesMailDeliveryServiceTests"
|
||||
}
|
||||
};
|
||||
|
||||
Assert.Single(request.Destination.ToAddresses);
|
||||
Assert.Equal(mailMessage.ToEmails.First(), request.Destination.ToAddresses.First());
|
||||
_hostingEnvironment = Substitute.For<IWebHostEnvironment>();
|
||||
_logger = Substitute.For<ILogger<AmazonSesMailDeliveryService>>();
|
||||
_amazonSimpleEmailService = Substitute.For<IAmazonSimpleEmailService>();
|
||||
|
||||
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);
|
||||
_sut = new AmazonSesMailDeliveryService(
|
||||
_globalSettings,
|
||||
_hostingEnvironment,
|
||||
_logger,
|
||||
_amazonSimpleEmailService
|
||||
);
|
||||
}
|
||||
|
||||
Assert.Single(request.Destination.BccAddresses);
|
||||
Assert.Equal(mailMessage.BccEmails.First(), request.Destination.BccAddresses.First());
|
||||
public void Dispose()
|
||||
{
|
||||
_sut?.Dispose();
|
||||
}
|
||||
|
||||
Assert.Contains(request.Tags, x => x.Name == "Environment");
|
||||
Assert.Contains(request.Tags, x => x.Name == "Sender");
|
||||
Assert.Contains(request.Tags, x => x.Name == "Category");
|
||||
}));
|
||||
[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");
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,73 +4,74 @@ using Bit.Core.Settings;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Services;
|
||||
|
||||
public class AmazonSqsBlockIpServiceTests : IDisposable
|
||||
namespace Bit.Core.Test.Services
|
||||
{
|
||||
private readonly AmazonSqsBlockIpService _sut;
|
||||
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly IAmazonSQS _amazonSqs;
|
||||
|
||||
public AmazonSqsBlockIpServiceTests()
|
||||
public class AmazonSqsBlockIpServiceTests : IDisposable
|
||||
{
|
||||
_globalSettings = new GlobalSettings
|
||||
private readonly AmazonSqsBlockIpService _sut;
|
||||
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly IAmazonSQS _amazonSqs;
|
||||
|
||||
public AmazonSqsBlockIpServiceTests()
|
||||
{
|
||||
Amazon =
|
||||
_globalSettings = new GlobalSettings
|
||||
{
|
||||
AccessKeyId = "AccessKeyId-AmazonSesMailDeliveryServiceTests",
|
||||
AccessKeySecret = "AccessKeySecret-AmazonSesMailDeliveryServiceTests",
|
||||
Region = "Region-AmazonSesMailDeliveryServiceTests"
|
||||
}
|
||||
};
|
||||
Amazon =
|
||||
{
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,35 +6,36 @@ using NSubstitute;
|
||||
using NSubstitute.Core;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Services;
|
||||
|
||||
[SutProviderCustomize]
|
||||
public class AppleIapServiceTests
|
||||
namespace Bit.Core.Test.Services
|
||||
{
|
||||
[Theory, BitAutoData]
|
||||
public async Task GetReceiptStatusAsync_MoreThanFourAttempts_Throws(SutProvider<AppleIapService> sutProvider)
|
||||
[SutProviderCustomize]
|
||||
public class AppleIapServiceTests
|
||||
{
|
||||
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)
|
||||
[Theory, BitAutoData]
|
||||
public async Task GetReceiptStatusAsync_MoreThanFourAttempts_Throws(SutProvider<AppleIapService> sutProvider)
|
||||
{
|
||||
if (call.GetMethodInfo().Name != "Log")
|
||||
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)
|
||||
{
|
||||
return false;
|
||||
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");
|
||||
}
|
||||
|
||||
var args = call.GetArguments();
|
||||
var logLevel = (LogLevel)args[0];
|
||||
var exception = (Exception)args[3];
|
||||
|
||||
return logLevel == LogLevel.Warning && exception.Message.Contains("null");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,28 +4,29 @@ using Microsoft.Extensions.Logging;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Services;
|
||||
|
||||
public class AzureAttachmentStorageServiceTests
|
||||
namespace Bit.Core.Test.Services
|
||||
{
|
||||
private readonly AzureAttachmentStorageService _sut;
|
||||
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly ILogger<AzureAttachmentStorageService> _logger;
|
||||
|
||||
public AzureAttachmentStorageServiceTests()
|
||||
public class AzureAttachmentStorageServiceTests
|
||||
{
|
||||
_globalSettings = new GlobalSettings();
|
||||
_logger = Substitute.For<ILogger<AzureAttachmentStorageService>>();
|
||||
private readonly AzureAttachmentStorageService _sut;
|
||||
|
||||
_sut = new AzureAttachmentStorageService(_globalSettings, _logger);
|
||||
}
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly ILogger<AzureAttachmentStorageService> _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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,26 +2,27 @@
|
||||
using Bit.Core.Settings;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Services;
|
||||
|
||||
public class AzureQueueBlockIpServiceTests
|
||||
namespace Bit.Core.Test.Services
|
||||
{
|
||||
private readonly AzureQueueBlockIpService _sut;
|
||||
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
|
||||
public AzureQueueBlockIpServiceTests()
|
||||
public class AzureQueueBlockIpServiceTests
|
||||
{
|
||||
_globalSettings = new GlobalSettings();
|
||||
private readonly AzureQueueBlockIpService _sut;
|
||||
|
||||
_sut = new AzureQueueBlockIpService(_globalSettings);
|
||||
}
|
||||
private readonly GlobalSettings _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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,30 +4,31 @@ using Bit.Core.Settings;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Services;
|
||||
|
||||
public class AzureQueueEventWriteServiceTests
|
||||
namespace Bit.Core.Test.Services
|
||||
{
|
||||
private readonly AzureQueueEventWriteService _sut;
|
||||
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly IEventRepository _eventRepository;
|
||||
|
||||
public AzureQueueEventWriteServiceTests()
|
||||
public class AzureQueueEventWriteServiceTests
|
||||
{
|
||||
_globalSettings = new GlobalSettings();
|
||||
_eventRepository = Substitute.For<IEventRepository>();
|
||||
private readonly AzureQueueEventWriteService _sut;
|
||||
|
||||
_sut = new AzureQueueEventWriteService(
|
||||
_globalSettings
|
||||
);
|
||||
}
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly IEventRepository _eventRepository;
|
||||
|
||||
// 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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,31 +4,32 @@ using Microsoft.AspNetCore.Http;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Services;
|
||||
|
||||
public class AzureQueuePushNotificationServiceTests
|
||||
namespace Bit.Core.Test.Services
|
||||
{
|
||||
private readonly AzureQueuePushNotificationService _sut;
|
||||
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
|
||||
public AzureQueuePushNotificationServiceTests()
|
||||
public class AzureQueuePushNotificationServiceTests
|
||||
{
|
||||
_globalSettings = new GlobalSettings();
|
||||
_httpContextAccessor = Substitute.For<IHttpContextAccessor>();
|
||||
private readonly AzureQueuePushNotificationService _sut;
|
||||
|
||||
_sut = new AzureQueuePushNotificationService(
|
||||
_globalSettings,
|
||||
_httpContextAccessor
|
||||
);
|
||||
}
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly IHttpContextAccessor _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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,208 +9,209 @@ using Core.Models.Data;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Services;
|
||||
|
||||
public class CipherServiceTests
|
||||
namespace Bit.Core.Test.Services
|
||||
{
|
||||
[Theory, UserCipherAutoData]
|
||||
public async Task SaveAsync_WrongRevisionDate_Throws(SutProvider<CipherService> sutProvider, Cipher cipher)
|
||||
public class CipherServiceTests
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
[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);
|
||||
}
|
||||
|
||||
[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);
|
||||
}
|
||||
|
||||
[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]
|
||||
public async Task SaveAsync_WrongRevisionDate_Throws(SutProvider<CipherService> sutProvider, Cipher cipher)
|
||||
{
|
||||
cipher.RevisionDate = previousRevisionDate;
|
||||
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 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)
|
||||
[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);
|
||||
}
|
||||
|
||||
[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);
|
||||
}
|
||||
|
||||
[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.Equal(revisionDate, cipher.RevisionDate);
|
||||
Assert.NotEqual(initialRevisionDate, 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
|
||||
[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)
|
||||
{
|
||||
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
|
||||
var previousRevisionDate = DateTime.UtcNow;
|
||||
foreach (var cipher in ciphers)
|
||||
{
|
||||
PlanType = Enums.PlanType.EnterpriseAnnually,
|
||||
MaxStorageGb = 100
|
||||
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
|
||||
});
|
||||
ciphers.FirstOrDefault().Attachments =
|
||||
"{\"attachment1\":{\"Size\":\"250\",\"FileName\":\"superCoolFile\","
|
||||
+ "\"Key\":\"superCoolFile\",\"ContainerName\":\"testContainer\",\"Validated\":false}}";
|
||||
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));
|
||||
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()));
|
||||
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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,160 +10,161 @@ using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Services;
|
||||
|
||||
public class CollectionServiceTest
|
||||
namespace Bit.Core.Test.Services
|
||||
{
|
||||
[Theory, CollectionAutoData]
|
||||
public async Task SaveAsync_DefaultId_CreatesCollectionInTheRepository(Collection collection, Organization organization, SutProvider<CollectionService> sutProvider)
|
||||
public class CollectionServiceTest
|
||||
{
|
||||
collection.Id = default;
|
||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
||||
var utcNow = DateTime.UtcNow;
|
||||
[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;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,32 +5,33 @@ using Bit.Core.Services;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Services;
|
||||
|
||||
public class DeviceServiceTests
|
||||
namespace Bit.Core.Test.Services
|
||||
{
|
||||
[Fact]
|
||||
public async Task DeviceSaveShouldUpdateRevisionDateAndPushRegistration()
|
||||
public class DeviceServiceTests
|
||||
{
|
||||
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
|
||||
[Fact]
|
||||
public async Task DeviceSaveShouldUpdateRevisionDateAndPushRegistration()
|
||||
{
|
||||
Id = id,
|
||||
Name = "test device",
|
||||
Type = DeviceType.Android,
|
||||
UserId = userId,
|
||||
PushToken = "testtoken",
|
||||
Identifier = "testid"
|
||||
};
|
||||
await deviceService.SaveAsync(device);
|
||||
var deviceRepo = Substitute.For<IDeviceRepository>();
|
||||
var pushRepo = Substitute.For<IPushRegistrationService>();
|
||||
var deviceService = new DeviceService(deviceRepo, pushRepo);
|
||||
|
||||
Assert.True(device.RevisionDate - DateTime.UtcNow < TimeSpan.FromSeconds(1));
|
||||
await pushRepo.Received().CreateOrUpdateRegistrationAsync("testtoken", id.ToString(),
|
||||
userId.ToString(), "testid", DeviceType.Android);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,162 +9,163 @@ using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Services;
|
||||
|
||||
public class EmergencyAccessServiceTests
|
||||
namespace Bit.Core.Test.Services
|
||||
{
|
||||
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
|
||||
public async Task SaveAsync_PremiumCannotUpdate(
|
||||
SutProvider<EmergencyAccessService> sutProvider, User savingUser)
|
||||
public class EmergencyAccessServiceTests
|
||||
{
|
||||
savingUser.Premium = false;
|
||||
var emergencyAccess = new EmergencyAccess
|
||||
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
|
||||
public async Task SaveAsync_PremiumCannotUpdate(
|
||||
SutProvider<EmergencyAccessService> sutProvider, User savingUser)
|
||||
{
|
||||
Type = Enums.EmergencyAccessType.Takeover,
|
||||
GrantorId = savingUser.Id,
|
||||
};
|
||||
|
||||
sutProvider.GetDependency<IUserService>().GetUserByIdAsync(savingUser.Id).Returns(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);
|
||||
}
|
||||
|
||||
[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
|
||||
{
|
||||
Status = Enums.EmergencyAccessStatusType.Accepted,
|
||||
GrantorId = confirmingUser.Id,
|
||||
Type = Enums.EmergencyAccessType.Takeover,
|
||||
};
|
||||
|
||||
sutProvider.GetDependency<IUserRepository>().GetByIdAsync(confirmingUser.Id).Returns(confirmingUser);
|
||||
sutProvider.GetDependency<IEmergencyAccessRepository>().GetByIdAsync(Arg.Any<Guid>()).Returns(emergencyAccess);
|
||||
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
||||
() => sutProvider.Sut.ConfirmUserAsync(new Guid(), key, confirmingUser.Id));
|
||||
|
||||
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
|
||||
{
|
||||
Type = Enums.EmergencyAccessType.Takeover,
|
||||
GrantorId = savingUser.Id,
|
||||
};
|
||||
|
||||
var userService = sutProvider.GetDependency<IUserService>();
|
||||
userService.GetUserByIdAsync(savingUser.Id).Returns(savingUser);
|
||||
userService.CanAccessPremium(savingUser).Returns(true);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
|
||||
public async Task InitiateAsync_UserWithKeyConnectorCannotUseTakeover(
|
||||
SutProvider<EmergencyAccessService> sutProvider, User initiatingUser, User grantor)
|
||||
{
|
||||
grantor.UsesKeyConnector = true;
|
||||
var emergencyAccess = new EmergencyAccess
|
||||
{
|
||||
Status = Enums.EmergencyAccessStatusType.Confirmed,
|
||||
GranteeId = initiatingUser.Id,
|
||||
GrantorId = grantor.Id,
|
||||
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.InitiateAsync(new Guid(), initiatingUser));
|
||||
|
||||
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 TakeoverAsync_UserWithKeyConnectorCannotUseTakeover(
|
||||
SutProvider<EmergencyAccessService> sutProvider, User requestingUser, User grantor)
|
||||
{
|
||||
grantor.UsesKeyConnector = true;
|
||||
var emergencyAccess = new EmergencyAccess
|
||||
{
|
||||
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)
|
||||
{
|
||||
grantor.UsesKeyConnector = true;
|
||||
grantor.UnknownDeviceVerificationEnabled = true;
|
||||
grantor.SetTwoFactorProviders(new Dictionary<TwoFactorProviderType, TwoFactorProvider>
|
||||
{
|
||||
[TwoFactorProviderType.Email] = new TwoFactorProvider
|
||||
savingUser.Premium = false;
|
||||
var emergencyAccess = new EmergencyAccess
|
||||
{
|
||||
MetaData = new Dictionary<string, object> { ["Email"] = "asdfasf" },
|
||||
Enabled = true
|
||||
}
|
||||
});
|
||||
var emergencyAccess = new EmergencyAccess
|
||||
Type = Enums.EmergencyAccessType.Takeover,
|
||||
GrantorId = savingUser.Id,
|
||||
};
|
||||
|
||||
sutProvider.GetDependency<IUserService>().GetUserByIdAsync(savingUser.Id).Returns(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);
|
||||
}
|
||||
|
||||
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
|
||||
public async Task InviteAsync_UserWithKeyConnectorCannotUseTakeover(
|
||||
SutProvider<EmergencyAccessService> sutProvider, User invitingUser, string email, int waitTime)
|
||||
{
|
||||
GrantorId = grantor.Id,
|
||||
GranteeId = requestingUser.Id,
|
||||
Status = Enums.EmergencyAccessStatusType.RecoveryApproved,
|
||||
Type = Enums.EmergencyAccessType.Takeover,
|
||||
};
|
||||
invitingUser.UsesKeyConnector = true;
|
||||
sutProvider.GetDependency<IUserService>().CanAccessPremium(invitingUser).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.InviteAsync(invitingUser, email, Enums.EmergencyAccessType.Takeover, waitTime));
|
||||
|
||||
await sutProvider.Sut.PasswordAsync(Guid.NewGuid(), requestingUser, "blablahash", "blablakey");
|
||||
Assert.Contains("You cannot use Emergency Access Takeover because you are using Key Connector", exception.Message);
|
||||
await sutProvider.GetDependency<IEmergencyAccessRepository>().DidNotReceiveWithAnyArgs().CreateAsync(default);
|
||||
}
|
||||
|
||||
Assert.False(grantor.UnknownDeviceVerificationEnabled);
|
||||
Assert.Empty(grantor.GetTwoFactorProviders());
|
||||
await sutProvider.GetDependency<IUserRepository>().Received().ReplaceAsync(grantor);
|
||||
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
|
||||
public async Task ConfirmUserAsync_UserWithKeyConnectorCannotUseTakeover(
|
||||
SutProvider<EmergencyAccessService> sutProvider, User confirmingUser, string key)
|
||||
{
|
||||
confirmingUser.UsesKeyConnector = true;
|
||||
var emergencyAccess = new EmergencyAccess
|
||||
{
|
||||
Status = Enums.EmergencyAccessStatusType.Accepted,
|
||||
GrantorId = confirmingUser.Id,
|
||||
Type = Enums.EmergencyAccessType.Takeover,
|
||||
};
|
||||
|
||||
sutProvider.GetDependency<IUserRepository>().GetByIdAsync(confirmingUser.Id).Returns(confirmingUser);
|
||||
sutProvider.GetDependency<IEmergencyAccessRepository>().GetByIdAsync(Arg.Any<Guid>()).Returns(emergencyAccess);
|
||||
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
||||
() => sutProvider.Sut.ConfirmUserAsync(new Guid(), key, confirmingUser.Id));
|
||||
|
||||
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
|
||||
{
|
||||
Type = Enums.EmergencyAccessType.Takeover,
|
||||
GrantorId = savingUser.Id,
|
||||
};
|
||||
|
||||
var userService = sutProvider.GetDependency<IUserService>();
|
||||
userService.GetUserByIdAsync(savingUser.Id).Returns(savingUser);
|
||||
userService.CanAccessPremium(savingUser).Returns(true);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
|
||||
public async Task InitiateAsync_UserWithKeyConnectorCannotUseTakeover(
|
||||
SutProvider<EmergencyAccessService> sutProvider, User initiatingUser, User grantor)
|
||||
{
|
||||
grantor.UsesKeyConnector = true;
|
||||
var emergencyAccess = new EmergencyAccess
|
||||
{
|
||||
Status = Enums.EmergencyAccessStatusType.Confirmed,
|
||||
GranteeId = initiatingUser.Id,
|
||||
GrantorId = grantor.Id,
|
||||
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.InitiateAsync(new Guid(), initiatingUser));
|
||||
|
||||
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 TakeoverAsync_UserWithKeyConnectorCannotUseTakeover(
|
||||
SutProvider<EmergencyAccessService> sutProvider, User requestingUser, User grantor)
|
||||
{
|
||||
grantor.UsesKeyConnector = true;
|
||||
var emergencyAccess = new EmergencyAccess
|
||||
{
|
||||
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)
|
||||
{
|
||||
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,
|
||||
};
|
||||
|
||||
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");
|
||||
|
||||
Assert.False(grantor.UnknownDeviceVerificationEnabled);
|
||||
Assert.Empty(grantor.GetTwoFactorProviders());
|
||||
await sutProvider.GetDependency<IUserRepository>().Received().ReplaceAsync(grantor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,98 +11,99 @@ using Bit.Test.Common.Helpers;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Services;
|
||||
|
||||
[SutProviderCustomize]
|
||||
public class EventServiceTests
|
||||
namespace Bit.Core.Test.Services
|
||||
{
|
||||
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)
|
||||
[SutProviderCustomize]
|
||||
public class EventServiceTests
|
||||
{
|
||||
organization.Enabled = true;
|
||||
organization.UseEvents = true;
|
||||
public static IEnumerable<object[]> InstallationIdTestCases => TestCaseHelper.GetCombinationsOfMultipleLists(
|
||||
new object[] { Guid.NewGuid(), null },
|
||||
Enum.GetValues<EventType>().Select(e => (object)e)
|
||||
).Select(p => p.ToArray());
|
||||
|
||||
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>()
|
||||
[Theory]
|
||||
[BitMemberAutoData(nameof(InstallationIdTestCases))]
|
||||
public async Task LogOrganizationEvent_ProvidesInstallationId(Guid? installationId, EventType eventType,
|
||||
Organization organization, SutProvider<EventService> sutProvider)
|
||||
{
|
||||
{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);
|
||||
organization.Enabled = true;
|
||||
organization.UseEvents = true;
|
||||
|
||||
await sutProvider.Sut.LogOrganizationUserEventAsync(orgUser, eventType, date);
|
||||
sutProvider.GetDependency<ICurrentContext>().InstallationId.Returns(installationId);
|
||||
|
||||
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.Sut.LogOrganizationEventAsync(organization, eventType);
|
||||
|
||||
await sutProvider.GetDependency<IEventWriteService>().Received(1).CreateManyAsync(Arg.Is(AssertHelper.AssertPropertyEqual<IEvent>(expected, new[] { "IdempotencyId" })));
|
||||
}
|
||||
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 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>()
|
||||
[Theory, BitAutoData]
|
||||
public async Task LogOrganizationUserEvent_LogsRequiredInfo(OrganizationUser orgUser, EventType eventType, DateTime date,
|
||||
Guid actingUserId, Guid providerId, string ipAddress, DeviceType deviceType, SutProvider<EventService> sutProvider)
|
||||
{
|
||||
{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()
|
||||
var orgAbilities = new Dictionary<Guid, OrganizationAbility>()
|
||||
{
|
||||
IpAddress = ipAddress,
|
||||
DeviceType = deviceType,
|
||||
ProviderId = providerUser.ProviderId,
|
||||
UserId = providerUser.UserId,
|
||||
ProviderUserId = providerUser.Id,
|
||||
Type = eventType,
|
||||
ActingUserId = actingUserId,
|
||||
Date = date
|
||||
}
|
||||
};
|
||||
{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);
|
||||
|
||||
await sutProvider.GetDependency<IEventWriteService>().Received(1).CreateManyAsync(Arg.Is(AssertHelper.AssertPropertyEqual<IEvent>(expected, new[] { "IdempotencyId" })));
|
||||
await sutProvider.Sut.LogOrganizationUserEventAsync(orgUser, eventType, date);
|
||||
|
||||
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)
|
||||
{
|
||||
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()
|
||||
{
|
||||
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" })));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,127 +10,128 @@ using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Services;
|
||||
|
||||
public class GroupServiceTests
|
||||
namespace Bit.Core.Test.Services
|
||||
{
|
||||
[Theory, GroupOrganizationAutoData]
|
||||
public async Task SaveAsync_DefaultGroupId_CreatesGroupInRepository(Group group, Organization organization, SutProvider<GroupService> sutProvider)
|
||||
public class GroupServiceTests
|
||||
{
|
||||
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_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;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,168 +8,169 @@ using Microsoft.Extensions.Logging;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Services;
|
||||
|
||||
public class HandlebarsMailServiceTests
|
||||
namespace Bit.Core.Test.Services
|
||||
{
|
||||
private readonly HandlebarsMailService _sut;
|
||||
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly IMailDeliveryService _mailDeliveryService;
|
||||
private readonly IMailEnqueuingService _mailEnqueuingService;
|
||||
|
||||
public HandlebarsMailServiceTests()
|
||||
public class HandlebarsMailServiceTests
|
||||
{
|
||||
_globalSettings = new GlobalSettings();
|
||||
_mailDeliveryService = Substitute.For<IMailDeliveryService>();
|
||||
_mailEnqueuingService = Substitute.For<IMailEnqueuingService>();
|
||||
private readonly HandlebarsMailService _sut;
|
||||
|
||||
_sut = new HandlebarsMailService(
|
||||
_globalSettings,
|
||||
_mailDeliveryService,
|
||||
_mailEnqueuingService
|
||||
);
|
||||
}
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly IMailDeliveryService _mailDeliveryService;
|
||||
private readonly IMailEnqueuingService _mailEnqueuingService;
|
||||
|
||||
[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>
|
||||
public HandlebarsMailServiceTests()
|
||||
{
|
||||
// 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(),
|
||||
_globalSettings = new GlobalSettings();
|
||||
_mailDeliveryService = Substitute.For<IMailDeliveryService>();
|
||||
_mailEnqueuingService = Substitute.For<IMailEnqueuingService>();
|
||||
|
||||
}},
|
||||
{ ("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);
|
||||
_sut = new HandlebarsMailService(
|
||||
_globalSettings,
|
||||
_mailDeliveryService,
|
||||
_mailEnqueuingService
|
||||
);
|
||||
}
|
||||
|
||||
async Task InvokeMethod(MethodInfo method)
|
||||
[Fact(Skip = "For local development")]
|
||||
public async Task SendAllEmails()
|
||||
{
|
||||
var parameters = method.GetParameters();
|
||||
var args = new object[parameters.Length];
|
||||
|
||||
for (var i = 0; i < parameters.Length; i++)
|
||||
// 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>
|
||||
{
|
||||
if (!namedParameters.TryGetValue((parameters[i].Name, parameters[i].ParameterType), out var value))
|
||||
// TODO: Swith to use env variable
|
||||
{ ("email", typeof(string)), "test@bitwarden.com" },
|
||||
{ ("user", typeof(User)), new User
|
||||
{
|
||||
throw new InvalidOperationException($"Couldn't find a parameter for name '{parameters[i].Name}' and type '{parameters[i].ParameterType.FullName}'");
|
||||
}
|
||||
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(),
|
||||
|
||||
args[i] = value;
|
||||
}},
|
||||
{ ("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);
|
||||
}
|
||||
|
||||
await (Task)method.Invoke(handlebarsService, args);
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,28 +3,29 @@ using Bit.Core.Services;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Services;
|
||||
|
||||
public class InMemoryApplicationCacheServiceTests
|
||||
namespace Bit.Core.Test.Services
|
||||
{
|
||||
private readonly InMemoryApplicationCacheService _sut;
|
||||
|
||||
private readonly IOrganizationRepository _organizationRepository;
|
||||
private readonly IProviderRepository _providerRepository;
|
||||
|
||||
public InMemoryApplicationCacheServiceTests()
|
||||
public class InMemoryApplicationCacheServiceTests
|
||||
{
|
||||
_organizationRepository = Substitute.For<IOrganizationRepository>();
|
||||
_providerRepository = Substitute.For<IProviderRepository>();
|
||||
private readonly InMemoryApplicationCacheService _sut;
|
||||
|
||||
_sut = new InMemoryApplicationCacheService(_organizationRepository, _providerRepository);
|
||||
}
|
||||
private readonly IOrganizationRepository _organizationRepository;
|
||||
private readonly IProviderRepository _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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,34 +4,35 @@ using Bit.Core.Settings;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Services;
|
||||
|
||||
public class InMemoryServiceBusApplicationCacheServiceTests
|
||||
namespace Bit.Core.Test.Services
|
||||
{
|
||||
private readonly InMemoryServiceBusApplicationCacheService _sut;
|
||||
|
||||
private readonly IOrganizationRepository _organizationRepository;
|
||||
private readonly IProviderRepository _providerRepository;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
|
||||
public InMemoryServiceBusApplicationCacheServiceTests()
|
||||
public class InMemoryServiceBusApplicationCacheServiceTests
|
||||
{
|
||||
_organizationRepository = Substitute.For<IOrganizationRepository>();
|
||||
_providerRepository = Substitute.For<IProviderRepository>();
|
||||
_globalSettings = new GlobalSettings();
|
||||
private readonly InMemoryServiceBusApplicationCacheService _sut;
|
||||
|
||||
_sut = new InMemoryServiceBusApplicationCacheService(
|
||||
_organizationRepository,
|
||||
_providerRepository,
|
||||
_globalSettings
|
||||
);
|
||||
}
|
||||
private readonly IOrganizationRepository _organizationRepository;
|
||||
private readonly IProviderRepository _providerRepository;
|
||||
private readonly GlobalSettings _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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,52 +9,53 @@ using Bit.Test.Common.AutoFixture;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Services;
|
||||
|
||||
[SutProviderCustomize]
|
||||
public class LicensingServiceTests
|
||||
namespace Bit.Core.Test.Services
|
||||
{
|
||||
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(() =>
|
||||
[SutProviderCustomize]
|
||||
public class LicensingServiceTests
|
||||
{
|
||||
var directory = Path.Combine(Path.GetTempPath(), "organization");
|
||||
if (!Directory.Exists(directory))
|
||||
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(() =>
|
||||
{
|
||||
Directory.CreateDirectory(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();
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
[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
|
||||
[Theory, BitAutoData, OrganizationLicenseCustomize]
|
||||
public async Task ReadOrganizationLicense(Organization organization, OrganizationLicense license)
|
||||
{
|
||||
Assert.Equal(JsonSerializer.Serialize(license), JsonSerializer.Serialize(actual));
|
||||
}
|
||||
finally
|
||||
{
|
||||
Directory.Delete(OrganizationLicenseDirectory.Value, true);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,75 +11,194 @@ using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Services;
|
||||
|
||||
public class LocalAttachmentStorageServiceTests
|
||||
namespace Bit.Core.Test.Services
|
||||
{
|
||||
|
||||
private void AssertFileCreation(string expectedPath, string expectedFileContents)
|
||||
public class LocalAttachmentStorageServiceTests
|
||||
{
|
||||
Assert.True(File.Exists(expectedPath));
|
||||
Assert.Equal(expectedFileContents, File.ReadAllText(expectedPath));
|
||||
}
|
||||
|
||||
[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())
|
||||
private void AssertFileCreation(string expectedPath, string expectedFileContents)
|
||||
{
|
||||
var sutProvider = GetSutProvider(tempDirectory);
|
||||
|
||||
await sutProvider.Sut.UploadNewAttachmentAsync(new MemoryStream(Encoding.UTF8.GetBytes(stream)),
|
||||
cipher, attachmentData);
|
||||
|
||||
AssertFileCreation($"{tempDirectory}/{cipher.Id}/{attachmentData.AttachmentId}", stream);
|
||||
Assert.True(File.Exists(expectedPath));
|
||||
Assert.Equal(expectedFileContents, File.ReadAllText(expectedPath));
|
||||
}
|
||||
}
|
||||
|
||||
[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())
|
||||
[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)
|
||||
{
|
||||
var sutProvider = GetSutProvider(tempDirectory);
|
||||
using (var tempDirectory = new TempDirectory())
|
||||
{
|
||||
var sutProvider = GetSutProvider(tempDirectory);
|
||||
|
||||
await sutProvider.Sut.UploadShareAttachmentAsync(new MemoryStream(Encoding.UTF8.GetBytes(stream)),
|
||||
cipher.Id, cipher.OrganizationId.Value, attachmentData);
|
||||
await sutProvider.Sut.UploadNewAttachmentAsync(new MemoryStream(Encoding.UTF8.GetBytes(stream)),
|
||||
cipher, attachmentData);
|
||||
|
||||
AssertFileCreation($"{tempDirectory}/temp/{cipher.Id}/{cipher.OrganizationId}/{attachmentData.AttachmentId}", stream);
|
||||
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 StartShareAttachmentAsync_NoSource_NoWork(Cipher cipher, CipherAttachment.MetaData attachmentData)
|
||||
{
|
||||
using (var tempDirectory = new TempDirectory())
|
||||
[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)
|
||||
{
|
||||
var sutProvider = GetSutProvider(tempDirectory);
|
||||
using (var tempDirectory = new TempDirectory())
|
||||
{
|
||||
var sutProvider = GetSutProvider(tempDirectory);
|
||||
|
||||
await sutProvider.Sut.StartShareAttachmentAsync(cipher.Id, cipher.OrganizationId.Value, attachmentData);
|
||||
await sutProvider.Sut.UploadShareAttachmentAsync(new MemoryStream(Encoding.UTF8.GetBytes(stream)),
|
||||
cipher.Id, cipher.OrganizationId.Value, attachmentData);
|
||||
|
||||
Assert.False(File.Exists($"{tempDirectory}/{cipher.Id}/{attachmentData.AttachmentId}"));
|
||||
Assert.False(File.Exists($"{tempDirectory}/{cipher.Id}/{attachmentData.AttachmentId}"));
|
||||
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 StartShareAttachmentAsync_NoDest_NoWork(string source, Cipher cipher, CipherAttachment.MetaData attachmentData)
|
||||
{
|
||||
using (var tempDirectory = new TempDirectory())
|
||||
[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)
|
||||
{
|
||||
var sutProvider = GetSutProvider(tempDirectory);
|
||||
|
||||
@@ -87,144 +206,26 @@ public class LocalAttachmentStorageServiceTests
|
||||
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.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));
|
||||
Assert.True(File.Exists(destPath));
|
||||
Assert.Equal(source, File.ReadAllText(destPath));
|
||||
Assert.True(File.Exists(rollBackPath));
|
||||
Assert.Equal(destOriginal, File.ReadAllText(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())
|
||||
private SutProvider<LocalAttachmentStorageService> GetSutProvider(TempDirectory tempDirectory)
|
||||
{
|
||||
var sutProvider = GetSutProvider(tempDirectory);
|
||||
var fixture = new Fixture().WithAutoNSubstitutions();
|
||||
fixture.Freeze<IGlobalSettings>().Attachment.BaseDirectory.Returns(tempDirectory.Directory);
|
||||
fixture.Freeze<IGlobalSettings>().Attachment.BaseUrl.Returns(Guid.NewGuid().ToString());
|
||||
|
||||
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));
|
||||
return new SutProvider<LocalAttachmentStorageService>(fixture).Create();
|
||||
}
|
||||
}
|
||||
|
||||
[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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,34 +4,35 @@ using Microsoft.Extensions.Logging;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Services;
|
||||
|
||||
public class MailKitSmtpMailDeliveryServiceTests
|
||||
namespace Bit.Core.Test.Services
|
||||
{
|
||||
private readonly MailKitSmtpMailDeliveryService _sut;
|
||||
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly ILogger<MailKitSmtpMailDeliveryService> _logger;
|
||||
|
||||
public MailKitSmtpMailDeliveryServiceTests()
|
||||
public class MailKitSmtpMailDeliveryServiceTests
|
||||
{
|
||||
_globalSettings = new GlobalSettings();
|
||||
_logger = Substitute.For<ILogger<MailKitSmtpMailDeliveryService>>();
|
||||
private readonly MailKitSmtpMailDeliveryService _sut;
|
||||
|
||||
_globalSettings.Mail.Smtp.Host = "unittests.example.com";
|
||||
_globalSettings.Mail.ReplyToEmail = "noreply@unittests.example.com";
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly ILogger<MailKitSmtpMailDeliveryService> _logger;
|
||||
|
||||
_sut = new MailKitSmtpMailDeliveryService(
|
||||
_globalSettings,
|
||||
_logger
|
||||
);
|
||||
}
|
||||
public MailKitSmtpMailDeliveryServiceTests()
|
||||
{
|
||||
_globalSettings = new GlobalSettings();
|
||||
_logger = Substitute.For<ILogger<MailKitSmtpMailDeliveryService>>();
|
||||
|
||||
// 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);
|
||||
_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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,49 +6,50 @@ using Microsoft.Extensions.Logging;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Services;
|
||||
|
||||
public class MultiServicePushNotificationServiceTests
|
||||
namespace Bit.Core.Test.Services
|
||||
{
|
||||
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()
|
||||
public class 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>>();
|
||||
private readonly MultiServicePushNotificationService _sut;
|
||||
|
||||
_sut = new MultiServicePushNotificationService(
|
||||
_httpFactory,
|
||||
_deviceRepository,
|
||||
_installationDeviceRepository,
|
||||
_globalSettings,
|
||||
_httpContextAccessor,
|
||||
_logger,
|
||||
_relayLogger,
|
||||
_hubLogger
|
||||
);
|
||||
}
|
||||
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;
|
||||
|
||||
// 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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,34 +5,35 @@ using Microsoft.AspNetCore.Http;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Services;
|
||||
|
||||
public class NotificationHubPushNotificationServiceTests
|
||||
namespace Bit.Core.Test.Services
|
||||
{
|
||||
private readonly NotificationHubPushNotificationService _sut;
|
||||
|
||||
private readonly IInstallationDeviceRepository _installationDeviceRepository;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
|
||||
public NotificationHubPushNotificationServiceTests()
|
||||
public class NotificationHubPushNotificationServiceTests
|
||||
{
|
||||
_installationDeviceRepository = Substitute.For<IInstallationDeviceRepository>();
|
||||
_globalSettings = new GlobalSettings();
|
||||
_httpContextAccessor = Substitute.For<IHttpContextAccessor>();
|
||||
private readonly NotificationHubPushNotificationService _sut;
|
||||
|
||||
_sut = new NotificationHubPushNotificationService(
|
||||
_installationDeviceRepository,
|
||||
_globalSettings,
|
||||
_httpContextAccessor
|
||||
);
|
||||
}
|
||||
private readonly IInstallationDeviceRepository _installationDeviceRepository;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly IHttpContextAccessor _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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,31 +4,32 @@ using Bit.Core.Settings;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Services;
|
||||
|
||||
public class NotificationHubPushRegistrationServiceTests
|
||||
namespace Bit.Core.Test.Services
|
||||
{
|
||||
private readonly NotificationHubPushRegistrationService _sut;
|
||||
|
||||
private readonly IInstallationDeviceRepository _installationDeviceRepository;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
|
||||
public NotificationHubPushRegistrationServiceTests()
|
||||
public class NotificationHubPushRegistrationServiceTests
|
||||
{
|
||||
_installationDeviceRepository = Substitute.For<IInstallationDeviceRepository>();
|
||||
_globalSettings = new GlobalSettings();
|
||||
private readonly NotificationHubPushRegistrationService _sut;
|
||||
|
||||
_sut = new NotificationHubPushRegistrationService(
|
||||
_installationDeviceRepository,
|
||||
_globalSettings
|
||||
);
|
||||
}
|
||||
private readonly IInstallationDeviceRepository _installationDeviceRepository;
|
||||
private readonly GlobalSettings _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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,37 +5,38 @@ using Microsoft.Extensions.Logging;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Services;
|
||||
|
||||
public class NotificationsApiPushNotificationServiceTests
|
||||
namespace Bit.Core.Test.Services
|
||||
{
|
||||
private readonly NotificationsApiPushNotificationService _sut;
|
||||
|
||||
private readonly IHttpClientFactory _httpFactory;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
private readonly ILogger<NotificationsApiPushNotificationService> _logger;
|
||||
|
||||
public NotificationsApiPushNotificationServiceTests()
|
||||
public class NotificationsApiPushNotificationServiceTests
|
||||
{
|
||||
_httpFactory = Substitute.For<IHttpClientFactory>();
|
||||
_globalSettings = new GlobalSettings();
|
||||
_httpContextAccessor = Substitute.For<IHttpContextAccessor>();
|
||||
_logger = Substitute.For<ILogger<NotificationsApiPushNotificationService>>();
|
||||
private readonly NotificationsApiPushNotificationService _sut;
|
||||
|
||||
_sut = new NotificationsApiPushNotificationService(
|
||||
_httpFactory,
|
||||
_globalSettings,
|
||||
_httpContextAccessor,
|
||||
_logger
|
||||
);
|
||||
}
|
||||
private readonly IHttpClientFactory _httpFactory;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
private readonly ILogger<NotificationsApiPushNotificationService> _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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -10,391 +10,392 @@ using NSubstitute;
|
||||
using Xunit;
|
||||
using PolicyFixtures = Bit.Core.Test.AutoFixture.PolicyFixtures;
|
||||
|
||||
namespace Bit.Core.Test.Services;
|
||||
|
||||
[SutProviderCustomize]
|
||||
public class PolicyServiceTests
|
||||
namespace Bit.Core.Test.Services
|
||||
{
|
||||
[Theory, BitAutoData]
|
||||
public async Task SaveAsync_OrganizationDoesNotExist_ThrowsBadRequest(
|
||||
[PolicyFixtures.Policy(PolicyType.DisableSend)] Policy policy, SutProvider<PolicyService> sutProvider)
|
||||
[SutProviderCustomize]
|
||||
public class PolicyServiceTests
|
||||
{
|
||||
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
|
||||
[Theory, BitAutoData]
|
||||
public async Task SaveAsync_OrganizationDoesNotExist_ThrowsBadRequest(
|
||||
[PolicyFixtures.Policy(PolicyType.DisableSend)] Policy policy, SutProvider<PolicyService> sutProvider)
|
||||
{
|
||||
UsePolicies = false,
|
||||
});
|
||||
SetupOrg(sutProvider, policy.OrganizationId, null);
|
||||
|
||||
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("cannot use policies", badRequestException.Message, StringComparison.OrdinalIgnoreCase);
|
||||
Assert.Contains("Organization not found", 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_SingleOrg_RequireSsoEnabled_ThrowsBadRequest(
|
||||
[PolicyFixtures.Policy(PolicyType.SingleOrg)] Policy policy, SutProvider<PolicyService> sutProvider)
|
||||
{
|
||||
policy.Enabled = false;
|
||||
|
||||
SetupOrg(sutProvider, policy.OrganizationId, new Organization
|
||||
[Theory, BitAutoData]
|
||||
public async Task SaveAsync_OrganizationCannotUsePolicies_ThrowsBadRequest(
|
||||
[PolicyFixtures.Policy(PolicyType.DisableSend)] Policy policy, SutProvider<PolicyService> sutProvider)
|
||||
{
|
||||
Id = policy.OrganizationId,
|
||||
UsePolicies = true,
|
||||
});
|
||||
var orgId = Guid.NewGuid();
|
||||
|
||||
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
|
||||
SetupOrg(sutProvider, policy.OrganizationId, new Organization
|
||||
{
|
||||
Id = policy.Id,
|
||||
Type = PolicyType.TwoFactorAuthentication,
|
||||
Enabled = false,
|
||||
UsePolicies = 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(),
|
||||
};
|
||||
var badRequestException = await Assert.ThrowsAsync<BadRequestException>(
|
||||
() => sutProvider.Sut.SaveAsync(policy,
|
||||
Substitute.For<IUserService>(),
|
||||
Substitute.For<IOrganizationService>(),
|
||||
Guid.NewGuid()));
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetManyDetailsByOrganizationAsync(policy.OrganizationId)
|
||||
.Returns(new List<Core.Models.Data.Organizations.OrganizationUsers.OrganizationUserUserDetails>
|
||||
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)
|
||||
{
|
||||
policy.Enabled = false;
|
||||
|
||||
SetupOrg(sutProvider, policy.OrganizationId, new Organization
|
||||
{
|
||||
orgUserDetail,
|
||||
Id = policy.OrganizationId,
|
||||
UsePolicies = true,
|
||||
});
|
||||
|
||||
var userService = Substitute.For<IUserService>();
|
||||
var organizationService = Substitute.For<IOrganizationService>();
|
||||
sutProvider.GetDependency<IPolicyRepository>()
|
||||
.GetByOrganizationIdTypeAsync(policy.OrganizationId, PolicyType.RequireSso)
|
||||
.Returns(Task.FromResult(new Policy { Enabled = true }));
|
||||
|
||||
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("Single Sign-On Authentication policy 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);
|
||||
await sutProvider.GetDependency<IEventService>()
|
||||
.DidNotReceiveWithAnyArgs()
|
||||
.LogPolicyEventAsync(default, default, default);
|
||||
}
|
||||
|
||||
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
|
||||
[Theory, BitAutoData]
|
||||
public async Task SaveAsync_SingleOrg_VaultTimeoutEnabled_ThrowsBadRequest([PolicyFixtures.Policy(Enums.PolicyType.SingleOrg)] Policy policy, SutProvider<PolicyService> sutProvider)
|
||||
{
|
||||
Id = policy.OrganizationId,
|
||||
UsePolicies = true,
|
||||
Name = "TEST",
|
||||
};
|
||||
policy.Enabled = false;
|
||||
|
||||
SetupOrg(sutProvider, policy.OrganizationId, org);
|
||||
|
||||
sutProvider.GetDependency<IPolicyRepository>()
|
||||
.GetByIdAsync(policy.Id)
|
||||
.Returns(new Policy
|
||||
SetupOrg(sutProvider, policy.OrganizationId, new Organization
|
||||
{
|
||||
Id = policy.Id,
|
||||
Type = PolicyType.SingleOrg,
|
||||
Enabled = false,
|
||||
Id = policy.OrganizationId,
|
||||
UsePolicies = true,
|
||||
});
|
||||
|
||||
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<IPolicyRepository>()
|
||||
.GetByOrganizationIdTypeAsync(policy.OrganizationId, Enums.PolicyType.MaximumVaultTimeout)
|
||||
.Returns(new Policy { Enabled = true });
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetManyDetailsByOrganizationAsync(policy.OrganizationId)
|
||||
.Returns(new List<Core.Models.Data.Organizations.OrganizationUsers.OrganizationUserUserDetails>
|
||||
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
|
||||
{
|
||||
orgUserDetail,
|
||||
Id = policy.OrganizationId,
|
||||
UsePolicies = true,
|
||||
});
|
||||
|
||||
var userService = Substitute.For<IUserService>();
|
||||
var organizationService = Substitute.For<IOrganizationService>();
|
||||
var ssoConfig = new SsoConfig { Enabled = true };
|
||||
var data = new SsoConfigurationData { KeyConnectorEnabled = true };
|
||||
ssoConfig.SetData(data);
|
||||
|
||||
userService.TwoFactorIsEnabledAsync(orgUserDetail)
|
||||
.Returns(false);
|
||||
sutProvider.GetDependency<ISsoConfigRepository>()
|
||||
.GetByOrganizationIdAsync(policy.OrganizationId)
|
||||
.Returns(ssoConfig);
|
||||
|
||||
var utcNow = DateTime.UtcNow;
|
||||
var badRequestException = await Assert.ThrowsAsync<BadRequestException>(
|
||||
() => sutProvider.Sut.SaveAsync(policy,
|
||||
Substitute.For<IUserService>(),
|
||||
Substitute.For<IOrganizationService>(),
|
||||
Guid.NewGuid()));
|
||||
|
||||
var savingUserId = Guid.NewGuid();
|
||||
Assert.Contains("Key Connector is enabled.", badRequestException.Message, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
await sutProvider.Sut.SaveAsync(policy, userService, organizationService, savingUserId);
|
||||
await sutProvider.GetDependency<IPolicyRepository>()
|
||||
.DidNotReceiveWithAnyArgs()
|
||||
.UpsertAsync(default);
|
||||
}
|
||||
|
||||
await sutProvider.GetDependency<IEventService>().Received()
|
||||
.LogPolicyEventAsync(policy, EventType.Policy_Updated);
|
||||
[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<IPolicyRepository>().Received()
|
||||
.UpsertAsync(policy);
|
||||
SetupOrg(sutProvider, policy.OrganizationId, new Organization
|
||||
{
|
||||
Id = policy.OrganizationId,
|
||||
UsePolicies = true,
|
||||
});
|
||||
|
||||
Assert.True(policy.CreationDate - utcNow < TimeSpan.FromSeconds(1));
|
||||
Assert.True(policy.RevisionDate - utcNow < TimeSpan.FromSeconds(1));
|
||||
}
|
||||
sutProvider.GetDependency<IPolicyRepository>()
|
||||
.GetByOrganizationIdTypeAsync(policy.OrganizationId, PolicyType.SingleOrg)
|
||||
.Returns(Task.FromResult(new Policy { Enabled = false }));
|
||||
|
||||
private static void SetupOrg(SutProvider<PolicyService> sutProvider, Guid organizationId, Organization organization)
|
||||
{
|
||||
sutProvider.GetDependency<IOrganizationRepository>()
|
||||
.GetByIdAsync(organizationId)
|
||||
.Returns(Task.FromResult(organization));
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,40 +6,41 @@ using Microsoft.Extensions.Logging;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Services;
|
||||
|
||||
public class RelayPushNotificationServiceTests
|
||||
namespace Bit.Core.Test.Services
|
||||
{
|
||||
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()
|
||||
public class RelayPushNotificationServiceTests
|
||||
{
|
||||
_httpFactory = Substitute.For<IHttpClientFactory>();
|
||||
_deviceRepository = Substitute.For<IDeviceRepository>();
|
||||
_globalSettings = new GlobalSettings();
|
||||
_httpContextAccessor = Substitute.For<IHttpContextAccessor>();
|
||||
_logger = Substitute.For<ILogger<RelayPushNotificationService>>();
|
||||
private readonly RelayPushNotificationService _sut;
|
||||
|
||||
_sut = new RelayPushNotificationService(
|
||||
_httpFactory,
|
||||
_deviceRepository,
|
||||
_globalSettings,
|
||||
_httpContextAccessor,
|
||||
_logger
|
||||
);
|
||||
}
|
||||
private readonly IHttpClientFactory _httpFactory;
|
||||
private readonly IDeviceRepository _deviceRepository;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
private readonly ILogger<RelayPushNotificationService> _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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,34 +4,35 @@ using Microsoft.Extensions.Logging;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Services;
|
||||
|
||||
public class RelayPushRegistrationServiceTests
|
||||
namespace Bit.Core.Test.Services
|
||||
{
|
||||
private readonly RelayPushRegistrationService _sut;
|
||||
|
||||
private readonly IHttpClientFactory _httpFactory;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly ILogger<RelayPushRegistrationService> _logger;
|
||||
|
||||
public RelayPushRegistrationServiceTests()
|
||||
public class RelayPushRegistrationServiceTests
|
||||
{
|
||||
_globalSettings = new GlobalSettings();
|
||||
_httpFactory = Substitute.For<IHttpClientFactory>();
|
||||
_logger = Substitute.For<ILogger<RelayPushRegistrationService>>();
|
||||
private readonly RelayPushRegistrationService _sut;
|
||||
|
||||
_sut = new RelayPushRegistrationService(
|
||||
_httpFactory,
|
||||
_globalSettings,
|
||||
_logger
|
||||
);
|
||||
}
|
||||
private readonly IHttpClientFactory _httpFactory;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly ILogger<RelayPushRegistrationService> _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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,26 +3,27 @@ using Bit.Core.Services;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Services;
|
||||
|
||||
public class RepositoryEventWriteServiceTests
|
||||
namespace Bit.Core.Test.Services
|
||||
{
|
||||
private readonly RepositoryEventWriteService _sut;
|
||||
|
||||
private readonly IEventRepository _eventRepository;
|
||||
|
||||
public RepositoryEventWriteServiceTests()
|
||||
public class RepositoryEventWriteServiceTests
|
||||
{
|
||||
_eventRepository = Substitute.For<IEventRepository>();
|
||||
private readonly RepositoryEventWriteService _sut;
|
||||
|
||||
_sut = new RepositoryEventWriteService(_eventRepository);
|
||||
}
|
||||
private readonly IEventRepository _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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,77 +8,78 @@ using SendGrid;
|
||||
using SendGrid.Helpers.Mail;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Services;
|
||||
|
||||
public class SendGridMailDeliveryServiceTests : IDisposable
|
||||
namespace Bit.Core.Test.Services
|
||||
{
|
||||
private readonly SendGridMailDeliveryService _sut;
|
||||
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly IWebHostEnvironment _hostingEnvironment;
|
||||
private readonly ILogger<SendGridMailDeliveryService> _logger;
|
||||
private readonly ISendGridClient _sendGridClient;
|
||||
|
||||
public SendGridMailDeliveryServiceTests()
|
||||
public class SendGridMailDeliveryServiceTests : IDisposable
|
||||
{
|
||||
_globalSettings = new GlobalSettings
|
||||
private readonly SendGridMailDeliveryService _sut;
|
||||
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly IWebHostEnvironment _hostingEnvironment;
|
||||
private readonly ILogger<SendGridMailDeliveryService> _logger;
|
||||
private readonly ISendGridClient _sendGridClient;
|
||||
|
||||
public SendGridMailDeliveryServiceTests()
|
||||
{
|
||||
Mail =
|
||||
_globalSettings = new GlobalSettings
|
||||
{
|
||||
SendGridApiKey = "SendGridApiKey"
|
||||
}
|
||||
};
|
||||
Mail =
|
||||
{
|
||||
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()
|
||||
{
|
||||
_sut?.Dispose();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SendEmailAsync_CallsSendEmailAsync_WhenMessageIsValid()
|
||||
{
|
||||
var mailMessage = new MailMessage
|
||||
public void Dispose()
|
||||
{
|
||||
ToEmails = new List<string> { "ToEmails" },
|
||||
BccEmails = new List<string> { "BccEmails" },
|
||||
Subject = "Subject",
|
||||
HtmlContent = "HtmlContent",
|
||||
TextContent = "TextContent",
|
||||
Category = "Category"
|
||||
};
|
||||
_sut?.Dispose();
|
||||
}
|
||||
|
||||
_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 =>
|
||||
[Fact]
|
||||
public async Task SendEmailAsync_CallsSendEmailAsync_WhenMessageIsValid()
|
||||
{
|
||||
var mailMessage = new MailMessage
|
||||
{
|
||||
msg.Received(1).AddTos(new List<EmailAddress> { new EmailAddress(mailMessage.ToEmails.First()) });
|
||||
msg.Received(1).AddBccs(new List<EmailAddress> { new EmailAddress(mailMessage.ToEmails.First()) });
|
||||
ToEmails = new List<string> { "ToEmails" },
|
||||
BccEmails = new List<string> { "BccEmails" },
|
||||
Subject = "Subject",
|
||||
HtmlContent = "HtmlContent",
|
||||
TextContent = "TextContent",
|
||||
Category = "Category"
|
||||
};
|
||||
|
||||
Assert.Equal(mailMessage.Subject, msg.Subject);
|
||||
Assert.Equal(mailMessage.HtmlContent, msg.HtmlContent);
|
||||
Assert.Equal(mailMessage.TextContent, msg.PlainTextContent);
|
||||
_sendGridClient.SendEmailAsync(Arg.Any<SendGridMessage>()).Returns(
|
||||
new Response(System.Net.HttpStatusCode.OK, null, null));
|
||||
await _sut.SendEmailAsync(mailMessage);
|
||||
|
||||
Assert.Contains("type:Cateogry", msg.Categories);
|
||||
Assert.Contains(msg.Categories, x => x.StartsWith("env:"));
|
||||
Assert.Contains(msg.Categories, x => x.StartsWith("sender:"));
|
||||
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()) });
|
||||
|
||||
msg.Received(1).SetClickTracking(false, false);
|
||||
msg.Received(1).SetOpenTracking(false);
|
||||
}));
|
||||
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);
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -9,308 +9,309 @@ using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Services;
|
||||
|
||||
public class SsoConfigServiceTests
|
||||
namespace Bit.Core.Test.Services
|
||||
{
|
||||
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
|
||||
public async Task SaveAsync_ExistingItem_UpdatesRevisionDateOnly(SutProvider<SsoConfigService> sutProvider,
|
||||
Organization organization)
|
||||
public class SsoConfigServiceTests
|
||||
{
|
||||
var utcNow = DateTime.UtcNow;
|
||||
|
||||
var ssoConfig = new SsoConfig
|
||||
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
|
||||
public async Task SaveAsync_ExistingItem_UpdatesRevisionDateOnly(SutProvider<SsoConfigService> sutProvider,
|
||||
Organization organization)
|
||||
{
|
||||
Id = 1,
|
||||
Data = "{}",
|
||||
Enabled = true,
|
||||
OrganizationId = organization.Id,
|
||||
CreationDate = utcNow.AddDays(-10),
|
||||
RevisionDate = utcNow.AddDays(-10),
|
||||
};
|
||||
var utcNow = DateTime.UtcNow;
|
||||
|
||||
sutProvider.GetDependency<ISsoConfigRepository>()
|
||||
.UpsertAsync(ssoConfig).Returns(Task.CompletedTask);
|
||||
|
||||
await sutProvider.Sut.SaveAsync(ssoConfig, organization);
|
||||
|
||||
await sutProvider.GetDependency<ISsoConfigRepository>().Received()
|
||||
.UpsertAsync(ssoConfig);
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
|
||||
await sutProvider.Sut.SaveAsync(ssoConfig, organization);
|
||||
|
||||
await sutProvider.GetDependency<ISsoConfigRepository>().Received()
|
||||
.UpsertAsync(ssoConfig);
|
||||
|
||||
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;
|
||||
|
||||
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 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 utcNow = DateTime.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 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 = false } });
|
||||
|
||||
await sutProvider.Sut.SaveAsync(newSsoConfig, organization);
|
||||
}
|
||||
|
||||
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
|
||||
public async Task SaveAsync_KeyConnector_SingleOrgNotEnabled_Throws(SutProvider<SsoConfigService> sutProvider,
|
||||
Organization organization)
|
||||
{
|
||||
var utcNow = DateTime.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 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 utcNow = DateTime.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),
|
||||
};
|
||||
|
||||
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));
|
||||
|
||||
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 utcNow = DateTime.UtcNow;
|
||||
|
||||
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
|
||||
var ssoConfig = new SsoConfig
|
||||
{
|
||||
Id = 1,
|
||||
Data = "{}",
|
||||
Enabled = true,
|
||||
});
|
||||
OrganizationId = organization.Id,
|
||||
CreationDate = utcNow.AddDays(-10),
|
||||
RevisionDate = utcNow.AddDays(-10),
|
||||
};
|
||||
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
||||
() => sutProvider.Sut.SaveAsync(ssoConfig, organization));
|
||||
sutProvider.GetDependency<ISsoConfigRepository>()
|
||||
.UpsertAsync(ssoConfig).Returns(Task.CompletedTask);
|
||||
|
||||
Assert.Contains("Organization cannot use Key Connector.", exception.Message);
|
||||
await sutProvider.Sut.SaveAsync(ssoConfig, organization);
|
||||
|
||||
await sutProvider.GetDependency<ISsoConfigRepository>().DidNotReceiveWithAnyArgs()
|
||||
.UpsertAsync(default);
|
||||
}
|
||||
await sutProvider.GetDependency<ISsoConfigRepository>().Received()
|
||||
.UpsertAsync(ssoConfig);
|
||||
|
||||
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
|
||||
public async Task SaveAsync_KeyConnector_Success(SutProvider<SsoConfigService> sutProvider,
|
||||
Organization organization)
|
||||
{
|
||||
var utcNow = DateTime.UtcNow;
|
||||
Assert.Equal(utcNow.AddDays(-10), ssoConfig.CreationDate);
|
||||
Assert.True(ssoConfig.RevisionDate - utcNow < TimeSpan.FromSeconds(1));
|
||||
}
|
||||
|
||||
organization.UseKeyConnector = true;
|
||||
var ssoConfig = new SsoConfig
|
||||
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
|
||||
public async Task SaveAsync_NewItem_UpdatesCreationAndRevisionDate(SutProvider<SsoConfigService> sutProvider,
|
||||
Organization organization)
|
||||
{
|
||||
Id = default,
|
||||
Data = new SsoConfigurationData
|
||||
{
|
||||
KeyConnectorEnabled = true,
|
||||
}.Serialize(),
|
||||
Enabled = true,
|
||||
OrganizationId = organization.Id,
|
||||
CreationDate = utcNow.AddDays(-10),
|
||||
RevisionDate = utcNow.AddDays(-10),
|
||||
};
|
||||
var utcNow = DateTime.UtcNow;
|
||||
|
||||
sutProvider.GetDependency<IPolicyRepository>().GetByOrganizationIdTypeAsync(
|
||||
Arg.Any<Guid>(), Arg.Any<Enums.PolicyType>()).Returns(new Policy
|
||||
var ssoConfig = new SsoConfig
|
||||
{
|
||||
Id = default,
|
||||
Data = "{}",
|
||||
Enabled = true,
|
||||
});
|
||||
OrganizationId = organization.Id,
|
||||
CreationDate = utcNow.AddDays(-10),
|
||||
RevisionDate = utcNow.AddDays(-10),
|
||||
};
|
||||
|
||||
await sutProvider.Sut.SaveAsync(ssoConfig, organization);
|
||||
sutProvider.GetDependency<ISsoConfigRepository>()
|
||||
.UpsertAsync(ssoConfig).Returns(Task.CompletedTask);
|
||||
|
||||
await sutProvider.GetDependency<ISsoConfigRepository>().ReceivedWithAnyArgs()
|
||||
.UpsertAsync(default);
|
||||
await sutProvider.Sut.SaveAsync(ssoConfig, organization);
|
||||
|
||||
await sutProvider.GetDependency<ISsoConfigRepository>().Received()
|
||||
.UpsertAsync(ssoConfig);
|
||||
|
||||
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;
|
||||
|
||||
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 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 utcNow = DateTime.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 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 = false } });
|
||||
|
||||
await sutProvider.Sut.SaveAsync(newSsoConfig, organization);
|
||||
}
|
||||
|
||||
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
|
||||
public async Task SaveAsync_KeyConnector_SingleOrgNotEnabled_Throws(SutProvider<SsoConfigService> sutProvider,
|
||||
Organization organization)
|
||||
{
|
||||
var utcNow = DateTime.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 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 utcNow = DateTime.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),
|
||||
};
|
||||
|
||||
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));
|
||||
|
||||
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 utcNow = DateTime.UtcNow;
|
||||
|
||||
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
|
||||
{
|
||||
Enabled = true,
|
||||
});
|
||||
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
||||
() => sutProvider.Sut.SaveAsync(ssoConfig, organization));
|
||||
|
||||
Assert.Contains("Organization cannot use Key Connector.", exception.Message);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,362 +12,363 @@ using NSubstitute;
|
||||
using Xunit;
|
||||
using PaymentMethodType = Bit.Core.Enums.PaymentMethodType;
|
||||
|
||||
namespace Bit.Core.Test.Services;
|
||||
|
||||
public class StripePaymentServiceTests
|
||||
namespace Bit.Core.Test.Services
|
||||
{
|
||||
[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)
|
||||
public class StripePaymentServiceTests
|
||||
{
|
||||
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
|
||||
[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)
|
||||
{
|
||||
Id = "C-1",
|
||||
});
|
||||
stripeAdapter.SubscriptionCreateAsync(default).ReturnsForAnyArgs(new Stripe.Subscription
|
||||
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 = "S-1",
|
||||
CurrentPeriodEnd = DateTime.Today.AddDays(10),
|
||||
});
|
||||
var plan = StaticStore.Plans.First(p => p.Type == PlanType.EnterpriseAnnually);
|
||||
|
||||
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 == 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
|
||||
));
|
||||
}
|
||||
|
||||
[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;
|
||||
|
||||
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)
|
||||
{
|
||||
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),
|
||||
});
|
||||
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);
|
||||
|
||||
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
|
||||
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
||||
stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer
|
||||
{
|
||||
PaymentIntent = new Stripe.PaymentIntent
|
||||
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 == 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
|
||||
));
|
||||
}
|
||||
|
||||
[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;
|
||||
|
||||
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)
|
||||
{
|
||||
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),
|
||||
});
|
||||
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);
|
||||
|
||||
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
|
||||
{
|
||||
Status = "requires_payment_method",
|
||||
PaymentIntent = new Stripe.PaymentIntent
|
||||
{
|
||||
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)
|
||||
{
|
||||
var plan = StaticStore.Plans.First(p => p.Type == PlanType.EnterpriseAnnually);
|
||||
|
||||
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
||||
stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer
|
||||
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
|
||||
public async void PurchaseOrganizationAsync_Stripe_RequiresAction(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
|
||||
{
|
||||
Id = "C-1",
|
||||
});
|
||||
stripeAdapter.SubscriptionCreateAsync(default).ReturnsForAnyArgs(new Stripe.Subscription
|
||||
{
|
||||
Id = "S-1",
|
||||
CurrentPeriodEnd = DateTime.Today.AddDays(10),
|
||||
Status = "incomplete",
|
||||
LatestInvoice = new Stripe.Invoice
|
||||
var plan = StaticStore.Plans.First(p => p.Type == PlanType.EnterpriseAnnually);
|
||||
|
||||
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
||||
stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer
|
||||
{
|
||||
PaymentIntent = new Stripe.PaymentIntent
|
||||
Id = "C-1",
|
||||
});
|
||||
stripeAdapter.SubscriptionCreateAsync(default).ReturnsForAnyArgs(new Stripe.Subscription
|
||||
{
|
||||
Id = "S-1",
|
||||
CurrentPeriodEnd = DateTime.Today.AddDays(10),
|
||||
Status = "incomplete",
|
||||
LatestInvoice = new Stripe.Invoice
|
||||
{
|
||||
Status = "requires_action",
|
||||
ClientSecret = "clientSecret",
|
||||
PaymentIntent = new Stripe.PaymentIntent
|
||||
{
|
||||
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)
|
||||
{
|
||||
var plan = StaticStore.Plans.First(p => p.Type == PlanType.EnterpriseAnnually);
|
||||
|
||||
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
||||
stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer
|
||||
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
|
||||
public async void PurchaseOrganizationAsync_Paypal(SutProvider<StripePaymentService> sutProvider, Organization organization, string paymentToken, TaxInfo taxInfo)
|
||||
{
|
||||
Id = "C-1",
|
||||
});
|
||||
stripeAdapter.SubscriptionCreateAsync(default).ReturnsForAnyArgs(new Stripe.Subscription
|
||||
{
|
||||
Id = "S-1",
|
||||
CurrentPeriodEnd = DateTime.Today.AddDays(10),
|
||||
});
|
||||
var plan = StaticStore.Plans.First(p => p.Type == PlanType.EnterpriseAnnually);
|
||||
|
||||
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)
|
||||
{
|
||||
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
|
||||
{
|
||||
Id = "C-1",
|
||||
});
|
||||
stripeAdapter.SubscriptionCreateAsync(default).ReturnsForAnyArgs(new Stripe.Subscription
|
||||
{
|
||||
Id = "S-1",
|
||||
CurrentPeriodEnd = DateTime.Today.AddDays(10),
|
||||
Status = "incomplete",
|
||||
LatestInvoice = new Stripe.Invoice
|
||||
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
||||
stripeAdapter.CustomerCreateAsync(default).ReturnsForAnyArgs(new Stripe.Customer
|
||||
{
|
||||
PaymentIntent = new Stripe.PaymentIntent
|
||||
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)
|
||||
{
|
||||
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
|
||||
{
|
||||
Id = "C-1",
|
||||
});
|
||||
stripeAdapter.SubscriptionCreateAsync(default).ReturnsForAnyArgs(new Stripe.Subscription
|
||||
{
|
||||
Id = "S-1",
|
||||
CurrentPeriodEnd = DateTime.Today.AddDays(10),
|
||||
Status = "incomplete",
|
||||
LatestInvoice = new Stripe.Invoice
|
||||
{
|
||||
Status = "requires_payment_method",
|
||||
PaymentIntent = new Stripe.PaymentIntent
|
||||
{
|
||||
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)
|
||||
{
|
||||
organization.GatewaySubscriptionId = null;
|
||||
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
||||
stripeAdapter.CustomerGetAsync(default).ReturnsForAnyArgs(new Stripe.Customer
|
||||
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
|
||||
public async void UpgradeFreeOrganizationAsync_Success(SutProvider<StripePaymentService> sutProvider,
|
||||
Organization organization, TaxInfo taxInfo)
|
||||
{
|
||||
Id = "C-1",
|
||||
Metadata = new Dictionary<string, string>
|
||||
organization.GatewaySubscriptionId = null;
|
||||
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
||||
stripeAdapter.CustomerGetAsync(default).ReturnsForAnyArgs(new Stripe.Customer
|
||||
{
|
||||
{ "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 { });
|
||||
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 { });
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,375 +14,376 @@ using NSubstitute;
|
||||
using NSubstitute.ReceivedExtensions;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Services;
|
||||
|
||||
public class UserServiceTests
|
||||
namespace Bit.Core.Test.Services
|
||||
{
|
||||
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
|
||||
public async Task UpdateLicenseAsync_Success(SutProvider<UserService> sutProvider,
|
||||
User user, UserLicense userLicense)
|
||||
public class UserServiceTests
|
||||
{
|
||||
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>
|
||||
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
|
||||
public async Task UpdateLicenseAsync_Success(SutProvider<UserService> sutProvider,
|
||||
User user, UserLicense userLicense)
|
||||
{
|
||||
[TwoFactorProviderType.Email] = new TwoFactorProvider
|
||||
{
|
||||
MetaData = new Dictionary<string, object> { ["Email"] = email },
|
||||
Enabled = true
|
||||
}
|
||||
});
|
||||
await sutProvider.Sut.SendTwoFactorEmailAsync(user);
|
||||
using var tempDir = new TempDirectory();
|
||||
|
||||
await sutProvider.GetDependency<IMailService>()
|
||||
.Received(1)
|
||||
.SendTwoFactorEmailAsync(email, token);
|
||||
}
|
||||
var now = DateTime.UtcNow;
|
||||
userLicense.Issued = now.AddDays(-10);
|
||||
userLicense.Expires = now.AddDays(10);
|
||||
userLicense.Version = 1;
|
||||
userLicense.Premium = true;
|
||||
|
||||
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
|
||||
public async Task SendTwoFactorEmailBecauseNewDeviceLoginAsync_Success(SutProvider<UserService> sutProvider, User user)
|
||||
{
|
||||
var email = user.Email.ToLowerInvariant();
|
||||
var token = "thisisatokentocompare";
|
||||
user.EmailVerified = true;
|
||||
user.Email = userLicense.Email;
|
||||
|
||||
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.GetDependency<Settings.IGlobalSettings>().SelfHosted = true;
|
||||
sutProvider.GetDependency<Settings.IGlobalSettings>().LicenseDirectory = tempDir.Directory;
|
||||
sutProvider.GetDependency<ILicensingService>()
|
||||
.VerifyLicense(userLicense)
|
||||
.Returns(true);
|
||||
|
||||
sutProvider.Sut.RegisterTokenProvider("Email", userTwoFactorTokenProvider);
|
||||
await sutProvider.Sut.UpdateLicenseAsync(user, userLicense);
|
||||
|
||||
user.SetTwoFactorProviders(new Dictionary<TwoFactorProviderType, TwoFactorProvider>
|
||||
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)
|
||||
{
|
||||
[TwoFactorProviderType.Email] = new 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>
|
||||
{
|
||||
MetaData = new Dictionary<string, object> { ["Email"] = email },
|
||||
Enabled = true
|
||||
}
|
||||
});
|
||||
await sutProvider.Sut.SendTwoFactorEmailAsync(user, true);
|
||||
[TwoFactorProviderType.Email] = new TwoFactorProvider
|
||||
{
|
||||
MetaData = new Dictionary<string, object> { ["Email"] = email },
|
||||
Enabled = true
|
||||
}
|
||||
});
|
||||
await sutProvider.Sut.SendTwoFactorEmailAsync(user);
|
||||
|
||||
await sutProvider.GetDependency<IMailService>()
|
||||
.Received(1)
|
||||
.SendNewDeviceLoginTwoFactorEmailAsync(email, token);
|
||||
}
|
||||
await sutProvider.GetDependency<IMailService>()
|
||||
.Received(1)
|
||||
.SendTwoFactorEmailAsync(email, token);
|
||||
}
|
||||
|
||||
[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>
|
||||
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
|
||||
public async Task SendTwoFactorEmailBecauseNewDeviceLoginAsync_Success(SutProvider<UserService> sutProvider, User user)
|
||||
{
|
||||
[TwoFactorProviderType.Email] = new 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>
|
||||
{
|
||||
MetaData = null,
|
||||
Enabled = true
|
||||
}
|
||||
});
|
||||
[TwoFactorProviderType.Email] = new TwoFactorProvider
|
||||
{
|
||||
MetaData = new Dictionary<string, object> { ["Email"] = email },
|
||||
Enabled = true
|
||||
}
|
||||
});
|
||||
await sutProvider.Sut.SendTwoFactorEmailAsync(user, true);
|
||||
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("No email.", () => sutProvider.Sut.SendTwoFactorEmailAsync(user));
|
||||
}
|
||||
await sutProvider.GetDependency<IMailService>()
|
||||
.Received(1)
|
||||
.SendNewDeviceLoginTwoFactorEmailAsync(email, token);
|
||||
}
|
||||
|
||||
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
|
||||
public async Task SendTwoFactorEmailAsync_ExceptionBecauseNoProviderEmailMetadataOnUser(SutProvider<UserService> sutProvider, User user)
|
||||
{
|
||||
user.SetTwoFactorProviders(new Dictionary<TwoFactorProviderType, TwoFactorProvider>
|
||||
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
|
||||
public async Task SendTwoFactorEmailAsync_ExceptionBecauseNoProviderOnUser(SutProvider<UserService> sutProvider, User user)
|
||||
{
|
||||
[TwoFactorProviderType.Email] = new TwoFactorProvider
|
||||
{
|
||||
MetaData = new Dictionary<string, object> { ["qweqwe"] = user.Email.ToLowerInvariant() },
|
||||
Enabled = true
|
||||
}
|
||||
});
|
||||
user.TwoFactorProviders = null;
|
||||
|
||||
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)
|
||||
{
|
||||
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>
|
||||
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
|
||||
public async Task SendTwoFactorEmailAsync_ExceptionBecauseNoProviderMetadataOnUser(SutProvider<UserService> sutProvider, User user)
|
||||
{
|
||||
[TwoFactorProviderType.Email] = new TwoFactorProvider
|
||||
user.SetTwoFactorProviders(new Dictionary<TwoFactorProviderType, TwoFactorProvider>
|
||||
{
|
||||
MetaData = new Dictionary<string, object> { ["Email"] = "asdfasf" },
|
||||
Enabled = true
|
||||
}
|
||||
});
|
||||
[TwoFactorProviderType.Email] = new TwoFactorProvider
|
||||
{
|
||||
MetaData = null,
|
||||
Enabled = true
|
||||
}
|
||||
});
|
||||
|
||||
sutProvider.GetDependency<Settings.IGlobalSettings>().TwoFactorAuth.EmailOnNewDeviceLogin.Returns(true);
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("No email.", () => sutProvider.Sut.SendTwoFactorEmailAsync(user));
|
||||
}
|
||||
|
||||
Assert.False(sutProvider.Sut.CanEditDeviceVerificationSettings(user));
|
||||
}
|
||||
[Theory, CustomAutoData(typeof(SutProviderCustomization))]
|
||||
public async Task SendTwoFactorEmailAsync_ExceptionBecauseNoProviderEmailMetadataOnUser(SutProvider<UserService> sutProvider, User user)
|
||||
{
|
||||
user.SetTwoFactorProviders(new Dictionary<TwoFactorProviderType, TwoFactorProvider>
|
||||
{
|
||||
[TwoFactorProviderType.Email] = new TwoFactorProvider
|
||||
{
|
||||
MetaData = new Dictionary<string, object> { ["qweqwe"] = user.Email.ToLowerInvariant() },
|
||||
Enabled = true
|
||||
}
|
||||
});
|
||||
|
||||
[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));
|
||||
await Assert.ThrowsAsync<ArgumentNullException>("No email.", () => sutProvider.Sut.SendTwoFactorEmailAsync(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";
|
||||
|
||||
[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<IDeviceRepository>()
|
||||
.GetManyByUserIdAsync(user.Id)
|
||||
.Returns(Task.FromResult<ICollection<Device>>(new List<Device>
|
||||
{
|
||||
new Device { Identifier = deviceIdInRepo }
|
||||
}));
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>().GetManyByUserAsync(user.Id).Returns(new List<OrganizationUser>() { orgUser });
|
||||
sutProvider.GetDependency<IApplicationCacheService>().GetOrganizationAbilitiesAsync().Returns(orgAbilities);
|
||||
|
||||
Assert.False(await sutProvider.Sut.HasPremiumFromOrganization(user));
|
||||
}
|
||||
sutProvider.GetDependency<Settings.IGlobalSettings>().TwoFactorAuth.EmailOnNewDeviceLogin.Returns(true);
|
||||
|
||||
[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) } };
|
||||
Assert.True(await sutProvider.Sut.Needs2FABecauseNewDeviceAsync(user, deviceIdToCheck, "password"));
|
||||
}
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>().GetManyByUserAsync(user.Id).Returns(new List<OrganizationUser>() { orgUser });
|
||||
sutProvider.GetDependency<IApplicationCacheService>().GetOrganizationAbilitiesAsync().Returns(orgAbilities);
|
||||
[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";
|
||||
|
||||
Assert.True(await sutProvider.Sut.HasPremiumFromOrganization(user));
|
||||
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
|
||||
{
|
||||
MetaData = new Dictionary<string, object> { ["Email"] = "asdfasf" },
|
||||
Enabled = true
|
||||
}
|
||||
});
|
||||
|
||||
sutProvider.GetDependency<Settings.IGlobalSettings>().TwoFactorAuth.EmailOnNewDeviceLogin.Returns(true);
|
||||
|
||||
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]
|
||||
[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);
|
||||
|
||||
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) } };
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>().GetManyByUserAsync(user.Id).Returns(new List<OrganizationUser>() { orgUser });
|
||||
sutProvider.GetDependency<IApplicationCacheService>().GetOrganizationAbilitiesAsync().Returns(orgAbilities);
|
||||
|
||||
Assert.True(await sutProvider.Sut.HasPremiumFromOrganization(user));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,38 +1,39 @@
|
||||
namespace Bit.Core.Test;
|
||||
|
||||
public class TempDirectory : IDisposable
|
||||
namespace Bit.Core.Test
|
||||
{
|
||||
public string Directory { get; private set; }
|
||||
|
||||
public TempDirectory()
|
||||
public class TempDirectory : IDisposable
|
||||
{
|
||||
Directory = Path.Combine(Path.GetTempPath(), $"bitwarden_{Guid.NewGuid().ToString().Replace("-", "")}");
|
||||
}
|
||||
public string Directory { get; private set; }
|
||||
|
||||
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)
|
||||
public TempDirectory()
|
||||
{
|
||||
try
|
||||
{
|
||||
System.IO.Directory.Delete(Directory, true);
|
||||
}
|
||||
catch { }
|
||||
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
|
||||
}
|
||||
# endregion
|
||||
}
|
||||
|
||||
@@ -7,121 +7,122 @@ using Bit.Test.Common.Helpers;
|
||||
using Microsoft.AspNetCore.DataProtection;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Tokens;
|
||||
|
||||
[SutProviderCustomize]
|
||||
public class DataProtectorTokenFactoryTests
|
||||
namespace Bit.Core.Test.Tokens
|
||||
{
|
||||
public static SutProvider<DataProtectorTokenFactory<TestTokenable>> GetSutProvider()
|
||||
[SutProviderCustomize]
|
||||
public class DataProtectorTokenFactoryTests
|
||||
{
|
||||
var fixture = new Fixture();
|
||||
return new SutProvider<DataProtectorTokenFactory<TestTokenable>>(fixture)
|
||||
.SetDependency<IDataProtectionProvider>(fixture.Create<EphemeralDataProtectionProvider>())
|
||||
.Create();
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,68 +3,69 @@ using AutoFixture.Xunit2;
|
||||
using Bit.Core.Utilities;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Tokens;
|
||||
|
||||
public class ExpiringTokenTests
|
||||
namespace Bit.Core.Test.Tokens
|
||||
{
|
||||
[Theory, AutoData]
|
||||
public void ExpirationSerializesToEpochMilliseconds(DateTime expirationDate)
|
||||
public class ExpiringTokenTests
|
||||
{
|
||||
var sut = new TestExpiringTokenable
|
||||
[Theory, AutoData]
|
||||
public void ExpirationSerializesToEpochMilliseconds(DateTime expirationDate)
|
||||
{
|
||||
ExpirationDate = expirationDate
|
||||
};
|
||||
var sut = new TestExpiringTokenable
|
||||
{
|
||||
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)
|
||||
{
|
||||
var sut = new TestExpiringTokenable
|
||||
[Theory, AutoData]
|
||||
public void ExpirationSerializationRoundTrip(DateTime expirationDate)
|
||||
{
|
||||
ExpirationDate = expirationDate
|
||||
};
|
||||
var sut = new TestExpiringTokenable
|
||||
{
|
||||
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()
|
||||
{
|
||||
var sut = new TestExpiringTokenable
|
||||
[Fact]
|
||||
public void InvalidIfPastExpiryDate()
|
||||
{
|
||||
ExpirationDate = DateTime.UtcNow.AddHours(-1)
|
||||
};
|
||||
var sut = new TestExpiringTokenable
|
||||
{
|
||||
ExpirationDate = DateTime.UtcNow.AddHours(-1)
|
||||
};
|
||||
|
||||
Assert.False(sut.Valid);
|
||||
}
|
||||
Assert.False(sut.Valid);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ValidIfWithinExpirationAndTokenReportsValid()
|
||||
{
|
||||
var sut = new TestExpiringTokenable
|
||||
[Fact]
|
||||
public void ValidIfWithinExpirationAndTokenReportsValid()
|
||||
{
|
||||
ExpirationDate = DateTime.UtcNow.AddHours(1)
|
||||
};
|
||||
var sut = new TestExpiringTokenable
|
||||
{
|
||||
ExpirationDate = DateTime.UtcNow.AddHours(1)
|
||||
};
|
||||
|
||||
Assert.True(sut.Valid);
|
||||
}
|
||||
Assert.True(sut.Valid);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HonorsTokenIsValidAbstractMember()
|
||||
{
|
||||
var sut = new TestExpiringTokenable(forceInvalid: true)
|
||||
[Fact]
|
||||
public void HonorsTokenIsValidAbstractMember()
|
||||
{
|
||||
ExpirationDate = DateTime.UtcNow.AddHours(1)
|
||||
};
|
||||
var sut = new TestExpiringTokenable(forceInvalid: true)
|
||||
{
|
||||
ExpirationDate = DateTime.UtcNow.AddHours(1)
|
||||
};
|
||||
|
||||
Assert.False(sut.Valid);
|
||||
Assert.False(sut.Valid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +1,26 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using Bit.Core.Tokens;
|
||||
|
||||
namespace Bit.Core.Test.Tokens;
|
||||
|
||||
public class TestTokenable : Tokenable
|
||||
namespace Bit.Core.Test.Tokens
|
||||
{
|
||||
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)
|
||||
public class TestTokenable : Tokenable
|
||||
{
|
||||
_forceInvalid = forceInvalid;
|
||||
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;
|
||||
}
|
||||
protected override bool TokenIsValid() => !_forceInvalid;
|
||||
}
|
||||
|
||||
@@ -2,37 +2,38 @@
|
||||
using Bit.Core.Tokens;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Tokens;
|
||||
|
||||
public class TokenTests
|
||||
namespace Bit.Core.Test.Tokens
|
||||
{
|
||||
[Theory, AutoData]
|
||||
public void InitializeWithString_ReturnsString(string initString)
|
||||
public class TokenTests
|
||||
{
|
||||
var token = new Token(initString);
|
||||
[Theory, AutoData]
|
||||
public void InitializeWithString_ReturnsString(string 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,35 +2,36 @@
|
||||
using Bit.Core.Utilities;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Utilities;
|
||||
|
||||
public class ClaimsExtensionsTests
|
||||
namespace Bit.Core.Test.Utilities
|
||||
{
|
||||
[Fact]
|
||||
public void HasSSOIdP_Returns_True_When_The_Claims_Has_One_Of_Type_IdP_And_Value_Sso()
|
||||
public class ClaimsExtensionsTests
|
||||
{
|
||||
var claims = new List<Claim> { new Claim("idp", "sso") };
|
||||
Assert.True(claims.HasSsoIdP());
|
||||
}
|
||||
[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());
|
||||
}
|
||||
|
||||
[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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,433 +12,434 @@ using IdentityModel;
|
||||
using Microsoft.AspNetCore.DataProtection;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Utilities;
|
||||
|
||||
public class CoreHelpersTests
|
||||
namespace Bit.Core.Test.Utilities
|
||||
{
|
||||
public static IEnumerable<object[]> _epochTestCases = new[]
|
||||
public class CoreHelpersTests
|
||||
{
|
||||
new object[] {new DateTime(2020, 12, 30, 11, 49, 12, DateTimeKind.Utc), 1609328952000L},
|
||||
};
|
||||
|
||||
[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
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> GenerateCombCases = new[]
|
||||
{
|
||||
new object[]
|
||||
public static IEnumerable<object[]> _epochTestCases = new[]
|
||||
{
|
||||
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[]
|
||||
new object[] {new DateTime(2020, 12, 30, 11, 49, 12, DateTimeKind.Utc), 1609328952000L},
|
||||
};
|
||||
|
||||
[Fact]
|
||||
public void GenerateComb_Success()
|
||||
{
|
||||
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"),
|
||||
}
|
||||
};
|
||||
// Arrange & Act
|
||||
var comb = CoreHelpers.GenerateComb();
|
||||
|
||||
[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());
|
||||
// Assert
|
||||
Assert.NotEqual(Guid.Empty, comb);
|
||||
// TODO: Add more asserts to make sure important aspects of
|
||||
// the comb are working properly
|
||||
}
|
||||
|
||||
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¶m2=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>
|
||||
public static IEnumerable<object[]> GenerateCombCases = new[]
|
||||
{
|
||||
new object[]
|
||||
{
|
||||
{ "param2", "value2" },
|
||||
{ "param3", "value3" }
|
||||
});
|
||||
|
||||
// Assert
|
||||
Assert.Equal("https://bitwarden.com/?param1=value1¶m2=value2¶m3=value3", newUri.ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExtendQuery_AddExistingParameter_Success()
|
||||
{
|
||||
// Arrange
|
||||
var uri = new Uri("https://bitwarden.com/?param1=value1¶m2=value2");
|
||||
|
||||
// Act
|
||||
var newUri = CoreHelpers.ExtendQuery(uri,
|
||||
new Dictionary<string, string> { { "param1", "test_value" } });
|
||||
|
||||
// Assert
|
||||
Assert.Equal("https://bitwarden.com/?param1=test_value¶m2=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 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)
|
||||
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());
|
||||
}
|
||||
|
||||
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¶m2=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¶m2=value2¶m3=value3", newUri.ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ExtendQuery_AddExistingParameter_Success()
|
||||
{
|
||||
// Arrange
|
||||
var uri = new Uri("https://bitwarden.com/?param1=value1¶m2=value2");
|
||||
|
||||
// Act
|
||||
var newUri = CoreHelpers.ExtendQuery(uri,
|
||||
new Dictionary<string, string> { { "param1", "test_value" } });
|
||||
|
||||
// Assert
|
||||
Assert.Equal("https://bitwarden.com/?param1=test_value¶m2=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);
|
||||
}
|
||||
else
|
||||
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 }))
|
||||
{
|
||||
Assert.DoesNotContain(claim, actual);
|
||||
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_ProviderClaims_Success(User user)
|
||||
{
|
||||
var fixture = new Fixture().WithAutoNSubstitutions();
|
||||
var providers = new List<CurrentContentProvider>();
|
||||
foreach (var providerUserType in Enum.GetValues<ProviderUserType>())
|
||||
[Theory, CustomAutoData(typeof(UserFixture))]
|
||||
public void BuildIdentityClaims_CustomOrganizationUserClaims_Success(User user, CurrentContentOrganization org)
|
||||
{
|
||||
var provider = fixture.Create<CurrentContentProvider>();
|
||||
provider.Type = providerUserType;
|
||||
providers.Add(provider);
|
||||
}
|
||||
var fixture = new Fixture().WithAutoNSubstitutions();
|
||||
org.Type = OrganizationUserType.Custom;
|
||||
|
||||
var claims = new List<KeyValuePair<string, string>>();
|
||||
|
||||
if (providers.Any())
|
||||
{
|
||||
foreach (var group in providers.GroupBy(o => o.Type))
|
||||
var actual = CoreHelpers.BuildIdentityClaims(user, new[] { org }, Array.Empty<CurrentContentProvider>(), false);
|
||||
foreach (var (permitted, claimName) in org.Permissions.ClaimsMap)
|
||||
{
|
||||
switch (group.Key)
|
||||
var claim = new KeyValuePair<string, string>(claimName, org.Id.ToString());
|
||||
if (permitted)
|
||||
{
|
||||
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;
|
||||
|
||||
Assert.Contains(claim, actual);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.DoesNotContain(claim, actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var actual = CoreHelpers.BuildIdentityClaims(user, Array.Empty<CurrentContentOrganization>(), providers, false);
|
||||
foreach (var claim in claims)
|
||||
[Theory, CustomAutoData(typeof(UserFixture))]
|
||||
public void BuildIdentityClaims_ProviderClaims_Success(User user)
|
||||
{
|
||||
Assert.Contains(claim, actual);
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> TokenIsValidData()
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
new object[]
|
||||
var fixture = new Fixture().WithAutoNSubstitutions();
|
||||
var providers = new List<CurrentContentProvider>();
|
||||
foreach (var providerUserType in Enum.GetValues<ProviderUserType>())
|
||||
{
|
||||
"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
|
||||
var provider = fixture.Create<CurrentContentProvider>();
|
||||
provider.Type = providerUserType;
|
||||
providers.Add(provider);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
[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)));
|
||||
var claims = new List<KeyValuePair<string, string>>();
|
||||
|
||||
Assert.Equal(isValid, CoreHelpers.TokenIsValid(firstPart, protector, "protected_token", userEmail, id, expirationInHours));
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class TestDataProtector : IDataProtector
|
||||
{
|
||||
private readonly string _token;
|
||||
public TestDataProtector(string token)
|
||||
{
|
||||
_token = token;
|
||||
var actual = CoreHelpers.BuildIdentityClaims(user, Array.Empty<CurrentContentOrganization>(), providers, false);
|
||||
foreach (var claim in claims)
|
||||
{
|
||||
Assert.Contains(claim, actual);
|
||||
}
|
||||
}
|
||||
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));
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,42 +1,43 @@
|
||||
using Bit.Core.Utilities;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Utilities;
|
||||
|
||||
public class EncryptedStringAttributeTests
|
||||
namespace Bit.Core.Test.Utilities
|
||||
{
|
||||
[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)
|
||||
public class EncryptedStringAttributeTests
|
||||
{
|
||||
var sut = new EncryptedStringAttribute();
|
||||
[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 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,64 +2,65 @@
|
||||
using Bit.Core.Utilities;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Helpers;
|
||||
|
||||
public class JsonHelpersTests
|
||||
namespace Bit.Core.Test.Helpers
|
||||
{
|
||||
private static void CompareJson<T>(T value, JsonSerializerOptions options, Newtonsoft.Json.JsonSerializerSettings settings)
|
||||
public class JsonHelpersTests
|
||||
{
|
||||
var stgJson = JsonSerializer.Serialize(value, options);
|
||||
var nsJson = Newtonsoft.Json.JsonConvert.SerializeObject(value, 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);
|
||||
|
||||
Assert.Equal(stgJson, nsJson);
|
||||
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,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void DefaultJsonOptions()
|
||||
public class SimpleTestObject
|
||||
{
|
||||
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 int Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
public class SimpleTestObject
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
}
|
||||
|
||||
@@ -5,164 +5,165 @@ using Bit.Core.Utilities;
|
||||
using Bit.Test.Common.Helpers;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Utilities;
|
||||
|
||||
public class PermissiveStringConverterTests
|
||||
namespace Bit.Core.Test.Utilities
|
||||
{
|
||||
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)
|
||||
public class PermissiveStringConverterTests
|
||||
{
|
||||
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));
|
||||
}
|
||||
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 + "]}";
|
||||
|
||||
[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
|
||||
[Theory]
|
||||
[InlineData(numberJson)]
|
||||
[InlineData(stringJson)]
|
||||
public void Read_Success(string json)
|
||||
{
|
||||
StringProp = "1",
|
||||
EnumerableStringProp = new List<string>
|
||||
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
|
||||
{
|
||||
"2",
|
||||
"3",
|
||||
},
|
||||
});
|
||||
StringProp = "1",
|
||||
EnumerableStringProp = new List<string>
|
||||
{
|
||||
"2",
|
||||
"3",
|
||||
},
|
||||
});
|
||||
|
||||
var jsonElement = JsonDocument.Parse(json).RootElement;
|
||||
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());
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Write_Null()
|
||||
public class TestObject
|
||||
{
|
||||
// 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,
|
||||
});
|
||||
[JsonConverter(typeof(PermissiveStringConverter))]
|
||||
public string StringProp { get; set; }
|
||||
|
||||
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());
|
||||
[JsonConverter(typeof(PermissiveStringEnumerableConverter))]
|
||||
public IEnumerable<string> EnumerableStringProp { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
public class TestObject
|
||||
{
|
||||
[JsonConverter(typeof(PermissiveStringConverter))]
|
||||
public string StringProp { get; set; }
|
||||
|
||||
[JsonConverter(typeof(PermissiveStringEnumerableConverter))]
|
||||
public IEnumerable<string> EnumerableStringProp { get; set; }
|
||||
}
|
||||
|
||||
@@ -10,82 +10,83 @@ using Microsoft.Extensions.DependencyInjection;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Utilities;
|
||||
|
||||
public class SelfHostedAttributeTests
|
||||
namespace Bit.Core.Test.Utilities
|
||||
{
|
||||
[Fact]
|
||||
public void NotSelfHosted_Throws_When_SelfHosted()
|
||||
public class SelfHostedAttributeTests
|
||||
{
|
||||
// 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
|
||||
[Fact]
|
||||
public void NotSelfHosted_Throws_When_SelfHosted()
|
||||
{
|
||||
SelfHosted = selfHosted
|
||||
};
|
||||
// Arrange
|
||||
var sha = new SelfHostedAttribute { NotSelfHostedOnly = true };
|
||||
|
||||
services.AddSingleton(globalSettings);
|
||||
// Act & Assert
|
||||
Assert.Throws<BadRequestException>(() => sha.OnActionExecuting(GetContext(selfHosted: true)));
|
||||
}
|
||||
|
||||
var httpContext = new DefaultHttpContext();
|
||||
httpContext.RequestServices = services.BuildServiceProvider();
|
||||
[Fact]
|
||||
public void NotSelfHosted_Success_When_NotSelfHosted()
|
||||
{
|
||||
// Arrange
|
||||
var sha = new SelfHostedAttribute { NotSelfHostedOnly = true };
|
||||
|
||||
var context = Substitute.For<ActionExecutingContext>(
|
||||
Substitute.For<ActionContext>(httpContext,
|
||||
new RouteData(),
|
||||
Substitute.For<ActionDescriptor>()),
|
||||
new List<IFilterMetadata>(),
|
||||
new Dictionary<string, object>(),
|
||||
Substitute.For<Controller>());
|
||||
// Act
|
||||
sha.OnActionExecuting(GetContext(selfHosted: false));
|
||||
|
||||
return context;
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,58 +1,59 @@
|
||||
using Bit.Core.Utilities;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Utilities;
|
||||
|
||||
public class StrictEmailAttributeTests
|
||||
namespace Bit.Core.Test.Utilities
|
||||
{
|
||||
[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)
|
||||
public class StrictEmailAttributeTests
|
||||
{
|
||||
var sut = new StrictEmailAddressAttribute();
|
||||
[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 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,53 +1,54 @@
|
||||
using Bit.Core.Utilities;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Utilities;
|
||||
|
||||
public class StrictEmailAddressListAttributeTests
|
||||
namespace Bit.Core.Test.Utilities
|
||||
{
|
||||
public static List<object[]> EmailList => new()
|
||||
public class StrictEmailAddressListAttributeTests
|
||||
{
|
||||
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
|
||||
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
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
[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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user