|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265 |
- // Copyright (c) Microsoft Corporation. All rights reserved.
- // RunContextStackTests.cs
-
- using FluentAssertions;
- using Microsoft.AutoGen.AgentChat.GroupChat;
- using Moq;
- using Xunit;
-
- namespace Microsoft.AutoGen.AgentChat.Tests;
-
- [Trait("Category", "UnitV2")]
- public class RunContextStackTests
- {
- public static IRunContextLayer CreateLayer(Action<Mock<IRunContextLayer>>? setupAction = null)
- {
- Mock<IRunContextLayer> layer = new();
-
- if (setupAction != null)
- {
- setupAction(layer);
- }
- else
- {
- layer.Setup(l => l.InitializeAsync()).Returns(ValueTask.CompletedTask);
- layer.Setup(l => l.DeinitializeAsync()).Returns(ValueTask.CompletedTask);
- }
-
- return layer.Object;
- }
-
- [Fact]
- public async Task Initialize_SucceedsWithNoLayers()
- {
- // Arrange
- RunContextStack stack = new RunContextStack();
-
- // Act
- Func<Task> func = async () => await stack.InitializeAsync();
-
- // Assert
- await func.Should().NotThrowAsync("RunContextStack should work without context frames");
- }
-
- [Fact]
- public async Task Deinitialize_SucceedsWithNoLayers()
- {
- // Arrange
- RunContextStack stack = new RunContextStack();
- await stack.InitializeAsync();
-
- // Act
- Func<Task> func = async () => await stack.DeinitializeAsync();
-
- // Assert
- await func.Should().NotThrowAsync("RunContextStack should work without context frames");
- }
-
- [Fact]
- public async Task PushLayer_FailsWhenInitialized()
- {
- // Arrange
- RunContextStack stack = new RunContextStack();
- await stack.InitializeAsync();
-
- // Act
- Action pushLayerAction = () => stack.PushLayer(CreateLayer());
-
- // Assert
- pushLayerAction.Should().Throw<InvalidOperationException>("RunContextStack should not allow pushing layers when initialized");
- }
-
- [Fact]
- public async Task PopLayer_FailsWhenInitialized()
- {
- // Arrange
- RunContextStack stack = new RunContextStack();
- await stack.InitializeAsync();
-
- // Act
- Action popLayerAction = stack.PopLayer;
-
- // Assert
- popLayerAction.Should().Throw<InvalidOperationException>("RunContextStack should not allow popping layers when initialized");
- }
-
- [Fact]
- public Task InitializeDeinitialize_ShouldInvokeLayersInOrder_WhenPushed()
- {
- return PrepareAndRun_LayerOrderTest(Arrange);
-
- static RunContextStack Arrange(IEnumerable<IRunContextLayer> layers)
- {
- RunContextStack stack = new RunContextStack();
-
- foreach (IRunContextLayer layer in layers)
- {
- stack.PushLayer(layer);
- }
-
- return stack;
- }
- }
-
- [Fact]
- public Task InitializeDeinitialize_ShouldInvokeLayersInOrder_WhenConstructed()
- {
- return PrepareAndRun_LayerOrderTest(Arrange);
-
- static RunContextStack Arrange(IEnumerable<IRunContextLayer> layers)
- {
- return new RunContextStack([.. layers]);
- }
- }
-
- private async Task PrepareAndRun_LayerOrderTest(Func<IEnumerable<IRunContextLayer>, RunContextStack> arrangeStack)
- {
- bool bottomLayerInit = false;
- bool bottomLayerDeinit = false;
-
- bool topLayerInit = false;
- bool topLayerDeinit = false;
-
- // Arrange
- IRunContextLayer topLayer = CreateLayer(mock =>
- {
- mock.Setup(l => l.InitializeAsync()).Callback(
- () =>
- {
- topLayerInit.Should().BeFalse("Top Layer should not have been initialized yet");
- bottomLayerInit.Should().BeFalse("Bottom Layer should not have been initialized yet");
-
- topLayerInit = true;
- }
- ).Returns(ValueTask.CompletedTask).Verifiable();
- mock.Setup(l => l.DeinitializeAsync()).Callback(
- () =>
- {
- topLayerInit.Should().BeTrue("Top Layer should have been initialized");
- bottomLayerInit.Should().BeTrue("Bottom Layer should have been initialized");
-
- bottomLayerDeinit.Should().BeTrue("Bottom Layer should be deinitialized before Top Layer");
- topLayerDeinit.Should().BeFalse("Top Layer should not have been deinitialized yet");
-
- topLayerDeinit = true;
- }).Returns(ValueTask.CompletedTask).Verifiable();
- });
-
- IRunContextLayer bottomLayer = CreateLayer(mock =>
- {
- mock.Setup(l => l.InitializeAsync()).Callback(
- () =>
- {
- topLayerInit.Should().BeTrue("Top Layer should have been initialized before Bottom Layer");
- bottomLayerInit.Should().BeFalse("Bottom Layer should not have been initialized yet");
-
- bottomLayerInit = true;
- }
- ).Returns(ValueTask.CompletedTask).Verifiable();
- mock.Setup(l => l.DeinitializeAsync()).Callback(
- () =>
- {
- topLayerInit.Should().BeTrue("Top Layer should have been initialized");
- bottomLayerInit.Should().BeTrue("Bottom Layer should have been initialized");
-
- bottomLayerDeinit.Should().BeFalse("Bottom Layer should not have been deinitialized yet");
- topLayerDeinit.Should().BeFalse("Top Layer should not have been deinitialized yet");
-
- bottomLayerDeinit = true;
- }).Returns(ValueTask.CompletedTask).Verifiable();
- });
-
- RunContextStack stack = arrangeStack([bottomLayer, topLayer]);
-
- // Act
- await stack.InitializeAsync();
-
- // Assert
- Mock.Get(topLayer).Verify(l => l.InitializeAsync(), Times.Once);
- Mock.Get(bottomLayer).Verify(l => l.InitializeAsync(), Times.Once);
-
- bottomLayerInit.Should().BeTrue("Top Layer should have been initialized");
- topLayerInit.Should().BeTrue("Bottom Layer should have been initialized");
-
- // Act 2
- await stack.DeinitializeAsync();
-
- // Assert 2
- Mock.Get(bottomLayer).Verify(l => l.DeinitializeAsync(), Times.Once);
- Mock.Get(topLayer).Verify(l => l.DeinitializeAsync(), Times.Once);
-
- topLayerDeinit.Should().BeTrue("Bottom Layer should have been deinitialized");
- bottomLayerDeinit.Should().BeTrue("Top Layer should have been deinitialized");
- }
-
- [Fact]
- public async Task CreateOverrides_GetsInvokedOnError()
- {
- int initializeErrors = 0;
- int deinitializeErrors = 0;
-
- // Arrange
- IRunContextLayer overrides = RunContextStack.OverrideErrors(
- initializeError: () => initializeErrors++,
- deinitializeError: () => deinitializeErrors++);
-
- RunContextStack stack = new RunContextStack(overrides);
-
- // Act
- Func<Task> deinitializeAction = async () => await stack.DeinitializeAsync();
-
- // Assert
- // The first Deinitialize should throw because we only override after the top layer it initialized
- await deinitializeAction.Should().ThrowAsync<InvalidOperationException>("Deinitialize should throw an exception");
-
- // Act 2
- await stack.InitializeAsync();
- Func<Task> initializeAgainAction = async () => await stack.InitializeAsync();
-
- // Assert 2
- // The second Initialize should not throw, because the overrides should be applied
- await initializeAgainAction.Should().NotThrowAsync("Initialize should not throw an exception");
-
- initializeErrors.Should().Be(1, "There should be one initialization error");
- deinitializeErrors.Should().Be(0, "There should not have been an overriden invocation of a deinitialize error.");
- }
-
- [Fact]
- public async Task Enter_DisposableWorksIdempotently()
- {
- int initializeCount = 0;
- int deinitializeCount = 0;
-
- // Arrange
- IRunContextLayer layer = CreateLayer(mock =>
- {
- mock.Setup(l => l.InitializeAsync()).Callback(() => initializeCount++).Returns(ValueTask.CompletedTask);
- mock.Setup(l => l.DeinitializeAsync()).Callback(() => deinitializeCount++).Returns(ValueTask.CompletedTask);
- });
-
- RunContextStack stack = new RunContextStack(layer);
-
- // Act
- IAsyncDisposable exitDisposable = await stack.Enter();
-
- // Assert
- initializeCount.Should().Be(1, "Layer should have been initialized once");
- deinitializeCount.Should().Be(0, "Layer should not have been deinitialized yet");
-
- // Act 2
- await exitDisposable.DisposeAsync();
-
- // Assert 2
- initializeCount.Should().Be(1, "Layer should have been initialized once");
- deinitializeCount.Should().Be(1, "Layer should have been deinitialized once");
-
- // Act 3
- Func<Task> disposeAgain = async () => await exitDisposable.DisposeAsync();
-
- // Assert 3
- await disposeAgain.Should().NotThrowAsync("Dispose should be idempotent");
-
- initializeCount.Should().Be(1, "Layer should have been initialized once");
- deinitializeCount.Should().Be(1, "Layer should have been deinitialized once");
- }
- }
|