microsoft/teams.net

Public

mirrored fromhttps://github.com/microsoft/teams.netAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
samples/migration-bot

Branches

Tags

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

Clone

HTTPS

Download ZIP

core/test/Microsoft.Teams.Bot.Compat.UnitTests/CompatConversationsTests.cs

387lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4using Microsoft.Bot.Schema;
5using Microsoft.Teams.Bot.Core;
6using Microsoft.Teams.Bot.Core.Schema;
7using Moq;
8
9namespace Microsoft.Teams.Bot.Compat.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.Properties["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<Dictionary<string, string>>(),
181 It.IsAny<CancellationToken>()))
182 .Callback<string, string, CoreActivity, Dictionary<string, string>?, CancellationToken>((_, _, act, _, _) => capturedActivity = act)
183 .ReturnsAsync(new UpdateActivityResponse { Id = TestActivityId });
184
185 // Act
186 await compatConversations.UpdateActivityWithHttpMessagesAsync(TestConversationId, TestActivityId, activity);
187
188 // Assert
189 Assert.NotNull(capturedActivity);
190 Assert.NotNull(capturedActivity.ServiceUrl);
191 Assert.Equal(TestServiceUrl.TrimEnd('/'), capturedActivity.ServiceUrl.ToString().TrimEnd('/'));
192 mockConversationClient.Verify(
193 c => c.UpdateActivityAsync(
194 TestConversationId,
195 TestActivityId,
196 It.IsAny<CoreActivity>(),
197 It.IsAny<Dictionary<string, string>>(),
198 It.IsAny<CancellationToken>()),
199 Times.Once);
200 }
201
202 [Fact]
203 public async Task UpdateActivityWithHttpMessagesAsync_DoesNotOverrideServiceUrl_WhenActivityServiceUrlIsSet()
204 {
205 // Arrange
206 const string activityServiceUrl = "https://custom.service.url/";
207 Mock<ConversationClient> mockConversationClient = CreateMockConversationClient();
208 CompatConversations compatConversations = new(mockConversationClient.Object)
209 {
210 ServiceUrl = TestServiceUrl
211 };
212
213 Activity activity = new()
214 {
215 Type = ActivityTypes.Message,
216 Text = "Updated message",
217 ServiceUrl = activityServiceUrl
218 };
219
220 CoreActivity? capturedActivity = null;
221 mockConversationClient
222 .Setup(c => c.UpdateActivityAsync(
223 It.IsAny<string>(),
224 It.IsAny<string>(),
225 It.IsAny<CoreActivity>(),
226 It.IsAny<Dictionary<string, string>>(),
227 It.IsAny<CancellationToken>()))
228 .Callback<string, string, CoreActivity, Dictionary<string, string>?, CancellationToken>((_, _, act, _, _) => capturedActivity = act)
229 .ReturnsAsync(new UpdateActivityResponse { Id = TestActivityId });
230
231 // Act
232 await compatConversations.UpdateActivityWithHttpMessagesAsync(TestConversationId, TestActivityId, activity);
233
234 // Assert
235 Assert.NotNull(capturedActivity);
236 Assert.NotNull(capturedActivity.ServiceUrl);
237 Assert.Equal(activityServiceUrl.TrimEnd('/'), capturedActivity.ServiceUrl.ToString().TrimEnd('/'));
238 mockConversationClient.Verify(
239 c => c.UpdateActivityAsync(
240 TestConversationId,
241 TestActivityId,
242 It.IsAny<CoreActivity>(),
243 It.IsAny<Dictionary<string, string>>(),
244 It.IsAny<CancellationToken>()),
245 Times.Once);
246 }
247
248 [Fact]
249 public async Task SendToConversationWithHttpMessagesAsync_EnsuresConversationIdIsSet()
250 {
251 // Arrange
252 Mock<ConversationClient> mockConversationClient = CreateMockConversationClient();
253 CompatConversations compatConversations = new(mockConversationClient.Object)
254 {
255 ServiceUrl = TestServiceUrl
256 };
257
258 Activity activity = new()
259 {
260 Type = ActivityTypes.Message,
261 Text = "Test message"
262 };
263
264 CoreActivity? capturedActivity = null;
265 mockConversationClient
266 .Setup(c => c.SendActivityAsync(It.IsAny<CoreActivity>(), It.IsAny<Dictionary<string, string>>(), It.IsAny<CancellationToken>()))
267 .Callback<CoreActivity, Dictionary<string, string>?, CancellationToken>((act, _, _) => capturedActivity = act)
268 .ReturnsAsync(new SendActivityResponse { Id = TestActivityId });
269
270 // Act
271 await compatConversations.SendToConversationWithHttpMessagesAsync(TestConversationId, activity);
272
273 // Assert
274 Assert.NotNull(capturedActivity);
275 Assert.NotNull(capturedActivity.Conversation);
276 Assert.Equal(TestConversationId, capturedActivity.Conversation.Id);
277 }
278
279 [Fact]
280 public async Task ReplyToActivityWithHttpMessagesAsync_SetsReplyToIdProperty()
281 {
282 // Arrange
283 Mock<ConversationClient> mockConversationClient = CreateMockConversationClient();
284 CompatConversations compatConversations = new(mockConversationClient.Object)
285 {
286 ServiceUrl = TestServiceUrl
287 };
288
289 Activity activity = new()
290 {
291 Type = ActivityTypes.Message,
292 Text = "Test reply"
293 };
294
295 CoreActivity? capturedActivity = null;
296 mockConversationClient
297 .Setup(c => c.SendActivityAsync(It.IsAny<CoreActivity>(), It.IsAny<Dictionary<string, string>>(), It.IsAny<CancellationToken>()))
298 .Callback<CoreActivity, Dictionary<string, string>?, CancellationToken>((act, _, _) => capturedActivity = act)
299 .ReturnsAsync(new SendActivityResponse { Id = TestActivityId });
300
301 // Act
302 await compatConversations.ReplyToActivityWithHttpMessagesAsync(TestConversationId, "parent-activity-id", activity);
303
304 // Assert
305 Assert.NotNull(capturedActivity);
306 Assert.True(capturedActivity.Properties.ContainsKey("replyToId"));
307 Assert.Equal("parent-activity-id", capturedActivity.Properties["replyToId"]);
308 Assert.NotNull(capturedActivity.Conversation);
309 Assert.Equal(TestConversationId, capturedActivity.Conversation.Id);
310 }
311
312 [Fact]
313 public async Task SendToConversationWithHttpMessagesAsync_WhenSendActivityReturnsNull_ReturnsStringEmptyForId()
314 {
315 // This test verifies the fix for the OAuth card null reference bug
316 // When APX returns 202 Accepted with no body, SendActivityAsync returns null
317 // We should return string.Empty for Id instead of null to maintain API contract
318
319 // Arrange
320 Mock<ConversationClient> mockConversationClient = CreateMockConversationClient();
321 mockConversationClient
322 .Setup(c => c.SendActivityAsync(It.IsAny<CoreActivity>(), It.IsAny<Dictionary<string, string>>(), It.IsAny<CancellationToken>()))
323 .ReturnsAsync((SendActivityResponse?)null); // Simulate 202 Accepted with no body
324
325 CompatConversations compatConversations = new(mockConversationClient.Object)
326 {
327 ServiceUrl = TestServiceUrl
328 };
329
330 Activity activity = new()
331 {
332 Type = ActivityTypes.Message,
333 Text = "Test message"
334 };
335
336 // Act
337 var result = await compatConversations.SendToConversationWithHttpMessagesAsync(TestConversationId, activity);
338
339 // Assert
340 Assert.NotNull(result);
341 Assert.NotNull(result.Body);
342 Assert.Equal(string.Empty, result.Body.Id); // Should be string.Empty, not null
343 }
344
345 [Fact]
346 public async Task ReplyToActivityWithHttpMessagesAsync_WhenSendActivityReturnsNull_ReturnsStringEmptyForId()
347 {
348 // This test verifies the fix for the OAuth card null reference bug in ReplyToActivity
349 // When APX returns 202 Accepted with no body, SendActivityAsync returns null
350 // We should return string.Empty for Id instead of null to maintain API contract
351
352 // Arrange
353 Mock<ConversationClient> mockConversationClient = CreateMockConversationClient();
354 mockConversationClient
355 .Setup(c => c.SendActivityAsync(It.IsAny<CoreActivity>(), It.IsAny<Dictionary<string, string>>(), It.IsAny<CancellationToken>()))
356 .ReturnsAsync((SendActivityResponse?)null); // Simulate 202 Accepted with no body
357
358 CompatConversations compatConversations = new(mockConversationClient.Object)
359 {
360 ServiceUrl = TestServiceUrl
361 };
362
363 Activity activity = new()
364 {
365 Type = ActivityTypes.Message,
366 Text = "Test reply"
367 };
368
369 // Act
370 var result = await compatConversations.ReplyToActivityWithHttpMessagesAsync(TestConversationId, TestActivityId, activity);
371
372 // Assert
373 Assert.NotNull(result);
374 Assert.NotNull(result.Body);
375 Assert.Equal(string.Empty, result.Body.Id); // Should be string.Empty, not null
376 }
377
378 private static Mock<ConversationClient> CreateMockConversationClient()
379 {
380 Mock<ConversationClient> mock = new(
381 Mock.Of<HttpClient>(),
382 null!);
383
384 return mock;
385 }
386 }
387}
388