1+ using NUnit . Framework ;
2+ using OutSystems . NetChecksumUtils ;
3+ using System ;
4+ using System . Text ;
5+ using System . Security . Cryptography ;
6+
7+ namespace OutSystems . NetChecksumUtils . Tests
8+ {
9+ /// <summary>
10+ /// Comprehensive tests for <see cref="NetChecksumUtils"/>.
11+ /// Validates hashing accuracy, performance tracking (ticks), and input validation.
12+ /// </summary>
13+ [ TestFixture ]
14+ public class NetChecksumUtilsTests
15+ {
16+ private readonly NetChecksumUtils _sut = new ( ) ;
17+
18+ #region Helper Methods
19+
20+ /// <summary>
21+ /// Reference implementation to verify the SUT output against standard .NET providers.
22+ /// Matches the internal logic of the SUT for cross-validation.
23+ /// </summary>
24+ private static string GetExpectedHash ( string algorithm , string text )
25+ {
26+ byte [ ] data = Encoding . UTF8 . GetBytes ( text ) ;
27+ return algorithm . Trim ( ) . ToUpperInvariant ( ) switch
28+ {
29+ "SHA256" or "SHA-256" => Convert . ToHexString ( SHA256 . HashData ( data ) ) ,
30+ "SHA512" or "SHA-512" => Convert . ToHexString ( SHA512 . HashData ( data ) ) ,
31+ "SHA3_256" or "SHA3-256" => Convert . ToHexString ( SHA3_256 . HashData ( data ) ) ,
32+ "MD5" => Convert . ToHexString ( MD5 . HashData ( data ) ) ,
33+ _ => throw new ArgumentException ( "Unsupported algorithm in test helper" , nameof ( algorithm ) )
34+ } ;
35+ }
36+
37+ #endregion
38+
39+ #region ComputeChecksum Tests
40+
41+ /// <summary>
42+ /// Validates that all supported algorithms produce the correct hash and record a valid duration.
43+ /// </summary>
44+ [ TestCase ( "SHA256" , "OutSystems" ) ]
45+ [ TestCase ( "SHA-256" , "OutSystems" ) ]
46+ [ TestCase ( "SHA512" , "High performance low-code" ) ]
47+ //[TestCase("SHA3_256", "Modern security")]
48+ [ TestCase ( "MD5" , "Legacy support" ) ]
49+ [ TestCase ( "SHA256" , "" ) ] // Edge case: empty string
50+ public void ComputeChecksum_ValidInput_ReturnsCorrectHash ( string algorithm , string text )
51+ {
52+ // Arrange
53+ string expected = GetExpectedHash ( algorithm , text ) ;
54+
55+ // Act
56+ _sut . ComputeChecksum ( algorithm , text , out string actual , out long ticks ) ;
57+
58+ // Assert
59+ Assert . Multiple ( ( ) =>
60+ {
61+ Assert . That ( actual , Is . EqualTo ( expected ) , $ "Hash mismatch for { algorithm } ") ;
62+ Assert . That ( ticks , Is . GreaterThanOrEqualTo ( 0 ) , "Duration should be non-negative." ) ;
63+ } ) ;
64+ }
65+
66+ #endregion
67+
68+ #region VerifyChecksum Tests
69+
70+ /// <summary>
71+ /// Validates that VerifyChecksum correctly identifies matching hashes (case-insensitive)
72+ /// and detects mismatched content.
73+ /// </summary>
74+ [ Test ]
75+ public void VerifyChecksum_CorrectHash_ReturnsTrue ( )
76+ {
77+ const string algo = "SHA256" ;
78+ const string text = "Check me" ;
79+ string validHash = GetExpectedHash ( algo , text ) ;
80+
81+ _sut . VerifyChecksum ( algo , text , validHash , out bool isValid , out _ ) ;
82+
83+ Assert . That ( isValid , Is . True , "Verification should pass for matching hash." ) ;
84+ }
85+
86+ [ Test ]
87+ public void VerifyChecksum_MismatchedHash_ReturnsFalse ( )
88+ {
89+ const string algo = "SHA256" ;
90+ const string text = "Check me" ;
91+ const string wrongHash = "A1B2C3D4E5F6" ; // Clearly wrong
92+
93+ _sut . VerifyChecksum ( algo , text , wrongHash , out bool isValid , out _ ) ;
94+
95+ Assert . That ( isValid , Is . False , "Verification should fail for incorrect hash." ) ;
96+ }
97+
98+ [ Test ]
99+ public void VerifyChecksum_CaseInsensitiveHash_ReturnsTrue ( )
100+ {
101+ const string algo = "SHA256" ;
102+ const string text = "Check me" ;
103+ string lowerHash = GetExpectedHash ( algo , text ) . ToLowerInvariant ( ) ;
104+
105+ _sut . VerifyChecksum ( algo , text , lowerHash , out bool isValid , out _ ) ;
106+
107+ Assert . That ( isValid , Is . True , "Verification should be case-insensitive." ) ;
108+ }
109+
110+ #endregion
111+
112+ #region Exception & Error Handling
113+
114+ /// <summary>
115+ /// Ensures ArgumentNullException is thrown when any required input is null.
116+ /// </summary>
117+ [ Test ]
118+ public void Methods_NullInputs_ThrowArgumentNullException ( )
119+ {
120+ Assert . Multiple ( ( ) =>
121+ {
122+ // ComputeChecksum null checks
123+ Assert . Throws < ArgumentNullException > ( ( ) => _sut . ComputeChecksum ( null ! , "text" , out _ , out _ ) ) ;
124+ Assert . Throws < ArgumentNullException > ( ( ) => _sut . ComputeChecksum ( "SHA256" , null ! , out _ , out _ ) ) ;
125+
126+ // VerifyChecksum null checks
127+ Assert . Throws < ArgumentNullException > ( ( ) => _sut . VerifyChecksum ( null ! , "text" , "hash" , out _ , out _ ) ) ;
128+ Assert . Throws < ArgumentNullException > ( ( ) => _sut . VerifyChecksum ( "SHA256" , null ! , "hash" , out _ , out _ ) ) ;
129+ Assert . Throws < ArgumentNullException > ( ( ) => _sut . VerifyChecksum ( "SHA256" , "text" , null ! , out _ , out _ ) ) ;
130+ } ) ;
131+ }
132+
133+ /// <summary>
134+ /// Ensures ArgumentException is thrown for unsupported algorithm strings.
135+ /// </summary>
136+ [ TestCase ( "SHA1" ) ] // Specifically not implemented in your GetHashFunction
137+ [ TestCase ( "ROT13" ) ]
138+ [ TestCase ( "" ) ]
139+ public void Methods_InvalidAlgorithm_ThrowsArgumentException ( string invalidAlgo )
140+ {
141+ Assert . Throws < ArgumentException > ( ( ) =>
142+ _sut . ComputeChecksum ( invalidAlgo , "text" , out _ , out _ ) ) ;
143+ }
144+
145+ #endregion
146+ }
147+ }
0 commit comments