microsoft/teams.net

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
cg/integration-test-caching

Branches

Tags

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

Clone

HTTPS

Download ZIP

core/test/Microsoft.Teams.Apps.UnitTests/HandlerStatePropagationTests.cs

566lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4using System.Text.Json.Nodes;
5using Microsoft.AspNetCore.Http;
6using Microsoft.Extensions.Configuration;
7using Microsoft.Extensions.Logging.Abstractions;
8using Microsoft.Teams.Apps.Api.Clients;
9using Microsoft.Teams.Apps.Handlers;
10using Microsoft.Teams.Apps.Handlers.MessageExtension;
11using Microsoft.Teams.Apps.Handlers.TaskModules;
12using Microsoft.Teams.Apps.Schema;
13using Microsoft.Teams.Apps.State;
14using Microsoft.Teams.Core;
15using Moq;
16
17namespace Microsoft.Teams.Apps.UnitTests;
18
19/// <summary>
20/// Verifies that all handler extension methods propagate State from the
21/// original context to the typed context passed to the user's handler,
22/// and that handlers work without state configured.
23/// </summary>
24public class HandlerStatePropagationTests
25{
26 // ===== Invoke handlers with state =====
27
28 [Fact]
29 public async Task OnAdaptiveCardAction_PropagatesState()
30 {
31 TeamsBotApplication app = CreateApp();
32 TurnStateContainer? captured = null;
33
34 app.OnAdaptiveCardAction((ctx, _) =>
35 {
36 captured = ctx.State;
37 return Task.FromResult(new InvokeResponse(200));
38 });
39
40 Context<TeamsActivity> context = CreateInvokeContext(app, InvokeNames.AdaptiveCardAction);
41 TurnStateContainer state = CreateState();
42 context.State = state;
43
44 await app.Router.DispatchWithReturnAsync(context);
45
46 Assert.NotNull(captured);
47 Assert.Same(state, captured);
48 }
49
50 [Fact]
51 public async Task OnFileConsent_PropagatesState()
52 {
53 TeamsBotApplication app = CreateApp();
54 TurnStateContainer? captured = null;
55
56 app.OnFileConsent((ctx, _) =>
57 {
58 captured = ctx.State;
59 return Task.FromResult(new InvokeResponse<AdaptiveCardResponse>(200));
60 });
61
62 Context<TeamsActivity> context = CreateInvokeContext(app, InvokeNames.FileConsent);
63 TurnStateContainer state = CreateState();
64 context.State = state;
65
66 await app.Router.DispatchWithReturnAsync(context);
67
68 Assert.NotNull(captured);
69 Assert.Same(state, captured);
70 }
71
72 [Fact]
73 public async Task OnQuery_PropagatesState()
74 {
75 TeamsBotApplication app = CreateApp();
76 TurnStateContainer? captured = null;
77
78 app.OnQuery((ctx, _) =>
79 {
80 captured = ctx.State;
81 return Task.FromResult(new InvokeResponse<MessageExtensionResponse>(200));
82 });
83
84 Context<TeamsActivity> context = CreateInvokeContext(app, InvokeNames.MessageExtensionQuery);
85 TurnStateContainer state = CreateState();
86 context.State = state;
87
88 await app.Router.DispatchWithReturnAsync(context);
89
90 Assert.NotNull(captured);
91 Assert.Same(state, captured);
92 }
93
94 [Fact]
95 public async Task OnSubmitAction_PropagatesState()
96 {
97 TeamsBotApplication app = CreateApp();
98 TurnStateContainer? captured = null;
99
100 app.OnSubmitAction((ctx, _) =>
101 {
102 captured = ctx.State;
103 return Task.FromResult(new InvokeResponse<MessageExtensionActionResponse>(200));
104 });
105
106 Context<TeamsActivity> context = CreateInvokeContext(app, InvokeNames.MessageExtensionSubmitAction);
107 TurnStateContainer state = CreateState();
108 context.State = state;
109
110 await app.Router.DispatchWithReturnAsync(context);
111
112 Assert.NotNull(captured);
113 Assert.Same(state, captured);
114 }
115
116 [Fact]
117 public async Task OnQueryLink_PropagatesState()
118 {
119 TeamsBotApplication app = CreateApp();
120 TurnStateContainer? captured = null;
121
122 app.OnQueryLink((ctx, _) =>
123 {
124 captured = ctx.State;
125 return Task.FromResult(new InvokeResponse<MessageExtensionResponse>(200));
126 });
127
128 Context<TeamsActivity> context = CreateInvokeContext(app, InvokeNames.MessageExtensionQueryLink);
129 TurnStateContainer state = CreateState();
130 context.State = state;
131
132 await app.Router.DispatchWithReturnAsync(context);
133
134 Assert.NotNull(captured);
135 Assert.Same(state, captured);
136 }
137
138 [Fact]
139 public async Task OnAnonQueryLink_PropagatesState()
140 {
141 TeamsBotApplication app = CreateApp();
142 TurnStateContainer? captured = null;
143
144 app.OnAnonQueryLink((ctx, _) =>
145 {
146 captured = ctx.State;
147 return Task.FromResult(new InvokeResponse<MessageExtensionResponse>(200));
148 });
149
150 Context<TeamsActivity> context = CreateInvokeContext(app, InvokeNames.MessageExtensionAnonQueryLink);
151 TurnStateContainer state = CreateState();
152 context.State = state;
153
154 await app.Router.DispatchWithReturnAsync(context);
155
156 Assert.NotNull(captured);
157 Assert.Same(state, captured);
158 }
159
160 [Fact]
161 public async Task OnFetchTask_PropagatesState()
162 {
163 TeamsBotApplication app = CreateApp();
164 TurnStateContainer? captured = null;
165
166 app.OnFetchTask((ctx, _) =>
167 {
168 captured = ctx.State;
169 return Task.FromResult(new InvokeResponse<MessageExtensionActionResponse>(200));
170 });
171
172 Context<TeamsActivity> context = CreateInvokeContext(app, InvokeNames.MessageExtensionFetchTask);
173 TurnStateContainer state = CreateState();
174 context.State = state;
175
176 await app.Router.DispatchWithReturnAsync(context);
177
178 Assert.NotNull(captured);
179 Assert.Same(state, captured);
180 }
181
182 [Fact]
183 public async Task OnSelectItem_PropagatesState()
184 {
185 TeamsBotApplication app = CreateApp();
186 TurnStateContainer? captured = null;
187
188 app.OnSelectItem((ctx, _) =>
189 {
190 captured = ctx.State;
191 return Task.FromResult(new InvokeResponse<MessageExtensionResponse>(200));
192 });
193
194 Context<TeamsActivity> context = CreateInvokeContext(app, InvokeNames.MessageExtensionSelectItem);
195 TurnStateContainer state = CreateState();
196 context.State = state;
197
198 await app.Router.DispatchWithReturnAsync(context);
199
200 Assert.NotNull(captured);
201 Assert.Same(state, captured);
202 }
203
204 [Fact]
205 public async Task OnQuerySettingUrl_PropagatesState()
206 {
207 TeamsBotApplication app = CreateApp();
208 TurnStateContainer? captured = null;
209
210 app.OnQuerySettingUrl((ctx, _) =>
211 {
212 captured = ctx.State;
213 return Task.FromResult(new InvokeResponse<MessageExtensionResponse>(200));
214 });
215
216 Context<TeamsActivity> context = CreateInvokeContext(app, InvokeNames.MessageExtensionQuerySettingUrl);
217 TurnStateContainer state = CreateState();
218 context.State = state;
219
220 await app.Router.DispatchWithReturnAsync(context);
221
222 Assert.NotNull(captured);
223 Assert.Same(state, captured);
224 }
225
226 [Fact]
227 public async Task OnMessageFetchTask_PropagatesState()
228 {
229 TeamsBotApplication app = CreateApp();
230 TurnStateContainer? captured = null;
231
232 app.OnMessageFetchTask((ctx, _) =>
233 {
234 captured = ctx.State;
235 return Task.FromResult(new InvokeResponse<TaskModuleResponse>(200));
236 });
237
238 Context<TeamsActivity> context = CreateInvokeContext(app, InvokeNames.MessageFetchTask);
239 TurnStateContainer state = CreateState();
240 context.State = state;
241
242 await app.Router.DispatchWithReturnAsync(context);
243
244 Assert.NotNull(captured);
245 Assert.Same(state, captured);
246 }
247
248 [Fact]
249 public async Task OnMessageSubmitAction_PropagatesState()
250 {
251 TeamsBotApplication app = CreateApp();
252 TurnStateContainer? captured = null;
253
254 app.OnMessageSubmitAction((ctx, _) =>
255 {
256 captured = ctx.State;
257 return Task.FromResult(new InvokeResponse(200));
258 });
259
260 Context<TeamsActivity> context = CreateInvokeContext(app, InvokeNames.MessageSubmitAction);
261 TurnStateContainer state = CreateState();
262 context.State = state;
263
264 await app.Router.DispatchWithReturnAsync(context);
265
266 Assert.NotNull(captured);
267 Assert.Same(state, captured);
268 }
269
270 [Fact]
271 public async Task OnMessageSubmitFeedback_PropagatesState()
272 {
273 TeamsBotApplication app = CreateApp();
274 TurnStateContainer? captured = null;
275
276 app.OnMessageSubmitFeedback((ctx, _) =>
277 {
278 captured = ctx.State;
279 return Task.FromResult(new InvokeResponse(200));
280 });
281
282 InvokeActivity activity = new()
283 {
284 Type = TeamsActivityType.Invoke,
285 Name = InvokeNames.MessageSubmitAction,
286 Value = new JsonObject
287 {
288 ["actionName"] = "feedback",
289 ["actionValue"] = new JsonObject()
290 }
291 };
292
293 Context<TeamsActivity> context = new(app, activity);
294 TurnStateContainer state = CreateState();
295 context.State = state;
296
297 await app.Router.DispatchWithReturnAsync(context);
298
299 Assert.NotNull(captured);
300 Assert.Same(state, captured);
301 }
302
303 [Fact]
304 public async Task OnTaskFetch_PropagatesState()
305 {
306 TeamsBotApplication app = CreateApp();
307 TurnStateContainer? captured = null;
308
309 app.OnTaskFetch((ctx, _) =>
310 {
311 captured = ctx.State;
312 return Task.FromResult(new InvokeResponse<TaskModuleResponse>(200));
313 });
314
315 Context<TeamsActivity> context = CreateInvokeContext(app, InvokeNames.TaskFetch);
316 TurnStateContainer state = CreateState();
317 context.State = state;
318
319 await app.Router.DispatchWithReturnAsync(context);
320
321 Assert.NotNull(captured);
322 Assert.Same(state, captured);
323 }
324
325 [Fact]
326 public async Task OnTaskSubmit_PropagatesState()
327 {
328 TeamsBotApplication app = CreateApp();
329 TurnStateContainer? captured = null;
330
331 app.OnTaskSubmit((ctx, _) =>
332 {
333 captured = ctx.State;
334 return Task.FromResult(new InvokeResponse<TaskModuleResponse>(200));
335 });
336
337 Context<TeamsActivity> context = CreateInvokeContext(app, InvokeNames.TaskSubmit);
338 TurnStateContainer state = CreateState();
339 context.State = state;
340
341 await app.Router.DispatchWithReturnAsync(context);
342
343 Assert.NotNull(captured);
344 Assert.Same(state, captured);
345 }
346
347 // ===== Event handlers with state =====
348
349 [Fact]
350 public async Task OnMeetingStart_PropagatesState()
351 {
352 TeamsBotApplication app = CreateApp();
353 TurnStateContainer? captured = null;
354
355 app.OnMeetingStart((ctx, _) =>
356 {
357 captured = ctx.State;
358 return Task.CompletedTask;
359 });
360
361 Context<TeamsActivity> context = CreateEventContext(app, EventNames.MeetingStart);
362 TurnStateContainer state = CreateState();
363 context.State = state;
364
365 await app.Router.DispatchAsync(context);
366
367 Assert.NotNull(captured);
368 Assert.Same(state, captured);
369 }
370
371 [Fact]
372 public async Task OnMeetingEnd_PropagatesState()
373 {
374 TeamsBotApplication app = CreateApp();
375 TurnStateContainer? captured = null;
376
377 app.OnMeetingEnd((ctx, _) =>
378 {
379 captured = ctx.State;
380 return Task.CompletedTask;
381 });
382
383 Context<TeamsActivity> context = CreateEventContext(app, EventNames.MeetingEnd);
384 TurnStateContainer state = CreateState();
385 context.State = state;
386
387 await app.Router.DispatchAsync(context);
388
389 Assert.NotNull(captured);
390 Assert.Same(state, captured);
391 }
392
393 [Fact]
394 public async Task OnMeetingParticipantJoin_PropagatesState()
395 {
396 TeamsBotApplication app = CreateApp();
397 TurnStateContainer? captured = null;
398
399 app.OnMeetingParticipantJoin((ctx, _) =>
400 {
401 captured = ctx.State;
402 return Task.CompletedTask;
403 });
404
405 Context<TeamsActivity> context = CreateEventContext(app, EventNames.MeetingParticipantJoin);
406 TurnStateContainer state = CreateState();
407 context.State = state;
408
409 await app.Router.DispatchAsync(context);
410
411 Assert.NotNull(captured);
412 Assert.Same(state, captured);
413 }
414
415 [Fact]
416 public async Task OnMeetingParticipantLeave_PropagatesState()
417 {
418 TeamsBotApplication app = CreateApp();
419 TurnStateContainer? captured = null;
420
421 app.OnMeetingParticipantLeave((ctx, _) =>
422 {
423 captured = ctx.State;
424 return Task.CompletedTask;
425 });
426
427 Context<TeamsActivity> context = CreateEventContext(app, EventNames.MeetingParticipantLeave);
428 TurnStateContainer state = CreateState();
429 context.State = state;
430
431 await app.Router.DispatchAsync(context);
432
433 Assert.NotNull(captured);
434 Assert.Same(state, captured);
435 }
436
437 // ===== Without state (HasState guard) =====
438
439 [Fact]
440 public async Task OnAdaptiveCardAction_WorksWithoutState()
441 {
442 TeamsBotApplication app = CreateApp();
443 bool handlerCalled = false;
444
445 app.OnAdaptiveCardAction((ctx, _) =>
446 {
447 handlerCalled = true;
448 Assert.False(ctx.HasState);
449 return Task.FromResult(new InvokeResponse(200));
450 });
451
452 Context<TeamsActivity> context = CreateInvokeContext(app, InvokeNames.AdaptiveCardAction);
453 // No state set on context
454
455 await app.Router.DispatchWithReturnAsync(context);
456
457 Assert.True(handlerCalled);
458 }
459
460 [Fact]
461 public async Task OnMeetingStart_WorksWithoutState()
462 {
463 TeamsBotApplication app = CreateApp();
464 bool handlerCalled = false;
465
466 app.OnMeetingStart((ctx, _) =>
467 {
468 handlerCalled = true;
469 Assert.False(ctx.HasState);
470 return Task.CompletedTask;
471 });
472
473 Context<TeamsActivity> context = CreateEventContext(app, EventNames.MeetingStart);
474 // No state set on context
475
476 await app.Router.DispatchAsync(context);
477
478 Assert.True(handlerCalled);
479 }
480
481 [Fact]
482 public async Task OnQuery_WorksWithoutState()
483 {
484 TeamsBotApplication app = CreateApp();
485 bool handlerCalled = false;
486
487 app.OnQuery((ctx, _) =>
488 {
489 handlerCalled = true;
490 Assert.False(ctx.HasState);
491 return Task.FromResult(new InvokeResponse<MessageExtensionResponse>(200));
492 });
493
494 Context<TeamsActivity> context = CreateInvokeContext(app, InvokeNames.MessageExtensionQuery);
495 // No state set on context
496
497 await app.Router.DispatchWithReturnAsync(context);
498
499 Assert.True(handlerCalled);
500 }
501
502 [Fact]
503 public async Task OnTaskFetch_WorksWithoutState()
504 {
505 TeamsBotApplication app = CreateApp();
506 bool handlerCalled = false;
507
508 app.OnTaskFetch((ctx, _) =>
509 {
510 handlerCalled = true;
511 Assert.False(ctx.HasState);
512 return Task.FromResult(new InvokeResponse<TaskModuleResponse>(200));
513 });
514
515 Context<TeamsActivity> context = CreateInvokeContext(app, InvokeNames.TaskFetch);
516 // No state set on context
517
518 await app.Router.DispatchWithReturnAsync(context);
519
520 Assert.True(handlerCalled);
521 }
522
523 // ===== Helpers =====
524
525 private static TurnStateContainer CreateState()
526 {
527 TurnState convState = new();
528 convState.Set("test-key", "test-value");
529 return new TurnStateContainer(convState, new TurnState());
530 }
531
532 private static Context<TeamsActivity> CreateInvokeContext(TeamsBotApplication app, string invokeName)
533 {
534 InvokeActivity activity = new() { Type = TeamsActivityType.Invoke, Name = invokeName };
535 return new Context<TeamsActivity>(app, activity);
536 }
537
538 private static Context<TeamsActivity> CreateEventContext(TeamsBotApplication app, string eventName)
539 {
540 EventActivity activity = new() { Type = TeamsActivityType.Event, Name = eventName };
541 return new Context<TeamsActivity>(app, activity);
542 }
543
544 private static TeamsBotApplication CreateApp()
545 {
546 Mock<UserTokenClient> mockUserTokenClient = new(
547 new HttpClient(),
548 new Mock<IConfiguration>().Object,
549 NullLogger<UserTokenClient>.Instance);
550
551 Mock<ConversationClient> mockConversationClient = new(
552 new HttpClient(),
553 NullLogger<ConversationClient>.Instance);
554
555 ApiClient apiClient = new(
556 new HttpClient(),
557 mockConversationClient.Object,
558 mockUserTokenClient.Object);
559
560 return new TeamsBotApplication(
561 apiClient,
562 new HttpContextAccessor(),
563 NullLogger<TeamsBotApplication>.Instance,
564 new TeamsBotApplicationOptions { AppId = "test-app-id" });
565 }
566}
567