Skip to content

Commit 3245d52

Browse files
authored
Refactor test and fix telemetry regression. (#53995)
* Refactor test and fix elemetry regression. * Fix
1 parent 596a73a commit 3245d52

File tree

8 files changed

+68
-97
lines changed

8 files changed

+68
-97
lines changed

sdk/ai/Azure.AI.Projects/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Release History
22

3+
## 1.2.0-beta.4 (Unreleaed)
4+
5+
### Bugs Fixed
6+
7+
- Fixed an issue preventing telemetry from being logged.
8+
39
## 1.2.0-beta.3 (2025-11-15)
410

511
### Bugs Fixed

sdk/ai/Azure.AI.Projects/assets.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22
"AssetsRepo": "Azure/azure-sdk-assets",
33
"AssetsRepoPrefixPath": "net",
44
"TagPrefix": "net/ai/Azure.AI.Projects",
5-
"Tag": "net/ai/Azure.AI.Projects_86e6a1c7b5"
5+
"Tag": "net/ai/Azure.AI.Projects_8ebce62e07"
66
}

sdk/ai/Azure.AI.Projects/src/Azure.AI.Projects.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<PropertyGroup>
33
<Description>This is the Azure.AI.Projects client library for developing .NET applications with rich experience.</Description>
44
<AssemblyTitle>Microsoft Azure.AI.Projects client library</AssemblyTitle>
5-
<Version>1.2.0-beta.3</Version>
5+
<Version>1.2.0-beta.4</Version>
66
<!--The ApiCompatVersion is managed automatically and should not generally be modified manually.-->
77
<ApiCompatVersion>1.1.0</ApiCompatVersion>
88
<PackageTags>Azure.AI.Projects</PackageTags>

sdk/ai/Azure.AI.Projects/src/Custom/Agents/AgentVersionCreationOptions.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,7 @@ public partial class AgentVersionCreationOptions
1919
public global::Azure.AI.Projects.OpenAI.AgentDefinition Definition { get; set; }
2020

2121
private static void DeserializeDefinitionValue(JsonProperty property, ref global::Azure.AI.Projects.OpenAI.AgentDefinition definition)
22-
=> CustomSerializationHelpers.DeserializeProjectOpenAIType<AgentDefinition>(property.Value, ModelSerializationExtensions.WireOptions);
22+
{
23+
definition = CustomSerializationHelpers.DeserializeProjectOpenAIType<AgentDefinition>(property.Value, ModelSerializationExtensions.WireOptions);
24+
}
2325
}

sdk/ai/Azure.AI.Projects/src/Custom/Telemetry/OpenTelemetryScope.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using System.Diagnostics;
99
using System.Diagnostics.Metrics;
1010
using System.IO;
11+
using System.Text;
1112
using System.Text.Encodings.Web;
1213
using System.Text.Json;
1314
using System.Text.Json.Serialization;

sdk/ai/Azure.AI.Projects/tests_agents/AgentsSmokeTests.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ public AgentsSmokeTests(bool isAsync) : base(isAsync, RecordedTestMode.Live)
2020
}
2121

2222
[Test]
23+
[LiveOnly]
2324
public void CanGetClients()
2425
{
2526
AIProjectClient projectClient = GetTestProjectClient();

sdk/ai/Azure.AI.Projects/tests_agents/AgentsTelemetryTests.cs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,12 @@
2121

2222
namespace Azure.AI.Projects.Tests;
2323

24-
[Ignore("Temporarily disabled pending post-packaging investigation of regressions")]
2524
public partial class AgentsTelemetryTests : AgentsTestBase
2625
{
2726
public const string TraceContentsEnvironmentVariable = "OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT";
2827
public const string EnableOpenTelemetryEnvironmentVariable = "AZURE_EXPERIMENTAL_ENABLE_ACTIVITY_SOURCE";
2928
private MemoryTraceExporter _exporter;
3029
private TracerProvider _tracerProvider;
31-
private GenAiTraceVerifier _traceVerifier;
3230
private bool _contentRecordingEnabledInitialValue = false;
3331
private bool _tracesEnabledInitialValue = false;
3432

@@ -40,7 +38,6 @@ public AgentsTelemetryTests(bool isAsync) : base(isAsync)
4038
public void Setup()
4139
{
4240
_exporter = new MemoryTraceExporter();
43-
_traceVerifier = new GenAiTraceVerifier();
4441

4542
_tracesEnabledInitialValue = string.Equals(
4643
Environment.GetEnvironmentVariable(TraceContentsEnvironmentVariable),
@@ -374,7 +371,7 @@ private void CheckCreateAgentTrace(Activity createAgentSpan, string modelName, s
374371
{ "gen_ai.agent.name", agentName },
375372
{ "gen_ai.agent.id", "*" }
376373
};
377-
Assert.That(_traceVerifier.CheckSpanAttributes(createAgentSpan, expectedCreateAgentAttributes), Is.True);
374+
GenAiTraceVerifier.ValidateSpanAttributes(createAgentSpan, expectedCreateAgentAttributes);
378375
var expectedCreateAgentEvents = new List<(string, Dictionary<string, object>)>
379376
{
380377
("gen_ai.system.message", new Dictionary<string, object>
@@ -383,7 +380,7 @@ private void CheckCreateAgentTrace(Activity createAgentSpan, string modelName, s
383380
{ "gen_ai.event.content", content }
384381
})
385382
};
386-
Assert.That(_traceVerifier.CheckSpanEvents(createAgentSpan, expectedCreateAgentEvents), Is.True);
383+
GenAiTraceVerifier.ValidateSpanEvents(createAgentSpan, expectedCreateAgentEvents);
387384
}
388385

389386
private void CheckCreateAgentVersionTrace(Activity createAgentSpan, string modelName, string agentName, string content)
@@ -400,7 +397,7 @@ private void CheckCreateAgentVersionTrace(Activity createAgentSpan, string model
400397
{ "gen_ai.agent.version", "1" },
401398
{ "gen_ai.agent.id", "*" }
402399
};
403-
Assert.That(_traceVerifier.CheckSpanAttributes(createAgentSpan, expectedCreateAgentAttributes), Is.True);
400+
GenAiTraceVerifier.ValidateSpanAttributes(createAgentSpan, expectedCreateAgentAttributes);
404401
var expectedCreateAgentEvents = new List<(string, Dictionary<string, object>)>
405402
{
406403
("gen_ai.system.message", new Dictionary<string, object>
@@ -409,7 +406,7 @@ private void CheckCreateAgentVersionTrace(Activity createAgentSpan, string model
409406
{ "gen_ai.event.content", content }
410407
})
411408
};
412-
Assert.That(_traceVerifier.CheckSpanEvents(createAgentSpan, expectedCreateAgentEvents), Is.True);
409+
GenAiTraceVerifier.ValidateSpanEvents(createAgentSpan, expectedCreateAgentEvents);
413410
}
414411

415412
private async Task WaitMayBe(int timeout = 1000)

sdk/ai/Azure.AI.Projects/tests_agents/Utilities/GenAITraceVerifier.cs

Lines changed: 51 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -6,149 +6,117 @@
66
using System.Diagnostics;
77
using System.Linq;
88
using System.Text.Json;
9+
using NUnit.Framework;
910

1011
namespace 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.\nExpected: {expectedKeys}\nActual: {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:\nActual {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}.\nExpected: {expectedProps}\nActual: {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}.\nExpected: {expectedItems}\nActual: {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

Comments
 (0)