// Copyright (c) Microsoft. All rights reserved.
using System;
using Microsoft.Agents.AI.Workflows.Declarative.Entities;
using Microsoft.Agents.ObjectModel;
using Microsoft.PowerFx.Types;
namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.Entities;
///
/// Tests for .
///
public sealed class EntityExtractorTest(ITestOutputHelper output) : WorkflowTest(output)
{
[Fact]
public void Parse_NullEntity_WithNonEmptyValue_ReturnsStringValue()
{
// Arrange
EntityReference? entity = null;
// Act
EntityExtractionResult result = EntityExtractor.Parse(entity, "test value");
// Assert
Assert.True(result.IsValid);
Assert.NotNull(result.Value);
StringValue stringValue = Assert.IsType(result.Value);
Assert.Equal("test value", stringValue.Value);
}
[Theory]
[InlineData("")]
[InlineData(" ")]
[InlineData("\t")]
public void Parse_NullEntity_WithEmptyValue_ReturnsBlankValue(string value)
{
// Arrange
EntityReference? entity = null;
// Act
EntityExtractionResult result = EntityExtractor.Parse(entity, value);
// Assert
Assert.True(result.IsValid);
Assert.IsType(result.Value);
}
[Theory]
[InlineData("true", true)]
[InlineData("false", false)]
[InlineData("True", true)]
[InlineData("False", false)]
[InlineData("TRUE", true)]
[InlineData("FALSE", false)]
public void Parse_BooleanEntity_ValidValue_ReturnsBoolean(string value, bool expected)
{
// Arrange
EntityReference entity = CreateBooleanEntity();
// Act
EntityExtractionResult result = EntityExtractor.Parse(entity, value);
// Assert
Assert.True(result.IsValid);
Assert.Equal(expected, (result.Value as BooleanValue)?.Value);
}
[Theory]
[InlineData("invalid")]
[InlineData("123")]
[InlineData("yes")]
public void Parse_BooleanEntity_InvalidValue_ReturnsError(string value)
{
// Arrange
EntityReference entity = CreateBooleanEntity();
// Act
EntityExtractionResult result = EntityExtractor.Parse(entity, value);
// Assert
Assert.False(result.IsValid);
Assert.Contains("Invalid boolean value", result.ErrorMessage);
}
[Theory]
[InlineData("2023-12-25")]
[InlineData("12/25/2023")]
[InlineData("2023-12-25 10:30:00")]
public void Parse_DateEntity_ValidValue_ReturnsDate(string value)
{
// Arrange
EntityReference entity = CreateDateEntity();
// Act
EntityExtractionResult result = EntityExtractor.Parse(entity, value);
// Assert
Assert.True(result.IsValid);
Assert.IsType(result.Value);
}
[Theory]
[InlineData("invalid date")]
[InlineData("not-a-date")]
public void Parse_DateEntity_InvalidValue_ReturnsError(string value)
{
// Arrange
EntityReference entity = CreateDateEntity();
// Act
EntityExtractionResult result = EntityExtractor.Parse(entity, value);
// Assert
Assert.False(result.IsValid);
Assert.Contains("Invalid date value", result.ErrorMessage);
}
[Theory]
[InlineData("2023-12-25 10:30:00")]
[InlineData("12/25/2023 10:30:00 AM")]
public void Parse_DateTimeEntity_ValidValue_ReturnsDateTime(string value)
{
// Arrange
EntityReference entity = CreateDateTimeEntity();
// Act
EntityExtractionResult result = EntityExtractor.Parse(entity, value);
// Assert
Assert.True(result.IsValid);
Assert.IsType(result.Value);
}
[Theory]
[InlineData("invalid datetime")]
public void Parse_DateTimeEntity_InvalidValue_ReturnsError(string value)
{
// Arrange
EntityReference entity = CreateDateTimeEntity();
// Act
EntityExtractionResult result = EntityExtractor.Parse(entity, value);
// Assert
Assert.False(result.IsValid);
Assert.Contains("Invalid date-time value", result.ErrorMessage);
}
[Theory]
[InlineData("2023-12-25 10:30:00")]
[InlineData("12/25/2023 10:30:00")]
public void Parse_DateTimeNoTimeZoneEntity_ValidValue_ReturnsDateTime(string value)
{
// Arrange
EntityReference entity = CreateDateTimeNoTimeZoneEntity();
// Act
EntityExtractionResult result = EntityExtractor.Parse(entity, value);
// Assert
Assert.True(result.IsValid);
DateTimeValue dateTimeValue = Assert.IsType(result.Value);
DateTime dateTime = dateTimeValue.GetConvertedValue(null);
Assert.Equal(DateTime.Parse(value), dateTime);
}
[Theory]
[InlineData("01:30:00")]
[InlineData("1:30:00")]
[InlineData("10.12:30:45")]
public void Parse_DurationEntity_ValidValue_ReturnsDuration(string value)
{
// Arrange
EntityReference entity = CreateDurationEntity();
// Act
EntityExtractionResult result = EntityExtractor.Parse(entity, value);
// Assert
Assert.True(result.IsValid);
Assert.IsType(result.Value);
}
[Theory]
[InlineData("invalid duration")]
[InlineData("not a timespan")]
public void Parse_DurationEntity_InvalidValue_ReturnsError(string value)
{
// Arrange
EntityReference entity = CreateDurationEntity();
// Act
EntityExtractionResult result = EntityExtractor.Parse(entity, value);
// Assert
Assert.False(result.IsValid);
Assert.Contains("Invalid duration value", result.ErrorMessage);
}
[Theory]
[InlineData("test@example.com")]
[InlineData("user.name@domain.co.uk")]
public void Parse_EmailEntity_ValidValue_ReturnsEmail(string value)
{
// Arrange
EntityReference entity = CreateEmailEntity();
// Act
EntityExtractionResult result = EntityExtractor.Parse(entity, value);
// Assert
Assert.True(result.IsValid);
Assert.Equal(value, (result.Value as StringValue)?.Value);
}
[Theory]
[InlineData("invalid email")]
[InlineData("@example.com")]
[InlineData("test@")]
public void Parse_EmailEntity_InvalidValue_ReturnsError(string value)
{
// Arrange
EntityReference entity = CreateEmailEntity();
// Act
EntityExtractionResult result = EntityExtractor.Parse(entity, value);
// Assert
Assert.False(result.IsValid);
Assert.Contains("Invalid email value", result.ErrorMessage);
}
[Theory]
[InlineData("123")]
[InlineData("456.78")]
[InlineData("-123.45")]
[InlineData("1,234.56")]
public void Parse_NumberEntity_ValidValue_ReturnsNumber(string value)
{
// Arrange
EntityReference entity = CreateNumberEntity();
// Act
EntityExtractionResult result = EntityExtractor.Parse(entity, value);
// Assert
Assert.True(result.IsValid);
Assert.IsType(result.Value);
}
[Theory]
[InlineData("not a number")]
[InlineData("abc")]
public void Parse_NumberEntity_InvalidValue_ReturnsError(string value)
{
// Arrange
EntityReference entity = CreateNumberEntity();
// Act
EntityExtractionResult result = EntityExtractor.Parse(entity, value);
// Assert
Assert.False(result.IsValid);
Assert.Contains("Invalid double value", result.ErrorMessage);
}
[Theory]
[InlineData("25 years")]
[InlineData("30 years old")]
[InlineData("45")]
public void Parse_AgeEntity_ValidValue_ReturnsString(string value)
{
// Arrange
EntityReference entity = CreateAgeEntity();
// Act
EntityExtractionResult result = EntityExtractor.Parse(entity, value);
// Assert
Assert.True(result.IsValid);
Assert.IsType(result.Value);
}
[Theory]
[InlineData("not an age")]
public void Parse_AgeEntity_InvalidValue_ReturnsError(string value)
{
// Arrange
EntityReference entity = CreateAgeEntity();
// Act
EntityExtractionResult result = EntityExtractor.Parse(entity, value);
// Assert
Assert.False(result.IsValid);
Assert.Contains("Invalid age value", result.ErrorMessage);
}
[Theory]
[InlineData("$100")]
[InlineData("100 dollars")]
[InlineData("123.45")]
public void Parse_MoneyEntity_ValidValue_ReturnsString(string value)
{
// Arrange
EntityReference entity = CreateMoneyEntity();
// Act
EntityExtractionResult result = EntityExtractor.Parse(entity, value);
// Assert
Assert.True(result.IsValid);
Assert.IsType(result.Value);
}
[Theory]
[InlineData("not money")]
public void Parse_MoneyEntity_InvalidValue_ReturnsError(string value)
{
// Arrange
EntityReference entity = CreateMoneyEntity();
// Act
EntityExtractionResult result = EntityExtractor.Parse(entity, value);
// Assert
Assert.False(result.IsValid);
Assert.Contains("Invalid money value", result.ErrorMessage);
}
[Theory]
[InlineData("50%")]
[InlineData("75 percent")]
[InlineData("99.5")]
public void Parse_PercentageEntity_ValidValue_ReturnsString(string value)
{
// Arrange
EntityReference entity = CreatePercentageEntity();
// Act
EntityExtractionResult result = EntityExtractor.Parse(entity, value);
// Assert
Assert.True(result.IsValid);
Assert.IsType(result.Value);
}
[Theory]
[InlineData("not a percentage")]
public void Parse_PercentageEntity_InvalidValue_ReturnsError(string value)
{
// Arrange
EntityReference entity = CreatePercentageEntity();
// Act
EntityExtractionResult result = EntityExtractor.Parse(entity, value);
// Assert
Assert.False(result.IsValid);
Assert.Contains("Invalid percentage value", result.ErrorMessage);
}
[Theory]
[InlineData("60 mph")]
[InlineData("100 km/h")]
[InlineData("25.5")]
public void Parse_SpeedEntity_ValidValue_ReturnsString(string value)
{
// Arrange
EntityReference entity = CreateSpeedEntity();
// Act
EntityExtractionResult result = EntityExtractor.Parse(entity, value);
// Assert
Assert.True(result.IsValid);
Assert.IsType(result.Value);
}
[Theory]
[InlineData("not a speed")]
public void Parse_SpeedEntity_InvalidValue_ReturnsError(string value)
{
// Arrange
EntityReference entity = CreateSpeedEntity();
// Act
EntityExtractionResult result = EntityExtractor.Parse(entity, value);
// Assert
Assert.False(result.IsValid);
Assert.Contains("Invalid speed value", result.ErrorMessage);
}
[Theory]
[InlineData("72°F")]
[InlineData("20°C")]
[InlineData("98.6")]
public void Parse_TemperatureEntity_ValidValue_ReturnsString(string value)
{
// Arrange
EntityReference entity = CreateTemperatureEntity();
// Act
EntityExtractionResult result = EntityExtractor.Parse(entity, value);
// Assert
Assert.True(result.IsValid);
Assert.IsType(result.Value);
}
[Theory]
[InlineData("not a temperature")]
public void Parse_TemperatureEntity_InvalidValue_ReturnsError(string value)
{
// Arrange
EntityReference entity = CreateTemperatureEntity();
// Act
EntityExtractionResult result = EntityExtractor.Parse(entity, value);
// Assert
Assert.False(result.IsValid);
Assert.Contains("Invalid temperature value", result.ErrorMessage);
}
[Theory]
[InlineData("150 lbs")]
[InlineData("70 kg")]
[InlineData("180.5")]
public void Parse_WeightEntity_ValidValue_ReturnsString(string value)
{
// Arrange
EntityReference entity = CreateWeightEntity();
// Act
EntityExtractionResult result = EntityExtractor.Parse(entity, value);
// Assert
Assert.True(result.IsValid);
Assert.IsType(result.Value);
}
[Theory]
[InlineData("not a weight")]
public void Parse_WeightEntity_InvalidValue_ReturnsError(string value)
{
// Arrange
EntityReference entity = CreateWeightEntity();
// Act
EntityExtractionResult result = EntityExtractor.Parse(entity, value);
// Assert
Assert.False(result.IsValid);
Assert.Contains("Invalid weight value", result.ErrorMessage);
}
[Theory]
[InlineData("https://www.example.com", "https://www.example.com/")]
[InlineData("http://test.com/path", "http://test.com/path")]
[InlineData("ftp://files.example.com", "ftp://files.example.com/")]
public void Parse_URLEntity_ValidValue_ReturnsURL(string value, string expected)
{
// Arrange
EntityReference entity = CreateURLEntity();
// Act
EntityExtractionResult result = EntityExtractor.Parse(entity, value);
// Assert
Assert.True(result.IsValid);
Assert.Equal(expected, (result.Value as StringValue)?.Value);
}
[Theory]
[InlineData("not a url")]
[InlineData("invalid url")]
public void Parse_URLEntity_InvalidValue_ReturnsError(string value)
{
// Arrange
EntityReference entity = CreateURLEntity();
// Act
EntityExtractionResult result = EntityExtractor.Parse(entity, value);
// Assert
Assert.False(result.IsValid);
Assert.Contains("Invalid double value", result.ErrorMessage);
}
[Theory]
[InlineData("Seattle")]
[InlineData("New York")]
public void Parse_CityEntity_ValidValue_ReturnsString(string value)
{
// Arrange
EntityReference entity = CreateCityEntity();
// Act
EntityExtractionResult result = EntityExtractor.Parse(entity, value);
// Assert
Assert.True(result.IsValid);
Assert.Equal(value, (result.Value as StringValue)?.Value);
}
[Theory]
[InlineData("")]
[InlineData(" ")]
public void Parse_CityEntity_EmptyValue_ReturnsError(string value)
{
// Arrange
EntityReference entity = CreateCityEntity();
// Act
EntityExtractionResult result = EntityExtractor.Parse(entity, value);
// Assert
Assert.False(result.IsValid);
Assert.Equal("Empty value", result.ErrorMessage);
}
[Theory]
[InlineData("Washington")]
[InlineData("California")]
public void Parse_StateEntity_ValidValue_ReturnsString(string value)
{
// Arrange
EntityReference entity = CreateStateEntity();
// Act
EntityExtractionResult result = EntityExtractor.Parse(entity, value);
// Assert
Assert.True(result.IsValid);
Assert.Equal(value, (result.Value as StringValue)?.Value);
}
[Theory]
[InlineData("USA")]
[InlineData("United Kingdom")]
public void Parse_CountryOrRegionEntity_ValidValue_ReturnsString(string value)
{
// Arrange
EntityReference entity = CreateCountryOrRegionEntity();
// Act
EntityExtractionResult result = EntityExtractor.Parse(entity, value);
// Assert
Assert.True(result.IsValid);
Assert.Equal(value, (result.Value as StringValue)?.Value);
}
[Theory]
[InlineData("Europe")]
[InlineData("Asia")]
public void Parse_ContinentEntity_ValidValue_ReturnsString(string value)
{
// Arrange
EntityReference entity = CreateContinentEntity();
// Act
EntityExtractionResult result = EntityExtractor.Parse(entity, value);
// Assert
Assert.True(result.IsValid);
Assert.Equal(value, (result.Value as StringValue)?.Value);
}
[Theory]
[InlineData("123 Main Street")]
[InlineData("456 Oak Avenue")]
public void Parse_StreetAddressEntity_ValidValue_ReturnsString(string value)
{
// Arrange
EntityReference entity = CreateStreetAddressEntity();
// Act
EntityExtractionResult result = EntityExtractor.Parse(entity, value);
// Assert
Assert.True(result.IsValid);
Assert.Equal(value, (result.Value as StringValue)?.Value);
}
[Theory]
[InlineData("+1-555-1234")]
[InlineData("(555) 123-4567")]
public void Parse_PhoneNumberEntity_ValidValue_ReturnsString(string value)
{
// Arrange
EntityReference entity = CreatePhoneNumberEntity();
// Act
EntityExtractionResult result = EntityExtractor.Parse(entity, value);
// Assert
Assert.True(result.IsValid);
Assert.Equal(value, (result.Value as StringValue)?.Value);
}
[Theory]
[InlineData("red")]
[InlineData("blue")]
public void Parse_ColorEntity_ValidValue_ReturnsString(string value)
{
// Arrange
EntityReference entity = CreateColorEntity();
// Act
EntityExtractionResult result = EntityExtractor.Parse(entity, value);
// Assert
Assert.True(result.IsValid);
Assert.Equal(value, (result.Value as StringValue)?.Value);
}
[Theory]
[InlineData("English")]
[InlineData("Spanish")]
public void Parse_LanguageEntity_ValidValue_ReturnsString(string value)
{
// Arrange
EntityReference entity = CreateLanguageEntity();
// Act
EntityExtractionResult result = EntityExtractor.Parse(entity, value);
// Assert
Assert.True(result.IsValid);
Assert.Equal(value, (result.Value as StringValue)?.Value);
}
[Theory]
[InlineData("Conference")]
[InlineData("Meeting")]
public void Parse_EventEntity_ValidValue_ReturnsString(string value)
{
// Arrange
EntityReference entity = CreateEventEntity();
// Act
EntityExtractionResult result = EntityExtractor.Parse(entity, value);
// Assert
Assert.True(result.IsValid);
Assert.Equal(value, (result.Value as StringValue)?.Value);
}
[Theory]
[InlineData("Starbucks")]
[InlineData("Museum")]
public void Parse_PointOfInterestEntity_ValidValue_ReturnsString(string value)
{
// Arrange
EntityReference entity = CreatePointOfInterestEntity();
// Act
EntityExtractionResult result = EntityExtractor.Parse(entity, value);
// Assert
Assert.True(result.IsValid);
Assert.Equal(value, (result.Value as StringValue)?.Value);
}
[Theory]
[InlineData("test string")]
[InlineData("any text")]
public void Parse_StringEntity_ValidValue_ReturnsString(string value)
{
// Arrange
EntityReference entity = CreateStringEntity();
// Act
EntityExtractionResult result = EntityExtractor.Parse(entity, value);
// Assert
Assert.True(result.IsValid);
Assert.Equal(value, (result.Value as StringValue)?.Value);
}
private static BooleanPrebuiltEntity CreateBooleanEntity() =>
new BooleanPrebuiltEntity.Builder().Build();
private static DatePrebuiltEntity CreateDateEntity() =>
new DatePrebuiltEntity.Builder().Build();
private static DateTimePrebuiltEntity CreateDateTimeEntity() =>
new DateTimePrebuiltEntity.Builder().Build();
private static DateTimeNoTimeZonePrebuiltEntity CreateDateTimeNoTimeZoneEntity() =>
new DateTimeNoTimeZonePrebuiltEntity.Builder().Build();
private static DurationPrebuiltEntity CreateDurationEntity() =>
new DurationPrebuiltEntity.Builder().Build();
private static EmailPrebuiltEntity CreateEmailEntity() =>
new EmailPrebuiltEntity.Builder().Build();
private static NumberPrebuiltEntity CreateNumberEntity() =>
new NumberPrebuiltEntity.Builder().Build();
private static AgePrebuiltEntity CreateAgeEntity() =>
new AgePrebuiltEntity.Builder().Build();
private static MoneyPrebuiltEntity CreateMoneyEntity() =>
new MoneyPrebuiltEntity.Builder().Build();
private static PercentagePrebuiltEntity CreatePercentageEntity() =>
new PercentagePrebuiltEntity.Builder().Build();
private static SpeedPrebuiltEntity CreateSpeedEntity() =>
new SpeedPrebuiltEntity.Builder().Build();
private static TemperaturePrebuiltEntity CreateTemperatureEntity() =>
new TemperaturePrebuiltEntity.Builder().Build();
private static WeightPrebuiltEntity CreateWeightEntity() =>
new WeightPrebuiltEntity.Builder().Build();
private static URLPrebuiltEntity CreateURLEntity() =>
new URLPrebuiltEntity.Builder().Build();
private static CityPrebuiltEntity CreateCityEntity() =>
new CityPrebuiltEntity.Builder().Build();
private static StatePrebuiltEntity CreateStateEntity() =>
new StatePrebuiltEntity.Builder().Build();
private static CountryOrRegionPrebuiltEntity CreateCountryOrRegionEntity() =>
new CountryOrRegionPrebuiltEntity.Builder().Build();
private static ContinentPrebuiltEntity CreateContinentEntity() =>
new ContinentPrebuiltEntity.Builder().Build();
private static StreetAddressPrebuiltEntity CreateStreetAddressEntity() =>
new StreetAddressPrebuiltEntity.Builder().Build();
private static PhoneNumberPrebuiltEntity CreatePhoneNumberEntity() =>
new PhoneNumberPrebuiltEntity.Builder().Build();
private static ColorPrebuiltEntity CreateColorEntity() =>
new ColorPrebuiltEntity.Builder().Build();
private static LanguagePrebuiltEntity CreateLanguageEntity() =>
new LanguagePrebuiltEntity.Builder().Build();
private static EventPrebuiltEntity CreateEventEntity() =>
new EventPrebuiltEntity.Builder().Build();
private static PointOfInterestPrebuiltEntity CreatePointOfInterestEntity() =>
new PointOfInterestPrebuiltEntity.Builder().Build();
private static StringPrebuiltEntity CreateStringEntity() =>
new StringPrebuiltEntity.Builder().Build();
}