// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Collections.Generic;
using Azure.Search.Documents.Indexes.Models;
using Microsoft.Extensions.VectorData;
using Microsoft.Extensions.VectorData.ProviderServices;
using Microsoft.SemanticKernel.Connectors.AzureAISearch;
using Xunit;
namespace SemanticKernel.Connectors.AzureAISearch.UnitTests;
///
/// Contains tests for the class.
///
public class AzureAISearchCollectionCreateMappingTests
{
[Fact]
public void MapKeyFieldCreatesSearchableField()
{
// Arrange
var keyProperty = new KeyPropertyModel("testkey", typeof(string)) { StorageName = "test_key" };
// Act
var result = AzureAISearchCollectionCreateMapping.MapKeyField(keyProperty);
// Assert
Assert.NotNull(result);
Assert.Equal("test_key", result.Name);
Assert.True(result.IsKey);
Assert.True(result.IsFilterable);
}
[Theory]
[InlineData(true)]
[InlineData(false)]
public void MapFilterableStringDataFieldCreatesSimpleField(bool isFilterable)
{
// Arrange
var dataProperty = new DataPropertyModel("testdata", typeof(string))
{
IsIndexed = isFilterable,
StorageName = "test_data"
};
// Act
var result = AzureAISearchCollectionCreateMapping.MapDataField(dataProperty);
// Assert
Assert.NotNull(result);
Assert.IsType(result);
Assert.Equal("test_data", result.Name);
Assert.False(result.IsKey);
Assert.Equal(isFilterable, result.IsFilterable);
}
[Theory]
[InlineData(true)]
[InlineData(false)]
public void MapFullTextSearchableStringDataFieldCreatesSearchableField(bool isFilterable)
{
// Arrange
var dataProperty = new DataPropertyModel("testdata", typeof(string))
{
IsIndexed = isFilterable,
IsFullTextIndexed = true,
StorageName = "test_data"
};
// Act
var result = AzureAISearchCollectionCreateMapping.MapDataField(dataProperty);
// Assert
Assert.NotNull(result);
Assert.IsType(result);
Assert.Equal("test_data", result.Name);
Assert.False(result.IsKey);
Assert.Equal(isFilterable, result.IsFilterable);
}
[Fact]
public void MapFullTextSearchableStringDataFieldThrowsForInvalidType()
{
// Arrange
var dataProperty = new DataPropertyModel("testdata", typeof(int))
{
IsFullTextIndexed = true,
StorageName = "test_data"
};
// Act & Assert
Assert.Throws(() => AzureAISearchCollectionCreateMapping.MapDataField(dataProperty));
}
[Theory]
[InlineData(true)]
[InlineData(false)]
public void MapDataFieldCreatesSimpleField(bool isFilterable)
{
// Arrange
var dataProperty = new DataPropertyModel("testdata", typeof(int))
{
IsIndexed = isFilterable,
StorageName = "test_data"
};
// Act
var result = AzureAISearchCollectionCreateMapping.MapDataField(dataProperty);
// Assert
Assert.NotNull(result);
Assert.IsType(result);
Assert.Equal("test_data", result.Name);
Assert.Equal(SearchFieldDataType.Int32, result.Type);
Assert.False(result.IsKey);
Assert.Equal(isFilterable, result.IsFilterable);
}
[Fact]
public void MapVectorFieldCreatesVectorSearchField()
{
// Arrange
var vectorProperty = new VectorPropertyModel("testvector", typeof(ReadOnlyMemory))
{
Dimensions = 10,
IndexKind = IndexKind.Flat,
DistanceFunction = DistanceFunction.DotProductSimilarity,
StorageName = "test_vector"
};
// Act
var (vectorSearchField, algorithmConfiguration, vectorSearchProfile) = AzureAISearchCollectionCreateMapping.MapVectorField(vectorProperty);
// Assert
Assert.NotNull(vectorSearchField);
Assert.NotNull(algorithmConfiguration);
Assert.NotNull(vectorSearchProfile);
Assert.Equal("test_vector", vectorSearchField.Name);
Assert.Equal(vectorProperty.Dimensions, vectorSearchField.VectorSearchDimensions);
Assert.Equal("test_vectorAlgoConfig", algorithmConfiguration.Name);
Assert.IsType(algorithmConfiguration);
var flatConfig = algorithmConfiguration as ExhaustiveKnnAlgorithmConfiguration;
Assert.Equal(VectorSearchAlgorithmMetric.DotProduct, flatConfig!.Parameters.Metric);
Assert.Equal("test_vectorProfile", vectorSearchProfile.Name);
Assert.Equal("test_vectorAlgoConfig", vectorSearchProfile.AlgorithmConfigurationName);
}
[Theory]
[InlineData(IndexKind.Hnsw, typeof(HnswAlgorithmConfiguration))]
[InlineData(IndexKind.Flat, typeof(ExhaustiveKnnAlgorithmConfiguration))]
public void MapVectorFieldCreatesExpectedAlgoConfigTypes(string indexKind, Type algoConfigType)
{
// Arrange
var vectorProperty = new VectorPropertyModel("testvector", typeof(ReadOnlyMemory))
{
Dimensions = 10,
IndexKind = indexKind,
DistanceFunction = DistanceFunction.DotProductSimilarity,
StorageName = "test_vector"
};
// Act
var (vectorSearchField, algorithmConfiguration, vectorSearchProfile) = AzureAISearchCollectionCreateMapping.MapVectorField(vectorProperty);
// Assert
Assert.Equal("test_vectorAlgoConfig", algorithmConfiguration.Name);
Assert.Equal(algoConfigType, algorithmConfiguration.GetType());
}
[Fact]
public void MapVectorFieldDefaultsToHsnwAndCosine()
{
// Arrange
var vectorProperty = new VectorPropertyModel("testvector", typeof(ReadOnlyMemory)) { Dimensions = 10 };
// Act
var (vectorSearchField, algorithmConfiguration, vectorSearchProfile) = AzureAISearchCollectionCreateMapping.MapVectorField(vectorProperty);
// Assert
Assert.IsType(algorithmConfiguration);
var hnswConfig = algorithmConfiguration as HnswAlgorithmConfiguration;
Assert.Equal(VectorSearchAlgorithmMetric.Cosine, hnswConfig!.Parameters.Metric);
}
[Fact]
public void MapVectorFieldThrowsForUnsupportedDistanceFunction()
{
// Arrange
var vectorProperty = new VectorPropertyModel("testvector", typeof(ReadOnlyMemory))
{
Dimensions = 10,
DistanceFunction = DistanceFunction.ManhattanDistance,
};
// Act & Assert
Assert.Throws(() => AzureAISearchCollectionCreateMapping.MapVectorField(vectorProperty));
}
[Theory]
[MemberData(nameof(DataTypeMappingOptions))]
public void GetSDKFieldDataTypeMapsTypesCorrectly(Type propertyType, SearchFieldDataType searchFieldDataType)
{
// Act & Assert
Assert.Equal(searchFieldDataType, AzureAISearchCollectionCreateMapping.GetSDKFieldDataType(propertyType));
}
public static IEnumerable