From e0df6caec6ef78b5acbf4521b3717f10a45a6a64 Mon Sep 17 00:00:00 2001 From: David Berry Date: Fri, 10 Oct 2025 22:26:41 -0500 Subject: [PATCH 1/7] Added unit tests for framework project to improve code coverage --- .../Data/IDataReaderExtensionsTests.cs | 450 +++++++++++++++++- .../DavidBerry.Framework.Tests.csproj | 11 + .../EmbeddedResources/ExampleFile.txt | 1 + .../EmbeddedResources/dotnet-logo.png | Bin 0 -> 2140 bytes .../Util/ChecksumExtensionsTests.cs | 127 +++++ .../Util/EmbeddedResourceUtilTests.cs | 56 +++ .../Util/StringExtensionsTests.cs | 43 ++ .../Util/ChecksumAlgorithm.cs | 20 + .../Util/ChecksumExtensions.cs | 66 +++ .../Util/EmbeddedResourceUtil.cs | 19 +- 10 files changed, 786 insertions(+), 7 deletions(-) create mode 100644 DavidBerry.Framework/DavidBerry.Framework.Tests/EmbeddedResources/ExampleFile.txt create mode 100644 DavidBerry.Framework/DavidBerry.Framework.Tests/EmbeddedResources/dotnet-logo.png create mode 100644 DavidBerry.Framework/DavidBerry.Framework.Tests/Util/ChecksumExtensionsTests.cs create mode 100644 DavidBerry.Framework/DavidBerry.Framework.Tests/Util/EmbeddedResourceUtilTests.cs create mode 100644 DavidBerry.Framework/DavidBerry.Framework/Util/ChecksumAlgorithm.cs create mode 100644 DavidBerry.Framework/DavidBerry.Framework/Util/ChecksumExtensions.cs diff --git a/DavidBerry.Framework/DavidBerry.Framework.Tests/Data/IDataReaderExtensionsTests.cs b/DavidBerry.Framework/DavidBerry.Framework.Tests/Data/IDataReaderExtensionsTests.cs index 1463d39..03ba7ff 100644 --- a/DavidBerry.Framework/DavidBerry.Framework.Tests/Data/IDataReaderExtensionsTests.cs +++ b/DavidBerry.Framework/DavidBerry.Framework.Tests/Data/IDataReaderExtensionsTests.cs @@ -16,6 +16,30 @@ namespace DavidBerry.Framework.Tests.Data public class IDataReaderExtensionsTests { + #region GetFieldType + + [Fact] + public void GetFieldTypeBy_CallsGetFieldType_WithCorrectOrdinalColumn() + { + // Data + string columnName = "IsActive"; + int ordinalPosition = 10; + + // Arrange + Mock mockDataReader = new Mock(); + mockDataReader.Setup(x => x.GetOrdinal(columnName)).Returns(ordinalPosition); + mockDataReader.Setup(x => x.GetFieldType(ordinalPosition)).Returns(typeof(string)); + + // Act + var result = mockDataReader.Object.GetFieldType(columnName); + + // Assert + mockDataReader.Verify(x => x.GetFieldType(ordinalPosition), Times.Once); + } + + #endregion + + #region GetBoolean [Theory()] @@ -227,6 +251,216 @@ public void GetBooleanWithDefaultByName_ReturnsDefault_WhenValueIsNull(bool defa #endregion + #region GetChar + + [Theory()] + [InlineData('A')] + [InlineData('B')] + public void GetCharByName_WorksAsExpected_WhenPassingColumnName(char value) + { + // Data + string columnName = "IsActive"; + int ordinalPosition = 10; + + // Arrange + Mock mockDataReader = new Mock(); + mockDataReader.Setup(x => x.GetOrdinal(columnName)).Returns(ordinalPosition); + mockDataReader.Setup(x => x.GetChar(ordinalPosition)).Returns(value); + + // Act + var result = mockDataReader.Object.GetChar(columnName); + + // Assert + mockDataReader.Verify(x => x.GetChar(ordinalPosition), Times.Once); + result.ShouldBe(value); + } + + + [Theory()] + [InlineData('A')] + [InlineData('B')] + public void GetCharNullable_ReturnsValue_WhenValueNotNull(char value) + { + // Data + int ordinalPosition = 10; + + // Arrange + Mock mockDataReader = new Mock(); + mockDataReader.Setup(x => x.IsDBNull(ordinalPosition)).Returns(false); + mockDataReader.Setup(x => x.GetChar(ordinalPosition)).Returns(value); + + // Act + var result = mockDataReader.Object.GetCharNullable(ordinalPosition); + + // Assert + mockDataReader.Verify(x => x.GetChar(ordinalPosition), Times.Once); + result.ShouldBe(value); + } + + + [Fact] + public void GetCharNullable_ReturnsNull_WhenValueIsNull() + { + // Data + int ordinalPosition = 10; + + // Arrange + Mock mockDataReader = new Mock(); + mockDataReader.Setup(x => x.IsDBNull(ordinalPosition)).Returns(true); + + // Act + var result = mockDataReader.Object.GetCharNullable(ordinalPosition); + + // Assert + mockDataReader.Verify(x => x.GetChar(ordinalPosition), Times.Never); + result.ShouldBeNull(); + } + + + + [Theory()] + [InlineData('A')] + [InlineData('B')] + public void GetCharNullableByName_ReturnsValue_WhenValueIsNotNull(char value) + { + // Data + string columnName = "IsActive"; + int ordinalPosition = 10; + + // Arrange + Mock mockDataReader = new Mock(); + mockDataReader.Setup(x => x.IsDBNull(ordinalPosition)).Returns(false); + mockDataReader.Setup(x => x.GetOrdinal(columnName)).Returns(ordinalPosition); + mockDataReader.Setup(x => x.GetChar(ordinalPosition)).Returns(value); + + // Act + var result = mockDataReader.Object.GetCharNullable(columnName); + + // Assert + mockDataReader.Verify(x => x.GetChar(ordinalPosition), Times.Once); + result.ShouldBe(value); + } + + + [Fact()] + public void GetCharNullableByName_ReturnsNull_WhenValueIsNull() + { + // Data + string columnName = "IsActive"; + int ordinalPosition = 10; + + // Arrange + Mock mockDataReader = new Mock(); + mockDataReader.Setup(x => x.IsDBNull(ordinalPosition)).Returns(true); + mockDataReader.Setup(x => x.GetOrdinal(columnName)).Returns(ordinalPosition); + + // Act + var result = mockDataReader.Object.GetCharNullable(columnName); + + // Assert + mockDataReader.Verify(x => x.GetChar(ordinalPosition), Times.Never); + result.ShouldBeNull(); + } + + + [Theory()] + [InlineData('A', 'X')] + [InlineData('B', 'Z')] + public void GetCharWithDefault_ReturnsValue_WhenValueIsNotNull(char databaseValue, char defaultValue) + { + // Data + int ordinalPosition = 10; + + // Arrange + Mock mockDataReader = new Mock(); + mockDataReader.Setup(x => x.IsDBNull(ordinalPosition)).Returns(false); + mockDataReader.Setup(x => x.GetChar(ordinalPosition)).Returns(databaseValue); + + // Act + var result = mockDataReader.Object.GetCharWithDefault(ordinalPosition, defaultValue); + + // Assert + mockDataReader.Verify(x => x.GetChar(ordinalPosition), Times.Once); + result.ShouldBe(databaseValue); + result.ShouldNotBe(defaultValue); + } + + + [Theory()] + [InlineData('A')] + [InlineData('B')] + public void GetCharWithDefault_ReturnsDefault_WhenValueIsNull(char defaultValue) + { + // Data + int ordinalPosition = 10; + + + // Arrange + Mock mockDataReader = new Mock(); + mockDataReader.Setup(x => x.IsDBNull(ordinalPosition)).Returns(true); + + // Act + var result = mockDataReader.Object.GetCharWithDefault(ordinalPosition, defaultValue); + + // Assert + mockDataReader.Verify(x => x.GetChar(ordinalPosition), Times.Never); + result.ShouldBe(defaultValue); + } + + + [Theory()] + [InlineData('A', 'X')] + [InlineData('B', 'Y')] + public void GetCharWithDefaultByName_ReturnsValue_WhenValueIsNotNull(char databaseValue, char defaultValue) + { + // Data + string columnName = "IsActive"; + int ordinalPosition = 10; + + // Arrange + Mock mockDataReader = new Mock(); + mockDataReader.Setup(x => x.IsDBNull(ordinalPosition)).Returns(false); + mockDataReader.Setup(x => x.GetChar(ordinalPosition)).Returns(databaseValue); + mockDataReader.Setup(x => x.GetOrdinal(columnName)).Returns(ordinalPosition); + + + // Act + var result = mockDataReader.Object.GetCharWithDefault(columnName, defaultValue); + + // Assert + mockDataReader.Verify(x => x.GetChar(ordinalPosition), Times.Once); + result.ShouldBe(databaseValue); + result.ShouldNotBe(defaultValue); + } + + + + [Theory()] + [InlineData('A')] + [InlineData('B')] + public void GetCharWithDefaultByName_ReturnsDefault_WhenValueIsNull(char defaultValue) + { + // Data + string columnName = "IsActive"; + int ordinalPosition = 10; + + // Arrange + Mock mockDataReader = new Mock(); + mockDataReader.Setup(x => x.IsDBNull(ordinalPosition)).Returns(true); + mockDataReader.Setup(x => x.GetOrdinal(columnName)).Returns(ordinalPosition); + + + // Act + var result = mockDataReader.Object.GetCharWithDefault(columnName, defaultValue); + + // Assert + mockDataReader.Verify(x => x.GetChar(ordinalPosition), Times.Never); + result.ShouldBe(defaultValue); + } + + + + #endregion #region Int16 @@ -295,7 +529,6 @@ public void GetInt16Nullable_ReturnsNull_WhenValueIsNull() } - [Theory()] [InlineData(3782)] [InlineData(-292)] @@ -649,8 +882,6 @@ public void GetInt32WithDefaultByName_ReturnsDefault_WhenValueIsNull(int default #endregion - - #region Int64 [Theory()] @@ -861,8 +1092,6 @@ public void GetInt64WithDefaultByName_ReturnsDefault_WhenValueIsNull(long defaul #endregion - - #region Float [Theory()] @@ -1283,6 +1512,216 @@ public void GetDoubleWithDefaultByName_ReturnsDefault_WhenValueIsNull(double def #endregion + #region Decimal + + [Theory()] + [InlineData(32.00)] + [InlineData(-46.50)] + public void GetDecimalByName_WorksAsExpected_WhenPassingColumnName(decimal value) + { + // Data + string columnName = "ColumnName"; + int ordinalPosition = 10; + + // Arrange + Mock mockDataReader = new Mock(); + mockDataReader.Setup(x => x.GetOrdinal(columnName)).Returns(ordinalPosition); + mockDataReader.Setup(x => x.GetDecimal(ordinalPosition)).Returns(value); + + // Act + var result = mockDataReader.Object.GetDecimal(columnName); + + // Assert + mockDataReader.Verify(x => x.GetDecimal(ordinalPosition), Times.Once); + result.ShouldBe(value); + } + + + [Theory()] + [InlineData(342.00)] + [InlineData(-292.25)] + public void GetDecimalNullable_ReturnsValue_WhenValueNotNull(decimal value) + { + // Data + int ordinalPosition = 10; + + // Arrange + Mock mockDataReader = new Mock(); + mockDataReader.Setup(x => x.IsDBNull(ordinalPosition)).Returns(false); + mockDataReader.Setup(x => x.GetDecimal(ordinalPosition)).Returns(value); + + // Act + var result = mockDataReader.Object.GetDecimalNullable(ordinalPosition); + + // Assert + mockDataReader.Verify(x => x.GetDecimal(ordinalPosition), Times.Once); + result.ShouldBe(value); + } + + + [Fact] + public void GetDecimalNullable_ReturnsNull_WhenValueIsNull() + { + // Data + int ordinalPosition = 10; + + // Arrange + Mock mockDataReader = new Mock(); + mockDataReader.Setup(x => x.IsDBNull(ordinalPosition)).Returns(true); + + // Act + var result = mockDataReader.Object.GetDecimalNullable(ordinalPosition); + + // Assert + mockDataReader.Verify(x => x.GetDecimal(ordinalPosition), Times.Never); + result.ShouldBeNull(); + } + + + + [Theory()] + [InlineData(3782.00)] + [InlineData(-292.75)] + public void GetDecimalNullableByName_ReturnsValue_WhenValueIsNotNull(decimal value) + { + // Data + string columnName = "ColumnName"; + int ordinalPosition = 10; + + // Arrange + Mock mockDataReader = new Mock(); + mockDataReader.Setup(x => x.IsDBNull(ordinalPosition)).Returns(false); + mockDataReader.Setup(x => x.GetOrdinal(columnName)).Returns(ordinalPosition); + mockDataReader.Setup(x => x.GetDecimal(ordinalPosition)).Returns(value); + + // Act + var result = mockDataReader.Object.GetDecimalNullable(columnName); + + // Assert + mockDataReader.Verify(x => x.GetDecimal(ordinalPosition), Times.Once); + result.ShouldBe(value); + } + + + [Fact()] + public void GetDecimalNullableByName_ReturnsNull_WhenValueIsNull() + { + // Data + string columnName = "IsActive"; + int ordinalPosition = 10; + + // Arrange + Mock mockDataReader = new Mock(); + mockDataReader.Setup(x => x.IsDBNull(ordinalPosition)).Returns(true); + mockDataReader.Setup(x => x.GetOrdinal(columnName)).Returns(ordinalPosition); + + // Act + var result = mockDataReader.Object.GetDecimalNullable(columnName); + + // Assert + mockDataReader.Verify(x => x.GetDecimal(ordinalPosition), Times.Never); + result.ShouldBeNull(); + } + + + [Theory()] + [InlineData(324.00, 0.00)] + [InlineData(0.50, 100.50)] + public void GetDecimalWithDefault_ReturnsValue_WhenValueIsNotNull(decimal databaseValue, decimal defaultValue) + { + // Data + int ordinalPosition = 10; + + // Arrange + Mock mockDataReader = new Mock(); + mockDataReader.Setup(x => x.IsDBNull(ordinalPosition)).Returns(false); + mockDataReader.Setup(x => x.GetDecimal(ordinalPosition)).Returns(databaseValue); + + // Act + var result = mockDataReader.Object.GetDecimalWithDefault(ordinalPosition, defaultValue); + + // Assert + mockDataReader.Verify(x => x.GetDecimal(ordinalPosition), Times.Once); + result.ShouldBe(databaseValue); + result.ShouldNotBe(defaultValue); + } + + + [Theory()] + [InlineData(0.00)] + [InlineData(100.550)] + public void GetDecimalWithDefault_ReturnsDefault_WhenValueIsNull(decimal defaultValue) + { + // Data + int ordinalPosition = 10; + + + // Arrange + Mock mockDataReader = new Mock(); + mockDataReader.Setup(x => x.IsDBNull(ordinalPosition)).Returns(true); + + // Act + var result = mockDataReader.Object.GetDecimalWithDefault(ordinalPosition, defaultValue); + + // Assert + mockDataReader.Verify(x => x.GetDecimal(ordinalPosition), Times.Never); + result.ShouldBe(defaultValue); + } + + + [Theory()] + [InlineData(100.00, 0.00)] + [InlineData(0.125, 10.525)] + public void GetDecimalWithDefaultByName_ReturnsValue_WhenValueIsNotNull(decimal databaseValue, decimal defaultValue) + { + // Data + string columnName = "IsActive"; + int ordinalPosition = 10; + + // Arrange + Mock mockDataReader = new Mock(); + mockDataReader.Setup(x => x.IsDBNull(ordinalPosition)).Returns(false); + mockDataReader.Setup(x => x.GetDecimal(ordinalPosition)).Returns(databaseValue); + mockDataReader.Setup(x => x.GetOrdinal(columnName)).Returns(ordinalPosition); + + + // Act + var result = mockDataReader.Object.GetDecimalWithDefault(columnName, defaultValue); + + // Assert + mockDataReader.Verify(x => x.GetDecimal(ordinalPosition), Times.Once); + result.ShouldBe(databaseValue); + result.ShouldNotBe(defaultValue); + } + + + + [Theory()] + [InlineData(0.00)] + [InlineData(100.50)] + public void GetDecimalWithDefaultByName_ReturnsDefault_WhenValueIsNull(decimal defaultValue) + { + // Data + string columnName = "IsActive"; + int ordinalPosition = 10; + + // Arrange + Mock mockDataReader = new Mock(); + mockDataReader.Setup(x => x.IsDBNull(ordinalPosition)).Returns(true); + mockDataReader.Setup(x => x.GetOrdinal(columnName)).Returns(ordinalPosition); + + + // Act + var result = mockDataReader.Object.GetDecimalWithDefault(columnName, defaultValue); + + // Assert + mockDataReader.Verify(x => x.GetDecimal(ordinalPosition), Times.Never); + result.ShouldBe(defaultValue); + } + + + #endregion + #region String @@ -1494,7 +1933,6 @@ public void GetStringWithDefaultByName_ReturnsDefault_WhenValueIsNull(string def #endregion - #region DateTime diff --git a/DavidBerry.Framework/DavidBerry.Framework.Tests/DavidBerry.Framework.Tests.csproj b/DavidBerry.Framework/DavidBerry.Framework.Tests/DavidBerry.Framework.Tests.csproj index cf1b196..f7fd415 100644 --- a/DavidBerry.Framework/DavidBerry.Framework.Tests/DavidBerry.Framework.Tests.csproj +++ b/DavidBerry.Framework/DavidBerry.Framework.Tests/DavidBerry.Framework.Tests.csproj @@ -14,6 +14,8 @@ + + @@ -34,6 +36,15 @@ + + + + + PreserveNewest + + + PreserveNewest + diff --git a/DavidBerry.Framework/DavidBerry.Framework.Tests/EmbeddedResources/ExampleFile.txt b/DavidBerry.Framework/DavidBerry.Framework.Tests/EmbeddedResources/ExampleFile.txt new file mode 100644 index 0000000..31d27f4 --- /dev/null +++ b/DavidBerry.Framework/DavidBerry.Framework.Tests/EmbeddedResources/ExampleFile.txt @@ -0,0 +1 @@ +This is a test embedded resource file. \ No newline at end of file diff --git a/DavidBerry.Framework/DavidBerry.Framework.Tests/EmbeddedResources/dotnet-logo.png b/DavidBerry.Framework/DavidBerry.Framework.Tests/EmbeddedResources/dotnet-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..fb00ecf91e4b78804c636194bb323bf3710fa1c6 GIT binary patch literal 2140 zcmeHIX;4#F6iz}QBp3(*v25~!F*qzP0ojoU1Wja7KpD}12oa>Xrfece2to*M4{<@1 zP%%ms&>BGn2e5_86S0m&0z?*tFd77$B7y>vzNa&tPJedV>7UNLH{U(qJ@=gNJNL&G zZwy{XCYg~i7z~-iW`$xfSQ!0vwGifWRzWuc0UHB1`G?p&M?Q^4^TQdnylqlFJQMHV zlMy}8cyoMm;xpH^t5o#5@y2-k+Mdkle$k#mS^4OrhKW<@s@vsbjW^%%!+QI>rn#<) z{_bgV=B_HFEH)`LIBec*h>(fF5SlnFpG|4X(PvmP2D6~~`@Sai?5+nm4-!M>!#e`& z78+VFVXe(SMlq!^eg7xWd6boUvM4P&8=5T0wgi! zp<$6Qus?iG)+d;(0n8C!5))oCLL24Oym*Gn(Wz_qt3FCV776EaQ0@8?Z;*X?j^|Sm zn!Z?CE$ZK^Bel^@7%D^$=pOWDB0`L52FJhnR;@@*4VX&YV4t-$6D&!6S(50;t4kpO zSu@uc7lG;JP{4;;gTKszM|=8RA1W@lCalj952n{cQ<2H{5Ho%XwCWD_{Z4eMJK>$z zk!wqDgEouoAc>PS^6@?wtqyq}w+)qQ-k}&oy@>2Jq0>$(&(2yz!kQ$B-aeP1JVAwi zLdDN$6HyHca*;h+GLwCO>;wmB&|!(*}Njk+((AJ3{PGOdx_ow6Z(=O$zl0DfhA9qWN%*0384ZX-%PdlCVsU;^WuLWS6 zFXEf8|L9P?|BxkhKeqYq++65zuqrIXB_=k7X0>One(W+8@jAr|KOqE zA{8rpb%xTK*LyH?wH7r3VwmvsXBR83fP-{+TLwEzY04n+9E2=$_+^Svj1xydpdM_| zOAWL@lsIQ|g;s%d*cN6$i7RV!TL3t`Nippk%z?;2>&ui}v9{Qry++zPRCad)IAFrl zVrN|@85!_xg;Kwn%BW~--xx!>Wk=-d&Bet~zEI_-Jkz#l_a=BNo+EN{T-sGgLK>2R zTl1?679M`Te?VAqflxs-Xlp(?+z^mfcBjCASoPc0Eo+$1Ukv);wnZsCeH&`cr6V+- zlv98Q2P&n*!Bn0NQC5WS;Rxq;oYdzbZwp4})3$-jUb&QY)+bmNVpr+``XIZdFn@{R z-y&lERCUNMmld3Uk>W<<`>Kw>#6lx$oBxuCIFtk;kF{VWJb!KMr(kW0zjXo2SiFoH kLN8~t3iGWE|8=58=$f)MBl>~e*<^I~9RFa}4c} + /// Calculate the chacksum for the specified data using the specified algorithm + /// + /// A byte array of the data to calculate the checksum for + /// The Chcecksum Algorithm to use + /// + /// + public static byte[] GetChecksum(this byte[] data, ChecksumAlgorithm algorithm) + { + ArgumentNullException.ThrowIfNull(data); + return algorithm switch + { + ChecksumAlgorithm.MD5 => System.Security.Cryptography.MD5.HashData(data), + ChecksumAlgorithm.SHA1 => System.Security.Cryptography.SHA1.HashData(data), + ChecksumAlgorithm.SHA256 => System.Security.Cryptography.SHA256.HashData(data), + ChecksumAlgorithm.SHA384 => System.Security.Cryptography.SHA384.HashData(data), + ChecksumAlgorithm.SHA512 => System.Security.Cryptography.SHA512.HashData(data), + ChecksumAlgorithm.SHA3_256 => System.Security.Cryptography.SHA3_256.HashData(data), + ChecksumAlgorithm.SHA3_384 => System.Security.Cryptography.SHA3_384.HashData(data), + ChecksumAlgorithm.SHA3_512 => System.Security.Cryptography.SHA3_512.HashData(data), + _ => throw new NotSupportedException($"The specified checksum algorithm '{algorithm}' is not supported."), + }; + } + + + public static byte[] GetChecksum(this string data, ChecksumAlgorithm algorithm) + { + ArgumentNullException.ThrowIfNull(data); + return GetChecksum(Encoding.UTF8.GetBytes(data), algorithm); + } + + + public static byte[] GetChecksum(this Stream stream, ChecksumAlgorithm algorithm) + { + using (MemoryStream memoryStream = new MemoryStream()) + { + // Copy the data from the source stream to the memory stream + stream.CopyTo(memoryStream); + + // Convert the memory stream to a byte array + byte[] byteArray = memoryStream.ToArray(); + + return GetChecksum(byteArray, algorithm); + } + } + + public static byte[] GetChecksum(this FileInfo file, ChecksumAlgorithm algorithm) + { + return File.ReadAllBytes(file.FullName).GetChecksum(algorithm); + } + + } +} diff --git a/DavidBerry.Framework/DavidBerry.Framework/Util/EmbeddedResourceUtil.cs b/DavidBerry.Framework/DavidBerry.Framework/Util/EmbeddedResourceUtil.cs index 5a592c8..9a65123 100644 --- a/DavidBerry.Framework/DavidBerry.Framework/Util/EmbeddedResourceUtil.cs +++ b/DavidBerry.Framework/DavidBerry.Framework/Util/EmbeddedResourceUtil.cs @@ -9,7 +9,12 @@ namespace DavidBerry.Framework.Util public static class EmbeddedResourceUtil { - + /// + /// Reads an embedded resource text file from the specified assembly and returns its contents as a string + /// + /// + /// + /// public static string ReadEmbeddedResourceTextFile(this Assembly assembly, string filename) { var resourceName = $"{assembly.GetName().Name}.{filename}"; @@ -23,6 +28,18 @@ public static string ReadEmbeddedResourceTextFile(this Assembly assembly, string + public static byte[] ReadEmbeddedResourceBinaryFile(this Assembly assembly, string filename) + { + var resourceName = $"{assembly.GetName().Name}.{filename}"; + + using (BinaryReader reader = new BinaryReader(assembly.GetManifestResourceStream(resourceName))) + { + byte[] bytes = reader.ReadBytes((int)reader.BaseStream.Length); + return bytes; + } + } + + } From 02ca1b33dfeacd079cfa0e60b1342e75b61a4c24 Mon Sep 17 00:00:00 2001 From: David Berry Date: Fri, 10 Oct 2025 23:16:24 -0500 Subject: [PATCH 2/7] Added tests for a couple validation regexes --- .../Util/ChecksumExtensionsTests.cs | 74 +++++++ .../Util/CommonValidationsTests.cs | 191 ++++++++++++++++++ 2 files changed, 265 insertions(+) create mode 100644 DavidBerry.Framework/DavidBerry.Framework.Tests/Util/CommonValidationsTests.cs diff --git a/DavidBerry.Framework/DavidBerry.Framework.Tests/Util/ChecksumExtensionsTests.cs b/DavidBerry.Framework/DavidBerry.Framework.Tests/Util/ChecksumExtensionsTests.cs index d2911fb..ce94dbb 100644 --- a/DavidBerry.Framework/DavidBerry.Framework.Tests/Util/ChecksumExtensionsTests.cs +++ b/DavidBerry.Framework/DavidBerry.Framework.Tests/Util/ChecksumExtensionsTests.cs @@ -123,5 +123,79 @@ public void GetChecksum_FromByteArray_ReturnsCorrectChecksum_ForSha3_512() checksum.ToHexadecimalString().ShouldBe("B8841178726F493E85FD41ED891CEC32B57781E707162ABF954C0D5E1C713262B84B37C68229017C698344CD8C7E0B4E7C1496DF7C303CFEA9F201A4DE9D12B2"); } + + + [Fact] + public void GetChecksum_FromString_ReturnsCorrectChecksum_ForMd5() + { + // Arrange + string data = "Chicago Illinois"; + + // Act + var checksum = data.GetChecksum(ChecksumAlgorithm.MD5); + + // Assert + checksum.ToHexadecimalString().ShouldBe("EBBD525CE0186C584C244F00E9B6688E"); + } + + + + + [Fact] + public void GetChecksum_FromString_ReturnsCorrectChecksum_ForSha1() + { + // Arrange + string data = "Chicago Illinois"; + + // Act + var checksum = data.GetChecksum(ChecksumAlgorithm.SHA1); + + // Assert + checksum.ToHexadecimalString().ShouldBe("5569151800725DBB8C7A7CA8205A307E84BD2F47"); + } + + + + [Fact] + public void GetChecksum_FromString_ReturnsCorrectChecksum_ForSha256() + { + // Arrange + string data = "Chicago Illinois"; + + // Act + var checksum = data.GetChecksum(ChecksumAlgorithm.SHA256); + + // Assert + checksum.ToHexadecimalString().ShouldBe("7B9CE053B82E7C40721F62AAE31D2A96E3B671B989140B0467585DBD82E41F61"); + } + + + [Fact] + public void GetChecksum_FromString_ReturnsCorrectChecksum_ForSha384() + { + // Arrange + string data = "Chicago Illinois"; + + // Act + var checksum = data.GetChecksum(ChecksumAlgorithm.SHA384); + + // Assert + checksum.ToHexadecimalString().ShouldBe("40EF0273A8A18794FA1072211A5AA2DE1B256CE1EAC645BB642D8549C45F66F658FE924710D2651917A41626F1722889"); + } + + [Fact] + public void GetChecksum_FromString_ReturnsCorrectChecksum_ForSha512() + { + // Arrange + string data = "Chicago Illinois"; + + // Act + var checksum = data.GetChecksum(ChecksumAlgorithm.SHA512); + + // Assert + checksum.ToHexadecimalString().ShouldBe("9C23ED95E64268269D2ABD8E3E5E4B9F1601CF3ABF0CE1679583D19434A3C4DAA3A55A1488BB681DA844CC46BC0CFB7A0DBA8D8DF8BC84812854EFE79BA4933A"); + } + + } } diff --git a/DavidBerry.Framework/DavidBerry.Framework.Tests/Util/CommonValidationsTests.cs b/DavidBerry.Framework/DavidBerry.Framework.Tests/Util/CommonValidationsTests.cs new file mode 100644 index 0000000..f951adb --- /dev/null +++ b/DavidBerry.Framework/DavidBerry.Framework.Tests/Util/CommonValidationsTests.cs @@ -0,0 +1,191 @@ +using DavidBerry.Framework.Util; +using Shouldly; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using Xunit; + +namespace DavidBerry.Framework.Tests.Util; + +public class CommonValidationsTests +{ + [Theory] + [InlineData("AL")] + [InlineData("AK")] + [InlineData("AZ")] + [InlineData("AR")] + [InlineData("CA")] + [InlineData("CO")] + [InlineData("CT")] + [InlineData("DE")] + [InlineData("FL")] + [InlineData("GA")] + [InlineData("HI")] + [InlineData("ID")] + [InlineData("IL")] + [InlineData("IN")] + [InlineData("IA")] + [InlineData("KS")] + [InlineData("KY")] + [InlineData("LA")] + [InlineData("ME")] + [InlineData("MD")] + [InlineData("MA")] + [InlineData("MI")] + [InlineData("MN")] + [InlineData("MS")] + [InlineData("MO")] + [InlineData("MT")] + [InlineData("NE")] + [InlineData("NV")] + [InlineData("NH")] + [InlineData("NJ")] + [InlineData("NM")] + [InlineData("NY")] + [InlineData("NC")] + [InlineData("ND")] + [InlineData("OH")] + [InlineData("OK")] + [InlineData("OR")] + [InlineData("PA")] + [InlineData("RI")] + [InlineData("SC")] + [InlineData("SD")] + [InlineData("TN")] + [InlineData("TX")] + [InlineData("UT")] + [InlineData("VT")] + [InlineData("VA")] + [InlineData("WA")] + [InlineData("WV")] + [InlineData("WI")] + [InlineData("WY")] + public void US_States_Validation_WorksForAllStateAbbreviations(string state) + { + // Act + var result = Regex.IsMatch(state, DavidBerry.Framework.Util.CommonValidations.US_STATES); + + // Assert + result.ShouldBeTrue(); + } + + + + [Theory] + [InlineData("al")] + [InlineData("ak")] + [InlineData("az")] + [InlineData("ar")] + [InlineData("ca")] + [InlineData("co")] + [InlineData("ct")] + [InlineData("de")] + [InlineData("fl")] + [InlineData("ga")] + [InlineData("hi")] + [InlineData("id")] + [InlineData("il")] + [InlineData("in")] + [InlineData("ia")] + [InlineData("ks")] + [InlineData("ky")] + [InlineData("la")] + [InlineData("me")] + [InlineData("md")] + [InlineData("ma")] + [InlineData("mi")] + [InlineData("mn")] + [InlineData("ms")] + [InlineData("mo")] + [InlineData("mt")] + [InlineData("ne")] + [InlineData("nv")] + [InlineData("nh")] + [InlineData("nj")] + [InlineData("nm")] + [InlineData("ny")] + [InlineData("nc")] + [InlineData("nd")] + [InlineData("oh")] + [InlineData("ok")] + [InlineData("or")] + [InlineData("pa")] + [InlineData("ri")] + [InlineData("sc")] + [InlineData("sr")] + [InlineData("tn")] + [InlineData("tx")] + [InlineData("ut")] + [InlineData("vt")] + [InlineData("va")] + [InlineData("wa")] + [InlineData("wv")] + [InlineData("wi")] + [InlineData("wy")] + public void US_States_Validation_ShouldFail_LowerCaseAbbreviations(string state) + { + // Act + var result = Regex.IsMatch(state, DavidBerry.Framework.Util.CommonValidations.US_STATES); + + // Assert + result.ShouldBeFalse(); + } + + + [Theory] + [InlineData("Alabama")] + [InlineData("")] + [InlineData("WYO")] + [InlineData("California ")] + [InlineData(" TX")] + [InlineData("New York")] + [InlineData("C")] + public void US_States_Validation_ShouldFail_InvalidAbbreviations(string state) + { + // Act + var result = Regex.IsMatch(state, CommonValidations.US_STATES); + + // Assert + result.ShouldBeFalse(); + } + + + [Theory] + [InlineData("11201")] + [InlineData("54911")] + [InlineData("60062")] + [InlineData("80401")] + [InlineData("94130")] + public void USZipCodes_Validation_WorksForValidZipCodes(string zipCode) + { + // Act + var result = Regex.IsMatch(zipCode, CommonValidations.US_ZIP_CODES); + + // Assert + result.ShouldBeTrue(); + } + + + + [Theory] + [InlineData("1201")] + [InlineData("54911 ")] + [InlineData("6066A")] + [InlineData("ZIPCD")] + [InlineData("")] + public void USZipCodes_Validation_FailsOnInvalidZipCodes(string zipCode) + { + // Act + var result = Regex.IsMatch(zipCode, CommonValidations.US_ZIP_CODES); + + // Assert + result.ShouldBeFalse(); + } + + +} + + From 2ca332e9d48cf988b26c942236ce24c37334408c Mon Sep 17 00:00:00 2001 From: David Berry Date: Fri, 10 Oct 2025 23:32:53 -0500 Subject: [PATCH 3/7] Added tests for Controller Extension Methods --- .../Controllers/ControllerExtensionsTests.cs | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 DavidBerry.Framework.ApiUtil/DavidBerry.Framework.ApiUtil.Tests/Controllers/ControllerExtensionsTests.cs diff --git a/DavidBerry.Framework.ApiUtil/DavidBerry.Framework.ApiUtil.Tests/Controllers/ControllerExtensionsTests.cs b/DavidBerry.Framework.ApiUtil/DavidBerry.Framework.ApiUtil.Tests/Controllers/ControllerExtensionsTests.cs new file mode 100644 index 0000000..b6d5cbc --- /dev/null +++ b/DavidBerry.Framework.ApiUtil/DavidBerry.Framework.ApiUtil.Tests/Controllers/ControllerExtensionsTests.cs @@ -0,0 +1,85 @@ +using DavidBerry.Framework.ApiUtil.Controllers; +using DavidBerry.Framework.ApiUtil.Results; +using Microsoft.AspNetCore.Mvc; +using Moq; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Xunit; +using Shouldly; + + +namespace DavidBerry.Framework.ApiUtil.Tests.Controllers; + + +public class ControllerExtensionsTests +{ + + [Fact] + public void ForbiddenExtensionMethod_ReturnsForbiddenResult() + { + // Arrange + var mockController = new Mock(); + mockController.CallBase = true; + + // Act + var result = mockController.Object.Forbidden(); + + // Assert + result.ShouldBeOfType(); + } + + + [Fact] + public void ForbiddenExtensionMethodWithValue_ReturnsForbiddenResultWithSameValue() + { + // Arrange + var mockController = new Mock(); + mockController.CallBase = true; + string message = "You do not have permission to access this resource."; + + // Act + var result = mockController.Object.Forbidden(message); + + // Assert + result.ShouldBeOfType(); + ((ForbiddenObjectResult)result).Value.ShouldBe(message); + } + + + + [Fact] + public void InternalServerErrorExtensionMethod_ReturnsInternalServerErrorResult() + { + // Arrange + var mockController = new Mock(); + mockController.CallBase = true; + + // Act + var result = mockController.Object.InternalServerError(); + + // Assert + result.ShouldBeOfType(); + } + + + + [Fact] + public void InternalServerErrorExtensionMethodWithValue_Returns_InternalServerErrorObjectResult_WithValue() + { + // Arrange + var mockController = new Mock(); + mockController.CallBase = true; + string message = "It all went terribly, terribly wrong!"; + + // Act + var result = mockController.Object.InternalServerError(message); + + // Assert + result.ShouldBeOfType(); + ((InternalServerErrorObjectResult)result).Value.ShouldBe(message); + } + +} \ No newline at end of file From f23d4f49bd4bbe9d83b029f17d48b11c253085b0 Mon Sep 17 00:00:00 2001 From: David Berry Date: Sat, 11 Oct 2025 12:51:08 -0500 Subject: [PATCH 4/7] Initial tests for ApiControllerBase --- .../Controllers/ApiControllerBaseTests.cs | 124 ++++++++++++++++++ .../Controllers/TestEntity.cs | 23 ++++ .../AssemblyOptions.cs | 3 + .../Controllers/ApiControllerBase.cs | 4 +- 4 files changed, 151 insertions(+), 3 deletions(-) create mode 100644 DavidBerry.Framework.ApiUtil/DavidBerry.Framework.ApiUtil.Tests/Controllers/ApiControllerBaseTests.cs create mode 100644 DavidBerry.Framework.ApiUtil/DavidBerry.Framework.ApiUtil.Tests/Controllers/TestEntity.cs create mode 100644 DavidBerry.Framework.ApiUtil/DavidBerry.Framework.ApiUtil/AssemblyOptions.cs diff --git a/DavidBerry.Framework.ApiUtil/DavidBerry.Framework.ApiUtil.Tests/Controllers/ApiControllerBaseTests.cs b/DavidBerry.Framework.ApiUtil/DavidBerry.Framework.ApiUtil.Tests/Controllers/ApiControllerBaseTests.cs new file mode 100644 index 0000000..82da921 --- /dev/null +++ b/DavidBerry.Framework.ApiUtil/DavidBerry.Framework.ApiUtil.Tests/Controllers/ApiControllerBaseTests.cs @@ -0,0 +1,124 @@ +using AutoMapper; +using DavidBerry.Framework.ApiUtil.Controllers; +using DavidBerry.Framework.ApiUtil.Models; +using DavidBerry.Framework.Functional; +using Microsoft.AspNetCore.Http.HttpResults; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Moq; +using Shouldly; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Xunit; + +namespace DavidBerry.Framework.ApiUtil.Tests.Controllers; + +public class ApiControllerBaseTests +{ + + [Fact] + public void MapErrorResult_ReturnsBadRequest_WhenInvalidDataErrorInResult() + { + // Arrange + Mock> mockLogger = new Mock>(); + Mock mockMapper = new Mock(); + Mock mockController = new Mock(mockLogger.Object, mockMapper.Object); + mockController.CallBase = true; + Result result = Result.Failure(new InvalidDataError("Invalid data")); + + // Act + var actionResult = mockController.Object.MapErrorResult(result); + + // Assert + actionResult.ShouldBeOfType(); + + var badRequestResult = actionResult as BadRequestObjectResult; + badRequestResult.Value.ShouldBeOfType(); + + var apiMessageModel = badRequestResult.Value as ApiMessageModel; + apiMessageModel.Message.ShouldBe("Invalid data"); + } + + + + [Fact] + public void MapErrorResult_ReturnsObjectNotFound_WhenObjectNotFoundErrorInResult() + { + // Arrange + Mock> mockLogger = new Mock>(); + Mock mockMapper = new Mock(); + Mock mockController = new Mock(mockLogger.Object, mockMapper.Object); + mockController.CallBase = true; + Result result = Result.Failure(new ObjectNotFoundError("We looked everywhere, we could not find it")); + + // Act + var actionResult = mockController.Object.MapErrorResult(result); + + // Assert + actionResult.ShouldBeOfType(); + + var notFoundResult = actionResult as NotFoundObjectResult; + notFoundResult.Value.ShouldBeOfType(); + + var apiMessageModel = notFoundResult.Value as ApiMessageModel; + apiMessageModel.Message.ShouldBe("We looked everywhere, we could not find it"); + } + + + + [Fact] + public void CreateResponse_CallsSuccessFunction_WhenResultIsSuccess() + { + // Arrange + Mock> mockLogger = new Mock>(); + Mock mockMapper = new Mock(); + Mock mockController = new Mock(mockLogger.Object, mockMapper.Object); + mockController.CallBase = true; + + var entity = new TestEntity { Id = 1, Name = "Test" }; + Result result = Result.Success(entity); + bool successFunctionCalled = false; + Func successFunction = (e) => + { + successFunctionCalled = true; + return new OkObjectResult(e); + }; + + // Act + var actionResult = mockController.Object.CreateResponse(result, successFunction); + + // Assert + successFunctionCalled.ShouldBeTrue(); + actionResult.ShouldBeOfType(); + var okResult = actionResult as OkObjectResult; + okResult.Value.ShouldBe(entity); + } + + [Fact] + public void CreateResponse_CallsMapErrorResult_WhenResultIsFailure() + { + // Arrange + Mock> mockLogger = new Mock>(); + Mock mockMapper = new Mock(); + Mock mockController = new Mock(mockLogger.Object, mockMapper.Object); + mockController.CallBase = true; + Result result = Result.Failure(new ObjectNotFoundError("Not found")); + + + // Act + var actionResult = mockController.Object.CreateResponse(result, It.IsAny>()); + + // Assert + actionResult.ShouldBeOfType(); + + var notFoundResult = actionResult as NotFoundObjectResult; + notFoundResult.Value.ShouldBeOfType(); + + var apiMessageModel = notFoundResult.Value as ApiMessageModel; + apiMessageModel.Message.ShouldBe("Not found"); + } + +} \ No newline at end of file diff --git a/DavidBerry.Framework.ApiUtil/DavidBerry.Framework.ApiUtil.Tests/Controllers/TestEntity.cs b/DavidBerry.Framework.ApiUtil/DavidBerry.Framework.ApiUtil.Tests/Controllers/TestEntity.cs new file mode 100644 index 0000000..d219a1e --- /dev/null +++ b/DavidBerry.Framework.ApiUtil/DavidBerry.Framework.ApiUtil.Tests/Controllers/TestEntity.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DavidBerry.Framework.ApiUtil.Tests.Controllers; + +internal class TestEntity +{ + + public int Id { get; set; } + public string Name { get; set; } +} + + +internal class TestModel +{ + public int Id { get; set; } + public string Name { get; set; } + +} + diff --git a/DavidBerry.Framework.ApiUtil/DavidBerry.Framework.ApiUtil/AssemblyOptions.cs b/DavidBerry.Framework.ApiUtil/DavidBerry.Framework.ApiUtil/AssemblyOptions.cs new file mode 100644 index 0000000..d5364b5 --- /dev/null +++ b/DavidBerry.Framework.ApiUtil/DavidBerry.Framework.ApiUtil/AssemblyOptions.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("DavidBerry.Framework.ApiUtil.Tests")] diff --git a/DavidBerry.Framework.ApiUtil/DavidBerry.Framework.ApiUtil/Controllers/ApiControllerBase.cs b/DavidBerry.Framework.ApiUtil/DavidBerry.Framework.ApiUtil/Controllers/ApiControllerBase.cs index 35b2fad..bab7f27 100644 --- a/DavidBerry.Framework.ApiUtil/DavidBerry.Framework.ApiUtil/Controllers/ApiControllerBase.cs +++ b/DavidBerry.Framework.ApiUtil/DavidBerry.Framework.ApiUtil/Controllers/ApiControllerBase.cs @@ -97,7 +97,7 @@ protected ActionResult CreateResponse(Result result) } - protected ActionResult CreateResponse(Result result, Func successFunction) + protected internal ActionResult CreateResponse(Result result, Func successFunction) { if (result.IsSuccess) { @@ -109,7 +109,6 @@ protected ActionResult CreateResponse(Result result, F } } - protected internal ActionResult MapErrorResult(Result result) { switch (result.Error) @@ -127,7 +126,6 @@ protected internal ActionResult MapErrorResult(Result result) } } - protected internal ActionResult MapErrorResult(Result result) { switch (result.Error) From dba62096ade9b6ee28064d329aed847bce9bfbcc Mon Sep 17 00:00:00 2001 From: David Berry Date: Sat, 11 Oct 2025 14:00:35 -0500 Subject: [PATCH 5/7] Additional Validation Regex tests --- .../Util/CommonValidationsTests.cs | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/DavidBerry.Framework/DavidBerry.Framework.Tests/Util/CommonValidationsTests.cs b/DavidBerry.Framework/DavidBerry.Framework.Tests/Util/CommonValidationsTests.cs index f951adb..0d5ba01 100644 --- a/DavidBerry.Framework/DavidBerry.Framework.Tests/Util/CommonValidationsTests.cs +++ b/DavidBerry.Framework/DavidBerry.Framework.Tests/Util/CommonValidationsTests.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reflection.Emit; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; @@ -186,6 +187,65 @@ public void USZipCodes_Validation_FailsOnInvalidZipCodes(string zipCode) } + + [Theory] + [InlineData("https://www.github.com/")] + [InlineData("https://www.github.com")] + [InlineData("https://github.com/")] + [InlineData("https://github.com")] + public void Url_validation_PassesValidHttpsUrls(string url) + { + // Act + var result = Regex.IsMatch(url, CommonValidations.URL); + + // Assert + result.ShouldBeTrue(); + } + + + [Theory] + [InlineData("http://www.yahoo.com/")] + [InlineData("http://www.yahoo.com")] + [InlineData("http://yahoo.com/")] + [InlineData("http://yahoo.com")] + public void Url_validation_PassesValidHttpUrls(string url) + { + // Act + var result = Regex.IsMatch(url, CommonValidations.URL); + + // Assert + result.ShouldBeTrue(); + } + + + [Theory] + [InlineData("10.0.0.0")] + [InlineData("192.168.1.1")] + [InlineData("255.255.255.255")] + public void IpAddress_Validation_PassesValidIpAddresses(string ipAddress) + { + // Act + var result = Regex.IsMatch(ipAddress, CommonValidations.IP_ADDRESS); + + // Assert + result.ShouldBeTrue(); + } + + + [Theory] + [InlineData("")] + [InlineData("192.168.1.")] + [InlineData("255.255.255.255.255")] + public void IpAddress_Validation_FailsInvalidIpAddresses(string ipAddress) + { + // Act + var result = Regex.IsMatch(ipAddress, CommonValidations.IP_ADDRESS); + + // Assert + result.ShouldBeFalse(); + } + + } From ffa79e1d5f28f7ef22cd637f001ca142614d28b8 Mon Sep 17 00:00:00 2001 From: David Berry Date: Sat, 11 Oct 2025 15:47:39 -0500 Subject: [PATCH 6/7] Additional Base Api Controller tests --- .../Controllers/ApiControllerBaseTests.cs | 117 ++++++++++++++++++ 1 file changed, 117 insertions(+) diff --git a/DavidBerry.Framework.ApiUtil/DavidBerry.Framework.ApiUtil.Tests/Controllers/ApiControllerBaseTests.cs b/DavidBerry.Framework.ApiUtil/DavidBerry.Framework.ApiUtil.Tests/Controllers/ApiControllerBaseTests.cs index 82da921..31125df 100644 --- a/DavidBerry.Framework.ApiUtil/DavidBerry.Framework.ApiUtil.Tests/Controllers/ApiControllerBaseTests.cs +++ b/DavidBerry.Framework.ApiUtil/DavidBerry.Framework.ApiUtil.Tests/Controllers/ApiControllerBaseTests.cs @@ -121,4 +121,121 @@ public void CreateResponse_CallsMapErrorResult_WhenResultIsFailure() apiMessageModel.Message.ShouldBe("Not found"); } + + + [Fact] + public void MapErrorResult_ReturnsBadRequest_WhenResultIsFailure_WithInvalidDataError() + { + // Arrange + Mock> mockLogger = new Mock>(); + Mock mockMapper = new Mock(); + Mock mockController = new Mock(mockLogger.Object, mockMapper.Object); + mockController.CallBase = true; + Result result = Result.Failure(new InvalidDataError("Invalid data")); + + // Act + var actionResult = mockController.Object.MapErrorResult(result); + + // Assert + actionResult.ShouldBeOfType(); + + var badRequestResult = actionResult as BadRequestObjectResult; + badRequestResult.Value.ShouldBeOfType(); + + var apiMessageModel = badRequestResult.Value as ApiMessageModel; + apiMessageModel.Message.ShouldBe("Invalid data"); + + } + + + [Fact] + public void MapErrorResult_ReturnsNotFound_WhenResultIsFailure_WithObjectNotFoundError() + { + // Arrange + Mock> mockLogger = new Mock>(); + Mock mockMapper = new Mock(); + Mock mockController = new Mock(mockLogger.Object, mockMapper.Object); + mockController.CallBase = true; + Result result = Result.Failure(new ObjectNotFoundError("The data was nowhere we looked")); + + // Act + var actionResult = mockController.Object.MapErrorResult(result); + + // Assert + actionResult.ShouldBeOfType(); + + var notFoundResult = actionResult as NotFoundObjectResult; + notFoundResult.Value.ShouldBeOfType(); + + var apiMessageModel = notFoundResult.Value as ApiMessageModel; + apiMessageModel.Message.ShouldBe("The data was nowhere we looked"); + + } + + + + [Fact] + public void MapErrorResult_ReturnsCreateObjectExistsConflictErrorResult_WhenResultIsFailure_WithObjectAlreadyExistsError() + { + // Arrange + TestEntity testEntity = new TestEntity() { Id = 1, Name = "I already exist" }; + + Mock> mockLogger = new Mock>(); + Mock mockMapper = new Mock(); + mockMapper.Setup(x => x.Map(testEntity)).Returns(new TestModel() { Id = 1, Name = "I already exist" }); + + Mock mockController = new Mock(mockLogger.Object, mockMapper.Object); + mockController.CallBase = true; + Result result = Result.Failure(new ObjectAlreadyExistsError("The object already exists", testEntity)); + + // Act + var actionResult = mockController.Object.MapErrorResult(result); + + // Assert + actionResult.ShouldBeOfType(); + + var conflictResult = actionResult as ConflictObjectResult; + conflictResult.Value.ShouldBeOfType>(); + + var concurrencyErrorModel = conflictResult.Value as ConcurrencyErrorModel; + concurrencyErrorModel.Message.ShouldBe("The object already exists"); + + concurrencyErrorModel.CurrentObject.ShouldBeOfType(); + concurrencyErrorModel.CurrentObject.Id.ShouldBe(1); + concurrencyErrorModel.CurrentObject.Name.ShouldBe("I already exist"); + } + + + [Fact] + public void MapErrorResult_ReturnsConcurrencyConflict_WhenResultIsFailure_WithConcurrencyError() + { + // Arrange + TestEntity testEntity = new TestEntity() { Id = 1, Name = "Concurrency Error" }; + + Mock> mockLogger = new Mock>(); + Mock mockMapper = new Mock(); + mockMapper.Setup(x => x.Map(testEntity)).Returns(new TestModel() { Id = 1, Name = "Concurrency Error" }); + + Mock mockController = new Mock(mockLogger.Object, mockMapper.Object); + mockController.CallBase = true; + Result result = Result.Failure(new ConcurrencyError("Someone else updated the object", testEntity)); + + // Act + var actionResult = mockController.Object.MapErrorResult(result); + + // Assert + actionResult.ShouldBeOfType(); + + var conflictResult = actionResult as ConflictObjectResult; + conflictResult.Value.ShouldBeOfType>(); + + var concurrencyErrorModel = conflictResult.Value as ConcurrencyErrorModel; + concurrencyErrorModel.Message.ShouldBe("Someone else updated the object"); + + concurrencyErrorModel.CurrentObject.ShouldBeOfType(); + concurrencyErrorModel.CurrentObject.Id.ShouldBe(1); + concurrencyErrorModel.CurrentObject.Name.ShouldBe("Concurrency Error"); + } + + } \ No newline at end of file From ecb261f4c9f4921cd76c0d56598a47e6fd6c3f32 Mon Sep 17 00:00:00 2001 From: David Berry Date: Sat, 11 Oct 2025 17:36:54 -0500 Subject: [PATCH 7/7] More Base Api Conntoller Tests --- .../Controllers/ApiControllerBaseTests.cs | 96 +++++++++++++++++++ .../Controllers/ApiControllerBase.cs | 6 +- 2 files changed, 99 insertions(+), 3 deletions(-) diff --git a/DavidBerry.Framework.ApiUtil/DavidBerry.Framework.ApiUtil.Tests/Controllers/ApiControllerBaseTests.cs b/DavidBerry.Framework.ApiUtil/DavidBerry.Framework.ApiUtil.Tests/Controllers/ApiControllerBaseTests.cs index 31125df..c4fd10d 100644 --- a/DavidBerry.Framework.ApiUtil/DavidBerry.Framework.ApiUtil.Tests/Controllers/ApiControllerBaseTests.cs +++ b/DavidBerry.Framework.ApiUtil/DavidBerry.Framework.ApiUtil.Tests/Controllers/ApiControllerBaseTests.cs @@ -1,6 +1,7 @@ using AutoMapper; using DavidBerry.Framework.ApiUtil.Controllers; using DavidBerry.Framework.ApiUtil.Models; +using DavidBerry.Framework.Exceptions; using DavidBerry.Framework.Functional; using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; @@ -238,4 +239,99 @@ public void MapErrorResult_ReturnsConcurrencyConflict_WhenResultIsFailure_WithCo } + [Fact] + public void CreateConcurrencyConflictErrorResult_CreatesConflictObjectResult_WhenPassedConcurrencyException() + { + // Arrange + TestEntity testEntity = new TestEntity() { Id = 1, Name = "Concurrency Error" }; + + Mock> mockLogger = new Mock>(); + Mock mockMapper = new Mock(); + mockMapper.Setup(x => x.Map(testEntity)).Returns(new TestModel() { Id = 1, Name = "Concurrency Error" }); + + Mock mockController = new Mock(mockLogger.Object, mockMapper.Object); + mockController.CallBase = true; + + var concurrencyException = new ConcurrencyException("Error Message", testEntity); + + // Act + var actionResult = mockController.Object.CreateConcurrencyConflictErrorResult(concurrencyException); + + // Assert + actionResult.ShouldBeOfType(); + + var conflictResult = actionResult as ConflictObjectResult; + conflictResult.Value.ShouldBeOfType>(); + + var errorModel = conflictResult.Value as ConcurrencyErrorModel; + errorModel.Message.ShouldBe("Error Message"); + errorModel.CurrentObject.ShouldBeOfType(); + errorModel.CurrentObject.Id.ShouldBe(1); + errorModel.CurrentObject.Name.ShouldBe("Concurrency Error"); + } + + + [Fact] + public void CreateConcurrencyConflictErrorResult_CreatesConflictObjectResult_WhenPassedConcurrencyError() + { + // Arrange + TestEntity testEntity = new TestEntity() { Id = 1, Name = "Concurrency Error" }; + + Mock> mockLogger = new Mock>(); + Mock mockMapper = new Mock(); + mockMapper.Setup(x => x.Map(testEntity)).Returns(new TestModel() { Id = 1, Name = "Concurrency Error" }); + + Mock mockController = new Mock(mockLogger.Object, mockMapper.Object); + mockController.CallBase = true; + + var concurrencyError = new ConcurrencyError("Error Message", testEntity); + + // Act + var actionResult = mockController.Object.CreateConcurrencyConflictErrorResult(concurrencyError); + + // Assert + actionResult.ShouldBeOfType(); + + var conflictResult = actionResult as ConflictObjectResult; + conflictResult.Value.ShouldBeOfType>(); + + var errorModel = conflictResult.Value as ConcurrencyErrorModel; + errorModel.Message.ShouldBe("Error Message"); + errorModel.CurrentObject.ShouldBeOfType(); + errorModel.CurrentObject.Id.ShouldBe(1); + errorModel.CurrentObject.Name.ShouldBe("Concurrency Error"); + } + + + [Fact] + public void CreateObjectExistsConflictErrorResult_CreatesConflictObjectResult_WhenPassedObjectAlreadyExistsError() + { + // Arrange + TestEntity testEntity = new TestEntity() { Id = 1, Name = "Object Exists Error" }; + + Mock> mockLogger = new Mock>(); + Mock mockMapper = new Mock(); + mockMapper.Setup(x => x.Map(testEntity)).Returns(new TestModel() { Id = 1, Name = "Object Exists Error" }); + + Mock mockController = new Mock(mockLogger.Object, mockMapper.Object); + mockController.CallBase = true; + + var objectExistsError = new ObjectAlreadyExistsError("Error Message", testEntity); + + // Act + var actionResult = mockController.Object.CreateObjectExistsConflictErrorResult(objectExistsError); + + // Assert + actionResult.ShouldBeOfType(); + + var conflictResult = actionResult as ConflictObjectResult; + conflictResult.Value.ShouldBeOfType>(); + + var errorModel = conflictResult.Value as ConcurrencyErrorModel; + errorModel.Message.ShouldBe("Error Message"); + errorModel.CurrentObject.ShouldBeOfType(); + errorModel.CurrentObject.Id.ShouldBe(1); + errorModel.CurrentObject.Name.ShouldBe("Object Exists Error"); + } + } \ No newline at end of file diff --git a/DavidBerry.Framework.ApiUtil/DavidBerry.Framework.ApiUtil/Controllers/ApiControllerBase.cs b/DavidBerry.Framework.ApiUtil/DavidBerry.Framework.ApiUtil/Controllers/ApiControllerBase.cs index bab7f27..7d59b9e 100644 --- a/DavidBerry.Framework.ApiUtil/DavidBerry.Framework.ApiUtil/Controllers/ApiControllerBase.cs +++ b/DavidBerry.Framework.ApiUtil/DavidBerry.Framework.ApiUtil/Controllers/ApiControllerBase.cs @@ -41,7 +41,7 @@ public ApiControllerBase(ILogger logger, IMapper mapper) : ba /// /// /// - protected virtual IActionResult CreateConcurrencyConflictErrorResult(ConcurrencyException concurrencyException) + protected internal virtual IActionResult CreateConcurrencyConflictErrorResult(ConcurrencyException concurrencyException) { var conflictingObject = concurrencyException.TypedObject; var model = _mapper.Map(conflictingObject); @@ -54,7 +54,7 @@ protected virtual IActionResult CreateConcurrencyConflictErrorResult(ConcurrencyError concurrencyError) + protected internal virtual ActionResult CreateConcurrencyConflictErrorResult(ConcurrencyError concurrencyError) { var model = _mapper.Map(concurrencyError.ConflictingObject); var message = new ConcurrencyErrorModel() @@ -65,7 +65,7 @@ protected virtual ActionResult CreateConcurrencyConflictErrorResult(ObjectAlreadyExistsError objectExistsError) + protected internal virtual ActionResult CreateObjectExistsConflictErrorResult(ObjectAlreadyExistsError objectExistsError) { var model = _mapper.Map(objectExistsError.ExistingObject); var message = new ConcurrencyErrorModel()