From a723bf9d05ad6c8ff18847fe8e075d69f43b0e4a Mon Sep 17 00:00:00 2001 From: DilmurodDeveloper Date: Mon, 21 Jul 2025 17:03:38 +0500 Subject: [PATCH 01/17] ShouldModifyReaderAsync -> FAIL --- .../ReaderServiceTests.Logic.Modify.cs | 53 +++++++++++++++++++ .../Foundations/Readers/IReaderService.cs | 1 + .../Foundations/Readers/ReaderService.cs | 3 ++ 3 files changed, 57 insertions(+) create mode 100644 LibraryManagement.Api.Tests.Unit/Services/Foundations/Readers/ReaderServiceTests.Logic.Modify.cs diff --git a/LibraryManagement.Api.Tests.Unit/Services/Foundations/Readers/ReaderServiceTests.Logic.Modify.cs b/LibraryManagement.Api.Tests.Unit/Services/Foundations/Readers/ReaderServiceTests.Logic.Modify.cs new file mode 100644 index 0000000..ddca185 --- /dev/null +++ b/LibraryManagement.Api.Tests.Unit/Services/Foundations/Readers/ReaderServiceTests.Logic.Modify.cs @@ -0,0 +1,53 @@ +//----------------------------------------------------------- +// Copyright (c) Coalition of Good-Hearted Engineers +// Free To Use To Build Reliable Library Management Solutions +//----------------------------------------------------------- + +using FluentAssertions; +using LibraryManagement.Api.Models.Foundations.Readers; +using Moq; + +namespace LibraryManagement.Api.Tests.Unit.Services.Foundations.Readers +{ + public partial class ReaderServiceTests + { + [Fact] + public async Task ShouldModifyReaderAsync() + { + // given + Reader randomReader = CreateRandomReader(); + Reader inputReader = randomReader; + Reader persistedReader = inputReader; + Reader updatedReader = inputReader; + Reader expectedReader = updatedReader; + Guid InputReaderId = inputReader.ReaderId; + + this.storageBrokerMock.Setup(broker => + broker.SelectReaderByIdAsync(InputReaderId)) + .ReturnsAsync(persistedReader); + + this.storageBrokerMock.Setup(broker => + broker.UpdateReaderAsync(inputReader)) + .ReturnsAsync(updatedReader); + + // when + Reader actualReader = + await this.readerService + .ModifyReaderAsync(inputReader); + + // then + actualReader.Should().BeEquivalentTo(expectedReader); + + this.storageBrokerMock.Verify(broker => + broker.SelectReaderByIdAsync(InputReaderId), + Times.Once); + + this.storageBrokerMock.Verify(broker => + broker.UpdateReaderAsync(inputReader), + Times.Once); + + this.storageBrokerMock.VerifyNoOtherCalls(); + this.loggingBrokerMock.VerifyNoOtherCalls(); + } + } +} diff --git a/LibraryManagement.Api/Services/Foundations/Readers/IReaderService.cs b/LibraryManagement.Api/Services/Foundations/Readers/IReaderService.cs index 44822b2..0c1fea4 100644 --- a/LibraryManagement.Api/Services/Foundations/Readers/IReaderService.cs +++ b/LibraryManagement.Api/Services/Foundations/Readers/IReaderService.cs @@ -12,5 +12,6 @@ public interface IReaderService ValueTask AddReaderAsync(Reader reader); IQueryable RetrieveAllReaders(); ValueTask RetrieveReaderByIdAsync(Guid readerId); + ValueTask ModifyReaderAsync(Reader reader); } } diff --git a/LibraryManagement.Api/Services/Foundations/Readers/ReaderService.cs b/LibraryManagement.Api/Services/Foundations/Readers/ReaderService.cs index a6a7753..e91be4e 100644 --- a/LibraryManagement.Api/Services/Foundations/Readers/ReaderService.cs +++ b/LibraryManagement.Api/Services/Foundations/Readers/ReaderService.cs @@ -45,5 +45,8 @@ public ValueTask RetrieveReaderByIdAsync(Guid readerId) => return maybeReader; }); + + public ValueTask ModifyReaderAsync(Reader reader) => + throw new NotImplementedException(); } } From 703b52a08b2474142b9ebe3c1314cf4e3e0b5234 Mon Sep 17 00:00:00 2001 From: DilmurodDeveloper Date: Mon, 21 Jul 2025 17:07:24 +0500 Subject: [PATCH 02/17] ShouldModifyReaderAsync -> PASS --- .../Readers/ReaderServiceTests.Logic.Modify.cs | 5 +++-- .../Services/Foundations/Readers/ReaderService.cs | 9 +++++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/LibraryManagement.Api.Tests.Unit/Services/Foundations/Readers/ReaderServiceTests.Logic.Modify.cs b/LibraryManagement.Api.Tests.Unit/Services/Foundations/Readers/ReaderServiceTests.Logic.Modify.cs index ddca185..5f49556 100644 --- a/LibraryManagement.Api.Tests.Unit/Services/Foundations/Readers/ReaderServiceTests.Logic.Modify.cs +++ b/LibraryManagement.Api.Tests.Unit/Services/Foundations/Readers/ReaderServiceTests.Logic.Modify.cs @@ -4,6 +4,7 @@ //----------------------------------------------------------- using FluentAssertions; +using Force.DeepCloner; using LibraryManagement.Api.Models.Foundations.Readers; using Moq; @@ -17,9 +18,9 @@ public async Task ShouldModifyReaderAsync() // given Reader randomReader = CreateRandomReader(); Reader inputReader = randomReader; - Reader persistedReader = inputReader; + Reader persistedReader = inputReader.DeepClone(); Reader updatedReader = inputReader; - Reader expectedReader = updatedReader; + Reader expectedReader = updatedReader.DeepClone(); Guid InputReaderId = inputReader.ReaderId; this.storageBrokerMock.Setup(broker => diff --git a/LibraryManagement.Api/Services/Foundations/Readers/ReaderService.cs b/LibraryManagement.Api/Services/Foundations/Readers/ReaderService.cs index e91be4e..7e226a6 100644 --- a/LibraryManagement.Api/Services/Foundations/Readers/ReaderService.cs +++ b/LibraryManagement.Api/Services/Foundations/Readers/ReaderService.cs @@ -46,7 +46,12 @@ public ValueTask RetrieveReaderByIdAsync(Guid readerId) => return maybeReader; }); - public ValueTask ModifyReaderAsync(Reader reader) => - throw new NotImplementedException(); + public async ValueTask ModifyReaderAsync(Reader reader) + { + Reader maybeReader = + await this.storageBroker.SelectReaderByIdAsync(reader.ReaderId); + + return await this.storageBroker.UpdateReaderAsync(reader); + } } } From 988ed22c4c0113dd4d441c212fa3d2f1b8959527 Mon Sep 17 00:00:00 2001 From: DilmurodDeveloper Date: Mon, 21 Jul 2025 17:10:06 +0500 Subject: [PATCH 03/17] ShouldThrowValidationExceptionOnModifyIfReaderIsNullAndLogItAsync -> FAIL --- .../ReaderServiceTests.Validations.Modify.cs | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 LibraryManagement.Api.Tests.Unit/Services/Foundations/Readers/ReaderServiceTests.Validations.Modify.cs diff --git a/LibraryManagement.Api.Tests.Unit/Services/Foundations/Readers/ReaderServiceTests.Validations.Modify.cs b/LibraryManagement.Api.Tests.Unit/Services/Foundations/Readers/ReaderServiceTests.Validations.Modify.cs new file mode 100644 index 0000000..943f151 --- /dev/null +++ b/LibraryManagement.Api.Tests.Unit/Services/Foundations/Readers/ReaderServiceTests.Validations.Modify.cs @@ -0,0 +1,50 @@ +//----------------------------------------------------------- +// Copyright (c) Coalition of Good-Hearted Engineers +// Free To Use To Build Reliable Library Management Solutions +//----------------------------------------------------------- + +using FluentAssertions; +using LibraryManagement.Api.Models.Foundations.Readers; +using LibraryManagement.Api.Models.Foundations.Readers.Exceptions; +using Moq; + +namespace LibraryManagement.Api.Tests.Unit.Services.Foundations.Readers +{ + public partial class ReaderServiceTests + { + [Fact] + public async Task ShouldThrowValidationExceptionOnModifyIfReaderIsNullAndLogItAsync() + { + // given + Reader nullReader = null; + var nullReaderException = new NullReaderException(); + + var expectedReaderValidationException = + new ReaderValidationException(nullReaderException); + + // when + ValueTask modifyReaderTask = + this.readerService.ModifyReaderAsync(nullReader); + + ReaderValidationException actualReaderValidationException = + await Assert.ThrowsAsync(() => + modifyReaderTask.AsTask()); + + // then + actualReaderValidationException.Should() + .BeEquivalentTo(expectedReaderValidationException); + + this.loggingBrokerMock.Verify(broker => + broker.LogError(It.Is(SameExceptionAs( + expectedReaderValidationException))), + Times.Once); + + this.storageBrokerMock.Verify(broker => + broker.UpdateReaderAsync(It.IsAny()), + Times.Never); + + this.loggingBrokerMock.VerifyNoOtherCalls(); + this.storageBrokerMock.VerifyNoOtherCalls(); + } + } +} From 408898c9a1c6b6c7089b8a9a9bebbba7bb3ceac3 Mon Sep 17 00:00:00 2001 From: DilmurodDeveloper Date: Mon, 21 Jul 2025 17:12:02 +0500 Subject: [PATCH 04/17] ShouldThrowValidationExceptionOnModifyIfReaderIsNullAndLogItAsync -> PASS --- .../Foundations/Readers/ReaderService.cs | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/LibraryManagement.Api/Services/Foundations/Readers/ReaderService.cs b/LibraryManagement.Api/Services/Foundations/Readers/ReaderService.cs index 7e226a6..a822201 100644 --- a/LibraryManagement.Api/Services/Foundations/Readers/ReaderService.cs +++ b/LibraryManagement.Api/Services/Foundations/Readers/ReaderService.cs @@ -6,6 +6,7 @@ using LibraryManagement.Api.Brokers.Loggings; using LibraryManagement.Api.Brokers.Storages; using LibraryManagement.Api.Models.Foundations.Readers; +using LibraryManagement.Api.Models.Foundations.Readers.Exceptions; namespace LibraryManagement.Api.Services.Foundations.Readers { @@ -48,10 +49,24 @@ public ValueTask RetrieveReaderByIdAsync(Guid readerId) => public async ValueTask ModifyReaderAsync(Reader reader) { - Reader maybeReader = - await this.storageBroker.SelectReaderByIdAsync(reader.ReaderId); + try + { + ValidateReaderNotNull(reader); + + Reader maybeReader = + await this.storageBroker.SelectReaderByIdAsync(reader.ReaderId); + + return await this.storageBroker.UpdateReaderAsync(reader); + } + catch (NullReaderException nullReaderException) + { + var readerValidationException = + new ReaderValidationException(nullReaderException); - return await this.storageBroker.UpdateReaderAsync(reader); + this.loggingBroker.LogError(readerValidationException); + + throw readerValidationException; + } } } } From 0c49e310d6e3735822eceef70f97a0bc958abd9d Mon Sep 17 00:00:00 2001 From: DilmurodDeveloper Date: Mon, 21 Jul 2025 17:14:20 +0500 Subject: [PATCH 05/17] ShouldThrowValidationExceptionOnModifyIfReaderIsInvalidAndLogItAsync -> FAIL --- .../ReaderServiceTests.Validations.Modify.cs | 59 +++++++++++++++++++ .../Foundations/Readers/ReaderService.cs | 2 +- 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/LibraryManagement.Api.Tests.Unit/Services/Foundations/Readers/ReaderServiceTests.Validations.Modify.cs b/LibraryManagement.Api.Tests.Unit/Services/Foundations/Readers/ReaderServiceTests.Validations.Modify.cs index 943f151..8016a72 100644 --- a/LibraryManagement.Api.Tests.Unit/Services/Foundations/Readers/ReaderServiceTests.Validations.Modify.cs +++ b/LibraryManagement.Api.Tests.Unit/Services/Foundations/Readers/ReaderServiceTests.Validations.Modify.cs @@ -46,5 +46,64 @@ await Assert.ThrowsAsync(() => this.loggingBrokerMock.VerifyNoOtherCalls(); this.storageBrokerMock.VerifyNoOtherCalls(); } + + [Theory] + [InlineData(null)] + [InlineData("")] + [InlineData(" ")] + public async Task ShouldThrowValidationExceptionOnModifyIfReaderIsInvalidAndLogItAsync( + string invalidText) + { + // given + var invalidReader = new Reader + { + FirstName = invalidText + }; + + var invalidReaderException = new InvalidReaderException(); + + invalidReaderException.AddData( + key: nameof(Reader.ReaderId), + values: "Id is required"); + + invalidReaderException.AddData( + key: nameof(Reader.FirstName), + values: "Text is required"); + + invalidReaderException.AddData( + key: nameof(Reader.LastName), + values: "Text is required"); + + invalidReaderException.AddData( + key: nameof(Reader.DateOfBirth), + values: "Date is required"); + + var expectedReaderValidationException = + new ReaderValidationException(invalidReaderException); + + // when + ValueTask modifyReaderTask = + this.readerService.ModifyReaderAsync(invalidReader); + + ReaderValidationException actualReaderValidationException = + await Assert.ThrowsAsync(() => + modifyReaderTask.AsTask()); + + // then + actualReaderValidationException.Should() + .BeEquivalentTo(expectedReaderValidationException); + + this.loggingBrokerMock.Verify(broker => + broker.LogError(It.Is(SameExceptionAs( + expectedReaderValidationException))), + Times.Once); + + this.storageBrokerMock.Verify(broker => + broker.UpdateReaderAsync(It.IsAny()), + Times.Never); + + this.loggingBrokerMock.VerifyNoOtherCalls(); + this.storageBrokerMock.VerifyNoOtherCalls(); + } } } diff --git a/LibraryManagement.Api/Services/Foundations/Readers/ReaderService.cs b/LibraryManagement.Api/Services/Foundations/Readers/ReaderService.cs index a822201..e912a30 100644 --- a/LibraryManagement.Api/Services/Foundations/Readers/ReaderService.cs +++ b/LibraryManagement.Api/Services/Foundations/Readers/ReaderService.cs @@ -64,7 +64,7 @@ public async ValueTask ModifyReaderAsync(Reader reader) new ReaderValidationException(nullReaderException); this.loggingBroker.LogError(readerValidationException); - + throw readerValidationException; } } From f69a0f16752910834c16253defc87c15abc0496e Mon Sep 17 00:00:00 2001 From: DilmurodDeveloper Date: Mon, 21 Jul 2025 17:17:18 +0500 Subject: [PATCH 06/17] ShouldThrowValidationExceptionOnModifyIfReaderIsInvalidAndLogItAsync -> PASS --- .../Readers/ReaderService.Validations.cs | 13 ++++++++++++- .../Services/Foundations/Readers/ReaderService.cs | 11 ++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/LibraryManagement.Api/Services/Foundations/Readers/ReaderService.Validations.cs b/LibraryManagement.Api/Services/Foundations/Readers/ReaderService.Validations.cs index 613cb5e..9108968 100644 --- a/LibraryManagement.Api/Services/Foundations/Readers/ReaderService.Validations.cs +++ b/LibraryManagement.Api/Services/Foundations/Readers/ReaderService.Validations.cs @@ -21,7 +21,18 @@ private void ValidateReaderOnAdd(Reader reader) (Rule: IsInvalid(reader.DateOfBirth), Parameter: nameof(Reader.DateOfBirth))); } - private void ValidateReaderNotNull(Reader reader) + private static void ValidateReaderOnModify(Reader reader) + { + ValidateReaderNotNull(reader); + + Validate( + (Rule: IsInvalid(reader.ReaderId), Parameter: nameof(Reader.ReaderId)), + (Rule: IsInvalid(reader.FirstName), Parameter: nameof(Reader.FirstName)), + (Rule: IsInvalid(reader.LastName), Parameter: nameof(Reader.LastName)), + (Rule: IsInvalid(reader.DateOfBirth), Parameter: nameof(Reader.DateOfBirth))); + } + + private static void ValidateReaderNotNull(Reader reader) { if (reader is null) { diff --git a/LibraryManagement.Api/Services/Foundations/Readers/ReaderService.cs b/LibraryManagement.Api/Services/Foundations/Readers/ReaderService.cs index e912a30..e300e96 100644 --- a/LibraryManagement.Api/Services/Foundations/Readers/ReaderService.cs +++ b/LibraryManagement.Api/Services/Foundations/Readers/ReaderService.cs @@ -51,7 +51,7 @@ public async ValueTask ModifyReaderAsync(Reader reader) { try { - ValidateReaderNotNull(reader); + ValidateReaderOnModify(reader); Reader maybeReader = await this.storageBroker.SelectReaderByIdAsync(reader.ReaderId); @@ -65,6 +65,15 @@ public async ValueTask ModifyReaderAsync(Reader reader) this.loggingBroker.LogError(readerValidationException); + throw readerValidationException; + } + catch (InvalidReaderException invalidReaderException) + { + var readerValidationException = + new ReaderValidationException(invalidReaderException); + + this.loggingBroker.LogError(readerValidationException); + throw readerValidationException; } } From 1e2c7fae655b7338c944783572fca8dfa90739d5 Mon Sep 17 00:00:00 2001 From: DilmurodDeveloper Date: Mon, 21 Jul 2025 17:18:50 +0500 Subject: [PATCH 07/17] ShouldThrowValidationExceptionOnModifyIfReaderDoesNotExistAndLogItAsync -> FAIL --- .../ReaderServiceTests.Validations.Modify.cs | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/LibraryManagement.Api.Tests.Unit/Services/Foundations/Readers/ReaderServiceTests.Validations.Modify.cs b/LibraryManagement.Api.Tests.Unit/Services/Foundations/Readers/ReaderServiceTests.Validations.Modify.cs index 8016a72..11b3485 100644 --- a/LibraryManagement.Api.Tests.Unit/Services/Foundations/Readers/ReaderServiceTests.Validations.Modify.cs +++ b/LibraryManagement.Api.Tests.Unit/Services/Foundations/Readers/ReaderServiceTests.Validations.Modify.cs @@ -105,5 +105,52 @@ await Assert.ThrowsAsync(() => this.loggingBrokerMock.VerifyNoOtherCalls(); this.storageBrokerMock.VerifyNoOtherCalls(); } + + [Fact] + public async Task ShouldThrowValidationExceptionOnModifyIfReaderDoesNotExistAndLogItAsync() + { + // given + Reader randomReader = CreateRandomReader(); + Reader nonExistReader = randomReader; + Reader nullReader = null; + + var notFoundReaderException = + new NotFoundReaderException(nonExistReader.ReaderId); + + var expectedReaderValidationException = + new ReaderValidationException(notFoundReaderException); + + this.storageBrokerMock.Setup(broker => + broker.SelectReaderByIdAsync(nonExistReader.ReaderId)) + .ReturnsAsync(nullReader); + + // when + ValueTask modifyReaderTask = + this.readerService.ModifyReaderAsync(nonExistReader); + + ReaderValidationException actualReaderValidationException = + await Assert.ThrowsAsync(() => + modifyReaderTask.AsTask()); + + // then + actualReaderValidationException.Should() + .BeEquivalentTo(expectedReaderValidationException); + + this.storageBrokerMock.Verify(broker => + broker.SelectReaderByIdAsync(nonExistReader.ReaderId), + Times.Once); + + this.loggingBrokerMock.Verify(broker => + broker.LogError(It.Is(SameExceptionAs( + expectedReaderValidationException))), + Times.Once); + + this.storageBrokerMock.Verify(broker => + broker.UpdateReaderAsync(nonExistReader), + Times.Never); + + this.storageBrokerMock.VerifyNoOtherCalls(); + this.loggingBrokerMock.VerifyNoOtherCalls(); + } } } From 3106037cd3ba728af19671b094cf814f8bfe4cbe Mon Sep 17 00:00:00 2001 From: DilmurodDeveloper Date: Mon, 21 Jul 2025 17:21:26 +0500 Subject: [PATCH 08/17] ShouldThrowValidationExceptionOnModifyIfReaderDoesNotExistAndLogItAsync -> PASS --- .../Foundations/Readers/ReaderService.Validations.cs | 11 +++++++++++ .../Services/Foundations/Readers/ReaderService.cs | 11 +++++++++++ 2 files changed, 22 insertions(+) diff --git a/LibraryManagement.Api/Services/Foundations/Readers/ReaderService.Validations.cs b/LibraryManagement.Api/Services/Foundations/Readers/ReaderService.Validations.cs index 9108968..4de8d2a 100644 --- a/LibraryManagement.Api/Services/Foundations/Readers/ReaderService.Validations.cs +++ b/LibraryManagement.Api/Services/Foundations/Readers/ReaderService.Validations.cs @@ -32,6 +32,17 @@ private static void ValidateReaderOnModify(Reader reader) (Rule: IsInvalid(reader.DateOfBirth), Parameter: nameof(Reader.DateOfBirth))); } + private static void ValidateAgainstStorageReaderOnModify(Reader reader, Reader storageReader) + { + ValidateStorageReader(storageReader, reader.ReaderId); + + Validate( + (Rule: IsInvalid(reader.ReaderId), Parameter: nameof(Reader.ReaderId)), + (Rule: IsInvalid(reader.FirstName), Parameter: nameof(Reader.FirstName)), + (Rule: IsInvalid(reader.LastName), Parameter: nameof(Reader.LastName)), + (Rule: IsInvalid(reader.DateOfBirth), Parameter: nameof(Reader.DateOfBirth))); + } + private static void ValidateReaderNotNull(Reader reader) { if (reader is null) diff --git a/LibraryManagement.Api/Services/Foundations/Readers/ReaderService.cs b/LibraryManagement.Api/Services/Foundations/Readers/ReaderService.cs index e300e96..6dbd7ab 100644 --- a/LibraryManagement.Api/Services/Foundations/Readers/ReaderService.cs +++ b/LibraryManagement.Api/Services/Foundations/Readers/ReaderService.cs @@ -56,6 +56,8 @@ public async ValueTask ModifyReaderAsync(Reader reader) Reader maybeReader = await this.storageBroker.SelectReaderByIdAsync(reader.ReaderId); + ValidateAgainstStorageReaderOnModify(reader, maybeReader); + return await this.storageBroker.UpdateReaderAsync(reader); } catch (NullReaderException nullReaderException) @@ -74,6 +76,15 @@ public async ValueTask ModifyReaderAsync(Reader reader) this.loggingBroker.LogError(readerValidationException); + throw readerValidationException; + } + catch (NotFoundReaderException notFoundReaderException) + { + var readerValidationException = + new ReaderValidationException(notFoundReaderException); + + this.loggingBroker.LogError(readerValidationException); + throw readerValidationException; } } From 945bbde0734f782c61d481cf1237fb20397a0a84 Mon Sep 17 00:00:00 2001 From: DilmurodDeveloper Date: Mon, 21 Jul 2025 17:24:06 +0500 Subject: [PATCH 09/17] ShouldThrowCriticalDependencyExceptionOnModifyIfSqlErrorOccursAndLogItAsync -> FAIL --- .../ReaderServiceTests.Exceptions.Modify.cs | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 LibraryManagement.Api.Tests.Unit/Services/Foundations/Readers/ReaderServiceTests.Exceptions.Modify.cs diff --git a/LibraryManagement.Api.Tests.Unit/Services/Foundations/Readers/ReaderServiceTests.Exceptions.Modify.cs b/LibraryManagement.Api.Tests.Unit/Services/Foundations/Readers/ReaderServiceTests.Exceptions.Modify.cs new file mode 100644 index 0000000..9a36cdc --- /dev/null +++ b/LibraryManagement.Api.Tests.Unit/Services/Foundations/Readers/ReaderServiceTests.Exceptions.Modify.cs @@ -0,0 +1,64 @@ +//----------------------------------------------------------- +// Copyright (c) Coalition of Good-Hearted Engineers +// Free To Use To Build Reliable Library Management Solutions +//----------------------------------------------------------- + +using FluentAssertions; +using LibraryManagement.Api.Models.Foundations.Readers; +using LibraryManagement.Api.Models.Foundations.Readers.Exceptions; +using Microsoft.Data.SqlClient; +using Moq; + +namespace LibraryManagement.Api.Tests.Unit.Services.Foundations.Readers +{ + public partial class ReaderServiceTests + { + [Fact] + public async Task ShouldThrowCriticalDependencyExceptionOnModifyIfSqlErrorOccursAndLogItAsync() + { + // given + Reader randomReader = CreateRandomReader(); + Reader someReader = randomReader; + Guid readerId = someReader.ReaderId; + SqlException sqlException = GetSqlError(); + + var failedReaderStorageException = + new FailedReaderStorageException(sqlException); + + var expectedReaderDependencyException = + new ReaderDependencyException(failedReaderStorageException); + + this.storageBrokerMock.Setup(broker => + broker.SelectReaderByIdAsync(readerId)) + .Throws(sqlException); + + // when + ValueTask modifyReaderTask = + this.readerService.ModifyReaderAsync(someReader); + + ReaderDependencyException actualReaderDependencyException = + await Assert.ThrowsAsync(() => + modifyReaderTask.AsTask()); + + // then + actualReaderDependencyException.Should() + .BeEquivalentTo(expectedReaderDependencyException); + + this.loggingBrokerMock.Verify(broker => + broker.LogCritical(It.Is(SameExceptionAs( + expectedReaderDependencyException))), + Times.Once); + + this.storageBrokerMock.Verify(broker => + broker.SelectReaderByIdAsync(readerId), + Times.Once); + + this.storageBrokerMock.Verify(broker => + broker.UpdateReaderAsync(someReader), + Times.Never); + + this.loggingBrokerMock.VerifyNoOtherCalls(); + this.storageBrokerMock.VerifyNoOtherCalls(); + } + } +} From 5e68cf4d6bd96a84bedc4e84ddf06633836c69e9 Mon Sep 17 00:00:00 2001 From: DilmurodDeveloper Date: Mon, 21 Jul 2025 17:25:16 +0500 Subject: [PATCH 10/17] ShouldThrowCriticalDependencyExceptionOnModifyIfSqlErrorOccursAndLogItAsync -> PASS --- .../Services/Foundations/Readers/ReaderService.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/LibraryManagement.Api/Services/Foundations/Readers/ReaderService.cs b/LibraryManagement.Api/Services/Foundations/Readers/ReaderService.cs index 6dbd7ab..6322ac3 100644 --- a/LibraryManagement.Api/Services/Foundations/Readers/ReaderService.cs +++ b/LibraryManagement.Api/Services/Foundations/Readers/ReaderService.cs @@ -7,6 +7,7 @@ using LibraryManagement.Api.Brokers.Storages; using LibraryManagement.Api.Models.Foundations.Readers; using LibraryManagement.Api.Models.Foundations.Readers.Exceptions; +using Microsoft.Data.SqlClient; namespace LibraryManagement.Api.Services.Foundations.Readers { @@ -87,6 +88,18 @@ public async ValueTask ModifyReaderAsync(Reader reader) throw readerValidationException; } + catch (SqlException sqlException) + { + var failedReaderStorageException = + new FailedReaderStorageException(sqlException); + + var readerDependencyException = + new ReaderDependencyException(failedReaderStorageException); + + this.loggingBroker.LogCritical(readerDependencyException); + + throw readerDependencyException; + } } } } From f250144ca571ac852276f9ef0c821caabc7ac8fd Mon Sep 17 00:00:00 2001 From: DilmurodDeveloper Date: Mon, 21 Jul 2025 17:26:48 +0500 Subject: [PATCH 11/17] ShouldThrowDependencyExceptionOnModifyIfDatabaseUpdateExceptionOccursAndLogItAsync -> FAIL --- .../ReaderServiceTests.Exceptions.Modify.cs | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/LibraryManagement.Api.Tests.Unit/Services/Foundations/Readers/ReaderServiceTests.Exceptions.Modify.cs b/LibraryManagement.Api.Tests.Unit/Services/Foundations/Readers/ReaderServiceTests.Exceptions.Modify.cs index 9a36cdc..6e168ea 100644 --- a/LibraryManagement.Api.Tests.Unit/Services/Foundations/Readers/ReaderServiceTests.Exceptions.Modify.cs +++ b/LibraryManagement.Api.Tests.Unit/Services/Foundations/Readers/ReaderServiceTests.Exceptions.Modify.cs @@ -7,6 +7,7 @@ using LibraryManagement.Api.Models.Foundations.Readers; using LibraryManagement.Api.Models.Foundations.Readers.Exceptions; using Microsoft.Data.SqlClient; +using Microsoft.EntityFrameworkCore; using Moq; namespace LibraryManagement.Api.Tests.Unit.Services.Foundations.Readers @@ -60,5 +61,53 @@ await Assert.ThrowsAsync(() => this.loggingBrokerMock.VerifyNoOtherCalls(); this.storageBrokerMock.VerifyNoOtherCalls(); } + + [Fact] + public async Task ShouldThrowDependencyExceptionOnModifyIfDatabaseUpdateExceptionOccursAndLogItAsync() + { + // given + Reader randomReader = CreateRandomReader(); + Reader someReader = randomReader; + Guid readerId = someReader.ReaderId; + var databaseUpdateException = new DbUpdateException(); + + var failedReaderStorageException = + new FailedReaderStorageException(databaseUpdateException); + + var expectedReaderDependencyException = + new ReaderDependencyException(failedReaderStorageException); + + this.storageBrokerMock.Setup(broker => + broker.SelectReaderByIdAsync(readerId)) + .Throws(databaseUpdateException); + + // when + ValueTask modifyReaderTask = + this.readerService.ModifyReaderAsync(someReader); + + ReaderDependencyException actualReaderDependencyException = + await Assert.ThrowsAsync(() => + modifyReaderTask.AsTask()); + + // then + actualReaderDependencyException.Should() + .BeEquivalentTo(expectedReaderDependencyException); + + this.loggingBrokerMock.Verify(broker => + broker.LogError(It.Is(SameExceptionAs( + expectedReaderDependencyException))), + Times.Once); + + this.storageBrokerMock.Verify(broker => + broker.SelectReaderByIdAsync(readerId), + Times.Once); + + this.storageBrokerMock.Verify(broker => + broker.UpdateReaderAsync(someReader), + Times.Never); + + this.loggingBrokerMock.VerifyNoOtherCalls(); + this.storageBrokerMock.VerifyNoOtherCalls(); + } } } From e9e5a67ea38002873d4a918cd2bbd6afdc6b8cb3 Mon Sep 17 00:00:00 2001 From: DilmurodDeveloper Date: Mon, 21 Jul 2025 17:28:10 +0500 Subject: [PATCH 12/17] ShouldThrowDependencyExceptionOnModifyIfDatabaseUpdateExceptionOccursAndLogItAsync -> PASS --- .../Services/Foundations/Readers/ReaderService.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/LibraryManagement.Api/Services/Foundations/Readers/ReaderService.cs b/LibraryManagement.Api/Services/Foundations/Readers/ReaderService.cs index 6322ac3..10cd385 100644 --- a/LibraryManagement.Api/Services/Foundations/Readers/ReaderService.cs +++ b/LibraryManagement.Api/Services/Foundations/Readers/ReaderService.cs @@ -8,6 +8,7 @@ using LibraryManagement.Api.Models.Foundations.Readers; using LibraryManagement.Api.Models.Foundations.Readers.Exceptions; using Microsoft.Data.SqlClient; +using Microsoft.EntityFrameworkCore; namespace LibraryManagement.Api.Services.Foundations.Readers { @@ -98,6 +99,18 @@ public async ValueTask ModifyReaderAsync(Reader reader) this.loggingBroker.LogCritical(readerDependencyException); + throw readerDependencyException; + } + catch (DbUpdateException dbUpdateException) + { + var failedReaderStorageException = + new FailedReaderStorageException(dbUpdateException); + + var readerDependencyException = + new ReaderDependencyException(failedReaderStorageException); + + this.loggingBroker.LogError(readerDependencyException); + throw readerDependencyException; } } From 5780055c9067b9c76df9444b1e699ff343c86990 Mon Sep 17 00:00:00 2001 From: DilmurodDeveloper Date: Mon, 21 Jul 2025 17:31:27 +0500 Subject: [PATCH 13/17] ShouldThrowDependencyValidationExceptionOnModifyIfDatabaseUpdateConcurrencyErrorOccursAndLogItAsync -> FAIL --- .../ReaderServiceTests.Exceptions.Modify.cs | 48 +++++++++++++++++++ .../Exceptions/LockedReaderException.cs | 17 +++++++ 2 files changed, 65 insertions(+) create mode 100644 LibraryManagement.Api/Models/Foundations/Readers/Exceptions/LockedReaderException.cs diff --git a/LibraryManagement.Api.Tests.Unit/Services/Foundations/Readers/ReaderServiceTests.Exceptions.Modify.cs b/LibraryManagement.Api.Tests.Unit/Services/Foundations/Readers/ReaderServiceTests.Exceptions.Modify.cs index 6e168ea..395a925 100644 --- a/LibraryManagement.Api.Tests.Unit/Services/Foundations/Readers/ReaderServiceTests.Exceptions.Modify.cs +++ b/LibraryManagement.Api.Tests.Unit/Services/Foundations/Readers/ReaderServiceTests.Exceptions.Modify.cs @@ -109,5 +109,53 @@ await Assert.ThrowsAsync(() => this.loggingBrokerMock.VerifyNoOtherCalls(); this.storageBrokerMock.VerifyNoOtherCalls(); } + + [Fact] + public async Task ShouldThrowDependencyValidationExceptionOnModifyIfDatabaseUpdateConcurrencyErrorOccursAndLogItAsync() + { + // given + Reader randomReader = CreateRandomReader(); + Reader someReader = randomReader; + Guid readerId = someReader.ReaderId; + var dbUpdateConcurrencyException = new DbUpdateConcurrencyException(); + + var lockedReaderException = + new LockedReaderException(dbUpdateConcurrencyException); + + var expectedReaderDependencyValidationException = + new ReaderDependencyValidationException(lockedReaderException); + + this.storageBrokerMock.Setup(broker => + broker.SelectReaderByIdAsync(readerId)) + .Throws(dbUpdateConcurrencyException); + + // when + ValueTask modifyReaderTask = + this.readerService.ModifyReaderAsync(someReader); + + ReaderDependencyValidationException actualReaderDependencyValidationException = + await Assert.ThrowsAsync(() => + modifyReaderTask.AsTask()); + + // then + actualReaderDependencyValidationException.Should() + .BeEquivalentTo(expectedReaderDependencyValidationException); + + this.loggingBrokerMock.Verify(broker => + broker.LogError(It.Is(SameExceptionAs( + expectedReaderDependencyValidationException))), + Times.Once); + + this.storageBrokerMock.Verify(broker => + broker.SelectReaderByIdAsync(readerId), + Times.Once); + + this.storageBrokerMock.Verify(broker => + broker.UpdateReaderAsync(someReader), + Times.Never); + + this.loggingBrokerMock.VerifyNoOtherCalls(); + this.storageBrokerMock.VerifyNoOtherCalls(); + } } } diff --git a/LibraryManagement.Api/Models/Foundations/Readers/Exceptions/LockedReaderException.cs b/LibraryManagement.Api/Models/Foundations/Readers/Exceptions/LockedReaderException.cs new file mode 100644 index 0000000..3c67c4a --- /dev/null +++ b/LibraryManagement.Api/Models/Foundations/Readers/Exceptions/LockedReaderException.cs @@ -0,0 +1,17 @@ +//----------------------------------------------------------- +// Copyright (c) Coalition of Good-Hearted Engineers +// Free To Use To Build Reliable Library Management Solutions +//----------------------------------------------------------- + +using Xeptions; + +namespace LibraryManagement.Api.Models.Foundations.Readers.Exceptions +{ + public class LockedReaderException : Xeption + { + public LockedReaderException(Exception innerException) + : base(message: "Reader is locked, please try again later.", + innerException) + { } + } +} From b39b853f5f2d84c7760e948eb3cd950b5acabd7d Mon Sep 17 00:00:00 2001 From: DilmurodDeveloper Date: Mon, 21 Jul 2025 17:32:55 +0500 Subject: [PATCH 14/17] ShouldThrowDependencyValidationExceptionOnModifyIfDatabaseUpdateConcurrencyErrorOccursAndLogItAsync -> PASS --- .../Services/Foundations/Readers/ReaderService.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/LibraryManagement.Api/Services/Foundations/Readers/ReaderService.cs b/LibraryManagement.Api/Services/Foundations/Readers/ReaderService.cs index 10cd385..6afa68c 100644 --- a/LibraryManagement.Api/Services/Foundations/Readers/ReaderService.cs +++ b/LibraryManagement.Api/Services/Foundations/Readers/ReaderService.cs @@ -101,6 +101,18 @@ public async ValueTask ModifyReaderAsync(Reader reader) throw readerDependencyException; } + catch (DbUpdateConcurrencyException dbUpdateConcurrencyException) + { + var lockedReaderException = + new LockedReaderException(dbUpdateConcurrencyException); + + var readerDependencyValidationException = + new ReaderDependencyValidationException(lockedReaderException); + + this.loggingBroker.LogError(readerDependencyValidationException); + + throw readerDependencyValidationException; + } catch (DbUpdateException dbUpdateException) { var failedReaderStorageException = From be7facb2af63322bb87a57136c8dfa8d0157f327 Mon Sep 17 00:00:00 2001 From: DilmurodDeveloper Date: Mon, 21 Jul 2025 17:33:59 +0500 Subject: [PATCH 15/17] ShouldThrowServiceExceptionOnModifyIfDatabaseUpdateErrorOccursAndLogItAsync-> FAIL --- .../ReaderServiceTests.Exceptions.Modify.cs | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/LibraryManagement.Api.Tests.Unit/Services/Foundations/Readers/ReaderServiceTests.Exceptions.Modify.cs b/LibraryManagement.Api.Tests.Unit/Services/Foundations/Readers/ReaderServiceTests.Exceptions.Modify.cs index 395a925..a398015 100644 --- a/LibraryManagement.Api.Tests.Unit/Services/Foundations/Readers/ReaderServiceTests.Exceptions.Modify.cs +++ b/LibraryManagement.Api.Tests.Unit/Services/Foundations/Readers/ReaderServiceTests.Exceptions.Modify.cs @@ -157,5 +157,53 @@ await Assert.ThrowsAsync(() => this.loggingBrokerMock.VerifyNoOtherCalls(); this.storageBrokerMock.VerifyNoOtherCalls(); } + + [Fact] + public async Task ShouldThrowServiceExceptionOnModifyIfDatabaseUpdateErrorOccursAndLogItAsync() + { + // given + Reader randomReader = CreateRandomReader(); + Reader someReader = randomReader; + Guid readerId = someReader.ReaderId; + Exception serviceException = new Exception(); + + var failedReaderServiceException = + new FailedReaderServiceException(serviceException); + + var expectedReaderServiceException = + new ReaderServiceException(failedReaderServiceException); + + this.storageBrokerMock.Setup(broker => + broker.SelectReaderByIdAsync(readerId)) + .Throws(serviceException); + + // when + ValueTask modifyReaderTask = + this.readerService.ModifyReaderAsync(someReader); + + ReaderServiceException actualReaderServiceException = + await Assert.ThrowsAsync(() => + modifyReaderTask.AsTask()); + + // then + actualReaderServiceException.Should() + .BeEquivalentTo(expectedReaderServiceException); + + this.loggingBrokerMock.Verify(broker => + broker.LogError(It.Is(SameExceptionAs( + expectedReaderServiceException))), + Times.Once); + + this.storageBrokerMock.Verify(broker => + broker.SelectReaderByIdAsync(readerId), + Times.Once); + + this.storageBrokerMock.Verify(broker => + broker.UpdateReaderAsync(someReader), + Times.Never); + + this.loggingBrokerMock.VerifyNoOtherCalls(); + this.storageBrokerMock.VerifyNoOtherCalls(); + } } } From fa17fff4fc27bd6d516c0cf8eba531e94d10d565 Mon Sep 17 00:00:00 2001 From: DilmurodDeveloper Date: Mon, 21 Jul 2025 17:35:17 +0500 Subject: [PATCH 16/17] ShouldThrowServiceExceptionOnModifyIfDatabaseUpdateErrorOccursAndLogItAsync -> PASS --- .../Readers/ReaderServiceTests.Exceptions.Modify.cs | 2 +- .../Services/Foundations/Readers/ReaderService.cs | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/LibraryManagement.Api.Tests.Unit/Services/Foundations/Readers/ReaderServiceTests.Exceptions.Modify.cs b/LibraryManagement.Api.Tests.Unit/Services/Foundations/Readers/ReaderServiceTests.Exceptions.Modify.cs index a398015..93cc7f9 100644 --- a/LibraryManagement.Api.Tests.Unit/Services/Foundations/Readers/ReaderServiceTests.Exceptions.Modify.cs +++ b/LibraryManagement.Api.Tests.Unit/Services/Foundations/Readers/ReaderServiceTests.Exceptions.Modify.cs @@ -165,7 +165,7 @@ public async Task ShouldThrowServiceExceptionOnModifyIfDatabaseUpdateErrorOccurs Reader randomReader = CreateRandomReader(); Reader someReader = randomReader; Guid readerId = someReader.ReaderId; - Exception serviceException = new Exception(); + var serviceException = new Exception(); var failedReaderServiceException = new FailedReaderServiceException(serviceException); diff --git a/LibraryManagement.Api/Services/Foundations/Readers/ReaderService.cs b/LibraryManagement.Api/Services/Foundations/Readers/ReaderService.cs index 6afa68c..d67ac0c 100644 --- a/LibraryManagement.Api/Services/Foundations/Readers/ReaderService.cs +++ b/LibraryManagement.Api/Services/Foundations/Readers/ReaderService.cs @@ -125,6 +125,18 @@ public async ValueTask ModifyReaderAsync(Reader reader) throw readerDependencyException; } + catch (Exception exception) + { + var failedReaderServiceException = + new FailedReaderServiceException(exception); + + var readerServiceException = + new ReaderServiceException(failedReaderServiceException); + + this.loggingBroker.LogError(readerServiceException); + + throw readerServiceException; + } } } } From 6cd3869fa8eb02cec5cbe1571a1f0be1bd4d7e54 Mon Sep 17 00:00:00 2001 From: DilmurodDeveloper Date: Mon, 21 Jul 2025 17:40:40 +0500 Subject: [PATCH 17/17] CODE RUB: Implement TryCatch --- .../Readers/ReaderService.Exceptions.cs | 23 +++++ .../Foundations/Readers/ReaderService.cs | 96 ++----------------- 2 files changed, 31 insertions(+), 88 deletions(-) diff --git a/LibraryManagement.Api/Services/Foundations/Readers/ReaderService.Exceptions.cs b/LibraryManagement.Api/Services/Foundations/Readers/ReaderService.Exceptions.cs index c046ebc..40768cf 100644 --- a/LibraryManagement.Api/Services/Foundations/Readers/ReaderService.Exceptions.cs +++ b/LibraryManagement.Api/Services/Foundations/Readers/ReaderService.Exceptions.cs @@ -7,6 +7,7 @@ using LibraryManagement.Api.Models.Foundations.Readers; using LibraryManagement.Api.Models.Foundations.Readers.Exceptions; using Microsoft.Data.SqlClient; +using Microsoft.EntityFrameworkCore; using Xeptions; namespace LibraryManagement.Api.Services.Foundations.Readers @@ -34,6 +35,20 @@ private async ValueTask TryCatch(ReturningReaderFunction returningReader { throw CreateAndLogValidationException(notFoundReaderException); } + catch (DbUpdateConcurrencyException dbUpdateConcurrencyException) + { + var lockedReaderException = + new LockedReaderException(dbUpdateConcurrencyException); + + throw CreateAndLogDependencyValidationException(lockedReaderException); + } + catch (DbUpdateException dbUpdateException) + { + var failedReaderStorageException = + new FailedReaderStorageException(dbUpdateException); + + throw CreateAndLogDependencyException(failedReaderStorageException); + } catch (SqlException sqlException) { var failedReaderStorageException = @@ -115,5 +130,13 @@ private ReaderServiceException CreateAndLogServiceException(Xeption exception) return readerServiceException; } + + private ReaderDependencyException CreateAndLogDependencyException(Xeption exception) + { + var readerDependencyException = new ReaderDependencyException(exception); + this.loggingBroker.LogError(readerDependencyException); + + return readerDependencyException; + } } } diff --git a/LibraryManagement.Api/Services/Foundations/Readers/ReaderService.cs b/LibraryManagement.Api/Services/Foundations/Readers/ReaderService.cs index d67ac0c..12769f2 100644 --- a/LibraryManagement.Api/Services/Foundations/Readers/ReaderService.cs +++ b/LibraryManagement.Api/Services/Foundations/Readers/ReaderService.cs @@ -6,9 +6,6 @@ using LibraryManagement.Api.Brokers.Loggings; using LibraryManagement.Api.Brokers.Storages; using LibraryManagement.Api.Models.Foundations.Readers; -using LibraryManagement.Api.Models.Foundations.Readers.Exceptions; -using Microsoft.Data.SqlClient; -using Microsoft.EntityFrameworkCore; namespace LibraryManagement.Api.Services.Foundations.Readers { @@ -49,94 +46,17 @@ public ValueTask RetrieveReaderByIdAsync(Guid readerId) => return maybeReader; }); - public async ValueTask ModifyReaderAsync(Reader reader) + public ValueTask ModifyReaderAsync(Reader reader) => + TryCatch(async () => { - try - { - ValidateReaderOnModify(reader); - - Reader maybeReader = - await this.storageBroker.SelectReaderByIdAsync(reader.ReaderId); - - ValidateAgainstStorageReaderOnModify(reader, maybeReader); - - return await this.storageBroker.UpdateReaderAsync(reader); - } - catch (NullReaderException nullReaderException) - { - var readerValidationException = - new ReaderValidationException(nullReaderException); - - this.loggingBroker.LogError(readerValidationException); - - throw readerValidationException; - } - catch (InvalidReaderException invalidReaderException) - { - var readerValidationException = - new ReaderValidationException(invalidReaderException); - - this.loggingBroker.LogError(readerValidationException); - - throw readerValidationException; - } - catch (NotFoundReaderException notFoundReaderException) - { - var readerValidationException = - new ReaderValidationException(notFoundReaderException); - - this.loggingBroker.LogError(readerValidationException); - - throw readerValidationException; - } - catch (SqlException sqlException) - { - var failedReaderStorageException = - new FailedReaderStorageException(sqlException); - - var readerDependencyException = - new ReaderDependencyException(failedReaderStorageException); + ValidateReaderOnModify(reader); - this.loggingBroker.LogCritical(readerDependencyException); - - throw readerDependencyException; - } - catch (DbUpdateConcurrencyException dbUpdateConcurrencyException) - { - var lockedReaderException = - new LockedReaderException(dbUpdateConcurrencyException); - - var readerDependencyValidationException = - new ReaderDependencyValidationException(lockedReaderException); - - this.loggingBroker.LogError(readerDependencyValidationException); - - throw readerDependencyValidationException; - } - catch (DbUpdateException dbUpdateException) - { - var failedReaderStorageException = - new FailedReaderStorageException(dbUpdateException); - - var readerDependencyException = - new ReaderDependencyException(failedReaderStorageException); - - this.loggingBroker.LogError(readerDependencyException); - - throw readerDependencyException; - } - catch (Exception exception) - { - var failedReaderServiceException = - new FailedReaderServiceException(exception); - - var readerServiceException = - new ReaderServiceException(failedReaderServiceException); + Reader maybeReader = + await this.storageBroker.SelectReaderByIdAsync(reader.ReaderId); - this.loggingBroker.LogError(readerServiceException); + ValidateAgainstStorageReaderOnModify(reader, maybeReader); - throw readerServiceException; - } - } + return await this.storageBroker.UpdateReaderAsync(reader); + }); } }