From 456f29c66410179055a66f41c3ff88b13b99f662 Mon Sep 17 00:00:00 2001 From: Rido Date: Fri, 23 Jan 2026 08:42:39 -0800 Subject: [PATCH 1/4] Fix Write Invoke Response when called from CompatMiddleware (#284) This pull request refactors the `CompatAdapter` and related middleware to use dependency injection for service resolution, improving maintainability and aligning with modern .NET practices. The main changes involve replacing constructor parameters with `IServiceProvider`, updating middleware to resolve dependencies, and enhancing logging in the `CompatBotAdapter`. **Dependency Injection Refactoring:** * `CompatAdapter` now receives an `IServiceProvider` in its constructor, and uses it to resolve `TeamsBotApplication` and `CompatBotAdapter` instead of receiving them directly as parameters. All usages of these services within the class have been updated accordingly. * `CompatAdapterMiddleware` now receives both the Bot Framework middleware and an `IServiceProvider`, enabling it to resolve required services such as `IHttpContextAccessor` and `ILogger`. **Middleware and Adapter Updates:** * Middleware instantiation in `CompatAdapter` is updated to pass the `IServiceProvider` to each `CompatAdapterMiddleware`, ensuring dependencies are available during middleware execution. * Within `CompatAdapterMiddleware`, the `CompatBotAdapter` is now constructed with resolved `IHttpContextAccessor` and `ILogger` instances, improving logging and context-awareness. **Logging Improvements:** * Enhanced logging in `CompatBotAdapter` to include HTTP status codes when sending invoke responses, and to avoid serialization if the response body is null. **General Code Modernization:** * Added missing `using` statements for dependency injection and logging namespaces in affected files. [[1]](diffhunk://#diff-e6d701ce121dda6651c0dea00498d45e2cc0b0cec446a578f3058be691c7c0f5R8) [[2]](diffhunk://#diff-80a974c881b9ff3a697fe5b07985c8f55edb2e2896384d6d437a3e8dfa5a2817R5-R6) --- .../CompatAdapter.cs | 45 +++++++++++++------ .../CompatAdapterMiddleware.cs | 10 +++-- .../CompatBotAdapter.cs | 7 ++- 3 files changed, 43 insertions(+), 19 deletions(-) diff --git a/core/src/Microsoft.Teams.Bot.Compat/CompatAdapter.cs b/core/src/Microsoft.Teams.Bot.Compat/CompatAdapter.cs index 81142ecb..55aa0e6f 100644 --- a/core/src/Microsoft.Teams.Bot.Compat/CompatAdapter.cs +++ b/core/src/Microsoft.Teams.Bot.Compat/CompatAdapter.cs @@ -5,6 +5,7 @@ using Microsoft.Bot.Builder; using Microsoft.Bot.Builder.Integration.AspNet.Core; using Microsoft.Bot.Schema; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Teams.Bot.Apps; using Microsoft.Teams.Bot.Core; using Microsoft.Teams.Bot.Core.Schema; @@ -20,10 +21,24 @@ namespace Microsoft.Teams.Bot.Compat; /// The adapter allows registration of middleware and error handling delegates, and supports processing HTTP requests /// and continuing conversations. Thread safety is not guaranteed; instances should not be shared across concurrent /// requests. -/// The bot application instance that handles activity processing and manages user token operations. -/// The underlying bot adapter used to interact with the bot framework and create turn contexts. -public class CompatAdapter(TeamsBotApplication botApplication, CompatBotAdapter compatBotAdapter) : IBotFrameworkHttpAdapter +public class CompatAdapter : IBotFrameworkHttpAdapter { + private readonly TeamsBotApplication _teamsBotApplication; + private readonly CompatBotAdapter _compatBotAdapter; + private readonly IServiceProvider _sp; + + + /// + /// Creates a new instance of the class. + /// + /// + public CompatAdapter(IServiceProvider sp) + { + _sp = sp; + _teamsBotApplication = sp.GetRequiredService(); + _compatBotAdapter = sp.GetRequiredService(); + } + /// /// Gets the collection of middleware components configured for the application. /// @@ -64,26 +79,28 @@ public async Task ProcessAsync(HttpRequest httpRequest, HttpResponse httpRespons ArgumentNullException.ThrowIfNull(httpRequest); ArgumentNullException.ThrowIfNull(httpResponse); ArgumentNullException.ThrowIfNull(bot); + CoreActivity? coreActivity = null; - botApplication.OnActivity = async (activity, cancellationToken1) => + _teamsBotApplication.OnActivity = async (activity, cancellationToken1) => { coreActivity = activity; - TurnContext turnContext = new(compatBotAdapter, activity.ToCompatActivity()); - turnContext.TurnState.Add(new CompatUserTokenClient(botApplication.UserTokenClient)); - CompatConnectorClient connectionClient = new(new CompatConversations(botApplication.ConversationClient) { ServiceUrl = activity.ServiceUrl?.ToString() }); + TurnContext turnContext = new(_compatBotAdapter, activity.ToCompatActivity()); + turnContext.TurnState.Add(new CompatUserTokenClient(_teamsBotApplication.UserTokenClient)); + CompatConnectorClient connectionClient = new(new CompatConversations(_teamsBotApplication.ConversationClient) { ServiceUrl = activity.ServiceUrl?.ToString() }); turnContext.TurnState.Add(connectionClient); - turnContext.TurnState.Add(botApplication.TeamsApiClient); + turnContext.TurnState.Add(_teamsBotApplication.TeamsApiClient); await bot.OnTurnAsync(turnContext, cancellationToken1).ConfigureAwait(false); }; + try { foreach (Microsoft.Bot.Builder.IMiddleware? middleware in MiddlewareSet) { - botApplication.Use(new CompatAdapterMiddleware(middleware)); + _teamsBotApplication.Use(new CompatAdapterMiddleware(middleware, _sp)); } - await botApplication.ProcessAsync(httpRequest.HttpContext, cancellationToken).ConfigureAwait(false); + await _teamsBotApplication.ProcessAsync(httpRequest.HttpContext, cancellationToken).ConfigureAwait(false); } catch (Exception ex) { @@ -92,7 +109,7 @@ public async Task ProcessAsync(HttpRequest httpRequest, HttpResponse httpRespons if (ex is BotHandlerException aex) { coreActivity = aex.Activity; - using TurnContext turnContext = new(compatBotAdapter, coreActivity!.ToCompatActivity()); + using TurnContext turnContext = new(_compatBotAdapter, coreActivity!.ToCompatActivity()); await OnTurnError(turnContext, ex).ConfigureAwait(false); } else @@ -124,9 +141,9 @@ public async Task ContinueConversationAsync(string botId, ConversationReference ArgumentNullException.ThrowIfNull(reference); ArgumentNullException.ThrowIfNull(callback); - using TurnContext turnContext = new(compatBotAdapter, reference.GetContinuationActivity()); - turnContext.TurnState.Add(new CompatConnectorClient(new CompatConversations(botApplication.ConversationClient) { ServiceUrl = reference.ServiceUrl })); - turnContext.TurnState.Add(botApplication.TeamsApiClient); + using TurnContext turnContext = new(_compatBotAdapter, reference.GetContinuationActivity()); + turnContext.TurnState.Add(new CompatConnectorClient(new CompatConversations(_teamsBotApplication.ConversationClient) { ServiceUrl = reference.ServiceUrl })); + turnContext.TurnState.Add(_teamsBotApplication.TeamsApiClient); await callback(turnContext, cancellationToken).ConfigureAwait(false); } } diff --git a/core/src/Microsoft.Teams.Bot.Compat/CompatAdapterMiddleware.cs b/core/src/Microsoft.Teams.Bot.Compat/CompatAdapterMiddleware.cs index 4cdf9f7a..1df8acbf 100644 --- a/core/src/Microsoft.Teams.Bot.Compat/CompatAdapterMiddleware.cs +++ b/core/src/Microsoft.Teams.Bot.Compat/CompatAdapterMiddleware.cs @@ -2,6 +2,8 @@ // Licensed under the MIT License. using Microsoft.Bot.Builder; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; using Microsoft.Teams.Bot.Apps; using Microsoft.Teams.Bot.Core; using Microsoft.Teams.Bot.Core.Schema; @@ -18,7 +20,8 @@ namespace Microsoft.Teams.Bot.Compat; /// This allows gradual migration from Bot Framework SDK to Teams Bot Core while preserving existing middleware investments. /// /// The Bot Framework middleware component to adapt into the Teams Bot Core pipeline. -internal sealed class CompatAdapterMiddleware(IMiddleware bfMiddleWare) : ITurnMiddleWare +/// The service provider used to resolve required services such as HTTP context accessor and logger. +internal sealed class CompatAdapterMiddleware(IMiddleware bfMiddleWare, IServiceProvider sp) : ITurnMiddleWare { /// /// Processes a turn by converting the CoreActivity to Bot Framework format and invoking the wrapped middleware. @@ -30,13 +33,14 @@ internal sealed class CompatAdapterMiddleware(IMiddleware bfMiddleWare) : ITurnM /// A task that represents the asynchronous operation. public Task OnTurnAsync(BotApplication botApplication, CoreActivity activity, NextTurn nextTurn, CancellationToken cancellationToken = default) { + AspNetCore.Http.IHttpContextAccessor httpContextAccessor = sp.GetRequiredService(); + ILogger logger = sp.GetRequiredService>(); if (botApplication is TeamsBotApplication tba) { #pragma warning disable CA2000 // Dispose objects before losing scope - TurnContext turnContext = new(new CompatBotAdapter(tba), activity.ToCompatActivity()); + TurnContext turnContext = new(new CompatBotAdapter(tba, httpContextAccessor, logger), activity.ToCompatActivity()); #pragma warning restore CA2000 // Dispose objects before losing scope - turnContext.TurnState.Add( new CompatUserTokenClient(botApplication.UserTokenClient) ); diff --git a/core/src/Microsoft.Teams.Bot.Compat/CompatBotAdapter.cs b/core/src/Microsoft.Teams.Bot.Compat/CompatBotAdapter.cs index 706bf423..f611918e 100644 --- a/core/src/Microsoft.Teams.Bot.Compat/CompatBotAdapter.cs +++ b/core/src/Microsoft.Teams.Bot.Compat/CompatBotAdapter.cs @@ -112,8 +112,11 @@ private void WriteInvokeResponseToHttpResponse(InvokeResponse? invokeResponse) response.StatusCode = invokeResponse.Status; using StreamWriter httpResponseStreamWriter = new(response.BodyWriter.AsStream()); using JsonTextWriter httpResponseJsonWriter = new(httpResponseStreamWriter); - logger.LogTrace("Sending Invoke Response: \n {InvokeResponse} \n", System.Text.Json.JsonSerializer.Serialize(invokeResponse.Body, _writeIndentedJsonOptions)); - Microsoft.Bot.Builder.Integration.AspNet.Core.HttpHelper.BotMessageSerializer.Serialize(httpResponseJsonWriter, invokeResponse.Body); + logger.LogTrace("Sending Invoke Response: \n {InvokeResponse} with status: {Status} \n", System.Text.Json.JsonSerializer.Serialize(invokeResponse.Body, _writeIndentedJsonOptions), invokeResponse.Status); + if (invokeResponse.Body is not null) + { + Microsoft.Bot.Builder.Integration.AspNet.Core.HttpHelper.BotMessageSerializer.Serialize(httpResponseJsonWriter, invokeResponse.Body); + } } else { From a354cc986f28c15d8915d9d73667f4747eaac75f Mon Sep 17 00:00:00 2001 From: Rido Date: Mon, 26 Jan 2026 06:53:41 -0800 Subject: [PATCH 2/4] Remove CompatMiddlewareAdapter (#286) The CompatMiddlware Adapter was creating a new TurnContext, and that created problems with Middleware. Instead of trying to adapt the middlware, we can just run the BF Middleware pipeline --- core/samples/CompatBot/MyCompatMiddleware.cs | 7 +- core/samples/CompatBot/Program.cs | 1 + .../CompatAdapter.cs | 8 +-- .../CompatAdapterMiddleware.cs | 65 ------------------- 4 files changed, 7 insertions(+), 74 deletions(-) delete mode 100644 core/src/Microsoft.Teams.Bot.Compat/CompatAdapterMiddleware.cs diff --git a/core/samples/CompatBot/MyCompatMiddleware.cs b/core/samples/CompatBot/MyCompatMiddleware.cs index 084384b6..ef87a97e 100644 --- a/core/samples/CompatBot/MyCompatMiddleware.cs +++ b/core/samples/CompatBot/MyCompatMiddleware.cs @@ -7,11 +7,14 @@ namespace CompatBot { public class MyCompatMiddleware : Microsoft.Bot.Builder.IMiddleware { - public Task OnTurnAsync(ITurnContext turnContext, NextDelegate next, CancellationToken cancellationToken = default) + public async Task OnTurnAsync(ITurnContext turnContext, NextDelegate next, CancellationToken cancellationToken = default) { Console.WriteLine("MyCompatMiddleware: OnTurnAsync"); Console.WriteLine(turnContext.Activity.Text); - return next(cancellationToken); + + await turnContext.SendActivityAsync(MessageFactory.Text("Hello from MyCompatMiddleware!"), cancellationToken); + + await next(cancellationToken).ConfigureAwait(false); } } } diff --git a/core/samples/CompatBot/Program.cs b/core/samples/CompatBot/Program.cs index 81f04a1d..ee8614ee 100644 --- a/core/samples/CompatBot/Program.cs +++ b/core/samples/CompatBot/Program.cs @@ -32,6 +32,7 @@ CompatAdapter compatAdapter = (CompatAdapter)app.Services.GetRequiredService(); compatAdapter.Use(new MyCompatMiddleware()); +compatAdapter.Use(new MyCompatMiddleware()); app.MapPost("/api/messages", async (IBotFrameworkHttpAdapter adapter, IBot bot, HttpRequest request, HttpResponse response, CancellationToken ct) => await adapter.ProcessAsync(request, response, bot, ct)); diff --git a/core/src/Microsoft.Teams.Bot.Compat/CompatAdapter.cs b/core/src/Microsoft.Teams.Bot.Compat/CompatAdapter.cs index 55aa0e6f..4182b9ba 100644 --- a/core/src/Microsoft.Teams.Bot.Compat/CompatAdapter.cs +++ b/core/src/Microsoft.Teams.Bot.Compat/CompatAdapter.cs @@ -89,17 +89,11 @@ public async Task ProcessAsync(HttpRequest httpRequest, HttpResponse httpRespons CompatConnectorClient connectionClient = new(new CompatConversations(_teamsBotApplication.ConversationClient) { ServiceUrl = activity.ServiceUrl?.ToString() }); turnContext.TurnState.Add(connectionClient); turnContext.TurnState.Add(_teamsBotApplication.TeamsApiClient); - await bot.OnTurnAsync(turnContext, cancellationToken1).ConfigureAwait(false); + await MiddlewareSet.ReceiveActivityWithStatusAsync(turnContext, bot.OnTurnAsync, cancellationToken).ConfigureAwait(false); }; - try { - foreach (Microsoft.Bot.Builder.IMiddleware? middleware in MiddlewareSet) - { - _teamsBotApplication.Use(new CompatAdapterMiddleware(middleware, _sp)); - } - await _teamsBotApplication.ProcessAsync(httpRequest.HttpContext, cancellationToken).ConfigureAwait(false); } catch (Exception ex) diff --git a/core/src/Microsoft.Teams.Bot.Compat/CompatAdapterMiddleware.cs b/core/src/Microsoft.Teams.Bot.Compat/CompatAdapterMiddleware.cs deleted file mode 100644 index 1df8acbf..00000000 --- a/core/src/Microsoft.Teams.Bot.Compat/CompatAdapterMiddleware.cs +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using Microsoft.Bot.Builder; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Microsoft.Teams.Bot.Apps; -using Microsoft.Teams.Bot.Core; -using Microsoft.Teams.Bot.Core.Schema; - -namespace Microsoft.Teams.Bot.Compat; - -/// -/// Adapts Bot Framework SDK middleware to work with the Teams Bot Core middleware pipeline. -/// -/// -/// This adapter enables legacy Bot Framework middleware components to be used in the new Teams Bot Core architecture. -/// It converts CoreActivity instances to Bot Framework Activity format, creates appropriate turn contexts with -/// compatibility clients (UserTokenClient and ConnectorClient), and delegates processing to the Bot Framework middleware. -/// This allows gradual migration from Bot Framework SDK to Teams Bot Core while preserving existing middleware investments. -/// -/// The Bot Framework middleware component to adapt into the Teams Bot Core pipeline. -/// The service provider used to resolve required services such as HTTP context accessor and logger. -internal sealed class CompatAdapterMiddleware(IMiddleware bfMiddleWare, IServiceProvider sp) : ITurnMiddleWare -{ - /// - /// Processes a turn by converting the CoreActivity to Bot Framework format and invoking the wrapped middleware. - /// - /// The bot application processing the turn. Must be a TeamsBotApplication instance. - /// The activity to process in Core format. - /// A delegate to invoke the next middleware in the pipeline. - /// A cancellation token that can be used to cancel the asynchronous operation. - /// A task that represents the asynchronous operation. - public Task OnTurnAsync(BotApplication botApplication, CoreActivity activity, NextTurn nextTurn, CancellationToken cancellationToken = default) - { - AspNetCore.Http.IHttpContextAccessor httpContextAccessor = sp.GetRequiredService(); - ILogger logger = sp.GetRequiredService>(); - - if (botApplication is TeamsBotApplication tba) - { -#pragma warning disable CA2000 // Dispose objects before losing scope - TurnContext turnContext = new(new CompatBotAdapter(tba, httpContextAccessor, logger), activity.ToCompatActivity()); -#pragma warning restore CA2000 // Dispose objects before losing scope - turnContext.TurnState.Add( - new CompatUserTokenClient(botApplication.UserTokenClient) - ); - - turnContext.TurnState.Add( - new CompatConnectorClient( - new CompatConversations(botApplication.ConversationClient) - { - ServiceUrl = activity.ServiceUrl?.ToString() - } - ) - ); - - turnContext.TurnState.Add(tba.TeamsApiClient); - - return bfMiddleWare.OnTurnAsync(turnContext, (activity) - => nextTurn(cancellationToken), cancellationToken); - } - return Task.CompletedTask; - } - -} From 92c31d7a8476982151a98fe1f22f875fc2308a90 Mon Sep 17 00:00:00 2001 From: "Ricardo Minguez Pablos (RIDO)" Date: Mon, 26 Jan 2026 19:21:54 -0800 Subject: [PATCH 3/4] Improve null safety for activity sender/recipient properties Enhance null handling for From/Recipient properties in activities and related classes. Make these properties nullable, add null checks before assignment, and update method calls to use null-conditional operators. Adjust JSON array deserialization to return null instead of empty lists when input is null. These changes increase robustness when handling incomplete or missing activity data. --- .../Schema/Entities/Entity.cs | 4 ++-- .../Schema/TeamsActivity.cs | 22 ++++++++++++++----- .../Schema/TeamsAttachment.cs | 2 +- .../CompatConversations.cs | 2 +- .../CompatTeamsInfo.cs | 2 +- .../ConversationClient.cs | 7 +++--- .../Schema/CoreActivity.cs | 4 ++-- .../Schema/CoreActivityBuilder.cs | 18 ++++++++------- 8 files changed, 38 insertions(+), 23 deletions(-) diff --git a/core/src/Microsoft.Teams.Bot.Apps/Schema/Entities/Entity.cs b/core/src/Microsoft.Teams.Bot.Apps/Schema/Entities/Entity.cs index b0186cb3..dc3d068e 100644 --- a/core/src/Microsoft.Teams.Bot.Apps/Schema/Entities/Entity.cs +++ b/core/src/Microsoft.Teams.Bot.Apps/Schema/Entities/Entity.cs @@ -45,9 +45,9 @@ public class EntityList : List /// public static EntityList FromJsonArray(JsonArray? jsonArray, JsonSerializerOptions? options = null) { - if (jsonArray == null) + if (jsonArray is null) { - return []; + return null!; } EntityList entities = []; foreach (JsonNode? item in jsonArray) diff --git a/core/src/Microsoft.Teams.Bot.Apps/Schema/TeamsActivity.cs b/core/src/Microsoft.Teams.Bot.Apps/Schema/TeamsActivity.cs index e2680bd7..7851dfee 100644 --- a/core/src/Microsoft.Teams.Bot.Apps/Schema/TeamsActivity.cs +++ b/core/src/Microsoft.Teams.Bot.Apps/Schema/TeamsActivity.cs @@ -87,9 +87,18 @@ protected TeamsActivity(CoreActivity activity) : base(activity) { ChannelData = new TeamsChannelData(activity.ChannelData); } - From = new TeamsConversationAccount(activity.From); - Recipient = new TeamsConversationAccount(activity.Recipient); + + if (activity.From is not null) + { + From = new TeamsConversationAccount(activity.From); + } + + if (activity.Recipient is not null) + { + Recipient = new TeamsConversationAccount(activity.Recipient); + } Conversation = new TeamsConversation(activity.Conversation); + Attachments = TeamsAttachment.FromJArray(activity.Attachments); Entities = EntityList.FromJsonArray(activity.Entities); @@ -104,7 +113,10 @@ internal TeamsActivity Rebase() { base.Attachments = this.Attachments?.ToJsonArray(); base.Entities = this.Entities?.ToJsonArray(); - base.ChannelData = new TeamsChannelData(this.ChannelData); + if (this.ChannelData is not null) + { + base.ChannelData = new TeamsChannelData(this.ChannelData); + } base.From = this.From; base.Recipient = this.Recipient; base.Conversation = this.Conversation; @@ -116,12 +128,12 @@ internal TeamsActivity Rebase() /// /// Gets or sets the account information for the sender of the Teams conversation. /// - [JsonPropertyName("from")] public new TeamsConversationAccount From { get; set; } + [JsonPropertyName("from")] public new TeamsConversationAccount? From { get; set; } /// /// Gets or sets the account information for the recipient of the Teams conversation. /// - [JsonPropertyName("recipient")] public new TeamsConversationAccount Recipient { get; set; } + [JsonPropertyName("recipient")] public new TeamsConversationAccount? Recipient { get; set; } /// /// Gets or sets the conversation information for the Teams conversation. diff --git a/core/src/Microsoft.Teams.Bot.Apps/Schema/TeamsAttachment.cs b/core/src/Microsoft.Teams.Bot.Apps/Schema/TeamsAttachment.cs index 1746378c..21a7b22f 100644 --- a/core/src/Microsoft.Teams.Bot.Apps/Schema/TeamsAttachment.cs +++ b/core/src/Microsoft.Teams.Bot.Apps/Schema/TeamsAttachment.cs @@ -35,7 +35,7 @@ static internal IList FromJArray(JsonArray? jsonArray) { if (jsonArray is null) { - return []; + return null!; } List attachments = []; foreach (JsonNode? item in jsonArray) diff --git a/core/src/Microsoft.Teams.Bot.Compat/CompatConversations.cs b/core/src/Microsoft.Teams.Bot.Compat/CompatConversations.cs index 75b161ec..62e12751 100644 --- a/core/src/Microsoft.Teams.Bot.Compat/CompatConversations.cs +++ b/core/src/Microsoft.Teams.Bot.Compat/CompatConversations.cs @@ -58,7 +58,7 @@ public async Task> CreateCon CreateConversationResponse res = await _client.CreateConversationAsync( convoParams, new Uri(ServiceUrl), - AgenticIdentity.FromProperties(convoParams.Activity?.From.Properties), + AgenticIdentity.FromProperties(convoParams.Activity?.From?.Properties), convertedHeaders, cancellationToken).ConfigureAwait(false); diff --git a/core/src/Microsoft.Teams.Bot.Compat/CompatTeamsInfo.cs b/core/src/Microsoft.Teams.Bot.Compat/CompatTeamsInfo.cs index e8711045..3320dc12 100644 --- a/core/src/Microsoft.Teams.Bot.Compat/CompatTeamsInfo.cs +++ b/core/src/Microsoft.Teams.Bot.Compat/CompatTeamsInfo.cs @@ -54,7 +54,7 @@ private static string GetServiceUrl(ITurnContext turnContext) private static AgenticIdentity GetIdentity(ITurnContext turnContext) { var coreActivity = ((Activity)turnContext.Activity).FromCompatActivity(); - return AgenticIdentity.FromProperties(coreActivity.From.Properties) ?? new AgenticIdentity(); + return AgenticIdentity.FromProperties(coreActivity.From?.Properties) ?? new AgenticIdentity(); } #endregion diff --git a/core/src/Microsoft.Teams.Bot.Core/ConversationClient.cs b/core/src/Microsoft.Teams.Bot.Core/ConversationClient.cs index bf89a24e..0aa088c0 100644 --- a/core/src/Microsoft.Teams.Bot.Core/ConversationClient.cs +++ b/core/src/Microsoft.Teams.Bot.Core/ConversationClient.cs @@ -51,6 +51,7 @@ public async Task SendActivityAsync(CoreActivity activity, string convId = conversationId.Length > 325 ? conversationId[..325] : conversationId; url = $"{activity.ServiceUrl.ToString().TrimEnd('/')}/v3/conversations/{convId}/activities"; } + activity.ServiceUrl = null; // do not serialize in the outgoing payload string body = activity.ToJson(); @@ -60,7 +61,7 @@ public async Task SendActivityAsync(CoreActivity activity, HttpMethod.Post, url, body, - CreateRequestOptions(activity.From.GetAgenticIdentity(), "sending activity", customHeaders), + CreateRequestOptions(activity.From?.GetAgenticIdentity(), "sending activity", customHeaders), cancellationToken).ConfigureAwait(false))!; } @@ -90,7 +91,7 @@ public async Task UpdateActivityAsync(string conversatio HttpMethod.Put, url, body, - CreateRequestOptions(activity.From.GetAgenticIdentity(), "updating activity", customHeaders), + CreateRequestOptions(activity.From?.GetAgenticIdentity(), "updating activity", customHeaders), cancellationToken).ConfigureAwait(false))!; } @@ -144,7 +145,7 @@ await DeleteActivityAsync( activity.Conversation.Id, activity.Id, activity.ServiceUrl, - activity.From.GetAgenticIdentity(), + activity.From?.GetAgenticIdentity(), customHeaders, cancellationToken).ConfigureAwait(false); } diff --git a/core/src/Microsoft.Teams.Bot.Core/Schema/CoreActivity.cs b/core/src/Microsoft.Teams.Bot.Core/Schema/CoreActivity.cs index 3fc0846b..4c88e717 100644 --- a/core/src/Microsoft.Teams.Bot.Core/Schema/CoreActivity.cs +++ b/core/src/Microsoft.Teams.Bot.Core/Schema/CoreActivity.cs @@ -52,11 +52,11 @@ public class CoreActivity /// /// Gets or sets the account that sent this activity. /// - [JsonPropertyName("from")] public ConversationAccount From { get; set; } = new(); + [JsonPropertyName("from")] public ConversationAccount? From { get; set; } /// /// Gets or sets the account that should receive this activity. /// - [JsonPropertyName("recipient")] public ConversationAccount Recipient { get; set; } = new(); + [JsonPropertyName("recipient")] public ConversationAccount? Recipient { get; set; } /// /// Gets or sets the conversation in which this activity is taking place. /// diff --git a/core/src/Microsoft.Teams.Bot.Core/Schema/CoreActivityBuilder.cs b/core/src/Microsoft.Teams.Bot.Core/Schema/CoreActivityBuilder.cs index d05e635e..00dbdb8a 100644 --- a/core/src/Microsoft.Teams.Bot.Core/Schema/CoreActivityBuilder.cs +++ b/core/src/Microsoft.Teams.Bot.Core/Schema/CoreActivityBuilder.cs @@ -37,17 +37,13 @@ protected CoreActivityBuilder(TActivity activity) public TBuilder WithConversationReference(TActivity activity) { ArgumentNullException.ThrowIfNull(activity); - ArgumentNullException.ThrowIfNull(activity.ChannelId); ArgumentNullException.ThrowIfNull(activity.ServiceUrl); ArgumentNullException.ThrowIfNull(activity.Conversation); - ArgumentNullException.ThrowIfNull(activity.From); ArgumentNullException.ThrowIfNull(activity.Recipient); - + WithServiceUrl(activity.ServiceUrl); - WithChannelId(activity.ChannelId); SetConversation(activity.Conversation); SetFrom(activity.Recipient); - SetRecipient(activity.From); return (TBuilder)this; } @@ -128,9 +124,12 @@ public TBuilder WithProperty(string name, T? value) /// /// The sender account. /// The builder instance for chaining. - public TBuilder WithFrom(ConversationAccount from) + public TBuilder WithFrom(ConversationAccount? from) { - SetFrom(from); + if (from is not null) + { + SetFrom(from); + } return (TBuilder)this; } @@ -163,7 +162,10 @@ public TBuilder WithConversation(Conversation conversation) /// The builder instance for chaining. public virtual TBuilder WithChannelData(ChannelData? channelData) { - _activity.ChannelData = channelData; + if (channelData is not null) + { + _activity.ChannelData = channelData; + } return (TBuilder)this; } From dad81c4d12b6a652276acedb3973e3c8586e0cb4 Mon Sep 17 00:00:00 2001 From: "Ricardo Minguez Pablos (RIDO)" Date: Mon, 26 Jan 2026 19:43:27 -0800 Subject: [PATCH 4/4] Refactor activity builder From/Recipient handling & tests - Update CoreActivityBuilder and TeamsActivityBuilder to set From to Recipient and leave Recipient null when From is missing in WithConversationReference. - Remove default assertions that From/Recipient are non-null in builder tests; add explicit null checks where needed. - WithConversationReference no longer throws for null ChannelId; throws for null Recipient. - WithChannelData(null) no longer overwrites existing ChannelData. - Add null-conditional access for ca.From?.Properties in EchoBot. - Explicitly set Recipient in test activities to prevent null reference errors. - Add settings.local.json with Bash command permissions. --- core/.claude/settings.local.json | 9 ++ core/samples/CompatBot/EchoBot.cs | 2 +- .../TeamsActivityBuilderTests.cs | 96 +++++++++---------- .../TeamsActivityTests.cs | 2 +- .../BotApplicationTests.cs | 9 +- .../CoreActivityBuilderTests.cs | 40 ++++---- .../MiddlewareTests.cs | 12 ++- .../Schema/CoreActivityTests.cs | 7 +- 8 files changed, 100 insertions(+), 77 deletions(-) create mode 100644 core/.claude/settings.local.json diff --git a/core/.claude/settings.local.json b/core/.claude/settings.local.json new file mode 100644 index 00000000..279b23c3 --- /dev/null +++ b/core/.claude/settings.local.json @@ -0,0 +1,9 @@ +{ + "permissions": { + "allow": [ + "Bash(dotnet build:*)", + "Bash(dotnet test:*)", + "Bash(dotnet clean:*)" + ] + } +} diff --git a/core/samples/CompatBot/EchoBot.cs b/core/samples/CompatBot/EchoBot.cs index 6b8da45e..e349e430 100644 --- a/core/samples/CompatBot/EchoBot.cs +++ b/core/samples/CompatBot/EchoBot.cs @@ -149,7 +149,7 @@ await conversationClient.UpdateActivityAsync( await Task.Delay(2000, cancellationToken); - await conversationClient.DeleteActivityAsync(cr.Conversation.Id, res.Id!, new Uri(turnContext.Activity.ServiceUrl), AgenticIdentity.FromProperties(ca.From.Properties), null, cancellationToken); + await conversationClient.DeleteActivityAsync(cr.Conversation.Id, res.Id!, new Uri(turnContext.Activity.ServiceUrl), AgenticIdentity.FromProperties(ca.From?.Properties), null, cancellationToken); await turnContext.SendActivityAsync(MessageFactory.Text("Proactive message sent and deleted."), cancellationToken); } diff --git a/core/test/Microsoft.Teams.Bot.Apps.UnitTests/TeamsActivityBuilderTests.cs b/core/test/Microsoft.Teams.Bot.Apps.UnitTests/TeamsActivityBuilderTests.cs index 1964ef5c..a88ecf52 100644 --- a/core/test/Microsoft.Teams.Bot.Apps.UnitTests/TeamsActivityBuilderTests.cs +++ b/core/test/Microsoft.Teams.Bot.Apps.UnitTests/TeamsActivityBuilderTests.cs @@ -21,8 +21,6 @@ public void Constructor_DefaultConstructor_CreatesNewActivity() TeamsActivity activity = builder.Build(); Assert.NotNull(activity); - Assert.NotNull(activity.From); - Assert.NotNull(activity.Recipient); Assert.NotNull(activity.Conversation); } @@ -113,6 +111,7 @@ public void WithFrom_SetsSenderAccount() .WithFrom(fromAccount) .Build(); + Assert.NotNull(activity.From); Assert.Equal("sender-id", activity.From.Id); Assert.Equal("Sender Name", activity.From.Name); } @@ -130,6 +129,7 @@ public void WithRecipient_SetsRecipientAccount() .WithRecipient(recipientAccount) .Build(); + Assert.NotNull(activity.Recipient); Assert.Equal("recipient-id", activity.Recipient.Id); Assert.Equal("Recipient Name", activity.Recipient.Name); } @@ -448,7 +448,9 @@ public void FluentAPI_CompleteActivity_BuildsCorrectly() Assert.Equal("activity-123", activity.Id); Assert.Equal("msteams", activity.ChannelId); Assert.Equal("User Test message", activity.Properties["text"]); + Assert.NotNull(activity.From); Assert.Equal("sender-id", activity.From.Id); + Assert.NotNull(activity.Recipient); Assert.Equal("recipient-id", activity.Recipient.Id); Assert.Equal("conv-id", activity.Conversation.Id); Assert.NotNull(activity.Entities); @@ -522,20 +524,18 @@ public void AddMention_UpdatesBaseEntityCollection() [Fact] public void WithChannelData_NullValue_SetsToNull() { - TeamsActivity activity = builder - .WithChannelData(null!) - .Build(); + TeamsChannelData channelData = new() { TeamsChannelId = "channel-1" }; + TeamsActivityBuilder testBuilder = TeamsActivity.CreateBuilder(); + testBuilder.WithChannelData(channelData); + testBuilder.WithChannelData(null!); + TeamsActivity activity = testBuilder.Build(); Assert.Null(activity.ChannelData); } [Fact] - public void AddEntity_NullEntitiesCollection_InitializesCollection() + public void AddEntity_InitializesCollectionIfNeeded() { - TeamsActivity activity = builder.Build(); - - Assert.NotNull(activity.Entities); - ClientInfoEntity entity = new() { Locale = "en-US" }; builder.AddEntity(entity); @@ -545,12 +545,8 @@ public void AddEntity_NullEntitiesCollection_InitializesCollection() } [Fact] - public void AddAttachment_NullAttachmentsCollection_InitializesCollection() + public void AddAttachment_InitializesCollectionIfNeeded() { - TeamsActivity activity = builder.Build(); - - Assert.NotNull(activity.Attachments); - TeamsAttachment attachment = new() { ContentType = "text/html" }; builder.AddAttachment(attachment); @@ -582,19 +578,20 @@ public void WithConversationReference_WithNullActivity_ThrowsArgumentNullExcepti } [Fact] - public void WithConversationReference_WithNullChannelId_ThrowsArgumentNullException() + public void WithConversationReference_WithNullChannelId_DoesNotThrow() { - TeamsActivity sourceActivity = new() - { - ChannelId = null, - ServiceUrl = new Uri("https://test.com"), - Conversation = new TeamsConversation(new Conversation()), - From = new TeamsConversationAccount(new ConversationAccount()), - Recipient = new TeamsConversationAccount(new ConversationAccount()) - }; + TeamsActivity sourceActivity = TeamsActivity.CreateBuilder() + .WithServiceUrl(new Uri("https://test.com")) + .WithConversation(new TeamsConversation(new Conversation())) + .WithFrom(new TeamsConversationAccount(new ConversationAccount())) + .WithRecipient(new TeamsConversationAccount(new ConversationAccount { Id = "bot-1" })) + .Build(); - Assert.Throws(() => builder.WithConversationReference(sourceActivity)); + TeamsActivity result = builder.WithConversationReference(sourceActivity).Build(); + // ChannelId is not set by WithConversationReference when null + Assert.Equal(ActivityType.Message, result.Type); + Assert.NotNull(result.From); } [Fact] @@ -613,41 +610,42 @@ public void WithConversationReference_WithNullServiceUrl_ThrowsArgumentNullExcep } [Fact] - public void WithConversationReference_WithEmptyConversationId_DoesNotThrow() + public void WithConversationReference_WithEmptyConversationId_SetsFromRecipient() { - TeamsActivity sourceActivity = new() - { - ChannelId = "msteams", - ServiceUrl = new Uri("https://test.com"), - Conversation = new TeamsConversation(new Conversation()), - From = new TeamsConversationAccount(new ConversationAccount { Id = "user-1" }), - Recipient = new TeamsConversationAccount(new ConversationAccount { Id = "bot-1" }) - }; + TeamsActivity sourceActivity = TeamsActivity.CreateBuilder() + .WithChannelId("msteams") + .WithServiceUrl(new Uri("https://test.com")) + .WithConversation(new TeamsConversation(new Conversation())) + .WithFrom(new TeamsConversationAccount(new ConversationAccount { Id = "user-1" })) + .WithRecipient(new TeamsConversationAccount(new ConversationAccount { Id = "bot-1" })) + .Build(); TeamsActivity result = builder.WithConversationReference(sourceActivity).Build(); Assert.NotNull(result.Conversation); + Assert.NotNull(result.From); + Assert.Equal("bot-1", result.From.Id); } [Fact] - public void WithConversationReference_WithEmptyFromId_DoesNotThrow() + public void WithConversationReference_WithEmptyFromId_SetsFromRecipient() { - TeamsActivity sourceActivity = new() - { - ChannelId = "msteams", - ServiceUrl = new Uri("https://test.com"), - Conversation = new TeamsConversation(new Conversation { Id = "conv-1" }), - From = new TeamsConversationAccount(new ConversationAccount()), - Recipient = new TeamsConversationAccount(new ConversationAccount { Id = "bot-1" }) - }; + TeamsActivity sourceActivity = TeamsActivity.CreateBuilder() + .WithChannelId("msteams") + .WithServiceUrl(new Uri("https://test.com")) + .WithConversation(new TeamsConversation(new Conversation { Id = "conv-1" })) + .WithFrom(new TeamsConversationAccount(new ConversationAccount())) + .WithRecipient(new TeamsConversationAccount(new ConversationAccount { Id = "bot-1" })) + .Build(); TeamsActivity result = builder.WithConversationReference(sourceActivity).Build(); Assert.NotNull(result.From); + Assert.Equal("bot-1", result.From.Id); } [Fact] - public void WithConversationReference_WithEmptyRecipientId_DoesNotThrow() + public void WithConversationReference_WithEmptyRecipientId_ThrowsArgumentNullException() { TeamsActivity sourceActivity = new() { @@ -655,12 +653,10 @@ public void WithConversationReference_WithEmptyRecipientId_DoesNotThrow() ServiceUrl = new Uri("https://test.com"), Conversation = new TeamsConversation(new Conversation { Id = "conv-1" }), From = new TeamsConversationAccount(new ConversationAccount { Id = "user-1" }), - Recipient = new TeamsConversationAccount(new ConversationAccount()) + Recipient = null! }; - TeamsActivity result = builder.WithConversationReference(sourceActivity).Build(); - - Assert.NotNull(result.Recipient); + Assert.Throws(() => builder.WithConversationReference(sourceActivity)); } [Fact] @@ -676,6 +672,7 @@ public void WithFrom_WithBaseConversationAccount_ConvertsToTeamsConversationAcco .WithFrom(baseAccount) .Build(); + Assert.NotNull(activity.From); Assert.IsType(activity.From); Assert.Equal("user-123", activity.From.Id); Assert.Equal("User Name", activity.From.Name); @@ -694,6 +691,7 @@ public void WithRecipient_WithBaseConversationAccount_ConvertsToTeamsConversatio .WithRecipient(baseAccount) .Build(); + Assert.NotNull(activity.Recipient); Assert.IsType(activity.Recipient); Assert.Equal("bot-123", activity.Recipient.Id); Assert.Equal("Bot Name", activity.Recipient.Name); @@ -834,7 +832,9 @@ public void IntegrationTest_CreateComplexActivity() Assert.Equal(serviceUrl, activity.ServiceUrl); Assert.Equal("msteams", activity.ChannelId); Assert.Equal("Manager Please review this document", activity.Properties["text"]); + Assert.NotNull(activity.From); Assert.Equal("bot-id", activity.From.Id); + Assert.NotNull(activity.Recipient); Assert.Equal("user-id", activity.Recipient.Id); Assert.Equal("conv-001", activity.Conversation.Id); Assert.Equal("tenant-001", activity.Conversation.TenantId); diff --git a/core/test/Microsoft.Teams.Bot.Apps.UnitTests/TeamsActivityTests.cs b/core/test/Microsoft.Teams.Bot.Apps.UnitTests/TeamsActivityTests.cs index 77c9b93b..791f336c 100644 --- a/core/test/Microsoft.Teams.Bot.Apps.UnitTests/TeamsActivityTests.cs +++ b/core/test/Microsoft.Teams.Bot.Apps.UnitTests/TeamsActivityTests.cs @@ -28,7 +28,7 @@ public void DeserializeTeamsActivityWithTeamsChannelData() TeamsChannelData tcd = activity.ChannelData!; Assert.Equal("19:6848757105754c8981c67612732d9aa7@thread.tacv2", tcd.TeamsChannelId); Assert.Equal("19:6848757105754c8981c67612732d9aa7@thread.tacv2", tcd.Channel!.Id); - Assert.Equal("b15a9416-0ad3-4172-9210-7beb711d3f70", activity.From.AadObjectId); + Assert.Equal("b15a9416-0ad3-4172-9210-7beb711d3f70", activity.From!.AadObjectId); Assert.Equal("19:6848757105754c8981c67612732d9aa7@thread.tacv2;messageid=1759881511856", activity.Conversation.Id); Assert.NotNull(activity.Attachments); diff --git a/core/test/Microsoft.Teams.Bot.Core.UnitTests/BotApplicationTests.cs b/core/test/Microsoft.Teams.Bot.Core.UnitTests/BotApplicationTests.cs index 9f0dc316..058f4153 100644 --- a/core/test/Microsoft.Teams.Bot.Core.UnitTests/BotApplicationTests.cs +++ b/core/test/Microsoft.Teams.Bot.Core.UnitTests/BotApplicationTests.cs @@ -54,7 +54,8 @@ public async Task ProcessAsync_WithValidActivity_ProcessesSuccessfully() CoreActivity activity = new() { Type = ActivityType.Message, - Id = "act123" + Id = "act123", + Recipient = new ConversationAccount() }; activity.Properties["text"] = "Test message"; activity.Recipient.Properties["appId"] = "test-app-id"; @@ -86,7 +87,8 @@ public async Task ProcessAsync_WithMiddleware_ExecutesMiddleware() CoreActivity activity = new() { Type = ActivityType.Message, - Id = "act123" + Id = "act123", + Recipient = new ConversationAccount() }; activity.Recipient.Properties["appId"] = "test-app-id"; @@ -130,7 +132,8 @@ public async Task ProcessAsync_WithException_ThrowsBotHandlerException() CoreActivity activity = new() { Type = ActivityType.Message, - Id = "act123" + Id = "act123", + Recipient = new ConversationAccount() }; activity.Recipient.Properties["appId"] = "test-app-id"; diff --git a/core/test/Microsoft.Teams.Bot.Core.UnitTests/CoreActivityBuilderTests.cs b/core/test/Microsoft.Teams.Bot.Core.UnitTests/CoreActivityBuilderTests.cs index c3f62fd0..14a6c79e 100644 --- a/core/test/Microsoft.Teams.Bot.Core.UnitTests/CoreActivityBuilderTests.cs +++ b/core/test/Microsoft.Teams.Bot.Core.UnitTests/CoreActivityBuilderTests.cs @@ -14,8 +14,6 @@ public void Constructor_DefaultConstructor_CreatesNewActivity() CoreActivity activity = builder.Build(); Assert.NotNull(activity); - Assert.NotNull(activity.From); - Assert.NotNull(activity.Recipient); Assert.NotNull(activity.Conversation); } @@ -104,6 +102,7 @@ public void WithFrom_SetsSenderAccount() .WithFrom(fromAccount) .Build(); + Assert.NotNull(activity.From); Assert.Equal("sender-id", activity.From.Id); Assert.Equal("Sender Name", activity.From.Name); } @@ -121,6 +120,7 @@ public void WithRecipient_SetsRecipientAccount() .WithRecipient(recipientAccount) .Build(); + Assert.NotNull(activity.Recipient); Assert.Equal("recipient-id", activity.Recipient.Id); Assert.Equal("Recipient Name", activity.Recipient.Name); } @@ -181,7 +181,9 @@ public void FluentAPI_CompleteActivity_BuildsCorrectly() Assert.Equal("activity-123", activity.Id); Assert.Equal("msteams", activity.ChannelId); Assert.Equal("Test message", activity.Properties["text"]?.ToString()); + Assert.NotNull(activity.From); Assert.Equal("sender-id", activity.From.Id); + Assert.NotNull(activity.Recipient); Assert.Equal("recipient-id", activity.Recipient.Id); Assert.Equal("conv-id", activity.Conversation.Id); } @@ -238,7 +240,7 @@ public void WithConversationReference_WithNullActivity_ThrowsArgumentNullExcepti } [Fact] - public void WithConversationReference_WithNullChannelId_ThrowsArgumentNullException() + public void WithConversationReference_WithNullChannelId_DoesNotThrow() { CoreActivityBuilder builder = new(); CoreActivity sourceActivity = new() @@ -250,7 +252,9 @@ public void WithConversationReference_WithNullChannelId_ThrowsArgumentNullExcept Recipient = new ConversationAccount() }; - Assert.Throws(() => builder.WithConversationReference(sourceActivity)); + CoreActivity activity = builder.WithConversationReference(sourceActivity).Build(); + // ChannelId is not set by WithConversationReference when null + Assert.Equal(ActivityType.Message, activity.Type); } [Fact] @@ -286,7 +290,7 @@ public void WithConversationReference_WithNullConversation_ThrowsArgumentNullExc } [Fact] - public void WithConversationReference_WithNullFrom_ThrowsArgumentNullException() + public void WithConversationReference_WithNullFrom_SetsFromToRecipient() { CoreActivityBuilder builder = new(); CoreActivity sourceActivity = new() @@ -295,10 +299,12 @@ public void WithConversationReference_WithNullFrom_ThrowsArgumentNullException() ServiceUrl = new Uri("https://test.com"), Conversation = new Conversation(), From = null!, - Recipient = new ConversationAccount() + Recipient = new ConversationAccount { Id = "bot-1", Name = "Bot" } }; - Assert.Throws(() => builder.WithConversationReference(sourceActivity)); + CoreActivity activity = builder.WithConversationReference(sourceActivity).Build(); + Assert.NotNull(activity.From); + Assert.Equal("bot-1", activity.From.Id); } [Fact] @@ -333,17 +339,16 @@ public void WithConversationReference_AppliesConversationReference() .WithConversationReference(sourceActivity) .Build(); - Assert.Equal("msteams", activity.ChannelId); Assert.Equal(new Uri("https://smba.trafficmanager.net/teams/"), activity.ServiceUrl); Assert.Equal("conv-123", activity.Conversation.Id); + Assert.NotNull(activity.From); Assert.Equal("bot-1", activity.From.Id); Assert.Equal("Bot", activity.From.Name); - Assert.Equal("user-1", activity.Recipient.Id); - Assert.Equal("User One", activity.Recipient.Name); + Assert.Null(activity.Recipient); } [Fact] - public void WithConversationReference_SwapsFromAndRecipient() + public void WithConversationReference_SetsFromFromRecipient() { CoreActivity incomingActivity = new() { @@ -358,21 +363,21 @@ public void WithConversationReference_SwapsFromAndRecipient() .WithConversationReference(incomingActivity) .Build(); + Assert.NotNull(replyActivity.From); Assert.Equal("bot-id", replyActivity.From.Id); Assert.Equal("Bot", replyActivity.From.Name); - Assert.Equal("user-id", replyActivity.Recipient.Id); - Assert.Equal("User", replyActivity.Recipient.Name); + Assert.Null(replyActivity.Recipient); } [Fact] - public void WithChannelData_WithNullValue_SetsToNull() + public void WithChannelData_WithNullValue_DoesNotOverwrite() { CoreActivity activity = new CoreActivityBuilder() .WithChannelData(new ChannelData()) .WithChannelData(null) .Build(); - Assert.Null(activity.ChannelData); + Assert.NotNull(activity.ChannelData); } [Fact] @@ -423,8 +428,9 @@ public void WithConversationReference_ChainedWithOtherMethods_MaintainsFluentInt .Build(); Assert.Equal(ActivityType.Message, activity.Type); + Assert.NotNull(activity.From); Assert.Equal("bot-1", activity.From.Id); - Assert.Equal("user-1", activity.Recipient.Id); + Assert.Null(activity.Recipient); } [Fact] @@ -475,7 +481,9 @@ public void IntegrationTest_CreateComplexActivity() Assert.Equal("msg-001", activity.Id); Assert.Equal(serviceUrl, activity.ServiceUrl); Assert.Equal("msteams", activity.ChannelId); + Assert.NotNull(activity.From); Assert.Equal("bot-id", activity.From.Id); + Assert.NotNull(activity.Recipient); Assert.Equal("user-id", activity.Recipient.Id); Assert.Equal("conv-001", activity.Conversation.Id); Assert.NotNull(activity.ChannelData); diff --git a/core/test/Microsoft.Teams.Bot.Core.UnitTests/MiddlewareTests.cs b/core/test/Microsoft.Teams.Bot.Core.UnitTests/MiddlewareTests.cs index be9d1fd6..614853dc 100644 --- a/core/test/Microsoft.Teams.Bot.Core.UnitTests/MiddlewareTests.cs +++ b/core/test/Microsoft.Teams.Bot.Core.UnitTests/MiddlewareTests.cs @@ -66,7 +66,8 @@ public async Task Middleware_ExecutesInOrder() CoreActivity activity = new() { Type = ActivityType.Message, - Id = "act123" + Id = "act123", + Recipient = new ConversationAccount() }; activity.Recipient.Properties["appId"] = "test-app-id"; @@ -112,7 +113,8 @@ public async Task Middleware_CanShortCircuit() CoreActivity activity = new() { Type = ActivityType.Message, - Id = "act123" + Id = "act123", + Recipient = new ConversationAccount() }; activity.Recipient.Properties["appId"] = "test-app-id"; @@ -156,7 +158,8 @@ public async Task Middleware_ReceivesCancellationToken() CoreActivity activity = new() { Type = ActivityType.Message, - Id = "act123" + Id = "act123", + Recipient = new ConversationAccount() }; activity.Recipient.Properties["appId"] = "test-app-id"; @@ -196,7 +199,8 @@ public async Task Middleware_ReceivesActivity() CoreActivity activity = new() { Type = ActivityType.Message, - Id = "act123" + Id = "act123", + Recipient = new ConversationAccount() }; activity.Recipient.Properties["appId"] = "test-app-id"; diff --git a/core/test/Microsoft.Teams.Bot.Core.UnitTests/Schema/CoreActivityTests.cs b/core/test/Microsoft.Teams.Bot.Core.UnitTests/Schema/CoreActivityTests.cs index 36835f3b..05e4e831 100644 --- a/core/test/Microsoft.Teams.Bot.Core.UnitTests/Schema/CoreActivityTests.cs +++ b/core/test/Microsoft.Teams.Bot.Core.UnitTests/Schema/CoreActivityTests.cs @@ -115,7 +115,7 @@ public void Deserialize_Unkown__Fields_In_KnownObjects() Assert.Equal("message", act.Type); Assert.NotNull(act.From); Assert.IsType(act.From); - Assert.Equal("1", act.From!.Id); + Assert.Equal("1", act.From.Id); Assert.Equal("tester", act.From.Name); Assert.True(act.From.Properties.ContainsKey("aadObjectId")); Assert.Equal("123", act.From.Properties["aadObjectId"]?.ToString()); @@ -287,14 +287,13 @@ public void CreateReply() Assert.NotNull(reply); Assert.Equal(ActivityType.Message, reply.Type); Assert.Equal("reply", reply.Properties["text"]); - Assert.Equal("channel1", reply.ChannelId); Assert.NotNull(reply.ServiceUrl); Assert.Equal("http://service.url/", reply.ServiceUrl.ToString()); Assert.Equal("conversation1", reply.Conversation.Id); + Assert.NotNull(reply.From); Assert.Equal("bot1", reply.From.Id); Assert.Equal("Bot One", reply.From.Name); - Assert.Equal("user1", reply.Recipient.Id); - Assert.Equal("User One", reply.Recipient.Name); + Assert.Null(reply.Recipient); } [Fact]