chore: import upstream snapshot with attribution
This commit is contained in:
+168
@@ -0,0 +1,168 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.Extensions.VectorData;
|
||||
using Xunit;
|
||||
|
||||
namespace AzureAISearch.ConformanceTests.Support;
|
||||
|
||||
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
|
||||
|
||||
public class AzureAISearchAllTypes
|
||||
{
|
||||
[VectorStoreKey]
|
||||
public string Id { get; set; }
|
||||
|
||||
[VectorStoreData]
|
||||
public bool BoolProperty { get; set; }
|
||||
[VectorStoreData]
|
||||
public bool? NullableBoolProperty { get; set; }
|
||||
[VectorStoreData]
|
||||
public string StringProperty { get; set; }
|
||||
[VectorStoreData]
|
||||
public string? NullableStringProperty { get; set; }
|
||||
[VectorStoreData]
|
||||
public int IntProperty { get; set; }
|
||||
[VectorStoreData]
|
||||
public int? NullableIntProperty { get; set; }
|
||||
[VectorStoreData]
|
||||
public long LongProperty { get; set; }
|
||||
[VectorStoreData]
|
||||
public long? NullableLongProperty { get; set; }
|
||||
[VectorStoreData]
|
||||
public float FloatProperty { get; set; }
|
||||
[VectorStoreData]
|
||||
public float? NullableFloatProperty { get; set; }
|
||||
[VectorStoreData]
|
||||
public double DoubleProperty { get; set; }
|
||||
[VectorStoreData]
|
||||
public double? NullableDoubleProperty { get; set; }
|
||||
[VectorStoreData]
|
||||
public DateTimeOffset DateTimeOffsetProperty { get; set; }
|
||||
[VectorStoreData]
|
||||
public DateTimeOffset? NullableDateTimeOffsetProperty { get; set; }
|
||||
|
||||
[VectorStoreData]
|
||||
public string[] StringArray { get; set; }
|
||||
[VectorStoreData]
|
||||
public List<string> StringList { get; set; }
|
||||
[VectorStoreData]
|
||||
public bool[] BoolArray { get; set; }
|
||||
[VectorStoreData]
|
||||
public List<bool> BoolList { get; set; }
|
||||
[VectorStoreData]
|
||||
public int[] IntArray { get; set; }
|
||||
[VectorStoreData]
|
||||
public List<int> IntList { get; set; }
|
||||
[VectorStoreData]
|
||||
public long[] LongArray { get; set; }
|
||||
[VectorStoreData]
|
||||
public List<long> LongList { get; set; }
|
||||
[VectorStoreData]
|
||||
public float[] FloatArray { get; set; }
|
||||
[VectorStoreData]
|
||||
public List<float> FloatList { get; set; }
|
||||
[VectorStoreData]
|
||||
public double[] DoubleArray { get; set; }
|
||||
[VectorStoreData]
|
||||
public List<double> DoubleList { get; set; }
|
||||
[VectorStoreData]
|
||||
public DateTimeOffset[] DateTimeOffsetArray { get; set; }
|
||||
[VectorStoreData]
|
||||
public List<DateTimeOffset> DateTimeOffsetList { get; set; }
|
||||
|
||||
[VectorStoreVector(Dimensions: 8, DistanceFunction = DistanceFunction.DotProductSimilarity)]
|
||||
public ReadOnlyMemory<float>? Embedding { get; set; }
|
||||
|
||||
internal void AssertEqual(AzureAISearchAllTypes other)
|
||||
{
|
||||
Assert.Equal(this.Id, other.Id);
|
||||
Assert.Equal(this.BoolProperty, other.BoolProperty);
|
||||
Assert.Equal(this.NullableBoolProperty, other.NullableBoolProperty);
|
||||
Assert.Equal(this.StringProperty, other.StringProperty);
|
||||
Assert.Equal(this.NullableStringProperty, other.NullableStringProperty);
|
||||
Assert.Equal(this.IntProperty, other.IntProperty);
|
||||
Assert.Equal(this.NullableIntProperty, other.NullableIntProperty);
|
||||
Assert.Equal(this.LongProperty, other.LongProperty);
|
||||
Assert.Equal(this.NullableLongProperty, other.NullableLongProperty);
|
||||
Assert.Equal(this.FloatProperty, other.FloatProperty);
|
||||
Assert.Equal(this.NullableFloatProperty, other.NullableFloatProperty);
|
||||
Assert.Equal(this.DoubleProperty, other.DoubleProperty);
|
||||
Assert.Equal(this.NullableDoubleProperty, other.NullableDoubleProperty);
|
||||
AssertEqual(this.DateTimeOffsetProperty, other.DateTimeOffsetProperty);
|
||||
Assert.Equal(this.NullableDateTimeOffsetProperty.HasValue, other.NullableDateTimeOffsetProperty.HasValue);
|
||||
if (this.NullableDateTimeOffsetProperty.HasValue && other.NullableDateTimeOffsetProperty.HasValue)
|
||||
{
|
||||
AssertEqual(this.NullableDateTimeOffsetProperty.Value, other.NullableDateTimeOffsetProperty.Value);
|
||||
}
|
||||
|
||||
Assert.Equal(this.StringArray, other.StringArray);
|
||||
Assert.Equal(this.StringList, other.StringList);
|
||||
Assert.Equal(this.BoolArray, other.BoolArray);
|
||||
Assert.Equal(this.BoolList, other.BoolList);
|
||||
Assert.Equal(this.IntArray, other.IntArray);
|
||||
Assert.Equal(this.IntList, other.IntList);
|
||||
Assert.Equal(this.LongArray, other.LongArray);
|
||||
Assert.Equal(this.LongList, other.LongList);
|
||||
Assert.Equal(this.FloatArray, other.FloatArray);
|
||||
Assert.Equal(this.FloatList, other.FloatList);
|
||||
Assert.Equal(this.DoubleArray, other.DoubleArray);
|
||||
Assert.Equal(this.DoubleList, other.DoubleList);
|
||||
Assert.Equal(this.DateTimeOffsetArray.Length, other.DateTimeOffsetArray.Length);
|
||||
for (int i = 0; i < this.DateTimeOffsetArray.Length; i++)
|
||||
{
|
||||
AssertEqual(this.DateTimeOffsetArray[i], other.DateTimeOffsetArray[i]);
|
||||
}
|
||||
Assert.Equal(this.DateTimeOffsetList.Count, other.DateTimeOffsetList.Count);
|
||||
for (int i = 0; i < this.DateTimeOffsetList.Count; i++)
|
||||
{
|
||||
AssertEqual(this.DateTimeOffsetList[i], other.DateTimeOffsetList[i]);
|
||||
}
|
||||
|
||||
Assert.Equal(this.Embedding!.Value.ToArray(), other.Embedding!.Value.ToArray());
|
||||
|
||||
static void AssertEqual(DateTimeOffset expected, DateTimeOffset actual)
|
||||
{
|
||||
Assert.Equal(expected, actual, TimeSpan.FromSeconds(0.01));
|
||||
}
|
||||
}
|
||||
|
||||
internal static VectorStoreCollectionDefinition GetRecordDefinition()
|
||||
=> new()
|
||||
{
|
||||
Properties =
|
||||
[
|
||||
new VectorStoreKeyProperty(nameof(AzureAISearchAllTypes.Id), typeof(string)),
|
||||
new VectorStoreDataProperty(nameof(AzureAISearchAllTypes.BoolProperty), typeof(bool)),
|
||||
new VectorStoreDataProperty(nameof(AzureAISearchAllTypes.NullableBoolProperty), typeof(bool?)),
|
||||
new VectorStoreDataProperty(nameof(AzureAISearchAllTypes.StringProperty), typeof(string)),
|
||||
new VectorStoreDataProperty(nameof(AzureAISearchAllTypes.NullableStringProperty), typeof(string)),
|
||||
new VectorStoreDataProperty(nameof(AzureAISearchAllTypes.IntProperty), typeof(int)),
|
||||
new VectorStoreDataProperty(nameof(AzureAISearchAllTypes.NullableIntProperty), typeof(int?)),
|
||||
new VectorStoreDataProperty(nameof(AzureAISearchAllTypes.LongProperty), typeof(long)),
|
||||
new VectorStoreDataProperty(nameof(AzureAISearchAllTypes.NullableLongProperty), typeof(long?)),
|
||||
new VectorStoreDataProperty(nameof(AzureAISearchAllTypes.FloatProperty), typeof(float)),
|
||||
new VectorStoreDataProperty(nameof(AzureAISearchAllTypes.NullableFloatProperty), typeof(float?)),
|
||||
new VectorStoreDataProperty(nameof(AzureAISearchAllTypes.DoubleProperty), typeof(double)),
|
||||
new VectorStoreDataProperty(nameof(AzureAISearchAllTypes.NullableDoubleProperty), typeof(double?)),
|
||||
new VectorStoreDataProperty(nameof(AzureAISearchAllTypes.DateTimeOffsetProperty), typeof(DateTimeOffset)),
|
||||
new VectorStoreDataProperty(nameof(AzureAISearchAllTypes.NullableDateTimeOffsetProperty), typeof(DateTimeOffset?)),
|
||||
|
||||
new VectorStoreDataProperty(nameof(AzureAISearchAllTypes.StringArray), typeof(string[])),
|
||||
new VectorStoreDataProperty(nameof(AzureAISearchAllTypes.StringList), typeof(List<string>)),
|
||||
new VectorStoreDataProperty(nameof(AzureAISearchAllTypes.BoolArray), typeof(bool[])),
|
||||
new VectorStoreDataProperty(nameof(AzureAISearchAllTypes.BoolList), typeof(List<bool>)),
|
||||
new VectorStoreDataProperty(nameof(AzureAISearchAllTypes.IntArray), typeof(int[])),
|
||||
new VectorStoreDataProperty(nameof(AzureAISearchAllTypes.IntList), typeof(List<int>)),
|
||||
new VectorStoreDataProperty(nameof(AzureAISearchAllTypes.LongArray), typeof(long[])),
|
||||
new VectorStoreDataProperty(nameof(AzureAISearchAllTypes.LongList), typeof(List<long>)),
|
||||
new VectorStoreDataProperty(nameof(AzureAISearchAllTypes.FloatArray), typeof(float[])),
|
||||
new VectorStoreDataProperty(nameof(AzureAISearchAllTypes.FloatList), typeof(List<float>)),
|
||||
new VectorStoreDataProperty(nameof(AzureAISearchAllTypes.DoubleArray), typeof(double[])),
|
||||
new VectorStoreDataProperty(nameof(AzureAISearchAllTypes.DoubleList), typeof(List<double>)),
|
||||
new VectorStoreDataProperty(nameof(AzureAISearchAllTypes.DateTimeOffsetArray), typeof(DateTimeOffset[])),
|
||||
new VectorStoreDataProperty(nameof(AzureAISearchAllTypes.DateTimeOffsetList), typeof(List<DateTimeOffset>)),
|
||||
|
||||
new VectorStoreVectorProperty(nameof(AzureAISearchAllTypes.Embedding), typeof(ReadOnlyMemory<float>?), 8) { DistanceFunction = Microsoft.Extensions.VectorData.DistanceFunction.DotProductSimilarity }
|
||||
]
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using VectorData.ConformanceTests.Support;
|
||||
|
||||
namespace AzureAISearch.ConformanceTests.Support;
|
||||
|
||||
public class AzureAISearchFixture : VectorStoreFixture
|
||||
{
|
||||
public override TestStore TestStore => AzureAISearchTestStore.Instance;
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace AzureAISearch.ConformanceTests.Support;
|
||||
|
||||
#pragma warning disable CA1810 // Initialize all static fields when those fields are declared
|
||||
|
||||
internal static class AzureAISearchTestEnvironment
|
||||
{
|
||||
#pragma warning disable CA1308 // Normalize strings to uppercase
|
||||
public static readonly string TestIndexPostfix = '-' + new Regex("[^a-zA-Z0-9]").Replace(Environment.MachineName.ToLowerInvariant(), "");
|
||||
#pragma warning restore CA1308 // Normalize strings to uppercase
|
||||
|
||||
public static readonly string? ServiceUrl, ApiKey;
|
||||
|
||||
public static bool IsConnectionInfoDefined => ServiceUrl is not null;
|
||||
|
||||
static AzureAISearchTestEnvironment()
|
||||
{
|
||||
var configuration = new ConfigurationBuilder()
|
||||
.AddJsonFile(path: "testsettings.json", optional: true)
|
||||
.AddJsonFile(path: "testsettings.development.json", optional: true)
|
||||
.AddEnvironmentVariables()
|
||||
.AddUserSecrets<AzureAISearchUrlRequiredAttribute>()
|
||||
.Build();
|
||||
|
||||
var azureAISearchSection = configuration.GetSection("AzureAISearch");
|
||||
ServiceUrl = azureAISearchSection?["ServiceUrl"];
|
||||
ApiKey = azureAISearchSection?["ApiKey"];
|
||||
}
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.Linq.Expressions;
|
||||
using Azure;
|
||||
using Azure.Identity;
|
||||
using Azure.Search.Documents.Indexes;
|
||||
using Humanizer;
|
||||
using Microsoft.Extensions.VectorData;
|
||||
using Microsoft.SemanticKernel.Connectors.AzureAISearch;
|
||||
using VectorData.ConformanceTests.Support;
|
||||
|
||||
namespace AzureAISearch.ConformanceTests.Support;
|
||||
|
||||
internal sealed class AzureAISearchTestStore : TestStore
|
||||
{
|
||||
public static AzureAISearchTestStore Instance { get; } = new();
|
||||
|
||||
private SearchIndexClient? _client;
|
||||
|
||||
public SearchIndexClient Client
|
||||
=> this._client ?? throw new InvalidOperationException("Call InitializeAsync() first");
|
||||
|
||||
public AzureAISearchVectorStore GetVectorStore(AzureAISearchVectorStoreOptions options)
|
||||
=> new(this.Client, options);
|
||||
|
||||
private AzureAISearchTestStore()
|
||||
{
|
||||
}
|
||||
|
||||
protected override Task StartAsync()
|
||||
{
|
||||
(string? serviceUrl, string? apiKey) = (AzureAISearchTestEnvironment.ServiceUrl, AzureAISearchTestEnvironment.ApiKey);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(serviceUrl))
|
||||
{
|
||||
throw new InvalidOperationException("Service URL is not configured, set AzureAISearch:ServiceUrl (and AzureAISearch:ApiKey if you want)");
|
||||
}
|
||||
|
||||
this._client = string.IsNullOrWhiteSpace(apiKey)
|
||||
? new SearchIndexClient(new Uri(serviceUrl), new DefaultAzureCredential())
|
||||
: new SearchIndexClient(new Uri(serviceUrl), new AzureKeyCredential(apiKey));
|
||||
|
||||
this.DefaultVectorStore = new AzureAISearchVectorStore(this._client);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
// Azure AI search only supports lowercase letters, digits or dashes.
|
||||
// Also, add a suffix containing machine name to allow multiple developers to work against the same cloud instance.
|
||||
public override string AdjustCollectionName(string baseName)
|
||||
=> baseName.Kebaberize() + AzureAISearchTestEnvironment.TestIndexPostfix;
|
||||
|
||||
public override async Task WaitForDataAsync<TKey, TRecord>(
|
||||
VectorStoreCollection<TKey, TRecord> collection,
|
||||
int recordCount,
|
||||
Expression<Func<TRecord, bool>>? filter = null,
|
||||
Expression<Func<TRecord, object?>>? vectorProperty = null,
|
||||
int? vectorSize = null,
|
||||
object? dummyVector = null)
|
||||
{
|
||||
await base.WaitForDataAsync(collection, recordCount, filter, vectorProperty, vectorSize, dummyVector);
|
||||
|
||||
// There seems to be some asynchronicity/race condition specific to Azure AI Search which isn't taken care
|
||||
// of by the generic retry loop in the base implementation.
|
||||
// TODO: Investigate this and remove
|
||||
await Task.Delay(TimeSpan.FromMilliseconds(1000));
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using VectorData.ConformanceTests.Xunit;
|
||||
|
||||
namespace AzureAISearch.ConformanceTests.Support;
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the sqlite_vec extension is properly installed, and skips the test(s) otherwise.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Assembly)]
|
||||
public sealed class AzureAISearchUrlRequiredAttribute : Attribute, ITestCondition
|
||||
{
|
||||
public ValueTask<bool> IsMetAsync() => new(AzureAISearchTestEnvironment.IsConnectionInfoDefined);
|
||||
|
||||
public string Skip { get; set; } = "Service URL is not configured, set AzureAISearch:ServiceUrl (and AzureAISearch:ApiKey if you don't use managed identity).";
|
||||
|
||||
public string SkipReason
|
||||
=> this.Skip;
|
||||
}
|
||||
Reference in New Issue
Block a user