From 69eabcd1fc5e2ec5ff54237f5866b9c5c7f6c640 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Mon, 23 Feb 2026 18:27:21 +0000 Subject: [PATCH] .NET: Fix case-sensitive property mismatch in CosmosChatHistoryProvider queries (#3485) * Initial plan * Fix case-sensitivity bug in Cosmos queries and add tests Co-authored-by: markwallace-microsoft <127216156+markwallace-microsoft@users.noreply.github.com> * Fix style issues and update tests for new API Co-authored-by: westey-m <164392973+westey-m@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: markwallace-microsoft <127216156+markwallace-microsoft@users.noreply.github.com> Co-authored-by: westey <164392973+westey-m@users.noreply.github.com> --- .../CosmosChatHistoryProvider.cs | 4 +- .../CosmosChatHistoryProviderTests.cs | 118 ++++++++++++++++++ 2 files changed, 120 insertions(+), 2 deletions(-) diff --git a/dotnet/src/Microsoft.Agents.AI.CosmosNoSql/CosmosChatHistoryProvider.cs b/dotnet/src/Microsoft.Agents.AI.CosmosNoSql/CosmosChatHistoryProvider.cs index c4e13fae29..f1670fbb84 100644 --- a/dotnet/src/Microsoft.Agents.AI.CosmosNoSql/CosmosChatHistoryProvider.cs +++ b/dotnet/src/Microsoft.Agents.AI.CosmosNoSql/CosmosChatHistoryProvider.cs @@ -435,7 +435,7 @@ public sealed class CosmosChatHistoryProvider : ChatHistoryProvider, IDisposable var partitionKey = BuildPartitionKey(state); // Efficient count query - var query = new QueryDefinition("SELECT VALUE COUNT(1) FROM c WHERE c.conversationId = @conversationId AND c.Type = @type") + var query = new QueryDefinition("SELECT VALUE COUNT(1) FROM c WHERE c.conversationId = @conversationId AND c.type = @type") .WithParameter("@conversationId", state.ConversationId) .WithParameter("@type", "ChatMessage"); @@ -469,7 +469,7 @@ public sealed class CosmosChatHistoryProvider : ChatHistoryProvider, IDisposable var partitionKey = BuildPartitionKey(state); // Batch delete for efficiency - var query = new QueryDefinition("SELECT VALUE c.id FROM c WHERE c.conversationId = @conversationId AND c.Type = @type") + var query = new QueryDefinition("SELECT VALUE c.id FROM c WHERE c.conversationId = @conversationId AND c.type = @type") .WithParameter("@conversationId", state.ConversationId) .WithParameter("@type", "ChatMessage"); diff --git a/dotnet/tests/Microsoft.Agents.AI.CosmosNoSql.UnitTests/CosmosChatHistoryProviderTests.cs b/dotnet/tests/Microsoft.Agents.AI.CosmosNoSql.UnitTests/CosmosChatHistoryProviderTests.cs index 0ab605e4d6..3cac6ff971 100644 --- a/dotnet/tests/Microsoft.Agents.AI.CosmosNoSql.UnitTests/CosmosChatHistoryProviderTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.CosmosNoSql.UnitTests/CosmosChatHistoryProviderTests.cs @@ -834,6 +834,124 @@ public sealed class CosmosChatHistoryProviderTests : IAsyncLifetime, IDisposable Assert.Equal("Message 10", messageList[9].Text); } + [SkippableFact] + [Trait("Category", "CosmosDB")] + public async Task GetMessageCountAsync_WithMessages_ShouldReturnCorrectCountAsync() + { + // Arrange + this.SkipIfEmulatorNotAvailable(); + var session = CreateMockSession(); + const string ConversationId = "count-test-conversation"; + + using var provider = new CosmosChatHistoryProvider(this._connectionString, s_testDatabaseId, TestContainerId, + _ => new CosmosChatHistoryProvider.State(ConversationId)); + + // Add 5 messages + var messages = new List(); + for (int i = 1; i <= 5; i++) + { + messages.Add(new ChatMessage(ChatRole.User, $"Message {i}")); + } + + var context = new ChatHistoryProvider.InvokedContext(s_mockAgent, session, messages, []); + await provider.InvokedAsync(context); + + // Wait for eventual consistency + await Task.Delay(100); + + // Act + var count = await provider.GetMessageCountAsync(session); + + // Assert + Assert.Equal(5, count); + } + + [SkippableFact] + [Trait("Category", "CosmosDB")] + public async Task GetMessageCountAsync_WithNoMessages_ShouldReturnZeroAsync() + { + // Arrange + this.SkipIfEmulatorNotAvailable(); + var session = CreateMockSession(); + const string ConversationId = "empty-count-test-conversation"; + + using var provider = new CosmosChatHistoryProvider(this._connectionString, s_testDatabaseId, TestContainerId, + _ => new CosmosChatHistoryProvider.State(ConversationId)); + + // Act + var count = await provider.GetMessageCountAsync(session); + + // Assert + Assert.Equal(0, count); + } + + [SkippableFact] + [Trait("Category", "CosmosDB")] + public async Task ClearMessagesAsync_WithMessages_ShouldDeleteAndReturnCountAsync() + { + // Arrange + this.SkipIfEmulatorNotAvailable(); + var session = CreateMockSession(); + const string ConversationId = "clear-test-conversation"; + + using var provider = new CosmosChatHistoryProvider(this._connectionString, s_testDatabaseId, TestContainerId, + _ => new CosmosChatHistoryProvider.State(ConversationId)); + + // Add 3 messages + var messages = new List + { + new(ChatRole.User, "Message 1"), + new(ChatRole.Assistant, "Message 2"), + new(ChatRole.User, "Message 3") + }; + + var context = new ChatHistoryProvider.InvokedContext(s_mockAgent, session, messages, []); + await provider.InvokedAsync(context); + + // Wait for eventual consistency + await Task.Delay(100); + + // Verify messages exist + var countBefore = await provider.GetMessageCountAsync(session); + Assert.Equal(3, countBefore); + + // Act + var deletedCount = await provider.ClearMessagesAsync(session); + + // Wait for eventual consistency + await Task.Delay(100); + + // Assert + Assert.Equal(3, deletedCount); + + // Verify messages are deleted + var countAfter = await provider.GetMessageCountAsync(session); + Assert.Equal(0, countAfter); + + var invokingContext = new ChatHistoryProvider.InvokingContext(s_mockAgent, session, []); + var retrievedMessages = await provider.InvokingAsync(invokingContext); + Assert.Empty(retrievedMessages); + } + + [SkippableFact] + [Trait("Category", "CosmosDB")] + public async Task ClearMessagesAsync_WithNoMessages_ShouldReturnZeroAsync() + { + // Arrange + this.SkipIfEmulatorNotAvailable(); + var session = CreateMockSession(); + const string ConversationId = "empty-clear-test-conversation"; + + using var provider = new CosmosChatHistoryProvider(this._connectionString, s_testDatabaseId, TestContainerId, + _ => new CosmosChatHistoryProvider.State(ConversationId)); + + // Act + var deletedCount = await provider.ClearMessagesAsync(session); + + // Assert + Assert.Equal(0, deletedCount); + } + #endregion #region Message Filter Tests