Files
agent-framework/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/StateSmokeTest.cs
T
Jacob Alber d7c39d72cc .NET: feat: Implement Checkpointing API (#420)
* feat: Implement Checkpointing API

* refactor: Normalzie Namespaces and break out multi-class files

* feat: Support checkpointing in AIAgentHostExecutor

* test: Representation tests

* feat: Add Step-level Tracing and WorkflowEvents

* feat: Add Checkpointing Sample and Smoke Test

* Fixes an issue where StateManager was not properly clearing the incoming queued updates.
* Fixes order of checkpointing and in-step event publication
* Adds import of RunContext state on LoadCheckpoint
* Add re-firing of events for unserviced ExternalRequests on Checkpoint load

* docs: Add documentation to publics

* Also adds documentation to ICheckpointManager which may go public

* refactor: Fix Union Aggregators and add Tests

* fix: Fix issues raised in PR comments and remove dead code
2025-08-25 23:04:38 +00:00

120 lines
4.3 KiB
C#

// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Threading.Tasks;
using Microsoft.Agents.Workflows.Execution;
namespace Microsoft.Agents.Workflows.UnitTests;
public class StateSmokeTest
{
[Fact]
public void Test_ScopeId_Equality()
{
// The rules of ScopeId are simple: Private executor scopes (executorId, scopeId=null) are only equal to
// themselves. Public ScopeIds are equal when their scopeNames are equal, regardless of executorId.
ScopeId privateScope1 = new("executor1", null);
ScopeId privateScope2 = new("executor2", null);
Assert.NotEqual(privateScope1, privateScope2);
Assert.Equal(privateScope1, new ScopeId("executor1", null));
ScopeId sharedScope1 = new("executor1", "sharedScope");
ScopeId sharedScope2 = new("executor2", "sharedScope");
Assert.Equal(sharedScope1, sharedScope2);
Assert.NotEqual(sharedScope1, new ScopeId("executor1", "differentScope"));
Assert.NotEqual(sharedScope1, privateScope1);
}
[Fact]
public void Test_UpdateKey_Equality()
{
// The rules of UpdateKey are different from ScopeId. In the case of "shared scope",
// two update keys with different ExecutorIds are not the same.
const string Key1 = "key1";
const string Key2 = "key2";
UpdateKey privateScope1Key = new("executor1", null, Key1);
UpdateKey privateScope1Key2 = new("executor1", null, Key2);
Assert.NotEqual(privateScope1Key, privateScope1Key2);
UpdateKey privateScope2Key = new("executor2", null, Key1);
Assert.NotEqual(privateScope1Key, privateScope2Key);
UpdateKey scope1Executor1Key = new("executor1", "sharedScope", Key1);
UpdateKey scope1Executor2Key = new("executor2", "sharedScope", Key1);
Assert.NotEqual(scope1Executor1Key, scope1Executor2Key);
}
[Fact]
public async Task Test_ReadQueueUpdateAsync()
{
ScopeId sharedScope1 = new("executor1", "sharedScope");
ScopeId sharedScope2 = new("executor2", "sharedScope");
StateManager manager = new();
// Default reads on "object" should be null
const string Key = "key1";
Assert.Null(await manager.ReadStateAsync<object>(sharedScope1, Key));
Assert.Null(await manager.ReadStateAsync<object>(sharedScope1, Key));
await manager.WriteStateAsync(sharedScope1, Key, new object());
// After writing, we should be able to read the value from the executor's scope
// but not the shared scope yet
Assert.NotNull(await manager.ReadStateAsync<object>(sharedScope1, Key));
Assert.Null(await manager.ReadStateAsync<object>(sharedScope2, Key));
// Writes to one key should not impact other keys
Assert.Null(await manager.ReadStateAsync<object>(sharedScope1, "key2"));
// Publish the write
await manager.PublishUpdatesAsync(tracer: null);
// Now all the executors should be able to see the new state
Assert.NotNull(await manager.ReadStateAsync<object>(sharedScope1, Key));
Assert.NotNull(await manager.ReadStateAsync<object>(sharedScope2, Key));
}
[Fact]
public async Task Test_ConflictingWritesRaiseExceptionAsync()
{
ScopeId sharedScope1 = new("executor1", "sharedScope");
ScopeId sharedScope2 = new("executor2", "sharedScope");
StateManager manager = new();
const string Key = "key1";
const string Value1 = "1";
const string Value2 = "2";
// Write values from both executors
await manager.WriteStateAsync(sharedScope1, Key, Value1);
await manager.WriteStateAsync(sharedScope2, Key, Value2);
// Check that reading each will result in the right value
Assert.Equal(Value1, await manager.ReadStateAsync<string>(sharedScope1, Key));
Assert.Equal(Value2, await manager.ReadStateAsync<string>(sharedScope2, Key));
// Try to publish the updates
try
{
await manager.PublishUpdatesAsync(tracer: null);
Assert.Fail("Expected InvalidOperationException due to conflicting writes.");
}
catch (InvalidOperationException)
{
}
catch (Exception ex)
{
Assert.Fail($"Expected InvalidOperationException, but got {ex.GetType().Name}.");
}
}
}