// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System.Text;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Teams.Core.Schema;
using Moq;
namespace Microsoft.Teams.Core.UnitTests;
public class MiddlewareTests
{
[Fact]
public async Task BotApplication_Use_AddsMiddlewareToChain()
{
BotApplication botApp = CreateBotApplication();
Mock<ITurnMiddleware> mockMiddleware = new();
ITurnMiddleware result = botApp.UseMiddleware(mockMiddleware.Object);
Assert.NotNull(result);
}
[Fact]
public async Task Middleware_ExecutesInOrder()
{
BotApplication botApp = CreateBotApplication();
List<int> executionOrder = [];
Mock<ITurnMiddleware> mockMiddleware1 = new();
mockMiddleware1
.Setup(m => m.OnTurnAsync(It.IsAny<BotApplication>(), It.IsAny<CoreActivity>(), It.IsAny<NextTurn>(), It.IsAny<CancellationToken>()))
.Callback<BotApplication, CoreActivity, NextTurn, CancellationToken>(async (app, act, next, ct) =>
{
executionOrder.Add(1);
await next(ct);
})
.Returns(Task.CompletedTask);
Mock<ITurnMiddleware> mockMiddleware2 = new();
mockMiddleware2
.Setup(m => m.OnTurnAsync(It.IsAny<BotApplication>(), It.IsAny<CoreActivity>(), It.IsAny<NextTurn>(), It.IsAny<CancellationToken>()))
.Callback<BotApplication, CoreActivity, NextTurn, CancellationToken>(async (app, act, next, ct) =>
{
executionOrder.Add(2);
await next(ct);
})
.Returns(Task.CompletedTask);
botApp.UseMiddleware(mockMiddleware1.Object);
botApp.UseMiddleware(mockMiddleware2.Object);
CoreActivity activity = new()
{
Type = ActivityType.Message,
Id = "act123"
};
DefaultHttpContext httpContext = CreateHttpContextWithActivity(activity);
botApp.OnActivity = (act, ct) =>
{
executionOrder.Add(3);
return Task.CompletedTask;
};
await botApp.ProcessAsync(httpContext);
int[] expected = [1, 2, 3];
Assert.Equal(expected, executionOrder);
}
[Fact]
public async Task Middleware_CanShortCircuit()
{
BotApplication botApp = CreateBotApplication();
bool secondMiddlewareCalled = false;
bool onActivityCalled = false;
Mock<ITurnMiddleware> mockMiddleware1 = new();
mockMiddleware1
.Setup(m => m.OnTurnAsync(It.IsAny<BotApplication>(), It.IsAny<CoreActivity>(), It.IsAny<NextTurn>(), It.IsAny<CancellationToken>()))
.Returns(Task.CompletedTask); // Don't call next
Mock<ITurnMiddleware> mockMiddleware2 = new();
mockMiddleware2
.Setup(m => m.OnTurnAsync(It.IsAny<BotApplication>(), It.IsAny<CoreActivity>(), It.IsAny<NextTurn>(), It.IsAny<CancellationToken>()))
.Callback(() => secondMiddlewareCalled = true)
.Returns(Task.CompletedTask);
botApp.UseMiddleware(mockMiddleware1.Object);
botApp.UseMiddleware(mockMiddleware2.Object);
CoreActivity activity = new()
{
Type = ActivityType.Message,
Id = "act123"
};
DefaultHttpContext httpContext = CreateHttpContextWithActivity(activity);
botApp.OnActivity = (act, ct) =>
{
onActivityCalled = true;
return Task.CompletedTask;
};
await botApp.ProcessAsync(httpContext);
Assert.False(secondMiddlewareCalled);
Assert.False(onActivityCalled);
}
[Fact]
public async Task Middleware_ReceivesCancellationToken()
{
BotApplication botApp = CreateBotApplication();
CancellationToken receivedToken = default;
Mock<ITurnMiddleware> mockMiddleware = new();
mockMiddleware
.Setup(m => m.OnTurnAsync(It.IsAny<BotApplication>(), It.IsAny<CoreActivity>(), It.IsAny<NextTurn>(), It.IsAny<CancellationToken>()))
.Callback<BotApplication, CoreActivity, NextTurn, CancellationToken>(async (app, act, next, ct) =>
{
receivedToken = ct;
await next(ct);
})
.Returns(Task.CompletedTask);
botApp.UseMiddleware(mockMiddleware.Object);
CoreActivity activity = new()
{
Type = ActivityType.Message,
Id = "act123"
};
DefaultHttpContext httpContext = CreateHttpContextWithActivity(activity);
await botApp.ProcessAsync(httpContext);
// ProcessAsync creates its own timeout-based CancellationToken instead of
// forwarding the caller's token, so the middleware should receive a valid
// (non-default) token that is not yet cancelled.
Assert.NotEqual(default, receivedToken);
Assert.False(receivedToken.IsCancellationRequested);
}
[Fact]
public async Task Middleware_ReceivesActivity()
{
BotApplication botApp = CreateBotApplication();
CoreActivity? receivedActivity = null;
Mock<ITurnMiddleware> mockMiddleware = new();
mockMiddleware
.Setup(m => m.OnTurnAsync(It.IsAny<BotApplication>(), It.IsAny<CoreActivity>(), It.IsAny<NextTurn>(), It.IsAny<CancellationToken>()))
.Callback<BotApplication, CoreActivity, NextTurn, CancellationToken>(async (app, act, next, ct) =>
{
receivedActivity = act;
await next(ct);
})
.Returns(Task.CompletedTask);
botApp.UseMiddleware(mockMiddleware.Object);
CoreActivity activity = new()
{
Type = ActivityType.Message,
Id = "act123"
};
DefaultHttpContext httpContext = CreateHttpContextWithActivity(activity);
await botApp.ProcessAsync(httpContext);
Assert.NotNull(receivedActivity);
Assert.Equal(ActivityType.Message, receivedActivity.Type);
}
private static BotApplication CreateBotApplication() =>
new(CreateMockConversationClient(), CreateMockUserTokenClient(), NullLogger<BotApplication>.Instance);
private static ConversationClient CreateMockConversationClient()
{
Mock<HttpClient> mockHttpClient = new();
return new ConversationClient(mockHttpClient.Object);
}
private static UserTokenClient CreateMockUserTokenClient()
{
Mock<HttpClient> mockHttpClient = new();
Mock<IConfiguration> mockConfig = new();
NullLogger<UserTokenClient> logger = NullLogger<UserTokenClient>.Instance;
return new UserTokenClient(mockHttpClient.Object, mockConfig.Object, logger);
}
private static DefaultHttpContext CreateHttpContextWithActivity(CoreActivity activity)
{
DefaultHttpContext httpContext = new();
string activityJson = activity.ToJson();
byte[] bodyBytes = Encoding.UTF8.GetBytes(activityJson);
httpContext.Request.Body = new MemoryStream(bodyBytes);
httpContext.Request.ContentType = "application/json";
return httpContext;
}
}microsoft/teams.net
Publicmirrored fromhttps://github.com/microsoft/teams.netAvailable
core/test/Microsoft.Teams.Core.UnitTests/MiddlewareTests.cs
216lines · modepreview