diff --git a/LibraryManagement.Api.Tests.Unit/Services/Foundations/Readers/ReaderServiceTests.Exceptions.RemoveById.cs b/LibraryManagement.Api.Tests.Unit/Services/Foundations/Readers/ReaderServiceTests.Exceptions.RemoveById.cs new file mode 100644 index 0000000..c1cae50 --- /dev/null +++ b/LibraryManagement.Api.Tests.Unit/Services/Foundations/Readers/ReaderServiceTests.Exceptions.RemoveById.cs @@ -0,0 +1,147 @@ +//----------------------------------------------------------- +// 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 Microsoft.EntityFrameworkCore; +using Moq; + +namespace LibraryManagement.Api.Tests.Unit.Services.Foundations.Readers +{ + public partial class ReaderServiceTests + { + [Fact] + public async Task ShouldThrowDependencyValidationOnRemoveIfDatabaseUpdateConcurrencyErrorOccursAndLogItAsync() + { + // given + Guid someReaderId = Guid.NewGuid(); + var dbUpdateConcurrencyException = new DbUpdateConcurrencyException(); + + var lockedReaderException = + new LockedReaderException(dbUpdateConcurrencyException); + + var expectedReaderDependencyValidationException = + new ReaderDependencyValidationException(lockedReaderException); + + this.storageBrokerMock.Setup(broker => + broker.SelectReaderByIdAsync(It.IsAny())) + .ThrowsAsync(dbUpdateConcurrencyException); + + // when + ValueTask removeReaderById = + this.readerService.RemoveReaderByIdAsync(someReaderId); + + ReaderDependencyValidationException actualReaderDependencyValidationException = + await Assert.ThrowsAsync(() => + removeReaderById.AsTask()); + + // then + actualReaderDependencyValidationException.Should() + .BeEquivalentTo(expectedReaderDependencyValidationException); + + this.storageBrokerMock.Verify(broker => + broker.SelectReaderByIdAsync(It.IsAny()), + Times.Once); + + this.loggingBrokerMock.Verify(broker => + broker.LogError(It.Is(SameExceptionAs( + expectedReaderDependencyValidationException))), + Times.Once); + + this.storageBrokerMock.Verify(broker => + broker.DeleteReaderAsync(It.IsAny()), + Times.Never); + + this.storageBrokerMock.VerifyNoOtherCalls(); + this.loggingBrokerMock.VerifyNoOtherCalls(); + } + + [Fact] + public async Task ShouldThrowDependencyExceptionOnRemoveWhenSqlExceptionOccursAndLogItAsync() + { + // given + Guid someLocationId = Guid.NewGuid(); + SqlException sqlException = GetSqlError(); + + var failedReaderStorageException = + new FailedReaderStorageException(sqlException); + + var expectedReaderDependencyException = + new ReaderDependencyException(failedReaderStorageException); + + this.storageBrokerMock.Setup(broker => + broker.SelectReaderByIdAsync(It.IsAny())) + .ThrowsAsync(sqlException); + + // when + ValueTask deleteReaderTask = + this.readerService.RemoveReaderByIdAsync(someLocationId); + + ReaderDependencyException actualReaderDependencyException = + await Assert.ThrowsAsync(() => + deleteReaderTask.AsTask()); + + // then + actualReaderDependencyException.Should() + .BeEquivalentTo(expectedReaderDependencyException); + + this.storageBrokerMock.Verify(broker => + broker.SelectReaderByIdAsync(It.IsAny()), + Times.Once); + + this.loggingBrokerMock.Verify(broker => + broker.LogCritical(It.Is(SameExceptionAs( + expectedReaderDependencyException))), + Times.Once); + + this.storageBrokerMock.VerifyNoOtherCalls(); + this.loggingBrokerMock.VerifyNoOtherCalls(); + } + + [Fact] + public async Task ShouldThrowServiceExceptionOnRemoveIfExceptionOccursAndLogItAsync() + { + // given + Guid someReaderId = Guid.NewGuid(); + var serviceException = new Exception(); + + var failedReaderServiceException = + new FailedReaderServiceException(serviceException); + + var expectedReaderServiceException = + new ReaderServiceException(failedReaderServiceException); + + this.storageBrokerMock.Setup(broker => + broker.SelectReaderByIdAsync(It.IsAny())) + .ThrowsAsync(serviceException); + + // when + ValueTask removeReaderByIdTask = + this.readerService.RemoveReaderByIdAsync(someReaderId); + + ReaderServiceException actualReaderServiceException = + await Assert.ThrowsAsync(() => + removeReaderByIdTask.AsTask()); + + // then + actualReaderServiceException.Should() + .BeEquivalentTo(expectedReaderServiceException); + + this.storageBrokerMock.Verify(broker => + broker.SelectReaderByIdAsync(It.IsAny()), + Times.Once); + + this.loggingBrokerMock.Verify(broker => + broker.LogError(It.Is(SameExceptionAs( + expectedReaderServiceException))), + Times.Once); + + this.storageBrokerMock.VerifyNoOtherCalls(); + this.loggingBrokerMock.VerifyNoOtherCalls(); + } + } +} diff --git a/LibraryManagement.Api.Tests.Unit/Services/Foundations/Readers/ReaderServiceTests.Logic.RemoveById.cs b/LibraryManagement.Api.Tests.Unit/Services/Foundations/Readers/ReaderServiceTests.Logic.RemoveById.cs new file mode 100644 index 0000000..cbaba0f --- /dev/null +++ b/LibraryManagement.Api.Tests.Unit/Services/Foundations/Readers/ReaderServiceTests.Logic.RemoveById.cs @@ -0,0 +1,54 @@ +//----------------------------------------------------------- +// Copyright (c) Coalition of Good-Hearted Engineers +// Free To Use To Build Reliable Library Management Solutions +//----------------------------------------------------------- + +using FluentAssertions; +using Force.DeepCloner; +using LibraryManagement.Api.Models.Foundations.Readers; +using Moq; + +namespace LibraryManagement.Api.Tests.Unit.Services.Foundations.Readers +{ + public partial class ReaderServiceTests + { + [Fact] + public async Task ShouldRemoveReaderByIdAsync() + { + // given + Guid randomId = Guid.NewGuid(); + Guid inputReaderId = randomId; + Reader randomReader = CreateRandomReader(); + Reader storageReader = randomReader; + Reader expectedInputReader = storageReader; + Reader deletedReader = expectedInputReader; + Reader expectedReader = deletedReader.DeepClone(); + + this.storageBrokerMock.Setup(broker => + broker.SelectReaderByIdAsync(inputReaderId)) + .ReturnsAsync(storageReader); + + this.storageBrokerMock.Setup(broker => + broker.DeleteReaderAsync(expectedInputReader)) + .ReturnsAsync(deletedReader); + + // when + Reader actualReader = + await this.readerService.RemoveReaderByIdAsync(randomId); + + // then + actualReader.Should().BeEquivalentTo(expectedReader); + + this.storageBrokerMock.Verify(broker => + broker.SelectReaderByIdAsync(inputReaderId), + Times.Once); + + this.storageBrokerMock.Verify(broker => + broker.DeleteReaderAsync(expectedInputReader), + Times.Once); + + this.storageBrokerMock.VerifyNoOtherCalls(); + this.loggingBrokerMock.VerifyNoOtherCalls(); + } + } +} diff --git a/LibraryManagement.Api.Tests.Unit/Services/Foundations/Readers/ReaderServiceTests.Validations.RemoveById.cs b/LibraryManagement.Api.Tests.Unit/Services/Foundations/Readers/ReaderServiceTests.Validations.RemoveById.cs new file mode 100644 index 0000000..3433c9f --- /dev/null +++ b/LibraryManagement.Api.Tests.Unit/Services/Foundations/Readers/ReaderServiceTests.Validations.RemoveById.cs @@ -0,0 +1,105 @@ +//----------------------------------------------------------- +// 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 ShouldThrowValidationExceptionOnRemoveIfIdIsInvalidAndLogItAsync() + { + // given + Guid invalidReaderId = Guid.Empty; + + var invalidReaderException = new InvalidReaderException(); + + invalidReaderException.AddData( + key: nameof(Reader.ReaderId), + values: "Id is required"); + + var expectedReaderValidationException = + new ReaderValidationException(invalidReaderException); + + // when + ValueTask removeReaderById = + this.readerService.RemoveReaderByIdAsync(invalidReaderId); + + ReaderValidationException actualReaderValidationException = + await Assert.ThrowsAsync(() => + removeReaderById.AsTask()); + + // then + actualReaderValidationException.Should() + .BeEquivalentTo(expectedReaderValidationException); + + this.loggingBrokerMock.Verify(broker => + broker.LogError(It.Is(SameExceptionAs( + expectedReaderValidationException))), + Times.Once); + + this.storageBrokerMock.Verify(broker => + broker.SelectReaderByIdAsync(It.IsAny()), + Times.Never); + + this.storageBrokerMock.Verify(broker => + broker.DeleteReaderAsync(It.IsAny()), + Times.Never); + + this.loggingBrokerMock.VerifyNoOtherCalls(); + this.storageBrokerMock.VerifyNoOtherCalls(); + } + + [Fact] + public async Task ShouldThrowNotFoundExceptionOnRemoveReaderByIdIsNotFoundAndLogItAsync() + { + // given + Guid inputReaderId = Guid.NewGuid(); + Reader noReader = null; + + var notFoundReaderException = + new NotFoundReaderException(inputReaderId); + + var expectedReaderValidationException = + new ReaderValidationException(notFoundReaderException); + + this.storageBrokerMock.Setup(broker => + broker.SelectReaderByIdAsync(It.IsAny())) + .ReturnsAsync(noReader); + + // when + ValueTask removeReaderById = + this.readerService.RemoveReaderByIdAsync(inputReaderId); + + var actualReaderValidationException = + await Assert.ThrowsAsync(() => + removeReaderById.AsTask()); + + // then + actualReaderValidationException.Should() + .BeEquivalentTo(expectedReaderValidationException); + + this.storageBrokerMock.Verify(broker => + broker.SelectReaderByIdAsync(It.IsAny()), + Times.Once); + + this.loggingBrokerMock.Verify(broker => + broker.LogError(It.Is(SameExceptionAs( + expectedReaderValidationException))), + Times.Once); + + this.storageBrokerMock.Verify(broker => + broker.DeleteReaderAsync(It.IsAny()), + Times.Never); + + 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 0c1fea4..2dd6ebb 100644 --- a/LibraryManagement.Api/Services/Foundations/Readers/IReaderService.cs +++ b/LibraryManagement.Api/Services/Foundations/Readers/IReaderService.cs @@ -13,5 +13,6 @@ public interface IReaderService IQueryable RetrieveAllReaders(); ValueTask RetrieveReaderByIdAsync(Guid readerId); ValueTask ModifyReaderAsync(Reader reader); + ValueTask RemoveReaderByIdAsync(Guid readerId); } } diff --git a/LibraryManagement.Api/Services/Foundations/Readers/ReaderService.cs b/LibraryManagement.Api/Services/Foundations/Readers/ReaderService.cs index 12769f2..a20251b 100644 --- a/LibraryManagement.Api/Services/Foundations/Readers/ReaderService.cs +++ b/LibraryManagement.Api/Services/Foundations/Readers/ReaderService.cs @@ -58,5 +58,18 @@ public ValueTask ModifyReaderAsync(Reader reader) => return await this.storageBroker.UpdateReaderAsync(reader); }); + + public ValueTask RemoveReaderByIdAsync(Guid readerId) => + TryCatch(async () => + { + ValidateReaderId(readerId); + + Reader maybeReader = + await this.storageBroker.SelectReaderByIdAsync(readerId); + + ValidateStorageReader(maybeReader, readerId); + + return await this.storageBroker.DeleteReaderAsync(maybeReader); + }); } }