mirror of
https://github.com/bitwarden/server
synced 2026-01-03 09:03:44 +00:00
Merge branch 'main' into auth/pm-22975/client-version-validator
This commit is contained in:
224
test/Core.Test/Auth/Entities/AuthRequestTests.cs
Normal file
224
test/Core.Test/Auth/Entities/AuthRequestTests.cs
Normal file
@@ -0,0 +1,224 @@
|
||||
using Bit.Core.Auth.Entities;
|
||||
using Bit.Core.Auth.Enums;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Auth.Entities;
|
||||
|
||||
public class AuthRequestTests
|
||||
{
|
||||
[Fact]
|
||||
public void IsValidForAuthentication_WithValidRequest_ReturnsTrue()
|
||||
{
|
||||
// Arrange
|
||||
var userId = Guid.NewGuid();
|
||||
var accessCode = "test-access-code";
|
||||
var authRequest = new AuthRequest
|
||||
{
|
||||
UserId = userId,
|
||||
Type = AuthRequestType.AuthenticateAndUnlock,
|
||||
ResponseDate = DateTime.UtcNow,
|
||||
Approved = true,
|
||||
CreationDate = DateTime.UtcNow,
|
||||
AuthenticationDate = null,
|
||||
AccessCode = accessCode
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = authRequest.IsValidForAuthentication(userId, accessCode);
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsValidForAuthentication_WithWrongUserId_ReturnsFalse()
|
||||
{
|
||||
// Arrange
|
||||
var userId = Guid.NewGuid();
|
||||
var differentUserId = Guid.NewGuid();
|
||||
var accessCode = "test-access-code";
|
||||
var authRequest = new AuthRequest
|
||||
{
|
||||
UserId = userId,
|
||||
Type = AuthRequestType.AuthenticateAndUnlock,
|
||||
ResponseDate = DateTime.UtcNow,
|
||||
Approved = true,
|
||||
CreationDate = DateTime.UtcNow,
|
||||
AuthenticationDate = null,
|
||||
AccessCode = accessCode
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = authRequest.IsValidForAuthentication(differentUserId, accessCode);
|
||||
|
||||
// Assert
|
||||
Assert.False(result, "Auth request should not validate for a different user");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsValidForAuthentication_WithWrongAccessCode_ReturnsFalse()
|
||||
{
|
||||
// Arrange
|
||||
var userId = Guid.NewGuid();
|
||||
var authRequest = new AuthRequest
|
||||
{
|
||||
UserId = userId,
|
||||
Type = AuthRequestType.AuthenticateAndUnlock,
|
||||
ResponseDate = DateTime.UtcNow,
|
||||
Approved = true,
|
||||
CreationDate = DateTime.UtcNow,
|
||||
AuthenticationDate = null,
|
||||
AccessCode = "correct-code"
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = authRequest.IsValidForAuthentication(userId, "wrong-code");
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsValidForAuthentication_WithoutResponseDate_ReturnsFalse()
|
||||
{
|
||||
// Arrange
|
||||
var userId = Guid.NewGuid();
|
||||
var accessCode = "test-access-code";
|
||||
var authRequest = new AuthRequest
|
||||
{
|
||||
UserId = userId,
|
||||
Type = AuthRequestType.AuthenticateAndUnlock,
|
||||
ResponseDate = null, // Not responded to
|
||||
Approved = true,
|
||||
CreationDate = DateTime.UtcNow,
|
||||
AuthenticationDate = null,
|
||||
AccessCode = accessCode
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = authRequest.IsValidForAuthentication(userId, accessCode);
|
||||
|
||||
// Assert
|
||||
Assert.False(result, "Unanswered auth requests should not be valid");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsValidForAuthentication_WithApprovedFalse_ReturnsFalse()
|
||||
{
|
||||
// Arrange
|
||||
var userId = Guid.NewGuid();
|
||||
var accessCode = "test-access-code";
|
||||
var authRequest = new AuthRequest
|
||||
{
|
||||
UserId = userId,
|
||||
Type = AuthRequestType.AuthenticateAndUnlock,
|
||||
ResponseDate = DateTime.UtcNow,
|
||||
Approved = false, // Denied
|
||||
CreationDate = DateTime.UtcNow,
|
||||
AuthenticationDate = null,
|
||||
AccessCode = accessCode
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = authRequest.IsValidForAuthentication(userId, accessCode);
|
||||
|
||||
// Assert
|
||||
Assert.False(result, "Denied auth requests should not be valid");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsValidForAuthentication_WithApprovedNull_ReturnsFalse()
|
||||
{
|
||||
// Arrange
|
||||
var userId = Guid.NewGuid();
|
||||
var accessCode = "test-access-code";
|
||||
var authRequest = new AuthRequest
|
||||
{
|
||||
UserId = userId,
|
||||
Type = AuthRequestType.AuthenticateAndUnlock,
|
||||
ResponseDate = DateTime.UtcNow,
|
||||
Approved = null, // Pending
|
||||
CreationDate = DateTime.UtcNow,
|
||||
AuthenticationDate = null,
|
||||
AccessCode = accessCode
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = authRequest.IsValidForAuthentication(userId, accessCode);
|
||||
|
||||
// Assert
|
||||
Assert.False(result, "Pending auth requests should not be valid");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsValidForAuthentication_WithExpiredRequest_ReturnsFalse()
|
||||
{
|
||||
// Arrange
|
||||
var userId = Guid.NewGuid();
|
||||
var accessCode = "test-access-code";
|
||||
var authRequest = new AuthRequest
|
||||
{
|
||||
UserId = userId,
|
||||
Type = AuthRequestType.AuthenticateAndUnlock,
|
||||
ResponseDate = DateTime.UtcNow,
|
||||
Approved = true,
|
||||
CreationDate = DateTime.UtcNow.AddMinutes(-20), // Expired (15 min timeout)
|
||||
AuthenticationDate = null,
|
||||
AccessCode = accessCode
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = authRequest.IsValidForAuthentication(userId, accessCode);
|
||||
|
||||
// Assert
|
||||
Assert.False(result, "Expired auth requests should not be valid");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsValidForAuthentication_WithWrongType_ReturnsFalse()
|
||||
{
|
||||
// Arrange
|
||||
var userId = Guid.NewGuid();
|
||||
var accessCode = "test-access-code";
|
||||
var authRequest = new AuthRequest
|
||||
{
|
||||
UserId = userId,
|
||||
Type = AuthRequestType.Unlock, // Wrong type
|
||||
ResponseDate = DateTime.UtcNow,
|
||||
Approved = true,
|
||||
CreationDate = DateTime.UtcNow,
|
||||
AuthenticationDate = null,
|
||||
AccessCode = accessCode
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = authRequest.IsValidForAuthentication(userId, accessCode);
|
||||
|
||||
// Assert
|
||||
Assert.False(result, "Only AuthenticateAndUnlock type should be valid");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsValidForAuthentication_WithAlreadyUsed_ReturnsFalse()
|
||||
{
|
||||
// Arrange
|
||||
var userId = Guid.NewGuid();
|
||||
var accessCode = "test-access-code";
|
||||
var authRequest = new AuthRequest
|
||||
{
|
||||
UserId = userId,
|
||||
Type = AuthRequestType.AuthenticateAndUnlock,
|
||||
ResponseDate = DateTime.UtcNow,
|
||||
Approved = true,
|
||||
CreationDate = DateTime.UtcNow,
|
||||
AuthenticationDate = DateTime.UtcNow, // Already used
|
||||
AccessCode = accessCode
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = authRequest.IsValidForAuthentication(userId, accessCode);
|
||||
|
||||
// Assert
|
||||
Assert.False(result, "Auth requests should only be valid for one-time use");
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using Bit.Core.Billing.Extensions;
|
||||
using System.Globalization;
|
||||
using Bit.Core.Billing.Extensions;
|
||||
using Stripe;
|
||||
using Xunit;
|
||||
|
||||
@@ -356,9 +357,18 @@ public class InvoiceExtensionsTests
|
||||
[Fact]
|
||||
public void FormatForProvider_ComplexScenario_HandlesAllLineTypes()
|
||||
{
|
||||
// Arrange
|
||||
var lineItems = new StripeList<InvoiceLineItem>();
|
||||
lineItems.Data = new List<InvoiceLineItem>
|
||||
// Set culture to en-US to ensure consistent decimal formatting in tests
|
||||
// This ensures tests pass on all machines regardless of system locale
|
||||
var originalCulture = Thread.CurrentThread.CurrentCulture;
|
||||
var originalUICulture = Thread.CurrentThread.CurrentUICulture;
|
||||
try
|
||||
{
|
||||
Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
|
||||
Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US");
|
||||
|
||||
// Arrange
|
||||
var lineItems = new StripeList<InvoiceLineItem>();
|
||||
lineItems.Data = new List<InvoiceLineItem>
|
||||
{
|
||||
new InvoiceLineItem
|
||||
{
|
||||
@@ -372,23 +382,29 @@ public class InvoiceExtensionsTests
|
||||
new InvoiceLineItem { Description = "Custom Service", Quantity = 2, Amount = 2000 }
|
||||
};
|
||||
|
||||
var invoice = new Invoice
|
||||
var invoice = new Invoice
|
||||
{
|
||||
Lines = lineItems,
|
||||
TotalTaxes = [new InvoiceTotalTax { Amount = 200 }] // Additional $2.00 tax
|
||||
};
|
||||
var subscription = new Subscription();
|
||||
|
||||
// Act
|
||||
var result = invoice.FormatForProvider(subscription);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(5, result.Count);
|
||||
Assert.Equal("5 × Manage service provider (at $6.00 / month)", result[0]);
|
||||
Assert.Equal("10 × Manage service provider (at $4.00 / month)", result[1]);
|
||||
Assert.Equal("1 × Tax (at $8.00 / month)", result[2]);
|
||||
Assert.Equal("Custom Service", result[3]);
|
||||
Assert.Equal("1 × Tax (at $2.00 / month)", result[4]);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Lines = lineItems,
|
||||
TotalTaxes = [new InvoiceTotalTax { Amount = 200 }] // Additional $2.00 tax
|
||||
};
|
||||
var subscription = new Subscription();
|
||||
|
||||
// Act
|
||||
var result = invoice.FormatForProvider(subscription);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(5, result.Count);
|
||||
Assert.Equal("5 × Manage service provider (at $6.00 / month)", result[0]);
|
||||
Assert.Equal("10 × Manage service provider (at $4.00 / month)", result[1]);
|
||||
Assert.Equal("1 × Tax (at $8.00 / month)", result[2]);
|
||||
Assert.Equal("Custom Service", result[3]);
|
||||
Assert.Equal("1 × Tax (at $2.00 / month)", result[4]);
|
||||
Thread.CurrentThread.CurrentCulture = originalCulture;
|
||||
Thread.CurrentThread.CurrentUICulture = originalUICulture;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -328,157 +328,6 @@ public class SubscriberServiceTests
|
||||
|
||||
#endregion
|
||||
|
||||
#region GetPaymentMethod
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task GetPaymentMethod_NullSubscriber_ThrowsArgumentNullException(
|
||||
SutProvider<SubscriberService> sutProvider) =>
|
||||
await Assert.ThrowsAsync<ArgumentNullException>(() => sutProvider.Sut.GetPaymentSource(null));
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task GetPaymentMethod_WithNegativeStripeAccountBalance_ReturnsCorrectAccountCreditAmount(Organization organization,
|
||||
SutProvider<SubscriberService> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
// Stripe reports balance in cents as a negative number for credit
|
||||
const int stripeAccountBalance = -593; // $5.93 credit (negative cents)
|
||||
const decimal creditAmount = 5.93M; // Same value in dollars
|
||||
|
||||
|
||||
var customer = new Customer
|
||||
{
|
||||
Balance = stripeAccountBalance,
|
||||
Subscriptions = new StripeList<Subscription>()
|
||||
{
|
||||
Data =
|
||||
[new Subscription { Id = organization.GatewaySubscriptionId, Status = "active" }]
|
||||
},
|
||||
InvoiceSettings = new CustomerInvoiceSettings
|
||||
{
|
||||
DefaultPaymentMethod = new PaymentMethod
|
||||
{
|
||||
Type = StripeConstants.PaymentMethodTypes.USBankAccount,
|
||||
UsBankAccount = new PaymentMethodUsBankAccount { BankName = "Chase", Last4 = "9999" }
|
||||
}
|
||||
}
|
||||
};
|
||||
sutProvider.GetDependency<IStripeAdapter>().CustomerGetAsync(organization.GatewayCustomerId,
|
||||
Arg.Is<CustomerGetOptions>(options => options.Expand.Contains("default_source") &&
|
||||
options.Expand.Contains("invoice_settings.default_payment_method")
|
||||
&& options.Expand.Contains("subscriptions")
|
||||
&& options.Expand.Contains("tax_ids")))
|
||||
.Returns(customer);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.GetPaymentMethod(organization);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal(creditAmount, result.AccountCredit);
|
||||
await sutProvider.GetDependency<IStripeAdapter>().Received(1).CustomerGetAsync(
|
||||
organization.GatewayCustomerId,
|
||||
Arg.Is<CustomerGetOptions>(options =>
|
||||
options.Expand.Contains("default_source") &&
|
||||
options.Expand.Contains("invoice_settings.default_payment_method") &&
|
||||
options.Expand.Contains("subscriptions") &&
|
||||
options.Expand.Contains("tax_ids")));
|
||||
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task GetPaymentMethod_WithZeroStripeAccountBalance_ReturnsCorrectAccountCreditAmount(
|
||||
Organization organization, SutProvider<SubscriberService> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
const int stripeAccountBalance = 0;
|
||||
|
||||
var customer = new Customer
|
||||
{
|
||||
Balance = stripeAccountBalance,
|
||||
Subscriptions = new StripeList<Subscription>()
|
||||
{
|
||||
Data =
|
||||
[new Subscription { Id = organization.GatewaySubscriptionId, Status = "active" }]
|
||||
},
|
||||
InvoiceSettings = new CustomerInvoiceSettings
|
||||
{
|
||||
DefaultPaymentMethod = new PaymentMethod
|
||||
{
|
||||
Type = StripeConstants.PaymentMethodTypes.USBankAccount,
|
||||
UsBankAccount = new PaymentMethodUsBankAccount { BankName = "Chase", Last4 = "9999" }
|
||||
}
|
||||
}
|
||||
};
|
||||
sutProvider.GetDependency<IStripeAdapter>().CustomerGetAsync(organization.GatewayCustomerId,
|
||||
Arg.Is<CustomerGetOptions>(options => options.Expand.Contains("default_source") &&
|
||||
options.Expand.Contains("invoice_settings.default_payment_method")
|
||||
&& options.Expand.Contains("subscriptions")
|
||||
&& options.Expand.Contains("tax_ids")))
|
||||
.Returns(customer);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.GetPaymentMethod(organization);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal(0, result.AccountCredit);
|
||||
await sutProvider.GetDependency<IStripeAdapter>().Received(1).CustomerGetAsync(
|
||||
organization.GatewayCustomerId,
|
||||
Arg.Is<CustomerGetOptions>(options =>
|
||||
options.Expand.Contains("default_source") &&
|
||||
options.Expand.Contains("invoice_settings.default_payment_method") &&
|
||||
options.Expand.Contains("subscriptions") &&
|
||||
options.Expand.Contains("tax_ids")));
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task GetPaymentMethod_WithPositiveStripeAccountBalance_ReturnsCorrectAccountCreditAmount(
|
||||
Organization organization, SutProvider<SubscriberService> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
const int stripeAccountBalance = 593; // $5.93 charge balance
|
||||
const decimal accountBalance = -5.93M; // account balance
|
||||
var customer = new Customer
|
||||
{
|
||||
Balance = stripeAccountBalance,
|
||||
Subscriptions = new StripeList<Subscription>()
|
||||
{
|
||||
Data =
|
||||
[new Subscription { Id = organization.GatewaySubscriptionId, Status = "active" }]
|
||||
},
|
||||
InvoiceSettings = new CustomerInvoiceSettings
|
||||
{
|
||||
DefaultPaymentMethod = new PaymentMethod
|
||||
{
|
||||
Type = StripeConstants.PaymentMethodTypes.USBankAccount,
|
||||
UsBankAccount = new PaymentMethodUsBankAccount { BankName = "Chase", Last4 = "9999" }
|
||||
}
|
||||
}
|
||||
};
|
||||
sutProvider.GetDependency<IStripeAdapter>().CustomerGetAsync(organization.GatewayCustomerId,
|
||||
Arg.Is<CustomerGetOptions>(options => options.Expand.Contains("default_source") &&
|
||||
options.Expand.Contains("invoice_settings.default_payment_method")
|
||||
&& options.Expand.Contains("subscriptions")
|
||||
&& options.Expand.Contains("tax_ids")))
|
||||
.Returns(customer);
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.GetPaymentMethod(organization);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal(accountBalance, result.AccountCredit);
|
||||
await sutProvider.GetDependency<IStripeAdapter>().Received(1).CustomerGetAsync(
|
||||
organization.GatewayCustomerId,
|
||||
Arg.Is<CustomerGetOptions>(options =>
|
||||
options.Expand.Contains("default_source") &&
|
||||
options.Expand.Contains("invoice_settings.default_payment_method") &&
|
||||
options.Expand.Contains("subscriptions") &&
|
||||
options.Expand.Contains("tax_ids")));
|
||||
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region GetPaymentSource
|
||||
|
||||
[Theory, BitAutoData]
|
||||
@@ -889,65 +738,6 @@ public class SubscriberServiceTests
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region GetTaxInformation
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task GetTaxInformation_NullSubscriber_ThrowsArgumentNullException(
|
||||
SutProvider<SubscriberService> sutProvider) =>
|
||||
await Assert.ThrowsAsync<ArgumentNullException>(() => sutProvider.Sut.GetTaxInformation(null));
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task GetTaxInformation_NullAddress_ReturnsNull(
|
||||
Organization organization,
|
||||
SutProvider<SubscriberService> sutProvider)
|
||||
{
|
||||
sutProvider.GetDependency<IStripeAdapter>().CustomerGetAsync(organization.GatewayCustomerId, Arg.Any<CustomerGetOptions>())
|
||||
.Returns(new Customer());
|
||||
|
||||
var taxInformation = await sutProvider.Sut.GetTaxInformation(organization);
|
||||
|
||||
Assert.Null(taxInformation);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task GetTaxInformation_Success(
|
||||
Organization organization,
|
||||
SutProvider<SubscriberService> sutProvider)
|
||||
{
|
||||
var address = new Address
|
||||
{
|
||||
Country = "US",
|
||||
PostalCode = "12345",
|
||||
Line1 = "123 Example St.",
|
||||
Line2 = "Unit 1",
|
||||
City = "Example Town",
|
||||
State = "NY"
|
||||
};
|
||||
|
||||
sutProvider.GetDependency<IStripeAdapter>().CustomerGetAsync(organization.GatewayCustomerId, Arg.Any<CustomerGetOptions>())
|
||||
.Returns(new Customer
|
||||
{
|
||||
Address = address,
|
||||
TaxIds = new StripeList<TaxId>
|
||||
{
|
||||
Data = [new TaxId { Value = "tax_id" }]
|
||||
}
|
||||
});
|
||||
|
||||
var taxInformation = await sutProvider.Sut.GetTaxInformation(organization);
|
||||
|
||||
Assert.NotNull(taxInformation);
|
||||
Assert.Equal(address.Country, taxInformation.Country);
|
||||
Assert.Equal(address.PostalCode, taxInformation.PostalCode);
|
||||
Assert.Equal("tax_id", taxInformation.TaxId);
|
||||
Assert.Equal(address.Line1, taxInformation.Line1);
|
||||
Assert.Equal(address.Line2, taxInformation.Line2);
|
||||
Assert.Equal(address.City, taxInformation.City);
|
||||
Assert.Equal(address.State, taxInformation.State);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region RemovePaymentMethod
|
||||
[Theory, BitAutoData]
|
||||
public async Task RemovePaymentMethod_NullSubscriber_ThrowsArgumentNullException(
|
||||
@@ -1844,48 +1634,6 @@ public class SubscriberServiceTests
|
||||
|
||||
#endregion
|
||||
|
||||
#region VerifyBankAccount
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task VerifyBankAccount_NoSetupIntentId_ThrowsBillingException(
|
||||
Provider provider,
|
||||
SutProvider<SubscriberService> sutProvider) => await ThrowsBillingExceptionAsync(() => sutProvider.Sut.VerifyBankAccount(provider, ""));
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task VerifyBankAccount_MakesCorrectInvocations(
|
||||
Provider provider,
|
||||
SutProvider<SubscriberService> sutProvider)
|
||||
{
|
||||
const string descriptorCode = "SM1234";
|
||||
|
||||
var setupIntent = new SetupIntent
|
||||
{
|
||||
Id = "setup_intent_id",
|
||||
PaymentMethodId = "payment_method_id"
|
||||
};
|
||||
|
||||
sutProvider.GetDependency<ISetupIntentCache>().GetSetupIntentIdForSubscriber(provider.Id).Returns(setupIntent.Id);
|
||||
|
||||
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
||||
|
||||
stripeAdapter.SetupIntentGet(setupIntent.Id).Returns(setupIntent);
|
||||
|
||||
await sutProvider.Sut.VerifyBankAccount(provider, descriptorCode);
|
||||
|
||||
await stripeAdapter.Received(1).SetupIntentVerifyMicroDeposit(setupIntent.Id,
|
||||
Arg.Is<SetupIntentVerifyMicrodepositsOptions>(
|
||||
options => options.DescriptorCode == descriptorCode));
|
||||
|
||||
await stripeAdapter.Received(1).PaymentMethodAttachAsync(setupIntent.PaymentMethodId,
|
||||
Arg.Is<PaymentMethodAttachOptions>(
|
||||
options => options.Customer == provider.GatewayCustomerId));
|
||||
|
||||
await stripeAdapter.Received(1).CustomerUpdateAsync(provider.GatewayCustomerId, Arg.Is<CustomerUpdateOptions>(
|
||||
options => options.InvoiceSettings.DefaultPaymentMethod == setupIntent.PaymentMethodId));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IsValidGatewayCustomerIdAsync
|
||||
|
||||
[Theory, BitAutoData]
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.KeyManagement.Queries;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Test.Common.AutoFixture;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.KeyManagement.Queries;
|
||||
|
||||
[SutProviderCustomize]
|
||||
public class KeyConnectorConfirmationDetailsQueryTests
|
||||
{
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task Run_OrganizationNotFound_Throws(SutProvider<KeyConnectorConfirmationDetailsQuery> sutProvider,
|
||||
Guid userId, string orgSsoIdentifier)
|
||||
{
|
||||
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.Run(orgSsoIdentifier, userId));
|
||||
|
||||
await sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.ReceivedWithAnyArgs(0)
|
||||
.GetByOrganizationAsync(Arg.Any<Guid>(), Arg.Any<Guid>());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task Run_OrganizationNotKeyConnector_Throws(
|
||||
SutProvider<KeyConnectorConfirmationDetailsQuery> sutProvider,
|
||||
Guid userId, string orgSsoIdentifier, Organization org)
|
||||
{
|
||||
org.Identifier = orgSsoIdentifier;
|
||||
org.UseKeyConnector = false;
|
||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdentifierAsync(orgSsoIdentifier).Returns(org);
|
||||
|
||||
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.Run(orgSsoIdentifier, userId));
|
||||
|
||||
await sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.ReceivedWithAnyArgs(0)
|
||||
.GetByOrganizationAsync(Arg.Any<Guid>(), Arg.Any<Guid>());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task Run_OrganizationUserNotFound_Throws(SutProvider<KeyConnectorConfirmationDetailsQuery> sutProvider,
|
||||
Guid userId, string orgSsoIdentifier
|
||||
, Organization org)
|
||||
{
|
||||
org.Identifier = orgSsoIdentifier;
|
||||
org.UseKeyConnector = true;
|
||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdentifierAsync(orgSsoIdentifier).Returns(org);
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetByOrganizationAsync(Arg.Any<Guid>(), Arg.Any<Guid>()).Returns(Task.FromResult<OrganizationUser>(null));
|
||||
|
||||
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.Run(orgSsoIdentifier, userId));
|
||||
|
||||
await sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.Received(1)
|
||||
.GetByOrganizationAsync(org.Id, userId);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task Run_Success(SutProvider<KeyConnectorConfirmationDetailsQuery> sutProvider, Guid userId,
|
||||
string orgSsoIdentifier
|
||||
, Organization org, OrganizationUser orgUser)
|
||||
{
|
||||
org.Identifier = orgSsoIdentifier;
|
||||
org.UseKeyConnector = true;
|
||||
orgUser.OrganizationId = org.Id;
|
||||
orgUser.UserId = userId;
|
||||
|
||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdentifierAsync(orgSsoIdentifier).Returns(org);
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>().GetByOrganizationAsync(org.Id, userId)
|
||||
.Returns(orgUser);
|
||||
|
||||
var result = await sutProvider.Sut.Run(orgSsoIdentifier, userId);
|
||||
|
||||
Assert.Equal(org.Name, result.OrganizationName);
|
||||
await sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.Received(1)
|
||||
.GetByOrganizationAsync(org.Id, userId);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,7 @@
|
||||
using Bit.Core.Billing.Constants;
|
||||
using Bit.Core.Billing.Enums;
|
||||
using Bit.Core.Billing.Pricing;
|
||||
using Bit.Core.Billing.Tax.Requests;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Test.Billing.Mocks.Plans;
|
||||
using Bit.Test.Common.AutoFixture;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using NSubstitute;
|
||||
@@ -17,506 +13,6 @@ namespace Bit.Core.Test.Services;
|
||||
[SutProviderCustomize]
|
||||
public class StripePaymentServiceTests
|
||||
{
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task
|
||||
PreviewInvoiceAsync_ForOrganization_CalculatesSalesTaxCorrectlyForFamiliesWithoutAdditionalStorage(
|
||||
SutProvider<StripePaymentService> sutProvider)
|
||||
{
|
||||
var familiesPlan = new FamiliesPlan();
|
||||
sutProvider.GetDependency<IPricingClient>()
|
||||
.GetPlanOrThrow(Arg.Is<PlanType>(p => p == PlanType.FamiliesAnnually))
|
||||
.Returns(familiesPlan);
|
||||
|
||||
var parameters = new PreviewOrganizationInvoiceRequestBody
|
||||
{
|
||||
PasswordManager =
|
||||
new OrganizationPasswordManagerRequestModel
|
||||
{
|
||||
Plan = PlanType.FamiliesAnnually,
|
||||
AdditionalStorage = 0
|
||||
},
|
||||
TaxInformation = new TaxInformationRequestModel { Country = "FR", PostalCode = "12345" }
|
||||
};
|
||||
|
||||
sutProvider.GetDependency<IStripeAdapter>()
|
||||
.InvoiceCreatePreviewAsync(Arg.Is<InvoiceCreatePreviewOptions>(p =>
|
||||
p.Currency == "usd" &&
|
||||
p.SubscriptionDetails.Items.Any(x =>
|
||||
x.Plan == familiesPlan.PasswordManager.StripePlanId &&
|
||||
x.Quantity == 1) &&
|
||||
p.SubscriptionDetails.Items.Any(x =>
|
||||
x.Plan == familiesPlan.PasswordManager.StripeStoragePlanId &&
|
||||
x.Quantity == 0)))
|
||||
.Returns(new Invoice
|
||||
{
|
||||
TotalExcludingTax = 4000,
|
||||
TotalTaxes = [new InvoiceTotalTax { Amount = 800 }],
|
||||
Total = 4800
|
||||
});
|
||||
|
||||
var actual = await sutProvider.Sut.PreviewInvoiceAsync(parameters, null, null);
|
||||
|
||||
Assert.Equal(8M, actual.TaxAmount);
|
||||
Assert.Equal(48M, actual.TotalAmount);
|
||||
Assert.Equal(40M, actual.TaxableBaseAmount);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task PreviewInvoiceAsync_ForOrganization_CalculatesSalesTaxCorrectlyForFamiliesWithAdditionalStorage(
|
||||
SutProvider<StripePaymentService> sutProvider)
|
||||
{
|
||||
var familiesPlan = new FamiliesPlan();
|
||||
sutProvider.GetDependency<IPricingClient>()
|
||||
.GetPlanOrThrow(Arg.Is<PlanType>(p => p == PlanType.FamiliesAnnually))
|
||||
.Returns(familiesPlan);
|
||||
|
||||
var parameters = new PreviewOrganizationInvoiceRequestBody
|
||||
{
|
||||
PasswordManager =
|
||||
new OrganizationPasswordManagerRequestModel
|
||||
{
|
||||
Plan = PlanType.FamiliesAnnually,
|
||||
AdditionalStorage = 1
|
||||
},
|
||||
TaxInformation = new TaxInformationRequestModel { Country = "FR", PostalCode = "12345" }
|
||||
};
|
||||
|
||||
sutProvider.GetDependency<IStripeAdapter>()
|
||||
.InvoiceCreatePreviewAsync(Arg.Is<InvoiceCreatePreviewOptions>(p =>
|
||||
p.Currency == "usd" &&
|
||||
p.SubscriptionDetails.Items.Any(x =>
|
||||
x.Plan == familiesPlan.PasswordManager.StripePlanId &&
|
||||
x.Quantity == 1) &&
|
||||
p.SubscriptionDetails.Items.Any(x =>
|
||||
x.Plan == familiesPlan.PasswordManager.StripeStoragePlanId &&
|
||||
x.Quantity == 1)))
|
||||
.Returns(new Invoice { TotalExcludingTax = 4000, TotalTaxes = [new InvoiceTotalTax { Amount = 800 }], Total = 4800 });
|
||||
|
||||
var actual = await sutProvider.Sut.PreviewInvoiceAsync(parameters, null, null);
|
||||
|
||||
Assert.Equal(8M, actual.TaxAmount);
|
||||
Assert.Equal(48M, actual.TotalAmount);
|
||||
Assert.Equal(40M, actual.TaxableBaseAmount);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task
|
||||
PreviewInvoiceAsync_ForOrganization_CalculatesSalesTaxCorrectlyForFamiliesForEnterpriseWithoutAdditionalStorage(
|
||||
SutProvider<StripePaymentService> sutProvider)
|
||||
{
|
||||
var familiesPlan = new FamiliesPlan();
|
||||
sutProvider.GetDependency<IPricingClient>()
|
||||
.GetPlanOrThrow(Arg.Is<PlanType>(p => p == PlanType.FamiliesAnnually))
|
||||
.Returns(familiesPlan);
|
||||
|
||||
var parameters = new PreviewOrganizationInvoiceRequestBody
|
||||
{
|
||||
PasswordManager = new OrganizationPasswordManagerRequestModel
|
||||
{
|
||||
Plan = PlanType.FamiliesAnnually,
|
||||
SponsoredPlan = PlanSponsorshipType.FamiliesForEnterprise,
|
||||
AdditionalStorage = 0
|
||||
},
|
||||
TaxInformation = new TaxInformationRequestModel { Country = "FR", PostalCode = "12345" }
|
||||
};
|
||||
|
||||
sutProvider.GetDependency<IStripeAdapter>()
|
||||
.InvoiceCreatePreviewAsync(Arg.Is<InvoiceCreatePreviewOptions>(p =>
|
||||
p.Currency == "usd" &&
|
||||
p.SubscriptionDetails.Items.Any(x =>
|
||||
x.Plan == "2021-family-for-enterprise-annually" &&
|
||||
x.Quantity == 1) &&
|
||||
p.SubscriptionDetails.Items.Any(x =>
|
||||
x.Plan == familiesPlan.PasswordManager.StripeStoragePlanId &&
|
||||
x.Quantity == 0)))
|
||||
.Returns(new Invoice { TotalExcludingTax = 0, TotalTaxes = [new InvoiceTotalTax { Amount = 0 }], Total = 0 });
|
||||
|
||||
var actual = await sutProvider.Sut.PreviewInvoiceAsync(parameters, null, null);
|
||||
|
||||
Assert.Equal(0M, actual.TaxAmount);
|
||||
Assert.Equal(0M, actual.TotalAmount);
|
||||
Assert.Equal(0M, actual.TaxableBaseAmount);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task
|
||||
PreviewInvoiceAsync_ForOrganization_CalculatesSalesTaxCorrectlyForFamiliesForEnterpriseWithAdditionalStorage(
|
||||
SutProvider<StripePaymentService> sutProvider)
|
||||
{
|
||||
var familiesPlan = new FamiliesPlan();
|
||||
sutProvider.GetDependency<IPricingClient>()
|
||||
.GetPlanOrThrow(Arg.Is<PlanType>(p => p == PlanType.FamiliesAnnually))
|
||||
.Returns(familiesPlan);
|
||||
|
||||
var parameters = new PreviewOrganizationInvoiceRequestBody
|
||||
{
|
||||
PasswordManager = new OrganizationPasswordManagerRequestModel
|
||||
{
|
||||
Plan = PlanType.FamiliesAnnually,
|
||||
SponsoredPlan = PlanSponsorshipType.FamiliesForEnterprise,
|
||||
AdditionalStorage = 1
|
||||
},
|
||||
TaxInformation = new TaxInformationRequestModel { Country = "FR", PostalCode = "12345" }
|
||||
};
|
||||
|
||||
sutProvider.GetDependency<IStripeAdapter>()
|
||||
.InvoiceCreatePreviewAsync(Arg.Is<InvoiceCreatePreviewOptions>(p =>
|
||||
p.Currency == "usd" &&
|
||||
p.SubscriptionDetails.Items.Any(x =>
|
||||
x.Plan == "2021-family-for-enterprise-annually" &&
|
||||
x.Quantity == 1) &&
|
||||
p.SubscriptionDetails.Items.Any(x =>
|
||||
x.Plan == familiesPlan.PasswordManager.StripeStoragePlanId &&
|
||||
x.Quantity == 1)))
|
||||
.Returns(new Invoice { TotalExcludingTax = 400, TotalTaxes = [new InvoiceTotalTax { Amount = 8 }], Total = 408 });
|
||||
|
||||
var actual = await sutProvider.Sut.PreviewInvoiceAsync(parameters, null, null);
|
||||
|
||||
Assert.Equal(0.08M, actual.TaxAmount);
|
||||
Assert.Equal(4.08M, actual.TotalAmount);
|
||||
Assert.Equal(4M, actual.TaxableBaseAmount);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task PreviewInvoiceAsync_USBased_PersonalUse_SetsAutomaticTaxEnabled(SutProvider<StripePaymentService> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var familiesPlan = new FamiliesPlan();
|
||||
sutProvider.GetDependency<IPricingClient>()
|
||||
.GetPlanOrThrow(Arg.Is<PlanType>(p => p == PlanType.FamiliesAnnually))
|
||||
.Returns(familiesPlan);
|
||||
|
||||
var parameters = new PreviewOrganizationInvoiceRequestBody
|
||||
{
|
||||
PasswordManager = new OrganizationPasswordManagerRequestModel
|
||||
{
|
||||
Plan = PlanType.FamiliesAnnually
|
||||
},
|
||||
TaxInformation = new TaxInformationRequestModel
|
||||
{
|
||||
Country = "US",
|
||||
PostalCode = "12345"
|
||||
}
|
||||
};
|
||||
|
||||
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
||||
stripeAdapter
|
||||
.InvoiceCreatePreviewAsync(Arg.Any<InvoiceCreatePreviewOptions>())
|
||||
.Returns(new Invoice
|
||||
{
|
||||
TotalExcludingTax = 400,
|
||||
TotalTaxes = [new InvoiceTotalTax { Amount = 8 }],
|
||||
Total = 408
|
||||
});
|
||||
|
||||
// Act
|
||||
await sutProvider.Sut.PreviewInvoiceAsync(parameters, null, null);
|
||||
|
||||
// Assert
|
||||
await stripeAdapter.Received(1).InvoiceCreatePreviewAsync(Arg.Is<InvoiceCreatePreviewOptions>(options =>
|
||||
options.AutomaticTax.Enabled == true
|
||||
));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task PreviewInvoiceAsync_USBased_BusinessUse_SetsAutomaticTaxEnabled(SutProvider<StripePaymentService> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var plan = new EnterprisePlan(true);
|
||||
sutProvider.GetDependency<IPricingClient>()
|
||||
.GetPlanOrThrow(Arg.Is<PlanType>(p => p == PlanType.EnterpriseAnnually))
|
||||
.Returns(plan);
|
||||
|
||||
var parameters = new PreviewOrganizationInvoiceRequestBody
|
||||
{
|
||||
PasswordManager = new OrganizationPasswordManagerRequestModel
|
||||
{
|
||||
Plan = PlanType.EnterpriseAnnually
|
||||
},
|
||||
TaxInformation = new TaxInformationRequestModel
|
||||
{
|
||||
Country = "US",
|
||||
PostalCode = "12345"
|
||||
}
|
||||
};
|
||||
|
||||
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
||||
stripeAdapter
|
||||
.InvoiceCreatePreviewAsync(Arg.Any<InvoiceCreatePreviewOptions>())
|
||||
.Returns(new Invoice
|
||||
{
|
||||
TotalExcludingTax = 400,
|
||||
TotalTaxes = [new InvoiceTotalTax { Amount = 8 }],
|
||||
Total = 408
|
||||
});
|
||||
|
||||
// Act
|
||||
await sutProvider.Sut.PreviewInvoiceAsync(parameters, null, null);
|
||||
|
||||
// Assert
|
||||
await stripeAdapter.Received(1).InvoiceCreatePreviewAsync(Arg.Is<InvoiceCreatePreviewOptions>(options =>
|
||||
options.AutomaticTax.Enabled == true
|
||||
));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task PreviewInvoiceAsync_NonUSBased_PersonalUse_SetsAutomaticTaxEnabled(SutProvider<StripePaymentService> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var familiesPlan = new FamiliesPlan();
|
||||
sutProvider.GetDependency<IPricingClient>()
|
||||
.GetPlanOrThrow(Arg.Is<PlanType>(p => p == PlanType.FamiliesAnnually))
|
||||
.Returns(familiesPlan);
|
||||
|
||||
var parameters = new PreviewOrganizationInvoiceRequestBody
|
||||
{
|
||||
PasswordManager = new OrganizationPasswordManagerRequestModel
|
||||
{
|
||||
Plan = PlanType.FamiliesAnnually
|
||||
},
|
||||
TaxInformation = new TaxInformationRequestModel
|
||||
{
|
||||
Country = "FR",
|
||||
PostalCode = "12345"
|
||||
}
|
||||
};
|
||||
|
||||
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
||||
stripeAdapter
|
||||
.InvoiceCreatePreviewAsync(Arg.Any<InvoiceCreatePreviewOptions>())
|
||||
.Returns(new Invoice
|
||||
{
|
||||
TotalExcludingTax = 400,
|
||||
TotalTaxes = [new InvoiceTotalTax { Amount = 8 }],
|
||||
Total = 408
|
||||
});
|
||||
|
||||
// Act
|
||||
await sutProvider.Sut.PreviewInvoiceAsync(parameters, null, null);
|
||||
|
||||
// Assert
|
||||
await stripeAdapter.Received(1).InvoiceCreatePreviewAsync(Arg.Is<InvoiceCreatePreviewOptions>(options =>
|
||||
options.AutomaticTax.Enabled == true
|
||||
));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task PreviewInvoiceAsync_NonUSBased_BusinessUse_SetsAutomaticTaxEnabled(SutProvider<StripePaymentService> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var plan = new EnterprisePlan(true);
|
||||
sutProvider.GetDependency<IPricingClient>()
|
||||
.GetPlanOrThrow(Arg.Is<PlanType>(p => p == PlanType.EnterpriseAnnually))
|
||||
.Returns(plan);
|
||||
|
||||
var parameters = new PreviewOrganizationInvoiceRequestBody
|
||||
{
|
||||
PasswordManager = new OrganizationPasswordManagerRequestModel
|
||||
{
|
||||
Plan = PlanType.EnterpriseAnnually
|
||||
},
|
||||
TaxInformation = new TaxInformationRequestModel
|
||||
{
|
||||
Country = "FR",
|
||||
PostalCode = "12345"
|
||||
}
|
||||
};
|
||||
|
||||
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
||||
stripeAdapter
|
||||
.InvoiceCreatePreviewAsync(Arg.Any<InvoiceCreatePreviewOptions>())
|
||||
.Returns(new Invoice
|
||||
{
|
||||
TotalExcludingTax = 400,
|
||||
TotalTaxes = [new InvoiceTotalTax { Amount = 8 }],
|
||||
Total = 408
|
||||
});
|
||||
|
||||
// Act
|
||||
await sutProvider.Sut.PreviewInvoiceAsync(parameters, null, null);
|
||||
|
||||
// Assert
|
||||
await stripeAdapter.Received(1).InvoiceCreatePreviewAsync(Arg.Is<InvoiceCreatePreviewOptions>(options =>
|
||||
options.AutomaticTax.Enabled == true
|
||||
));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task PreviewInvoiceAsync_USBased_PersonalUse_DoesNotSetTaxExempt(SutProvider<StripePaymentService> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var familiesPlan = new FamiliesPlan();
|
||||
sutProvider.GetDependency<IPricingClient>()
|
||||
.GetPlanOrThrow(Arg.Is<PlanType>(p => p == PlanType.FamiliesAnnually))
|
||||
.Returns(familiesPlan);
|
||||
|
||||
var parameters = new PreviewOrganizationInvoiceRequestBody
|
||||
{
|
||||
PasswordManager = new OrganizationPasswordManagerRequestModel
|
||||
{
|
||||
Plan = PlanType.FamiliesAnnually
|
||||
},
|
||||
TaxInformation = new TaxInformationRequestModel
|
||||
{
|
||||
Country = "US",
|
||||
PostalCode = "12345"
|
||||
}
|
||||
};
|
||||
|
||||
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
||||
stripeAdapter
|
||||
.InvoiceCreatePreviewAsync(Arg.Any<InvoiceCreatePreviewOptions>())
|
||||
.Returns(new Invoice
|
||||
{
|
||||
TotalExcludingTax = 400,
|
||||
TotalTaxes = [new InvoiceTotalTax { Amount = 8 }],
|
||||
Total = 408
|
||||
});
|
||||
|
||||
// Act
|
||||
await sutProvider.Sut.PreviewInvoiceAsync(parameters, null, null);
|
||||
|
||||
// Assert
|
||||
await stripeAdapter.Received(1).InvoiceCreatePreviewAsync(Arg.Is<InvoiceCreatePreviewOptions>(options =>
|
||||
options.CustomerDetails.TaxExempt == null
|
||||
));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task PreviewInvoiceAsync_USBased_BusinessUse_DoesNotSetTaxExempt(SutProvider<StripePaymentService> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var plan = new EnterprisePlan(true);
|
||||
sutProvider.GetDependency<IPricingClient>()
|
||||
.GetPlanOrThrow(Arg.Is<PlanType>(p => p == PlanType.EnterpriseAnnually))
|
||||
.Returns(plan);
|
||||
|
||||
var parameters = new PreviewOrganizationInvoiceRequestBody
|
||||
{
|
||||
PasswordManager = new OrganizationPasswordManagerRequestModel
|
||||
{
|
||||
Plan = PlanType.EnterpriseAnnually
|
||||
},
|
||||
TaxInformation = new TaxInformationRequestModel
|
||||
{
|
||||
Country = "US",
|
||||
PostalCode = "12345"
|
||||
}
|
||||
};
|
||||
|
||||
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
||||
stripeAdapter
|
||||
.InvoiceCreatePreviewAsync(Arg.Any<InvoiceCreatePreviewOptions>())
|
||||
.Returns(new Invoice
|
||||
{
|
||||
TotalExcludingTax = 400,
|
||||
TotalTaxes = [new InvoiceTotalTax { Amount = 8 }],
|
||||
Total = 408
|
||||
});
|
||||
|
||||
// Act
|
||||
await sutProvider.Sut.PreviewInvoiceAsync(parameters, null, null);
|
||||
|
||||
// Assert
|
||||
await stripeAdapter.Received(1).InvoiceCreatePreviewAsync(Arg.Is<InvoiceCreatePreviewOptions>(options =>
|
||||
options.CustomerDetails.TaxExempt == null
|
||||
));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task PreviewInvoiceAsync_NonUSBased_PersonalUse_DoesNotSetTaxExempt(SutProvider<StripePaymentService> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var familiesPlan = new FamiliesPlan();
|
||||
sutProvider.GetDependency<IPricingClient>()
|
||||
.GetPlanOrThrow(Arg.Is<PlanType>(p => p == PlanType.FamiliesAnnually))
|
||||
.Returns(familiesPlan);
|
||||
|
||||
var parameters = new PreviewOrganizationInvoiceRequestBody
|
||||
{
|
||||
PasswordManager = new OrganizationPasswordManagerRequestModel
|
||||
{
|
||||
Plan = PlanType.FamiliesAnnually
|
||||
},
|
||||
TaxInformation = new TaxInformationRequestModel
|
||||
{
|
||||
Country = "FR",
|
||||
PostalCode = "12345"
|
||||
}
|
||||
};
|
||||
|
||||
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
||||
stripeAdapter
|
||||
.InvoiceCreatePreviewAsync(Arg.Any<InvoiceCreatePreviewOptions>())
|
||||
.Returns(new Invoice
|
||||
{
|
||||
TotalExcludingTax = 400,
|
||||
TotalTaxes = [new InvoiceTotalTax { Amount = 8 }],
|
||||
Total = 408
|
||||
});
|
||||
|
||||
// Act
|
||||
await sutProvider.Sut.PreviewInvoiceAsync(parameters, null, null);
|
||||
|
||||
// Assert
|
||||
await stripeAdapter.Received(1).InvoiceCreatePreviewAsync(Arg.Is<InvoiceCreatePreviewOptions>(options =>
|
||||
options.CustomerDetails.TaxExempt == null
|
||||
));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task PreviewInvoiceAsync_NonUSBased_BusinessUse_SetsTaxExemptReverse(SutProvider<StripePaymentService> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var plan = new EnterprisePlan(true);
|
||||
sutProvider.GetDependency<IPricingClient>()
|
||||
.GetPlanOrThrow(Arg.Is<PlanType>(p => p == PlanType.EnterpriseAnnually))
|
||||
.Returns(plan);
|
||||
|
||||
var parameters = new PreviewOrganizationInvoiceRequestBody
|
||||
{
|
||||
PasswordManager = new OrganizationPasswordManagerRequestModel
|
||||
{
|
||||
Plan = PlanType.EnterpriseAnnually
|
||||
},
|
||||
TaxInformation = new TaxInformationRequestModel
|
||||
{
|
||||
Country = "FR",
|
||||
PostalCode = "12345"
|
||||
}
|
||||
};
|
||||
|
||||
var stripeAdapter = sutProvider.GetDependency<IStripeAdapter>();
|
||||
stripeAdapter
|
||||
.InvoiceCreatePreviewAsync(Arg.Any<InvoiceCreatePreviewOptions>())
|
||||
.Returns(new Invoice
|
||||
{
|
||||
TotalExcludingTax = 400,
|
||||
TotalTaxes = [new InvoiceTotalTax { Amount = 8 }],
|
||||
Total = 408
|
||||
});
|
||||
|
||||
// Act
|
||||
await sutProvider.Sut.PreviewInvoiceAsync(parameters, null, null);
|
||||
|
||||
// Assert
|
||||
await stripeAdapter.Received(1).InvoiceCreatePreviewAsync(Arg.Is<InvoiceCreatePreviewOptions>(options =>
|
||||
options.CustomerDetails.TaxExempt == StripeConstants.TaxExempt.Reverse
|
||||
));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task GetSubscriptionAsync_WithCustomerDiscount_ReturnsDiscountFromCustomer(
|
||||
|
||||
Reference in New Issue
Block a user