diff --git a/ClearBank.DeveloperTest.Tests/ClearBank.DeveloperTest.Tests.csproj b/ClearBank.DeveloperTest.Tests/ClearBank.DeveloperTest.Tests.csproj index b21ab73..bc0ae4b 100644 --- a/ClearBank.DeveloperTest.Tests/ClearBank.DeveloperTest.Tests.csproj +++ b/ClearBank.DeveloperTest.Tests/ClearBank.DeveloperTest.Tests.csproj @@ -3,10 +3,16 @@ net8.0 true + true + + + + + \ No newline at end of file diff --git a/ClearBank.DeveloperTest.Tests/Tests/PaymentServiceTests.cs b/ClearBank.DeveloperTest.Tests/Tests/PaymentServiceTests.cs new file mode 100644 index 0000000..cbd9524 --- /dev/null +++ b/ClearBank.DeveloperTest.Tests/Tests/PaymentServiceTests.cs @@ -0,0 +1,73 @@ +using ClearBank.DeveloperTest.Data; +using ClearBank.DeveloperTest.Services; +using ClearBank.DeveloperTest.Types; +using System; +using Xunit; + +namespace ClearBank.DeveloperTest.Tests +{ + public class PaymentServiceTests + { + private class FakeStore : IAccountDataStore + { + public Account Account { get; set; } + + public Account GetAccount(string accountNumber) => Account; + + public void UpdateAccount(Account account) => Account = account; + } + + private class FakeFactory : IAccountDataStoreFactory + { + public FakeStore Store { get; } = new FakeStore(); + + public IAccountDataStore Create(string type) => Store; + } + + [Fact] + public void Bacs_Fails_When_Account_Null() + { + var factory = new FakeFactory(); + factory.Store.Account = null; + + var svc = new PaymentService(factory); + + var req = new MakePaymentRequest { DebtorAccountNumber = "123", Amount = 10m, PaymentScheme = PaymentScheme.Bacs }; + + var result = svc.MakePayment(req); + + Assert.False(result.Success); + } + + [Fact] + public void FasterPayments_Fails_When_Insufficient_Balance() + { + var factory = new FakeFactory(); + factory.Store.Account = new Account { AccountNumber = "123", Balance = 5m, AllowedPaymentSchemes = AllowedPaymentSchemes.FasterPayments }; + + var svc = new PaymentService(factory); + + var req = new MakePaymentRequest { DebtorAccountNumber = "123", Amount = 10m, PaymentScheme = PaymentScheme.FasterPayments }; + + var result = svc.MakePayment(req); + + Assert.False(result.Success); + } + + [Fact] + public void Chaps_Succeeds_When_Live_And_Allowed() + { + var factory = new FakeFactory(); + factory.Store.Account = new Account { AccountNumber = "123", Balance = 100m, AllowedPaymentSchemes = AllowedPaymentSchemes.Chaps, Status = AccountStatus.Live }; + + var svc = new PaymentService(factory); + + var req = new MakePaymentRequest { DebtorAccountNumber = "123", Amount = 10m, PaymentScheme = PaymentScheme.Chaps }; + + var result = svc.MakePayment(req); + + Assert.True(result.Success); + Assert.Equal(90m, factory.Store.Account.Balance); + } + } +} diff --git a/ClearBank.DeveloperTest/Data/AccountDataStore.cs b/ClearBank.DeveloperTest/Data/AccountDataStore.cs index 89e49dd..a2b8360 100644 --- a/ClearBank.DeveloperTest/Data/AccountDataStore.cs +++ b/ClearBank.DeveloperTest/Data/AccountDataStore.cs @@ -2,7 +2,7 @@ namespace ClearBank.DeveloperTest.Data { - public class AccountDataStore + public class AccountDataStore : IAccountDataStore { public Account GetAccount(string accountNumber) { diff --git a/ClearBank.DeveloperTest/Data/AccountDataStoreFactory.cs b/ClearBank.DeveloperTest/Data/AccountDataStoreFactory.cs new file mode 100644 index 0000000..4c95b7c --- /dev/null +++ b/ClearBank.DeveloperTest/Data/AccountDataStoreFactory.cs @@ -0,0 +1,17 @@ +using System.Configuration; + +namespace ClearBank.DeveloperTest.Data +{ + public class AccountDataStoreFactory : IAccountDataStoreFactory + { + public IAccountDataStore Create(string type) + { + if (type == "Backup") + { + return new BackupAccountDataStore(); + } + + return new AccountDataStore(); + } + } +} diff --git a/ClearBank.DeveloperTest/Data/BackupAccountDataStore.cs b/ClearBank.DeveloperTest/Data/BackupAccountDataStore.cs index 170362c..d00fbaa 100644 --- a/ClearBank.DeveloperTest/Data/BackupAccountDataStore.cs +++ b/ClearBank.DeveloperTest/Data/BackupAccountDataStore.cs @@ -2,7 +2,7 @@ namespace ClearBank.DeveloperTest.Data { - public class BackupAccountDataStore + public class BackupAccountDataStore : IAccountDataStore { public Account GetAccount(string accountNumber) { diff --git a/ClearBank.DeveloperTest/Data/IAccountDataStore.cs b/ClearBank.DeveloperTest/Data/IAccountDataStore.cs new file mode 100644 index 0000000..258ad77 --- /dev/null +++ b/ClearBank.DeveloperTest/Data/IAccountDataStore.cs @@ -0,0 +1,10 @@ +using ClearBank.DeveloperTest.Types; + +namespace ClearBank.DeveloperTest.Data +{ + public interface IAccountDataStore + { + Account GetAccount(string accountNumber); + void UpdateAccount(Account account); + } +} diff --git a/ClearBank.DeveloperTest/Data/IAccountDataStoreFactory.cs b/ClearBank.DeveloperTest/Data/IAccountDataStoreFactory.cs new file mode 100644 index 0000000..428497b --- /dev/null +++ b/ClearBank.DeveloperTest/Data/IAccountDataStoreFactory.cs @@ -0,0 +1,7 @@ +namespace ClearBank.DeveloperTest.Data +{ + public interface IAccountDataStoreFactory + { + IAccountDataStore Create(string type); + } +} diff --git a/ClearBank.DeveloperTest/Services/PaymentService.cs b/ClearBank.DeveloperTest/Services/PaymentService.cs index 573713d..9aba5b0 100644 --- a/ClearBank.DeveloperTest/Services/PaymentService.cs +++ b/ClearBank.DeveloperTest/Services/PaymentService.cs @@ -6,65 +6,44 @@ namespace ClearBank.DeveloperTest.Services { public class PaymentService : IPaymentService { + private readonly IAccountDataStoreFactory _factory; + + // Default constructor preserves original behavior + public PaymentService() : this(new AccountDataStoreFactory()) { } + + // Allow injection for testability + public PaymentService(IAccountDataStoreFactory factory) + { + _factory = factory; + } + public MakePaymentResult MakePayment(MakePaymentRequest request) { var dataStoreType = ConfigurationManager.AppSettings["DataStoreType"]; - Account account = null; + var accountStore = _factory.Create(dataStoreType); + var account = accountStore.GetAccount(request.DebtorAccountNumber); - if (dataStoreType == "Backup") - { - var accountDataStore = new BackupAccountDataStore(); - account = accountDataStore.GetAccount(request.DebtorAccountNumber); - } - else - { - var accountDataStore = new AccountDataStore(); - account = accountDataStore.GetAccount(request.DebtorAccountNumber); - } - - var result = new MakePaymentResult(); + var result = new MakePaymentResult { Success = true }; - result.Success = true; - switch (request.PaymentScheme) { case PaymentScheme.Bacs: - if (account == null) - { - result.Success = false; - } - else if (!account.AllowedPaymentSchemes.HasFlag(AllowedPaymentSchemes.Bacs)) + if (account == null || !account.AllowedPaymentSchemes.HasFlag(AllowedPaymentSchemes.Bacs)) { result.Success = false; } break; case PaymentScheme.FasterPayments: - if (account == null) - { - result.Success = false; - } - else if (!account.AllowedPaymentSchemes.HasFlag(AllowedPaymentSchemes.FasterPayments)) - { - result.Success = false; - } - else if (account.Balance < request.Amount) + if (account == null || !account.AllowedPaymentSchemes.HasFlag(AllowedPaymentSchemes.FasterPayments) || account.Balance < request.Amount) { result.Success = false; } break; case PaymentScheme.Chaps: - if (account == null) - { - result.Success = false; - } - else if (!account.AllowedPaymentSchemes.HasFlag(AllowedPaymentSchemes.Chaps)) - { - result.Success = false; - } - else if (account.Status != AccountStatus.Live) + if (account == null || !account.AllowedPaymentSchemes.HasFlag(AllowedPaymentSchemes.Chaps) || account.Status != AccountStatus.Live) { result.Success = false; } @@ -74,17 +53,7 @@ public MakePaymentResult MakePayment(MakePaymentRequest request) if (result.Success) { account.Balance -= request.Amount; - - if (dataStoreType == "Backup") - { - var accountDataStore = new BackupAccountDataStore(); - accountDataStore.UpdateAccount(account); - } - else - { - var accountDataStore = new AccountDataStore(); - accountDataStore.UpdateAccount(account); - } + accountStore.UpdateAccount(account); } return result;