Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions ClearBank.DeveloperTest.Tests/ClearBank.DeveloperTest.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,18 @@
<IsTestProject>true</IsTestProject>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.0" />
<PackageReference Include="Moq" Version="4.20.72" />
<PackageReference Include="MSTest.TestAdapter" Version="3.11.0" />
<PackageReference Include="MSTest.TestFramework" Version="3.11.0" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\ClearBank.DeveloperTest\ClearBank.DeveloperTest.csproj" />
</ItemGroup>
Expand Down
182 changes: 182 additions & 0 deletions ClearBank.DeveloperTest.Tests/PaymentServiceTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
using ClearBank.DeveloperTest.Data;
using ClearBank.DeveloperTest.Services;
using ClearBank.DeveloperTest.Types;
using Microsoft.Extensions.Logging;
using Moq;
using Xunit;

namespace ClearBank.DeveloperTest.Tests
{
public class PaymentServiceTests
{
private readonly Mock<IAccountDataStore> _accountDataStoreMock;

private readonly Mock<ILogger<PaymentService>> _loggerMock;

public PaymentServiceTests()
{
_accountDataStoreMock = new Mock<IAccountDataStore>();
_loggerMock = new Mock<ILogger<PaymentService>>();
}

//Define special condition accounts to be used in tests
private readonly Account _accountNoBacs = new Account
{
AccountNumber = "123456",
Balance = 500,
Status = AccountStatus.Live,
AllowedPaymentSchemes = AllowedPaymentSchemes.FasterPayments | AllowedPaymentSchemes.Chaps
};

private readonly Account _noBalanceFasterPaymentAccount = new Account
{
AccountNumber = "111222",
Balance = 0,
Status = AccountStatus.Live,
AllowedPaymentSchemes = AllowedPaymentSchemes.FasterPayments
};

private readonly Account _disabledChapsAccount = new Account
{
AccountNumber = "111222",
Balance = 500,
Status = AccountStatus.Disabled,
AllowedPaymentSchemes = AllowedPaymentSchemes.Chaps
};

[Fact]
public void MakePayment_ShouldReturnFalse_WhenAccountIsNull()
{
// Arrange
var paymentService = new PaymentService(_accountDataStoreMock.Object, _loggerMock.Object);
var request = new MakePaymentRequest
{
DebtorAccountNumber = "12345678",
Amount = 100,
PaymentScheme = PaymentScheme.Bacs
};
_accountDataStoreMock.Setup(x => x.GetAccount(request.DebtorAccountNumber)).Returns((Account)null);
// Act
var result = paymentService.MakePayment(request);
// Assert
Assert.False(result.Success);
}

[Fact]
public void MakeBacsPayment_ShouldReturnFalse_WhenAccountBacsPaymentSchemeIsDisallowed()
{
// Arrange
var paymentService = new PaymentService(_accountDataStoreMock.Object, _loggerMock.Object);
var request = new MakePaymentRequest
{
DebtorAccountNumber = "12345678",
Amount = 100,
PaymentScheme = PaymentScheme.Bacs
};
_accountDataStoreMock.Setup(x => x.GetAccount(request.DebtorAccountNumber)).Returns(_accountNoBacs);
// Act
var result = paymentService.MakePayment(request);
// Assert
Assert.False(result.Success);
}

[Fact]
public void MakeFasterPaymentsPayment_ShouldReturnFalse_WhenAccountHasZeroBalance()
{
// Arrange
var paymentService = new PaymentService(_accountDataStoreMock.Object, _loggerMock.Object);
var request = new MakePaymentRequest
{
DebtorAccountNumber = "12345678",
Amount = 100,
PaymentScheme = PaymentScheme.FasterPayments
};
_accountDataStoreMock.Setup(x => x.GetAccount(request.DebtorAccountNumber)).Returns(_noBalanceFasterPaymentAccount);
// Act
var result = paymentService.MakePayment(request);
// Assert
Assert.False(result.Success);
}

[Fact]
public void MakeChapsPayment_ShouldReturnFalse_WhenAccountAccountStatusIsDisabled()
{
// Arrange
var paymentService = new PaymentService(_accountDataStoreMock.Object, _loggerMock.Object);
var request = new MakePaymentRequest
{
DebtorAccountNumber = "12345678",
Amount = 100,
PaymentScheme = PaymentScheme.Chaps
};
_accountDataStoreMock.Setup(x => x.GetAccount(request.DebtorAccountNumber)).Returns(_disabledChapsAccount);
// Act
var result = paymentService.MakePayment(request);
// Assert
Assert.False(result.Success);
}

[Theory]
[InlineData(PaymentScheme.Bacs, AllowedPaymentSchemes.Bacs)]
[InlineData(PaymentScheme.FasterPayments, AllowedPaymentSchemes.FasterPayments)]
[InlineData(PaymentScheme.Chaps, AllowedPaymentSchemes.Chaps)]
[InlineData(PaymentScheme.Bacs, AllowedPaymentSchemes.Bacs | AllowedPaymentSchemes.Chaps | AllowedPaymentSchemes.FasterPayments)]
[InlineData(PaymentScheme.Chaps, AllowedPaymentSchemes.Chaps | AllowedPaymentSchemes.FasterPayments)]
[InlineData(PaymentScheme.FasterPayments, AllowedPaymentSchemes.Bacs | AllowedPaymentSchemes.FasterPayments)]
public void MakePayment_ShouldReturnTrue_WhenAccountIsActive_AndHasCorrectPaymentScheme(PaymentScheme paymentScheme, AllowedPaymentSchemes allowedPaymentSchemes)
{
// Arrange
var paymentService = new PaymentService(_accountDataStoreMock.Object, _loggerMock.Object);

var account = new Account
{
AccountNumber = "12345678",
Balance = 500,
Status = AccountStatus.Live,
AllowedPaymentSchemes = allowedPaymentSchemes
};
var request = new MakePaymentRequest
{
DebtorAccountNumber = "12345678",
Amount = 100,
PaymentScheme = paymentScheme
};
_accountDataStoreMock.Setup(x => x.GetAccount(request.DebtorAccountNumber)).Returns(account);
// Act
var result = paymentService.MakePayment(request);
// Assert
Assert.True(result.Success);
}

[Theory]
[InlineData(PaymentScheme.Chaps, AllowedPaymentSchemes.Bacs)]
[InlineData(PaymentScheme.Bacs, AllowedPaymentSchemes.FasterPayments)]
[InlineData(PaymentScheme.FasterPayments, AllowedPaymentSchemes.Chaps)]
[InlineData(PaymentScheme.Bacs, AllowedPaymentSchemes.Chaps | AllowedPaymentSchemes.FasterPayments)]
[InlineData(PaymentScheme.Chaps, AllowedPaymentSchemes.Bacs | AllowedPaymentSchemes.FasterPayments)]
public void MakePayment_ShouldReturnFalse_WhenAccountIsActive_AndHasIncorrectPaymentScheme(PaymentScheme paymentScheme, AllowedPaymentSchemes allowedPaymentSchemes)
{
// Arrange
var paymentService = new PaymentService(_accountDataStoreMock.Object, _loggerMock.Object);

var account = new Account
{
AccountNumber = "12345678",
Balance = 500,
Status = AccountStatus.Live,
AllowedPaymentSchemes = allowedPaymentSchemes
};
var request = new MakePaymentRequest
{
DebtorAccountNumber = "12345678",
Amount = 100,
PaymentScheme = paymentScheme
};
_accountDataStoreMock.Setup(x => x.GetAccount(request.DebtorAccountNumber)).Returns(account);
// Act
var result = paymentService.MakePayment(request);
// Assert
Assert.False(result.Success);
}
}
}
8 changes: 5 additions & 3 deletions ClearBank.DeveloperTest/ClearBank.DeveloperTest.csproj
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="System.Configuration.ConfigurationManager" Version="8.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
</ItemGroup>

</Project>
</Project>
24 changes: 24 additions & 0 deletions ClearBank.DeveloperTest/Controllers/PaymentServiceController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using ClearBank.DeveloperTest.Services;
using ClearBank.DeveloperTest.Types;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;

namespace ClearBank.DeveloperTest.Controllers
{
[ApiController]
[Route("[controller]")]
public class PaymentServiceController : ControllerBase
{
private readonly IPaymentService _paymentService;
public PaymentServiceController(IPaymentService paymentService)
{
_paymentService = paymentService;
}

[HttpPost]
public MakePaymentResult Post(MakePaymentRequest request)
{
return _paymentService.MakePayment(request);
}
}
}
7 changes: 6 additions & 1 deletion ClearBank.DeveloperTest/Data/AccountDataStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,13 @@

namespace ClearBank.DeveloperTest.Data
{
public class AccountDataStore
public class AccountDataStore : IAccountDataStore
{
public AccountDataStore()
{
// I'd add here the database context, or the account service provided its an external service
}

public Account GetAccount(string accountNumber)
{
// Access database to retrieve account, code removed for brevity
Expand Down
7 changes: 6 additions & 1 deletion ClearBank.DeveloperTest/Data/BackupAccountDataStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,13 @@

namespace ClearBank.DeveloperTest.Data
{
public class BackupAccountDataStore
public class BackupAccountDataStore : IAccountDataStore
{
public BackupAccountDataStore()
{
// I'd add here the backup database context if needed, or the backup account service provided its an external service
}

public Account GetAccount(string accountNumber)
{
// Access backup data base to retrieve account, code removed for brevity
Expand Down
11 changes: 11 additions & 0 deletions ClearBank.DeveloperTest/Data/IAccountDataStore.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using ClearBank.DeveloperTest.Types;

namespace ClearBank.DeveloperTest.Data
{
public interface IAccountDataStore
{
Account GetAccount(string accountNumber);

void UpdateAccount(Account account);
}
}
42 changes: 42 additions & 0 deletions ClearBank.DeveloperTest/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using ClearBank.DeveloperTest.Services;
using ClearBank.DeveloperTest.Data;
using ClearBank.DeveloperTest.Types;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddLogging();

bool useBackupDataStore = (bool)builder.Configuration.GetValue(typeof(bool), "UseBackupDataStore");
if (useBackupDataStore)
{
builder.Services.AddSingleton<IAccountDataStore, BackupAccountDataStore>();
}
else
{
builder.Services.AddSingleton<IAccountDataStore, AccountDataStore>();
}

builder.Services.AddScoped<IPaymentService, PaymentService>();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();
Loading