// Copyright (c) Microsoft. All rights reserved. using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Microsoft.Agents.AI.Compaction; using Microsoft.Extensions.AI; using Moq; namespace Microsoft.Agents.AI.UnitTests.Compaction; /// /// Contains tests for . /// public class CompactionStrategyCreateTests { private static IChatClient CreateMockChatClient() { Mock mock = new(); mock.Setup(c => c.GetResponseAsync( It.IsAny>(), It.IsAny(), It.IsAny())) .ReturnsAsync(new ChatResponse([new ChatMessage(ChatRole.Assistant, "summary")])); return mock.Object; } // ── Gentle ──────────────────────────────────────────────────────────────── [Fact] public void CreateGentleCompactReturnsTwoStrategyPipeline() { PipelineCompactionStrategy pipeline = Assert.IsType( CompactionStrategy.Create(CompactionApproach.Gentle, CompactionSize.Compact)); Assert.Equal(2, pipeline.Strategies.Count); Assert.IsType(pipeline.Strategies[0]); Assert.IsType(pipeline.Strategies[1]); } [Fact] public void CreateGentleModerateReturnsTwoStrategyPipeline() { PipelineCompactionStrategy pipeline = Assert.IsType( CompactionStrategy.Create(CompactionApproach.Gentle, CompactionSize.Moderate)); Assert.Equal(2, pipeline.Strategies.Count); Assert.IsType(pipeline.Strategies[0]); Assert.IsType(pipeline.Strategies[1]); } [Fact] public void CreateGentleGenerousReturnsTwoStrategyPipeline() { PipelineCompactionStrategy pipeline = Assert.IsType( CompactionStrategy.Create(CompactionApproach.Gentle, CompactionSize.Generous)); Assert.Equal(2, pipeline.Strategies.Count); Assert.IsType(pipeline.Strategies[0]); Assert.IsType(pipeline.Strategies[1]); } [Fact] public void CreateGentleDoesNotRequireChatClient() { // No chatClient supplied — should succeed without throwing. CompactionStrategy strategy = CompactionStrategy.Create(CompactionApproach.Gentle, CompactionSize.Moderate); Assert.NotNull(strategy); } // ── Balanced ────────────────────────────────────────────────────────────── [Fact] public void CreateBalancedCompactReturnsThreeStrategyPipeline() { PipelineCompactionStrategy pipeline = Assert.IsType( CompactionStrategy.Create(CompactionApproach.Balanced, CompactionSize.Compact, CreateMockChatClient())); Assert.Equal(3, pipeline.Strategies.Count); Assert.IsType(pipeline.Strategies[0]); Assert.IsType(pipeline.Strategies[1]); Assert.IsType(pipeline.Strategies[2]); } [Fact] public void CreateBalancedModerateReturnsThreeStrategyPipeline() { PipelineCompactionStrategy pipeline = Assert.IsType( CompactionStrategy.Create(CompactionApproach.Balanced, CompactionSize.Moderate, CreateMockChatClient())); Assert.Equal(3, pipeline.Strategies.Count); Assert.IsType(pipeline.Strategies[0]); Assert.IsType(pipeline.Strategies[1]); Assert.IsType(pipeline.Strategies[2]); } [Fact] public void CreateBalancedGenerousReturnsThreeStrategyPipeline() { PipelineCompactionStrategy pipeline = Assert.IsType( CompactionStrategy.Create(CompactionApproach.Balanced, CompactionSize.Generous, CreateMockChatClient())); Assert.Equal(3, pipeline.Strategies.Count); Assert.IsType(pipeline.Strategies[0]); Assert.IsType(pipeline.Strategies[1]); Assert.IsType(pipeline.Strategies[2]); } [Fact] public void CreateBalancedNullChatClientThrows() { Assert.Throws( () => CompactionStrategy.Create(CompactionApproach.Balanced, CompactionSize.Moderate, null)); } // ── Aggressive ──────────────────────────────────────────────────────────── [Fact] public void CreateAggressiveCompactReturnsFourStrategyPipeline() { PipelineCompactionStrategy pipeline = Assert.IsType( CompactionStrategy.Create(CompactionApproach.Aggressive, CompactionSize.Compact, CreateMockChatClient())); Assert.Equal(4, pipeline.Strategies.Count); Assert.IsType(pipeline.Strategies[0]); Assert.IsType(pipeline.Strategies[1]); Assert.IsType(pipeline.Strategies[2]); Assert.IsType(pipeline.Strategies[3]); } [Fact] public void CreateAggressiveModerateReturnsFourStrategyPipeline() { PipelineCompactionStrategy pipeline = Assert.IsType( CompactionStrategy.Create(CompactionApproach.Aggressive, CompactionSize.Moderate, CreateMockChatClient())); Assert.Equal(4, pipeline.Strategies.Count); Assert.IsType(pipeline.Strategies[0]); Assert.IsType(pipeline.Strategies[1]); Assert.IsType(pipeline.Strategies[2]); Assert.IsType(pipeline.Strategies[3]); } [Fact] public void CreateAggressiveGenerousReturnsFourStrategyPipeline() { PipelineCompactionStrategy pipeline = Assert.IsType( CompactionStrategy.Create(CompactionApproach.Aggressive, CompactionSize.Generous, CreateMockChatClient())); Assert.Equal(4, pipeline.Strategies.Count); Assert.IsType(pipeline.Strategies[0]); Assert.IsType(pipeline.Strategies[1]); Assert.IsType(pipeline.Strategies[2]); Assert.IsType(pipeline.Strategies[3]); } [Fact] public void CreateAggressiveNullChatClientThrows() { Assert.Throws( () => CompactionStrategy.Create(CompactionApproach.Aggressive, CompactionSize.Moderate, null)); } // ── Invalid enum values ─────────────────────────────────────────────────── [Fact] public void CreateInvalidApproachThrows() { Assert.Throws( () => CompactionStrategy.Create((CompactionApproach)99, CompactionSize.Moderate)); } [Fact] public void CreateInvalidSizeThrows() { Assert.Throws( () => CompactionStrategy.Create(CompactionApproach.Gentle, (CompactionSize)99)); } // ── Size-threshold behavioral verification ──────────────────────────────── /// /// Verifies that and /// configure different message thresholds. /// [Fact] public async Task CreateGentleSizeDifferentiatesMessageThresholdsAsync() { // Arrange: 1 tool-call group + 99 (User, Assistant) groups = 100 groups / 200 messages. // Compact ToolResult triggers at MessagesExceed(50) → 200 > 50 → fires. // Moderate ToolResult triggers at MessagesExceed(500) → 200 < 500 → does not fire. // // The configuration preserves a number of most-recent groups, leaving the oldest // tool-call group eligible for collapsing, which makes the behavioral difference // between Compact and Moderate sizes observable in this test. CompactionStrategy compactPipeline = CompactionStrategy.Create(CompactionApproach.Gentle, CompactionSize.Compact); CompactionStrategy moderatePipeline = CompactionStrategy.Create(CompactionApproach.Gentle, CompactionSize.Moderate); List messages = [ // 1 tool-call group (assistant FunctionCall + tool result = 2 messages, 1 group) new(ChatRole.Assistant, [new FunctionCallContent("c1", "fetch")]), new(ChatRole.Tool, "data"), ]; for (int index = 0; index < 99; ++index) { messages.Add(new(ChatRole.User, $"Q{index}")); messages.Add(new(ChatRole.Assistant, $"A{index}")); } // Two separate indexes so strategies run independently. CompactionMessageIndex compactIndex = CompactionMessageIndex.Create(messages); CompactionMessageIndex moderateIndex = CompactionMessageIndex.Create(messages); // Act bool compactCompacted = await compactPipeline.CompactAsync(compactIndex); bool moderateCompacted = await moderatePipeline.CompactAsync(moderateIndex); // Assert Assert.True(compactCompacted, "Compact size should trigger ToolResult compaction."); Assert.False(moderateCompacted, "Moderate size should NOT trigger ToolResult compaction."); } }