microsoft/teams.net

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
samples/repro-da

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

393lines · modecode

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