public async Task Read_ValidQrCodeData_CreatesTicketScanRequest() { // Arrange var itemCount = new Random().Next(1, 10); var tickets = new DigitalTicket[itemCount]; for (int i = 0; i < itemCount; i++) { tickets[i] = new DigitalTicket { Seat = new Seat { Number = i + 1, Letter = 'D' }, Secret = "plaintext", NameKey = new byte[32], NameIV = new byte[16] }; } var validQrCodeData = JsonConvert.SerializeObject(tickets); // Act await _qrCodeValidator.Validate(validQrCodeData); // Assert _mediator.Verify(callTo => callTo.Send( It.Is <TicketScanRequest>(request => request.Tickets.Count() == tickets.Length), It.IsAny <CancellationToken>()), Times.Once); }
public async Task Read_ValidQrCodeData_OnValidQrCodeInvoked() { var eventInvoked = false; var tickets = new DigitalTicket[] { new DigitalTicket { Seat = new Seat { Number = 2, Letter = 'D' }, Secret = "plaintext", NameKey = new byte[32], NameIV = new byte[16] } }; var validQrCodeData = JsonConvert.SerializeObject(tickets); _qrCodeValidator.OnValidQrCode += (s, e) => { eventInvoked = true; }; // Act await _qrCodeValidator.Validate(validQrCodeData); // Assert Assert.That(eventInvoked, Is.True); _qrCodeValidator.OnValidQrCode -= (s, e) => { eventInvoked = true; }; }
public async Task Handle_CheckTicketThrowsException_PublishesTicketScanResultNotificationNullResult() { var ticket = new DigitalTicket() { Seat = new Seat { Number = 1, Letter = 'A' } }; var ticketScanRequest = new TicketScanRequest(ticket); var ticketTransaction = new Ticket { Address = new Address(5, 5, 4, 3, 5), Price = 2490000000, Seat = new Seat { Number = 1, Letter = 'A' }, Secret = new byte[16], CustomerIdentifier = new byte[16] }; var ticketTransactionReceipts = new Receipt <object, Ticket>[] { new Receipt <object, Ticket> { BlockHash = "vZxae80111cab8c7e8f932289fdda9a2", Logs = new LogDto <Ticket>[] { new LogDto <Ticket> { Log = ticketTransaction } } } }; _smartContractService.Setup(callTo => callTo.FetchReceiptsAsync <Ticket>()).Returns(Task.FromResult(ticketTransactionReceipts)); _smartContractService.Setup(callTo => callTo.FetchReceiptsAsync <Show>()).Returns(Task.FromResult(_showReceipts)); _blockStoreService.Setup(callTo => callTo.GetBlockDataAsync("ks9I72n9Hjj9azM2l9D3Palq2jfBjkb9")).Returns(Task.FromResult(new BlockDto { Height = 1 })); _blockStoreService.Setup(callTo => callTo.GetBlockDataAsync("vZxae80111cab8c7e8f932289fdda9a2")).Returns(Task.FromResult(new BlockDto { Height = 2 })); _ticketChecker.Setup(callTo => callTo.CheckTicket(ticket, ticketTransaction)).Throws <Exception>(); // Act try { await _ticketScanRequestHandler.Handle(ticketScanRequest, default); } catch (Exception) { } // Assert _mediator.Verify(callTo => callTo.Publish(It.Is <TicketScanResultNotification>(notification => notification.Result == null), It.IsAny <CancellationToken>()), Times.Once); }
public void CheckTicket_ProvidedSecretHashDoesNotMatchActualHash_ReturnsResultDoesNotOwnTicket() { // Arrange var secret = new byte[16] { 203, 92, 1, 93, 84, 38, 27, 94, 190, 10, 199, 232, 28, 2, 34, 83 }; var scannedTicket = new DigitalTicket { Seat = new Seat { Number = 1, Letter = 'A' }, Secret = "f09aIm3-hH9379c" }; var actualTicket = new Ticket { Seat = new Seat { Number = 1, Letter = 'A' }, Secret = secret }; // Act var result = _ticketChecker.CheckTicket(scannedTicket, actualTicket); // Assert Assert.That(result.OwnsTicket, Is.False); }
public async Task Handle_EvenMatchingTicketTransactions_PublishesInvalidTicketScanResultNotification() { // Arrange var ticketCount = new Random().Next(1, 3) * 2; var ticket = new DigitalTicket() { Seat = new Seat { Number = 1, Letter = 'A' } }; var ticketScanRequest = new TicketScanRequest(ticket); var ticketTransactionReceipts = new Receipt <object, Ticket> [ticketCount]; for (int i = 0; i < ticketCount; i++) { ticketTransactionReceipts[i] = new Receipt <object, Ticket> { BlockHash = "vZxae80111cab8c7e8f932289fdda9a2", Logs = new LogDto <Ticket>[] { new LogDto <Ticket> { Log = new Ticket { Address = new Address(5, 5, 4, 3, 5), Price = 2490000000, Seat = new Seat { Number = 1, Letter = 'A' }, Secret = i % 2 == 0 ? new byte[16] : null, CustomerIdentifier = i % 2 == 0 ? new byte[16] : null } } } }; } _smartContractService.Setup(callTo => callTo.FetchReceiptsAsync <Ticket>()).Returns(Task.FromResult(ticketTransactionReceipts)); _smartContractService.Setup(callTo => callTo.FetchReceiptsAsync <Show>()).Returns(Task.FromResult(_showReceipts)); _blockStoreService.Setup(callTo => callTo.GetBlockDataAsync("ks9I72n9Hjj9azM2l9D3Palq2jfBjkb9")).Returns(Task.FromResult(new BlockDto { Height = 1 })); _blockStoreService.Setup(callTo => callTo.GetBlockDataAsync("vZxae80111cab8c7e8f932289fdda9a2")).Returns(Task.FromResult(new BlockDto { Height = 2 })); // Act await _ticketScanRequestHandler.Handle(ticketScanRequest, default); // Assert _mediator.Verify(callTo => callTo.Publish(It.Is <TicketScanResultNotification>(notification => !notification.Result.OwnsTicket), It.IsAny <CancellationToken>()), Times.Once); }
public TicketScanResult CheckTicket(DigitalTicket scannedTicket, Ticket actualTicket) { if (scannedTicket is null) { throw new ArgumentNullException(nameof(scannedTicket), "Cannot check null ticket"); } if (!scannedTicket.Seat.Equals(actualTicket.Seat)) { throw new ArgumentException(nameof(actualTicket), "Seats do not match"); } byte[] scannedSecretHash; try { using var hasher = Sha3.Sha3224(); scannedSecretHash = hasher.ComputeHash(scannedTicket.Secret); } catch (ArgumentException e) { _logger.LogWarning(e.Message); return(null); } if (!actualTicket.Secret.SequenceEqual(scannedSecretHash)) { return(new TicketScanResult(false, string.Empty)); } if (actualTicket.CustomerIdentifier is null) { return(new TicketScanResult(true, string.Empty)); } string plainTextCustomerIdentifier; try { using var aes = _cipherFactory.CreateCbcProvider(); plainTextCustomerIdentifier = aes.Decrypt(actualTicket.CustomerIdentifier, scannedTicket.NameKey, scannedTicket.NameIV); } catch (CryptographicException e) { _logger.LogDebug(e.Message); return(new TicketScanResult(true, string.Empty)); } catch (ArgumentException e) { _logger.LogWarning(e.Message); return(null); } return(new TicketScanResult(true, plainTextCustomerIdentifier)); }
public async Task Handle_CheckTicketThrowsArgumentException_LogsError() { var ticket = new DigitalTicket() { Seat = new Seat { Number = 1, Letter = 'A' } }; var ticketScanRequest = new TicketScanRequest(ticket); var ticketTransaction = new Ticket { Address = new Address(5, 5, 4, 3, 5), Price = 2490000000, Seat = new Seat { Number = 1, Letter = 'A' }, Secret = new byte[16], CustomerIdentifier = new byte[16] }; var ticketTransactionReceipts = new Receipt <object, Ticket>[] { new Receipt <object, Ticket> { BlockHash = "vZxae80111cab8c7e8f932289fdda9a2", Logs = new LogDto <Ticket>[] { new LogDto <Ticket> { Log = ticketTransaction } } } }; _smartContractService.Setup(callTo => callTo.FetchReceiptsAsync <Ticket>()).Returns(Task.FromResult(ticketTransactionReceipts)); _smartContractService.Setup(callTo => callTo.FetchReceiptsAsync <Show>()).Returns(Task.FromResult(_showReceipts)); _blockStoreService.Setup(callTo => callTo.GetBlockDataAsync("ks9I72n9Hjj9azM2l9D3Palq2jfBjkb9")).Returns(Task.FromResult(new BlockDto { Height = 1 })); _blockStoreService.Setup(callTo => callTo.GetBlockDataAsync("vZxae80111cab8c7e8f932289fdda9a2")).Returns(Task.FromResult(new BlockDto { Height = 2 })); _ticketChecker.Setup(callTo => callTo.CheckTicket(ticket, ticketTransaction)).Throws <ArgumentException>(); // Act await _ticketScanRequestHandler.Handle(ticketScanRequest, default); // Assert _logger.VerifyLog(LogLevel.Error); }
public void CheckTicket_ProvidedSecretMatchesCustomerIdentifierDecrypted_ReturnsResultOwnsTicketNameMatchesDecryptedValue() { // Arrange var plainTextSecret = "f09aIm3-hH9379c"; byte[] hashedSecret; using (var hasher = Sha3.Sha3224()) { hashedSecret = hasher.ComputeHash(plainTextSecret); } var customerIdentifier = new byte[16] { 33, 93, 23, 252, 24, 38, 43, 94, 224, 10, 12, 232, 28, 211, 64, 99 }; var name = "Benjamin Swift"; var scannedTicket = new DigitalTicket { Seat = new Seat { Number = 1, Letter = 'A' }, Secret = plainTextSecret }; var actualTicket = new Ticket { Seat = new Seat { Number = 1, Letter = 'A' }, Secret = hashedSecret, CustomerIdentifier = customerIdentifier }; _cbc.Setup(callTo => callTo.Decrypt(customerIdentifier, It.IsAny <byte[]>(), It.IsAny <byte[]>())).Returns(name); // Act var result = _ticketChecker.CheckTicket(scannedTicket, actualTicket); // Assert Assert.Multiple(() => { Assert.That(result.OwnsTicket, Is.True, nameof(TicketScanResult.OwnsTicket)); Assert.That(result.Name, Is.EqualTo(name), nameof(TicketScanResult.Name)); }); }
public void CheckTicket_ProvidedSecretMatchesDecryptCustomerIdentifierThrowsArgumentException_ReturnsNull() { // Arrange var plainTextSecret = "f09aIm3-hH9379c"; byte[] hashedSecret; using (var hasher = Sha3.Sha3224()) { hashedSecret = hasher.ComputeHash(plainTextSecret); } var customerIdentifier = new byte[16] { 33, 93, 23, 252, 24, 38, 43, 94, 224, 10, 12, 232, 28, 211, 64, 99 }; var scannedTicket = new DigitalTicket { Seat = new Seat { Number = 1, Letter = 'A' }, Secret = plainTextSecret }; var actualTicket = new Ticket { Seat = new Seat { Number = 1, Letter = 'A' }, Secret = hashedSecret, CustomerIdentifier = customerIdentifier }; _cbc.Setup(callTo => callTo.Decrypt(customerIdentifier, It.IsAny <byte[]>(), It.IsAny <byte[]>())).Throws <ArgumentException>(); // Act var result = _ticketChecker.CheckTicket(scannedTicket, actualTicket); // Assert Assert.That(result, Is.Null); }
public async Task Handle_TicketsProvided_PublishesTicketScanStartedNotifications() { // Arrange var ticketCount = new Random().Next(0, 5); var tickets = new DigitalTicket[ticketCount]; for (int i = 0; i < ticketCount; i++) { tickets[i] = new DigitalTicket { Seat = new Seat { Number = i + 1, Letter = 'A' } }; } var ticketScanRequest = new TicketScanRequest(tickets); // Act await _ticketScanRequestHandler.Handle(ticketScanRequest, default); // Assert _mediator.Verify(callTo => callTo.Publish(It.IsAny <TicketScanStartedNotification>(), It.IsAny <CancellationToken>()), Times.Exactly(ticketCount)); }
public void CheckTicket_SeatsDoNotMatch_ThrowsArgumentException() { // Arrange var scannedTicket = new DigitalTicket { Seat = new Seat { Number = 1, Letter = 'A' } }; var actualTicket = new Ticket { Seat = new Seat { Number = 1, Letter = 'B' } }; // Act var ticketCheckCall = new Action(() => _ticketChecker.CheckTicket(scannedTicket, actualTicket)); // Assert Assert.That(ticketCheckCall, Throws.ArgumentException); }
public void CheckTicket_ProvidedSecretMatchesCustomerIdentifierNull_ReturnsResultOwnsTicketNameEmpty() { // Arrange var plainTextSecret = "f09aIm3-hH9379c"; byte[] hashedSecret; using (var hasher = Sha3.Sha3224()) { hashedSecret = hasher.ComputeHash(plainTextSecret); } var scannedTicket = new DigitalTicket { Seat = new Seat { Number = 1, Letter = 'A' }, Secret = plainTextSecret }; var actualTicket = new Ticket { Seat = new Seat { Number = 1, Letter = 'A' }, Secret = hashedSecret }; // Act var result = _ticketChecker.CheckTicket(scannedTicket, actualTicket); // Assert Assert.Multiple(() => { Assert.That(result.OwnsTicket, Is.True, nameof(TicketScanResult.OwnsTicket)); Assert.That(result.Name, Is.Empty, nameof(TicketScanResult.Name)); }); }
public void CheckTicket_DecryptNullSecret_LogsWarning() { // Arrange var scannedTicket = new DigitalTicket { Seat = new Seat { Number = 1, Letter = 'A' } }; var actualTicket = new Ticket { Seat = new Seat { Number = 1, Letter = 'A' }, Secret = null }; // Act _ticketChecker.CheckTicket(scannedTicket, actualTicket); // Assert _logger.VerifyLog(LogLevel.Warning); }
public void CheckTicket_DecryptNullSecret_ReturnsNull() { // Arrange var scannedTicket = new DigitalTicket { Seat = new Seat { Number = 1, Letter = 'A' } }; var actualTicket = new Ticket { Seat = new Seat { Number = 1, Letter = 'A' }, Secret = null }; // Act var result = _ticketChecker.CheckTicket(scannedTicket, actualTicket); // Assert Assert.That(result, Is.Null); }