microsoft/teams.net

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
cg/sovereign-cloud-nextcore

Branches

Tags

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

Clone

HTTPS

Download ZIP

core/test/Microsoft.Teams.Bot.Core.UnitTests/MiddlewareTests.cs

232lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4using System.Text;
5using Microsoft.AspNetCore.Http;
6using Microsoft.Extensions.Configuration;
7using Microsoft.Extensions.Logging.Abstractions;
8using Microsoft.Teams.Bot.Core.Schema;
9using Moq;
10
11namespace Microsoft.Teams.Bot.Core.UnitTests;
12
13public class MiddlewareTests
14{
15 [Fact]
16 public async Task BotApplication_Use_AddsMiddlewareToChain()
17 {
18 BotApplication botApp = CreateBotApplication();
19
20 Mock<ITurnMiddleware> mockMiddleware = new();
21
22 ITurnMiddleware result = botApp.UseMiddleware(mockMiddleware.Object);
23
24 Assert.NotNull(result);
25 }
26
27
28 [Fact]
29 public async Task Middleware_ExecutesInOrder()
30 {
31 BotApplication botApp = CreateBotApplication();
32
33 List<int> executionOrder = [];
34
35 Mock<ITurnMiddleware> mockMiddleware1 = new();
36 mockMiddleware1
37 .Setup(m => m.OnTurnAsync(It.IsAny<BotApplication>(), It.IsAny<CoreActivity>(), It.IsAny<NextTurn>(), It.IsAny<CancellationToken>()))
38 .Callback<BotApplication, CoreActivity, NextTurn, CancellationToken>(async (app, act, next, ct) =>
39 {
40 executionOrder.Add(1);
41 await next(ct);
42 })
43 .Returns(Task.CompletedTask);
44
45 Mock<ITurnMiddleware> mockMiddleware2 = new();
46 mockMiddleware2
47 .Setup(m => m.OnTurnAsync(It.IsAny<BotApplication>(), It.IsAny<CoreActivity>(), It.IsAny<NextTurn>(), It.IsAny<CancellationToken>()))
48 .Callback<BotApplication, CoreActivity, NextTurn, CancellationToken>(async (app, act, next, ct) =>
49 {
50 executionOrder.Add(2);
51 await next(ct);
52 })
53 .Returns(Task.CompletedTask);
54
55 botApp.UseMiddleware(mockMiddleware1.Object);
56 botApp.UseMiddleware(mockMiddleware2.Object);
57
58 CoreActivity activity = new()
59 {
60 Type = ActivityType.Message,
61 Id = "act123"
62 };
63 if (activity.Recipient is not null)
64 {
65 activity.Recipient.Properties["appId"] = "test-app-id";
66 }
67
68 DefaultHttpContext httpContext = CreateHttpContextWithActivity(activity);
69
70 botApp.OnActivity = (act, ct) =>
71 {
72 executionOrder.Add(3);
73 return Task.CompletedTask;
74 };
75
76 await botApp.ProcessAsync(httpContext);
77 int[] expected = [1, 2, 3];
78 Assert.Equal(expected, executionOrder);
79 }
80
81 [Fact]
82 public async Task Middleware_CanShortCircuit()
83 {
84 BotApplication botApp = CreateBotApplication();
85
86 bool secondMiddlewareCalled = false;
87 bool onActivityCalled = false;
88
89 Mock<ITurnMiddleware> mockMiddleware1 = new();
90 mockMiddleware1
91 .Setup(m => m.OnTurnAsync(It.IsAny<BotApplication>(), It.IsAny<CoreActivity>(), It.IsAny<NextTurn>(), It.IsAny<CancellationToken>()))
92 .Returns(Task.CompletedTask); // Don't call next
93
94 Mock<ITurnMiddleware> mockMiddleware2 = new();
95 mockMiddleware2
96 .Setup(m => m.OnTurnAsync(It.IsAny<BotApplication>(), It.IsAny<CoreActivity>(), It.IsAny<NextTurn>(), It.IsAny<CancellationToken>()))
97 .Callback(() => secondMiddlewareCalled = true)
98 .Returns(Task.CompletedTask);
99
100 botApp.UseMiddleware(mockMiddleware1.Object);
101 botApp.UseMiddleware(mockMiddleware2.Object);
102
103 CoreActivity activity = new()
104 {
105 Type = ActivityType.Message,
106 Id = "act123"
107 };
108 if (activity.Recipient is not null)
109 {
110 activity.Recipient.Properties["appId"] = "test-app-id";
111 }
112
113 DefaultHttpContext httpContext = CreateHttpContextWithActivity(activity);
114
115 botApp.OnActivity = (act, ct) =>
116 {
117 onActivityCalled = true;
118 return Task.CompletedTask;
119 };
120
121 await botApp.ProcessAsync(httpContext);
122
123 Assert.False(secondMiddlewareCalled);
124 Assert.False(onActivityCalled);
125 }
126
127 [Fact]
128 public async Task Middleware_ReceivesCancellationToken()
129 {
130 BotApplication botApp = CreateBotApplication();
131
132 CancellationToken receivedToken = default;
133
134 Mock<ITurnMiddleware> mockMiddleware = new();
135 mockMiddleware
136 .Setup(m => m.OnTurnAsync(It.IsAny<BotApplication>(), It.IsAny<CoreActivity>(), It.IsAny<NextTurn>(), It.IsAny<CancellationToken>()))
137 .Callback<BotApplication, CoreActivity, NextTurn, CancellationToken>(async (app, act, next, ct) =>
138 {
139 receivedToken = ct;
140 await next(ct);
141 })
142 .Returns(Task.CompletedTask);
143
144 botApp.UseMiddleware(mockMiddleware.Object);
145
146 CoreActivity activity = new()
147 {
148 Type = ActivityType.Message,
149 Id = "act123"
150 };
151
152 if (activity.Recipient is not null)
153 {
154 activity.Recipient.Properties["appId"] = "test-app-id";
155 }
156
157 DefaultHttpContext httpContext = CreateHttpContextWithActivity(activity);
158
159 await botApp.ProcessAsync(httpContext);
160
161 // ProcessAsync creates its own timeout-based CancellationToken instead of
162 // forwarding the caller's token, so the middleware should receive a valid
163 // (non-default) token that is not yet cancelled.
164 Assert.NotEqual(default, receivedToken);
165 Assert.False(receivedToken.IsCancellationRequested);
166 }
167
168 [Fact]
169 public async Task Middleware_ReceivesActivity()
170 {
171 BotApplication botApp = CreateBotApplication();
172
173 CoreActivity? receivedActivity = null;
174
175 Mock<ITurnMiddleware> mockMiddleware = new();
176 mockMiddleware
177 .Setup(m => m.OnTurnAsync(It.IsAny<BotApplication>(), It.IsAny<CoreActivity>(), It.IsAny<NextTurn>(), It.IsAny<CancellationToken>()))
178 .Callback<BotApplication, CoreActivity, NextTurn, CancellationToken>(async (app, act, next, ct) =>
179 {
180 receivedActivity = act;
181 await next(ct);
182 })
183 .Returns(Task.CompletedTask);
184
185 botApp.UseMiddleware(mockMiddleware.Object);
186
187 CoreActivity activity = new()
188 {
189 Type = ActivityType.Message,
190 Id = "act123"
191 };
192
193 if (activity.Recipient is not null)
194 {
195 activity.Recipient.Properties["appId"] = "test-app-id";
196 }
197
198 DefaultHttpContext httpContext = CreateHttpContextWithActivity(activity);
199
200 await botApp.ProcessAsync(httpContext);
201
202 Assert.NotNull(receivedActivity);
203 Assert.Equal(ActivityType.Message, receivedActivity.Type);
204 }
205
206 private static BotApplication CreateBotApplication() =>
207 new(CreateMockConversationClient(), CreateMockUserTokenClient(), NullLogger<BotApplication>.Instance);
208
209 private static ConversationClient CreateMockConversationClient()
210 {
211 Mock<HttpClient> mockHttpClient = new();
212 return new ConversationClient(mockHttpClient.Object);
213 }
214
215 private static UserTokenClient CreateMockUserTokenClient()
216 {
217 Mock<HttpClient> mockHttpClient = new();
218 Mock<IConfiguration> mockConfig = new();
219 NullLogger<UserTokenClient> logger = NullLogger<UserTokenClient>.Instance;
220 return new UserTokenClient(mockHttpClient.Object, mockConfig.Object, logger);
221 }
222
223 private static DefaultHttpContext CreateHttpContextWithActivity(CoreActivity activity)
224 {
225 DefaultHttpContext httpContext = new();
226 string activityJson = activity.ToJson();
227 byte[] bodyBytes = Encoding.UTF8.GetBytes(activityJson);
228 httpContext.Request.Body = new MemoryStream(bodyBytes);
229 httpContext.Request.ContentType = "application/json";
230 return httpContext;
231 }
232}
233