|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.ComponentModel;
using System.Text.Json;
using System.Text.Json.Serialization;
using Xunit;
namespace Microsoft.Extensions.AI;
public static class AIJsonUtilitiesTests
{
[Fact]
public static void DefaultOptions_HasExpectedConfiguration()
{
var options = AIJsonUtilities.DefaultOptions;
// Must be read-only singleton.
Assert.NotNull(options);
Assert.Same(options, AIJsonUtilities.DefaultOptions);
Assert.True(options.IsReadOnly);
// Must conform to JsonSerializerDefaults.Web
Assert.Equal(JsonNamingPolicy.CamelCase, options.PropertyNamingPolicy);
Assert.True(options.PropertyNameCaseInsensitive);
Assert.Equal(JsonNumberHandling.AllowReadingFromString, options.NumberHandling);
// Additional settings
Assert.Equal(JsonIgnoreCondition.WhenWritingNull, options.DefaultIgnoreCondition);
Assert.True(options.WriteIndented);
}
[Theory]
[InlineData(false)]
[InlineData(true)]
public static void AIJsonSchemaCreateOptions_DefaultInstance_ReturnsExpectedValues(bool useSingleton)
{
AIJsonSchemaCreateOptions options = useSingleton ? AIJsonSchemaCreateOptions.Default : new AIJsonSchemaCreateOptions();
Assert.False(options.IncludeTypeInEnumSchemas);
Assert.False(options.DisallowAdditionalProperties);
Assert.False(options.IncludeSchemaKeyword);
}
[Fact]
public static void CreateJsonSchema_DefaultParameters_GeneratesExpectedJsonSchema()
{
JsonElement expected = JsonDocument.Parse("""
{
"description": "The type",
"type": "object",
"properties": {
"Key": {
"description": "The parameter",
"type": "integer"
},
"EnumValue": {
"enum": ["A", "B"]
},
"Value": {
"type": ["string", "null"],
"default": null
}
},
"required": ["Key", "EnumValue"]
}
"""{
"description": "The type",
"type": "object",
"properties": {
"Key": {
"description": "The parameter",
"type": "integer"
},
"EnumValue": {
"enum": ["A", "B"]
},
"Value": {
"type": ["string", "null"],
"default": null
}
},
"required": ["Key", "EnumValue"]
}
""").RootElement;
JsonElement actual = AIJsonUtilities.CreateJsonSchema(typeof(MyPoco), serializerOptions: JsonSerializerOptions.Default);
Assert.True(JsonElement.DeepEquals(expected, actual));
}
[Fact]
public static void CreateJsonSchema_OverriddenParameters_GeneratesExpectedJsonSchema()
{
JsonElement expected = JsonDocument.Parse("""
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"description": "alternative description",
"type": "object",
"properties": {
"Key": {
"description": "The parameter",
"type": "integer"
},
"EnumValue": {
"type": "string",
"enum": ["A", "B"]
},
"Value": {
"type": ["string", "null"],
"default": null
}
},
"required": ["Key", "EnumValue"],
"additionalProperties": false,
"default": "42"
}
"""{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"description": "alternative description",
"type": "object",
"properties": {
"Key": {
"description": "The parameter",
"type": "integer"
},
"EnumValue": {
"type": "string",
"enum": ["A", "B"]
},
"Value": {
"type": ["string", "null"],
"default": null
}
},
"required": ["Key", "EnumValue"],
"additionalProperties": false,
"default": "42"
}
""").RootElement;
AIJsonSchemaCreateOptions inferenceOptions = new AIJsonSchemaCreateOptions
{
IncludeTypeInEnumSchemas = true,
DisallowAdditionalProperties = true,
IncludeSchemaKeyword = true
};
JsonElement actual = AIJsonUtilities.CreateJsonSchema(typeof(MyPoco),
description: "alternative description",
hasDefaultValue: true,
defaultValue: 42,
JsonSerializerOptions.Default,
inferenceOptions);
Assert.True(JsonElement.DeepEquals(expected, actual));
}
[Fact]
public static void ResolveParameterJsonSchema_ReturnsExpectedValue()
{
JsonSerializerOptions options = new(JsonSerializerOptions.Default);
AIFunction func = AIFunctionFactory.Create((int x, int y) => x + y, serializerOptions: options);
AIFunctionMetadata metadata = func.Metadata;
AIFunctionParameterMetadata param = metadata.Parameters[0];
JsonElement generatedSchema = Assert.IsType<JsonElement>(param.Schema);
JsonElement resolvedSchema;
resolvedSchema = AIJsonUtilities.ResolveParameterJsonSchema(param, metadata, options);
Assert.True(JsonElement.DeepEquals(generatedSchema, resolvedSchema));
options = new(options) { NumberHandling = JsonNumberHandling.AllowReadingFromString };
resolvedSchema = AIJsonUtilities.ResolveParameterJsonSchema(param, metadata, options);
Assert.False(JsonElement.DeepEquals(generatedSchema, resolvedSchema));
}
[Description("The type")]
public record MyPoco([Description("The parameter")] int Key, MyEnumValue EnumValue, string? Value = null);
[JsonConverter(typeof(JsonStringEnumConverter<MyEnumValue>))]
public enum MyEnumValue
{
A = 1,
B = 2
}
}
|