diff --git a/core/samples/CustomHosting/MyTeamsBotApp.cs b/core/samples/CustomHosting/MyTeamsBotApp.cs index f2498d401..26069248e 100644 --- a/core/samples/CustomHosting/MyTeamsBotApp.cs +++ b/core/samples/CustomHosting/MyTeamsBotApp.cs @@ -1,17 +1,21 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using Microsoft.AspNetCore.Http; using Microsoft.Teams.Apps; using Microsoft.Teams.Apps.Api.Clients; using Microsoft.Teams.Apps.Handlers; -using Microsoft.Teams.Core; -using Microsoft.Teams.Core.Hosting; namespace CustomHosting; public class MyTeamsBotApp : TeamsBotApplication { - public MyTeamsBotApp(ConversationClient conversationClient, UserTokenClient userTokenClient, ApiClient teamsApiClient, IHttpContextAccessor httpContextAccessor, ILogger logger, BotApplicationOptions? options = null) : base(conversationClient, userTokenClient, teamsApiClient, httpContextAccessor, logger, options) + public MyTeamsBotApp( + ApiClient api, + IHttpContextAccessor accessor, + ILogger logger, + TeamsBotApplicationOptions? options = null) + : base(api, accessor, logger, options) { this.OnMessage(async (ctx, ct) => { diff --git a/core/samples/ExtAIBot/TeamsBotAppHandlers.cs b/core/samples/ExtAIBot/ExtAIBotApp.cs similarity index 71% rename from core/samples/ExtAIBot/TeamsBotAppHandlers.cs rename to core/samples/ExtAIBot/ExtAIBotApp.cs index 3d95d6cf4..2436c8de1 100644 --- a/core/samples/ExtAIBot/TeamsBotAppHandlers.cs +++ b/core/samples/ExtAIBot/ExtAIBotApp.cs @@ -1,7 +1,9 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using Microsoft.AspNetCore.Http; using Microsoft.Teams.Apps; +using Microsoft.Teams.Apps.Api.Clients; using Microsoft.Teams.Apps.Handlers; using Microsoft.Teams.Apps.Handlers.TaskModules; using Microsoft.Teams.Apps.Schema; @@ -10,35 +12,47 @@ namespace ExtAIBot; -// Bot activity handlers: incoming messages, clarification-card submits, and the -// custom-feedback fetch/submit pair. Each handler ultimately funnels a user-supplied -// string back through the Agent. -internal static class TeamsBotAppHandlers +// Teams bot subclass: wires the four activity handlers (message, clarification card +// submit, custom-feedback fetch/submit) in its constructor. Each handler funnels a +// user-supplied string back through the Agent. +internal class ExtAIBotApp : TeamsBotApplication { - public static TeamsBotApplication RegisterHandlers(this TeamsBotApplication teamsApp, Agent agent, ILogger logger) + private readonly Agent _agent; + private readonly ILogger _logger; + + public ExtAIBotApp( + Agent agent, + ApiClient api, + IHttpContextAccessor accessor, + ILogger logger, + TeamsBotApplicationOptions? options = null) + : base(api, accessor, logger, options) { + _agent = agent; + _logger = logger; + // Message handler. - teamsApp.OnMessage(async (context, cancellationToken) => + this.OnMessage(async (context, cancellationToken) => { string userText = context.Activity.TextWithoutMentions ?? ""; - await RespondAsync(agent, context, userText, cancellationToken); + await RespondAsync(context, userText, cancellationToken); }); // Clarification: adaptive card action. // Triggered when the user submits the clarification card (Action.Execute, verb "clarification"). - teamsApp.OnAdaptiveCardAction(async (context, cancellationToken) => + this.OnAdaptiveCardAction(async (context, cancellationToken) => { if (context.Activity.Value?.Action?.Verb == "clarification") { string choice = context.Activity.Value.Action.Data?["clarificationChoice"]?.ToString() ?? ""; - await RespondAsync(agent, context, choice, cancellationToken); + await RespondAsync(context, choice, cancellationToken); } return InvokeResponse.Ok(); }); // Feedback: message fetch task. // Triggered when the user clicks thumbs up or thumbs down on a bot reply. - teamsApp.OnMessageFetchTask((context, cancellationToken) => + this.OnMessageFetchTask((context, cancellationToken) => { string? reaction = context.Activity.Value?.Data?.ActionValue?.Reaction; @@ -52,28 +66,26 @@ public static TeamsBotApplication RegisterHandlers(this TeamsBotApplication team }); // Feedback: message submit action. - teamsApp.OnMessageSubmitFeedback((context, cancellationToken) => + this.OnMessageSubmitFeedback((context, cancellationToken) => { MessageSubmitFeedbackValue? feedback = context.Activity.Value; - logger.LogInformation("Feedback received — reaction: {Reaction}, feedback: {Feedback}", + _logger.LogInformation("Feedback received — reaction: {Reaction}, feedback: {Feedback}", feedback?.Reaction, feedback?.Feedback); return Task.FromResult(InvokeResponse.Ok()); }); - - return teamsApp; } // Runs the agent and streams a response back. Shared between the incoming-message // handler and the clarification-card submit handler — both flows ultimately just // feed a user-supplied string into the agent. - private static async Task RespondAsync(Agent agent, Context context, string userText, CancellationToken cancellationToken) + private async Task RespondAsync(Context context, string userText, CancellationToken cancellationToken) where TActivity : TeamsActivity { _ = context.Activity.Conversation?.Id ?? throw new InvalidOperationException("Missing conversation ID."); TeamsStreamingWriter writer = TeamsStreamingWriter.CreateFromContext(context); - RunResult result = await agent.RunAsync(context.Activity.Conversation!.Id, userText, writer, cancellationToken); + RunResult result = await _agent.RunAsync(context.Activity.Conversation!.Id, userText, writer, cancellationToken); IList entities = result.Citations.BuildEntities(result.FullText); diff --git a/core/samples/ExtAIBot/Program.cs b/core/samples/ExtAIBot/Program.cs index 4101d4ede..c772b24d0 100644 --- a/core/samples/ExtAIBot/Program.cs +++ b/core/samples/ExtAIBot/Program.cs @@ -7,21 +7,20 @@ using Microsoft.Extensions.AI; using Microsoft.Teams.Apps; -// Wires up the Teams bot application and delegates AI execution to Agent. -// Handler registration lives in TeamsBotAppHandlers.cs. +// Wires up the Teams bot application. Handler registration lives in ExtAIBotApp. WebApplicationBuilder builder = WebApplication.CreateSlimBuilder(args); -builder.Services.AddTeamsBotApplication(); +builder.Services.AddTeamsBotApplication(); builder.Services.AddSingleton(sp => { IConfiguration config = sp.GetRequiredService(); string endpoint = config["AzureOpenAI:Endpoint"] ?? throw new InvalidOperationException("AzureOpenAI:Endpoint is required."); string apiKey = config["AzureOpenAI:ApiKey"] ?? throw new InvalidOperationException("AzureOpenAI:ApiKey is required."); - string modelId = config["AzureOpenAI:ModelId"] ?? throw new InvalidOperationException("AzureOpenAI:ModelId is required."); + string deployment = config["AzureOpenAI:Deployment"] ?? throw new InvalidOperationException("AzureOpenAI:Deployment is required."); return new AzureOpenAIClient(new Uri(endpoint), new ApiKeyCredential(apiKey)) - .GetChatClient(modelId) + .GetChatClient(deployment) .AsIChatClient() .AsBuilder() .UseFunctionInvocation() @@ -34,10 +33,5 @@ builder.Services.AddSingleton(); WebApplication webApp = builder.Build(); - -Agent agent = webApp.Services.GetRequiredService(); -ILogger handlerLogger = webApp.Services.GetRequiredService().CreateLogger("ExtAIBot.TeamsBotAppHandlers"); - -webApp.UseTeamsBotApplication().RegisterHandlers(agent, handlerLogger); - +webApp.UseTeamsBotApplication(); webApp.Run(); diff --git a/core/samples/ExtAIBot/appsettings.json b/core/samples/ExtAIBot/appsettings.json index 057db1509..6c9fd3f11 100644 --- a/core/samples/ExtAIBot/appsettings.json +++ b/core/samples/ExtAIBot/appsettings.json @@ -10,6 +10,6 @@ "AzureOpenAI": { "Endpoint": "", "ApiKey": "", - "ModelId": "" + "Deployment": "" } } diff --git a/core/src/Microsoft.Teams.Apps/Api/Clients/ApiClient.cs b/core/src/Microsoft.Teams.Apps/Api/Clients/ApiClient.cs index 3dc4556d0..1fb29248b 100644 --- a/core/src/Microsoft.Teams.Apps/Api/Clients/ApiClient.cs +++ b/core/src/Microsoft.Teams.Apps/Api/Clients/ApiClient.cs @@ -27,8 +27,10 @@ namespace Microsoft.Teams.Apps.Api.Clients; public class ApiClient { private readonly BotHttpClient _http; - private readonly CoreConversationClient _conversationClient; - private readonly CoreUserTokenClient _userTokenClient; + + internal CoreConversationClient ConversationClient { get; } + + internal CoreUserTokenClient UserTokenClient { get; } /// /// The service URL used by this client. @@ -78,8 +80,8 @@ public ApiClient(HttpClient httpClient, CoreConversationClient conversationClien ArgumentNullException.ThrowIfNull(userTokenClient); _http = new BotHttpClient(httpClient, logger); - _conversationClient = conversationClient; - _userTokenClient = userTokenClient; + ConversationClient = conversationClient; + UserTokenClient = userTokenClient; Bots = new BotClient(userTokenClient); Users = new UserClient(userTokenClient); @@ -106,8 +108,8 @@ public ApiClient(Uri serviceUrl, HttpClient httpClient, CoreConversationClient c ArgumentNullException.ThrowIfNull(userTokenClient); _http = new BotHttpClient(httpClient, logger); - _conversationClient = conversationClient; - _userTokenClient = userTokenClient; + ConversationClient = conversationClient; + UserTokenClient = userTokenClient; ServiceUrl = serviceUrl; Bots = new BotClient(userTokenClient); Conversations = new ConversationApiClient(serviceUrl, conversationClient); @@ -125,8 +127,8 @@ public ApiClient(ApiClient client) ServiceUrl = client.ServiceUrl; _http = client._http; - _conversationClient = client._conversationClient; - _userTokenClient = client._userTokenClient; + ConversationClient = client.ConversationClient; + UserTokenClient = client.UserTokenClient; Bots = client.Bots; Conversations = client.Conversations; Users = client.Users; @@ -138,8 +140,8 @@ public ApiClient(ApiClient client) private ApiClient(BotHttpClient http, CoreConversationClient conversationClient, CoreUserTokenClient userTokenClient, Uri serviceUrl) { _http = http; - _conversationClient = conversationClient; - _userTokenClient = userTokenClient; + ConversationClient = conversationClient; + UserTokenClient = userTokenClient; ServiceUrl = serviceUrl; Bots = new BotClient(userTokenClient); Conversations = new ConversationApiClient(serviceUrl, conversationClient); @@ -157,6 +159,6 @@ private ApiClient(BotHttpClient http, CoreConversationClient conversationClient, public virtual ApiClient ForServiceUrl(Uri serviceUrl) { ArgumentNullException.ThrowIfNull(serviceUrl); - return new ApiClient(_http, _conversationClient, _userTokenClient, serviceUrl); + return new ApiClient(_http, ConversationClient, UserTokenClient, serviceUrl); } } diff --git a/core/src/Microsoft.Teams.Apps/TeamsBotApplication.HostingExtensions.cs b/core/src/Microsoft.Teams.Apps/TeamsBotApplication.HostingExtensions.cs index cbb2e9f09..49c87046f 100644 --- a/core/src/Microsoft.Teams.Apps/TeamsBotApplication.HostingExtensions.cs +++ b/core/src/Microsoft.Teams.Apps/TeamsBotApplication.HostingExtensions.cs @@ -107,7 +107,7 @@ public static IServiceCollection AddTeamsBotApplication(this IServiceColle BotConfig botConfig = BotConfig.Resolve(services, sectionName); // Register TeamsBotApplicationOptions - TeamsBotApplicationOptions teamsOptions = new(); + TeamsBotApplicationOptions teamsOptions = new() { AppId = botConfig.ClientId }; configure?.Invoke(teamsOptions); services.AddSingleton(teamsOptions); diff --git a/core/src/Microsoft.Teams.Apps/TeamsBotApplication.cs b/core/src/Microsoft.Teams.Apps/TeamsBotApplication.cs index 20c01da13..1f2bbfaf0 100644 --- a/core/src/Microsoft.Teams.Apps/TeamsBotApplication.cs +++ b/core/src/Microsoft.Teams.Apps/TeamsBotApplication.cs @@ -81,36 +81,50 @@ public OAuthFlow GetOAuthFlow(string connectionName) /// public ApiClient Api { get; } - /// The conversation client for sending and managing activities. - /// The user token client for OAuth operations. - /// The Teams API client for Teams-specific operations. - /// The HTTP context accessor for reading invoke responses. - /// The logger instance. - /// Options containing the application (client) ID, used for logging and diagnostics. Defaults to an empty instance if not provided. - /// Teams-specific options including OAuth flow configuration. Defaults to an empty instance if not provided. + /// + /// Initializes a new . + /// + /// The Teams API facade. Also carries the underlying Core conversation and user-token clients. + /// Accessor used to write invoke responses back to the current HTTP request. + /// Logger used by the bot and exposed as . + /// Optional Teams bot options (AppId, OAuth flows, etc.). + /// + /// + /// public class MyBot : TeamsBotApplication + /// { + /// public MyBot(ApiClient api, IHttpContextAccessor accessor, ILogger<MyBot> logger, TeamsBotApplicationOptions? options = null) + /// : base(api, accessor, logger, options) + /// { + /// this.OnMessage(async (ctx, ct) => + /// await ctx.SendActivityAsync("Hello!", ct)); + /// } + /// } + /// + /// public TeamsBotApplication( - ConversationClient conversationClient, - UserTokenClient userTokenClient, ApiClient teamsApiClient, IHttpContextAccessor httpContextAccessor, ILogger logger, - BotApplicationOptions? options = null, - TeamsBotApplicationOptions? teamsOptions = null) - : base(conversationClient, userTokenClient, logger, options) + TeamsBotApplicationOptions? options = null) + : base( + (teamsApiClient ?? throw new ArgumentNullException(nameof(teamsApiClient))).ConversationClient, + teamsApiClient.UserTokenClient, + logger, + options) { _teamsApiClient = teamsApiClient; Api = teamsApiClient; Logger = logger; Router = new Router(logger); - // Auto-register OAuth flows from DI options - if (teamsOptions is not null) + if (options is not null) { - foreach (TeamsBotApplicationOptions.OAuthFlowDescriptor descriptor in teamsOptions.OAuthFlows) + foreach (TeamsBotApplicationOptions.OAuthFlowDescriptor descriptor in options.OAuthFlows) { this.AddOAuthFlow(descriptor.Options); } } + OnActivity = async (activity, cancellationToken) => { logger.LogDebug("OnActivity invoked for activity: Id={Id}", activity.Id); diff --git a/core/src/Microsoft.Teams.Apps/TeamsBotApplicationOptions.cs b/core/src/Microsoft.Teams.Apps/TeamsBotApplicationOptions.cs index e26146070..deccf72f2 100644 --- a/core/src/Microsoft.Teams.Apps/TeamsBotApplicationOptions.cs +++ b/core/src/Microsoft.Teams.Apps/TeamsBotApplicationOptions.cs @@ -2,13 +2,15 @@ // Licensed under the MIT License. using Microsoft.Teams.Apps.OAuth; +using Microsoft.Teams.Core.Hosting; namespace Microsoft.Teams.Apps; /// /// Options for configuring a . +/// Inherits so a single options object covers both Core and Teams settings. /// -public sealed class TeamsBotApplicationOptions +public sealed class TeamsBotApplicationOptions : BotApplicationOptions { internal List OAuthFlows { get; } = []; diff --git a/core/src/Microsoft.Teams.Core/Hosting/BotApplicationOptions.cs b/core/src/Microsoft.Teams.Core/Hosting/BotApplicationOptions.cs index bc2221bb5..0b14d46df 100644 --- a/core/src/Microsoft.Teams.Core/Hosting/BotApplicationOptions.cs +++ b/core/src/Microsoft.Teams.Core/Hosting/BotApplicationOptions.cs @@ -6,7 +6,7 @@ namespace Microsoft.Teams.Core.Hosting; /// /// Options for configuring a bot application instance. /// -public sealed class BotApplicationOptions +public class BotApplicationOptions { /// /// Gets or sets the application (client) ID, used for logging and diagnostics. diff --git a/core/test/Microsoft.Teams.Apps.BotBuilder.UnitTests/CompatBotAdapterTests.cs b/core/test/Microsoft.Teams.Apps.BotBuilder.UnitTests/CompatBotAdapterTests.cs index e06cb9980..78724df20 100644 --- a/core/test/Microsoft.Teams.Apps.BotBuilder.UnitTests/CompatBotAdapterTests.cs +++ b/core/test/Microsoft.Teams.Apps.BotBuilder.UnitTests/CompatBotAdapterTests.cs @@ -4,9 +4,8 @@ using Microsoft.AspNetCore.Http; using Microsoft.Bot.Builder; using Microsoft.Bot.Schema; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging.Abstractions; -using Microsoft.Teams.Apps; -using Microsoft.Teams.Apps.Api.Clients; using Microsoft.Teams.Core; using Microsoft.Teams.Core.Schema; using Moq; @@ -253,59 +252,19 @@ private static Mock CreateMockConversationClient() return mock; } - private static Mock CreateMockTeamsBotApplication() - { - Mock mockConversationClient = CreateMockConversationClient(); - Mock mockUserTokenClient = new( - new HttpClient(), - Mock.Of(), - NullLogger.Instance); - ApiClient mockTeamsApiClient = new( - new Uri("https://service.url"), - new HttpClient(), - mockConversationClient.Object, - mockUserTokenClient.Object, - NullLogger.Instance); - - Mock mock = new( - mockConversationClient.Object, - mockUserTokenClient.Object, - mockTeamsApiClient, - Mock.Of(), - NullLogger.Instance); - - return mock; - } - private static TeamsBotAdapter CreateCompatBotAdapter(ConversationClient conversationClient) { Mock mockUserTokenClient = new( new HttpClient(), - Mock.Of(), + Mock.Of(), NullLogger.Instance); - ApiClient mockTeamsApiClient = new( - new Uri("https://service.url"), - new HttpClient(), + BotApplication botApplication = new( conversationClient, mockUserTokenClient.Object, - NullLogger.Instance); - TeamsBotApplication teamsBotApplication = new( - conversationClient, - mockUserTokenClient.Object, - mockTeamsApiClient, - Mock.Of(), - NullLogger.Instance); + NullLogger.Instance); return new TeamsBotAdapter( - teamsBotApplication, - Mock.Of(), - NullLogger.Instance); - } - - private static TeamsBotAdapter CreateCompatBotAdapter(TeamsBotApplication teamsBotApplication) - { - return new TeamsBotAdapter( - teamsBotApplication, + botApplication, Mock.Of(), NullLogger.Instance); } diff --git a/core/test/Microsoft.Teams.Apps.BotBuilder.UnitTests/Microsoft.Teams.Apps.BotBuilder.UnitTests.csproj b/core/test/Microsoft.Teams.Apps.BotBuilder.UnitTests/Microsoft.Teams.Apps.BotBuilder.UnitTests.csproj index 41c9b2702..961f74e2d 100644 --- a/core/test/Microsoft.Teams.Apps.BotBuilder.UnitTests/Microsoft.Teams.Apps.BotBuilder.UnitTests.csproj +++ b/core/test/Microsoft.Teams.Apps.BotBuilder.UnitTests/Microsoft.Teams.Apps.BotBuilder.UnitTests.csproj @@ -22,7 +22,6 @@ - diff --git a/core/test/Microsoft.Teams.Apps.UnitTests/OAuthFlowTests.cs b/core/test/Microsoft.Teams.Apps.UnitTests/OAuthFlowTests.cs index 5e0cf2a31..c3465896d 100644 --- a/core/test/Microsoft.Teams.Apps.UnitTests/OAuthFlowTests.cs +++ b/core/test/Microsoft.Teams.Apps.UnitTests/OAuthFlowTests.cs @@ -427,12 +427,10 @@ private static TestHarness CreateHarness(params string[] connectionNames) mockUserTokenClient.Object); TeamsBotApplication app = new( - mockConversationClient.Object, - mockUserTokenClient.Object, apiClient, new HttpContextAccessor(), NullLogger.Instance, - new BotApplicationOptions { AppId = "test-app-id" }); + new TeamsBotApplicationOptions { AppId = "test-app-id" }); OAuthFlow? graphFlow = null; OAuthFlow? githubFlow = null; diff --git a/core/test/Microsoft.Teams.Apps.UnitTests/PromptPreviewTests.cs b/core/test/Microsoft.Teams.Apps.UnitTests/PromptPreviewTests.cs index 27da03854..9612f2e9b 100644 --- a/core/test/Microsoft.Teams.Apps.UnitTests/PromptPreviewTests.cs +++ b/core/test/Microsoft.Teams.Apps.UnitTests/PromptPreviewTests.cs @@ -220,12 +220,10 @@ private static TestHarness CreateHarness() mockUserTokenClient.Object); TeamsBotApplication app = new( - mockConversationClient.Object, - mockUserTokenClient.Object, apiClient, new HttpContextAccessor(), NullLogger.Instance, - new BotApplicationOptions { AppId = "test-app-id" }); + new TeamsBotApplicationOptions { AppId = "test-app-id" }); return new TestHarness { diff --git a/core/test/Microsoft.Teams.Apps.UnitTests/TeamsBotApplicationHostingExtensionsTests.cs b/core/test/Microsoft.Teams.Apps.UnitTests/TeamsBotApplicationHostingExtensionsTests.cs new file mode 100644 index 000000000..229aa4dc5 --- /dev/null +++ b/core/test/Microsoft.Teams.Apps.UnitTests/TeamsBotApplicationHostingExtensionsTests.cs @@ -0,0 +1,89 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Teams.Apps.Api.Clients; +using Microsoft.Teams.Core.Hosting; + +namespace Microsoft.Teams.Apps.UnitTests; + +public class TeamsBotApplicationHostingExtensionsTests +{ + private static ServiceProvider BuildServiceProvider(Dictionary configData) + { + IConfigurationRoot configuration = new ConfigurationBuilder() + .AddInMemoryCollection(configData) + .Build(); + + ServiceCollection services = new(); + services.AddSingleton(configuration); + services.AddLogging(); + services.AddTeamsBotApplication(); + + return services.BuildServiceProvider(); + } + + [Fact] + public void AddTeamsBotApplication_RegistersAllRequiredServices() + { + Dictionary configData = new() + { + ["AzureAd:ClientId"] = "teams-bundle-client-id", + ["AzureAd:TenantId"] = "teams-bundle-tenant-id" + }; + + using ServiceProvider serviceProvider = BuildServiceProvider(configData); + + Assert.NotNull(serviceProvider.GetRequiredService()); + Assert.NotNull(serviceProvider.GetRequiredService()); + TeamsBotApplicationOptions options = serviceProvider.GetRequiredService(); + Assert.Equal("teams-bundle-client-id", options.AppId); + + BotApplicationOptions botOptions = serviceProvider.GetRequiredService(); + Assert.Equal("teams-bundle-client-id", botOptions.AppId); + Assert.Equal(options.AppId, botOptions.AppId); + } + + [Fact] + public void AddTeamsBotApplication_WithCustomSubclass_ResolvesViaDI() + { + Dictionary configData = new() + { + ["AzureAd:ClientId"] = "subclass-client-id", + ["AzureAd:TenantId"] = "subclass-tenant-id" + }; + + IConfigurationRoot configuration = new ConfigurationBuilder() + .AddInMemoryCollection(configData) + .Build(); + + ServiceCollection services = new(); + services.AddSingleton(configuration); + services.AddLogging(); + services.AddTeamsBotApplication(); + + using ServiceProvider serviceProvider = services.BuildServiceProvider(); + SubclassBot bot = serviceProvider.GetRequiredService(); + + Assert.True(bot.ConstructedViaDI); + Assert.Equal("subclass-client-id", bot.AppId); + } + + private sealed class SubclassBot : TeamsBotApplication + { + public bool ConstructedViaDI { get; } + + public SubclassBot( + ApiClient api, + IHttpContextAccessor accessor, + ILogger logger, + TeamsBotApplicationOptions? options = null) + : base(api, accessor, logger, options) + { + ConstructedViaDI = true; + } + } +} diff --git a/core/test/Microsoft.Teams.Apps.UnitTests/TeamsBotApplicationTests.cs b/core/test/Microsoft.Teams.Apps.UnitTests/TeamsBotApplicationTests.cs index 968a9ec6e..75c8f9b22 100644 --- a/core/test/Microsoft.Teams.Apps.UnitTests/TeamsBotApplicationTests.cs +++ b/core/test/Microsoft.Teams.Apps.UnitTests/TeamsBotApplicationTests.cs @@ -57,11 +57,9 @@ private static TeamsBotApplication CreateApp() mockUserTokenClient.Object); return new TeamsBotApplication( - mockConversationClient.Object, - mockUserTokenClient.Object, apiClient, new HttpContextAccessor(), NullLogger.Instance, - new BotApplicationOptions { AppId = "test-app-id" }); + new TeamsBotApplicationOptions { AppId = "test-app-id" }); } }