Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
{
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Eventing.Reader;
using System.Linq;
using System.Threading;
Expand Down Expand Up @@ -267,6 +268,7 @@ await this.TransactionAggregateManager.RecordAdditionalResponseData(estateId,
/// <param name="fieldName">Name of the field.</param>
/// <param name="additionalTransactionMetadata">The additional transaction metadata.</param>
/// <returns></returns>
[ExcludeFromCodeCoverage]
private T ExtractFieldFromMetadata<T>(String fieldName,
Dictionary<String, String> additionalTransactionMetadata)
{
Expand Down Expand Up @@ -304,6 +306,7 @@ await this.EstateClient.AddDeviceToMerchant(this.TokenResponse.AccessToken,
/// Generates the transaction reference.
/// </summary>
/// <returns></returns>
[ExcludeFromCodeCoverage]
private String GenerateTransactionReference()
{
Int64 i = 1;
Expand Down
4 changes: 4 additions & 0 deletions TransactionProcessor.Testing/TestData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ public class TestData

public static Guid EstateId = Guid.Parse("A522FA27-F9D0-470A-A88D-325DED3B62EE");

public static Guid ContractId = Guid.Parse("97A9ED00-E522-428C-B3C3-5931092DBDCE");

public static Guid ProductId = Guid.Parse("ABA0E536-4E43-4E26-8362-7FB549DDA534");

public static String EstateName = "Test Estate 1";

public static String FailedSafaricomTopup =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
namespace TransactionProcessor.Transaction.DomainEvents
{
using System;
using Newtonsoft.Json;
using Shared.DomainDrivenDesign.EventSourcing;

/// <summary>
///
/// </summary>
/// <seealso cref="Shared.DomainDrivenDesign.EventSourcing.DomainEvent" />
[JsonObject]
public class ProductDetailsAddedToTransactionEvent : DomainEvent
{
#region Constructors

/// <summary>
/// Initializes a new instance of the <see cref="ProductDetailsAddedToTransactionEvent" /> class.
/// </summary>
/// <param name="aggregateId">The aggregate identifier.</param>
/// <param name="eventId">The event identifier.</param>
/// <param name="estateId">The estate identifier.</param>
/// <param name="contractId">The contract identifier.</param>
/// <param name="productId">The product identifier.</param>
private ProductDetailsAddedToTransactionEvent(Guid aggregateId,
Guid eventId,
Guid estateId,
Guid contractId,
Guid productId) : base(aggregateId, eventId)
{
this.TransactionId = aggregateId;
this.EstateId = estateId;
this.ContractId = contractId;
this.ProductId = productId;
}

#endregion

#region Properties

/// <summary>
/// Gets the contract identifier.
/// </summary>
/// <value>
/// The contract identifier.
/// </value>
[JsonProperty]
public Guid ContractId { get; private set; }

/// <summary>
/// Gets the estate identifier.
/// </summary>
/// <value>
/// The estate identifier.
/// </value>
[JsonProperty]
public Guid EstateId { get; private set; }

/// <summary>
/// Gets the product identifier.
/// </summary>
/// <value>
/// The product identifier.
/// </value>
[JsonProperty]
public Guid ProductId { get; private set; }

/// <summary>
/// Gets the transaction identifier.
/// </summary>
/// <value>
/// The transaction identifier.
/// </value>
[JsonProperty]
public Guid TransactionId { get; private set; }

#endregion

#region Methods

/// <summary>
/// Creates the specified aggregate identifier.
/// </summary>
/// <param name="aggregateId">The aggregate identifier.</param>
/// <param name="estateId">The estate identifier.</param>
/// <param name="contractId">The contract identifier.</param>
/// <param name="productId">The product identifier.</param>
/// <returns></returns>
public static ProductDetailsAddedToTransactionEvent Create(Guid aggregateId,
Guid estateId,
Guid contractId,
Guid productId)
{
return new ProductDetailsAddedToTransactionEvent(aggregateId, Guid.NewGuid(), estateId, contractId, productId);
}

#endregion
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -230,5 +230,21 @@ public void CustomerEmailReceiptRequestedEvent_CanBeCreated_IsCreated()
customerEmailReceiptRequestedEvent.MerchantId.ShouldBe(TestData.MerchantId);
customerEmailReceiptRequestedEvent.CustomerEmailAddress.ShouldBe(TestData.CustomerEmailAddress);
}

[Fact]
public void ProductDetailsAddedToTransactionEvent_CanBeCreated_IsCreated()
{
ProductDetailsAddedToTransactionEvent productDetailsAddedToTransactionEvent = ProductDetailsAddedToTransactionEvent.Create(TestData.TransactionId, TestData.EstateId,TestData.ContractId, TestData.ProductId);

productDetailsAddedToTransactionEvent.ShouldNotBeNull();
productDetailsAddedToTransactionEvent.AggregateId.ShouldBe(TestData.TransactionId);
productDetailsAddedToTransactionEvent.EventCreatedDateTime.ShouldNotBe(DateTime.MinValue);
productDetailsAddedToTransactionEvent.EventId.ShouldNotBe(Guid.Empty);
productDetailsAddedToTransactionEvent.TransactionId.ShouldBe(TestData.TransactionId);
productDetailsAddedToTransactionEvent.EstateId.ShouldBe(TestData.EstateId);
productDetailsAddedToTransactionEvent.ProductId.ShouldBe(TestData.ProductId);
productDetailsAddedToTransactionEvent.ContractId.ShouldBe(TestData.ContractId);

}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ public void TransactionAggregate_CanBeCreated_IsCreated()
}

[Theory]
[InlineData(TransactionType.Logon)]
[InlineData(TransactionType.Sale)]
public void TransactionAggregate_StartTransaction_TransactionIsStarted(TransactionType transactionType)
{
Expand All @@ -38,6 +37,25 @@ public void TransactionAggregate_StartTransaction_TransactionIsStarted(Transacti
transactionAggregate.TransactionAmount.ShouldBe(TestData.TransactionAmount);
}

[Theory]
[InlineData(TransactionType.Logon)]
public void TransactionAggregate_StartTransaction_NullAmount_TransactionIsStarted(TransactionType transactionType)
{
TransactionAggregate transactionAggregate = TransactionAggregate.Create(TestData.TransactionId);
transactionAggregate.StartTransaction(TestData.TransactionDateTime, TestData.TransactionNumber, transactionType,
TestData.TransactionReference, TestData.EstateId, TestData.MerchantId, TestData.DeviceIdentifier, null);

transactionAggregate.IsStarted.ShouldBeTrue();
transactionAggregate.TransactionDateTime.ShouldBe(TestData.TransactionDateTime);
transactionAggregate.TransactionNumber.ShouldBe(TestData.TransactionNumber);
transactionAggregate.TransactionType.ShouldBe(transactionType);
transactionAggregate.EstateId.ShouldBe(TestData.EstateId);
transactionAggregate.MerchantId.ShouldBe(TestData.MerchantId);
transactionAggregate.DeviceIdentifier.ShouldBe(TestData.DeviceIdentifier);
transactionAggregate.TransactionReference.ShouldBe(TestData.TransactionReference);
transactionAggregate.TransactionAmount.ShouldBeNull();
}

[Theory]
[InlineData(TransactionType.Logon)]
[InlineData(TransactionType.Sale)]
Expand Down Expand Up @@ -144,6 +162,65 @@ public void TransactionAggregate_StartTransaction_InvalidData_ErrorThrown(Boolea
});
}

[Theory]
[InlineData(TransactionType.Sale)]
public void TransactionAggregate_AddProductDetails_ProductDetailsAdded(TransactionType transactionType)
{
TransactionAggregate transactionAggregate = TransactionAggregate.Create(TestData.TransactionId);
transactionAggregate.StartTransaction(TestData.TransactionDateTime, TestData.TransactionNumber, transactionType, TestData.TransactionReference, TestData.EstateId, TestData.MerchantId, TestData.DeviceIdentifier,
TestData.TransactionAmount);

transactionAggregate.AddProductDetails(TestData.ContractId,TestData.ProductId);

transactionAggregate.IsProductDetailsAdded.ShouldBeTrue();
transactionAggregate.ContractId.ShouldBe(TestData.ContractId);
transactionAggregate.ProductId.ShouldBe(TestData.ProductId);
}

[Theory]
[InlineData(TransactionType.Sale)]
public void TransactionAggregate_AddProductDetails_InvalidContractId_ErrorThrown(TransactionType transactionType)
{
TransactionAggregate transactionAggregate = TransactionAggregate.Create(TestData.TransactionId);
transactionAggregate.StartTransaction(TestData.TransactionDateTime, TestData.TransactionNumber, transactionType, TestData.TransactionReference, TestData.EstateId, TestData.MerchantId, TestData.DeviceIdentifier,
TestData.TransactionAmount);

Should.Throw<ArgumentException>(() =>
{
transactionAggregate.AddProductDetails(Guid.Empty, TestData.ProductId);
});
}

[Theory]
[InlineData(TransactionType.Sale)]
public void TransactionAggregate_AddProductDetails_InvalidProductId_ErrorThrown(TransactionType transactionType)
{
TransactionAggregate transactionAggregate = TransactionAggregate.Create(TestData.TransactionId);
transactionAggregate.StartTransaction(TestData.TransactionDateTime, TestData.TransactionNumber, transactionType, TestData.TransactionReference, TestData.EstateId, TestData.MerchantId, TestData.DeviceIdentifier,
TestData.TransactionAmount);

Should.Throw<ArgumentException>(() =>
{
transactionAggregate.AddProductDetails(TestData.ContractId, Guid.Empty);
});
}

[Theory]
[InlineData(TransactionType.Sale)]
public void TransactionAggregate_AddProductDetails_ProductDetailsAlreadyAdded_ErrorThrown(TransactionType transactionType)
{
TransactionAggregate transactionAggregate = TransactionAggregate.Create(TestData.TransactionId);
transactionAggregate.StartTransaction(TestData.TransactionDateTime, TestData.TransactionNumber, transactionType, TestData.TransactionReference, TestData.EstateId, TestData.MerchantId, TestData.DeviceIdentifier,
TestData.TransactionAmount);

transactionAggregate.AddProductDetails(TestData.ContractId, TestData.ProductId);

Should.Throw<InvalidOperationException>(() =>
{
transactionAggregate.AddProductDetails(TestData.ContractId, TestData.ProductId);
});
}

[Theory]
[InlineData(TransactionType.Logon)]
public void TransactionAggregate_AuthoriseTransactionLocally_TransactionIsAuthorised(TransactionType transactionType)
Expand Down
59 changes: 59 additions & 0 deletions TransactionProcessor.TransactionAgrgegate/TransactionAggregate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,21 @@ private TransactionAggregate(Guid aggregateId)
/// </value>
public Guid EstateId { get; private set; }

/// <summary>
/// Gets the contract identifier.
/// </summary>
/// <value>
/// The contract identifier.
/// </value>
public Guid ContractId { get; private set; }
/// <summary>
/// Gets the product identifier.
/// </summary>
/// <value>
/// The product identifier.
/// </value>
public Guid ProductId { get; private set; }

/// <summary>
/// Gets a value indicating whether this instance is authorised.
/// </summary>
Expand Down Expand Up @@ -471,6 +486,27 @@ public void StartTransaction(DateTime transactionDateTime,
this.ApplyAndPend(transactionHasStartedEvent);
}

/// <summary>
/// Adds the product details.
/// </summary>
/// <param name="contractId">The contract identifier.</param>
/// <param name="productId">The product identifier.</param>
public void AddProductDetails(Guid contractId,
Guid productId)
{
Guard.ThrowIfInvalidGuid(contractId, typeof(ArgumentException), $"Contract Id must not be [{Guid.Empty}]");
Guard.ThrowIfInvalidGuid(productId, typeof(ArgumentException), $"Product Id must not be [{Guid.Empty}]");

this.CheckProductDetailsNotAlreadyAdded();

ProductDetailsAddedToTransactionEvent productDetailsAddedToTransactionEvent = ProductDetailsAddedToTransactionEvent.Create(this.AggregateId,
this.EstateId,
contractId,
productId);

this.ApplyAndPend(productDetailsAddedToTransactionEvent);
}

/// <summary>
/// Gets the metadata.
/// </summary>
Expand All @@ -493,6 +529,14 @@ protected override void PlayEvent(DomainEvent domainEvent)
this.PlayEvent((dynamic)domainEvent);
}

private void CheckProductDetailsNotAlreadyAdded()
{
if (this.IsProductDetailsAdded)
{
throw new InvalidOperationException("Product details already added");
}
}

/// <summary>
/// Checks the additional request data not already recorded.
/// </summary>
Expand Down Expand Up @@ -737,6 +781,21 @@ private void PlayEvent(TransactionDeclinedByOperatorEvent domainEvent)
this.ResponseMessage = domainEvent.ResponseMessage;
}

private void PlayEvent(ProductDetailsAddedToTransactionEvent domainEvent)
{
this.IsProductDetailsAdded = true;
this.ContractId = domainEvent.ContractId;
this.ProductId = domainEvent.ProductId;
}

/// <summary>
/// Gets a value indicating whether this instance is product details added.
/// </summary>
/// <value>
/// <c>true</c> if this instance is product details added; otherwise, <c>false</c>.
/// </value>
public Boolean IsProductDetailsAdded { get; private set; }

#endregion
}
}