Skip to content

Commit

Permalink
Optimize completion (#35)
Browse files Browse the repository at this point in the history
* wip

* Add Transaction logic for incoming Data
  • Loading branch information
tinohager committed Aug 7, 2023
1 parent 5c4911c commit 783f97d
Show file tree
Hide file tree
Showing 7 changed files with 261 additions and 97 deletions.
6 changes: 3 additions & 3 deletions src/Portalum.Zvt.TerminalSimulator/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

class Program
{
private static readonly byte[] _acknowledgePackage = new byte[] { 0x80, 0x00, 0x00 };
private static readonly byte[] _commandCompletionPackage = new byte[] { 0x80, 0x00, 0x00 };
private static readonly byte[] _completionPackage = new byte[] { 0x06, 0x0F, 0x00 };

private static SimpleTcpServer? _tcpServer;
Expand Down Expand Up @@ -38,8 +38,8 @@ private static void Events_DataReceived(object? sender, DataReceivedEventArgs e)
{
Thread.Sleep(500);

Console.WriteLine("Send Acknowledge");
_tcpServer.Send(e.IpPort, _acknowledgePackage);
Console.WriteLine("Send Command Completion");
_tcpServer.Send(e.IpPort, _commandCompletionPackage);

Thread.Sleep(1000);

Expand Down
98 changes: 89 additions & 9 deletions src/Portalum.Zvt.UnitTest/ZvtClientTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace Portalum.Zvt.UnitTest
public class ZvtClientTest
{
[TestMethod]
public async Task EndOfDayAsync_AcknowledgeReceivedWithoutCompletionReceived_Successful()
public async Task EndOfDayAsync_CommandCompletionReceivedWithoutCompletionReceived_Successful()
{
var loggerZvtClient = LoggerHelper.GetLogger<ZvtClient>();
var mockDeviceCommunication = new Mock<IDeviceCommunication>();
Expand All @@ -34,7 +34,7 @@ public async Task EndOfDayAsync_AcknowledgeReceivedWithoutCompletionReceived_Suc
}

[TestMethod]
public async Task EndOfDayAsync_AcknowledgeReceivedWithCompletionReceived_Successful()
public async Task EndOfDayAsync_CommandCompletionReceivedWithCompletionReceived_Successful()
{
var loggerZvtClient = LoggerHelper.GetLogger<ZvtClient>();
var mockDeviceCommunication = new Mock<IDeviceCommunication>();
Expand All @@ -58,7 +58,7 @@ public async Task EndOfDayAsync_AcknowledgeReceivedWithCompletionReceived_Succes
}

[TestMethod]
public async Task EndOfDayAsync_AcknowledgeReceivedWithAbortReceived1_Successful()
public async Task EndOfDayAsync_CommandCompletionReceivedWithAbortReceived1_Successful()
{
var loggerZvtClient = LoggerHelper.GetLogger<ZvtClient>();
var mockDeviceCommunication = new Mock<IDeviceCommunication>();
Expand All @@ -82,7 +82,7 @@ public async Task EndOfDayAsync_AcknowledgeReceivedWithAbortReceived1_Successful
}

[TestMethod]
public async Task EndOfDayAsync_AcknowledgeReceivedWithAbortReceived2_Successful()
public async Task EndOfDayAsync_CommandCompletionReceivedWithAbortReceived2_Successful()
{
var loggerZvtClient = LoggerHelper.GetLogger<ZvtClient>();
var mockDeviceCommunication = new Mock<IDeviceCommunication>();
Expand Down Expand Up @@ -159,12 +159,12 @@ void statusInformationReceived(StatusInformation statusInformation)
mockDeviceCommunication.Raise(mock => mock.DataReceived += null, paymentTerminalStatusInformation);
await Task.Delay(3000, dataSentCancellationTokenSource.Token).ContinueWith(_ => { });

Assert.IsNotNull(receivedStatusInformation);
Assert.IsNotNull(receivedStatusInformation, "No StatusInformation received");
receivedStatusInformation = null;

// check that the ECR answers immediately, as no issueGoodsCallback is set
var electronicCashRegisterAcknowlegeForStatusInformation = new byte[] { 0x80, 0x00, 0x00 };
CollectionAssert.AreEqual(electronicCashRegisterAcknowlegeForStatusInformation, dataSent, $"Collection is wrong {BitConverter.ToString(dataSent)}");
var electronicCashRegisterCommandCompletionForStatusInformation = new byte[] { 0x80, 0x00, 0x00 };
CollectionAssert.AreEqual(electronicCashRegisterCommandCompletionForStatusInformation, dataSent, $"Collection is wrong {BitConverter.ToString(dataSent)}");

dataSentCancellationTokenSource.Dispose();
dataSentCancellationTokenSource = new CancellationTokenSource();
Expand All @@ -173,7 +173,7 @@ void statusInformationReceived(StatusInformation statusInformation)
await Task.Delay(3000, dataSentCancellationTokenSource.Token).ContinueWith(_ => { });

var commandResponse = await paymentTask;
Assert.IsFalse(startAsyncCompletionCalled);
Assert.IsFalse(startAsyncCompletionCalled, "CompletionStartReceived not called");

zvtClient.StatusInformationReceived -= statusInformationReceived;

Expand All @@ -183,6 +183,86 @@ void statusInformationReceived(StatusInformation statusInformation)
Assert.AreEqual(CommandResponseState.Successful, commandResponse.State);
}

[TestMethod]
public async Task PaymentAsync_CorruptDataFlow_Failure()
{
var loggerZvtClient = LoggerHelper.GetLogger<ZvtClient>();
var mockDeviceCommunication = new Mock<IDeviceCommunication>();

StatusInformation receivedStatusInformation = null;

void statusInformationReceived(StatusInformation statusInformation)
{
receivedStatusInformation = statusInformation;
}

var dataSent = Array.Empty<byte>();
var dataSentCancellationTokenSource = new CancellationTokenSource();

mockDeviceCommunication
.Setup(c => c.SendAsync(It.IsAny<byte[]>(), It.IsAny<CancellationToken>()))
.ReturnsAsync((byte[] data, CancellationToken cancellationToken) =>
{
dataSent = data;
dataSentCancellationTokenSource?.Cancel();
return true;
});

var startAsyncCompletionCalled = false;
var clientConfig = new ZvtClientConfig
{
CommandCompletionTimeout = TimeSpan.FromSeconds(5)
};

var zvtClient = new ZvtClient(mockDeviceCommunication.Object, loggerZvtClient.Object, clientConfig);
zvtClient.StatusInformationReceived += statusInformationReceived;
zvtClient.CompletionStartReceived += (_) => startAsyncCompletionCalled = true;

// Send payment request for 10€
var paymentTask = zvtClient.PaymentAsync(10);
// Wait for send response
await Task.Delay(500, dataSentCancellationTokenSource.Token).ContinueWith(_ => { });

dataSentCancellationTokenSource.Dispose();
dataSentCancellationTokenSource = new CancellationTokenSource();

// ensure the timeout is not set, when nothing is passed to PaymentAsync
CollectionAssert.AreEqual(new byte[] { 0x06, 0x01, 0x07, 0x04, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00 }, dataSent, $"Invalid Payment command");

dataSent = Array.Empty<byte>();

// Send wrong data instead of positive completion
var paymentTerminalPositiveCompletion = new byte[] { 0x01, 0x02, 0x03 };
mockDeviceCommunication.Raise(mock => mock.DataReceived += null, paymentTerminalPositiveCompletion);

var paymentTerminalStatusInformation = new byte[] { 0x04, 0x0F, 0x02, 0x27, 0x00 };
mockDeviceCommunication.Raise(mock => mock.DataReceived += null, paymentTerminalStatusInformation);
await Task.Delay(3000, dataSentCancellationTokenSource.Token).ContinueWith(_ => { });

Assert.IsNotNull(receivedStatusInformation, "No StatusInformation received");
receivedStatusInformation = null;

// check that the ECR answers immediately, as no issueGoodsCallback is set
var electronicCashRegisterCommandCompletionForStatusInformation = new byte[] { 0x80, 0x00, 0x00 };
CollectionAssert.AreEqual(electronicCashRegisterCommandCompletionForStatusInformation, dataSent, $"Collection is wrong {BitConverter.ToString(dataSent)}");

dataSentCancellationTokenSource.Dispose();
dataSentCancellationTokenSource = new CancellationTokenSource();

mockDeviceCommunication.Raise(mock => mock.DataReceived += null, new byte[] { 0x06, 0x0F, 0x00 });
await Task.Delay(3000, dataSentCancellationTokenSource.Token).ContinueWith(_ => { });

var commandResponse = await paymentTask;
Assert.IsFalse(startAsyncCompletionCalled, "CompletionStartReceived not called");

zvtClient.StatusInformationReceived -= statusInformationReceived;

zvtClient.Dispose();
dataSentCancellationTokenSource.Dispose();

Assert.AreEqual(CommandResponseState.Error, commandResponse.State);
}

[TestMethod]
public async Task PaymentAsync_CardRejected_Successful()
{
Expand Down Expand Up @@ -481,7 +561,7 @@ public async Task PaymentAsync_IssueOfGoods_RejectedCard_Successful()
Assert.IsFalse(startAsyncCompletionCalled);

// ensure we answer with an ack in this case
CollectionAssert.AreEqual(new byte[] { 0x80, 0x00, 0x00 }, dataSent, $"Collection is wrong {BitConverter.ToString(dataSent)}");
CollectionAssert.AreEqual(new byte[] { 0x84, 0x66, 0x00 }, dataSent, $"Collection is wrong {BitConverter.ToString(dataSent)}");
dataSent = Array.Empty<byte>();

mockDeviceCommunication.Raise(mock => mock.DataReceived += null, new byte[] { 0x06, 0x1E, 0x01, 0x05 });
Expand Down
23 changes: 11 additions & 12 deletions src/Portalum.Zvt.UnitTest/ZvtCommunicationTest.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System.Diagnostics;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using Portalum.Zvt.Models;
using System.Linq;
Expand All @@ -11,7 +10,7 @@ namespace Portalum.Zvt.UnitTest
public class ZvtCommunicationTest
{
[TestMethod]
public async Task SendCommandAsync_AcknowledgeReceived_Successful()
public async Task SendCommandAsync_CommandCompletionReceived_Successful()
{
var additionalDataReceived = false;

Expand All @@ -27,7 +26,7 @@ ProcessData dataReceived(byte[] data)
var zvtCommunication = new ZvtCommunication(loggerZvtCommunication.Object, mockDeviceCommunication.Object);
zvtCommunication.DataReceived += dataReceived;

var sendCommandTask = zvtCommunication.SendCommandAsync(new byte[] { 0x01 }, acknowledgeReceiveTimeoutMilliseconds: 1000);
var sendCommandTask = zvtCommunication.SendCommandAsync(new byte[] { 0x01 }, commandCompletionReceiveTimeoutMilliseconds: 1000);
mockDeviceCommunication.Raise(mock => mock.DataReceived += null, new byte[] { 0x80, 0x00, 0x00 });
var sendCommandResult = await sendCommandTask;

Expand All @@ -39,7 +38,7 @@ ProcessData dataReceived(byte[] data)
}

[TestMethod]
public async Task SendCommandAsync_AcknowledgeReceivedWithDataFragment_Successful()
public async Task SendCommandAsync_CommandCompletionReceivedWithDataFragment_Successful()
{
var additionalDataReceived = false;
byte[] additionalData = null;
Expand All @@ -57,7 +56,7 @@ ProcessData dataReceived(byte[] data)
var zvtCommunication = new ZvtCommunication(loggerZvtCommunication.Object, mockDeviceCommunication.Object);
zvtCommunication.DataReceived += dataReceived;

var sendCommandTask = zvtCommunication.SendCommandAsync(new byte[] { 0x01 }, acknowledgeReceiveTimeoutMilliseconds: 1000);
var sendCommandTask = zvtCommunication.SendCommandAsync(new byte[] { 0x01 }, commandCompletionReceiveTimeoutMilliseconds: 1000);
mockDeviceCommunication.Raise(mock => mock.DataReceived += null, new byte[] { 0x80, 0x00, 0x00, 0x01, 0x02 });
var sendCommandResult = await sendCommandTask;

Expand All @@ -70,7 +69,7 @@ ProcessData dataReceived(byte[] data)
}

[TestMethod]
public async Task SendCommandAsync_AcknowledgeReceivedTooLate_Successful()
public async Task SendCommandAsync_CommandCompletionReceivedTooLate_Successful()
{
var timeout = 1000;

Expand All @@ -79,7 +78,7 @@ public async Task SendCommandAsync_AcknowledgeReceivedTooLate_Successful()

var zvtCommunication = new ZvtCommunication(loggerZvtCommunication.Object, mockDeviceCommunication.Object);

var sendCommandTask = zvtCommunication.SendCommandAsync(new byte[] { 0x01 }, acknowledgeReceiveTimeoutMilliseconds: timeout);
var sendCommandTask = zvtCommunication.SendCommandAsync(new byte[] { 0x01 }, commandCompletionReceiveTimeoutMilliseconds: timeout);
await Task.Delay(timeout + 100);
mockDeviceCommunication.Raise(mock => mock.DataReceived += null, new byte[] { 0x80, 0x00, 0x00 });
var sendCommandResult = await sendCommandTask;
Expand All @@ -97,7 +96,7 @@ public async Task SendCommandAsync_NegativeCompletionReceived_Successful()

var zvtCommunication = new ZvtCommunication(loggerZvtCommunication.Object, mockDeviceCommunication.Object);

var sendCommandTask = zvtCommunication.SendCommandAsync(new byte[] { 0x01 }, acknowledgeReceiveTimeoutMilliseconds: 1000);
var sendCommandTask = zvtCommunication.SendCommandAsync(new byte[] { 0x01 }, commandCompletionReceiveTimeoutMilliseconds: 1000);
mockDeviceCommunication.Raise(mock => mock.DataReceived += null, new byte[] { 0x84, 0x01, 0x00 });
var sendCommandResult = await sendCommandTask;

Expand All @@ -114,7 +113,7 @@ public async Task SendCommandAsync_ReceiveInvalidData_Successful()

var zvtCommunication = new ZvtCommunication(loggerZvtCommunication.Object, mockDeviceCommunication.Object);

var sendCommandTask = zvtCommunication.SendCommandAsync(new byte[] { 0x01 }, acknowledgeReceiveTimeoutMilliseconds: 1000);
var sendCommandTask = zvtCommunication.SendCommandAsync(new byte[] { 0x01 }, commandCompletionReceiveTimeoutMilliseconds: 1000);
mockDeviceCommunication.Raise(mock => mock.DataReceived += null, new byte[] { 0x01, 0x02, 0x03 });
var sendCommandResult = await sendCommandTask;

Expand All @@ -131,7 +130,7 @@ public async Task SendCommandAsync_NotSupported_Successful()

var zvtCommunication = new ZvtCommunication(loggerZvtCommunication.Object, mockDeviceCommunication.Object);

var sendCommandTask = zvtCommunication.SendCommandAsync(new byte[] { 0x01 }, acknowledgeReceiveTimeoutMilliseconds: 1000);
var sendCommandTask = zvtCommunication.SendCommandAsync(new byte[] { 0x01 }, commandCompletionReceiveTimeoutMilliseconds: 1000);
mockDeviceCommunication.Raise(mock => mock.DataReceived += null, new byte[] { 0x84, 0x83, 0x00 });
var sendCommandResult = await sendCommandTask;

Expand All @@ -148,7 +147,7 @@ public async Task SendCommandAsync_NoDataReceived_Successful()

var zvtCommunication = new ZvtCommunication(loggerZvtCommunication.Object, mockDeviceCommunication.Object);

var sendCommandResult = await zvtCommunication.SendCommandAsync(new byte[] { 0x01 }, acknowledgeReceiveTimeoutMilliseconds: 1000);
var sendCommandResult = await zvtCommunication.SendCommandAsync(new byte[] { 0x01 }, commandCompletionReceiveTimeoutMilliseconds: 1000);

zvtCommunication.Dispose();

Expand Down
2 changes: 1 addition & 1 deletion src/Portalum.Zvt/Models/SendCommandResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
public enum SendCommandResult
{
/// <summary>
/// Acknowledge received
/// Positive completion received
/// </summary>
PositiveCompletionReceived,

Expand Down
Loading

0 comments on commit 783f97d

Please sign in to comment.