From 204ac3e4975d45ce1d09dc149b31aef467b752cd Mon Sep 17 00:00:00 2001 From: DilmurodDeveloper Date: Wed, 23 Jul 2025 16:39:44 +0500 Subject: [PATCH 01/17] ShouldModifyPetAsync -> FAIL --- .../Pets/PetServiceTests.Logic.Modify.cs | 53 +++++++++++++++++++ .../Services/Foundations/Pets/IPetService.cs | 1 + .../Services/Foundations/Pets/PetService.cs | 3 ++ 3 files changed, 57 insertions(+) create mode 100644 xChanger.Api.Tests.Unit/Services/Foundations/Pets/PetServiceTests.Logic.Modify.cs diff --git a/xChanger.Api.Tests.Unit/Services/Foundations/Pets/PetServiceTests.Logic.Modify.cs b/xChanger.Api.Tests.Unit/Services/Foundations/Pets/PetServiceTests.Logic.Modify.cs new file mode 100644 index 0000000..ef139f9 --- /dev/null +++ b/xChanger.Api.Tests.Unit/Services/Foundations/Pets/PetServiceTests.Logic.Modify.cs @@ -0,0 +1,53 @@ +//- - - - - - - - - - - - - - - - - - - - - - - - - - +// Copyright (c) Coalition of Good-Hearted Engineers +// Free to Use for Precise File Conversion +//- - - - - - - - - - - - - - - - - - - - - - - - - - + +using FluentAssertions; +using Force.DeepCloner; +using Moq; +using xChanger.Api.Models.Foundations.Pets; + +namespace xChanger.Api.Tests.Unit.Services.Foundations.Pets +{ + public partial class PetServiceTests + { + [Fact] + public async Task ShouldModifyPetAsync() + { + // given + Pet randomPet = CreateRandomPet(); + Pet inputPet = randomPet; + Pet persistedPet = inputPet.DeepClone(); + Pet updatedPet = inputPet; + Pet expectedPet = updatedPet.DeepClone(); + Guid InputPetId = inputPet.Id; + + this.storageBrokerMock.Setup(broker => + broker.SelectPetByIdAsync(InputPetId)) + .ReturnsAsync(persistedPet); + + this.storageBrokerMock.Setup(broker => + broker.UpdatePetAsync(inputPet)) + .ReturnsAsync(updatedPet); + + // when + Pet actualPet = + await this.petService.ModifyPetAsync(inputPet); + + // then + actualPet.Should().BeEquivalentTo(expectedPet); + + this.storageBrokerMock.Verify(broker => + broker.SelectPetByIdAsync(InputPetId), + Times.Once); + + this.storageBrokerMock.Verify(broker => + broker.UpdatePetAsync(inputPet), + Times.Once); + + this.storageBrokerMock.VerifyNoOtherCalls(); + this.loggingBrokerMock.VerifyNoOtherCalls(); + } + } +} diff --git a/xChanger.Api/Services/Foundations/Pets/IPetService.cs b/xChanger.Api/Services/Foundations/Pets/IPetService.cs index fc6c342..003b5cd 100644 --- a/xChanger.Api/Services/Foundations/Pets/IPetService.cs +++ b/xChanger.Api/Services/Foundations/Pets/IPetService.cs @@ -12,5 +12,6 @@ public interface IPetService ValueTask AddPetAsync(Pet pet); IQueryable RetrieveAllPets(); ValueTask RetrievePetByIdAsync(Guid petId); + ValueTask ModifyPetAsync(Pet pet); } } diff --git a/xChanger.Api/Services/Foundations/Pets/PetService.cs b/xChanger.Api/Services/Foundations/Pets/PetService.cs index 1db6dca..0c6d056 100644 --- a/xChanger.Api/Services/Foundations/Pets/PetService.cs +++ b/xChanger.Api/Services/Foundations/Pets/PetService.cs @@ -45,5 +45,8 @@ public ValueTask RetrievePetByIdAsync(Guid petId) => return maybePet; }); + + public ValueTask ModifyPetAsync(Pet pet) => + throw new NotImplementedException(); } } From e037148c2a50fc1fc552fd572260e47515dd06e0 Mon Sep 17 00:00:00 2001 From: DilmurodDeveloper Date: Wed, 23 Jul 2025 16:40:47 +0500 Subject: [PATCH 02/17] ShouldModifyPetAsync -> PASS --- xChanger.Api/Services/Foundations/Pets/PetService.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/xChanger.Api/Services/Foundations/Pets/PetService.cs b/xChanger.Api/Services/Foundations/Pets/PetService.cs index 0c6d056..5003b1a 100644 --- a/xChanger.Api/Services/Foundations/Pets/PetService.cs +++ b/xChanger.Api/Services/Foundations/Pets/PetService.cs @@ -46,7 +46,12 @@ public ValueTask RetrievePetByIdAsync(Guid petId) => return maybePet; }); - public ValueTask ModifyPetAsync(Pet pet) => - throw new NotImplementedException(); + public async ValueTask ModifyPetAsync(Pet pet) + { + Pet maybePet = + await this.storageBroker.SelectPetByIdAsync(pet.Id); + + return await storageBroker.UpdatePetAsync(pet); + } } } From d2e46f77fa6ef12f86509e39ce7bd9795e193fe5 Mon Sep 17 00:00:00 2001 From: DilmurodDeveloper Date: Wed, 23 Jul 2025 16:42:59 +0500 Subject: [PATCH 03/17] ShouldThrowValidationExceptionOnModifyIfPetIsNullAndLogItAsync -> FAIL --- .../PetServiceTests.Validations.Modify.cs | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 xChanger.Api.Tests.Unit/Services/Foundations/Pets/PetServiceTests.Validations.Modify.cs diff --git a/xChanger.Api.Tests.Unit/Services/Foundations/Pets/PetServiceTests.Validations.Modify.cs b/xChanger.Api.Tests.Unit/Services/Foundations/Pets/PetServiceTests.Validations.Modify.cs new file mode 100644 index 0000000..a400506 --- /dev/null +++ b/xChanger.Api.Tests.Unit/Services/Foundations/Pets/PetServiceTests.Validations.Modify.cs @@ -0,0 +1,50 @@ +//- - - - - - - - - - - - - - - - - - - - - - - - - - +// Copyright (c) Coalition of Good-Hearted Engineers +// Free to Use for Precise File Conversion +//- - - - - - - - - - - - - - - - - - - - - - - - - - + +using FluentAssertions; +using Moq; +using xChanger.Api.Models.Foundations.Pets; +using xChanger.Api.Models.Foundations.Pets.Exceptions; + +namespace xChanger.Api.Tests.Unit.Services.Foundations.Pets +{ + public partial class PetServiceTests + { + [Fact] + public async Task ShouldThrowValidationExceptionOnModifyIfPetIsNullAndLogItAsync() + { + // given + Pet nullPet = null; + var nullPetException = new NullPetException(); + + var expectedPetValidationException = + new PetValidationException(nullPetException); + + // when + ValueTask modifyPetTask = + this.petService.ModifyPetAsync(nullPet); + + PetValidationException actualPetValidationException = + await Assert.ThrowsAsync( + modifyPetTask.AsTask); + + // then + actualPetValidationException.Should() + .BeEquivalentTo(expectedPetValidationException); + + this.loggingBrokerMock.Verify(broker => + broker.LogError(It.Is(SameExceptionAs( + expectedPetValidationException))), + Times.Once); + + this.storageBrokerMock.Verify(broker => + broker.UpdatePetAsync(It.IsAny()), + Times.Never); + + this.loggingBrokerMock.VerifyNoOtherCalls(); + this.storageBrokerMock.VerifyNoOtherCalls(); + } + } +} From 989afbbbae9f56799cabe511a293af06f16f580a Mon Sep 17 00:00:00 2001 From: DilmurodDeveloper Date: Wed, 23 Jul 2025 16:44:41 +0500 Subject: [PATCH 04/17] ShouldThrowValidationExceptionOnModifyIfPetIsNullAndLogItAsync -> PASS --- .../Services/Foundations/Pets/PetService.cs | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/xChanger.Api/Services/Foundations/Pets/PetService.cs b/xChanger.Api/Services/Foundations/Pets/PetService.cs index 5003b1a..dc5bf31 100644 --- a/xChanger.Api/Services/Foundations/Pets/PetService.cs +++ b/xChanger.Api/Services/Foundations/Pets/PetService.cs @@ -6,6 +6,7 @@ using xChanger.Api.Brokers.Loggings; using xChanger.Api.Brokers.Storages; using xChanger.Api.Models.Foundations.Pets; +using xChanger.Api.Models.Foundations.Pets.Exceptions; namespace xChanger.Api.Services.Foundations.Pets { @@ -48,10 +49,24 @@ public ValueTask RetrievePetByIdAsync(Guid petId) => public async ValueTask ModifyPetAsync(Pet pet) { - Pet maybePet = - await this.storageBroker.SelectPetByIdAsync(pet.Id); + try + { + ValidatePetNotNull(pet); + + Pet maybePet = + await this.storageBroker.SelectPetByIdAsync(pet.Id); + + return await storageBroker.UpdatePetAsync(pet); + } + catch (NullPetException nullPetException) + { + var petValidationException = + new PetValidationException(nullPetException); + + this.loggingBroker.LogError(petValidationException); - return await storageBroker.UpdatePetAsync(pet); + throw petValidationException; + } } } } From 651524365837db505bda82baf9c37f62c2a14f4d Mon Sep 17 00:00:00 2001 From: DilmurodDeveloper Date: Wed, 23 Jul 2025 16:45:47 +0500 Subject: [PATCH 05/17] ShouldThrowValidationExceptionOnModifyIfPetIsInvalidAndLogItAsync -> FAIL --- .../PetServiceTests.Validations.Modify.cs | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/xChanger.Api.Tests.Unit/Services/Foundations/Pets/PetServiceTests.Validations.Modify.cs b/xChanger.Api.Tests.Unit/Services/Foundations/Pets/PetServiceTests.Validations.Modify.cs index a400506..4973aa3 100644 --- a/xChanger.Api.Tests.Unit/Services/Foundations/Pets/PetServiceTests.Validations.Modify.cs +++ b/xChanger.Api.Tests.Unit/Services/Foundations/Pets/PetServiceTests.Validations.Modify.cs @@ -46,5 +46,60 @@ await Assert.ThrowsAsync( this.loggingBrokerMock.VerifyNoOtherCalls(); this.storageBrokerMock.VerifyNoOtherCalls(); } + + [Theory] + [InlineData(null)] + [InlineData("")] + [InlineData(" ")] + public async Task ShouldThrowValidationExceptionOnModifyIfPetIsInvalidAndLogItAsync( + string invalidText) + { + // given + Pet invalidPet = new Pet + { + Name = invalidText + }; + + var invalidPetException = new InvalidPetException(); + + invalidPetException.AddData( + key: nameof(Pet.Id), + values: "Id is required"); + + invalidPetException.AddData( + key: nameof(Pet.Name), + values: "Text is required"); + + invalidPetException.AddData( + key: nameof(Pet.PersonId), + values: "Id is required"); + + var expectedPetValidationException = + new PetValidationException(invalidPetException); + + // when + ValueTask modifyPetTask = + this.petService.ModifyPetAsync(invalidPet); + + PetValidationException actualPetValidationException = + await Assert.ThrowsAsync( + modifyPetTask.AsTask); + + // then + actualPetValidationException.Should() + .BeEquivalentTo(expectedPetValidationException); + + this.loggingBrokerMock.Verify(broker => + broker.LogError(It.Is(SameExceptionAs( + expectedPetValidationException))), + Times.Once); + + this.storageBrokerMock.Verify(broker => + broker.UpdatePetAsync(It.IsAny()), + Times.Never); + + this.loggingBrokerMock.VerifyNoOtherCalls(); + this.storageBrokerMock.VerifyNoOtherCalls(); + } } } From a52e1a3e9ff734d0549d18fae4ffe473b9b2ada6 Mon Sep 17 00:00:00 2001 From: DilmurodDeveloper Date: Wed, 23 Jul 2025 16:47:42 +0500 Subject: [PATCH 06/17] ShouldThrowValidationExceptionOnModifyIfPetIsInvalidAndLogItAsync -> PASS --- .../Foundations/Pets/PetService.Validations.cs | 11 +++++++++++ xChanger.Api/Services/Foundations/Pets/PetService.cs | 11 ++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/xChanger.Api/Services/Foundations/Pets/PetService.Validations.cs b/xChanger.Api/Services/Foundations/Pets/PetService.Validations.cs index 3b59c04..5bcb181 100644 --- a/xChanger.Api/Services/Foundations/Pets/PetService.Validations.cs +++ b/xChanger.Api/Services/Foundations/Pets/PetService.Validations.cs @@ -21,6 +21,17 @@ private void ValidatePetOnAdd(Pet pet) (Rule: IsInvalid(pet.PersonId), Parameter: nameof(Pet.PersonId))); } + private static void ValidatePetOnModify(Pet pet) + { + ValidatePetNotNull(pet); + + Validate( + (Rule: IsInvalid(pet.Id), Parameter: nameof(Pet.Id)), + (Rule: IsInvalid(pet.Name), Parameter: nameof(Pet.Name)), + (Rule: IsInvalid(pet.Type), Parameter: nameof(Pet.Type)), + (Rule: IsInvalid(pet.PersonId), Parameter: nameof(Pet.PersonId))); + } + private static void ValidatePetId(Guid petId) => Validate((Rule: IsInvalid(petId), Parameter: nameof(Pet.Id))); diff --git a/xChanger.Api/Services/Foundations/Pets/PetService.cs b/xChanger.Api/Services/Foundations/Pets/PetService.cs index dc5bf31..9f157f1 100644 --- a/xChanger.Api/Services/Foundations/Pets/PetService.cs +++ b/xChanger.Api/Services/Foundations/Pets/PetService.cs @@ -51,7 +51,7 @@ public async ValueTask ModifyPetAsync(Pet pet) { try { - ValidatePetNotNull(pet); + ValidatePetOnModify(pet); Pet maybePet = await this.storageBroker.SelectPetByIdAsync(pet.Id); @@ -65,6 +65,15 @@ public async ValueTask ModifyPetAsync(Pet pet) this.loggingBroker.LogError(petValidationException); + throw petValidationException; + } + catch (InvalidPetException invalidPetException) + { + var petValidationException = + new PetValidationException(invalidPetException); + + this.loggingBroker.LogError(petValidationException); + throw petValidationException; } } From cd611c0cf83ee84e43dd1bd10b9022976aa77eaa Mon Sep 17 00:00:00 2001 From: DilmurodDeveloper Date: Wed, 23 Jul 2025 16:48:52 +0500 Subject: [PATCH 07/17] ShouldThrowValidationExceptionOnModifyIfPetDoesNotExistAndLogItAsync -> FAIL --- .../PetServiceTests.Validations.Modify.cs | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/xChanger.Api.Tests.Unit/Services/Foundations/Pets/PetServiceTests.Validations.Modify.cs b/xChanger.Api.Tests.Unit/Services/Foundations/Pets/PetServiceTests.Validations.Modify.cs index 4973aa3..57a5533 100644 --- a/xChanger.Api.Tests.Unit/Services/Foundations/Pets/PetServiceTests.Validations.Modify.cs +++ b/xChanger.Api.Tests.Unit/Services/Foundations/Pets/PetServiceTests.Validations.Modify.cs @@ -101,5 +101,52 @@ await Assert.ThrowsAsync( this.loggingBrokerMock.VerifyNoOtherCalls(); this.storageBrokerMock.VerifyNoOtherCalls(); } + + [Fact] + public async Task ShouldThrowValidationExceptionOnModifyIfPetDoesNotExistAndLogItAsync() + { + // given + Pet randomPet = CreateRandomPet(); + Pet nonExistPet = randomPet; + Pet nullPet = null; + + var notFoundPetException = + new NotFoundPetException(nonExistPet.Id); + + var expectedPetValidationException = + new PetValidationException(notFoundPetException); + + this.storageBrokerMock.Setup(broker => + broker.SelectPetByIdAsync(nonExistPet.Id)) + .ReturnsAsync(nullPet); + + // when + ValueTask modifyPetTask = + this.petService.ModifyPetAsync(nonExistPet); + + PetValidationException actualPetValidationException = + await Assert.ThrowsAsync(() => + modifyPetTask.AsTask()); + + // then + actualPetValidationException.Should() + .BeEquivalentTo(expectedPetValidationException); + + this.storageBrokerMock.Verify(broker => + broker.SelectPetByIdAsync(nonExistPet.Id), + Times.Once); + + this.loggingBrokerMock.Verify(broker => + broker.LogError(It.Is(SameExceptionAs( + expectedPetValidationException))), + Times.Once); + + this.storageBrokerMock.Verify(broker => + broker.UpdatePetAsync(nonExistPet), + Times.Never); + + this.storageBrokerMock.VerifyNoOtherCalls(); + this.loggingBrokerMock.VerifyNoOtherCalls(); + } } } From c38d5bfabc52f0c1cf8acb7bd0058bcbc277bf59 Mon Sep 17 00:00:00 2001 From: DilmurodDeveloper Date: Wed, 23 Jul 2025 16:50:50 +0500 Subject: [PATCH 08/17] ShouldThrowValidationExceptionOnModifyIfPetDoesNotExistAndLogItAsync -> PASS --- .../Foundations/Pets/PetService.Validations.cs | 11 +++++++++++ xChanger.Api/Services/Foundations/Pets/PetService.cs | 11 +++++++++++ 2 files changed, 22 insertions(+) diff --git a/xChanger.Api/Services/Foundations/Pets/PetService.Validations.cs b/xChanger.Api/Services/Foundations/Pets/PetService.Validations.cs index 5bcb181..0afb40a 100644 --- a/xChanger.Api/Services/Foundations/Pets/PetService.Validations.cs +++ b/xChanger.Api/Services/Foundations/Pets/PetService.Validations.cs @@ -32,6 +32,17 @@ private static void ValidatePetOnModify(Pet pet) (Rule: IsInvalid(pet.PersonId), Parameter: nameof(Pet.PersonId))); } + private static void ValidateAgainstStoragePetOnModify(Pet pet, Pet storagePet) + { + ValidateStoragePet(storagePet, pet.Id); + + Validate( + (Rule: IsInvalid(pet.Id), Parameter: nameof(Pet.Id)), + (Rule: IsInvalid(pet.Name), Parameter: nameof(Pet.Name)), + (Rule: IsInvalid(pet.Type), Parameter: nameof(Pet.Type)), + (Rule: IsInvalid(pet.PersonId), Parameter: nameof(Pet.PersonId))); + } + private static void ValidatePetId(Guid petId) => Validate((Rule: IsInvalid(petId), Parameter: nameof(Pet.Id))); diff --git a/xChanger.Api/Services/Foundations/Pets/PetService.cs b/xChanger.Api/Services/Foundations/Pets/PetService.cs index 9f157f1..a55ecdf 100644 --- a/xChanger.Api/Services/Foundations/Pets/PetService.cs +++ b/xChanger.Api/Services/Foundations/Pets/PetService.cs @@ -56,6 +56,8 @@ public async ValueTask ModifyPetAsync(Pet pet) Pet maybePet = await this.storageBroker.SelectPetByIdAsync(pet.Id); + ValidateAgainstStoragePetOnModify(pet, maybePet); + return await storageBroker.UpdatePetAsync(pet); } catch (NullPetException nullPetException) @@ -74,6 +76,15 @@ public async ValueTask ModifyPetAsync(Pet pet) this.loggingBroker.LogError(petValidationException); + throw petValidationException; + } + catch (NotFoundPetException notFoundPetException) + { + var petValidationException = + new PetValidationException(notFoundPetException); + + this.loggingBroker.LogError(petValidationException); + throw petValidationException; } } From b479eeedc508f1e5478c61ab3bec12af76fd344c Mon Sep 17 00:00:00 2001 From: DilmurodDeveloper Date: Wed, 23 Jul 2025 16:52:33 +0500 Subject: [PATCH 09/17] ShouldThrowCriticalDependencyExceptionOnModifyIfSqlErrorOccursAndLogItAsync -> FAIL --- .../Pets/PetServiceTests.Exceptions.Modify.cs | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 xChanger.Api.Tests.Unit/Services/Foundations/Pets/PetServiceTests.Exceptions.Modify.cs diff --git a/xChanger.Api.Tests.Unit/Services/Foundations/Pets/PetServiceTests.Exceptions.Modify.cs b/xChanger.Api.Tests.Unit/Services/Foundations/Pets/PetServiceTests.Exceptions.Modify.cs new file mode 100644 index 0000000..5f5318b --- /dev/null +++ b/xChanger.Api.Tests.Unit/Services/Foundations/Pets/PetServiceTests.Exceptions.Modify.cs @@ -0,0 +1,64 @@ +//- - - - - - - - - - - - - - - - - - - - - - - - - - +// Copyright (c) Coalition of Good-Hearted Engineers +// Free to Use for Precise File Conversion +//- - - - - - - - - - - - - - - - - - - - - - - - - - + +using FluentAssertions; +using Microsoft.Data.SqlClient; +using Moq; +using xChanger.Api.Models.Foundations.Pets; +using xChanger.Api.Models.Foundations.Pets.Exceptions; + +namespace xChanger.Api.Tests.Unit.Services.Foundations.Pets +{ + public partial class PetServiceTests + { + [Fact] + public async Task ShouldThrowCriticalDependencyExceptionOnModifyIfSqlErrorOccursAndLogItAsync() + { + // given + Pet randomPet = CreateRandomPet(); + Pet somePet = randomPet; + Guid petId = somePet.Id; + SqlException sqlException = GetSqlError(); + + var failedPetStorageException = + new FailedPetStorageException(sqlException); + + var expectedPetDependencyException = + new PetDependencyException(failedPetStorageException); + + this.storageBrokerMock.Setup(broker => + broker.SelectPetByIdAsync(petId)) + .Throws(sqlException); + + // when + ValueTask modifyPetTask = + this.petService.ModifyPetAsync(somePet); + + PetDependencyException actualPetDependencyException = + await Assert.ThrowsAsync(() => + modifyPetTask.AsTask()); + + // then + actualPetDependencyException.Should() + .BeEquivalentTo(expectedPetDependencyException); + + this.loggingBrokerMock.Verify(broker => + broker.LogCritical(It.Is(SameExceptionAs( + expectedPetDependencyException))), + Times.Once); + + this.storageBrokerMock.Verify(broker => + broker.SelectPetByIdAsync(petId), + Times.Once); + + this.storageBrokerMock.Verify(broker => + broker.UpdatePetAsync(somePet), + Times.Never); + + this.loggingBrokerMock.VerifyNoOtherCalls(); + this.storageBrokerMock.VerifyNoOtherCalls(); + } + } +} From 3c42b7a35ebf1f5e0546503c69430cfb4a2a0125 Mon Sep 17 00:00:00 2001 From: DilmurodDeveloper Date: Wed, 23 Jul 2025 16:54:06 +0500 Subject: [PATCH 10/17] ShouldThrowCriticalDependencyExceptionOnModifyIfSqlErrorOccursAndLogItAsync -> PASS --- .../Services/Foundations/Pets/PetService.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/xChanger.Api/Services/Foundations/Pets/PetService.cs b/xChanger.Api/Services/Foundations/Pets/PetService.cs index a55ecdf..6edf39e 100644 --- a/xChanger.Api/Services/Foundations/Pets/PetService.cs +++ b/xChanger.Api/Services/Foundations/Pets/PetService.cs @@ -3,6 +3,7 @@ // Free to Use for Precise File Conversion //- - - - - - - - - - - - - - - - - - - - - - - - - - +using Microsoft.Data.SqlClient; using xChanger.Api.Brokers.Loggings; using xChanger.Api.Brokers.Storages; using xChanger.Api.Models.Foundations.Pets; @@ -87,6 +88,18 @@ public async ValueTask ModifyPetAsync(Pet pet) throw petValidationException; } + catch (SqlException sqlException) + { + var failedPetStorageException = + new FailedPetStorageException(sqlException); + + var petDependencyException = + new PetDependencyException(failedPetStorageException); + + this.loggingBroker.LogCritical(petDependencyException); + + throw petDependencyException; + } } } } From abd406ca67a94b91f285fd5f4ffab8bce14ade58 Mon Sep 17 00:00:00 2001 From: DilmurodDeveloper Date: Wed, 23 Jul 2025 16:55:13 +0500 Subject: [PATCH 11/17] ShouldThrowDependencyExceptionOnModifyIfDatabaseUpdateExceptionOccursAndLogItAsync -> FAIL --- .../Pets/PetServiceTests.Exceptions.Modify.cs | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/xChanger.Api.Tests.Unit/Services/Foundations/Pets/PetServiceTests.Exceptions.Modify.cs b/xChanger.Api.Tests.Unit/Services/Foundations/Pets/PetServiceTests.Exceptions.Modify.cs index 5f5318b..9baf618 100644 --- a/xChanger.Api.Tests.Unit/Services/Foundations/Pets/PetServiceTests.Exceptions.Modify.cs +++ b/xChanger.Api.Tests.Unit/Services/Foundations/Pets/PetServiceTests.Exceptions.Modify.cs @@ -5,6 +5,7 @@ using FluentAssertions; using Microsoft.Data.SqlClient; +using Microsoft.EntityFrameworkCore; using Moq; using xChanger.Api.Models.Foundations.Pets; using xChanger.Api.Models.Foundations.Pets.Exceptions; @@ -60,5 +61,53 @@ await Assert.ThrowsAsync(() => this.loggingBrokerMock.VerifyNoOtherCalls(); this.storageBrokerMock.VerifyNoOtherCalls(); } + + [Fact] + public async Task ShouldThrowDependencyExceptionOnModifyIfDatabaseUpdateExceptionOccursAndLogItAsync() + { + // given + Pet randomPet = CreateRandomPet(); + Pet somePet = randomPet; + Guid petId = somePet.Id; + var databaseUpdateException = new DbUpdateException(); + + var failedPetStorageException = + new FailedPetStorageException(databaseUpdateException); + + var expectedPetDependencyException = + new PetDependencyException(failedPetStorageException); + + this.storageBrokerMock.Setup(broker => + broker.SelectPetByIdAsync(petId)) + .Throws(databaseUpdateException); + + // when + ValueTask modifyPetTask = + this.petService.ModifyPetAsync(somePet); + + PetDependencyException actualPetDependencyException = + await Assert.ThrowsAsync(() => + modifyPetTask.AsTask()); + + // then + actualPetDependencyException.Should() + .BeEquivalentTo(expectedPetDependencyException); + + this.loggingBrokerMock.Verify(broker => + broker.LogError(It.Is(SameExceptionAs( + expectedPetDependencyException))), + Times.Once); + + this.storageBrokerMock.Verify(broker => + broker.SelectPetByIdAsync(petId), + Times.Once); + + this.storageBrokerMock.Verify(broker => + broker.UpdatePetAsync(somePet), + Times.Never); + + this.loggingBrokerMock.VerifyNoOtherCalls(); + this.storageBrokerMock.VerifyNoOtherCalls(); + } } } From cdb02e09b3af4f27e92790a0cb7f902661dd81a4 Mon Sep 17 00:00:00 2001 From: DilmurodDeveloper Date: Wed, 23 Jul 2025 16:56:53 +0500 Subject: [PATCH 12/17] ShouldThrowDependencyExceptionOnModifyIfDatabaseUpdateExceptionOccursAndLogItAsync -> PASS --- .../Services/Foundations/Pets/PetService.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/xChanger.Api/Services/Foundations/Pets/PetService.cs b/xChanger.Api/Services/Foundations/Pets/PetService.cs index 6edf39e..c29a074 100644 --- a/xChanger.Api/Services/Foundations/Pets/PetService.cs +++ b/xChanger.Api/Services/Foundations/Pets/PetService.cs @@ -4,6 +4,7 @@ //- - - - - - - - - - - - - - - - - - - - - - - - - - using Microsoft.Data.SqlClient; +using Microsoft.EntityFrameworkCore; using xChanger.Api.Brokers.Loggings; using xChanger.Api.Brokers.Storages; using xChanger.Api.Models.Foundations.Pets; @@ -98,6 +99,18 @@ public async ValueTask ModifyPetAsync(Pet pet) this.loggingBroker.LogCritical(petDependencyException); + throw petDependencyException; + } + catch (DbUpdateException dbUpdateException) + { + var failedPetStorageException = + new FailedPetStorageException(dbUpdateException); + + var petDependencyException = + new PetDependencyException(failedPetStorageException); + + this.loggingBroker.LogError(petDependencyException); + throw petDependencyException; } } From ad8ab828a7f49144052f646b874c26f02f09c1a9 Mon Sep 17 00:00:00 2001 From: DilmurodDeveloper Date: Wed, 23 Jul 2025 16:59:24 +0500 Subject: [PATCH 13/17] ShouldThrowDependencyValidationExceptionOnModifyIfDatabaseUpdateConcurrencyErrorOccursAndLogItAsync -> FAIL --- .../Pets/PetServiceTests.Exceptions.Modify.cs | 48 +++++++++++++++++++ .../Pets/Exceptions/LockedPetException.cs | 17 +++++++ 2 files changed, 65 insertions(+) create mode 100644 xChanger.Api/Models/Foundations/Pets/Exceptions/LockedPetException.cs diff --git a/xChanger.Api.Tests.Unit/Services/Foundations/Pets/PetServiceTests.Exceptions.Modify.cs b/xChanger.Api.Tests.Unit/Services/Foundations/Pets/PetServiceTests.Exceptions.Modify.cs index 9baf618..1fa890e 100644 --- a/xChanger.Api.Tests.Unit/Services/Foundations/Pets/PetServiceTests.Exceptions.Modify.cs +++ b/xChanger.Api.Tests.Unit/Services/Foundations/Pets/PetServiceTests.Exceptions.Modify.cs @@ -109,5 +109,53 @@ await Assert.ThrowsAsync(() => this.loggingBrokerMock.VerifyNoOtherCalls(); this.storageBrokerMock.VerifyNoOtherCalls(); } + + [Fact] + public async Task ShouldThrowDependencyValidationExceptionOnModifyIfDatabaseUpdateConcurrencyErrorOccursAndLogItAsync() + { + // given + Pet randomPet = CreateRandomPet(); + Pet somePet = randomPet; + Guid petId = somePet.Id; + var dbUpdateConcurrencyException = new DbUpdateConcurrencyException(); + + var lockedPetException = + new LockedPetException(dbUpdateConcurrencyException); + + var expectedPetDependencyValidationException = + new PetDependencyValidationException(lockedPetException); + + this.storageBrokerMock.Setup(broker => + broker.SelectPetByIdAsync(petId)) + .Throws(dbUpdateConcurrencyException); + + // when + ValueTask modifyPetTask = + this.petService.ModifyPetAsync(somePet); + + PetDependencyValidationException actualPetDependencyValidationException = + await Assert.ThrowsAsync(() => + modifyPetTask.AsTask()); + + // then + actualPetDependencyValidationException.Should() + .BeEquivalentTo(expectedPetDependencyValidationException); + + this.loggingBrokerMock.Verify(broker => + broker.LogError(It.Is(SameExceptionAs( + expectedPetDependencyValidationException))), + Times.Once); + + this.storageBrokerMock.Verify(broker => + broker.SelectPetByIdAsync(petId), + Times.Once); + + this.storageBrokerMock.Verify(broker => + broker.UpdatePetAsync(somePet), + Times.Never); + + this.loggingBrokerMock.VerifyNoOtherCalls(); + this.storageBrokerMock.VerifyNoOtherCalls(); + } } } diff --git a/xChanger.Api/Models/Foundations/Pets/Exceptions/LockedPetException.cs b/xChanger.Api/Models/Foundations/Pets/Exceptions/LockedPetException.cs new file mode 100644 index 0000000..3907fe4 --- /dev/null +++ b/xChanger.Api/Models/Foundations/Pets/Exceptions/LockedPetException.cs @@ -0,0 +1,17 @@ +//- - - - - - - - - - - - - - - - - - - - - - - - - - +// Copyright (c) Coalition of Good-Hearted Engineers +// Free to Use for Precise File Conversion +//- - - - - - - - - - - - - - - - - - - - - - - - - - + +using Xeptions; + +namespace xChanger.Api.Models.Foundations.Pets.Exceptions +{ + public class LockedPetException : Xeption + { + public LockedPetException(Exception innerException) + : base(message: "Pet is locked, please try again", + innerException) + { } + } +} From 3239f11c217e4c14c2cc9d7e4b2125692a62aa27 Mon Sep 17 00:00:00 2001 From: DilmurodDeveloper Date: Wed, 23 Jul 2025 17:00:25 +0500 Subject: [PATCH 14/17] ShouldThrowDependencyValidationExceptionOnModifyIfDatabaseUpdateConcurrencyErrorOccursAndLogItAsync -> PASS --- xChanger.Api/Services/Foundations/Pets/PetService.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/xChanger.Api/Services/Foundations/Pets/PetService.cs b/xChanger.Api/Services/Foundations/Pets/PetService.cs index c29a074..56c23dd 100644 --- a/xChanger.Api/Services/Foundations/Pets/PetService.cs +++ b/xChanger.Api/Services/Foundations/Pets/PetService.cs @@ -101,6 +101,18 @@ public async ValueTask ModifyPetAsync(Pet pet) throw petDependencyException; } + catch (DbUpdateConcurrencyException dbUpdateConcurrencyException) + { + var lockedPetException = + new LockedPetException(dbUpdateConcurrencyException); + + var petDependencyValidationException = + new PetDependencyValidationException(lockedPetException); + + this.loggingBroker.LogError(petDependencyValidationException); + + throw petDependencyValidationException; + } catch (DbUpdateException dbUpdateException) { var failedPetStorageException = From 423886df5acc4ac1f04f12c342b30ef04da29f44 Mon Sep 17 00:00:00 2001 From: DilmurodDeveloper Date: Wed, 23 Jul 2025 17:01:24 +0500 Subject: [PATCH 15/17] ShouldThrowServiceExceptionOnModifyIfDatabaseUpdateErrorOccursAndLogItAsync -> FAIL --- .../Pets/PetServiceTests.Exceptions.Modify.cs | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/xChanger.Api.Tests.Unit/Services/Foundations/Pets/PetServiceTests.Exceptions.Modify.cs b/xChanger.Api.Tests.Unit/Services/Foundations/Pets/PetServiceTests.Exceptions.Modify.cs index 1fa890e..972b1d6 100644 --- a/xChanger.Api.Tests.Unit/Services/Foundations/Pets/PetServiceTests.Exceptions.Modify.cs +++ b/xChanger.Api.Tests.Unit/Services/Foundations/Pets/PetServiceTests.Exceptions.Modify.cs @@ -157,5 +157,53 @@ await Assert.ThrowsAsync(() => this.loggingBrokerMock.VerifyNoOtherCalls(); this.storageBrokerMock.VerifyNoOtherCalls(); } + + [Fact] + public async Task ShouldThrowServiceExceptionOnModifyIfDatabaseUpdateErrorOccursAndLogItAsync() + { + // given + Pet randomPet = CreateRandomPet(); + Pet somePet = randomPet; + Guid petId = somePet.Id; + var serviceException = new Exception(); + + var failedPetServiceException = + new FailedPetServiceException(serviceException); + + var expectedPetServiceException = + new PetServiceException(failedPetServiceException); + + this.storageBrokerMock.Setup(broker => + broker.SelectPetByIdAsync(petId)) + .Throws(serviceException); + + // when + ValueTask modifyPetTask = + this.petService.ModifyPetAsync(somePet); + + PetServiceException actualPetServiceException = + await Assert.ThrowsAsync(() => + modifyPetTask.AsTask()); + + // then + actualPetServiceException.Should() + .BeEquivalentTo(expectedPetServiceException); + + this.loggingBrokerMock.Verify(broker => + broker.LogError(It.Is(SameExceptionAs( + expectedPetServiceException))), + Times.Once); + + this.storageBrokerMock.Verify(broker => + broker.SelectPetByIdAsync(petId), + Times.Once); + + this.storageBrokerMock.Verify(broker => + broker.UpdatePetAsync(somePet), + Times.Never); + + this.loggingBrokerMock.VerifyNoOtherCalls(); + this.storageBrokerMock.VerifyNoOtherCalls(); + } } } From 125b690fc036e69deafb9f66d2b00124cbfc9679 Mon Sep 17 00:00:00 2001 From: DilmurodDeveloper Date: Wed, 23 Jul 2025 17:02:32 +0500 Subject: [PATCH 16/17] ShouldThrowServiceExceptionOnModifyIfDatabaseUpdateErrorOccursAndLogItAsync -> PASS --- xChanger.Api/Services/Foundations/Pets/PetService.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/xChanger.Api/Services/Foundations/Pets/PetService.cs b/xChanger.Api/Services/Foundations/Pets/PetService.cs index 56c23dd..915c09f 100644 --- a/xChanger.Api/Services/Foundations/Pets/PetService.cs +++ b/xChanger.Api/Services/Foundations/Pets/PetService.cs @@ -125,6 +125,18 @@ public async ValueTask ModifyPetAsync(Pet pet) throw petDependencyException; } + catch (Exception exception) + { + var failedPetServiceException = + new FailedPetServiceException(exception); + + var petServiceException = + new PetServiceException(failedPetServiceException); + + this.loggingBroker.LogError(petServiceException); + + throw petServiceException; + } } } } From adfd803b765dba6780d66b77beff970b3c5bda25 Mon Sep 17 00:00:00 2001 From: DilmurodDeveloper Date: Wed, 23 Jul 2025 17:04:43 +0500 Subject: [PATCH 17/17] CODE RUB: Implement TryCatch --- .../Foundations/Pets/PetService.Exceptions.cs | 21 ++++ .../Services/Foundations/Pets/PetService.cs | 96 ++----------------- 2 files changed, 29 insertions(+), 88 deletions(-) diff --git a/xChanger.Api/Services/Foundations/Pets/PetService.Exceptions.cs b/xChanger.Api/Services/Foundations/Pets/PetService.Exceptions.cs index c3028ca..30491a8 100644 --- a/xChanger.Api/Services/Foundations/Pets/PetService.Exceptions.cs +++ b/xChanger.Api/Services/Foundations/Pets/PetService.Exceptions.cs @@ -5,6 +5,7 @@ using EFxceptions.Models.Exceptions; using Microsoft.Data.SqlClient; +using Microsoft.EntityFrameworkCore; using xChanger.Api.Models.Foundations.Pets; using xChanger.Api.Models.Foundations.Pets.Exceptions; using Xeptions; @@ -41,6 +42,18 @@ private async ValueTask TryCatch(ReturningPetFunction returningPetFunction) throw CreateAndLogCriticalDependencyException(failedPetStorageException); } + catch (DbUpdateConcurrencyException dbUpdateConcurrencyException) + { + var lockedPetException = new LockedPetException(dbUpdateConcurrencyException); + + throw CreateAndLogDependencyValidationException(lockedPetException); + } + catch (DbUpdateException dbUpdateException) + { + var failedPetStorageException = new FailedPetStorageException(dbUpdateException); + + throw CreateAndLogDependencyException(failedPetStorageException); + } catch (DuplicateKeyException duplicateKeyException) { var alreadyExistsPetException = @@ -115,5 +128,13 @@ private PetServiceException CreateAndLogServiceException(Xeption exception) return petServiceException; } + + private PetDependencyException CreateAndLogDependencyException(Xeption exception) + { + var petDependencyException = new PetDependencyException(exception); + this.loggingBroker.LogError(petDependencyException); + + return petDependencyException; + } } } diff --git a/xChanger.Api/Services/Foundations/Pets/PetService.cs b/xChanger.Api/Services/Foundations/Pets/PetService.cs index 915c09f..f15bd96 100644 --- a/xChanger.Api/Services/Foundations/Pets/PetService.cs +++ b/xChanger.Api/Services/Foundations/Pets/PetService.cs @@ -3,12 +3,9 @@ // Free to Use for Precise File Conversion //- - - - - - - - - - - - - - - - - - - - - - - - - - -using Microsoft.Data.SqlClient; -using Microsoft.EntityFrameworkCore; using xChanger.Api.Brokers.Loggings; using xChanger.Api.Brokers.Storages; using xChanger.Api.Models.Foundations.Pets; -using xChanger.Api.Models.Foundations.Pets.Exceptions; namespace xChanger.Api.Services.Foundations.Pets { @@ -49,94 +46,17 @@ public ValueTask RetrievePetByIdAsync(Guid petId) => return maybePet; }); - public async ValueTask ModifyPetAsync(Pet pet) + public ValueTask ModifyPetAsync(Pet pet) => + TryCatch(async () => { - try - { - ValidatePetOnModify(pet); - - Pet maybePet = - await this.storageBroker.SelectPetByIdAsync(pet.Id); - - ValidateAgainstStoragePetOnModify(pet, maybePet); - - return await storageBroker.UpdatePetAsync(pet); - } - catch (NullPetException nullPetException) - { - var petValidationException = - new PetValidationException(nullPetException); - - this.loggingBroker.LogError(petValidationException); - - throw petValidationException; - } - catch (InvalidPetException invalidPetException) - { - var petValidationException = - new PetValidationException(invalidPetException); - - this.loggingBroker.LogError(petValidationException); - - throw petValidationException; - } - catch (NotFoundPetException notFoundPetException) - { - var petValidationException = - new PetValidationException(notFoundPetException); - - this.loggingBroker.LogError(petValidationException); - - throw petValidationException; - } - catch (SqlException sqlException) - { - var failedPetStorageException = - new FailedPetStorageException(sqlException); - - var petDependencyException = - new PetDependencyException(failedPetStorageException); + ValidatePetOnModify(pet); - this.loggingBroker.LogCritical(petDependencyException); - - throw petDependencyException; - } - catch (DbUpdateConcurrencyException dbUpdateConcurrencyException) - { - var lockedPetException = - new LockedPetException(dbUpdateConcurrencyException); - - var petDependencyValidationException = - new PetDependencyValidationException(lockedPetException); - - this.loggingBroker.LogError(petDependencyValidationException); - - throw petDependencyValidationException; - } - catch (DbUpdateException dbUpdateException) - { - var failedPetStorageException = - new FailedPetStorageException(dbUpdateException); - - var petDependencyException = - new PetDependencyException(failedPetStorageException); - - this.loggingBroker.LogError(petDependencyException); - - throw petDependencyException; - } - catch (Exception exception) - { - var failedPetServiceException = - new FailedPetServiceException(exception); - - var petServiceException = - new PetServiceException(failedPetServiceException); + Pet maybePet = + await this.storageBroker.SelectPetByIdAsync(pet.Id); - this.loggingBroker.LogError(petServiceException); + ValidateAgainstStoragePetOnModify(pet, maybePet); - throw petServiceException; - } - } + return await storageBroker.UpdatePetAsync(pet); + }); } }