microsoft/teams.net

Public

mirrored from https://github.com/microsoft/teams.netAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
fix/msal-agentic-cache

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

core/test/Microsoft.Teams.Apps.BotBuilder.UnitTests/CompatConversationsTests.cs

394lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4using Microsoft.Bot.Schema;
5using Microsoft.Rest;
6using Microsoft.Teams.Core;
7using Microsoft.Teams.Core.Schema;
8using Moq;
9
10namespace Microsoft.Teams.Apps.BotBuilder.UnitTests
11{
12 public class CompatConversationsTests
13 {
14 private const string TestServiceUrl = "https://smba.trafficmanager.net/amer/";
15 private const string TestConversationId = "test-conversation-id";
16 private const string TestActivityId = "test-activity-id";
17
18 [Fact]
19 public async Task SendToConversationWithHttpMessagesAsync_SetsServiceUrlFromProperty_WhenActivityServiceUrlIsNull()
20 {
21 // Arrange
22 Mock<ConversationClient> mockConversationClient = CreateMockConversationClient();
23 CompatConversations compatConversations = new(mockConversationClient.Object)
24 {
25 ServiceUrl = TestServiceUrl
26 };
27
28 Activity activity = new()
29 {
30 Type = ActivityTypes.Message,
31 Text = "Test message"
32 };
33
34 CoreActivity? capturedActivity = null;
35 mockConversationClient
36 .Setup(c => c.SendActivityAsync(It.IsAny<CoreActivity>(), It.IsAny<Dictionary<string, string>?>(), It.IsAny<CancellationToken>()))
37 .Callback<CoreActivity, Dictionary<string, string>?, CancellationToken>((act, _, _) => capturedActivity = act)
38 .ReturnsAsync(new SendActivityResponse { Id = TestActivityId });
39
40 // Act
41 await compatConversations.SendToConversationWithHttpMessagesAsync(TestConversationId, activity);
42
43 // Assert
44 Assert.NotNull(capturedActivity);
45 Assert.NotNull(capturedActivity.ServiceUrl);
46 Assert.Equal(TestServiceUrl.TrimEnd('/'), capturedActivity.ServiceUrl.ToString().TrimEnd('/'));
47 mockConversationClient.Verify(
48 c => c.SendActivityAsync(It.IsAny<CoreActivity>(), It.IsAny<Dictionary<string, string>?>(), It.IsAny<CancellationToken>()),
49 Times.Once);
50 }
51
52 [Fact]
53 public async Task SendToConversationWithHttpMessagesAsync_DoesNotOverrideServiceUrl_WhenActivityServiceUrlIsSet()
54 {
55 // Arrange
56 const string activityServiceUrl = "https://custom.service.url/";
57 Mock<ConversationClient> mockConversationClient = CreateMockConversationClient();
58 CompatConversations compatConversations = new(mockConversationClient.Object)
59 {
60 ServiceUrl = TestServiceUrl
61 };
62
63 Activity activity = new()
64 {
65 Type = ActivityTypes.Message,
66 Text = "Test message",
67 ServiceUrl = activityServiceUrl
68 };
69
70 CoreActivity? capturedActivity = null;
71 mockConversationClient
72 .Setup(c => c.SendActivityAsync(It.IsAny<CoreActivity>(), It.IsAny<Dictionary<string, string>?>(), It.IsAny<CancellationToken>()))
73 .Callback<CoreActivity, Dictionary<string, string>?, CancellationToken>((act, _, _) => capturedActivity = act)
74 .ReturnsAsync(new SendActivityResponse { Id = TestActivityId });
75
76 // Act
77 await compatConversations.SendToConversationWithHttpMessagesAsync(TestConversationId, activity);
78
79 // Assert
80 Assert.NotNull(capturedActivity);
81 Assert.NotNull(capturedActivity.ServiceUrl);
82 Assert.Equal(activityServiceUrl.TrimEnd('/'), capturedActivity.ServiceUrl.ToString().TrimEnd('/'));
83 mockConversationClient.Verify(
84 c => c.SendActivityAsync(It.IsAny<CoreActivity>(), It.IsAny<Dictionary<string, string>?>(), It.IsAny<CancellationToken>()),
85 Times.Once);
86 }
87
88 [Fact]
89 public async Task ReplyToActivityWithHttpMessagesAsync_SetsServiceUrlFromProperty_WhenActivityServiceUrlIsNull()
90 {
91 // Arrange
92 Mock<ConversationClient> mockConversationClient = CreateMockConversationClient();
93 CompatConversations compatConversations = new(mockConversationClient.Object)
94 {
95 ServiceUrl = TestServiceUrl
96 };
97
98 Activity activity = new()
99 {
100 Type = ActivityTypes.Message,
101 Text = "Test reply"
102 };
103
104 CoreActivity? capturedActivity = null;
105 mockConversationClient
106 .Setup(c => c.SendActivityAsync(It.IsAny<CoreActivity>(), It.IsAny<Dictionary<string, string>?>(), It.IsAny<CancellationToken>()))
107 .Callback<CoreActivity, Dictionary<string, string>?, CancellationToken>((act, _, _) => capturedActivity = act)
108 .ReturnsAsync(new SendActivityResponse { Id = TestActivityId });
109
110 // Act
111 await compatConversations.ReplyToActivityWithHttpMessagesAsync(TestConversationId, TestActivityId, activity);
112
113 // Assert
114 Assert.NotNull(capturedActivity);
115 Assert.NotNull(capturedActivity.ServiceUrl);
116 Assert.Equal(TestServiceUrl.TrimEnd('/'), capturedActivity.ServiceUrl.ToString().TrimEnd('/'));
117 Assert.Equal(TestActivityId, capturedActivity.ReplyToId);
118 mockConversationClient.Verify(
119 c => c.SendActivityAsync(It.IsAny<CoreActivity>(), It.IsAny<Dictionary<string, string>?>(), It.IsAny<CancellationToken>()),
120 Times.Once);
121 }
122
123 [Fact]
124 public async Task ReplyToActivityWithHttpMessagesAsync_DoesNotOverrideServiceUrl_WhenActivityServiceUrlIsSet()
125 {
126 // Arrange
127 const string activityServiceUrl = "https://custom.service.url/";
128 Mock<ConversationClient> mockConversationClient = CreateMockConversationClient();
129 CompatConversations compatConversations = new(mockConversationClient.Object)
130 {
131 ServiceUrl = TestServiceUrl
132 };
133
134 Activity activity = new()
135 {
136 Type = ActivityTypes.Message,
137 Text = "Test reply",
138 ServiceUrl = activityServiceUrl
139 };
140
141 CoreActivity? capturedActivity = null;
142 mockConversationClient
143 .Setup(c => c.SendActivityAsync(It.IsAny<CoreActivity>(), It.IsAny<Dictionary<string, string>?>(), It.IsAny<CancellationToken>()))
144 .Callback<CoreActivity, Dictionary<string, string>?, CancellationToken>((act, _, _) => capturedActivity = act)
145 .ReturnsAsync(new SendActivityResponse { Id = TestActivityId });
146
147 // Act
148 await compatConversations.ReplyToActivityWithHttpMessagesAsync(TestConversationId, TestActivityId, activity);
149
150 // Assert
151 Assert.NotNull(capturedActivity);
152 Assert.NotNull(capturedActivity.ServiceUrl);
153 Assert.Equal(activityServiceUrl.TrimEnd('/'), capturedActivity.ServiceUrl.ToString().TrimEnd('/'));
154 mockConversationClient.Verify(
155 c => c.SendActivityAsync(It.IsAny<CoreActivity>(), It.IsAny<Dictionary<string, string>?>(), It.IsAny<CancellationToken>()),
156 Times.Once);
157 }
158
159 [Fact]
160 public async Task UpdateActivityWithHttpMessagesAsync_SetsServiceUrlFromProperty_WhenActivityServiceUrlIsNull()
161 {
162 // Arrange
163 Mock<ConversationClient> mockConversationClient = CreateMockConversationClient();
164 CompatConversations compatConversations = new(mockConversationClient.Object)
165 {
166 ServiceUrl = TestServiceUrl
167 };
168
169 Activity activity = new()
170 {
171 Type = ActivityTypes.Message,
172 Text = "Updated message"
173 };
174
175 CoreActivity? capturedActivity = null;
176 mockConversationClient
177 .Setup(c => c.UpdateActivityAsync(
178 It.IsAny<string>(),
179 It.IsAny<string>(),
180 It.IsAny<CoreActivity>(),
181 It.IsAny<bool>(),
182 It.IsAny<AgenticIdentity?>(),
183 It.IsAny<Dictionary<string, string>?>(),
184 It.IsAny<CancellationToken>()))
185 .Callback<string, string, CoreActivity, bool, AgenticIdentity?, Dictionary<string, string>?, CancellationToken>((_, _, act, _, _, _, _) => capturedActivity = act)
186 .ReturnsAsync(new UpdateActivityResponse { Id = TestActivityId });
187
188 // Act
189 await compatConversations.UpdateActivityWithHttpMessagesAsync(TestConversationId, TestActivityId, activity);
190
191 // Assert
192 Assert.NotNull(capturedActivity);
193 Assert.NotNull(capturedActivity.ServiceUrl);
194 Assert.Equal(TestServiceUrl.TrimEnd('/'), capturedActivity.ServiceUrl.ToString().TrimEnd('/'));
195 mockConversationClient.Verify(
196 c => c.UpdateActivityAsync(
197 TestConversationId,
198 TestActivityId,
199 It.IsAny<CoreActivity>(),
200 It.IsAny<bool>(),
201 It.IsAny<AgenticIdentity?>(),
202 It.IsAny<Dictionary<string, string>?>(),
203 It.IsAny<CancellationToken>()),
204 Times.Once);
205 }
206
207 [Fact]
208 public async Task UpdateActivityWithHttpMessagesAsync_DoesNotOverrideServiceUrl_WhenActivityServiceUrlIsSet()
209 {
210 // Arrange
211 const string activityServiceUrl = "https://custom.service.url/";
212 Mock<ConversationClient> mockConversationClient = CreateMockConversationClient();
213 CompatConversations compatConversations = new(mockConversationClient.Object)
214 {
215 ServiceUrl = TestServiceUrl
216 };
217
218 Activity activity = new()
219 {
220 Type = ActivityTypes.Message,
221 Text = "Updated message",
222 ServiceUrl = activityServiceUrl
223 };
224
225 CoreActivity? capturedActivity = null;
226 mockConversationClient
227 .Setup(c => c.UpdateActivityAsync(
228 It.IsAny<string>(),
229 It.IsAny<string>(),
230 It.IsAny<CoreActivity>(),
231 It.IsAny<bool>(),
232 It.IsAny<AgenticIdentity?>(),
233 It.IsAny<Dictionary<string, string>?>(),
234 It.IsAny<CancellationToken>()))
235 .Callback<string, string, CoreActivity, bool, AgenticIdentity?, Dictionary<string, string>?, CancellationToken>((_, _, act, _, _, _, _) => capturedActivity = act)
236 .ReturnsAsync(new UpdateActivityResponse { Id = TestActivityId });
237
238 // Act
239 await compatConversations.UpdateActivityWithHttpMessagesAsync(TestConversationId, TestActivityId, activity);
240
241 // Assert
242 Assert.NotNull(capturedActivity);
243 Assert.NotNull(capturedActivity.ServiceUrl);
244 Assert.Equal(activityServiceUrl.TrimEnd('/'), capturedActivity.ServiceUrl.ToString().TrimEnd('/'));
245 mockConversationClient.Verify(
246 c => c.UpdateActivityAsync(
247 TestConversationId,
248 TestActivityId,
249 It.IsAny<CoreActivity>(),
250 It.IsAny<bool>(),
251 It.IsAny<AgenticIdentity?>(),
252 It.IsAny<Dictionary<string, string>?>(),
253 It.IsAny<CancellationToken>()),
254 Times.Once);
255 }
256
257 [Fact]
258 public async Task SendToConversationWithHttpMessagesAsync_EnsuresConversationIdIsSet()
259 {
260 // Arrange
261 Mock<ConversationClient> mockConversationClient = CreateMockConversationClient();
262 CompatConversations compatConversations = new(mockConversationClient.Object)
263 {
264 ServiceUrl = TestServiceUrl
265 };
266
267 Activity activity = new()
268 {
269 Type = ActivityTypes.Message,
270 Text = "Test message"
271 };
272
273 CoreActivity? capturedActivity = null;
274 mockConversationClient
275 .Setup(c => c.SendActivityAsync(It.IsAny<CoreActivity>(), It.IsAny<Dictionary<string, string>?>(), It.IsAny<CancellationToken>()))
276 .Callback<CoreActivity, Dictionary<string, string>?, CancellationToken>((act, _, _) => capturedActivity = act)
277 .ReturnsAsync(new SendActivityResponse { Id = TestActivityId });
278
279 // Act
280 await compatConversations.SendToConversationWithHttpMessagesAsync(TestConversationId, activity);
281
282 // Assert
283 Assert.NotNull(capturedActivity?.Conversation);
284 Assert.Equal(TestConversationId, capturedActivity.Conversation.Id);
285 }
286
287 [Fact]
288 public async Task ReplyToActivityWithHttpMessagesAsync_SetsReplyToIdProperty()
289 {
290 // Arrange
291 Mock<ConversationClient> mockConversationClient = CreateMockConversationClient();
292 CompatConversations compatConversations = new(mockConversationClient.Object)
293 {
294 ServiceUrl = TestServiceUrl
295 };
296
297 Activity activity = new()
298 {
299 Type = ActivityTypes.Message,
300 Text = "Test reply"
301 };
302
303 CoreActivity? capturedActivity = null;
304 mockConversationClient
305 .Setup(c => c.SendActivityAsync(It.IsAny<CoreActivity>(), It.IsAny<Dictionary<string, string>?>(), It.IsAny<CancellationToken>()))
306 .Callback<CoreActivity, Dictionary<string, string>?, CancellationToken>((act, _, _) => capturedActivity = act)
307 .ReturnsAsync(new SendActivityResponse { Id = TestActivityId });
308
309 // Act
310 await compatConversations.ReplyToActivityWithHttpMessagesAsync(TestConversationId, "parent-activity-id", activity);
311
312 // Assert
313 Assert.NotNull(capturedActivity);
314 Assert.Equal("parent-activity-id", capturedActivity.ReplyToId);
315 Assert.NotNull(capturedActivity.Conversation);
316 Assert.Equal(TestConversationId, capturedActivity.Conversation.Id);
317 }
318
319 [Fact]
320 public async Task SendToConversationWithHttpMessagesAsync_WhenSendActivityReturnsNull_ReturnsStringEmptyForId()
321 {
322 // This test verifies the fix for the OAuth card null reference bug
323 // When APX returns 202 Accepted with no body, SendActivityAsync returns null
324 // We should return string.Empty for Id instead of null to maintain API contract
325
326 // Arrange
327 Mock<ConversationClient> mockConversationClient = CreateMockConversationClient();
328 mockConversationClient
329 .Setup(c => c.SendActivityAsync(It.IsAny<CoreActivity>(), It.IsAny<Dictionary<string, string>?>(), It.IsAny<CancellationToken>()))
330 .ReturnsAsync((SendActivityResponse?)null); // Simulate 202 Accepted with no body
331
332 CompatConversations compatConversations = new(mockConversationClient.Object)
333 {
334 ServiceUrl = TestServiceUrl
335 };
336
337 Activity activity = new()
338 {
339 Type = ActivityTypes.Message,
340 Text = "Test message"
341 };
342
343 // Act
344 HttpOperationResponse<ResourceResponse> result = await compatConversations.SendToConversationWithHttpMessagesAsync(TestConversationId, activity);
345
346 // Assert
347 Assert.NotNull(result);
348 Assert.NotNull(result.Body);
349 Assert.Equal(string.Empty, result.Body.Id); // Should be string.Empty, not null
350 }
351
352 [Fact]
353 public async Task ReplyToActivityWithHttpMessagesAsync_WhenSendActivityReturnsNull_ReturnsStringEmptyForId()
354 {
355 // This test verifies the fix for the OAuth card null reference bug in ReplyToActivity
356 // When APX returns 202 Accepted with no body, SendActivityAsync returns null
357 // We should return string.Empty for Id instead of null to maintain API contract
358
359 // Arrange
360 Mock<ConversationClient> mockConversationClient = CreateMockConversationClient();
361 mockConversationClient
362 .Setup(c => c.SendActivityAsync(It.IsAny<CoreActivity>(), It.IsAny<Dictionary<string, string>?>(), It.IsAny<CancellationToken>()))
363 .ReturnsAsync((SendActivityResponse?)null); // Simulate 202 Accepted with no body
364
365 CompatConversations compatConversations = new(mockConversationClient.Object)
366 {
367 ServiceUrl = TestServiceUrl
368 };
369
370 Activity activity = new()
371 {
372 Type = ActivityTypes.Message,
373 Text = "Test reply"
374 };
375
376 // Act
377 HttpOperationResponse<ResourceResponse> result = await compatConversations.ReplyToActivityWithHttpMessagesAsync(TestConversationId, TestActivityId, activity);
378
379 // Assert
380 Assert.NotNull(result);
381 Assert.NotNull(result.Body);
382 Assert.Equal(string.Empty, result.Body.Id); // Should be string.Empty, not null
383 }
384
385 private static Mock<ConversationClient> CreateMockConversationClient()
386 {
387 Mock<ConversationClient> mock = new(
388 Mock.Of<HttpClient>(),
389 null!);
390
391 return mock;
392 }
393 }
394}
395