From 2170c299990f4aea5b8e97515e6c2e2fe33ed885 Mon Sep 17 00:00:00 2001 From: Igor Malinovskiy Date: Tue, 30 Sep 2025 16:22:40 +0200 Subject: [PATCH 01/18] Assert on index size before running queries in tests --- tests/NRedisStack.Tests/Search/SearchTests.cs | 156 +++++++++--------- 1 file changed, 80 insertions(+), 76 deletions(-) diff --git a/tests/NRedisStack.Tests/Search/SearchTests.cs b/tests/NRedisStack.Tests/Search/SearchTests.cs index 5d3f7728..4f6c442a 100644 --- a/tests/NRedisStack.Tests/Search/SearchTests.cs +++ b/tests/NRedisStack.Tests/Search/SearchTests.cs @@ -62,6 +62,34 @@ private async Task AssertDatabaseSizeAsync(IDatabase db, int expected) { Assert.Equal(expected, await DatabaseSizeAsync(db)); } + + private void AssertIndexSize(ISearchCommands ft, string index, int expected) + { + long indexed = -1; + // allow search time to catch up + for (int i = 0; i < 10; i++) + { + indexed = ft.Info(index).NumDocs; + + if (indexed == expected) + break; + } + Assert.Equal(expected, indexed); + } + + private async Task AssertIndexSizeAsync(ISearchCommandsAsync ft, string index, int expected) + { + long indexed = -1; + // allow search time to catch up + for (int i = 0; i < 10; i++) + { + indexed = (await ft.InfoAsync(index)).NumDocs; + + if (indexed == expected) + break; + } + Assert.Equal(expected, indexed); + } [SkipIfRedisTheory(Is.Enterprise)] [MemberData(nameof(EndpointsFixture.Env.AllEnvironments), MemberType = typeof(EndpointsFixture.Env))] @@ -249,7 +277,7 @@ public void TestAggregationsLoad(string endpointId) ft.Create("idx", new(), sc); AddDocument(db, new Document("doc1").Set("t1", "hello").Set("t2", "world")); - AssertDatabaseSize(db, 1); + AssertIndexSize(ft, "idx", 1); // load t1 var req = new AggregationRequest("*").Load(new FieldName("t1")); @@ -283,7 +311,7 @@ public async Task TestAggregationsLoadAsync(string endpointId) await ft.CreateAsync("idx", new(), sc); AddDocument(db, new Document("doc1").Set("t1", "hello").Set("t2", "world")); - await AssertDatabaseSizeAsync(db, 1); + await AssertIndexSizeAsync(ft, "idx", 1); // load t1 var req = new AggregationRequest("*").Load(new FieldName("t1")); @@ -533,52 +561,37 @@ public void TestApplyAndFilterAggregations(string endpointId) sc.AddNumericField("subj1", sortable: true); sc.AddNumericField("subj2", sortable: true); ft.Create(index, FTCreateParams.CreateParams(), sc); - // client.AddDocument(db, new Document("data1").Set("name", "abc").Set("subj1", 20).Set("subj2", 70)); - // client.AddDocument(db, new Document("data2").Set("name", "def").Set("subj1", 60).Set("subj2", 40)); - // client.AddDocument(db, new Document("data3").Set("name", "ghi").Set("subj1", 50).Set("subj2", 80)); - // client.AddDocument(db, new Document("data4").Set("name", "abc").Set("subj1", 30).Set("subj2", 20)); - // client.AddDocument(db, new Document("data5").Set("name", "def").Set("subj1", 65).Set("subj2", 45)); - // client.AddDocument(db, new Document("data6").Set("name", "ghi").Set("subj1", 70).Set("subj2", 70)); AddDocument(db, new Document("data1").Set("name", "abc").Set("subj1", 20).Set("subj2", 70)); AddDocument(db, new Document("data2").Set("name", "def").Set("subj1", 60).Set("subj2", 40)); AddDocument(db, new Document("data3").Set("name", "ghi").Set("subj1", 50).Set("subj2", 80)); AddDocument(db, new Document("data4").Set("name", "abc").Set("subj1", 30).Set("subj2", 20)); AddDocument(db, new Document("data5").Set("name", "def").Set("subj1", 65).Set("subj2", 45)); AddDocument(db, new Document("data6").Set("name", "ghi").Set("subj1", 70).Set("subj2", 70)); - AssertDatabaseSize(db, 6); + AssertIndexSize(ft, index, 6); + + AggregationRequest r = new AggregationRequest().Apply("(@subj1+@subj2)/2", "attemptavg") + .GroupBy("@name", Reducers.Avg("@attemptavg").As("avgscore")) + .Filter("@avgscore>=50") + .SortBy(10, SortedField.Asc("@name")); - int maxAttempts = endpointId == EndpointsFixture.Env.Cluster ? 10 : 3; - for (int attempt = 1; attempt <= maxAttempts; attempt++) - { - AggregationRequest r = new AggregationRequest().Apply("(@subj1+@subj2)/2", "attemptavg") - .GroupBy("@name", Reducers.Avg("@attemptavg").As("avgscore")) - .Filter("@avgscore>=50") - .SortBy(10, SortedField.Asc("@name")); + // abc: 20+70 => 45, 30+20 => 25, filtered out + // def: 60+40 => 50, 65+45 => 55, avg 52.5 + // ghi: 50+80 => 65, 70+70 => 70, avg 67.5 - // abc: 20+70 => 45, 30+20 => 25, filtered out - // def: 60+40 => 50, 65+45 => 55, avg 52.5 - // ghi: 50+80 => 65, 70+70 => 70, avg 67.5 + // actual search + AggregationResult res = ft.Aggregate(index, r); + Assert.Equal(2, res.TotalResults); - // actual search - AggregationResult res = ft.Aggregate(index, r); - Assert.Equal(2, res.TotalResults); + Row r1 = res.GetRow(0); + Row r2 = res.GetRow(1); - Row r1 = res.GetRow(0); - Row r2 = res.GetRow(1); - Log($"Attempt {attempt} of {maxAttempts}: avgscore {r2.GetDouble("avgscore")}"); - if (attempt != maxAttempts && !IsNear(r2.GetDouble("avgscore"), 67.5)) - { - Thread.Sleep(400); // allow extra cluster replication time - continue; - } + Assert.True(IsNear(r2.GetDouble("avgscore"), 67.5)); - Assert.Equal("def", r1.GetString("name")); - Assert.Equal(52.5, r1.GetDouble("avgscore"), 0); + Assert.Equal("def", r1.GetString("name")); + Assert.Equal(52.5, r1.GetDouble("avgscore"), 0); - Assert.Equal("ghi", r2.GetString("name")); - Assert.Equal(67.5, r2.GetDouble("avgscore"), 0); - break; // success! - } + Assert.Equal("ghi", r2.GetString("name")); + Assert.Equal(67.5, r2.GetDouble("avgscore"), 0); } private static bool IsNear(double a, double b, double epsilon = 0.1) => Math.Abs(a - b) < epsilon; @@ -602,7 +615,7 @@ public void TestCreate(string endpointId) db.HashSet("pupil:4444", [new("first", "Pat"), new("last", "Shu"), new("age", "21")]); db.HashSet("student:5555", [new("first", "Joen"), new("last", "Ko"), new("age", "20")]); db.HashSet("teacher:6666", [new("first", "Pat"), new("last", "Rod"), new("age", "20")]); - AssertDatabaseSize(db, 7); + AssertIndexSize(ft, index, 5); // only pupil and student keys are indexed var noFilters = ft.Search(index, new()); Assert.Equal(4, noFilters.TotalResults); @@ -634,7 +647,7 @@ public async Task TestCreateAsync(string endpointId) db.HashSet("pupil:4444", [new("first", "Pat"), new("last", "Shu"), new("age", "21")]); db.HashSet("student:5555", [new("first", "Joen"), new("last", "Ko"), new("age", "20")]); db.HashSet("teacher:6666", [new("first", "Pat"), new("last", "Rod"), new("age", "20")]); - await AssertDatabaseSizeAsync(db, 7); + await AssertIndexSizeAsync(ft, index, 5); // only pupil and student keys are indexed var noFilters = ft.Search(index, new()); Assert.Equal(4, noFilters.TotalResults); @@ -661,7 +674,7 @@ public void CreateNoParams(string endpointId) db.HashSet("student:3333", [new("first", "El"), new("last", "Mark"), new("age", 17)]); db.HashSet("pupil:4444", [new("first", "Pat"), new("last", "Shu"), new("age", 21)]); db.HashSet("student:5555", [new("first", "Joen"), new("last", "Ko"), new("age", 20)]); - AssertDatabaseSize(db, 4); + AssertIndexSize(ft, index, 4); SearchResult noFilters = ft.Search(index, new()); Assert.Equal(4, noFilters.TotalResults); @@ -691,7 +704,7 @@ public async Task CreateNoParamsAsync(string endpointId) db.HashSet("student:3333", [new("first", "El"), new("last", "Mark"), new("age", 17)]); db.HashSet("pupil:4444", [new("first", "Pat"), new("last", "Shu"), new("age", 21)]); db.HashSet("student:5555", [new("first", "Joen"), new("last", "Ko"), new("age", 20)]); - await AssertDatabaseSizeAsync(db, 4); + await AssertIndexSizeAsync(ft, index, 4); SearchResult noFilters = ft.Search(index, new()); Assert.Equal(4, noFilters.TotalResults); @@ -725,7 +738,8 @@ public void CreateWithFieldNames(string endpointId) db.HashSet("pupil:4444", [new("first", "Pat"), new("last", "Shu"), new("age", "21")]); db.HashSet("student:5555", [new("first", "Joen"), new("last", "Ko"), new("age", "20")]); db.HashSet("teacher:6666", [new("first", "Pat"), new("last", "Rod"), new("age", "20")]); - AssertDatabaseSize(db, 7); + + AssertIndexSize(ft, index, 5); // only pupil and student keys are indexed SearchResult noFilters = ft.Search(index, new()); Assert.Equal(5, noFilters.TotalResults); @@ -1288,7 +1302,7 @@ public async Task TestCursor(string endpointId) AddDocument(db, new Document("data1").Set("name", "abc").Set("count", 10)); AddDocument(db, new Document("data2").Set("name", "def").Set("count", 5)); AddDocument(db, new Document("data3").Set("name", "def").Set("count", 25)); - AssertDatabaseSize(db, 3); + AssertIndexSize(ft, index, 3); AggregationRequest r = new AggregationRequest() .GroupBy("@name", Reducers.Sum("@count").As("sum")) @@ -1344,7 +1358,7 @@ public void TestCursorEnumerable(string endpointId) AddDocument(db, new Document("data1").Set("name", "abc").Set("count", 10)); AddDocument(db, new Document("data2").Set("name", "def").Set("count", 5)); AddDocument(db, new Document("data3").Set("name", "def").Set("count", 25)); - AssertDatabaseSize(db, 3); + AssertIndexSize(ft, index, 3); AggregationRequest r = new AggregationRequest() .GroupBy("@name", Reducers.Sum("@count").As("sum")) @@ -1383,7 +1397,7 @@ public async Task TestCursorAsync(string endpointId) AddDocument(db, new Document("data1").Set("name", "abc").Set("count", 10)); AddDocument(db, new Document("data2").Set("name", "def").Set("count", 5)); AddDocument(db, new Document("data3").Set("name", "def").Set("count", 25)); - await AssertDatabaseSizeAsync(db, 3); + await AssertIndexSizeAsync(ft, index, 3); AggregationRequest r = new AggregationRequest() .GroupBy("@name", Reducers.Sum("@count").As("sum")) @@ -1439,7 +1453,7 @@ public async Task TestCursorEnumerableAsync(string endpointId) AddDocument(db, new Document("data1").Set("name", "abc").Set("count", 10)); AddDocument(db, new Document("data2").Set("name", "def").Set("count", 5)); AddDocument(db, new Document("data3").Set("name", "def").Set("count", 25)); - await AssertDatabaseSizeAsync(db, 3); + await AssertIndexSizeAsync(ft, index, 3); AggregationRequest r = new AggregationRequest() .GroupBy("@name", Reducers.Sum("@count").As("sum")) @@ -1637,17 +1651,11 @@ public void TestDropIndex(string endpointId) Assert.True(ft.DropIndex(index)); - try - { - ft.Search(index, new("hello world")); - //fail("Index should not exist."); - } - catch (RedisServerException ex) - { - Assert.Contains("no such index", ex.Message, StringComparison.OrdinalIgnoreCase); - } - - AssertDatabaseSize(db, 100); + var ex = Record.Exception(() => { ft.Search(index, new("hello world")); }); + + Assert.NotNull(ex); + Assert.IsType(ex); + Assert.Contains("no such index", ex.Message, StringComparison.OrdinalIgnoreCase); } private int DatabaseSize(IDatabase db) => DatabaseSize(db, out _); @@ -1707,17 +1715,11 @@ public async Task TestDropIndexAsync(string endpointId) Assert.True(await ft.DropIndexAsync(index)); - try - { - ft.Search(index, new("hello world")); - //fail("Index should not exist."); - } - catch (RedisServerException ex) - { - Assert.Contains("no such index", ex.Message, StringComparison.OrdinalIgnoreCase); - } - - AssertDatabaseSize(db, 100); + var ex = Record.Exception(() => { ft.Search(index, new("hello world")); }); + + Assert.NotNull(ex); + Assert.IsType(ex); + Assert.Contains("no such index", ex.Message, StringComparison.OrdinalIgnoreCase); } [SkippableTheory] @@ -1736,6 +1738,7 @@ public void dropIndexDD(string endpointId) { AddDocument(db, $"doc{i}", fields); } + AssertIndexSize(ft, index, 100); SearchResult res = ft.Search(index, new("hello world")); Assert.Equal(100, res.TotalResults); @@ -1763,6 +1766,7 @@ public async Task dropIndexDDAsync(string endpointId) { AddDocument(db, $"doc{i}", fields); } + AssertIndexSize(ft, index, 100); SearchResult res = ft.Search(index, new("hello world")); Assert.Equal(100, res.TotalResults); @@ -2523,7 +2527,7 @@ public void TestLimit(string endpointId) Document doc2 = new("doc2", new() { { "t1", "b" }, { "t2", "a" } }); AddDocument(db, doc1); AddDocument(db, doc2); - AssertDatabaseSize(db, 2); + AssertIndexSize(ft, "idx", 2); var req = new AggregationRequest("*").SortBy("@t1").Limit(1); var res = ft.Aggregate("idx", req); @@ -2545,7 +2549,7 @@ public async Task TestLimitAsync(string endpointId) Document doc2 = new("doc2", new() { { "t1", "b" }, { "t2", "a" } }); AddDocument(db, doc1); AddDocument(db, doc2); - await AssertDatabaseSizeAsync(db, 2); + await AssertIndexSizeAsync(ft, "idx", 2); var req = new AggregationRequest("*").SortBy("@t1").Limit(1, 1); var res = await ft.AggregateAsync("idx", req); @@ -2633,7 +2637,7 @@ public void VectorSimilaritySearch(string endpointId) float[] vec = [2, 2, 2, 2]; byte[] queryVec = MemoryMarshal.Cast(vec).ToArray(); - AssertDatabaseSize(db, 4); + AssertIndexSize(ft, "vss_idx", 4); var query = new Query("*=>[KNN 3 @vector $query_vec]") .AddParam("query_vec", queryVec) .SetSortBy("__vector_score") @@ -2672,7 +2676,7 @@ public void QueryingVectorFields(string endpointId) db.HashSet("b", "v", "aaaabaaa"); db.HashSet("c", "v", "aaaaabaa"); - AssertDatabaseSize(db, 3); + AssertIndexSize(ft, "idx", 3); var q = new Query("*=>[KNN 2 @v $vec]").ReturnFields("__v_score").Dialect(2); var res = ft.Search("idx", q.AddParam("vec", "aaaaaaaa")); Assert.Equal(2, res.TotalResults); @@ -2714,7 +2718,7 @@ public void TestQueryAddParam_DefaultDialect(string endpointId) db.HashSet("2", "numval", 2); db.HashSet("3", "numval", 3); - AssertDatabaseSize(db, 3); + AssertIndexSize(ft, "idx", 3); Query query = new Query("@numval:[$min $max]").AddParam("min", 1).AddParam("max", 2); var res = ft.Search("idx", query); Assert.Equal(2, res.TotalResults); @@ -2735,7 +2739,7 @@ public async Task TestQueryAddParam_DefaultDialectAsync(string endpointId) db.HashSet("2", "numval", 2); db.HashSet("3", "numval", 3); - await AssertDatabaseSizeAsync(db, 3); + await AssertIndexSizeAsync(ft, "idx", 3); Query query = new Query("@numval:[$min $max]").AddParam("min", 1).AddParam("max", 2); var res = await ft.SearchAsync("idx", query); Assert.Equal(2, res.TotalResults); @@ -2756,7 +2760,7 @@ public void TestQueryParamsWithParams_DefaultDialect(string endpointId) db.HashSet("2", "numval", 2); db.HashSet("3", "numval", 3); - AssertDatabaseSize(db, 3); + AssertIndexSize(ft, "idx", 3); Query query = new Query("@numval:[$min $max]").AddParam("min", 1).AddParam("max", 2); var res = ft.Search("idx", query); Assert.Equal(2, res.TotalResults); @@ -2807,7 +2811,7 @@ public async Task TestBasicSpellCheckAsync(string endpointId) db.HashSet("doc1", [new("name", "name2"), new("body", "body2")]); db.HashSet("doc1", [new("name", "name2"), new("body", "name2")]); - await AssertDatabaseSizeAsync(db, 1); + await AssertIndexSizeAsync(ft, index, 1); var reply = await ft.SpellCheckAsync(index, "name"); Assert.Single(reply.Keys); Assert.Equal("name", reply.Keys.First()); @@ -2929,7 +2933,7 @@ public async Task TestQueryParamsWithParams_DefaultDialectAsync(string endpointI db.HashSet("2", "numval", 2); db.HashSet("3", "numval", 3); - AssertDatabaseSize(db, 3); + AssertIndexSize(ft, "idx", 3); Query query = new Query("@numval:[$min $max]").AddParam("min", 1).AddParam("max", 2); var res = await ft.SearchAsync("idx", query); Assert.Equal(2, res.TotalResults); From f1182f6a6217aac014b7191f77a7703e605a06a8 Mon Sep 17 00:00:00 2001 From: Igor Malinovskiy Date: Tue, 30 Sep 2025 16:23:49 +0200 Subject: [PATCH 02/18] Remove assertions on config options unsupported by RE --- tests/NRedisStack.Tests/CommunityEditionUpdatesTests.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/NRedisStack.Tests/CommunityEditionUpdatesTests.cs b/tests/NRedisStack.Tests/CommunityEditionUpdatesTests.cs index 672bbf9f..c4630c22 100644 --- a/tests/NRedisStack.Tests/CommunityEditionUpdatesTests.cs +++ b/tests/NRedisStack.Tests/CommunityEditionUpdatesTests.cs @@ -35,14 +35,10 @@ public void ConfigSearchSettings(string endpointId) Assert.Single(server.ConfigGet("search-max-prefix-expansions")); - Assert.Single(server.ConfigGet("search-max-doctablesize")); - Assert.Single(server.ConfigGet("search-max-search-results")); Assert.Single(server.ConfigGet("search-max-aggregate-results")); - Assert.Single(server.ConfigGet("search-friso-ini")); - Assert.Single(server.ConfigGet("search-default-dialect")); } From 5edc928ee32e5a92651409e07e10c34d80d09b8b Mon Sep 17 00:00:00 2001 From: Igor Malinovskiy Date: Tue, 30 Sep 2025 16:31:15 +0200 Subject: [PATCH 03/18] Fix formatting --- tests/NRedisStack.Tests/Search/SearchTests.cs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/NRedisStack.Tests/Search/SearchTests.cs b/tests/NRedisStack.Tests/Search/SearchTests.cs index 4f6c442a..dc4c5501 100644 --- a/tests/NRedisStack.Tests/Search/SearchTests.cs +++ b/tests/NRedisStack.Tests/Search/SearchTests.cs @@ -62,29 +62,29 @@ private async Task AssertDatabaseSizeAsync(IDatabase db, int expected) { Assert.Equal(expected, await DatabaseSizeAsync(db)); } - + private void AssertIndexSize(ISearchCommands ft, string index, int expected) { - long indexed = -1; + long indexed = -1; // allow search time to catch up for (int i = 0; i < 10; i++) { indexed = ft.Info(index).NumDocs; - + if (indexed == expected) break; } Assert.Equal(expected, indexed); } - + private async Task AssertIndexSizeAsync(ISearchCommandsAsync ft, string index, int expected) { - long indexed = -1; + long indexed = -1; // allow search time to catch up for (int i = 0; i < 10; i++) { indexed = (await ft.InfoAsync(index)).NumDocs; - + if (indexed == expected) break; } @@ -568,7 +568,7 @@ public void TestApplyAndFilterAggregations(string endpointId) AddDocument(db, new Document("data5").Set("name", "def").Set("subj1", 65).Set("subj2", 45)); AddDocument(db, new Document("data6").Set("name", "ghi").Set("subj1", 70).Set("subj2", 70)); AssertIndexSize(ft, index, 6); - + AggregationRequest r = new AggregationRequest().Apply("(@subj1+@subj2)/2", "attemptavg") .GroupBy("@name", Reducers.Avg("@attemptavg").As("avgscore")) .Filter("@avgscore>=50") @@ -738,7 +738,7 @@ public void CreateWithFieldNames(string endpointId) db.HashSet("pupil:4444", [new("first", "Pat"), new("last", "Shu"), new("age", "21")]); db.HashSet("student:5555", [new("first", "Joen"), new("last", "Ko"), new("age", "20")]); db.HashSet("teacher:6666", [new("first", "Pat"), new("last", "Rod"), new("age", "20")]); - + AssertIndexSize(ft, index, 5); // only pupil and student keys are indexed SearchResult noFilters = ft.Search(index, new()); @@ -1652,7 +1652,7 @@ public void TestDropIndex(string endpointId) Assert.True(ft.DropIndex(index)); var ex = Record.Exception(() => { ft.Search(index, new("hello world")); }); - + Assert.NotNull(ex); Assert.IsType(ex); Assert.Contains("no such index", ex.Message, StringComparison.OrdinalIgnoreCase); @@ -1716,7 +1716,7 @@ public async Task TestDropIndexAsync(string endpointId) Assert.True(await ft.DropIndexAsync(index)); var ex = Record.Exception(() => { ft.Search(index, new("hello world")); }); - + Assert.NotNull(ex); Assert.IsType(ex); Assert.Contains("no such index", ex.Message, StringComparison.OrdinalIgnoreCase); From c7151933595dd92f09c0770c29fccccfbb93ec1e Mon Sep 17 00:00:00 2001 From: Igor Malinovskiy Date: Tue, 30 Sep 2025 16:51:56 +0200 Subject: [PATCH 04/18] Fix index size assertion --- tests/NRedisStack.Tests/Search/SearchTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/NRedisStack.Tests/Search/SearchTests.cs b/tests/NRedisStack.Tests/Search/SearchTests.cs index dc4c5501..0b605e8f 100644 --- a/tests/NRedisStack.Tests/Search/SearchTests.cs +++ b/tests/NRedisStack.Tests/Search/SearchTests.cs @@ -615,7 +615,7 @@ public void TestCreate(string endpointId) db.HashSet("pupil:4444", [new("first", "Pat"), new("last", "Shu"), new("age", "21")]); db.HashSet("student:5555", [new("first", "Joen"), new("last", "Ko"), new("age", "20")]); db.HashSet("teacher:6666", [new("first", "Pat"), new("last", "Rod"), new("age", "20")]); - AssertIndexSize(ft, index, 5); // only pupil and student keys are indexed + AssertIndexSize(ft, index, 4); // only pupil and student keys are indexed var noFilters = ft.Search(index, new()); Assert.Equal(4, noFilters.TotalResults); @@ -647,7 +647,7 @@ public async Task TestCreateAsync(string endpointId) db.HashSet("pupil:4444", [new("first", "Pat"), new("last", "Shu"), new("age", "21")]); db.HashSet("student:5555", [new("first", "Joen"), new("last", "Ko"), new("age", "20")]); db.HashSet("teacher:6666", [new("first", "Pat"), new("last", "Rod"), new("age", "20")]); - await AssertIndexSizeAsync(ft, index, 5); // only pupil and student keys are indexed + await AssertIndexSizeAsync(ft, index, 4); // only pupil and student keys are indexed var noFilters = ft.Search(index, new()); Assert.Equal(4, noFilters.TotalResults); From aa08000f2053301f8c695ca2f32d6f5e43c57254 Mon Sep 17 00:00:00 2001 From: Marc Gravell Date: Tue, 18 Nov 2025 09:34:48 +0000 Subject: [PATCH 05/18] Add 8.4 to CI matrix integration.yml (#450) * Add 8.4 to CI matrix integration.yml * Update integration.yml * Update integration.yml * Update integration.yml --- .github/workflows/integration.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index 1f860259..ee0f7d32 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -37,7 +37,7 @@ jobs: max-parallel: 15 fail-fast: false matrix: - redis-version: [ '8.2.1', '${{ needs.redis_version.outputs.CURRENT }}', '7.4.1', '7.2.6', '6.2.16'] + redis-version: [ '8.4-GA-pre.3', '8.2.1', '${{ needs.redis_version.outputs.CURRENT }}', '7.4.1', '7.2.6', '6.2.16'] dotnet-version: ['8.0', '9.0'] env: ACTIONS_ALLOW_UNSECURE_COMMANDS: true From a3440586b1a96365fb497b3b4d2d92fd358f10d3 Mon Sep 17 00:00:00 2001 From: Igor Malinovskiy Date: Tue, 30 Sep 2025 16:22:40 +0200 Subject: [PATCH 06/18] Assert on index size before running queries in tests --- tests/NRedisStack.Tests/Search/SearchTests.cs | 156 +++++++++--------- 1 file changed, 80 insertions(+), 76 deletions(-) diff --git a/tests/NRedisStack.Tests/Search/SearchTests.cs b/tests/NRedisStack.Tests/Search/SearchTests.cs index 5d3f7728..4f6c442a 100644 --- a/tests/NRedisStack.Tests/Search/SearchTests.cs +++ b/tests/NRedisStack.Tests/Search/SearchTests.cs @@ -62,6 +62,34 @@ private async Task AssertDatabaseSizeAsync(IDatabase db, int expected) { Assert.Equal(expected, await DatabaseSizeAsync(db)); } + + private void AssertIndexSize(ISearchCommands ft, string index, int expected) + { + long indexed = -1; + // allow search time to catch up + for (int i = 0; i < 10; i++) + { + indexed = ft.Info(index).NumDocs; + + if (indexed == expected) + break; + } + Assert.Equal(expected, indexed); + } + + private async Task AssertIndexSizeAsync(ISearchCommandsAsync ft, string index, int expected) + { + long indexed = -1; + // allow search time to catch up + for (int i = 0; i < 10; i++) + { + indexed = (await ft.InfoAsync(index)).NumDocs; + + if (indexed == expected) + break; + } + Assert.Equal(expected, indexed); + } [SkipIfRedisTheory(Is.Enterprise)] [MemberData(nameof(EndpointsFixture.Env.AllEnvironments), MemberType = typeof(EndpointsFixture.Env))] @@ -249,7 +277,7 @@ public void TestAggregationsLoad(string endpointId) ft.Create("idx", new(), sc); AddDocument(db, new Document("doc1").Set("t1", "hello").Set("t2", "world")); - AssertDatabaseSize(db, 1); + AssertIndexSize(ft, "idx", 1); // load t1 var req = new AggregationRequest("*").Load(new FieldName("t1")); @@ -283,7 +311,7 @@ public async Task TestAggregationsLoadAsync(string endpointId) await ft.CreateAsync("idx", new(), sc); AddDocument(db, new Document("doc1").Set("t1", "hello").Set("t2", "world")); - await AssertDatabaseSizeAsync(db, 1); + await AssertIndexSizeAsync(ft, "idx", 1); // load t1 var req = new AggregationRequest("*").Load(new FieldName("t1")); @@ -533,52 +561,37 @@ public void TestApplyAndFilterAggregations(string endpointId) sc.AddNumericField("subj1", sortable: true); sc.AddNumericField("subj2", sortable: true); ft.Create(index, FTCreateParams.CreateParams(), sc); - // client.AddDocument(db, new Document("data1").Set("name", "abc").Set("subj1", 20).Set("subj2", 70)); - // client.AddDocument(db, new Document("data2").Set("name", "def").Set("subj1", 60).Set("subj2", 40)); - // client.AddDocument(db, new Document("data3").Set("name", "ghi").Set("subj1", 50).Set("subj2", 80)); - // client.AddDocument(db, new Document("data4").Set("name", "abc").Set("subj1", 30).Set("subj2", 20)); - // client.AddDocument(db, new Document("data5").Set("name", "def").Set("subj1", 65).Set("subj2", 45)); - // client.AddDocument(db, new Document("data6").Set("name", "ghi").Set("subj1", 70).Set("subj2", 70)); AddDocument(db, new Document("data1").Set("name", "abc").Set("subj1", 20).Set("subj2", 70)); AddDocument(db, new Document("data2").Set("name", "def").Set("subj1", 60).Set("subj2", 40)); AddDocument(db, new Document("data3").Set("name", "ghi").Set("subj1", 50).Set("subj2", 80)); AddDocument(db, new Document("data4").Set("name", "abc").Set("subj1", 30).Set("subj2", 20)); AddDocument(db, new Document("data5").Set("name", "def").Set("subj1", 65).Set("subj2", 45)); AddDocument(db, new Document("data6").Set("name", "ghi").Set("subj1", 70).Set("subj2", 70)); - AssertDatabaseSize(db, 6); + AssertIndexSize(ft, index, 6); + + AggregationRequest r = new AggregationRequest().Apply("(@subj1+@subj2)/2", "attemptavg") + .GroupBy("@name", Reducers.Avg("@attemptavg").As("avgscore")) + .Filter("@avgscore>=50") + .SortBy(10, SortedField.Asc("@name")); - int maxAttempts = endpointId == EndpointsFixture.Env.Cluster ? 10 : 3; - for (int attempt = 1; attempt <= maxAttempts; attempt++) - { - AggregationRequest r = new AggregationRequest().Apply("(@subj1+@subj2)/2", "attemptavg") - .GroupBy("@name", Reducers.Avg("@attemptavg").As("avgscore")) - .Filter("@avgscore>=50") - .SortBy(10, SortedField.Asc("@name")); + // abc: 20+70 => 45, 30+20 => 25, filtered out + // def: 60+40 => 50, 65+45 => 55, avg 52.5 + // ghi: 50+80 => 65, 70+70 => 70, avg 67.5 - // abc: 20+70 => 45, 30+20 => 25, filtered out - // def: 60+40 => 50, 65+45 => 55, avg 52.5 - // ghi: 50+80 => 65, 70+70 => 70, avg 67.5 + // actual search + AggregationResult res = ft.Aggregate(index, r); + Assert.Equal(2, res.TotalResults); - // actual search - AggregationResult res = ft.Aggregate(index, r); - Assert.Equal(2, res.TotalResults); + Row r1 = res.GetRow(0); + Row r2 = res.GetRow(1); - Row r1 = res.GetRow(0); - Row r2 = res.GetRow(1); - Log($"Attempt {attempt} of {maxAttempts}: avgscore {r2.GetDouble("avgscore")}"); - if (attempt != maxAttempts && !IsNear(r2.GetDouble("avgscore"), 67.5)) - { - Thread.Sleep(400); // allow extra cluster replication time - continue; - } + Assert.True(IsNear(r2.GetDouble("avgscore"), 67.5)); - Assert.Equal("def", r1.GetString("name")); - Assert.Equal(52.5, r1.GetDouble("avgscore"), 0); + Assert.Equal("def", r1.GetString("name")); + Assert.Equal(52.5, r1.GetDouble("avgscore"), 0); - Assert.Equal("ghi", r2.GetString("name")); - Assert.Equal(67.5, r2.GetDouble("avgscore"), 0); - break; // success! - } + Assert.Equal("ghi", r2.GetString("name")); + Assert.Equal(67.5, r2.GetDouble("avgscore"), 0); } private static bool IsNear(double a, double b, double epsilon = 0.1) => Math.Abs(a - b) < epsilon; @@ -602,7 +615,7 @@ public void TestCreate(string endpointId) db.HashSet("pupil:4444", [new("first", "Pat"), new("last", "Shu"), new("age", "21")]); db.HashSet("student:5555", [new("first", "Joen"), new("last", "Ko"), new("age", "20")]); db.HashSet("teacher:6666", [new("first", "Pat"), new("last", "Rod"), new("age", "20")]); - AssertDatabaseSize(db, 7); + AssertIndexSize(ft, index, 5); // only pupil and student keys are indexed var noFilters = ft.Search(index, new()); Assert.Equal(4, noFilters.TotalResults); @@ -634,7 +647,7 @@ public async Task TestCreateAsync(string endpointId) db.HashSet("pupil:4444", [new("first", "Pat"), new("last", "Shu"), new("age", "21")]); db.HashSet("student:5555", [new("first", "Joen"), new("last", "Ko"), new("age", "20")]); db.HashSet("teacher:6666", [new("first", "Pat"), new("last", "Rod"), new("age", "20")]); - await AssertDatabaseSizeAsync(db, 7); + await AssertIndexSizeAsync(ft, index, 5); // only pupil and student keys are indexed var noFilters = ft.Search(index, new()); Assert.Equal(4, noFilters.TotalResults); @@ -661,7 +674,7 @@ public void CreateNoParams(string endpointId) db.HashSet("student:3333", [new("first", "El"), new("last", "Mark"), new("age", 17)]); db.HashSet("pupil:4444", [new("first", "Pat"), new("last", "Shu"), new("age", 21)]); db.HashSet("student:5555", [new("first", "Joen"), new("last", "Ko"), new("age", 20)]); - AssertDatabaseSize(db, 4); + AssertIndexSize(ft, index, 4); SearchResult noFilters = ft.Search(index, new()); Assert.Equal(4, noFilters.TotalResults); @@ -691,7 +704,7 @@ public async Task CreateNoParamsAsync(string endpointId) db.HashSet("student:3333", [new("first", "El"), new("last", "Mark"), new("age", 17)]); db.HashSet("pupil:4444", [new("first", "Pat"), new("last", "Shu"), new("age", 21)]); db.HashSet("student:5555", [new("first", "Joen"), new("last", "Ko"), new("age", 20)]); - await AssertDatabaseSizeAsync(db, 4); + await AssertIndexSizeAsync(ft, index, 4); SearchResult noFilters = ft.Search(index, new()); Assert.Equal(4, noFilters.TotalResults); @@ -725,7 +738,8 @@ public void CreateWithFieldNames(string endpointId) db.HashSet("pupil:4444", [new("first", "Pat"), new("last", "Shu"), new("age", "21")]); db.HashSet("student:5555", [new("first", "Joen"), new("last", "Ko"), new("age", "20")]); db.HashSet("teacher:6666", [new("first", "Pat"), new("last", "Rod"), new("age", "20")]); - AssertDatabaseSize(db, 7); + + AssertIndexSize(ft, index, 5); // only pupil and student keys are indexed SearchResult noFilters = ft.Search(index, new()); Assert.Equal(5, noFilters.TotalResults); @@ -1288,7 +1302,7 @@ public async Task TestCursor(string endpointId) AddDocument(db, new Document("data1").Set("name", "abc").Set("count", 10)); AddDocument(db, new Document("data2").Set("name", "def").Set("count", 5)); AddDocument(db, new Document("data3").Set("name", "def").Set("count", 25)); - AssertDatabaseSize(db, 3); + AssertIndexSize(ft, index, 3); AggregationRequest r = new AggregationRequest() .GroupBy("@name", Reducers.Sum("@count").As("sum")) @@ -1344,7 +1358,7 @@ public void TestCursorEnumerable(string endpointId) AddDocument(db, new Document("data1").Set("name", "abc").Set("count", 10)); AddDocument(db, new Document("data2").Set("name", "def").Set("count", 5)); AddDocument(db, new Document("data3").Set("name", "def").Set("count", 25)); - AssertDatabaseSize(db, 3); + AssertIndexSize(ft, index, 3); AggregationRequest r = new AggregationRequest() .GroupBy("@name", Reducers.Sum("@count").As("sum")) @@ -1383,7 +1397,7 @@ public async Task TestCursorAsync(string endpointId) AddDocument(db, new Document("data1").Set("name", "abc").Set("count", 10)); AddDocument(db, new Document("data2").Set("name", "def").Set("count", 5)); AddDocument(db, new Document("data3").Set("name", "def").Set("count", 25)); - await AssertDatabaseSizeAsync(db, 3); + await AssertIndexSizeAsync(ft, index, 3); AggregationRequest r = new AggregationRequest() .GroupBy("@name", Reducers.Sum("@count").As("sum")) @@ -1439,7 +1453,7 @@ public async Task TestCursorEnumerableAsync(string endpointId) AddDocument(db, new Document("data1").Set("name", "abc").Set("count", 10)); AddDocument(db, new Document("data2").Set("name", "def").Set("count", 5)); AddDocument(db, new Document("data3").Set("name", "def").Set("count", 25)); - await AssertDatabaseSizeAsync(db, 3); + await AssertIndexSizeAsync(ft, index, 3); AggregationRequest r = new AggregationRequest() .GroupBy("@name", Reducers.Sum("@count").As("sum")) @@ -1637,17 +1651,11 @@ public void TestDropIndex(string endpointId) Assert.True(ft.DropIndex(index)); - try - { - ft.Search(index, new("hello world")); - //fail("Index should not exist."); - } - catch (RedisServerException ex) - { - Assert.Contains("no such index", ex.Message, StringComparison.OrdinalIgnoreCase); - } - - AssertDatabaseSize(db, 100); + var ex = Record.Exception(() => { ft.Search(index, new("hello world")); }); + + Assert.NotNull(ex); + Assert.IsType(ex); + Assert.Contains("no such index", ex.Message, StringComparison.OrdinalIgnoreCase); } private int DatabaseSize(IDatabase db) => DatabaseSize(db, out _); @@ -1707,17 +1715,11 @@ public async Task TestDropIndexAsync(string endpointId) Assert.True(await ft.DropIndexAsync(index)); - try - { - ft.Search(index, new("hello world")); - //fail("Index should not exist."); - } - catch (RedisServerException ex) - { - Assert.Contains("no such index", ex.Message, StringComparison.OrdinalIgnoreCase); - } - - AssertDatabaseSize(db, 100); + var ex = Record.Exception(() => { ft.Search(index, new("hello world")); }); + + Assert.NotNull(ex); + Assert.IsType(ex); + Assert.Contains("no such index", ex.Message, StringComparison.OrdinalIgnoreCase); } [SkippableTheory] @@ -1736,6 +1738,7 @@ public void dropIndexDD(string endpointId) { AddDocument(db, $"doc{i}", fields); } + AssertIndexSize(ft, index, 100); SearchResult res = ft.Search(index, new("hello world")); Assert.Equal(100, res.TotalResults); @@ -1763,6 +1766,7 @@ public async Task dropIndexDDAsync(string endpointId) { AddDocument(db, $"doc{i}", fields); } + AssertIndexSize(ft, index, 100); SearchResult res = ft.Search(index, new("hello world")); Assert.Equal(100, res.TotalResults); @@ -2523,7 +2527,7 @@ public void TestLimit(string endpointId) Document doc2 = new("doc2", new() { { "t1", "b" }, { "t2", "a" } }); AddDocument(db, doc1); AddDocument(db, doc2); - AssertDatabaseSize(db, 2); + AssertIndexSize(ft, "idx", 2); var req = new AggregationRequest("*").SortBy("@t1").Limit(1); var res = ft.Aggregate("idx", req); @@ -2545,7 +2549,7 @@ public async Task TestLimitAsync(string endpointId) Document doc2 = new("doc2", new() { { "t1", "b" }, { "t2", "a" } }); AddDocument(db, doc1); AddDocument(db, doc2); - await AssertDatabaseSizeAsync(db, 2); + await AssertIndexSizeAsync(ft, "idx", 2); var req = new AggregationRequest("*").SortBy("@t1").Limit(1, 1); var res = await ft.AggregateAsync("idx", req); @@ -2633,7 +2637,7 @@ public void VectorSimilaritySearch(string endpointId) float[] vec = [2, 2, 2, 2]; byte[] queryVec = MemoryMarshal.Cast(vec).ToArray(); - AssertDatabaseSize(db, 4); + AssertIndexSize(ft, "vss_idx", 4); var query = new Query("*=>[KNN 3 @vector $query_vec]") .AddParam("query_vec", queryVec) .SetSortBy("__vector_score") @@ -2672,7 +2676,7 @@ public void QueryingVectorFields(string endpointId) db.HashSet("b", "v", "aaaabaaa"); db.HashSet("c", "v", "aaaaabaa"); - AssertDatabaseSize(db, 3); + AssertIndexSize(ft, "idx", 3); var q = new Query("*=>[KNN 2 @v $vec]").ReturnFields("__v_score").Dialect(2); var res = ft.Search("idx", q.AddParam("vec", "aaaaaaaa")); Assert.Equal(2, res.TotalResults); @@ -2714,7 +2718,7 @@ public void TestQueryAddParam_DefaultDialect(string endpointId) db.HashSet("2", "numval", 2); db.HashSet("3", "numval", 3); - AssertDatabaseSize(db, 3); + AssertIndexSize(ft, "idx", 3); Query query = new Query("@numval:[$min $max]").AddParam("min", 1).AddParam("max", 2); var res = ft.Search("idx", query); Assert.Equal(2, res.TotalResults); @@ -2735,7 +2739,7 @@ public async Task TestQueryAddParam_DefaultDialectAsync(string endpointId) db.HashSet("2", "numval", 2); db.HashSet("3", "numval", 3); - await AssertDatabaseSizeAsync(db, 3); + await AssertIndexSizeAsync(ft, "idx", 3); Query query = new Query("@numval:[$min $max]").AddParam("min", 1).AddParam("max", 2); var res = await ft.SearchAsync("idx", query); Assert.Equal(2, res.TotalResults); @@ -2756,7 +2760,7 @@ public void TestQueryParamsWithParams_DefaultDialect(string endpointId) db.HashSet("2", "numval", 2); db.HashSet("3", "numval", 3); - AssertDatabaseSize(db, 3); + AssertIndexSize(ft, "idx", 3); Query query = new Query("@numval:[$min $max]").AddParam("min", 1).AddParam("max", 2); var res = ft.Search("idx", query); Assert.Equal(2, res.TotalResults); @@ -2807,7 +2811,7 @@ public async Task TestBasicSpellCheckAsync(string endpointId) db.HashSet("doc1", [new("name", "name2"), new("body", "body2")]); db.HashSet("doc1", [new("name", "name2"), new("body", "name2")]); - await AssertDatabaseSizeAsync(db, 1); + await AssertIndexSizeAsync(ft, index, 1); var reply = await ft.SpellCheckAsync(index, "name"); Assert.Single(reply.Keys); Assert.Equal("name", reply.Keys.First()); @@ -2929,7 +2933,7 @@ public async Task TestQueryParamsWithParams_DefaultDialectAsync(string endpointI db.HashSet("2", "numval", 2); db.HashSet("3", "numval", 3); - AssertDatabaseSize(db, 3); + AssertIndexSize(ft, "idx", 3); Query query = new Query("@numval:[$min $max]").AddParam("min", 1).AddParam("max", 2); var res = await ft.SearchAsync("idx", query); Assert.Equal(2, res.TotalResults); From b4fa260367d781696e3d537c4253bb9b037727ce Mon Sep 17 00:00:00 2001 From: Igor Malinovskiy Date: Tue, 30 Sep 2025 16:23:49 +0200 Subject: [PATCH 07/18] Remove assertions on config options unsupported by RE --- tests/NRedisStack.Tests/CommunityEditionUpdatesTests.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/NRedisStack.Tests/CommunityEditionUpdatesTests.cs b/tests/NRedisStack.Tests/CommunityEditionUpdatesTests.cs index 672bbf9f..c4630c22 100644 --- a/tests/NRedisStack.Tests/CommunityEditionUpdatesTests.cs +++ b/tests/NRedisStack.Tests/CommunityEditionUpdatesTests.cs @@ -35,14 +35,10 @@ public void ConfigSearchSettings(string endpointId) Assert.Single(server.ConfigGet("search-max-prefix-expansions")); - Assert.Single(server.ConfigGet("search-max-doctablesize")); - Assert.Single(server.ConfigGet("search-max-search-results")); Assert.Single(server.ConfigGet("search-max-aggregate-results")); - Assert.Single(server.ConfigGet("search-friso-ini")); - Assert.Single(server.ConfigGet("search-default-dialect")); } From d1afe07607bbfc4ce93de94b7b00a4ae77f91094 Mon Sep 17 00:00:00 2001 From: Igor Malinovskiy Date: Tue, 30 Sep 2025 16:31:15 +0200 Subject: [PATCH 08/18] Fix formatting --- tests/NRedisStack.Tests/Search/SearchTests.cs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/NRedisStack.Tests/Search/SearchTests.cs b/tests/NRedisStack.Tests/Search/SearchTests.cs index 4f6c442a..dc4c5501 100644 --- a/tests/NRedisStack.Tests/Search/SearchTests.cs +++ b/tests/NRedisStack.Tests/Search/SearchTests.cs @@ -62,29 +62,29 @@ private async Task AssertDatabaseSizeAsync(IDatabase db, int expected) { Assert.Equal(expected, await DatabaseSizeAsync(db)); } - + private void AssertIndexSize(ISearchCommands ft, string index, int expected) { - long indexed = -1; + long indexed = -1; // allow search time to catch up for (int i = 0; i < 10; i++) { indexed = ft.Info(index).NumDocs; - + if (indexed == expected) break; } Assert.Equal(expected, indexed); } - + private async Task AssertIndexSizeAsync(ISearchCommandsAsync ft, string index, int expected) { - long indexed = -1; + long indexed = -1; // allow search time to catch up for (int i = 0; i < 10; i++) { indexed = (await ft.InfoAsync(index)).NumDocs; - + if (indexed == expected) break; } @@ -568,7 +568,7 @@ public void TestApplyAndFilterAggregations(string endpointId) AddDocument(db, new Document("data5").Set("name", "def").Set("subj1", 65).Set("subj2", 45)); AddDocument(db, new Document("data6").Set("name", "ghi").Set("subj1", 70).Set("subj2", 70)); AssertIndexSize(ft, index, 6); - + AggregationRequest r = new AggregationRequest().Apply("(@subj1+@subj2)/2", "attemptavg") .GroupBy("@name", Reducers.Avg("@attemptavg").As("avgscore")) .Filter("@avgscore>=50") @@ -738,7 +738,7 @@ public void CreateWithFieldNames(string endpointId) db.HashSet("pupil:4444", [new("first", "Pat"), new("last", "Shu"), new("age", "21")]); db.HashSet("student:5555", [new("first", "Joen"), new("last", "Ko"), new("age", "20")]); db.HashSet("teacher:6666", [new("first", "Pat"), new("last", "Rod"), new("age", "20")]); - + AssertIndexSize(ft, index, 5); // only pupil and student keys are indexed SearchResult noFilters = ft.Search(index, new()); @@ -1652,7 +1652,7 @@ public void TestDropIndex(string endpointId) Assert.True(ft.DropIndex(index)); var ex = Record.Exception(() => { ft.Search(index, new("hello world")); }); - + Assert.NotNull(ex); Assert.IsType(ex); Assert.Contains("no such index", ex.Message, StringComparison.OrdinalIgnoreCase); @@ -1716,7 +1716,7 @@ public async Task TestDropIndexAsync(string endpointId) Assert.True(await ft.DropIndexAsync(index)); var ex = Record.Exception(() => { ft.Search(index, new("hello world")); }); - + Assert.NotNull(ex); Assert.IsType(ex); Assert.Contains("no such index", ex.Message, StringComparison.OrdinalIgnoreCase); From 7146bafbfa10fabac1cf51f14e868e3ee6850246 Mon Sep 17 00:00:00 2001 From: Igor Malinovskiy Date: Tue, 30 Sep 2025 16:51:56 +0200 Subject: [PATCH 09/18] Fix index size assertion --- tests/NRedisStack.Tests/Search/SearchTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/NRedisStack.Tests/Search/SearchTests.cs b/tests/NRedisStack.Tests/Search/SearchTests.cs index dc4c5501..0b605e8f 100644 --- a/tests/NRedisStack.Tests/Search/SearchTests.cs +++ b/tests/NRedisStack.Tests/Search/SearchTests.cs @@ -615,7 +615,7 @@ public void TestCreate(string endpointId) db.HashSet("pupil:4444", [new("first", "Pat"), new("last", "Shu"), new("age", "21")]); db.HashSet("student:5555", [new("first", "Joen"), new("last", "Ko"), new("age", "20")]); db.HashSet("teacher:6666", [new("first", "Pat"), new("last", "Rod"), new("age", "20")]); - AssertIndexSize(ft, index, 5); // only pupil and student keys are indexed + AssertIndexSize(ft, index, 4); // only pupil and student keys are indexed var noFilters = ft.Search(index, new()); Assert.Equal(4, noFilters.TotalResults); @@ -647,7 +647,7 @@ public async Task TestCreateAsync(string endpointId) db.HashSet("pupil:4444", [new("first", "Pat"), new("last", "Shu"), new("age", "21")]); db.HashSet("student:5555", [new("first", "Joen"), new("last", "Ko"), new("age", "20")]); db.HashSet("teacher:6666", [new("first", "Pat"), new("last", "Rod"), new("age", "20")]); - await AssertIndexSizeAsync(ft, index, 5); // only pupil and student keys are indexed + await AssertIndexSizeAsync(ft, index, 4); // only pupil and student keys are indexed var noFilters = ft.Search(index, new()); Assert.Equal(4, noFilters.TotalResults); From e8d810006e91c43e787655f4f5eb7f8663758de0 Mon Sep 17 00:00:00 2001 From: Igor Malinovskiy Date: Fri, 7 Nov 2025 12:45:17 +0100 Subject: [PATCH 10/18] Debug --- tests/NRedisStack.Tests/Search/SearchTests.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/NRedisStack.Tests/Search/SearchTests.cs b/tests/NRedisStack.Tests/Search/SearchTests.cs index 0b605e8f..ac767d2a 100644 --- a/tests/NRedisStack.Tests/Search/SearchTests.cs +++ b/tests/NRedisStack.Tests/Search/SearchTests.cs @@ -669,10 +669,14 @@ public void CreateNoParams(string endpointId) Schema sc = new Schema().AddTextField("first", 1.0).AddTextField("last", 1.0).AddNumericField("age"); Assert.True(ft.Create(index, FTCreateParams.CreateParams(), sc)); - + AssertIndexSize(ft, index, 0); + db.HashSet("student:1111", [new("first", "Joe"), new("last", "Dod"), new("age", 18)]); + AssertIndexSize(ft, index, 1); db.HashSet("student:3333", [new("first", "El"), new("last", "Mark"), new("age", 17)]); + AssertIndexSize(ft, index, 2); db.HashSet("pupil:4444", [new("first", "Pat"), new("last", "Shu"), new("age", 21)]); + AssertIndexSize(ft, index, 3); db.HashSet("student:5555", [new("first", "Joen"), new("last", "Ko"), new("age", 20)]); AssertIndexSize(ft, index, 4); From a08f209402d678be782e63e26ce527cd37bbe172 Mon Sep 17 00:00:00 2001 From: Igor Malinovskiy Date: Fri, 5 Dec 2025 14:38:47 +0100 Subject: [PATCH 11/18] Clean debugging --- tests/NRedisStack.Tests/Search/SearchTests.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/NRedisStack.Tests/Search/SearchTests.cs b/tests/NRedisStack.Tests/Search/SearchTests.cs index ac767d2a..671749fc 100644 --- a/tests/NRedisStack.Tests/Search/SearchTests.cs +++ b/tests/NRedisStack.Tests/Search/SearchTests.cs @@ -672,11 +672,8 @@ public void CreateNoParams(string endpointId) AssertIndexSize(ft, index, 0); db.HashSet("student:1111", [new("first", "Joe"), new("last", "Dod"), new("age", 18)]); - AssertIndexSize(ft, index, 1); db.HashSet("student:3333", [new("first", "El"), new("last", "Mark"), new("age", 17)]); - AssertIndexSize(ft, index, 2); db.HashSet("pupil:4444", [new("first", "Pat"), new("last", "Shu"), new("age", 21)]); - AssertIndexSize(ft, index, 3); db.HashSet("student:5555", [new("first", "Joen"), new("last", "Ko"), new("age", 20)]); AssertIndexSize(ft, index, 4); From 665e90c570c99beb563d1411f566ffa09a6662d0 Mon Sep 17 00:00:00 2001 From: Igor Malinovskiy Date: Fri, 5 Dec 2025 15:58:52 +0100 Subject: [PATCH 12/18] Skip search tests with cluster if Redis < 8.4 --- tests/NRedisStack.Tests/Search/SearchTests.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/NRedisStack.Tests/Search/SearchTests.cs b/tests/NRedisStack.Tests/Search/SearchTests.cs index 671749fc..bc251353 100644 --- a/tests/NRedisStack.Tests/Search/SearchTests.cs +++ b/tests/NRedisStack.Tests/Search/SearchTests.cs @@ -26,6 +26,11 @@ private static void SkipClusterPre8(string endpointId) // afresh from v8, where things behave much more predictably and reasonably. Skip.If(endpointId == EndpointsFixture.Env.Cluster && EndpointsFixture.RedisVersion.Major < 8, "Ignoring cluster tests for FT.SEARCH pre Redis 8.0"); + + // FIXME(imalinovskyi): We should skip cluster tests until https://github.com/RediSearch/RediSearch/pull/6960 + // is released as part of Redis v8.2.x + Skip.If(endpointId == EndpointsFixture.Env.Cluster + && EndpointsFixture.RedisVersion.Minor < 4, "Ignoring cluster tests for FT.SEARCH pre Redis 8.4"); } private void AddDocument(IDatabase db, Document doc) From 463cd74a94256a00f6420e7e3cf35df28c1fbb3f Mon Sep 17 00:00:00 2001 From: Igor Malinovskiy Date: Fri, 5 Dec 2025 15:59:12 +0100 Subject: [PATCH 13/18] Fix formatting --- tests/NRedisStack.Tests/Search/SearchTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/NRedisStack.Tests/Search/SearchTests.cs b/tests/NRedisStack.Tests/Search/SearchTests.cs index bc251353..357d8b53 100644 --- a/tests/NRedisStack.Tests/Search/SearchTests.cs +++ b/tests/NRedisStack.Tests/Search/SearchTests.cs @@ -675,7 +675,7 @@ public void CreateNoParams(string endpointId) Schema sc = new Schema().AddTextField("first", 1.0).AddTextField("last", 1.0).AddNumericField("age"); Assert.True(ft.Create(index, FTCreateParams.CreateParams(), sc)); AssertIndexSize(ft, index, 0); - + db.HashSet("student:1111", [new("first", "Joe"), new("last", "Dod"), new("age", 18)]); db.HashSet("student:3333", [new("first", "El"), new("last", "Mark"), new("age", 17)]); db.HashSet("pupil:4444", [new("first", "Pat"), new("last", "Shu"), new("age", 21)]); From db8869a79686d9a806fe07baafe49d29aee0f272 Mon Sep 17 00:00:00 2001 From: Igor Malinovskiy Date: Fri, 5 Dec 2025 16:02:07 +0100 Subject: [PATCH 14/18] Clean up test matrix - 8.0 is for security fixes now, so the "current" version should be switched to 8.2 - 8.4 has stable release now --- .github/workflows/integration.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index ee0f7d32..e297b403 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -17,7 +17,7 @@ concurrency: env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} - CURRENT_REDIS_VERSION: '8.0.2' + CURRENT_REDIS_VERSION: '8.2.3' jobs: redis_version: @@ -37,7 +37,7 @@ jobs: max-parallel: 15 fail-fast: false matrix: - redis-version: [ '8.4-GA-pre.3', '8.2.1', '${{ needs.redis_version.outputs.CURRENT }}', '7.4.1', '7.2.6', '6.2.16'] + redis-version: [ '8.4.0', '${{ needs.redis_version.outputs.CURRENT }}', '7.4.1', '7.2.6', '6.2.16'] dotnet-version: ['8.0', '9.0'] env: ACTIONS_ALLOW_UNSECURE_COMMANDS: true From b58ee3037fb7f761a2bfe992b691d3d0c657c4d8 Mon Sep 17 00:00:00 2001 From: Marc Gravell Date: Fri, 5 Dec 2025 15:31:53 +0000 Subject: [PATCH 15/18] dotnet format --- src/NRedisStack/CoreCommands/Enums/SetInfoAttr.cs | 1 + src/NRedisStack/Search/AggregationRequest.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/NRedisStack/CoreCommands/Enums/SetInfoAttr.cs b/src/NRedisStack/CoreCommands/Enums/SetInfoAttr.cs index 16f10a9a..84e3c64d 100644 --- a/src/NRedisStack/CoreCommands/Enums/SetInfoAttr.cs +++ b/src/NRedisStack/CoreCommands/Enums/SetInfoAttr.cs @@ -1,4 +1,5 @@ namespace NRedisStack.Core; + public enum SetInfoAttr { /// diff --git a/src/NRedisStack/Search/AggregationRequest.cs b/src/NRedisStack/Search/AggregationRequest.cs index 8cac4f77..46b771cb 100644 --- a/src/NRedisStack/Search/AggregationRequest.cs +++ b/src/NRedisStack/Search/AggregationRequest.cs @@ -2,6 +2,7 @@ using NRedisStack.Search.Literals; namespace NRedisStack.Search; + public class AggregationRequest : IDialectAwareParam { private readonly List args = []; // Check if Readonly From 557ed7327501367ddb2e7f5c9b33575fcdd2bccf Mon Sep 17 00:00:00 2001 From: Marc Gravell Date: Fri, 5 Dec 2025 15:55:53 +0000 Subject: [PATCH 16/18] Update linter.yaml --- .github/workflows/linter.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/linter.yaml b/.github/workflows/linter.yaml index 7c776851..c7b91594 100644 --- a/.github/workflows/linter.yaml +++ b/.github/workflows/linter.yaml @@ -20,6 +20,10 @@ jobs: run: | dotnet format + - name: status + run: | + git status + - name: Check for modified files run: | if (git status | Select-String -Pattern 'modified') { From dab18049d09d6ddf61daebe44dcba0fb6a7e91ce Mon Sep 17 00:00:00 2001 From: Marc Gravell Date: Fri, 5 Dec 2025 15:59:27 +0000 Subject: [PATCH 17/18] more fixin' --- tests/Doc/Bf_tutorial.cs | 1 + tests/Doc/Bitmap_tutorial.cs | 1 + tests/Doc/CmdsGenericExample.cs | 1 + tests/Doc/CmdsHashExample.cs | 1 + tests/Doc/CmdsListExample.cs | 1 + tests/Doc/CmdsSortedSetExamples.cs | 1 + tests/Doc/CmdsStringExample.cs | 1 + tests/Doc/Cms_tutorial.cs | 1 + tests/Doc/Cuckoo_tutorial.cs | 1 + tests/Doc/Geo_tutorial.cs | 1 + tests/Doc/HashExample.cs | 1 + tests/Doc/Hll_tutorial.cs | 1 + tests/Doc/HomeJsonExample.cs | 1 + tests/Doc/Json_tutorial.cs | 1 + tests/Doc/ListTutorial.cs | 1 + tests/Doc/PipeTransExample.cs | 1 + tests/Doc/QueryAggExample.cs | 1 + tests/Doc/QueryEmExample.cs | 1 + tests/Doc/QueryFtExample.cs | 1 + tests/Doc/QueryRangeExample.cs | 1 + tests/Doc/SearchQuickstartExample.cs | 1 + tests/Doc/SetGetExample.cs | 1 + tests/Doc/SetsTutorial.cs | 1 + tests/Doc/SortedSetExample.cs | 1 + tests/Doc/StreamTutorial.cs | 1 + tests/Doc/StringSnippets.cs | 1 + tests/Doc/Tdigest_tutorial.cs | 1 + tests/Doc/TimeSeriesTutorial.cs | 1 + tests/Doc/Topk_tutorial.cs | 1 + 29 files changed, 29 insertions(+) diff --git a/tests/Doc/Bf_tutorial.cs b/tests/Doc/Bf_tutorial.cs index 0cc16100..5bdeb5e8 100644 --- a/tests/Doc/Bf_tutorial.cs +++ b/tests/Doc/Bf_tutorial.cs @@ -9,6 +9,7 @@ // REMOVE_START namespace Doc; + [Collection("DocsTests")] // REMOVE_END diff --git a/tests/Doc/Bitmap_tutorial.cs b/tests/Doc/Bitmap_tutorial.cs index 2770752b..a2c0c1eb 100644 --- a/tests/Doc/Bitmap_tutorial.cs +++ b/tests/Doc/Bitmap_tutorial.cs @@ -8,6 +8,7 @@ // REMOVE_START namespace Doc; + [Collection("DocsTests")] // REMOVE_END diff --git a/tests/Doc/CmdsGenericExample.cs b/tests/Doc/CmdsGenericExample.cs index f9c0517d..29c62514 100644 --- a/tests/Doc/CmdsGenericExample.cs +++ b/tests/Doc/CmdsGenericExample.cs @@ -8,6 +8,7 @@ // REMOVE_START namespace Doc; + [Collection("DocsTests")] // REMOVE_END diff --git a/tests/Doc/CmdsHashExample.cs b/tests/Doc/CmdsHashExample.cs index 46a19cd9..ee1e86c9 100644 --- a/tests/Doc/CmdsHashExample.cs +++ b/tests/Doc/CmdsHashExample.cs @@ -6,6 +6,7 @@ using NRedisStack.Tests; namespace Doc; + [Collection("DocsTests")] // REMOVE_END diff --git a/tests/Doc/CmdsListExample.cs b/tests/Doc/CmdsListExample.cs index 158db82c..775e854a 100644 --- a/tests/Doc/CmdsListExample.cs +++ b/tests/Doc/CmdsListExample.cs @@ -8,6 +8,7 @@ // REMOVE_START namespace Doc; + [Collection("DocsTests")] // REMOVE_END diff --git a/tests/Doc/CmdsSortedSetExamples.cs b/tests/Doc/CmdsSortedSetExamples.cs index 852b5606..cf3d56fa 100644 --- a/tests/Doc/CmdsSortedSetExamples.cs +++ b/tests/Doc/CmdsSortedSetExamples.cs @@ -8,6 +8,7 @@ // REMOVE_START namespace Doc; + [Collection("DocsTests")] // REMOVE_END diff --git a/tests/Doc/CmdsStringExample.cs b/tests/Doc/CmdsStringExample.cs index d4d3e29c..58361ddc 100644 --- a/tests/Doc/CmdsStringExample.cs +++ b/tests/Doc/CmdsStringExample.cs @@ -8,6 +8,7 @@ // REMOVE_START namespace Doc; + [Collection("DocsTests")] // REMOVE_END diff --git a/tests/Doc/Cms_tutorial.cs b/tests/Doc/Cms_tutorial.cs index 10d19bd8..05c4ba87 100644 --- a/tests/Doc/Cms_tutorial.cs +++ b/tests/Doc/Cms_tutorial.cs @@ -10,6 +10,7 @@ // REMOVE_START namespace Doc; + [Collection("DocsTests")] // REMOVE_END diff --git a/tests/Doc/Cuckoo_tutorial.cs b/tests/Doc/Cuckoo_tutorial.cs index 24a8f061..5d83d828 100644 --- a/tests/Doc/Cuckoo_tutorial.cs +++ b/tests/Doc/Cuckoo_tutorial.cs @@ -9,6 +9,7 @@ // REMOVE_START namespace Doc; + [Collection("DocsTests")] // REMOVE_END diff --git a/tests/Doc/Geo_tutorial.cs b/tests/Doc/Geo_tutorial.cs index 180feb89..95d293ec 100644 --- a/tests/Doc/Geo_tutorial.cs +++ b/tests/Doc/Geo_tutorial.cs @@ -8,6 +8,7 @@ // REMOVE_START namespace Doc; + [Collection("DocsTests")] // REMOVE_END diff --git a/tests/Doc/HashExample.cs b/tests/Doc/HashExample.cs index b057284c..9ef2b10a 100644 --- a/tests/Doc/HashExample.cs +++ b/tests/Doc/HashExample.cs @@ -6,6 +6,7 @@ //REMOVE_START namespace Doc; + [Collection("DocsTests")] //REMOVE_END public class HashExample diff --git a/tests/Doc/Hll_tutorial.cs b/tests/Doc/Hll_tutorial.cs index d55891ff..84b2562f 100644 --- a/tests/Doc/Hll_tutorial.cs +++ b/tests/Doc/Hll_tutorial.cs @@ -8,6 +8,7 @@ // REMOVE_START namespace Doc; + [Collection("DocsTests")] // REMOVE_END diff --git a/tests/Doc/HomeJsonExample.cs b/tests/Doc/HomeJsonExample.cs index b4088477..1dd309ad 100644 --- a/tests/Doc/HomeJsonExample.cs +++ b/tests/Doc/HomeJsonExample.cs @@ -12,6 +12,7 @@ using NRedisStack.Tests; namespace Doc; + [Collection("DocsTests")] // REMOVE_END diff --git a/tests/Doc/Json_tutorial.cs b/tests/Doc/Json_tutorial.cs index 5d746488..7d11cf0a 100644 --- a/tests/Doc/Json_tutorial.cs +++ b/tests/Doc/Json_tutorial.cs @@ -10,6 +10,7 @@ // REMOVE_START namespace Doc; + [Collection("DocsTests")] // REMOVE_END diff --git a/tests/Doc/ListTutorial.cs b/tests/Doc/ListTutorial.cs index e87e0b03..aa1cd486 100644 --- a/tests/Doc/ListTutorial.cs +++ b/tests/Doc/ListTutorial.cs @@ -9,6 +9,7 @@ //REMOVE_START namespace Doc; + [Collection("DocsTests")] //REMOVE_END public class ListExample diff --git a/tests/Doc/PipeTransExample.cs b/tests/Doc/PipeTransExample.cs index 140b904b..87a13726 100644 --- a/tests/Doc/PipeTransExample.cs +++ b/tests/Doc/PipeTransExample.cs @@ -5,6 +5,7 @@ using NRedisStack.Tests; namespace Doc; + [Collection("DocsTests")] //REMOVE_END public class PipeTransExample diff --git a/tests/Doc/QueryAggExample.cs b/tests/Doc/QueryAggExample.cs index efaec41c..d7d853eb 100644 --- a/tests/Doc/QueryAggExample.cs +++ b/tests/Doc/QueryAggExample.cs @@ -12,6 +12,7 @@ // REMOVE_START namespace Doc; + [Collection("DocsTests")] // REMOVE_END diff --git a/tests/Doc/QueryEmExample.cs b/tests/Doc/QueryEmExample.cs index 6627efe7..54164b08 100644 --- a/tests/Doc/QueryEmExample.cs +++ b/tests/Doc/QueryEmExample.cs @@ -11,6 +11,7 @@ // REMOVE_START namespace Doc; + [Collection("DocsTests")] // REMOVE_END diff --git a/tests/Doc/QueryFtExample.cs b/tests/Doc/QueryFtExample.cs index 9a7f2895..73d6294c 100644 --- a/tests/Doc/QueryFtExample.cs +++ b/tests/Doc/QueryFtExample.cs @@ -11,6 +11,7 @@ // REMOVE_START namespace Doc; + [Collection("DocsTests")] // REMOVE_END diff --git a/tests/Doc/QueryRangeExample.cs b/tests/Doc/QueryRangeExample.cs index 26274fa7..c67a5170 100644 --- a/tests/Doc/QueryRangeExample.cs +++ b/tests/Doc/QueryRangeExample.cs @@ -11,6 +11,7 @@ // REMOVE_START namespace Doc; + [Collection("DocsTests")] // REMOVE_END diff --git a/tests/Doc/SearchQuickstartExample.cs b/tests/Doc/SearchQuickstartExample.cs index 7a561574..6cb31070 100644 --- a/tests/Doc/SearchQuickstartExample.cs +++ b/tests/Doc/SearchQuickstartExample.cs @@ -9,6 +9,7 @@ // REMOVE_START namespace Doc; + [Collection("DocsTests")] // REMOVE_END public class SearchQuickstartExample diff --git a/tests/Doc/SetGetExample.cs b/tests/Doc/SetGetExample.cs index cf636563..4d8a2e31 100644 --- a/tests/Doc/SetGetExample.cs +++ b/tests/Doc/SetGetExample.cs @@ -6,6 +6,7 @@ //REMOVE_START namespace Doc; + [Collection("DocsTests")] //REMOVE_END public class SetGetExample diff --git a/tests/Doc/SetsTutorial.cs b/tests/Doc/SetsTutorial.cs index ea9e47b9..391c79bd 100644 --- a/tests/Doc/SetsTutorial.cs +++ b/tests/Doc/SetsTutorial.cs @@ -8,6 +8,7 @@ //REMOVE_START namespace Doc; + [Collection("DocsTests")] //REMOVE_END diff --git a/tests/Doc/SortedSetExample.cs b/tests/Doc/SortedSetExample.cs index 6be0dcdf..c271b24d 100644 --- a/tests/Doc/SortedSetExample.cs +++ b/tests/Doc/SortedSetExample.cs @@ -7,6 +7,7 @@ //REMOVE_START namespace Doc; + [Collection("DocsTests")] //REMOVE_END public class SortedSetExample diff --git a/tests/Doc/StreamTutorial.cs b/tests/Doc/StreamTutorial.cs index 902fff5a..59d93492 100644 --- a/tests/Doc/StreamTutorial.cs +++ b/tests/Doc/StreamTutorial.cs @@ -8,6 +8,7 @@ // REMOVE_START namespace Doc; + [Collection("DocsTests")] // REMOVE_END diff --git a/tests/Doc/StringSnippets.cs b/tests/Doc/StringSnippets.cs index 7812ff32..cc7bb64e 100644 --- a/tests/Doc/StringSnippets.cs +++ b/tests/Doc/StringSnippets.cs @@ -7,6 +7,7 @@ using StackExchange.Redis; namespace Doc; + [Collection("DocsTests")] //REMOVE_END public class StringSnippets diff --git a/tests/Doc/Tdigest_tutorial.cs b/tests/Doc/Tdigest_tutorial.cs index d8deb091..8d6b91ed 100644 --- a/tests/Doc/Tdigest_tutorial.cs +++ b/tests/Doc/Tdigest_tutorial.cs @@ -9,6 +9,7 @@ // REMOVE_START namespace Doc; + [Collection("DocsTests")] // REMOVE_END diff --git a/tests/Doc/TimeSeriesTutorial.cs b/tests/Doc/TimeSeriesTutorial.cs index a82bc948..be2b1adf 100644 --- a/tests/Doc/TimeSeriesTutorial.cs +++ b/tests/Doc/TimeSeriesTutorial.cs @@ -15,6 +15,7 @@ using NRedisStack; namespace Doc; + [Collection("DocsTests")] // REMOVE_END diff --git a/tests/Doc/Topk_tutorial.cs b/tests/Doc/Topk_tutorial.cs index 5004bda4..33a9ec23 100644 --- a/tests/Doc/Topk_tutorial.cs +++ b/tests/Doc/Topk_tutorial.cs @@ -9,6 +9,7 @@ // REMOVE_START namespace Doc; + [Collection("DocsTests")] // REMOVE_END From cf7f6716739bc7fe541784262c23f9d376fdddc4 Mon Sep 17 00:00:00 2001 From: Igor Malinovskiy Date: Mon, 8 Dec 2025 10:32:24 +0100 Subject: [PATCH 18/18] Do not skip OSS Cluster tests on RE --- tests/NRedisStack.Tests/Search/SearchTests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/NRedisStack.Tests/Search/SearchTests.cs b/tests/NRedisStack.Tests/Search/SearchTests.cs index 357d8b53..ca37c673 100644 --- a/tests/NRedisStack.Tests/Search/SearchTests.cs +++ b/tests/NRedisStack.Tests/Search/SearchTests.cs @@ -30,6 +30,7 @@ private static void SkipClusterPre8(string endpointId) // FIXME(imalinovskyi): We should skip cluster tests until https://github.com/RediSearch/RediSearch/pull/6960 // is released as part of Redis v8.2.x Skip.If(endpointId == EndpointsFixture.Env.Cluster + && !EndpointsFixture.IsEnterprise && EndpointsFixture.RedisVersion.Minor < 4, "Ignoring cluster tests for FT.SEARCH pre Redis 8.4"); }