using System.Net;
using System.Text;
using System.Text.Json;
using Microsoft.AspNetCore.Http;
using Microsoft.Teams.Api;
using Microsoft.Teams.Api.Activities;
using Microsoft.Teams.Api.Auth;
using Microsoft.Teams.Apps;
using Microsoft.Teams.Apps.Events;
using Microsoft.Teams.Common.Logging;
using Moq;
namespace Microsoft.Teams.Plugins.AspNetCore.Tests;
public class AspNetCorePluginTests
{
private static AspNetCorePlugin CreatePlugin(Mock<ILogger>? loggerMock = null, EventFunction? events = null)
{
var plugin = new AspNetCorePlugin();
if (loggerMock is not null)
{
plugin.Logger = loggerMock.Object;
}
else
{
plugin.Logger = new ConsoleLogger("Test", LogLevel.Debug);
}
plugin.Client = new Mock<Microsoft.Teams.Common.Http.IHttpClient>().Object;
if (events is not null)
{
plugin.Events += events;
}
return plugin;
}
private static DefaultHttpContext CreateHttpContext(IActivity activity, string bearer = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjQ3MDI1MTUyMDB9.signature")
{
var ctx = new DefaultHttpContext();
ctx.TraceIdentifier = Guid.NewGuid().ToString();
ctx.Request.Headers.Append("Authorization", $"Bearer {bearer}");
var json = JsonSerializer.Serialize(activity, new JsonSerializerOptions { DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull });
var bytes = Encoding.UTF8.GetBytes(json);
ctx.Request.Body = new MemoryStream(bytes);
ctx.Request.ContentLength = bytes.Length;
return ctx;
}
private static MessageActivity CreateMessageActivity()
{
return new MessageActivity("hi")
{
From = new() { Id = "user" },
Recipient = new() { Id = "bot" },
Conversation = new Conversation() { Id = "conv", Type = ConversationType.Personal }
};
}
[Fact]
public async Task Test_Do_Http_CallsExtractTokenAndActivity_AndCallsCoreDo()
{
// Arrange
var activity = CreateMessageActivity();
var coreResponse = new Response(HttpStatusCode.Accepted, new { ok = true });
var eventsCalled = new List<string>();
EventFunction events = (plugin, name, payload, ct) =>
{
eventsCalled.Add(name);
if (name == "activity") return Task.FromResult<object?>(coreResponse); // returned directly by core Do
return Task.FromResult<object?>(null);
};
var logger = new Mock<ILogger>();
var plugin = CreatePlugin(logger, events);
var ctx = CreateHttpContext(activity);
// Act
var result = await plugin.Do(ctx);
// Assert
Assert.Contains("activity", eventsCalled);
var jsonResult = Assert.IsType<Microsoft.AspNetCore.Http.HttpResults.JsonHttpResult<object?>>(result);
Assert.Equal((int)coreResponse.Status, jsonResult.StatusCode);
}
[Fact]
public async Task Test_Do_Http_SetsHeadersFromResponseMeta()
{
// Arrange
var activity = CreateMessageActivity();
var response = new Response(HttpStatusCode.OK, new { hello = "world" });
response.Meta.Add("routes", 3);
response.Meta.Add("custom", "value");
EventFunction events = (plugin, name, payload, ct) =>
{
if (name == "activity") return Task.FromResult<object?>(response);
return Task.FromResult<object?>(null);
};
var plugin = CreatePlugin(new Mock<ILogger>(), events);
var ctx = CreateHttpContext(activity);
// Act
var result = await plugin.Do(ctx);
// Assert body result type
var jsonResult = Assert.IsType<Microsoft.AspNetCore.Http.HttpResults.JsonHttpResult<object?>>(result);
Assert.Equal((int)HttpStatusCode.OK, jsonResult.StatusCode);
// Headers: routes & custom should be present
Assert.Contains("X-Teams-Routes", ctx.Response.Headers.Keys); // capitalized first char
Assert.Contains("X-Teams-Custom", ctx.Response.Headers.Keys);
}
[Fact]
public async Task Test_Do_Http_ErrorPath_ProducesProblemResult()
{
// Arrange -> throw inside events
EventFunction events = (plugin, name, payload, ct) =>
{
if (name == "activity") throw new InvalidOperationException("boom");
return Task.FromResult<object?>(null);
};
var logger = new Mock<ILogger>();
var plugin = CreatePlugin(logger, events);
var ctx = CreateHttpContext(CreateMessageActivity());
// Act
var result = await plugin.Do(ctx);
// Assert
var problem = Assert.IsType<Microsoft.AspNetCore.Http.HttpResults.JsonHttpResult<object>>(result);
Assert.Equal(500, problem.StatusCode);
Assert.Contains("boom", problem.Value!.ToString());
logger.Verify(l => l.Error(It.IsAny<object[]>()), Times.AtLeastOnce);
}
[Fact]
public void Test_ExtractToken_ReturnsToken()
{
var plugin = CreatePlugin();
var ctx = CreateHttpContext(CreateMessageActivity(), "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjQ3MDI1MTUyMDB9.token123");
var token = plugin.ExtractToken(ctx.Request);
Assert.NotNull(token);
Assert.Contains("token123", token.ToString());
}
[Fact]
public async Task Test_ExtractActivity_ReturnsActivity()
{
var plugin = CreatePlugin();
var activity = CreateMessageActivity();
var ctx = CreateHttpContext(activity);
var extracted = await plugin.ParseActivity(ctx.Request);
Assert.NotNull(extracted);
Assert.True(activity.Type.Equals(extracted.Type));
}
[Fact]
public async Task Test_ExtractActivity_HttpRequestBodyAlreadyRead_ReturnsActivity()
{
var plugin = CreatePlugin();
var activity = CreateMessageActivity();
var ctx = CreateHttpContext(activity);
// simulate body already read by setting position to end
ctx.Request.Body.Position = ctx.Request.Body.Length;
var extracted = await plugin.ParseActivity(ctx.Request);
Assert.NotNull(extracted);
Assert.True(activity.Type.Equals(extracted.Type));
}
[Fact]
public async Task Test_Do_Core_ReturnsResponseAndLogs()
{
// Arrange core path tests the ActivityEvent Do(ActivityEvent)
var response = new Response(HttpStatusCode.OK, new { test = 1 });
EventFunction events = (plugin, name, payload, ct) =>
{
if (name == "activity") return Task.FromResult<object?>(response);
return Task.FromResult<object?>(null);
};
var logger = new Mock<ILogger>();
var plugin = CreatePlugin(logger, events);
var evt = new ActivityEvent() { Token = new JsonWebToken("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjQ3MDI1MTUyMDB9.signature"), Activity = CreateMessageActivity() };
// Act
var res = await plugin.Do(evt);
// Assert
Assert.Same(response, res);
logger.Verify(l => l.Debug(It.IsAny<object[]>()), Times.AtLeastOnce);
}
}microsoft/teams.net
Publicmirrored fromhttps://github.com/microsoft/teams.netAvailable
Tests/Microsoft.Teams.Plugins.AspNetCore.Tests/AspNetCorePluginTests.cs
199lines · modepreview