66using System . Diagnostics ;
77using System . Linq ;
88using System . Text . Json ;
9+ using NUnit . Framework ;
910
1011namespace Azure . AI . Projects . Tests . Utilities
1112{
1213 public class GenAiTraceVerifier
1314 {
14- public bool CheckSpanAttributes ( Activity span , Dictionary < string , object > expectedAttributes )
15+ public static void ValidateSpanAttributes ( Activity span , Dictionary < string , object > expectedAttributes )
1516 {
1617 var actualAttributes = new Dictionary < string , object > ( ) ;
17- foreach ( var tag in span . EnumerateTagObjects ( ) )
18+ foreach ( KeyValuePair < string , object > tag in span . EnumerateTagObjects ( ) )
1819 {
1920 actualAttributes [ tag . Key ] = tag . Value ;
2021 }
2122
22- foreach ( var expected in expectedAttributes )
23+ foreach ( KeyValuePair < string , object > expected in expectedAttributes )
2324 {
24- if ( ! actualAttributes . ContainsKey ( expected . Key ) )
25- {
26- Console . WriteLine ( $ "Attribute '{ expected . Key } ' not found in span.") ;
27- return false ;
28- }
29-
25+ Assert . That ( actualAttributes , Contains . Key ( expected . Key ) , $ "Attribute '{ expected . Key } ' not found in span.") ;
3026 var actualValue = actualAttributes [ expected . Key ] ;
31-
32- if ( ! CheckAttributeValue ( expected . Value , actualValue ) )
33- {
34- Console . WriteLine ( $ "Attribute '{ expected . Key } ' value mismatch. Expected: { expected . Value } , Actual: { actualValue } ") ;
35- return false ;
36- }
27+ ValidateAttributeValue ( expected . Value , actualValue , expected . Key ) ;
3728 }
38- return true ;
3929 }
4030
41- public bool CheckSpanEvents ( Activity span , List < ( string Name , Dictionary < string , object > Attributes ) > expectedEvents )
31+ public static void ValidateSpanEvents ( Activity span , List < ( string Name , Dictionary < string , object > Attributes ) > expectedEvents )
4232 {
4333 var spanEvents = span . Events . ToList ( ) ;
44- return CheckSpanEvents ( spanEvents , expectedEvents ) ;
34+ ValidateSpanEvents ( spanEvents , expectedEvents ) ;
4535 }
4636
47- public bool CheckSpanEvents ( List < ActivityEvent > spanEvents , List < ( string Name , Dictionary < string , object > Attributes ) > expectedEvents , bool allowAdditionalEvents = false )
37+ public static void ValidateSpanEvents ( List < ActivityEvent > spanEvents , List < ( string Name , Dictionary < string , object > Attributes ) > expectedEvents , bool allowAdditionalEvents = false )
4838 {
4939 foreach ( var expectedEvent in expectedEvents )
5040 {
5141 var matchingEvent = spanEvents . FirstOrDefault ( e => e . Name == expectedEvent . Name ) ;
52- if ( matchingEvent . Name == null )
53- {
54- Console . WriteLine ( $ "Event '{ expectedEvent . Name } ' not found.") ;
55- return false ;
56- }
42+ Assert . That ( matchingEvent . Name , Is . Not . Null , $ "Event '{ expectedEvent . Name } ' not found.") ;
5743
5844 var actualEventAttributes = new Dictionary < string , object > ( ) ;
5945 foreach ( var tag in matchingEvent . EnumerateTagObjects ( ) )
6046 {
6147 actualEventAttributes [ tag . Key ] = tag . Value ;
6248 }
6349
64- if ( ! CheckEventAttributes ( expectedEvent . Attributes , actualEventAttributes ) )
65- {
66- Console . WriteLine ( $ "Event '{ expectedEvent . Name } ' attributes mismatch.") ;
67- return false ;
68- }
69-
50+ ValidateEventAttributes ( expectedEvent . Attributes , actualEventAttributes , expectedEvent . Name ) ;
7051 spanEvents . Remove ( matchingEvent ) ;
7152 }
7253
73- if ( spanEvents . Any ( ) && ! allowAdditionalEvents )
74- {
75- Console . WriteLine ( "Unexpected additional events found in span." ) ;
76- return false ;
77- }
78-
79- return true ;
54+ Assert . That ( spanEvents . Any ( ) && ! allowAdditionalEvents , Is . False , $ "Unexpected additional events { spanEvents } found in span.") ;
8055 }
8156
82- public bool CheckEventAttributes ( Dictionary < string , object > expected , Dictionary < string , object > actual )
57+ public static void ValidateEventAttributes ( Dictionary < string , object > expected , Dictionary < string , object > actual , string eventName )
8358 {
8459 var expectedKeys = new HashSet < string > ( expected . Keys ) ;
8560 var actualKeys = new HashSet < string > ( actual . Keys ) ;
8661
87- if ( ! expectedKeys . SetEquals ( actualKeys ) )
88- {
89- Console . WriteLine ( "Event attribute keys mismatch." ) ;
90- return false ;
91- }
92-
62+ Assert . That ( expectedKeys . SetEquals ( actualKeys ) , Is . True , $ "The { eventName } event attribute keys mismatch.\n Expected: { expectedKeys } \n Actual: { actualKeys } ") ;
9363 foreach ( var key in expectedKeys )
9464 {
95- if ( ! CheckAttributeValue ( expected [ key ] , actual [ key ] ) )
96- {
97- Console . WriteLine ( $ "Event attribute '{ key } ' value mismatch. Expected: { expected [ key ] } , Actual: { actual [ key ] } ") ;
98- return false ;
99- }
65+ ValidateAttributeValue ( expected [ key ] , actual [ key ] , key ) ;
10066 }
101-
102- return true ;
10367 }
10468
105- private bool CheckAttributeValue ( object expected , object actual )
69+ private static void ValidateAttributeValue ( object expected , object actual , string key )
10670 {
10771 if ( expected is string expectedStr )
10872 {
10973 if ( expectedStr == "*" )
11074 {
111- return ! string . IsNullOrEmpty ( actual ? . ToString ( ) ) ;
75+ Assert . That ( actual , Is . Not . Null , $ "The value for { key } i expected to be { actual } but was null.") ;
76+ Assert . That ( actual , Is . Not . Empty , $ "The value for { key } i expected to be { actual } but was empty.") ;
11277 }
113- if ( expectedStr == "+" )
78+ else if ( expectedStr == "+" )
11479 {
11580 if ( double . TryParse ( actual ? . ToString ( ) , out double numericValue ) )
11681 {
117- return numericValue >= 0 ;
82+ Assert . That ( numericValue , Is . GreaterThanOrEqualTo ( 0 ) , $ "The value for { key } is expected to be more then 0, but was { numericValue } " ) ;
11883 }
119- return false ;
84+ Assert . Fail ( $ "The value for { key } was not set." ) ;
12085 }
121- if ( IsValidJson ( expectedStr ) && IsValidJson ( actual ? . ToString ( ) ) )
86+ else if ( IsValidJson ( expectedStr ) && IsValidJson ( actual ? . ToString ( ) ) )
12287 {
123- return CheckJsonString ( expectedStr , actual . ToString ( ) ) ;
88+ ValidateJsonString ( expectedStr , actual . ToString ( ) , key ) ;
89+ }
90+ else
91+ {
92+ Assert . That ( actual ? . ToString ( ) , Is . EqualTo ( expectedStr ) , $ "Expected value for { key } is { expectedStr } , but was { actual ? . ToString ( ) } ") ;
12493 }
125- return expectedStr == actual ? . ToString ( ) ;
12694 }
12795 else if ( expected is Dictionary < string , object > expectedDict )
12896 {
12997 if ( actual is string actualStr && IsValidJson ( actualStr ) )
13098 {
131- var actualDict = JsonSerializer . Deserialize < Dictionary < string , object > > ( actualStr ) ;
132- return CheckEventAttributes ( expectedDict , actualDict ) ;
99+ Dictionary < string , object > actualDict = JsonSerializer . Deserialize < Dictionary < string , object > > ( actualStr ) ;
100+ ValidateAttributeValue ( expectedDict , actualDict , key ) ;
133101 }
134- return false ;
102+ Assert . Fail ( $ "The value for { key } is not a valid JSON: { actual } " ) ;
135103 }
136104 else if ( expected is IEnumerable < object > expectedList )
137105 {
138106 if ( actual is string actualStr && IsValidJson ( actualStr ) )
139107 {
140- var actualList = JsonSerializer . Deserialize < List < object > > ( actualStr ) ;
141- return expectedList . SequenceEqual ( actualList ) ;
108+ List < object > actualList = JsonSerializer . Deserialize < List < object > > ( actualStr ) ;
109+ Assert . That ( expectedList . SequenceEqual ( actualList ) , Is . True , $ "The lists for { key } are different: \n Actual { actualList } \n { expectedList } " ) ;
142110 }
143- return false ;
111+ Assert . Fail ( $ "The value for { key } is not a valid JSON: { actual } " ) ;
144112 }
145113 else
146114 {
147- return expected . Equals ( actual ) ;
115+ Assert . That ( actual , Is . EqualTo ( expected ) , $ "Expected value for { key } is { expected } , but was { actual } " ) ;
148116 }
149117 }
150118
151- private bool IsValidJson ( string json )
119+ private static bool IsValidJson ( string json )
152120 {
153121 if ( string . IsNullOrWhiteSpace ( json ) )
154122 return false ;
@@ -163,55 +131,51 @@ private bool IsValidJson(string json)
163131 }
164132 }
165133
166- private bool CheckJsonString ( string expectedJson , string actualJson )
134+ private static void ValidateJsonString ( string expectedJson , string actualJson , string key )
167135 {
168136 try
169137 {
170138 var expectedDoc = JsonDocument . Parse ( expectedJson ) ;
171139 var actualDoc = JsonDocument . Parse ( actualJson ) ;
172- return JsonElementDeepEquals ( expectedDoc . RootElement , actualDoc . RootElement ) ;
140+ AssertJsonElementDeepEquals ( expectedDoc . RootElement , actualDoc . RootElement , key ) ;
173141 }
174142 catch
175143 {
176- return false ;
144+ Assert . Fail ( $ "Unable to parse expected or actual for { key } . Expected: { expectedJson } ; Actual: { actualJson } " ) ;
177145 }
178146 }
179147
180- private bool JsonElementDeepEquals ( JsonElement expected , JsonElement actual )
148+ private static void AssertJsonElementDeepEquals ( JsonElement expected , JsonElement actual , string key )
181149 {
182- if ( expected . ValueKind != actual . ValueKind )
183- return false ;
150+ Assert . That ( actual . ValueKind , Is . EqualTo ( expected . ValueKind ) , $ "The value kind for key { key } differs. Expected: { expected . ValueKind } , Actual: { actual . ValueKind } ") ;
184151
185152 switch ( expected . ValueKind )
186153 {
187154 case JsonValueKind . Object :
188155 var expectedProps = expected . EnumerateObject ( ) . OrderBy ( p => p . Name ) . ToList ( ) ;
189156 var actualProps = actual . EnumerateObject ( ) . OrderBy ( p => p . Name ) . ToList ( ) ;
190- if ( expectedProps . Count != actualProps . Count )
191- return false ;
157+ Assert . That ( actualProps . Count , Is . EqualTo ( expectedProps . Count ) , $ "The number of propertie for { key } was different. Expected: { expectedProps . Count } , but was { actualProps . Count } .\n Expected: { expectedProps } \n Actual: { actualProps } .") ;
192158 for ( int i = 0 ; i < expectedProps . Count ; i ++ )
193159 {
194- if ( expectedProps [ i ] . Name != actualProps [ i ] . Name ||
195- ! JsonElementDeepEquals ( expectedProps [ i ] . Value , actualProps [ i ] . Value ) )
196- return false ;
160+ Assert . That ( actualProps [ i ] . Name , Is . EqualTo ( expectedProps [ i ] . Name ) , $ "The { i } -th property of { key } is named { actualProps [ i ] . Name } bit expected property is { expectedProps [ i ] . Name } ") ;
161+ AssertJsonElementDeepEquals ( expectedProps [ i ] . Value , actualProps [ i ] . Value , $ "{ key } /{ actualProps [ i ] . Name } ") ;
197162 }
198- return true ;
199-
163+ break ;
200164 case JsonValueKind . Array :
201165 var expectedItems = expected . EnumerateArray ( ) . ToList ( ) ;
202166 var actualItems = actual . EnumerateArray ( ) . ToList ( ) ;
203- if ( expectedItems . Count != actualItems . Count )
204- return false ;
167+ Assert . That ( actualItems . Count , Is . EqualTo ( expectedItems . Count ) , $ "The number of elements in { key } is different. Expected: { expectedItems . Count } , but was { actualItems . Count } .\n Expected: { expectedItems } \n Actual: { actualItems } .") ;
205168 for ( int i = 0 ; i < expectedItems . Count ; i ++ )
206169 {
207- if ( ! JsonElementDeepEquals ( expectedItems [ i ] , actualItems [ i ] ) )
208- return false ;
170+ AssertJsonElementDeepEquals ( expectedItems [ i ] , actualItems [ i ] , $ "{ key } /[{ i } ]") ;
209171 }
210- return true ;
172+ break ;
211173 case JsonValueKind . Number :
212- return expected . GetInt64 ( ) == actual . GetInt64 ( ) ;
174+ Assert . That ( actual . GetInt64 ( ) , Is . EqualTo ( expected . GetInt64 ( ) ) , $ "Expected value for { key } is { expected } , but was { actual } .") ;
175+ break ;
213176 default :
214- return CheckAttributeValue ( expected . GetString ( ) , actual . GetString ( ) ) ;
177+ ValidateAttributeValue ( expected . GetString ( ) , actual . GetString ( ) , key ) ;
178+ break ;
215179 }
216180 }
217181 }
0 commit comments