From b298ae74b6a55d1efe6d76a2f48b4afd766c00ba Mon Sep 17 00:00:00 2001 From: tnickelsen Date: Thu, 19 Dec 2024 13:43:15 +0100 Subject: [PATCH 1/7] Added logging to transfer flow --- .../SendInformationToReceiverWalletActivity.cs | 12 +++++++----- .../Activities/TransferFullSliceActivity.cs | 1 + 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/ProjectOrigin.Vault/Activities/SendInformationToReceiverWalletActivity.cs b/src/ProjectOrigin.Vault/Activities/SendInformationToReceiverWalletActivity.cs index 7d581ee4..5c366a32 100644 --- a/src/ProjectOrigin.Vault/Activities/SendInformationToReceiverWalletActivity.cs +++ b/src/ProjectOrigin.Vault/Activities/SendInformationToReceiverWalletActivity.cs @@ -43,10 +43,12 @@ public async Task Execute(ExecuteContext SendOverRestToExternalWallet( { try { - _logger.LogDebug("Preparing to send information to receiver"); + _logger.LogInformation("Preparing to send information to receiver"); var request = new ReceiveRequest { @@ -81,13 +83,13 @@ private async Task SendOverRestToExternalWallet( }; var client = new HttpClient(); - _logger.LogDebug("Sending information to receiver"); + _logger.LogInformation("Sending information to receiver"); var response = await client.PostAsJsonAsync(externalEndpoint.Endpoint, request); response.EnsureSuccessStatusCode(); await _unitOfWork.TransferRepository.SetTransferredSliceState(newSlice.Id, TransferredSliceState.Transferred); - _logger.LogDebug("Information Sent to receiver"); + _logger.LogInformation("Information Sent to receiver"); return context.Completed(); } @@ -100,7 +102,7 @@ private async Task SendOverRestToExternalWallet( private async Task InsertIntoLocalWallet(ExecuteContext context, TransferredSlice newSlice, ExternalEndpoint externalEndpoint) { - _logger.LogDebug("Receiver is local."); + _logger.LogInformation("Receiver is local."); var walletEndpoint = await _unitOfWork.WalletRepository.GetWalletEndpoint(externalEndpoint.PublicKey); @@ -131,7 +133,7 @@ private async Task InsertIntoLocalWallet(ExecuteContext Execute(ExecuteContext Date: Tue, 28 Jan 2025 12:53:03 +0100 Subject: [PATCH 2/7] Claim requests now fails on Claim not allowed errors and general exceptions --- .../ClaimCertificateCommandHandler.cs | 4 +++ .../Extensions/HttpClientExtensions.cs | 3 ++ .../FlowTests/ClaimTests.cs | 30 +++++++++++++++++++ 3 files changed, 37 insertions(+) diff --git a/src/ProjectOrigin.Vault/CommandHandlers/ClaimCertificateCommandHandler.cs b/src/ProjectOrigin.Vault/CommandHandlers/ClaimCertificateCommandHandler.cs index 736b5b77..b13b6bd0 100644 --- a/src/ProjectOrigin.Vault/CommandHandlers/ClaimCertificateCommandHandler.cs +++ b/src/ProjectOrigin.Vault/CommandHandlers/ClaimCertificateCommandHandler.cs @@ -61,6 +61,8 @@ public async Task Consume(ConsumeContext context) { _unitOfWork.Rollback(); _logger.LogWarning(ex, "Claim is not allowed."); + await _unitOfWork.RequestStatusRepository.SetRequestStatus(context.Message.ClaimId, context.Message.Owner, RequestStatusState.Failed, failedReason: "Claim is not allowed."); + _unitOfWork.Commit(); } catch (QuantityNotYetAvailableToReserveException ex) { @@ -72,6 +74,8 @@ public async Task Consume(ConsumeContext context) { _unitOfWork.Rollback(); _logger.LogError(ex, "failed to handle claim"); + await _unitOfWork.RequestStatusRepository.SetRequestStatus(context.Message.ClaimId, context.Message.Owner, RequestStatusState.Failed, failedReason: "failed to handle claim"); + _unitOfWork.Commit(); } } diff --git a/test/ProjectOrigin.Vault.Tests/Extensions/HttpClientExtensions.cs b/test/ProjectOrigin.Vault.Tests/Extensions/HttpClientExtensions.cs index f7c4818f..ef968273 100644 --- a/test/ProjectOrigin.Vault.Tests/Extensions/HttpClientExtensions.cs +++ b/test/ProjectOrigin.Vault.Tests/Extensions/HttpClientExtensions.cs @@ -30,6 +30,9 @@ public static Task CreateExternalEndpoint(this H public static Task> GetCertificates(this HttpClient client) => client.GetAsync($"v1/certificates").ParseJson>(); + public static Task GetRequestStatus(this HttpClient client, Guid requestId) => + client.GetAsync($"v1/request-status/{requestId}").ParseJson(); + public static Task> GetCertificatesWithTimeout(this HttpClient client, int count, TimeSpan timeout) => Timeout(async () => { diff --git a/test/ProjectOrigin.Vault.Tests/FlowTests/ClaimTests.cs b/test/ProjectOrigin.Vault.Tests/FlowTests/ClaimTests.cs index 1a2b8690..b3be493d 100644 --- a/test/ProjectOrigin.Vault.Tests/FlowTests/ClaimTests.cs +++ b/test/ProjectOrigin.Vault.Tests/FlowTests/ClaimTests.cs @@ -97,4 +97,34 @@ public async Task CannotAllocateAfterExpired() allocateEventStatus.Status.Should().Be(TransactionState.Failed); allocateEventStatus.Message.Should().Be("Certificate has expired"); } + + [Fact] + public async Task WhenClaimingMoreThanPossible_ClaimRequestSetToFailed() + { + var position = 1; + var endDate = DateTimeOffset.UtcNow; + var startDate = endDate.AddHours(-1); + + var client = WalletTestFixture.ServerFixture.CreateHttpClient(); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", WalletTestFixture.JwtTokenIssuerFixture.GenerateRandomToken()); + + var wallet = await client.CreateWallet(); + var endpoint = await client.CreateWalletEndpoint(wallet.WalletId); + + var productionId = await IssueCertificateToEndpoint(endpoint.WalletReference, Electricity.V1.GranularCertificateType.Production, new SecretCommitmentInfo(200), position++, startDate, endDate); + var consumptionId = await IssueCertificateToEndpoint(endpoint.WalletReference, Electricity.V1.GranularCertificateType.Consumption, new SecretCommitmentInfo(300), position++, startDate, endDate); + + await client.GetCertificatesWithTimeout(2, TimeSpan.FromMinutes(1)); + + var response = await client.CreateClaim( + consumptionId, + productionId, + 400u); + + await Task.Delay(TimeSpan.FromSeconds(5)); + + var requestStatus = await client.GetRequestStatus(response.ClaimRequestId); + + Assert.Equal(RequestStatus.Failed, requestStatus.Status); + } } From 97ca14d79223531a89b3c6fc02acd097ce76fa5e Mon Sep 17 00:00:00 2001 From: tnickelsen Date: Tue, 28 Jan 2025 14:46:54 +0100 Subject: [PATCH 3/7] other places --- .../Activities/AllocateActivity.cs | 10 +++++++--- .../Activities/UpdateClaimStateActivity.cs | 5 +++++ .../RegistryProcessBuilder/Claim.cs | 14 ++++++++++---- .../ActivityTests/ChroniclerExecutionTest.cs | 7 +++++-- .../RegistryProcessBuilder/ClaimTests.cs | 16 ++++++++-------- 5 files changed, 35 insertions(+), 17 deletions(-) diff --git a/src/ProjectOrigin.Vault/Activities/AllocateActivity.cs b/src/ProjectOrigin.Vault/Activities/AllocateActivity.cs index 2669e303..7b641967 100644 --- a/src/ProjectOrigin.Vault/Activities/AllocateActivity.cs +++ b/src/ProjectOrigin.Vault/Activities/AllocateActivity.cs @@ -20,8 +20,7 @@ public record AllocateArguments public required Guid ProductionSliceId { get; init; } public Guid? ChroniclerRequestId { get; init; } public required FederatedStreamId CertificateId { get; init; } - public required Guid RequestId { get; init; } - public required string Owner { get; init; } + public required RequestStatusArgs RequestStatusArgs { get; init; } } public class AllocateActivity : IExecuteActivity @@ -44,7 +43,7 @@ public async Task Execute(ExecuteContext con { try { - _logger.LogInformation("Starting Activity: {Activity}, RequestId: {RequestId} ", nameof(SendRegistryTransactionActivity), context.Arguments.RequestId); + _logger.LogInformation("Starting Activity: {Activity}, RequestId: {RequestId} ", nameof(SendRegistryTransactionActivity), context.Arguments.RequestStatusArgs.RequestId); var cons = await _unitOfWork.CertificateRepository.GetWalletSlice(context.Arguments.ConsumptionSliceId); var prod = await _unitOfWork.CertificateRepository.GetWalletSlice(context.Arguments.ProductionSliceId); @@ -70,6 +69,11 @@ public async Task Execute(ExecuteContext con catch (Exception ex) { _logger.LogError(ex, "Error registering claim intent with Chronicler"); + await _unitOfWork.RequestStatusRepository.SetRequestStatus(context.Arguments.RequestStatusArgs.RequestId, + context.Arguments.RequestStatusArgs.Owner, + RequestStatusState.Failed, + "Error registering claim intent with Chronicler"); + _unitOfWork.Commit(); return context.Faulted(ex); } } diff --git a/src/ProjectOrigin.Vault/Activities/UpdateClaimStateActivity.cs b/src/ProjectOrigin.Vault/Activities/UpdateClaimStateActivity.cs index f93c371d..bb711f76 100644 --- a/src/ProjectOrigin.Vault/Activities/UpdateClaimStateActivity.cs +++ b/src/ProjectOrigin.Vault/Activities/UpdateClaimStateActivity.cs @@ -57,6 +57,11 @@ public async Task Execute(ExecuteContext(new AllocateArguments @@ -35,8 +38,11 @@ public async Task Claim(WalletSlice productionSlice, WalletSlice consumptionSlic ProductionSliceId = productionSlice.Id, ConsumptionSliceId = consumptionSlice.Id, ChroniclerRequestId = await GetClaimIntentId(consumptionSlice), - Owner = _owner, - RequestId = _routingSlipId + RequestStatusArgs = new RequestStatusArgs + { + Owner = _owner, + RequestId = _routingSlipId + } }); await _unitOfWork.ClaimRepository.InsertClaim(new Claim diff --git a/test/ProjectOrigin.Vault.Tests/ActivityTests/ChroniclerExecutionTest.cs b/test/ProjectOrigin.Vault.Tests/ActivityTests/ChroniclerExecutionTest.cs index fa6fb7e8..f4edee87 100644 --- a/test/ProjectOrigin.Vault.Tests/ActivityTests/ChroniclerExecutionTest.cs +++ b/test/ProjectOrigin.Vault.Tests/ActivityTests/ChroniclerExecutionTest.cs @@ -113,8 +113,11 @@ public async Task ChroniclerAndAllocate_ShouldRevise() AllocationId = Guid.NewGuid(), ChroniclerRequestId = chroniclerId, CertificateId = certId, - Owner = "", - RequestId = Guid.NewGuid(), + RequestStatusArgs = new RequestStatusArgs + { + Owner = "", + RequestId = Guid.NewGuid() + }, ConsumptionSliceId = Guid.NewGuid(), ProductionSliceId = Guid.NewGuid(), }); diff --git a/test/ProjectOrigin.Vault.Tests/RegistryProcessBuilder/ClaimTests.cs b/test/ProjectOrigin.Vault.Tests/RegistryProcessBuilder/ClaimTests.cs index 40711b21..2f1f7438 100644 --- a/test/ProjectOrigin.Vault.Tests/RegistryProcessBuilder/ClaimTests.cs +++ b/test/ProjectOrigin.Vault.Tests/RegistryProcessBuilder/ClaimTests.cs @@ -82,8 +82,8 @@ public async Task TestClaimEqualSize_WithoutChronicler() x.ChroniclerRequestId == null && x.CertificateId.Registry == prodCert.RegistryName && x.CertificateId.StreamId.Value == prodCert.Id.ToString() && - x.RequestId != Guid.Empty && - x.Owner != string.Empty); + x.RequestStatusArgs.RequestId != Guid.Empty && + x.RequestStatusArgs.Owner != string.Empty); var allocationId = slip.Itinerary[0].ShouldBeActivity().AllocationId.ToString(); @@ -95,8 +95,8 @@ public async Task TestClaimEqualSize_WithoutChronicler() x.ChroniclerRequestId == null && x.CertificateId.Registry == consCert.RegistryName && x.CertificateId.StreamId.Value == consCert.Id.ToString() && - x.RequestId != Guid.Empty && - x.Owner != string.Empty); + x.RequestStatusArgs.RequestId != Guid.Empty && + x.RequestStatusArgs.Owner != string.Empty); var (t3, _) = slip.Itinerary[2].ShouldBeTransactionWithEvent( transaction => @@ -201,8 +201,8 @@ public async Task TestClaimEqualSize_WithChronicler() x.ChroniclerRequestId.Equals(chronId) && x.CertificateId.Registry == prodCert.RegistryName && x.CertificateId.StreamId.Value == prodCert.Id.ToString() && - x.RequestId != Guid.Empty && - x.Owner != string.Empty); + x.RequestStatusArgs.RequestId != Guid.Empty && + x.RequestStatusArgs.Owner != string.Empty); var allocationId = slip.Itinerary[1].ShouldBeActivity().AllocationId.ToString(); @@ -224,8 +224,8 @@ public async Task TestClaimEqualSize_WithChronicler() x.ChroniclerRequestId.Equals(chronId2) && x.CertificateId.Registry == consCert.RegistryName && x.CertificateId.StreamId.Value == consCert.Id.ToString() && - x.RequestId != Guid.Empty && - x.Owner != string.Empty); + x.RequestStatusArgs.RequestId != Guid.Empty && + x.RequestStatusArgs.Owner != string.Empty); var (t3, _) = slip.Itinerary[4].ShouldBeTransactionWithEvent( transaction => From 0f714bbba5a398e7d8cc7a05c4f910eee51cda8a Mon Sep 17 00:00:00 2001 From: tnickelsen Date: Tue, 28 Jan 2025 15:04:07 +0100 Subject: [PATCH 4/7] fixed unit test --- .../ActivityTests/UpdateClaimStateActivityTests.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/test/ProjectOrigin.Vault.Tests/ActivityTests/UpdateClaimStateActivityTests.cs b/test/ProjectOrigin.Vault.Tests/ActivityTests/UpdateClaimStateActivityTests.cs index 2f00a994..d4b80349 100644 --- a/test/ProjectOrigin.Vault.Tests/ActivityTests/UpdateClaimStateActivityTests.cs +++ b/test/ProjectOrigin.Vault.Tests/ActivityTests/UpdateClaimStateActivityTests.cs @@ -103,7 +103,6 @@ public async Task Execute_WhenSetClaimThrowsException_ShouldThrowException() // Assert _context.Received(1).Faulted(Arg.Is(exceptionToBeThrown)); _unitOfWork.Received(1).Rollback(); - _unitOfWork.DidNotReceive().Commit(); _context.DidNotReceive().Completed(); } } From 0a8fdfaf6a40ba245d86c7181077cb24e6a1ca9d Mon Sep 17 00:00:00 2001 From: tnickelsen Date: Wed, 29 Jan 2025 13:28:49 +0100 Subject: [PATCH 5/7] Added test --- .../FlowTests/ClaimTests.cs | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/test/ProjectOrigin.Vault.Tests/FlowTests/ClaimTests.cs b/test/ProjectOrigin.Vault.Tests/FlowTests/ClaimTests.cs index b3be493d..855598bd 100644 --- a/test/ProjectOrigin.Vault.Tests/FlowTests/ClaimTests.cs +++ b/test/ProjectOrigin.Vault.Tests/FlowTests/ClaimTests.cs @@ -127,4 +127,33 @@ public async Task WhenClaimingMoreThanPossible_ClaimRequestSetToFailed() Assert.Equal(RequestStatus.Failed, requestStatus.Status); } + + [Fact] + public async Task WhenTryingToClaimUnknownCertificate_ClaimRequestSetToFailed() + { + var position = 1; + var endDate = DateTimeOffset.UtcNow; + var startDate = endDate.AddHours(-1); + + var client = WalletTestFixture.ServerFixture.CreateHttpClient(); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", WalletTestFixture.JwtTokenIssuerFixture.GenerateRandomToken()); + + var wallet = await client.CreateWallet(); + var endpoint = await client.CreateWalletEndpoint(wallet.WalletId); + + var consumptionId = await IssueCertificateToEndpoint(endpoint.WalletReference, Electricity.V1.GranularCertificateType.Consumption, new SecretCommitmentInfo(300), position++, startDate, endDate); + + await client.GetCertificatesWithTimeout(1, TimeSpan.FromMinutes(1)); + + var response = await client.CreateClaim( + consumptionId, + consumptionId with { StreamId = Guid.NewGuid() }, + 400u); + + await Task.Delay(TimeSpan.FromSeconds(5)); + + var requestStatus = await client.GetRequestStatus(response.ClaimRequestId); + + Assert.Equal(RequestStatus.Failed, requestStatus.Status); + } } From 5a592c9e33f3a4478e29840023ddab062c32b44d Mon Sep 17 00:00:00 2001 From: tnickelsen Date: Wed, 29 Jan 2025 13:55:38 +0100 Subject: [PATCH 6/7] UpdateClaimState can now be replayed. --- .../Activities/UpdateClaimStateActivity.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/ProjectOrigin.Vault/Activities/UpdateClaimStateActivity.cs b/src/ProjectOrigin.Vault/Activities/UpdateClaimStateActivity.cs index bb711f76..e73d78b7 100644 --- a/src/ProjectOrigin.Vault/Activities/UpdateClaimStateActivity.cs +++ b/src/ProjectOrigin.Vault/Activities/UpdateClaimStateActivity.cs @@ -57,12 +57,7 @@ public async Task Execute(ExecuteContext Date: Wed, 29 Jan 2025 14:08:53 +0100 Subject: [PATCH 7/7] corrected unit test --- .../ActivityTests/UpdateClaimStateActivityTests.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/ProjectOrigin.Vault.Tests/ActivityTests/UpdateClaimStateActivityTests.cs b/test/ProjectOrigin.Vault.Tests/ActivityTests/UpdateClaimStateActivityTests.cs index d4b80349..b3e05ef7 100644 --- a/test/ProjectOrigin.Vault.Tests/ActivityTests/UpdateClaimStateActivityTests.cs +++ b/test/ProjectOrigin.Vault.Tests/ActivityTests/UpdateClaimStateActivityTests.cs @@ -98,10 +98,9 @@ public async Task Execute_WhenSetClaimThrowsException_ShouldThrowException() _unitOfWork.ClaimRepository.When(x => x.SetClaimState(Arg.Any(), Arg.Any())).Do(x => throw exceptionToBeThrown); // Act - await _activity.Execute(_context); + await Assert.ThrowsAsync(async () => await _activity.Execute(_context)); // Assert - _context.Received(1).Faulted(Arg.Is(exceptionToBeThrown)); _unitOfWork.Received(1).Rollback(); _context.DidNotReceive().Completed(); }