22Simple trace assertion - just check that expected spans exist with required attributes.
33Much simpler than the tree-based approach.
44"""
5-
65import json
76from typing import List , Dict , Any
87
9-
108def load_traces (traces_file : str ) -> List [Dict [str , Any ]]:
119 """Load traces from a JSONL file."""
1210 traces = []
@@ -16,27 +14,32 @@ def load_traces(traces_file: str) -> List[Dict[str, Any]]:
1614 traces .append (json .loads (line ))
1715 return traces
1816
19-
2017def load_expected_traces (expected_file : str ) -> List [Dict [str , Any ]]:
2118 """Load expected trace definitions from a JSON file."""
2219 with open (expected_file , 'r' , encoding = 'utf-8' ) as f :
2320 data = json .load (f )
2421 return data .get ('required_spans' , [])
2522
26-
2723def get_attributes (span : Dict [str , Any ]) -> Dict [str , Any ]:
28- """Parse attributes from a span."""
24+ """
25+ Parse attributes from a span.
26+ Supports both formats:
27+ - Old format: 'Attributes' as a JSON string
28+ - New format: 'attributes' as a dict
29+ """
30+ # New format: attributes is already a dict
31+ if 'attributes' in span and isinstance (span ['attributes' ], dict ):
32+ return span ['attributes' ]
33+ # Old format: Attributes is a JSON string
2934 attributes_str = span .get ('Attributes' , '{}' )
3035 try :
3136 return json .loads (attributes_str )
3237 except json .JSONDecodeError :
3338 return {}
3439
35-
3640def matches_value (expected_value : Any , actual_value : Any ) -> bool :
3741 """
3842 Check if an actual value matches the expected value.
39-
4043 Supports:
4144 - List of possible values: ["value1", "value2"]
4245 - Wildcard: "*" (any value accepted)
@@ -45,32 +48,38 @@ def matches_value(expected_value: Any, actual_value: Any) -> bool:
4548 # Wildcard - accept any value
4649 if expected_value == "*" :
4750 return True
48-
4951 # List of possible values
5052 if isinstance (expected_value , list ):
5153 return actual_value in expected_value
52-
5354 # Exact match
5455 return expected_value == actual_value
5556
56-
5757def matches_expected (span : Dict [str , Any ], expected : Dict [str , Any ]) -> bool :
58- """Check if a span matches the expected definition."""
58+ """
59+ Check if a span matches the expected definition.
60+ Supports both formats:
61+ - Old format: 'Name', 'SpanType' fields
62+ - New format: 'name', 'attributes.span_type' fields
63+ """
5964 # Check name - can be a string or list of possible names
6065 expected_name = expected .get ('name' )
61- actual_name = span . get ( ' Name' )
62-
66+ # Support both old format ( Name) and new format (name )
67+ actual_name = span . get ( 'name' ) or span . get ( 'Name' )
6368 if isinstance (expected_name , list ):
6469 if actual_name not in expected_name :
6570 return False
6671 elif expected_name != actual_name :
6772 return False
68-
6973 # Check span type if specified
7074 if 'span_type' in expected :
71- if span .get ('SpanType' ) != expected ['span_type' ]:
75+ # Old format: SpanType field
76+ # New format: attributes.span_type field
77+ actual_span_type = span .get ('SpanType' )
78+ if not actual_span_type :
79+ actual_attrs = get_attributes (span )
80+ actual_span_type = actual_attrs .get ('span_type' )
81+ if actual_span_type != expected ['span_type' ]:
7282 return False
73-
7483 # Check attributes if specified
7584 if 'attributes' in expected :
7685 actual_attrs = get_attributes (span )
@@ -80,29 +89,22 @@ def matches_expected(span: Dict[str, Any], expected: Dict[str, Any]) -> bool:
8089 # Use flexible value matching
8190 if not matches_value (expected_value , actual_attrs [key ]):
8291 return False
83-
8492 return True
8593
86-
8794def assert_traces (traces_file : str , expected_file : str ) -> None :
8895 """
8996 Assert that all expected traces exist in the traces file.
90-
9197 Args:
9298 traces_file: Path to the traces.jsonl file
9399 expected_file: Path to the expected_traces.json file
94-
95100 Raises:
96101 AssertionError: If any expected trace is not found
97102 """
98103 traces = load_traces (traces_file )
99104 expected_spans = load_expected_traces (expected_file )
100-
101105 print (f"Loaded { len (traces )} traces from { traces_file } " )
102106 print (f"Checking { len (expected_spans )} expected spans..." )
103-
104107 missing_spans = []
105-
106108 for expected in expected_spans :
107109 # Find a matching span
108110 found = False
@@ -111,20 +113,12 @@ def assert_traces(traces_file: str, expected_file: str) -> None:
111113 found = True
112114 print (f"✓ Found span: { expected ['name' ]} " )
113115 break
114-
115116 if not found :
116117 missing_spans .append (expected ['name' ])
117118 print (f"✗ Missing span: { expected ['name' ]} " )
118-
119119 if missing_spans :
120120 raise AssertionError (
121121 f"Missing expected spans: { ', ' .join (missing_spans )} \n "
122122 f"Expected { len (expected_spans )} spans, found { len (expected_spans ) - len (missing_spans )} "
123123 )
124-
125124 print (f"\n ✓ All { len (expected_spans )} expected spans found!" )
126-
127-
128- if __name__ == "__main__" :
129- # Example usage
130- assert_traces (".uipath/traces.jsonl" , "expected_traces.json" )
0 commit comments