// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.AI;
namespace Microsoft.Agents.AI.UnitTests.AgentSkills;
///
/// Unit tests for .
///
public sealed class AgentInlineSkillTests
{
[Fact]
public void Constructor_WithNameAndDescription_SetsFrontmatter()
{
// Arrange & Act
var skill = new AgentInlineSkill("my-skill", "A valid skill.", "Instructions.");
// Assert
Assert.Equal("my-skill", skill.Frontmatter.Name);
Assert.Equal("A valid skill.", skill.Frontmatter.Description);
Assert.Null(skill.Frontmatter.License);
Assert.Null(skill.Frontmatter.Compatibility);
Assert.Null(skill.Frontmatter.AllowedTools);
Assert.Null(skill.Frontmatter.Metadata);
}
[Fact]
public void Constructor_WithAllProps_SetsFrontmatter()
{
// Arrange
var metadata = new AdditionalPropertiesDictionary { ["key"] = "value" };
// Act
var skill = new AgentInlineSkill(
"my-skill",
"A valid skill.",
"Instructions.",
license: "MIT",
compatibility: "gpt-4",
allowedTools: "tool-a tool-b",
metadata: metadata);
// Assert
Assert.Equal("my-skill", skill.Frontmatter.Name);
Assert.Equal("A valid skill.", skill.Frontmatter.Description);
Assert.Equal("MIT", skill.Frontmatter.License);
Assert.Equal("gpt-4", skill.Frontmatter.Compatibility);
Assert.Equal("tool-a tool-b", skill.Frontmatter.AllowedTools);
Assert.NotNull(skill.Frontmatter.Metadata);
Assert.Equal("value", skill.Frontmatter.Metadata["key"]);
}
[Fact]
public void Constructor_WithFrontmatter_UsesFrontmatterDirectly()
{
// Arrange
var frontmatter = new AgentSkillFrontmatter("my-skill", "A valid skill.")
{
License = "Apache-2.0",
Compatibility = "gpt-4",
AllowedTools = "tool-a",
Metadata = new AdditionalPropertiesDictionary { ["env"] = "prod" },
};
// Act
var skill = new AgentInlineSkill(frontmatter, "Instructions.");
// Assert
Assert.Same(frontmatter, skill.Frontmatter);
Assert.Equal("Apache-2.0", skill.Frontmatter.License);
Assert.Equal("gpt-4", skill.Frontmatter.Compatibility);
Assert.Equal("tool-a", skill.Frontmatter.AllowedTools);
Assert.Equal("prod", skill.Frontmatter.Metadata!["env"]);
}
[Fact]
public void Constructor_WithFrontmatter_NullFrontmatter_Throws()
{
// Act & Assert
Assert.Throws(() =>
new AgentInlineSkill(null!, "Instructions."));
}
[Fact]
public void Constructor_WithFrontmatter_NullInstructions_Throws()
{
// Arrange
var frontmatter = new AgentSkillFrontmatter("my-skill", "A valid skill.");
// Act & Assert
Assert.Throws(() =>
new AgentInlineSkill(frontmatter, null!));
}
[Fact]
public void Constructor_WithAllProps_NullInstructions_Throws()
{
// Act & Assert
Assert.Throws(() =>
new AgentInlineSkill("my-skill", "A valid skill.", null!));
}
[Fact]
public async Task Content_ContainsNameDescriptionAndInstructionsAsync()
{
// Arrange
var skill = new AgentInlineSkill("my-skill", "A valid skill.", "Do the thing.");
// Act
var content = await skill.GetContentAsync();
// Assert
Assert.Contains("my-skill", content);
Assert.Contains("A valid skill.", content);
Assert.Contains("\nDo the thing.\n", content);
}
[Fact]
public async Task Content_EscapesXmlCharactersAsync()
{
// Arrange
var skill = new AgentInlineSkill("my-skill", "xz\"w & it's more", "1 & 2 < 3");
// Act
var content = await skill.GetContentAsync();
// Assert
Assert.Contains("my-skill", content);
Assert.Contains("x<y>z"w & it's more", content);
Assert.Contains("1 & 2 < 3", content); // instructions are escaped
}
[Fact]
public async Task Content_IsCachedAcrossAccessesAsync()
{
// Arrange
var skill = new AgentInlineSkill("my-skill", "A valid skill.", "Instructions.");
// Act
var first = await skill.GetContentAsync();
var second = await skill.GetContentAsync();
// Assert
Assert.Same(first, second);
}
[Fact]
public async Task Content_IncludesResourcesInBodyAsync()
{
// Arrange
var skill = new AgentInlineSkill("my-skill", "A valid skill.", "Instructions.");
skill.AddResource("config", "value1", "A config resource.");
// Act
var content = await skill.GetContentAsync();
// Assert — resources are rendered in the body so the model can discover them
Assert.Contains("", content);
Assert.Contains("", content);
}
[Fact]
public async Task Content_IncludesDelegateResourcesInBodyAsync()
{
// Arrange
var skill = new AgentInlineSkill("my-skill", "A valid skill.", "Instructions.");
skill.AddResource("dynamic", () => "hello");
// Act
var content = await skill.GetContentAsync();
// Assert — resources are rendered in the body
Assert.Contains("", content);
Assert.Contains("", content);
}
[Fact]
public async Task Content_IncludesScriptsAddedBeforeFirstAccessAsync()
{
// Arrange
var skill = new AgentInlineSkill("my-skill", "A valid skill.", "Instructions.");
skill.AddScript("run", () => "result", "Runs something.");
// Act
var content = await skill.GetContentAsync();
// Assert
Assert.Contains("", content);
Assert.Contains("