Skip to content

Commit cc43169

Browse files
authored
.Net: [MEVD] Test cleanup pack (#13307)
This is an (almost) entirely test-only cleanup (I plan to do a bit more later). * Remove BasicQueryTests, rename BasicFilterTests to FilterTests and move out to the root * BasicFilterTests and BasicQueryTests both run the same filter scenarios, the former via SearchAsync and the latter via filtering GetAsync. Although in theory, some filter query construct could work in one but not in the other, in practice no such example exists in the 12 providers we have (all databases use the same filter language/engine for both APIs. So we had needless duplication for all the various test exceptions/customizations, which had to be overridden in exactly the same way in both test suites. * At least for now, decided to remove BasicQueryTests as it adds maintenance burden without any real coverage; coverage (included missing coverage) specifically for filtered GetAsync was added separately (e.g. we weren't testing ordering, skip/take with filtered get). * If we do want to add back support for all query constructs (I'd want to have a good reason to do so), I'd do that within FilterTests: just like each scenario already exercises both static and dynamic models, it could run GetAsync as well. * Renamed CollectionConformanceTests to CollecitonManagementTests and moved it out to the root. * In general, this PR removes "Conformance" from the test type names. * Big cleanup/refactor of all the so-called CRUD tests. * We now have a ModelTests folder with a test suite for each different model; BasicModelTests covers Get/Upsert/Delete (and their batching variants) for the basic scenario (strongly-typed model with data properties and a single vector property). Alongside that we have DynamicModelTests, NoDataModelTests, NoVectorModelTests (I'll be added MultipleVectorsModelTests). * These tests no longer use random GUIDs for the collection name (difficult to debug, the collection never gets cleaned up if the test crashes/is stopped in the middle), but rather has fixed names per test suite. * The collection is created once per test suite, but cleared and reseeded between each test run, providing full isolation (previously we had no isolation, and documented each record in the seed data as belonging to this scenario or that - which is brittle). * Many other small improvements and cleanup. Part of #12507
1 parent c79c269 commit cc43169

File tree

205 files changed

+2857
-3057
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

205 files changed

+2857
-3057
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,9 @@ FodyWeavers.xsd
415415
*.iml
416416
*.iws
417417

418+
# VS Code
419+
.vscode/
420+
418421
.env
419422
certs/
420423
launchSettings.json

dotnet/src/VectorData/CosmosNoSql/CosmosNoSqlCollection.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,8 @@ await this._database
322322
/// <inheritdoc />
323323
public override async Task<TRecord?> GetAsync(TKey key, RecordRetrievalOptions? options = null, CancellationToken cancellationToken = default)
324324
{
325+
Verify.NotNull(key);
326+
325327
return await this.GetAsync([key], options, cancellationToken)
326328
.FirstOrDefaultAsync(cancellationToken)
327329
.ConfigureAwait(false);
@@ -343,11 +345,17 @@ public override async IAsyncEnumerable<TRecord> GetAsync(
343345
throw new NotSupportedException(VectorDataStrings.IncludeVectorsNotSupportedWithEmbeddingGeneration);
344346
}
345347

348+
var compositeKeys = GetCompositeKeys(keys).ToList();
349+
if (compositeKeys.Count == 0)
350+
{
351+
yield break;
352+
}
353+
346354
var queryDefinition = CosmosNoSqlCollectionQueryBuilder.BuildSelectQuery(
347355
this._model,
348356
this._model.KeyProperty.StorageName,
349357
this._partitionKeyProperty.StorageName,
350-
GetCompositeKeys(keys).ToList(),
358+
compositeKeys,
351359
includeVectors);
352360

353361
await foreach (var jsonObject in this.GetItemsAsync<JsonObject>(queryDefinition, OperationName, cancellationToken).ConfigureAwait(false))

dotnet/src/VectorData/Redis/RedisJsonCollection.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,11 @@ public override async IAsyncEnumerable<TRecord> GetAsync(IEnumerable<TKey> keys,
289289
};
290290
#pragma warning restore CA1851 // Possible multiple enumerations of 'IEnumerable' collection
291291

292+
if (keysList.Count == 0)
293+
{
294+
yield break;
295+
}
296+
292297
// Create Options
293298
var maybePrefixedKeys = keysList.Select(key => this.PrefixKeyIfNeeded(key));
294299
var redisKeys = maybePrefixedKeys.Select(x => new RedisKey(x)).ToArray();
@@ -392,6 +397,11 @@ public override async Task UpsertAsync(IEnumerable<TRecord> records, Cancellatio
392397
redisRecords.Add((maybePrefixedKey, redisJsonRecord.Key, redisJsonRecord.SerializedRecord));
393398
}
394399

400+
if (redisRecords.Count == 0)
401+
{
402+
return;
403+
}
404+
395405
// Upsert.
396406
var keyPathValues = redisRecords.Select(x => new KeyPathValue(x.maybePrefixedKey, "$", x.serializedRecord)).ToArray();
397407
await this.RunOperationAsync(
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
3+
using AzureAISearch.ConformanceTests.Support;
4+
using Microsoft.Extensions.VectorData;
5+
using VectorData.ConformanceTests.Xunit;
6+
using Xunit;
7+
8+
namespace AzureAISearch.ConformanceTests;
9+
10+
public class AzureAISearchAllSupportedTypesTests(AzureAISearchFixture fixture) : IClassFixture<AzureAISearchFixture>
11+
{
12+
[ConditionalFact]
13+
public async Task AllTypesBatchGetAsync()
14+
{
15+
var collection = fixture.TestStore.DefaultVectorStore.GetCollection<string, AzureAISearchAllTypes>("all-types", AzureAISearchAllTypes.GetRecordDefinition());
16+
await collection.EnsureCollectionExistsAsync();
17+
18+
List<AzureAISearchAllTypes> records =
19+
[
20+
new()
21+
{
22+
Id = "all-types-1",
23+
BoolProperty = true,
24+
NullableBoolProperty = false,
25+
StringProperty = "string prop 1",
26+
NullableStringProperty = "nullable prop 1",
27+
IntProperty = 1,
28+
NullableIntProperty = 10,
29+
LongProperty = 100L,
30+
NullableLongProperty = 1000L,
31+
FloatProperty = 10.5f,
32+
NullableFloatProperty = 100.5f,
33+
DoubleProperty = 23.75d,
34+
NullableDoubleProperty = 233.75d,
35+
DateTimeOffsetProperty = DateTimeOffset.UtcNow,
36+
NullableDateTimeOffsetProperty = DateTimeOffset.UtcNow,
37+
StringArray = ["one", "two"],
38+
StringList = ["eleven", "twelve"],
39+
BoolArray = [true, false],
40+
BoolList = [true, false],
41+
IntArray = [1, 2],
42+
IntList = [11, 12],
43+
LongArray = [100L, 200L],
44+
LongList = [1100L, 1200L],
45+
FloatArray = [1.5f, 2.5f],
46+
FloatList = [11.5f, 12.5f],
47+
DoubleArray = [1.5d, 2.5d],
48+
DoubleList = [11.5d, 12.5d],
49+
DateTimeOffsetArray = [DateTimeOffset.UtcNow, DateTimeOffset.UtcNow],
50+
DateTimeOffsetList = [DateTimeOffset.UtcNow, DateTimeOffset.UtcNow],
51+
Embedding = new ReadOnlyMemory<float>([1.5f, 2.5f, 3.5f, 4.5f, 5.5f, 6.5f, 7.5f, 8.5f])
52+
},
53+
new()
54+
{
55+
Id = "all-types-2",
56+
BoolProperty = false,
57+
NullableBoolProperty = null,
58+
StringProperty = "string prop 2",
59+
NullableStringProperty = null,
60+
IntProperty = 2,
61+
NullableIntProperty = null,
62+
LongProperty = 200L,
63+
NullableLongProperty = null,
64+
FloatProperty = 20.5f,
65+
NullableFloatProperty = null,
66+
DoubleProperty = 43.75,
67+
NullableDoubleProperty = null,
68+
Embedding = ReadOnlyMemory<float>.Empty,
69+
// From https://learn.microsoft.com/en-us/rest/api/searchservice/supported-data-types:
70+
// "All of the above types are nullable, except for collections of primitive and complex types, for example, Collection(Edm.String)"
71+
// So for collections, we can't use nulls.
72+
StringArray = [],
73+
StringList = [],
74+
BoolArray = [],
75+
BoolList = [],
76+
IntArray = [],
77+
IntList = [],
78+
LongArray = [],
79+
LongList = [],
80+
FloatArray = [],
81+
FloatList = [],
82+
DoubleArray = [],
83+
DoubleList = [],
84+
DateTimeOffsetArray = [],
85+
DateTimeOffsetList = [],
86+
}
87+
];
88+
89+
try
90+
{
91+
await collection.UpsertAsync(records);
92+
93+
var allTypes = await collection.GetAsync(records.Select(r => r.Id), new RecordRetrievalOptions { IncludeVectors = true }).ToListAsync();
94+
95+
var allTypes1 = allTypes.Single(x => x.Id == records[0].Id);
96+
var allTypes2 = allTypes.Single(x => x.Id == records[1].Id);
97+
98+
records[0].AssertEqual(allTypes1);
99+
records[1].AssertEqual(allTypes2);
100+
}
101+
finally
102+
{
103+
await collection.EnsureCollectionDeletedAsync();
104+
}
105+
}
106+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
3+
using AzureAISearch.ConformanceTests.Support;
4+
using VectorData.ConformanceTests;
5+
using Xunit;
6+
7+
namespace AzureAISearch.ConformanceTests;
8+
9+
public class AzureAISearchCollectionManagementTests(AzureAISearchFixture fixture)
10+
: CollectionManagementTests<string>(fixture), IClassFixture<AzureAISearchFixture>
11+
{
12+
// Azure AI search only supports lowercase letters, digits or dashes.
13+
public override string CollectionName => "collection-tests";
14+
}
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
// Copyright (c) Microsoft. All rights reserved.
22

33
using AzureAISearch.ConformanceTests.Support;
4-
using VectorData.ConformanceTests.Filter;
4+
using VectorData.ConformanceTests;
55
using VectorData.ConformanceTests.Support;
66
using Xunit;
77

8-
namespace AzureAISearch.ConformanceTests.Filter;
8+
namespace AzureAISearch.ConformanceTests;
99

10-
public class AzureAISearchBasicFilterTests(AzureAISearchBasicFilterTests.Fixture fixture)
11-
: BasicFilterTests<string>(fixture), IClassFixture<AzureAISearchBasicFilterTests.Fixture>
10+
public class AzureAISearchFilterTests(AzureAISearchFilterTests.Fixture fixture)
11+
: FilterTests<string>(fixture), IClassFixture<AzureAISearchFilterTests.Fixture>
1212
{
1313
// Azure AI Search only supports search.in() over strings
1414
public override Task Contains_over_inline_int_array()
1515
=> Assert.ThrowsAsync<NotSupportedException>(() => base.Contains_over_inline_int_array());
1616

17-
public new class Fixture : BasicFilterTests<string>.Fixture
17+
public new class Fixture : FilterTests<string>.Fixture
1818
{
1919
public override TestStore TestStore => AzureAISearchTestStore.Instance;
2020

dotnet/test/VectorData/AzureAISearch.ConformanceTests/CRUD/AzureAISearchBatchConformanceTests.cs

Lines changed: 0 additions & 12 deletions
This file was deleted.

dotnet/test/VectorData/AzureAISearch.ConformanceTests/CRUD/AzureAISearchDynamicDataModelConformanceTests.cs

Lines changed: 0 additions & 12 deletions
This file was deleted.

dotnet/test/VectorData/AzureAISearch.ConformanceTests/CRUD/AzureAISearchNoDataConformanceTests.cs

Lines changed: 0 additions & 19 deletions
This file was deleted.

dotnet/test/VectorData/AzureAISearch.ConformanceTests/CRUD/AzureAISearchNoVectorConformanceTests.cs

Lines changed: 0 additions & 19 deletions
This file was deleted.

0 commit comments

Comments
 (0)