From b7ebf4e15a2795b3707eba9a91a989bdab650d61 Mon Sep 17 00:00:00 2001 From: currantw Date: Mon, 11 May 2026 17:56:04 -0700 Subject: [PATCH 01/18] Initial implementation Signed-off-by: currantw --- .../StackExchange/CommandFlagsTests.cs | 382 ++++++++++++++++++ 1 file changed, 382 insertions(+) diff --git a/tests/Valkey.Glide.IntegrationTests/StackExchange/CommandFlagsTests.cs b/tests/Valkey.Glide.IntegrationTests/StackExchange/CommandFlagsTests.cs index bf8c3828..f991eed0 100644 --- a/tests/Valkey.Glide.IntegrationTests/StackExchange/CommandFlagsTests.cs +++ b/tests/Valkey.Glide.IntegrationTests/StackExchange/CommandFlagsTests.cs @@ -1041,6 +1041,203 @@ public async Task StringSetRangeAsync_ThrowsOnCommandFlags(IDatabaseAsync db) => _ = await Assert.ThrowsAsync( () => db.StringSetRangeAsync("key", 0, "value", UnsupportedFlag)); + [Theory(DisableDiscoveryEnumeration = true)] + [MemberData(nameof(TestConfiguration.TestDatabases), MemberType = typeof(TestConfiguration))] + public async Task StringSetAsync_WithExpiry_ThrowsOnCommandFlags(IDatabaseAsync db) + { + _ = await Assert.ThrowsAsync( + () => db.StringSetAsync("key", "value", TimeSpan.FromSeconds(10), false, When.Always, UnsupportedFlag)); + _ = await Assert.ThrowsAsync( + () => db.StringSetAsync("key", "value", TimeSpan.FromSeconds(10), When.Always, UnsupportedFlag)); + } + + [Theory(DisableDiscoveryEnumeration = true)] + [MemberData(nameof(TestConfiguration.TestDatabases), MemberType = typeof(TestConfiguration))] + public async Task StringSetAndGetAsync_WithExpiry_ThrowsOnCommandFlags(IDatabaseAsync db) + => _ = await Assert.ThrowsAsync( + () => db.StringSetAndGetAsync("key", "value", TimeSpan.FromSeconds(10), When.Always, UnsupportedFlag)); + + #endregion + #region Stream Commands + + [Theory(DisableDiscoveryEnumeration = true)] + [MemberData(nameof(TestConfiguration.TestDatabases), MemberType = typeof(TestConfiguration))] + public async Task StreamAddAsync_SingleField_ThrowsOnCommandFlags(IDatabaseAsync db) + => _ = await Assert.ThrowsAsync( + () => db.StreamAddAsync("key", "field", "value", flags: UnsupportedFlag)); + + [Theory(DisableDiscoveryEnumeration = true)] + [MemberData(nameof(TestConfiguration.TestDatabases), MemberType = typeof(TestConfiguration))] + public async Task StreamAddAsync_MultiField_ThrowsOnCommandFlags(IDatabaseAsync db) + => _ = await Assert.ThrowsAsync( + () => db.StreamAddAsync("key", [], flags: UnsupportedFlag)); + + [Theory(DisableDiscoveryEnumeration = true)] + [MemberData(nameof(TestConfiguration.TestDatabases), MemberType = typeof(TestConfiguration))] + public async Task StreamReadAsync_SingleKey_ThrowsOnCommandFlags(IDatabaseAsync db) + => _ = await Assert.ThrowsAsync( + () => db.StreamReadAsync("key", "0-0", flags: UnsupportedFlag)); + + [Theory(DisableDiscoveryEnumeration = true)] + [MemberData(nameof(TestConfiguration.TestDatabases), MemberType = typeof(TestConfiguration))] + public async Task StreamReadAsync_MultiKey_ThrowsOnCommandFlags(IDatabaseAsync db) + => _ = await Assert.ThrowsAsync( + () => db.StreamReadAsync([], flags: UnsupportedFlag)); + + [Theory(DisableDiscoveryEnumeration = true)] + [MemberData(nameof(TestConfiguration.TestDatabases), MemberType = typeof(TestConfiguration))] + public async Task StreamRangeAsync_ThrowsOnCommandFlags(IDatabaseAsync db) + => _ = await Assert.ThrowsAsync( + () => db.StreamRangeAsync("key", flags: UnsupportedFlag)); + + [Theory(DisableDiscoveryEnumeration = true)] + [MemberData(nameof(TestConfiguration.TestDatabases), MemberType = typeof(TestConfiguration))] + public async Task StreamReadGroupAsync_SingleKey_ThrowsOnCommandFlags(IDatabaseAsync db) + => _ = await Assert.ThrowsAsync( + () => db.StreamReadGroupAsync("key", "group", "consumer", flags: UnsupportedFlag)); + + [Theory(DisableDiscoveryEnumeration = true)] + [MemberData(nameof(TestConfiguration.TestDatabases), MemberType = typeof(TestConfiguration))] + public async Task StreamReadGroupAsync_WithClaimMinIdleTime_ThrowsOnCommandFlags(IDatabaseAsync db) + => _ = await Assert.ThrowsAsync( + () => db.StreamReadGroupAsync("key", "group", "consumer", null, null, false, TimeSpan.FromSeconds(1), UnsupportedFlag)); + + [Theory(DisableDiscoveryEnumeration = true)] + [MemberData(nameof(TestConfiguration.TestDatabases), MemberType = typeof(TestConfiguration))] + public async Task StreamReadGroupAsync_MultiKey_ThrowsOnCommandFlags(IDatabaseAsync db) + => _ = await Assert.ThrowsAsync( + () => db.StreamReadGroupAsync([], "group", "consumer", flags: UnsupportedFlag)); + + [Theory(DisableDiscoveryEnumeration = true)] + [MemberData(nameof(TestConfiguration.TestDatabases), MemberType = typeof(TestConfiguration))] + public async Task StreamLengthAsync_ThrowsOnCommandFlags(IDatabaseAsync db) + => _ = await Assert.ThrowsAsync( + () => db.StreamLengthAsync("key", UnsupportedFlag)); + + [Theory(DisableDiscoveryEnumeration = true)] + [MemberData(nameof(TestConfiguration.TestDatabases), MemberType = typeof(TestConfiguration))] + public async Task StreamDeleteAsync_ThrowsOnCommandFlags(IDatabaseAsync db) + => _ = await Assert.ThrowsAsync( + () => db.StreamDeleteAsync("key", [], UnsupportedFlag)); + + [Theory(DisableDiscoveryEnumeration = true)] + [MemberData(nameof(TestConfiguration.TestDatabases), MemberType = typeof(TestConfiguration))] + public async Task StreamCreateConsumerGroupAsync_ThrowsOnCommandFlags(IDatabaseAsync db) + { + _ = await Assert.ThrowsAsync( + () => db.StreamCreateConsumerGroupAsync("key", "group", flags: UnsupportedFlag)); + _ = await Assert.ThrowsAsync( + () => db.StreamCreateConsumerGroupAsync("key", "group", null, true, 0L, UnsupportedFlag)); + } + + [Theory(DisableDiscoveryEnumeration = true)] + [MemberData(nameof(TestConfiguration.TestDatabases), MemberType = typeof(TestConfiguration))] + public async Task StreamDeleteConsumerGroupAsync_ThrowsOnCommandFlags(IDatabaseAsync db) + => _ = await Assert.ThrowsAsync( + () => db.StreamDeleteConsumerGroupAsync("key", "group", UnsupportedFlag)); + + [Theory(DisableDiscoveryEnumeration = true)] + [MemberData(nameof(TestConfiguration.TestDatabases), MemberType = typeof(TestConfiguration))] + public async Task StreamCreateConsumerAsync_ThrowsOnCommandFlags(IDatabaseAsync db) + => _ = await Assert.ThrowsAsync( + () => db.StreamCreateConsumerAsync("key", "group", "consumer", UnsupportedFlag)); + + [Theory(DisableDiscoveryEnumeration = true)] + [MemberData(nameof(TestConfiguration.TestDatabases), MemberType = typeof(TestConfiguration))] + public async Task StreamDeleteConsumerAsync_ThrowsOnCommandFlags(IDatabaseAsync db) + => _ = await Assert.ThrowsAsync( + () => db.StreamDeleteConsumerAsync("key", "group", "consumer", UnsupportedFlag)); + + [Theory(DisableDiscoveryEnumeration = true)] + [MemberData(nameof(TestConfiguration.TestDatabases), MemberType = typeof(TestConfiguration))] + public async Task StreamConsumerGroupSetPositionAsync_ThrowsOnCommandFlags(IDatabaseAsync db) + { + _ = await Assert.ThrowsAsync( + () => db.StreamConsumerGroupSetPositionAsync("key", "group", "0-0", UnsupportedFlag)); + _ = await Assert.ThrowsAsync( + () => db.StreamConsumerGroupSetPositionAsync("key", "group", "0-0", 0L, UnsupportedFlag)); + } + + [Theory(DisableDiscoveryEnumeration = true)] + [MemberData(nameof(TestConfiguration.TestDatabases), MemberType = typeof(TestConfiguration))] + public async Task StreamAcknowledgeAsync_ThrowsOnCommandFlags(IDatabaseAsync db) + { + _ = await Assert.ThrowsAsync( + () => db.StreamAcknowledgeAsync("key", "group", "0-0", UnsupportedFlag)); + _ = await Assert.ThrowsAsync( + () => db.StreamAcknowledgeAsync("key", "group", [], UnsupportedFlag)); + } + + [Theory(DisableDiscoveryEnumeration = true)] + [MemberData(nameof(TestConfiguration.TestDatabases), MemberType = typeof(TestConfiguration))] + public async Task StreamPendingAsync_ThrowsOnCommandFlags(IDatabaseAsync db) + => _ = await Assert.ThrowsAsync( + () => db.StreamPendingAsync("key", "group", UnsupportedFlag)); + + [Theory(DisableDiscoveryEnumeration = true)] + [MemberData(nameof(TestConfiguration.TestDatabases), MemberType = typeof(TestConfiguration))] + public async Task StreamPendingMessagesAsync_ThrowsOnCommandFlags(IDatabaseAsync db) + => _ = await Assert.ThrowsAsync( + () => db.StreamPendingMessagesAsync("key", "group", 10, "consumer", flags: UnsupportedFlag)); + + [Theory(DisableDiscoveryEnumeration = true)] + [MemberData(nameof(TestConfiguration.TestDatabases), MemberType = typeof(TestConfiguration))] + public async Task StreamClaimAsync_ThrowsOnCommandFlags(IDatabaseAsync db) + => _ = await Assert.ThrowsAsync( + () => db.StreamClaimAsync("key", "group", "consumer", 0, [], UnsupportedFlag)); + + [Theory(DisableDiscoveryEnumeration = true)] + [MemberData(nameof(TestConfiguration.TestDatabases), MemberType = typeof(TestConfiguration))] + public async Task StreamClaimIdsOnlyAsync_ThrowsOnCommandFlags(IDatabaseAsync db) + => _ = await Assert.ThrowsAsync( + () => db.StreamClaimIdsOnlyAsync("key", "group", "consumer", 0, [], UnsupportedFlag)); + + [Theory(DisableDiscoveryEnumeration = true)] + [MemberData(nameof(TestConfiguration.TestDatabases), MemberType = typeof(TestConfiguration))] + public async Task StreamAutoClaimAsync_ThrowsOnCommandFlags(IDatabaseAsync db) + => _ = await Assert.ThrowsAsync( + () => db.StreamAutoClaimAsync("key", "group", "consumer", 0, "0-0", flags: UnsupportedFlag)); + + [Theory(DisableDiscoveryEnumeration = true)] + [MemberData(nameof(TestConfiguration.TestDatabases), MemberType = typeof(TestConfiguration))] + public async Task StreamAutoClaimIdsOnlyAsync_ThrowsOnCommandFlags(IDatabaseAsync db) + => _ = await Assert.ThrowsAsync( + () => db.StreamAutoClaimIdsOnlyAsync("key", "group", "consumer", 0, "0-0", flags: UnsupportedFlag)); + + [Theory(DisableDiscoveryEnumeration = true)] + [MemberData(nameof(TestConfiguration.TestDatabases), MemberType = typeof(TestConfiguration))] + public async Task StreamTrimAsync_ThrowsOnCommandFlags(IDatabaseAsync db) + { + _ = await Assert.ThrowsAsync( + () => db.StreamTrimAsync("key", 100, false, UnsupportedFlag)); + _ = await Assert.ThrowsAsync( + () => db.StreamTrimAsync("key", 100L, false, null, StreamTrimMode.KeepReferences, UnsupportedFlag)); + } + + [Theory(DisableDiscoveryEnumeration = true)] + [MemberData(nameof(TestConfiguration.TestDatabases), MemberType = typeof(TestConfiguration))] + public async Task StreamTrimByMinIdAsync_ThrowsOnCommandFlags(IDatabaseAsync db) + => _ = await Assert.ThrowsAsync( + () => db.StreamTrimByMinIdAsync("key", "0-0", flags: UnsupportedFlag)); + + [Theory(DisableDiscoveryEnumeration = true)] + [MemberData(nameof(TestConfiguration.TestDatabases), MemberType = typeof(TestConfiguration))] + public async Task StreamInfoAsync_ThrowsOnCommandFlags(IDatabaseAsync db) + => _ = await Assert.ThrowsAsync( + () => db.StreamInfoAsync("key", UnsupportedFlag)); + + [Theory(DisableDiscoveryEnumeration = true)] + [MemberData(nameof(TestConfiguration.TestDatabases), MemberType = typeof(TestConfiguration))] + public async Task StreamGroupInfoAsync_ThrowsOnCommandFlags(IDatabaseAsync db) + => _ = await Assert.ThrowsAsync( + () => db.StreamGroupInfoAsync("key", UnsupportedFlag)); + + [Theory(DisableDiscoveryEnumeration = true)] + [MemberData(nameof(TestConfiguration.TestDatabases), MemberType = typeof(TestConfiguration))] + public async Task StreamConsumerInfoAsync_ThrowsOnCommandFlags(IDatabaseAsync db) + => _ = await Assert.ThrowsAsync( + () => db.StreamConsumerInfoAsync("key", "group", UnsupportedFlag)); + #endregion #region Server Management Commands @@ -1092,5 +1289,190 @@ public async Task IServer_FlushAllDatabasesAsync_UnsupportedFlags_Throws(IServer => _ = await Assert.ThrowsAsync( () => server.FlushAllDatabasesAsync(UnsupportedFlag)); + [Theory(DisableDiscoveryEnumeration = true)] + [MemberData(nameof(TestConfiguration.TestServers), MemberType = typeof(TestConfiguration))] + public async Task IServer_ExecuteAsync_UnsupportedFlags_Throws(IServer server) + => _ = await Assert.ThrowsAsync( + () => server.ExecuteAsync("PING", [], UnsupportedFlag)); + + [Theory(DisableDiscoveryEnumeration = true)] + [MemberData(nameof(TestConfiguration.TestServers), MemberType = typeof(TestConfiguration))] + public async Task IServer_InfoRawAsync_UnsupportedFlags_Throws(IServer server) + => _ = await Assert.ThrowsAsync( + () => server.InfoRawAsync(flags: UnsupportedFlag)); + + [Theory(DisableDiscoveryEnumeration = true)] + [MemberData(nameof(TestConfiguration.TestServers), MemberType = typeof(TestConfiguration))] + public async Task IServer_InfoAsync_UnsupportedFlags_Throws(IServer server) + => _ = await Assert.ThrowsAsync( + () => server.InfoAsync(flags: UnsupportedFlag)); + + [Theory(DisableDiscoveryEnumeration = true)] + [MemberData(nameof(TestConfiguration.TestServers), MemberType = typeof(TestConfiguration))] + public async Task IServer_PingAsync_UnsupportedFlags_Throws(IServer server) + => _ = await Assert.ThrowsAsync( + () => server.PingAsync("hello", UnsupportedFlag)); + + [Theory(DisableDiscoveryEnumeration = true)] + [MemberData(nameof(TestConfiguration.TestServers), MemberType = typeof(TestConfiguration))] + public async Task IServer_EchoAsync_UnsupportedFlags_Throws(IServer server) + => _ = await Assert.ThrowsAsync( + () => server.EchoAsync("hello", UnsupportedFlag)); + + [Theory(DisableDiscoveryEnumeration = true)] + [MemberData(nameof(TestConfiguration.TestServers), MemberType = typeof(TestConfiguration))] + public async Task IServer_ConfigRewriteAsync_UnsupportedFlags_Throws(IServer server) + => _ = await Assert.ThrowsAsync( + () => server.ConfigRewriteAsync(UnsupportedFlag)); + + [Theory(DisableDiscoveryEnumeration = true)] + [MemberData(nameof(TestConfiguration.TestServers), MemberType = typeof(TestConfiguration))] + public async Task IServer_DatabaseSizeAsync_UnsupportedFlags_Throws(IServer server) + => _ = await Assert.ThrowsAsync( + () => server.DatabaseSizeAsync(UnsupportedFlag)); + + [Theory(DisableDiscoveryEnumeration = true)] + [MemberData(nameof(TestConfiguration.TestServers), MemberType = typeof(TestConfiguration))] + public async Task IServer_ClientGetNameAsync_UnsupportedFlags_Throws(IServer server) + => _ = await Assert.ThrowsAsync( + () => server.ClientGetNameAsync(UnsupportedFlag)); + + [Theory(DisableDiscoveryEnumeration = true)] + [MemberData(nameof(TestConfiguration.TestServers), MemberType = typeof(TestConfiguration))] + public async Task IServer_ClientIdAsync_UnsupportedFlags_Throws(IServer server) + => _ = await Assert.ThrowsAsync( + () => server.ClientIdAsync(UnsupportedFlag)); + + [Theory(DisableDiscoveryEnumeration = true)] + [MemberData(nameof(TestConfiguration.TestServers), MemberType = typeof(TestConfiguration))] + public async Task IServer_ScriptExistsAsync_String_UnsupportedFlags_Throws(IServer server) + => _ = await Assert.ThrowsAsync( + () => server.ScriptExistsAsync("return 1", UnsupportedFlag)); + + [Theory(DisableDiscoveryEnumeration = true)] + [MemberData(nameof(TestConfiguration.TestServers), MemberType = typeof(TestConfiguration))] + public async Task IServer_ScriptExistsAsync_ByteArray_UnsupportedFlags_Throws(IServer server) + => _ = await Assert.ThrowsAsync( + () => server.ScriptExistsAsync([], UnsupportedFlag)); + + [Theory(DisableDiscoveryEnumeration = true)] + [MemberData(nameof(TestConfiguration.TestServers), MemberType = typeof(TestConfiguration))] + public async Task IServer_ScriptLoadAsync_String_UnsupportedFlags_Throws(IServer server) + => _ = await Assert.ThrowsAsync( + () => server.ScriptLoadAsync("return 1", UnsupportedFlag)); + + [Theory(DisableDiscoveryEnumeration = true)] + [MemberData(nameof(TestConfiguration.TestServers), MemberType = typeof(TestConfiguration))] + public async Task IServer_ScriptLoadAsync_LuaScript_UnsupportedFlags_Throws(IServer server) + => _ = await Assert.ThrowsAsync( + () => server.ScriptLoadAsync(LuaScript.Prepare("return 1"), UnsupportedFlag)); + + [Theory(DisableDiscoveryEnumeration = true)] + [MemberData(nameof(TestConfiguration.TestServers), MemberType = typeof(TestConfiguration))] + public async Task IServer_ScriptFlushAsync_UnsupportedFlags_Throws(IServer server) + => _ = await Assert.ThrowsAsync( + () => server.ScriptFlushAsync(UnsupportedFlag)); + + [Theory(DisableDiscoveryEnumeration = true)] + [MemberData(nameof(TestConfiguration.TestServers), MemberType = typeof(TestConfiguration))] + public async Task IServer_KeysAsync_UnsupportedFlags_Throws(IServer server) + => _ = await Assert.ThrowsAsync( + async () => { await foreach (var _ in server.KeysAsync(flags: UnsupportedFlag)) { } }); + + #endregion + #region IDatabaseAsync Commands + + [Theory(DisableDiscoveryEnumeration = true)] + [MemberData(nameof(TestConfiguration.TestDatabases), MemberType = typeof(TestConfiguration))] + public async Task ExecuteAsync_ThrowsOnCommandFlags(IDatabaseAsync db) + => _ = await Assert.ThrowsAsync( + () => db.ExecuteAsync("PING", null, UnsupportedFlag)); + + [Theory(DisableDiscoveryEnumeration = true)] + [MemberData(nameof(TestConfiguration.TestDatabases), MemberType = typeof(TestConfiguration))] + public async Task PingAsync_ThrowsOnCommandFlags(IDatabaseAsync db) + => _ = await Assert.ThrowsAsync( + () => db.PingAsync(UnsupportedFlag)); + + #endregion + #region Transaction Commands + + [Theory(DisableDiscoveryEnumeration = true)] + [MemberData(nameof(TestConfiguration.TestDatabases), MemberType = typeof(TestConfiguration))] + public async Task WatchAsync_ThrowsOnCommandFlags(IDatabaseAsync db) + => _ = await Assert.ThrowsAsync( + () => db.WatchAsync([], UnsupportedFlag)); + + [Theory(DisableDiscoveryEnumeration = true)] + [MemberData(nameof(TestConfiguration.TestDatabases), MemberType = typeof(TestConfiguration))] + public async Task UnwatchAsync_ThrowsOnCommandFlags(IDatabaseAsync db) + => _ = await Assert.ThrowsAsync( + () => db.UnwatchAsync(UnsupportedFlag)); + + [Theory(DisableDiscoveryEnumeration = true)] + [MemberData(nameof(TestConfiguration.TestDatabases), MemberType = typeof(TestConfiguration))] + public async Task UnwatchAsync_WithRoute_ThrowsOnCommandFlags(IDatabaseAsync db) + => _ = await Assert.ThrowsAsync( + () => db.UnwatchAsync(Route.Random, UnsupportedFlag)); + + #endregion + #region Subscriber Commands + + [Theory(DisableDiscoveryEnumeration = true)] + [MemberData(nameof(TestConfiguration.TestStandaloneConnections), MemberType = typeof(TestConfiguration))] + public async Task ISubscriber_PublishAsync_ThrowsOnCommandFlags(ConnectionMultiplexer connection) + { + var subscriber = connection.GetSubscriber(); + _ = await Assert.ThrowsAsync( + () => subscriber.PublishAsync( + ValkeyChannel.Literal("test-channel"), + "hello", + UnsupportedFlag)); + } + + [Theory(DisableDiscoveryEnumeration = true)] + [MemberData(nameof(TestConfiguration.TestStandaloneConnections), MemberType = typeof(TestConfiguration))] + public async Task ISubscriber_SubscribeAsync_WithHandler_ThrowsOnCommandFlags(ConnectionMultiplexer connection) + { + var subscriber = connection.GetSubscriber(); + _ = await Assert.ThrowsAsync( + () => subscriber.SubscribeAsync( + ValkeyChannel.Literal("test-channel"), + (_, _) => { }, + UnsupportedFlag)); + } + + [Theory(DisableDiscoveryEnumeration = true)] + [MemberData(nameof(TestConfiguration.TestStandaloneConnections), MemberType = typeof(TestConfiguration))] + public async Task ISubscriber_SubscribeAsync_Queue_ThrowsOnCommandFlags(ConnectionMultiplexer connection) + { + var subscriber = connection.GetSubscriber(); + _ = await Assert.ThrowsAsync( + () => subscriber.SubscribeAsync( + ValkeyChannel.Literal("test-channel"), + UnsupportedFlag)); + } + + [Theory(DisableDiscoveryEnumeration = true)] + [MemberData(nameof(TestConfiguration.TestStandaloneConnections), MemberType = typeof(TestConfiguration))] + public async Task ISubscriber_UnsubscribeAsync_ThrowsOnCommandFlags(ConnectionMultiplexer connection) + { + var subscriber = connection.GetSubscriber(); + _ = await Assert.ThrowsAsync( + () => subscriber.UnsubscribeAsync( + ValkeyChannel.Literal("test-channel"), + null, + UnsupportedFlag)); + } + + [Theory(DisableDiscoveryEnumeration = true)] + [MemberData(nameof(TestConfiguration.TestStandaloneConnections), MemberType = typeof(TestConfiguration))] + public async Task ISubscriber_UnsubscribeAllAsync_ThrowsOnCommandFlags(ConnectionMultiplexer connection) + { + var subscriber = connection.GetSubscriber(); + _ = await Assert.ThrowsAsync( + () => subscriber.UnsubscribeAllAsync(UnsupportedFlag)); + } + #endregion } From fbca8a3a9459d8e589d893dc08c17fe38603f9e3 Mon Sep 17 00:00:00 2001 From: currantw Date: Mon, 11 May 2026 18:00:33 -0700 Subject: [PATCH 02/18] Use TimeSpan.Zero throughout Signed-off-by: currantw --- .../StackExchange/CommandFlagsTests.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/Valkey.Glide.IntegrationTests/StackExchange/CommandFlagsTests.cs b/tests/Valkey.Glide.IntegrationTests/StackExchange/CommandFlagsTests.cs index f991eed0..1ce5b5da 100644 --- a/tests/Valkey.Glide.IntegrationTests/StackExchange/CommandFlagsTests.cs +++ b/tests/Valkey.Glide.IntegrationTests/StackExchange/CommandFlagsTests.cs @@ -1046,16 +1046,16 @@ public async Task StringSetRangeAsync_ThrowsOnCommandFlags(IDatabaseAsync db) public async Task StringSetAsync_WithExpiry_ThrowsOnCommandFlags(IDatabaseAsync db) { _ = await Assert.ThrowsAsync( - () => db.StringSetAsync("key", "value", TimeSpan.FromSeconds(10), false, When.Always, UnsupportedFlag)); + () => db.StringSetAsync("key", "value", TimeSpan.Zero, false, When.Always, UnsupportedFlag)); _ = await Assert.ThrowsAsync( - () => db.StringSetAsync("key", "value", TimeSpan.FromSeconds(10), When.Always, UnsupportedFlag)); + () => db.StringSetAsync("key", "value", TimeSpan.Zero, When.Always, UnsupportedFlag)); } [Theory(DisableDiscoveryEnumeration = true)] [MemberData(nameof(TestConfiguration.TestDatabases), MemberType = typeof(TestConfiguration))] public async Task StringSetAndGetAsync_WithExpiry_ThrowsOnCommandFlags(IDatabaseAsync db) => _ = await Assert.ThrowsAsync( - () => db.StringSetAndGetAsync("key", "value", TimeSpan.FromSeconds(10), When.Always, UnsupportedFlag)); + () => db.StringSetAndGetAsync("key", "value", TimeSpan.Zero, When.Always, UnsupportedFlag)); #endregion #region Stream Commands @@ -1100,7 +1100,7 @@ public async Task StreamReadGroupAsync_SingleKey_ThrowsOnCommandFlags(IDatabaseA [MemberData(nameof(TestConfiguration.TestDatabases), MemberType = typeof(TestConfiguration))] public async Task StreamReadGroupAsync_WithClaimMinIdleTime_ThrowsOnCommandFlags(IDatabaseAsync db) => _ = await Assert.ThrowsAsync( - () => db.StreamReadGroupAsync("key", "group", "consumer", null, null, false, TimeSpan.FromSeconds(1), UnsupportedFlag)); + () => db.StreamReadGroupAsync("key", "group", "consumer", null, null, false, TimeSpan.Zero, UnsupportedFlag)); [Theory(DisableDiscoveryEnumeration = true)] [MemberData(nameof(TestConfiguration.TestDatabases), MemberType = typeof(TestConfiguration))] From 145968b93e0b04244b0db5847c810d880cb267c8 Mon Sep 17 00:00:00 2001 From: currantw Date: Mon, 11 May 2026 18:13:46 -0700 Subject: [PATCH 03/18] Check command flags first Signed-off-by: currantw --- sources/Valkey.Glide/Abstract/ValkeyServer.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/sources/Valkey.Glide/Abstract/ValkeyServer.cs b/sources/Valkey.Glide/Abstract/ValkeyServer.cs index 9c3fada0..4d009d68 100644 --- a/sources/Valkey.Glide/Abstract/ValkeyServer.cs +++ b/sources/Valkey.Glide/Abstract/ValkeyServer.cs @@ -163,13 +163,13 @@ public async Task LolwutAsync(CommandFlags flags = CommandFlags.None) public async Task ScriptExistsAsync(string script, CommandFlags flags = CommandFlags.None) { + GuardClauses.ThrowIfCommandFlags(flags); + if (string.IsNullOrEmpty(script)) { throw new ArgumentException("Script cannot be null or empty", nameof(script)); } - GuardClauses.ThrowIfCommandFlags(flags); - // Calculate SHA1 hash of the script using Script scriptObj = new(script); string hash = scriptObj.Hash; @@ -181,13 +181,13 @@ public async Task ScriptExistsAsync(string script, CommandFlags flags = Co public async Task ScriptExistsAsync(byte[] sha1, CommandFlags flags = CommandFlags.None) { + GuardClauses.ThrowIfCommandFlags(flags); + if (sha1 == null || sha1.Length == 0) { throw new ArgumentException("SHA1 hash cannot be null or empty", nameof(sha1)); } - GuardClauses.ThrowIfCommandFlags(flags); - // Convert byte array to hex string string hash = BitConverter.ToString(sha1).Replace("-", "").ToLowerInvariant(); @@ -198,13 +198,13 @@ public async Task ScriptExistsAsync(byte[] sha1, CommandFlags flags = Comm public async Task ScriptLoadAsync(string script, CommandFlags flags = CommandFlags.None) { + GuardClauses.ThrowIfCommandFlags(flags); + if (string.IsNullOrEmpty(script)) { throw new ArgumentException("Script cannot be null or empty", nameof(script)); } - GuardClauses.ThrowIfCommandFlags(flags); - // Use custom command to call SCRIPT LOAD ValkeyResult result = await ExecuteAsync("SCRIPT", ["LOAD", script], flags); string? hashString = (string?)result; @@ -220,13 +220,13 @@ public async Task ScriptLoadAsync(string script, CommandFlags flags = Co public async Task ScriptLoadAsync(LuaScript script, CommandFlags flags = CommandFlags.None) { + GuardClauses.ThrowIfCommandFlags(flags); + if (script == null) { throw new ArgumentNullException(nameof(script)); } - GuardClauses.ThrowIfCommandFlags(flags); - // Load the executable script byte[] hash = await ScriptLoadAsync(script.ExecutableScript, flags); return new LoadedLuaScript(script, hash, script.ExecutableScript); From 9745967511efcfeb0492b57bc8413fdab741b068 Mon Sep 17 00:00:00 2001 From: currantw Date: Mon, 11 May 2026 18:20:02 -0700 Subject: [PATCH 04/18] Update Taskfile to properly handle test name filter Signed-off-by: currantw --- Taskfile.yml | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/Taskfile.yml b/Taskfile.yml index 615d6bad..85aa9785 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -7,10 +7,16 @@ vars: UNIT_TEST_PROJECT: tests/Valkey.Glide.UnitTests/Valkey.Glide.UnitTests.csproj INTEGRATION_TEST_PROJECT: tests/Valkey.Glide.IntegrationTests/Valkey.Glide.IntegrationTests.csproj - # Build options + # Build the solution or a specific target. + # Usage: `task build target=lib` TARGET: '{{.target | default "all"}}' TARGET_PATH: '{{if eq .TARGET "lib"}}sources/Valkey.Glide/{{end}}' + # Filter tests by class or method name. + # Usage: `task test:unit name=MyTestClass` + NAME: '{{.name | default ""}}' + TEST_FILTER: '{{if .NAME}}--filter "FullyQualifiedName~{{.NAME}}"{{end}}' + tasks: clean: desc: Clean test results and reports directories @@ -30,14 +36,18 @@ tasks: test:unit: desc: Run unit tests only deps: [clean] - cmds: - - dotnet test {{.UNIT_TEST_PROJECT}} --configuration Release --verbosity normal + cmd: dotnet test {{.UNIT_TEST_PROJECT}} + --configuration Release + --verbosity normal + {{.TEST_FILTER}} test:integration: desc: Run integration tests only deps: [clean] - cmds: - - dotnet test {{.INTEGRATION_TEST_PROJECT}} --configuration Release --verbosity normal + cmd: dotnet test {{.INTEGRATION_TEST_PROJECT}} + --configuration Release + --verbosity normal + {{.TEST_FILTER}} test:coverage:unit: desc: Run unit tests with coverage collection From a1ddb1b67c84dec7ff1b7d891b2a45a4e0267278 Mon Sep 17 00:00:00 2001 From: currantw Date: Mon, 11 May 2026 18:25:48 -0700 Subject: [PATCH 05/18] Fix Taskfile syntax Signed-off-by: currantw --- Taskfile.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Taskfile.yml b/Taskfile.yml index 85aa9785..e0544525 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -15,7 +15,7 @@ vars: # Filter tests by class or method name. # Usage: `task test:unit name=MyTestClass` NAME: '{{.name | default ""}}' - TEST_FILTER: '{{if .NAME}}--filter "FullyQualifiedName~{{.NAME}}"{{end}}' + TEST_FILTER: '{{if .NAME}}--filter FullyQualifiedName~{{.NAME}}{{end}}' tasks: clean: From 7d68f91eab4541d32ea6987b33582df161d149d1 Mon Sep 17 00:00:00 2001 From: currantw Date: Tue, 12 May 2026 08:07:13 -0700 Subject: [PATCH 06/18] More Taskfile cleanup Signed-off-by: currantw --- Taskfile.yml | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/Taskfile.yml b/Taskfile.yml index e0544525..7445c4ea 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -36,18 +36,20 @@ tasks: test:unit: desc: Run unit tests only deps: [clean] - cmd: dotnet test {{.UNIT_TEST_PROJECT}} - --configuration Release - --verbosity normal - {{.TEST_FILTER}} + cmds: + - dotnet test {{.UNIT_TEST_PROJECT}} + --configuration Release + -verbosity normal + {{.TEST_FILTER}} test:integration: desc: Run integration tests only deps: [clean] - cmd: dotnet test {{.INTEGRATION_TEST_PROJECT}} - --configuration Release - --verbosity normal - {{.TEST_FILTER}} + cmds: + - dotnet test {{.INTEGRATION_TEST_PROJECT}} + --configuration Release + --verbosity normal + {{.TEST_FILTER}} test:coverage:unit: desc: Run unit tests with coverage collection From c8398f9fd61e902ad5c19621cae85bbe19ec319c Mon Sep 17 00:00:00 2001 From: currantw Date: Tue, 12 May 2026 08:16:20 -0700 Subject: [PATCH 07/18] Use `--filter` instead of `--name`. Signed-off-by: currantw --- Taskfile.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Taskfile.yml b/Taskfile.yml index 7445c4ea..687cc4ca 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -10,12 +10,12 @@ vars: # Build the solution or a specific target. # Usage: `task build target=lib` TARGET: '{{.target | default "all"}}' - TARGET_PATH: '{{if eq .TARGET "lib"}}sources/Valkey.Glide/{{end}}' + BUILD_TARGET: '{{if eq .TARGET "lib"}}sources/Valkey.Glide/{{end}}' # Filter tests by class or method name. - # Usage: `task test:unit name=MyTestClass` - NAME: '{{.name | default ""}}' - TEST_FILTER: '{{if .NAME}}--filter FullyQualifiedName~{{.NAME}}{{end}}' + # Usage: `task test:unit filter=MyTestClass` + FILTER: '{{.filter | default ""}}' + TEST_FILTER: '{{if .FILTER}}--filter FullyQualifiedName~{{.FILTER}}{{end}}' tasks: clean: @@ -159,7 +159,7 @@ tasks: build: desc: Build the solution cmds: - - dotnet build {{.TARGET_PATH}} --configuration Release + - dotnet build {{.BUILD_TARGET}} --configuration Release test: desc: Build and run all tests From 78c301c910265a0a381d97416ba2bea8f4947103 Mon Sep 17 00:00:00 2001 From: currantw Date: Tue, 12 May 2026 08:22:08 -0700 Subject: [PATCH 08/18] Update DEVELOPER and AGENTS Signed-off-by: currantw --- AGENTS.md | 5 ++--- DEVELOPER.md | 8 ++++++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index c7eb31b2..001fe85d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -33,9 +33,8 @@ Common commands: - `task test:integration` (integration tests only) - Filter tests: - - By class: `--filter "FullyQualifiedName~ClassName"` - - By method: `--filter "FullyQualifiedName~MethodName"` - - By display name pattern: `--filter "DisplayName~Pattern"` + - By class: `task test --filter "ClassName"` + - By method: `task test --filter "MethodName"` - Coverage and reports (preferred via Task): - `task coverage`, `task coverage:unit`, `task coverage:integration` diff --git a/DEVELOPER.md b/DEVELOPER.md index ad83894a..ad8dc781 100644 --- a/DEVELOPER.md +++ b/DEVELOPER.md @@ -196,8 +196,12 @@ In Windows, run from following commands from the appropriate Visual Studio Comma task test # Run specific test suites - task test:unit # Unit tests only - task test:integration # Integration tests only + task test:unit + task test:integration + + # Run specific test classes or methods + task test:unit filter=MyTestClass + task test:integration filter=MyMethodName # Run tests with coverage and generate reports task coverage # All tests with coverage From ab99ff969221056f4eeb0c706cbcd3e5f54fc7aa Mon Sep 17 00:00:00 2001 From: currantw Date: Tue, 12 May 2026 08:28:26 -0700 Subject: [PATCH 09/18] More Taskfile cleanup Signed-off-by: currantw --- Taskfile.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Taskfile.yml b/Taskfile.yml index 687cc4ca..1a52c085 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -15,7 +15,7 @@ vars: # Filter tests by class or method name. # Usage: `task test:unit filter=MyTestClass` FILTER: '{{.filter | default ""}}' - TEST_FILTER: '{{if .FILTER}}--filter FullyQualifiedName~{{.FILTER}}{{end}}' + TEST_FILTER: "{{if .FILTER}}--filter FullyQualifiedName~{{.FILTER}}{{end}}" tasks: clean: From 5e0e7284609a7d7243e86d27d2d96df21618444d Mon Sep 17 00:00:00 2001 From: currantw Date: Tue, 12 May 2026 08:43:25 -0700 Subject: [PATCH 10/18] fix: address review comments for Taskfile and AGENTS.md Signed-off-by: currantw --- AGENTS.md | 5 +++-- Taskfile.yml | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 001fe85d..d0e74b9a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -33,8 +33,9 @@ Common commands: - `task test:integration` (integration tests only) - Filter tests: - - By class: `task test --filter "ClassName"` - - By method: `task test --filter "MethodName"` + - Via Task: `task test:unit filter=ClassName` or `task test:integration filter=MethodName` + - Via dotnet directly: `dotnet test --filter "FullyQualifiedName~ClassName"` + - By display name pattern: `dotnet test --filter "DisplayName~Pattern"` - Coverage and reports (preferred via Task): - `task coverage`, `task coverage:unit`, `task coverage:integration` diff --git a/Taskfile.yml b/Taskfile.yml index 1a52c085..b3670b89 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -39,7 +39,7 @@ tasks: cmds: - dotnet test {{.UNIT_TEST_PROJECT}} --configuration Release - -verbosity normal + --verbosity normal {{.TEST_FILTER}} test:integration: From 6c6c9fd1d880659bff11813b6668c46c7be0c656 Mon Sep 17 00:00:00 2001 From: currantw Date: Tue, 12 May 2026 08:45:24 -0700 Subject: [PATCH 11/18] Update AGENTS Signed-off-by: currantw --- AGENTS.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index d0e74b9a..4598e63a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -33,9 +33,8 @@ Common commands: - `task test:integration` (integration tests only) - Filter tests: - - Via Task: `task test:unit filter=ClassName` or `task test:integration filter=MethodName` - - Via dotnet directly: `dotnet test --filter "FullyQualifiedName~ClassName"` - - By display name pattern: `dotnet test --filter "DisplayName~Pattern"` + - By class: `task test:unit filter=MyTestClass` + - By method: `task test:integration filter=MyMethodName` - Coverage and reports (preferred via Task): - `task coverage`, `task coverage:unit`, `task coverage:integration` From 66d47f93b3ad7afce1091ac8775524403cc78979 Mon Sep 17 00:00:00 2001 From: currantw Date: Tue, 12 May 2026 10:26:35 -0700 Subject: [PATCH 12/18] fix(test): add unique prefixes to FtInfoTests to prevent flaky Size assertions Without a prefix, FT.CREATE indexes scan all keys in the database. When tests run in parallel, stray keys from other tests can be indexed, causing attr.Size to be non-zero unexpectedly. Adding a unique prefix (based on the index GUID) isolates each test's index from other data in the database. Signed-off-by: currantw --- .../ServerModules/FtInfoTests.cs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/tests/Valkey.Glide.IntegrationTests/ServerModules/FtInfoTests.cs b/tests/Valkey.Glide.IntegrationTests/ServerModules/FtInfoTests.cs index fff4ae90..95557df3 100644 --- a/tests/Valkey.Glide.IntegrationTests/ServerModules/FtInfoTests.cs +++ b/tests/Valkey.Glide.IntegrationTests/ServerModules/FtInfoTests.cs @@ -101,8 +101,9 @@ public async Task InfoLocalAsync_TextFieldAttributes(BaseClient client) await SkipUtils.IfSearchModuleNotLoaded(client); var index = Guid.NewGuid().ToString(); + var prefix = $"{index}:"; var field = new Ft.CreateTextField("$.title", "title") { NoStem = true, WithSuffixTrie = false }; - await Ft.CreateAsync(client, index, field); + await Ft.CreateAsync(client, index, field, new Ft.CreateOptions { Prefixes = [prefix] }); var info = await Ft.InfoLocalAsync(client, index); var attr = Assert.IsType(Assert.Single(info.Attributes)); @@ -122,8 +123,9 @@ public async Task InfoLocalAsync_TagFieldAttributes(BaseClient client) await SkipUtils.IfSearchModuleNotLoaded(client); var index = Guid.NewGuid().ToString(); + var prefix = $"{index}:"; var field = new Ft.CreateTagField("category") { Separator = '|', CaseSensitive = true }; - await Ft.CreateAsync(client, index, field); + await Ft.CreateAsync(client, index, field, new Ft.CreateOptions { Prefixes = [prefix] }); var info = await Ft.InfoLocalAsync(client, index); var attr = Assert.IsType(Assert.Single(info.Attributes)); @@ -144,8 +146,9 @@ public async Task InfoLocalAsync_NumericFieldAttributes(BaseClient client) await SkipUtils.IfSearchModuleNotLoaded(client); var index = Guid.NewGuid().ToString(); + var prefix = $"{index}:"; var field = new Ft.CreateNumericField("price"); - await Ft.CreateAsync(client, index, field); + await Ft.CreateAsync(client, index, field, new Ft.CreateOptions { Prefixes = [prefix] }); var info = await Ft.InfoLocalAsync(client, index); var attr = Assert.IsType(Assert.Single(info.Attributes)); @@ -162,13 +165,14 @@ public async Task InfoLocalAsync_VectorFieldFlatAttributes(BaseClient client) await SkipUtils.IfSearchModuleNotLoaded(client); var index = Guid.NewGuid().ToString(); + var prefix = $"{index}:"; var field = new Ft.CreateVectorFieldFlat { Identifier = "embedding", Dimensions = 128, DistanceMetric = Ft.DistanceMetric.Cosine, }; - await Ft.CreateAsync(client, index, field); + await Ft.CreateAsync(client, index, field, new Ft.CreateOptions { Prefixes = [prefix] }); var info = await Ft.InfoLocalAsync(client, index); var attr = Assert.IsType(Assert.Single(info.Attributes)); @@ -190,6 +194,7 @@ public async Task InfoLocalAsync_VectorFieldHnswAttributes(BaseClient client) await SkipUtils.IfSearchModuleNotLoaded(client); var index = Guid.NewGuid().ToString(); + var prefix = $"{index}:"; var field = new Ft.CreateVectorFieldHnsw { Identifier = "vec", @@ -199,7 +204,7 @@ public async Task InfoLocalAsync_VectorFieldHnswAttributes(BaseClient client) VectorsExaminedOnConstruction = 200, VectorsExaminedOnRuntime = 10, }; - await Ft.CreateAsync(client, index, field); + await Ft.CreateAsync(client, index, field, new Ft.CreateOptions { Prefixes = [prefix] }); var info = await Ft.InfoLocalAsync(client, index); var attr = Assert.IsType(Assert.Single(info.Attributes)); From 42c78eb56f197b78754e128d4aa8f4a22a9342e2 Mon Sep 17 00:00:00 2001 From: currantw Date: Tue, 12 May 2026 10:54:32 -0700 Subject: [PATCH 13/18] fix(test): test both TimeSpan and DateTime overloads for multi-field hash expiry HashFieldGetAndSetExpiryAsync_MultiField and HashFieldSetAndSetExpiryAsync_MultiField were calling the same TimeSpan-based overload twice. The second assertion now calls the DateTime-based overload to cover both signatures. Signed-off-by: currantw --- .../StackExchange/CommandFlagsTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Valkey.Glide.IntegrationTests/StackExchange/CommandFlagsTests.cs b/tests/Valkey.Glide.IntegrationTests/StackExchange/CommandFlagsTests.cs index 1ce5b5da..513cd240 100644 --- a/tests/Valkey.Glide.IntegrationTests/StackExchange/CommandFlagsTests.cs +++ b/tests/Valkey.Glide.IntegrationTests/StackExchange/CommandFlagsTests.cs @@ -423,7 +423,7 @@ public async Task HashFieldGetAndSetExpiryAsync_MultiField_ThrowsOnCommandFlags( _ = await Assert.ThrowsAsync( () => db.HashFieldGetAndSetExpiryAsync("key", [], flags: UnsupportedFlag)); _ = await Assert.ThrowsAsync( - () => db.HashFieldGetAndSetExpiryAsync("key", [], flags: UnsupportedFlag)); + () => db.HashFieldGetAndSetExpiryAsync("key", [], DateTime.MinValue, UnsupportedFlag)); } [Theory(DisableDiscoveryEnumeration = true)] @@ -443,7 +443,7 @@ public async Task HashFieldSetAndSetExpiryAsync_MultiField_ThrowsOnCommandFlags( _ = await Assert.ThrowsAsync( () => db.HashFieldSetAndSetExpiryAsync("key", [], flags: UnsupportedFlag)); _ = await Assert.ThrowsAsync( - () => db.HashFieldSetAndSetExpiryAsync("key", [], flags: UnsupportedFlag)); + () => db.HashFieldSetAndSetExpiryAsync("key", [], DateTime.MinValue, flags: UnsupportedFlag)); } #endregion From 885f0738925d2cc4c73131093d5931682f88d29f Mon Sep 17 00:00:00 2001 From: currantw Date: Tue, 12 May 2026 13:21:26 -0700 Subject: [PATCH 14/18] Add test for `ChannelMessageQueue.UnsubscribeAsync`. Signed-off-by: currantw --- .../StackExchange/CommandFlagsTests.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/Valkey.Glide.IntegrationTests/StackExchange/CommandFlagsTests.cs b/tests/Valkey.Glide.IntegrationTests/StackExchange/CommandFlagsTests.cs index 513cd240..cfc7f94e 100644 --- a/tests/Valkey.Glide.IntegrationTests/StackExchange/CommandFlagsTests.cs +++ b/tests/Valkey.Glide.IntegrationTests/StackExchange/CommandFlagsTests.cs @@ -1474,5 +1474,15 @@ public async Task ISubscriber_UnsubscribeAllAsync_ThrowsOnCommandFlags(Connectio () => subscriber.UnsubscribeAllAsync(UnsupportedFlag)); } + [Theory(DisableDiscoveryEnumeration = true)] + [MemberData(nameof(TestConfiguration.TestStandaloneConnections), MemberType = typeof(TestConfiguration))] + public async Task ChannelMessageQueue_UnsubscribeAsync_ThrowsOnCommandFlags(ConnectionMultiplexer connection) + { + var subscriber = connection.GetSubscriber(); + var queue = await subscriber.SubscribeAsync(ValkeyChannel.Literal(Guid.NewGuid().ToString())); + _ = await Assert.ThrowsAsync( + () => queue.UnsubscribeAsync(UnsupportedFlag)); + } + #endregion } From 9fb4b09c208bdd245623c8edb1429c0dcd1442e3 Mon Sep 17 00:00:00 2001 From: currantw Date: Tue, 12 May 2026 13:56:21 -0700 Subject: [PATCH 15/18] Use compatible connection for subscriber Signed-off-by: currantw --- .../StackExchange/CommandFlagsTests.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/Valkey.Glide.IntegrationTests/StackExchange/CommandFlagsTests.cs b/tests/Valkey.Glide.IntegrationTests/StackExchange/CommandFlagsTests.cs index cfc7f94e..d745404b 100644 --- a/tests/Valkey.Glide.IntegrationTests/StackExchange/CommandFlagsTests.cs +++ b/tests/Valkey.Glide.IntegrationTests/StackExchange/CommandFlagsTests.cs @@ -1474,11 +1474,10 @@ public async Task ISubscriber_UnsubscribeAllAsync_ThrowsOnCommandFlags(Connectio () => subscriber.UnsubscribeAllAsync(UnsupportedFlag)); } - [Theory(DisableDiscoveryEnumeration = true)] - [MemberData(nameof(TestConfiguration.TestStandaloneConnections), MemberType = typeof(TestConfiguration))] - public async Task ChannelMessageQueue_UnsubscribeAsync_ThrowsOnCommandFlags(ConnectionMultiplexer connection) + [Fact] + public async Task ChannelMessageQueue_UnsubscribeAsync_ThrowsOnCommandFlags() { - var subscriber = connection.GetSubscriber(); + var subscriber = TestConfiguration.DefaultCompatibleConnection().GetSubscriber(); var queue = await subscriber.SubscribeAsync(ValkeyChannel.Literal(Guid.NewGuid().ToString())); _ = await Assert.ThrowsAsync( () => queue.UnsubscribeAsync(UnsupportedFlag)); From f926102fe36799cd5f982f41d552b17d460ab5bb Mon Sep 17 00:00:00 2001 From: currantw Date: Tue, 12 May 2026 14:30:32 -0700 Subject: [PATCH 16/18] Consolidate methods and remove unneccessary lambdas Signed-off-by: currantw --- .../ClientSideCacheTests.cs | 27 +++---------------- 1 file changed, 3 insertions(+), 24 deletions(-) diff --git a/tests/Valkey.Glide.IntegrationTests/ClientSideCacheTests.cs b/tests/Valkey.Glide.IntegrationTests/ClientSideCacheTests.cs index b8120f74..2a2418ba 100644 --- a/tests/Valkey.Glide.IntegrationTests/ClientSideCacheTests.cs +++ b/tests/Valkey.Glide.IntegrationTests/ClientSideCacheTests.cs @@ -11,31 +11,10 @@ public class ClientSideCacheTests { #region Helpers - private static async Task CreateStandaloneClientWithCache(ClientSideCacheConfig cache) - { - var config = TestConfiguration.DefaultClientConfig() - .WithClientSideCache(cache) - .Build(); - return await GlideClient.CreateClient(config); - } - - private static async Task CreateClusterClientWithCache(ClientSideCacheConfig cache) - { - var config = TestConfiguration.DefaultClusterClientConfig() - .WithClientSideCache(cache) - .Build(); - return await GlideClusterClient.CreateClient(config); - } - private static async Task CreateClientWithCache(ClientSideCacheConfig cache, bool clusterMode) - { - if (clusterMode) - { - return await CreateClusterClientWithCache(cache); - } - - return await CreateStandaloneClientWithCache(cache); - } + => clusterMode + ? await GlideClusterClient.CreateClient(TestConfiguration.DefaultClusterClientConfig().WithClientSideCache(cache).Build()) + : await GlideClient.CreateClient(TestConfiguration.DefaultClientConfig().WithClientSideCache(cache).Build()); #endregion From 6668f4bb552e54fe19cf6cc625e4f21a8a3c1ad9 Mon Sep 17 00:00:00 2001 From: currantw Date: Tue, 12 May 2026 14:32:00 -0700 Subject: [PATCH 17/18] Consolidate methods and remove unneccessary lambdas Signed-off-by: currantw --- .../ClientSideCacheTests.cs | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/Valkey.Glide.IntegrationTests/ClientSideCacheTests.cs b/tests/Valkey.Glide.IntegrationTests/ClientSideCacheTests.cs index 2a2418ba..8b13fec9 100644 --- a/tests/Valkey.Glide.IntegrationTests/ClientSideCacheTests.cs +++ b/tests/Valkey.Glide.IntegrationTests/ClientSideCacheTests.cs @@ -91,11 +91,11 @@ public async Task WithoutMetrics_MetricCallsFail(bool clusterMode) Assert.Equal("value", value.ToString()); // Metrics that require EnableMetrics should fail - _ = await Assert.ThrowsAsync(() => client.GetCacheHitRateAsync()); - _ = await Assert.ThrowsAsync(() => client.GetCacheMissRateAsync()); - _ = await Assert.ThrowsAsync(() => client.GetCacheEvictionsAsync()); - _ = await Assert.ThrowsAsync(() => client.GetCacheExpirationsAsync()); - _ = await Assert.ThrowsAsync(() => client.GetCacheTotalLookupsAsync()); + _ = await Assert.ThrowsAsync(client.GetCacheHitRateAsync); + _ = await Assert.ThrowsAsync(client.GetCacheMissRateAsync); + _ = await Assert.ThrowsAsync(client.GetCacheEvictionsAsync); + _ = await Assert.ThrowsAsync(client.GetCacheExpirationsAsync); + _ = await Assert.ThrowsAsync(client.GetCacheTotalLookupsAsync); // Entry count should still work (doesn't require metrics) long entryCount = await client.GetCacheEntryCountAsync(); @@ -111,12 +111,12 @@ public async Task WithoutMetrics_MetricCallsFail(bool clusterMode) public async Task NoCacheConfigured_AllMetricsFail(BaseClient client) { // Without cache configured, all metric calls should fail - _ = await Assert.ThrowsAsync(() => client.GetCacheHitRateAsync()); - _ = await Assert.ThrowsAsync(() => client.GetCacheMissRateAsync()); - _ = await Assert.ThrowsAsync(() => client.GetCacheEntryCountAsync()); - _ = await Assert.ThrowsAsync(() => client.GetCacheEvictionsAsync()); - _ = await Assert.ThrowsAsync(() => client.GetCacheExpirationsAsync()); - _ = await Assert.ThrowsAsync(() => client.GetCacheTotalLookupsAsync()); + _ = await Assert.ThrowsAsync(client.GetCacheHitRateAsync); + _ = await Assert.ThrowsAsync(client.GetCacheMissRateAsync); + _ = await Assert.ThrowsAsync(client.GetCacheEntryCountAsync); + _ = await Assert.ThrowsAsync(client.GetCacheEvictionsAsync); + _ = await Assert.ThrowsAsync(client.GetCacheExpirationsAsync); + _ = await Assert.ThrowsAsync(client.GetCacheTotalLookupsAsync); } #endregion From 36e93c809363aa475d019533808fb2e160d66b6b Mon Sep 17 00:00:00 2001 From: currantw Date: Tue, 12 May 2026 14:33:51 -0700 Subject: [PATCH 18/18] Fix unrelated flaky tests Signed-off-by: currantw --- .../ClientSideCacheTests.cs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/tests/Valkey.Glide.IntegrationTests/ClientSideCacheTests.cs b/tests/Valkey.Glide.IntegrationTests/ClientSideCacheTests.cs index 8b13fec9..be4c60b6 100644 --- a/tests/Valkey.Glide.IntegrationTests/ClientSideCacheTests.cs +++ b/tests/Valkey.Glide.IntegrationTests/ClientSideCacheTests.cs @@ -252,9 +252,8 @@ public async Task EvictionPolicyLRU(bool clusterMode) Assert.Equal(largeValue, value.ToString()); } - // Verify 2 evictions occurred - long evictions = await client.GetCacheEvictionsAsync(); - Assert.Equal(2, evictions); + // Verify at least 2 evictions occurred (possibly more due to internal overhead) + Assert.True(await client.GetCacheEvictionsAsync() >= 2); // Verify cache is working (hit rate > 0) double hitRate = await client.GetCacheHitRateAsync(); @@ -335,9 +334,8 @@ public async Task EvictionPolicyLFU(bool clusterMode) entryCount = await client.GetCacheEntryCountAsync(); Assert.Equal(3, entryCount); - // Verify 1 eviction occurred - long evictions = await client.GetCacheEvictionsAsync(); - Assert.Equal(1, evictions); + // Verify at least 1 eviction occurred (possibly more due to internal overhead) + Assert.True(await client.GetCacheEvictionsAsync() >= 1); // Check that key1 (highest frequency) is still cached double oldHitRate = await client.GetCacheHitRateAsync();