chore: import upstream snapshot with attribution
CodeQL / Analyze (csharp) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 13:21:23 +08:00
commit b957a53def
5423 changed files with 863745 additions and 0 deletions
@@ -0,0 +1,450 @@
// Copyright (c) Microsoft. All rights reserved.
using Microsoft.Extensions.VectorData;
using VectorData.ConformanceTests.Support;
using VectorData.ConformanceTests.Xunit;
using Xunit;
namespace VectorData.ConformanceTests.ModelTests;
public abstract class DynamicModelTests<TKey>(DynamicModelTests<TKey>.Fixture fixture) : IAsyncLifetime
where TKey : notnull
{
#region Get
[ConditionalTheory, MemberData(nameof(IncludeVectorsData))]
public virtual async Task GetAsync_single_record(bool includeVectors)
{
var expectedRecord = fixture.TestData[0];
var received = await fixture.Collection.GetAsync(
(TKey)expectedRecord[KeyPropertyName]!,
new() { IncludeVectors = includeVectors });
AssertEquivalent(expectedRecord, received, includeVectors, fixture.TestStore.VectorsComparable);
}
[ConditionalTheory, MemberData(nameof(IncludeVectorsData))]
public virtual async Task GetAsync_multiple_records(bool includeVectors)
{
var expectedRecords = fixture.TestData.Take(2);
var ids = expectedRecords.Select(record => record[KeyPropertyName]!);
var received = await fixture.Collection.GetAsync(ids, new() { IncludeVectors = includeVectors }).ToArrayAsync();
foreach (var record in expectedRecords)
{
AssertEquivalent(
record,
received.Single(r => r[KeyPropertyName]!.Equals(record[KeyPropertyName])),
includeVectors,
fixture.TestStore.VectorsComparable);
}
}
[ConditionalFact]
public virtual async Task GetAsync_throws_for_null_key()
{
// Skip this test for value type keys
if (default(TKey) is not null)
{
return;
}
ArgumentNullException ex = await Assert.ThrowsAsync<ArgumentNullException>(() => fixture.Collection.GetAsync((TKey)default!));
Assert.Equal("key", ex.ParamName);
}
[ConditionalFact]
public virtual async Task GetAsync_throws_for_null_keys()
{
ArgumentNullException ex = await Assert.ThrowsAsync<ArgumentNullException>(() => fixture.Collection.GetAsync(keys: null!).ToArrayAsync().AsTask());
Assert.Equal("keys", ex.ParamName);
}
[ConditionalFact]
public virtual async Task GetAsync_returns_null_for_missing_key()
{
TKey key = fixture.GenerateNextKey<TKey>();
Assert.Null(await fixture.Collection.GetAsync(key));
}
[ConditionalFact]
public virtual async Task GetAsync_returns_empty_for_empty_keys()
{
Assert.Empty(await fixture.Collection.GetAsync([]).ToArrayAsync());
}
[ConditionalTheory, MemberData(nameof(IncludeVectorsData))]
public virtual async Task GetAsync_with_filter(bool includeVectors)
{
var expectedRecord = fixture.TestData[0];
var results = await fixture.Collection.GetAsync(
r => (int)r[IntegerPropertyName]! == 1,
top: 2,
new() { IncludeVectors = includeVectors })
.ToListAsync();
var receivedRecord = Assert.Single(results);
AssertEquivalent(expectedRecord, receivedRecord, includeVectors, fixture.TestStore.VectorsComparable);
}
[ConditionalFact]
public virtual async Task GetAsync_with_filter_by_true()
{
var count = await fixture.Collection.GetAsync(r => true, top: 100).CountAsync();
Assert.Equal(fixture.TestData.Count, count);
Assert.True(count < 100);
}
[ConditionalFact]
public virtual async Task GetAsync_with_filter_and_OrderBy()
{
var ascendingNumbers = fixture.TestData
.Where(r => (int)r[IntegerPropertyName]! > 1)
.OrderBy(r => r[IntegerPropertyName])
.Take(2)
.Select(r => (int)r[IntegerPropertyName]!)
.ToList();
var descendingNumbers = fixture.TestData
.Where(r => (int)r[IntegerPropertyName]! > 1)
.OrderByDescending(r => r[IntegerPropertyName])
.Take(2)
.Select(r => (int)r[IntegerPropertyName]!)
.ToList();
// Make sure the actual results are different for ascending/descending, otherwise the test is meaningless
Assert.NotEqual(ascendingNumbers, descendingNumbers);
// Finally, query once with ascending and once with descending, comparing against the expected results above.
var results = await fixture.Collection.GetAsync(
r => (int)r[IntegerPropertyName]! > 1,
top: 2,
new() { OrderBy = o => o.Ascending(r => r[IntegerPropertyName]) })
.Select(r => (int)r[IntegerPropertyName]!)
.ToListAsync();
Assert.Equal(ascendingNumbers, results);
results = await fixture.Collection.GetAsync(
r => (int)r[IntegerPropertyName]! > 1,
top: 2,
new() { OrderBy = o => o.Descending(r => r[IntegerPropertyName]) })
.Select(r => (int)r[IntegerPropertyName]!)
.ToListAsync();
Assert.Equal(descendingNumbers, results);
}
[ConditionalFact]
public virtual async Task GetAsync_with_filter_and_multiple_OrderBys()
{
var ascendingNumbers = fixture.TestData
.OrderByDescending(r => r[StringPropertyName])
.ThenBy(r => r[IntegerPropertyName])
.Take(2)
.Select(r => (int)r[IntegerPropertyName]!)
.ToList();
var descendingNumbers = fixture.TestData
.OrderByDescending(r => r[StringPropertyName])
.ThenByDescending(r => r[IntegerPropertyName])
.Take(2)
.Select(r => (int)r[IntegerPropertyName]!)
.ToList();
// Make sure the actual results are different for ascending/descending, otherwise the test is meaningless
Assert.NotEqual(ascendingNumbers, descendingNumbers);
var results = await fixture.Collection.GetAsync(
r => true,
top: 2,
new() { OrderBy = o => o.Descending(r => r[StringPropertyName]).Ascending(r => r[IntegerPropertyName]) })
.Select(r => (int)r[IntegerPropertyName]!)
.ToListAsync();
Assert.Equal(ascendingNumbers, results);
results = await fixture.Collection.GetAsync(
r => true,
top: 2,
new() { OrderBy = o => o.Descending(r => r[StringPropertyName]).Descending(r => r[IntegerPropertyName]) })
.Select(r => (int)r[IntegerPropertyName]!)
.ToListAsync();
Assert.Equal(descendingNumbers, results);
}
[ConditionalFact]
public virtual async Task GetAsync_with_filter_and_OrderBy_and_Skip()
{
var results = await fixture.Collection.GetAsync(
r => (int)r[IntegerPropertyName]! > 1,
top: 2,
new() { OrderBy = o => o.Ascending(r => r[IntegerPropertyName]), Skip = 1 })
.Select(r => (int)r[IntegerPropertyName]!)
.ToListAsync();
Assert.Equal(
fixture.TestData
.Where(r => (int)r[IntegerPropertyName]! > 1)
.OrderBy(r => r[IntegerPropertyName])
.Skip(1)
.Take(2)
.Select(r => (int)r[IntegerPropertyName]!),
results);
}
#endregion Get
#region Upsert
[ConditionalFact]
public virtual async Task Insert_single_record()
{
TKey expectedKey = fixture.GenerateNextKey<TKey>();
var inserted = new Dictionary<string, object?>
{
[KeyPropertyName] = expectedKey,
[StringPropertyName] = "some",
[IntegerPropertyName] = 123,
[VectorPropertyName] = new ReadOnlyMemory<float>([10, 0, 0])
};
Assert.Null(await this.Collection.GetAsync(expectedKey));
await this.Collection.UpsertAsync(inserted);
var received = await this.Collection.GetAsync(expectedKey, new() { IncludeVectors = true });
AssertEquivalent(inserted, received, includeVectors: true, fixture.TestStore.VectorsComparable);
}
[ConditionalFact]
public virtual async Task Update_single_record()
{
var existingRecord = fixture.TestData[1];
var updated = new Dictionary<string, object?>
{
[KeyPropertyName] = existingRecord[KeyPropertyName],
[StringPropertyName] = "different",
[IntegerPropertyName] = 456,
[VectorPropertyName] = new ReadOnlyMemory<float>(Enumerable.Repeat(0.7f, 3).ToArray())
};
Assert.NotNull(await this.Collection.GetAsync((TKey)existingRecord[KeyPropertyName]!));
await this.Collection.UpsertAsync(updated);
var received = await this.Collection.GetAsync((TKey)existingRecord[KeyPropertyName]!, new() { IncludeVectors = true });
AssertEquivalent(updated, received, includeVectors: true, fixture.TestStore.VectorsComparable);
}
[ConditionalFact]
public virtual async Task Insert_multiple_records()
{
Dictionary<string, object?>[] newRecords =
[
new()
{
[KeyPropertyName] = fixture.GenerateNextKey<TKey>(),
[IntegerPropertyName] = 100,
[StringPropertyName] = "New record 1",
[VectorPropertyName] = new ReadOnlyMemory<float>([10, 0, 1])
},
new()
{
[KeyPropertyName] = fixture.GenerateNextKey<TKey>(),
[IntegerPropertyName] = 101,
[StringPropertyName] = "New record 2",
[VectorPropertyName] = new ReadOnlyMemory<float>([10, 0, 2])
},
];
var keys = newRecords.Select(record => record[KeyPropertyName]!).ToArray();
Assert.Empty(await this.Collection.GetAsync(keys).ToArrayAsync());
await this.Collection.UpsertAsync(newRecords);
var received = await this.Collection.GetAsync(keys, new() { IncludeVectors = true }).ToArrayAsync();
Assert.Collection(
received.OrderBy(r => r[IntegerPropertyName]),
r => AssertEquivalent(newRecords[0], r, includeVectors: true, fixture.TestStore.VectorsComparable),
r => AssertEquivalent(newRecords[1], r, includeVectors: true, fixture.TestStore.VectorsComparable));
}
#endregion Upsert
#region Delete
[ConditionalFact]
public async Task Delete_single_record()
{
var recordToRemove = fixture.TestData[2];
Assert.NotNull(await fixture.Collection.GetAsync((TKey)recordToRemove[KeyPropertyName]!));
await fixture.Collection.DeleteAsync((TKey)recordToRemove[KeyPropertyName]!);
Assert.Null(await fixture.Collection.GetAsync((TKey)recordToRemove[KeyPropertyName]!));
}
// TODO: https://github.com/microsoft/semantic-kernel/issues/13303
// [ConditionalFact]
// public virtual async Task Delete_multiple_records()
// {
// TKey[] keysToRemove = [(TKey)fixture.TestData[0][KeyPropertyName]!, (TKey)fixture.TestData[1][KeyPropertyName]!];
// await this.Collection.DeleteAsync(keysToRemove);
// Assert.Empty(await this.Collection.GetAsync(keysToRemove).ToArrayAsync());
// Assert.Equal(fixture.TestData.Count - 2, await this.GetRecordCount());
// }
[ConditionalFact]
public virtual async Task DeleteAsync_does_nothing_for_non_existing_key()
{
TKey key = fixture.GenerateNextKey<TKey>();
await fixture.Collection.DeleteAsync(key);
}
#endregion Delete
#region Search
[ConditionalTheory, MemberData(nameof(IncludeVectorsData))]
public virtual async Task SearchAsync(bool includeVectors)
{
var expectedRecord = fixture.TestData[0];
var result = await this.Collection
.SearchAsync(
expectedRecord[VectorPropertyName]!,
top: 1,
new() { IncludeVectors = includeVectors })
.SingleAsync();
AssertEquivalent(expectedRecord, result.Record, includeVectors, fixture.TestStore.VectorsComparable);
}
[ConditionalFact]
public virtual async Task SearchAsync_with_Skip()
{
var result = await this.Collection
.SearchAsync(
fixture.TestData[0][VectorPropertyName]!,
top: 1,
new() { Skip = 1 })
.SingleAsync();
AssertEquivalent(fixture.TestData[1], result.Record, includeVectors: false, fixture.TestStore.VectorsComparable);
}
[ConditionalFact]
public virtual async Task SearchAsync_with_Filter()
{
var result = await this.Collection
.SearchAsync(
fixture.TestData[0][VectorPropertyName]!,
top: 1,
new() { Filter = r => (int)r[IntegerPropertyName]! == 2 })
.SingleAsync();
AssertEquivalent(fixture.TestData[1], result.Record, includeVectors: false, fixture.TestStore.VectorsComparable);
}
#endregion Search
protected static void AssertEquivalent(Dictionary<string, object?> expected, Dictionary<string, object?>? actual, bool includeVectors, bool compareVectors)
{
Assert.NotNull(actual);
Assert.Equal(expected[KeyPropertyName], actual[KeyPropertyName]);
Assert.Equal(expected[StringPropertyName], actual[StringPropertyName]);
Assert.Equal(expected[IntegerPropertyName], actual[IntegerPropertyName]);
if (includeVectors)
{
Assert.Equal(
((ReadOnlyMemory<float>)expected[VectorPropertyName]!).Length,
((ReadOnlyMemory<float>)actual[VectorPropertyName]!).Length);
if (compareVectors)
{
Assert.Equal(
((ReadOnlyMemory<float>)expected[VectorPropertyName]!).ToArray(),
((ReadOnlyMemory<float>)actual[VectorPropertyName]!).ToArray());
}
}
else
{
Assert.False(actual.ContainsKey(VectorPropertyName));
}
}
public const string KeyPropertyName = "key";
public const string StringPropertyName = "text";
public const string IntegerPropertyName = "integer";
public const string VectorPropertyName = "vector";
protected VectorStoreCollection<object, Dictionary<string, object?>> Collection => fixture.Collection;
public abstract class Fixture : DynamicVectorStoreCollectionFixture<TKey>
{
protected override string CollectionNameBase => nameof(DynamicModelTests<int>);
protected override string KeyPropertyName => DynamicModelTests<TKey>.KeyPropertyName;
protected override VectorStoreCollection<object, Dictionary<string, object?>> GetCollection()
=> this.TestStore.CreateDynamicCollection(this.CollectionName, this.CreateRecordDefinition());
public override VectorStoreCollectionDefinition CreateRecordDefinition()
=> new()
{
Properties =
[
new VectorStoreKeyProperty(this.KeyPropertyName, typeof(TKey)),
new VectorStoreDataProperty(StringPropertyName, typeof(string)) { IsIndexed = true},
new VectorStoreDataProperty(IntegerPropertyName, typeof(int)) { IsIndexed = true },
new VectorStoreVectorProperty(VectorPropertyName, typeof(ReadOnlyMemory<float>), dimensions: 3)
{
DistanceFunction = this.DistanceFunction,
IndexKind = this.IndexKind
}
]
};
protected override List<Dictionary<string, object?>> BuildTestData() =>
[
new()
{
[this.KeyPropertyName] = this.GenerateNextKey<TKey>(),
[StringPropertyName] = "foo",
[IntegerPropertyName] = 1,
[VectorPropertyName] = new ReadOnlyMemory<float>([1, 2, 3])
},
new()
{
[this.KeyPropertyName] = this.GenerateNextKey<TKey>(),
[StringPropertyName] = "bar",
[IntegerPropertyName] = 2,
[VectorPropertyName] = new ReadOnlyMemory<float>([1, 2, 4])
},
new()
{
[this.KeyPropertyName] = this.GenerateNextKey<TKey>(),
[StringPropertyName] = "foo", // identical text as above
[IntegerPropertyName] = 3,
[VectorPropertyName] = new ReadOnlyMemory<float>([1, 2, 5])
}
];
}
public Task InitializeAsync()
=> fixture.ReseedAsync();
public Task DisposeAsync()
=> Task.CompletedTask;
public static readonly TheoryData<bool> IncludeVectorsData = [false, true];
}