diff --git a/STX.EFxceptions.SQLite.Base.Tests.Unit/Services/Foundations/SQLiteEFxceptionServiceTests.Exceptions.cs b/STX.EFxceptions.SQLite.Base.Tests.Unit/Services/Foundations/SQLiteEFxceptionServiceTests.Exceptions.cs index e465945..b725ff4 100644 --- a/STX.EFxceptions.SQLite.Base.Tests.Unit/Services/Foundations/SQLiteEFxceptionServiceTests.Exceptions.cs +++ b/STX.EFxceptions.SQLite.Base.Tests.Unit/Services/Foundations/SQLiteEFxceptionServiceTests.Exceptions.cs @@ -2,8 +2,11 @@ // Copyright(c) The Standard Organization: A coalition of the Good-Hearted Engineers // ---------------------------------------------------------------------------------- +using FluentAssertions; using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; +using Moq; +using STX.EFxceptions.Abstractions.Models.Exceptions; using STX.EFxceptions.SQLite.Base.Models.Exceptions; using Xunit; @@ -17,19 +20,34 @@ public void ShouldThrowDbUpdateExceptionIfErrorCodeIsNotRecognized() // given int sqlForeignKeyConstraintConflictErrorCode = 0000; string randomErrorMessage = CreateRandomErrorMessage(); - SqliteException foreignKeyConstraintConflictException = CreateSQLiteException(); + + SqliteException foreignKeyConstraintConflictExceptionThrown = + CreateSQLiteException( + message: randomErrorMessage, + errorCode: sqlForeignKeyConstraintConflictErrorCode); var dbUpdateException = new DbUpdateException( message: randomErrorMessage, - innerException: foreignKeyConstraintConflictException); + innerException: foreignKeyConstraintConflictExceptionThrown); + + DbUpdateException expectedDbUpdateException = dbUpdateException; this.sqliteErrorBrokerMock.Setup(broker => - broker.GetErrorCode(foreignKeyConstraintConflictException)) + broker.GetErrorCode(foreignKeyConstraintConflictExceptionThrown)) .Returns(sqlForeignKeyConstraintConflictErrorCode); - // when . then - Assert.Throws(() => - this.sqliteEFxceptionService.ThrowMeaningfulException(dbUpdateException)); + // when + DbUpdateException actualDbUpdateException = + Assert.Throws(() => this.sqliteEFxceptionService + .ThrowMeaningfulException(dbUpdateException)); + + // then + actualDbUpdateException.Should().BeEquivalentTo(expectedDbUpdateException); + + this.sqliteErrorBrokerMock.Verify(broker => broker + .GetErrorCode(foreignKeyConstraintConflictExceptionThrown), Times.Once()); + + sqliteErrorBrokerMock.VerifyNoOtherCalls(); } [Fact] @@ -37,104 +55,267 @@ public void ShouldThrowInvalidColumnNameException() { // given int sqlInvalidColumnNameErrorCode = 207; - string randomErrorMessage = CreateRandomErrorMessage(); - SqliteException invalidColumnNameException = CreateSQLiteException(); + string randomSqliteExceptionMessage = CreateRandomErrorMessage(); - var dbUpdateException = new DbUpdateException( - message: randomErrorMessage, - innerException: invalidColumnNameException); + SqliteException invalidColumnNameExceptionThrown = + CreateSQLiteException( + message: randomSqliteExceptionMessage, + errorCode: sqlInvalidColumnNameErrorCode); + + string randomDbUpdateExceptionMessage = CreateRandomErrorMessage(); + + DbUpdateException dbUpdateExceptionThrown = new DbUpdateException( + message: randomDbUpdateExceptionMessage, + innerException: invalidColumnNameExceptionThrown); + + var ivalidColumnNameSqliteException = + new InvalidColumnNameSQLiteException( + message: invalidColumnNameExceptionThrown.Message); + + var expectedInvalidColumnNameException = + new InvalidColumnNameException( + message: ivalidColumnNameSqliteException.Message, + innerException: ivalidColumnNameSqliteException); this.sqliteErrorBrokerMock.Setup(broker => - broker.GetErrorCode(invalidColumnNameException)) + broker.GetErrorCode(invalidColumnNameExceptionThrown)) .Returns(sqlInvalidColumnNameErrorCode); - // when . then - Assert.Throws(() => - this.sqliteEFxceptionService.ThrowMeaningfulException(dbUpdateException)); + // when + InvalidColumnNameException actualInvalidColumnNameException = + Assert.Throws(() => + this.sqliteEFxceptionService.ThrowMeaningfulException(dbUpdateExceptionThrown)); + // then + actualInvalidColumnNameException.Should() + .BeEquivalentTo( + expectation: expectedInvalidColumnNameException, + config: options => options + .Excluding(ex => ex.TargetSite) + .Excluding(ex => ex.StackTrace) + .Excluding(ex => ex.Source) + .Excluding(ex => ex.InnerException.TargetSite) + .Excluding(ex => ex.InnerException.StackTrace) + .Excluding(ex => ex.InnerException.Source)); + + this.sqliteErrorBrokerMock.Verify(broker => broker + .GetErrorCode(invalidColumnNameExceptionThrown), Times.Once()); + + sqliteErrorBrokerMock.VerifyNoOtherCalls(); } [Fact] - public void ShouldThrowInvalidObjectNameSqliteException() + public void ShouldThrowInvalidObjectNameException() { // given int sqlInvalidObjectNameErrorCode = 208; - string randomErrorMessage = CreateRandomErrorMessage(); - SqliteException invalidObjectNameException = CreateSQLiteException(); + string randomSqliteExceptionMessage = CreateRandomErrorMessage(); + + SqliteException invalidObjectNameExceptionThrown = + CreateSQLiteException( + message: randomSqliteExceptionMessage, + errorCode: sqlInvalidObjectNameErrorCode); + + string randomDbUpdateExceptionMessage = CreateRandomErrorMessage(); var dbUpdateException = new DbUpdateException( - message: randomErrorMessage, - innerException: invalidObjectNameException); + message: randomDbUpdateExceptionMessage, + innerException: invalidObjectNameExceptionThrown); + + var invalidObjectNameSqliteException = + new InvalidObjectNameSQLiteException( + message: invalidObjectNameExceptionThrown.Message); + + var expectedInvalidObjectNameException = + new InvalidObjectNameException( + message: invalidObjectNameSqliteException.Message, + innerException: invalidObjectNameSqliteException); this.sqliteErrorBrokerMock.Setup(broker => - broker.GetErrorCode(invalidObjectNameException)) + broker.GetErrorCode(invalidObjectNameExceptionThrown)) .Returns(sqlInvalidObjectNameErrorCode); - // when . then - Assert.Throws(() => - this.sqliteEFxceptionService.ThrowMeaningfulException(dbUpdateException)); + // when + InvalidObjectNameException actualInvalidObjectNameException = + Assert.Throws(() => + this.sqliteEFxceptionService.ThrowMeaningfulException(dbUpdateException)); + + // then + actualInvalidObjectNameException.Should() + .BeEquivalentTo( + expectation: expectedInvalidObjectNameException, + config: options => options + .Excluding(ex => ex.TargetSite) + .Excluding(ex => ex.StackTrace) + .Excluding(ex => ex.Source) + .Excluding(ex => ex.InnerException.TargetSite) + .Excluding(ex => ex.InnerException.StackTrace) + .Excluding(ex => ex.InnerException.Source)); + + this.sqliteErrorBrokerMock.Verify(broker => broker + .GetErrorCode(invalidObjectNameExceptionThrown), Times.Once()); + + sqliteErrorBrokerMock.VerifyNoOtherCalls(); } [Fact] - public void ShouldThrowForeignKeyConstraintConflictSqliteException() + public void ShouldThrowForeignKeyConstraintConflictException() { // given int sqlForeignKeyConstraintConflictErrorCode = 547; - string randomErrorMessage = CreateRandomErrorMessage(); - SqliteException foreignKeyConstraintConflictException = CreateSQLiteException(); + string randomSqliteExceptionMessage = CreateRandomErrorMessage(); + + SqliteException foreignKeyConstraintConflictSqliteExceptionThrown = + CreateSQLiteException( + message: randomSqliteExceptionMessage, + errorCode: sqlForeignKeyConstraintConflictErrorCode); + + string randomDbUpdateExceptionMessage = CreateRandomErrorMessage(); var dbUpdateException = new DbUpdateException( - message: randomErrorMessage, - innerException: foreignKeyConstraintConflictException); + message: randomDbUpdateExceptionMessage, + innerException: foreignKeyConstraintConflictSqliteExceptionThrown); + + var foreignKeyConstraintConflictSqliteException = + new ForeignKeyConstraintConflictSQLiteException( + message: foreignKeyConstraintConflictSqliteExceptionThrown.Message); + + var expectedForeignKeyConstraintConflictException = + new ForeignKeyConstraintConflictException( + message: foreignKeyConstraintConflictSqliteException.Message, + innerException: foreignKeyConstraintConflictSqliteException); this.sqliteErrorBrokerMock.Setup(broker => - broker.GetErrorCode(foreignKeyConstraintConflictException)) + broker.GetErrorCode(foreignKeyConstraintConflictSqliteExceptionThrown)) .Returns(sqlForeignKeyConstraintConflictErrorCode); - // when . then - Assert.Throws(() => - this.sqliteEFxceptionService.ThrowMeaningfulException(dbUpdateException)); + // when + var actualForeignKeyConstraintConflictException = + Assert.Throws(() => + this.sqliteEFxceptionService.ThrowMeaningfulException(dbUpdateException)); + + // then + actualForeignKeyConstraintConflictException.Should() + .BeEquivalentTo( + expectation: expectedForeignKeyConstraintConflictException, + config: options => options + .Excluding(ex => ex.TargetSite) + .Excluding(ex => ex.StackTrace) + .Excluding(ex => ex.Source) + .Excluding(ex => ex.InnerException.TargetSite) + .Excluding(ex => ex.InnerException.StackTrace) + .Excluding(ex => ex.InnerException.Source)); + + this.sqliteErrorBrokerMock.Verify(broker => broker + .GetErrorCode(foreignKeyConstraintConflictSqliteExceptionThrown), Times.Once()); + + sqliteErrorBrokerMock.VerifyNoOtherCalls(); } [Fact] - public void ShouldThrowDuplicateKeyWithUniqueIndexSqliteException() + public void ShouldThrowDuplicateKeyWithUniqueIndexException() { // given - int sqlDuplicateKeyWithUniqueIndexErrorCode = 2601; - string randomErrorMessage = CreateRandomErrorMessage(); - SqliteException sqlDuplicateKeyWithUniqueIndexException = CreateSQLiteException(); + int sqlDuplicateKeyErrorCode = 2601; + string randomSqliteExceptionMessage = CreateRandomErrorMessage(); + + SqliteException duplicateKeyWithUniqueIndexSqliteExceptionThrown = + CreateSQLiteException( + message: randomSqliteExceptionMessage, + errorCode: sqlDuplicateKeyErrorCode); + + string randomDbUpdateExceptionMessage = CreateRandomErrorMessage(); var dbUpdateException = new DbUpdateException( - message: randomErrorMessage, - innerException: sqlDuplicateKeyWithUniqueIndexException); + message: randomDbUpdateExceptionMessage, + innerException: duplicateKeyWithUniqueIndexSqliteExceptionThrown); + + var duplicateKeyWithUniqueIndexSqliteException = + new DuplicateKeyWithUniqueIndexSQLiteException( + message: duplicateKeyWithUniqueIndexSqliteExceptionThrown.Message); + + var expectedDuplicateKeyWithUniqueIndexException = + new DuplicateKeyWithUniqueIndexException( + message: duplicateKeyWithUniqueIndexSqliteException.Message, + innerException: duplicateKeyWithUniqueIndexSqliteException); this.sqliteErrorBrokerMock.Setup(broker => - broker.GetErrorCode(sqlDuplicateKeyWithUniqueIndexException)) - .Returns(sqlDuplicateKeyWithUniqueIndexErrorCode); + broker.GetErrorCode(duplicateKeyWithUniqueIndexSqliteExceptionThrown)) + .Returns(sqlDuplicateKeyErrorCode); - // when . then - Assert.Throws(() => - this.sqliteEFxceptionService.ThrowMeaningfulException(dbUpdateException)); + // when + var actualDuplicateKeyWithUniqueIndexException = + Assert.Throws(() => + this.sqliteEFxceptionService.ThrowMeaningfulException(dbUpdateException)); + + // then + actualDuplicateKeyWithUniqueIndexException.Should() + .BeEquivalentTo( + expectation: expectedDuplicateKeyWithUniqueIndexException, + config: options => options + .Excluding(ex => ex.TargetSite) + .Excluding(ex => ex.StackTrace) + .Excluding(ex => ex.Source) + .Excluding(ex => ex.InnerException.TargetSite) + .Excluding(ex => ex.InnerException.StackTrace) + .Excluding(ex => ex.InnerException.Source)); + + this.sqliteErrorBrokerMock.Verify(broker => broker + .GetErrorCode(duplicateKeyWithUniqueIndexSqliteExceptionThrown), Times.Once()); + + sqliteErrorBrokerMock.VerifyNoOtherCalls(); } [Fact] - public void ShouldThrowDuplicateKeySqliteException() + public void ShouldThrowDuplicateKeyException() { // given int sqlDuplicateKeyErrorCode = 2627; - string randomErrorMessage = CreateRandomErrorMessage(); - SqliteException sqlDuplicateKeyException = CreateSQLiteException(); + string randomSqliteExceptionMessage = CreateRandomErrorMessage(); + + SqliteException duplicateKeySqliteExceptionThrown = CreateSQLiteException( + message: randomSqliteExceptionMessage, + errorCode: sqlDuplicateKeyErrorCode); + + string randomDbUpdateExceptionMessage = CreateRandomErrorMessage(); var dbUpdateException = new DbUpdateException( - message: randomErrorMessage, - innerException: sqlDuplicateKeyException); + message: randomDbUpdateExceptionMessage, + innerException: duplicateKeySqliteExceptionThrown); + + var duplicateKeySqliteException = + new DuplicateKeySQLiteException( + message: duplicateKeySqliteExceptionThrown.Message); + + var expectedDuplicateKeyException = + new DuplicateKeyException( + message: duplicateKeySqliteException.Message, + innerException: duplicateKeySqliteException); this.sqliteErrorBrokerMock.Setup(broker => - broker.GetErrorCode(sqlDuplicateKeyException)) + broker.GetErrorCode(duplicateKeySqliteExceptionThrown)) .Returns(sqlDuplicateKeyErrorCode); - // when . then - Assert.Throws(() => - this.sqliteEFxceptionService.ThrowMeaningfulException(dbUpdateException)); + // when + var actualDuplicateKeyException = + Assert.Throws(() => + this.sqliteEFxceptionService.ThrowMeaningfulException(dbUpdateException)); + + // then + actualDuplicateKeyException.Should() + .BeEquivalentTo( + expectation: expectedDuplicateKeyException, + config: options => options + .Excluding(ex => ex.TargetSite) + .Excluding(ex => ex.StackTrace) + .Excluding(ex => ex.Source) + .Excluding(ex => ex.InnerException.TargetSite) + .Excluding(ex => ex.InnerException.StackTrace) + .Excluding(ex => ex.InnerException.Source)); + + this.sqliteErrorBrokerMock.Verify(broker => broker + .GetErrorCode(duplicateKeySqliteExceptionThrown), Times.Once()); + + sqliteErrorBrokerMock.VerifyNoOtherCalls(); } } } diff --git a/STX.EFxceptions.SQLite.Base.Tests.Unit/Services/Foundations/SQLiteEFxceptionServiceTests.Validations.cs b/STX.EFxceptions.SQLite.Base.Tests.Unit/Services/Foundations/SQLiteEFxceptionServiceTests.Validations.cs index ffb2d7f..659f15e 100644 --- a/STX.EFxceptions.SQLite.Base.Tests.Unit/Services/Foundations/SQLiteEFxceptionServiceTests.Validations.cs +++ b/STX.EFxceptions.SQLite.Base.Tests.Unit/Services/Foundations/SQLiteEFxceptionServiceTests.Validations.cs @@ -2,10 +2,11 @@ // Copyright(c) The Standard Organization: A coalition of the Good-Hearted Engineers // ---------------------------------------------------------------------------------- -using System; +using FluentAssertions; using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; using Moq; +using System; using Xunit; namespace STX.EFxceptions.SQLite.Base.Tests.Unit.Services.Foundations @@ -16,16 +17,31 @@ public partial class SQLiteEFxceptionServiceTests public void ShouldThrowDbUpdateExceptionIfSQLiteExceptionsIsNull() { // given - DbUpdateException dbUpdateException = - new DbUpdateException(null, default(Exception)); - - // when - Assert.Throws(() => this.sqliteEFxceptionService - .ThrowMeaningfulException(dbUpdateException)); + var dbUpdateException = new DbUpdateException(null, default(Exception)); + DbUpdateException expectedDbUpdateException = dbUpdateException; + // when + DbUpdateException actualDbUpdateException = + Assert.Throws(() => + this.sqliteEFxceptionService + .ThrowMeaningfulException(dbUpdateException)); // then + actualDbUpdateException.Should() + .BeEquivalentTo( + expectation: expectedDbUpdateException, + config: options => options + .Excluding(ex => ex.TargetSite) + .Excluding(ex => ex.StackTrace) + .Excluding(ex => ex.Source) + .Excluding(ex => ex.InnerException.TargetSite) + .Excluding(ex => ex.InnerException.StackTrace) + .Excluding(ex => ex.InnerException.Source)); + this.sqliteErrorBrokerMock.Verify(broker => - broker.GetErrorCode(It.IsAny()), Times.Never); + broker.GetErrorCode(It.IsAny()), + Times.Never); + + this.sqliteErrorBrokerMock.VerifyNoOtherCalls(); } } } diff --git a/STX.EFxceptions.SQLite.Base.Tests.Unit/Services/Foundations/SQLiteEFxceptionServiceTests.cs b/STX.EFxceptions.SQLite.Base.Tests.Unit/Services/Foundations/SQLiteEFxceptionServiceTests.cs index fb79b09..5ca8d1c 100644 --- a/STX.EFxceptions.SQLite.Base.Tests.Unit/Services/Foundations/SQLiteEFxceptionServiceTests.cs +++ b/STX.EFxceptions.SQLite.Base.Tests.Unit/Services/Foundations/SQLiteEFxceptionServiceTests.cs @@ -2,6 +2,7 @@ // Copyright(c) The Standard Organization: A coalition of the Good-Hearted Engineers // ---------------------------------------------------------------------------------- +using System.Reflection; using System.Runtime.Serialization; using Microsoft.Data.Sqlite; using Moq; @@ -26,7 +27,26 @@ public SQLiteEFxceptionServiceTests() private string CreateRandomErrorMessage() => new MnemonicString().GetValue(); - private SqliteException CreateSQLiteException() => - FormatterServices.GetSafeUninitializedObject(typeof(SqliteException)) as SqliteException; + private SqliteException CreateSQLiteException(string message, int errorCode) + { + SqliteException sqliteException = + (SqliteException)FormatterServices.GetUninitializedObject(typeof(SqliteException)); + + FieldInfo messageField = typeof(SqliteException).GetField( + name: "_message", + bindingAttr: BindingFlags.Instance | BindingFlags.NonPublic); + + if (messageField != null) + messageField.SetValue(sqliteException, message); + + FieldInfo errorCodeField = typeof(SqliteException).GetField( + name: "_errorCode", + bindingAttr: BindingFlags.Instance | BindingFlags.NonPublic); + + if (errorCodeField != null) + errorCodeField.SetValue(sqliteException, errorCode); + + return sqliteException; + } } } diff --git a/STX.EFxceptions.SQLite.Base/Services/Foundations/SQLiteEFxceptionService.Exceptions.cs b/STX.EFxceptions.SQLite.Base/Services/Foundations/SQLiteEFxceptionService.Exceptions.cs index 320f22f..66958d2 100644 --- a/STX.EFxceptions.SQLite.Base/Services/Foundations/SQLiteEFxceptionService.Exceptions.cs +++ b/STX.EFxceptions.SQLite.Base/Services/Foundations/SQLiteEFxceptionService.Exceptions.cs @@ -1,12 +1,58 @@ -using STX.EFxceptions.SQLite.Base.Models.Exceptions; +using Microsoft.EntityFrameworkCore; +using STX.EFxceptions.Abstractions.Models.Exceptions; +using STX.EFxceptions.SQLite.Base.Models.Exceptions; namespace STX.EFxceptions.SQLite.Base.Services.Foundations { public partial class SQLiteEFxceptionService { - private void ConvertAndThrowMeaningfulException(int sqliteErrorCode, string message) + public delegate void ReturningExceptionFunction(); + + public void TryCatch(ReturningExceptionFunction returningExceptionFunction) + { + try + { + returningExceptionFunction(); + } + catch (InvalidColumnNameSQLiteException ivalidColumnNameSQLiteException) + { + throw new InvalidColumnNameException( + message: ivalidColumnNameSQLiteException.Message, + innerException: ivalidColumnNameSQLiteException); + } + catch (InvalidObjectNameSQLiteException invalidObjectNameSQLiteException) + { + throw new InvalidObjectNameException( + message: invalidObjectNameSQLiteException.Message, + innerException: invalidObjectNameSQLiteException); + } + catch (ForeignKeyConstraintConflictSQLiteException foreignKeyConstraintConflictSQLiteException) + { + throw new ForeignKeyConstraintConflictException( + message: foreignKeyConstraintConflictSQLiteException.Message, + innerException: foreignKeyConstraintConflictSQLiteException); + } + catch (DuplicateKeyWithUniqueIndexSQLiteException duplicateKeyWithUniqueIndexSQLiteException) + { + throw new DuplicateKeyWithUniqueIndexException( + message: duplicateKeyWithUniqueIndexSQLiteException.Message, + innerException: duplicateKeyWithUniqueIndexSQLiteException); + } + catch (DuplicateKeySQLiteException duplicateKeySQLiteException) + { + throw new DuplicateKeyException( + message: duplicateKeySQLiteException.Message, + innerException: duplicateKeySQLiteException); + } + catch (DbUpdateException) + { + throw; + } + } + + private void ConvertAndThrowMeaningfulException(int sqlErrorCode, string message) { - switch (sqliteErrorCode) + switch (sqlErrorCode) { case 207: throw new InvalidColumnNameSQLiteException(message); diff --git a/STX.EFxceptions.SQLite.Base/Services/Foundations/SQLiteEFxceptionService.cs b/STX.EFxceptions.SQLite.Base/Services/Foundations/SQLiteEFxceptionService.cs index 734e9ae..2b1d28a 100644 --- a/STX.EFxceptions.SQLite.Base/Services/Foundations/SQLiteEFxceptionService.cs +++ b/STX.EFxceptions.SQLite.Base/Services/Foundations/SQLiteEFxceptionService.cs @@ -16,14 +16,15 @@ public partial class SQLiteEFxceptionService : ISQLiteEFxceptionService public SQLiteEFxceptionService(IDbErrorBroker sqliteErrorBroker) => this.sqliteErrorBroker = sqliteErrorBroker; - public void ThrowMeaningfulException(DbUpdateException dbUpdateException) + public void ThrowMeaningfulException(DbUpdateException dbUpdateException) => + TryCatch(() => { ValidateInnerException(dbUpdateException); SqliteException sqliteException = GetSqliteException(dbUpdateException); int sqliteErrorCode = this.sqliteErrorBroker.GetErrorCode(sqliteException); ConvertAndThrowMeaningfulException(sqliteErrorCode, sqliteException.Message); throw dbUpdateException; - } + }); private SqliteException GetSqliteException(Exception exception) => (SqliteException)exception.InnerException;