// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.Agents.AI.UnitTests.AgentSkills;
///
/// Unit tests for .
///
public sealed class AgentInlineSkillResourceTests
{
[Fact]
public async Task ReadAsync_StaticValue_ReturnsValueAsync()
{
// Arrange
var resource = new AgentInlineSkillResource("config", "my-value");
// Act
var result = await resource.ReadAsync();
// Assert
Assert.Equal("my-value", result);
}
[Fact]
public async Task ReadAsync_StaticObjectValue_ReturnsSameInstanceAsync()
{
// Arrange
var obj = new object();
var resource = new AgentInlineSkillResource("ref", obj);
// Act
var result = await resource.ReadAsync();
// Assert
Assert.Same(obj, result);
}
[Fact]
public async Task ReadAsync_Delegate_InvokesFunctionAsync()
{
// Arrange
int callCount = 0;
var resource = new AgentInlineSkillResource("dynamic", () =>
{
callCount++;
return "computed";
});
// Act
var result = await resource.ReadAsync();
// Assert
Assert.Equal("computed", result?.ToString());
Assert.Equal(1, callCount);
}
[Fact]
public async Task ReadAsync_Delegate_InvokesEachTimeAsync()
{
// Arrange
int callCount = 0;
var resource = new AgentInlineSkillResource("counter", () => ++callCount);
// Act
await resource.ReadAsync();
await resource.ReadAsync();
var result = await resource.ReadAsync();
// Assert
Assert.Equal(3, callCount);
}
[Fact]
public void Constructor_StaticValue_SetsNameAndDescription()
{
// Arrange & Act
var resource = new AgentInlineSkillResource("my-res", "val", "A description.");
// Assert
Assert.Equal("my-res", resource.Name);
Assert.Equal("A description.", resource.Description);
}
[Fact]
public void Constructor_StaticValue_NullDescription_DescriptionIsNull()
{
// Arrange & Act
var resource = new AgentInlineSkillResource("my-res", "val");
// Assert
Assert.Null(resource.Description);
}
[Fact]
public void Constructor_StaticValue_NullValue_Throws()
{
// Act & Assert — cast needed to target the object overload
#pragma warning disable IDE0004
Assert.Throws(() =>
new AgentInlineSkillResource("my-res", (object)null!));
#pragma warning restore IDE0004
}
[Fact]
public void Constructor_Delegate_NullMethod_Throws()
{
// Act & Assert
Assert.Throws(() =>
new AgentInlineSkillResource("my-res", null!));
}
[Fact]
public void Constructor_NullName_Throws()
{
// Act & Assert
Assert.Throws(() =>
new AgentInlineSkillResource(null!, "val"));
}
[Fact]
public void Constructor_WhitespaceName_Throws()
{
// Act & Assert
Assert.Throws(() =>
new AgentInlineSkillResource(" ", "val"));
}
[Fact]
public void Constructor_Delegate_SetsNameAndDescription()
{
// Arrange & Act
var resource = new AgentInlineSkillResource("dyn-res", () => "hello", "Dynamic resource.");
// Assert
Assert.Equal("dyn-res", resource.Name);
Assert.Equal("Dynamic resource.", resource.Description);
}
[Fact]
public async Task ReadAsync_WithSerializerOptions_SerializesReturnCustomTypeAsync()
{
// Arrange — delegate resource returns a custom type; the JSO includes a source-generated context for it
var jso = SkillTestJsonContext.Default.Options;
var resource = new AgentInlineSkillResource("config", () => new SkillConfig { Theme = "dark", Verbose = true }, serializerOptions: jso);
// Act
var result = await resource.ReadAsync();
// Assert — the custom type was returned successfully
Assert.NotNull(result);
Assert.Contains("dark", result!.ToString()!);
}
[Fact]
public async Task ReadAsync_SupportsCancellationTokenAsync()
{
// Arrange
using var cts = new CancellationTokenSource();
var resource = new AgentInlineSkillResource("cancellable", "value");
// Act — should not throw with a non-cancelled token
var result = await resource.ReadAsync(cancellationToken: cts.Token);
// Assert
Assert.Equal("value", result);
}
[Fact]
public void Constructor_MethodInfo_SetsNameAndDescription()
{
// Arrange
var method = typeof(AgentInlineSkillResourceTests).GetMethod(nameof(StaticResourceHelper), BindingFlags.NonPublic | BindingFlags.Static)!;
// Act
var resource = new AgentInlineSkillResource("method-resource", method, target: null, description: "A method resource.");
// Assert
Assert.Equal("method-resource", resource.Name);
Assert.Equal("A method resource.", resource.Description);
}
[Fact]
public async Task ReadAsync_MethodInfo_StaticMethod_ReturnsValueAsync()
{
// Arrange
var method = typeof(AgentInlineSkillResourceTests).GetMethod(nameof(StaticResourceHelper), BindingFlags.NonPublic | BindingFlags.Static)!;
var resource = new AgentInlineSkillResource("static-method-res", method, target: null);
// Act
var result = await resource.ReadAsync();
// Assert
Assert.Equal("static-resource-value", result?.ToString());
}
[Fact]
public async Task ReadAsync_MethodInfo_InstanceMethod_ReturnsValueAsync()
{
// Arrange
var method = typeof(AgentInlineSkillResourceTests).GetMethod(nameof(InstanceResourceHelper), BindingFlags.NonPublic | BindingFlags.Instance)!;
var resource = new AgentInlineSkillResource("instance-method-res", method, target: this);
// Act
var result = await resource.ReadAsync();
// Assert
Assert.Equal("instance-resource-value", result?.ToString());
}
[Fact]
public void Constructor_MethodInfo_NullMethod_Throws()
{
// Act & Assert
Assert.Throws(() =>
new AgentInlineSkillResource("my-res", null!, target: null));
}
private static string StaticResourceHelper() => "static-resource-value";
private string InstanceResourceHelper() => "instance-resource-value";
}