// Copyright (c) Microsoft. All rights reserved. using System; namespace Microsoft.Agents.AI.Abstractions.UnitTests; /// /// Contains tests for the class. /// public class ProviderSessionStateTests { #region Constructor Tests [Fact] public void Constructor_ThrowsForNullStateInitializer() { // Act & Assert Assert.Throws(() => new ProviderSessionState(null!, "test-key")); } [Fact] public void Constructor_ThrowsForNullStateKey() { // Act & Assert Assert.Throws(() => new ProviderSessionState(_ => new TestState(), null!)); } [Theory] [InlineData("")] [InlineData(" ")] public void Constructor_ThrowsForEmptyOrWhitespaceStateKey(string stateKey) { // Act & Assert Assert.Throws(() => new ProviderSessionState(_ => new TestState(), stateKey)); } [Fact] public void Constructor_AcceptsNullJsonSerializerOptions() { // Act - should not throw var sessionState = new ProviderSessionState(_ => new TestState(), "test-key", jsonSerializerOptions: null); // Assert - instance is created and functional Assert.Equal("test-key", sessionState.StateKey); } [Fact] public void Constructor_AcceptsCustomJsonSerializerOptions() { // Arrange var customOptions = new System.Text.Json.JsonSerializerOptions(); // Act - should not throw var sessionState = new ProviderSessionState(_ => new TestState(), "test-key", customOptions); // Assert - instance is created and functional Assert.Equal("test-key", sessionState.StateKey); } #endregion #region GetOrInitializeState Tests [Fact] public void GetOrInitializeState_InitializesFromStateInitializerOnFirstCall() { // Arrange var expectedState = new TestState { Value = "initialized" }; var sessionState = new ProviderSessionState(_ => expectedState, "test-key"); var session = new TestAgentSession(); // Act var state = sessionState.GetOrInitializeState(session); // Assert Assert.Same(expectedState, state); } [Fact] public void GetOrInitializeState_ReturnsCachedStateFromStateBagOnSecondCall() { // Arrange var callCount = 0; var sessionState = new ProviderSessionState(_ => { callCount++; return new TestState { Value = $"init-{callCount}" }; }, "test-key"); var session = new TestAgentSession(); // Act var state1 = sessionState.GetOrInitializeState(session); var state2 = sessionState.GetOrInitializeState(session); // Assert - initializer called only once; second call reads from StateBag Assert.Equal(1, callCount); Assert.Equal("init-1", state1.Value); Assert.Equal("init-1", state2.Value); } [Fact] public void GetOrInitializeState_WorksWhenSessionIsNull() { // Arrange var sessionState = new ProviderSessionState(_ => new TestState { Value = "no-session" }, "test-key"); // Act var state = sessionState.GetOrInitializeState(null); // Assert Assert.Equal("no-session", state.Value); } [Fact] public void GetOrInitializeState_ReInitializesWhenSessionIsNull() { // Arrange - without a session, state can't be cached in StateBag var callCount = 0; var sessionState = new ProviderSessionState(_ => { callCount++; return new TestState { Value = $"init-{callCount}" }; }, "test-key"); // Act sessionState.GetOrInitializeState(null); sessionState.GetOrInitializeState(null); // Assert - initializer called each time since there's no session to cache in Assert.Equal(2, callCount); } #endregion #region SaveState Tests [Fact] public void SaveState_SavesToStateBag() { // Arrange var sessionState = new ProviderSessionState(_ => new TestState(), "test-key"); var session = new TestAgentSession(); var state = new TestState { Value = "saved" }; // Act sessionState.SaveState(session, state); var retrieved = sessionState.GetOrInitializeState(session); // Assert Assert.Equal("saved", retrieved.Value); } [Fact] public void SaveState_NoOpWhenSessionIsNull() { // Arrange var sessionState = new ProviderSessionState(_ => new TestState { Value = "default" }, "test-key"); // Act - should not throw sessionState.SaveState(null, new TestState { Value = "saved" }); // Assert - no exception; can't verify further without a session } #endregion #region StateKey Tests [Fact] public void StateKey_UsesProvidedKey() { // Arrange var sessionState = new ProviderSessionState(_ => new TestState(), "my-provider-key"); // Act & Assert Assert.Equal("my-provider-key", sessionState.StateKey); } [Fact] public void StateKey_UsesCustomKeyWhenProvided() { // Arrange var sessionState = new ProviderSessionState(_ => new TestState(), "custom-key"); // Act & Assert Assert.Equal("custom-key", sessionState.StateKey); } #endregion #region Isolation Tests [Fact] public void GetOrInitializeState_IsolatesStateBetweenDifferentKeys() { // Arrange var sessionState1 = new ProviderSessionState(_ => new TestState { Value = "state-1" }, "key-1"); var sessionState2 = new ProviderSessionState(_ => new TestState { Value = "state-2" }, "key-2"); var session = new TestAgentSession(); // Act var state1 = sessionState1.GetOrInitializeState(session); var state2 = sessionState2.GetOrInitializeState(session); // Assert - each key maintains independent state Assert.Equal("state-1", state1.Value); Assert.Equal("state-2", state2.Value); } [Fact] public void GetOrInitializeState_IsolatesStateBetweenDifferentSessions() { // Arrange var callCount = 0; var sessionState = new ProviderSessionState(_ => { callCount++; return new TestState { Value = $"init-{callCount}" }; }, "test-key"); var session1 = new TestAgentSession(); var session2 = new TestAgentSession(); // Act var state1 = sessionState.GetOrInitializeState(session1); var state2 = sessionState.GetOrInitializeState(session2); // Assert - each session gets its own state Assert.Equal(2, callCount); Assert.Equal("init-1", state1.Value); Assert.Equal("init-2", state2.Value); } #endregion public sealed class TestState { public string Value { get; set; } = string.Empty; } private sealed class TestAgentSession : AgentSession; }