From 4e8431ea85a318e9f3d5ed92a4ec4044b89a5df0 Mon Sep 17 00:00:00 2001 From: shacharPash <93581407+shacharPash@users.noreply.github.com> Date: Tue, 24 May 2022 18:31:43 +0300 Subject: [PATCH] Support COMMAND [...] (#2143) Add support for `COMMAND` commands (part of #2055): COMMAND COUNT - https://redis.io/commands/command-count/ COMMAND GETKEYS - https://redis.io/commands/command-getkeys/ COMMAND LIST - https://redis.io/commands/command-list/ Co-authored-by: Nick Craver --- docs/ReleaseNotes.md | 2 + .../APITypes/LCSMatchResult.cs | 5 ++ src/StackExchange.Redis/Enums/RedisCommand.cs | 2 + src/StackExchange.Redis/Interfaces/IServer.cs | 35 +++++++++ src/StackExchange.Redis/Message.cs | 1 + src/StackExchange.Redis/PublicAPI.Shipped.txt | 7 ++ src/StackExchange.Redis/RawResult.cs | 3 + src/StackExchange.Redis/RedisDatabase.cs | 24 +++--- src/StackExchange.Redis/RedisLiterals.cs | 5 ++ src/StackExchange.Redis/RedisServer.cs | 73 +++++++++++++++++++ src/StackExchange.Redis/ResultProcessor.cs | 20 ++++- tests/StackExchange.Redis.Tests/Databases.cs | 63 +++++++++++++++- tests/StackExchange.Redis.Tests/Strings.cs | 4 +- 13 files changed, 227 insertions(+), 17 deletions(-) diff --git a/docs/ReleaseNotes.md b/docs/ReleaseNotes.md index 001c8104f..e92a4084d 100644 --- a/docs/ReleaseNotes.md +++ b/docs/ReleaseNotes.md @@ -34,6 +34,8 @@ - Adds: Support for `BIT | BYTE` to `BITCOUNT` and `BITPOS` with `.StringBitCount()`/`.StringBitCountAsync()` and `.StringBitPosition()`/`.StringBitPositionAsync()` [#2116 by Avital-Fine](https://github.com/StackExchange/StackExchange.Redis/pull/2116)) - Adds: Support for pub/sub payloads that are unary arrays ([#2118 by Marc Gravell](https://github.com/StackExchange/StackExchange.Redis/pull/2118)) - Fix: Sentinel timer race during dispose ([#2133 by ewisuri](https://github.com/StackExchange/StackExchange.Redis/pull/2133)) +- Adds: Support for `COMMAND COUNT`, `COMMAND GETKEYS`, and `COMMAND LIST`, with `.CommandCount()`/`.CommandCountAsync()`, `.CommandGetKeys()`/`.CommandGetKeysAsync()`, and `.CommandList()`/`.CommandListAsync()` ([#2143 by shacharPash](https://github.com/StackExchange/StackExchange.Redis/pull/2143)) + ## 2.5.61 diff --git a/src/StackExchange.Redis/APITypes/LCSMatchResult.cs b/src/StackExchange.Redis/APITypes/LCSMatchResult.cs index 97ff8c1cd..fdeea89ff 100644 --- a/src/StackExchange.Redis/APITypes/LCSMatchResult.cs +++ b/src/StackExchange.Redis/APITypes/LCSMatchResult.cs @@ -10,6 +10,11 @@ public readonly struct LCSMatchResult { internal static LCSMatchResult Null { get; } = new LCSMatchResult(Array.Empty(), 0); + /// + /// Whether this match result contains any matches. + /// + public bool IsEmpty => LongestMatchLength == 0 && (Matches is null || Matches.Length == 0); + /// /// The matched positions of all the sub-matched strings. /// diff --git a/src/StackExchange.Redis/Enums/RedisCommand.cs b/src/StackExchange.Redis/Enums/RedisCommand.cs index 8587ceedf..6eded0e78 100644 --- a/src/StackExchange.Redis/Enums/RedisCommand.cs +++ b/src/StackExchange.Redis/Enums/RedisCommand.cs @@ -23,6 +23,7 @@ internal enum RedisCommand CLUSTER, CONFIG, COPY, + COMMAND, DBSIZE, DEBUG, @@ -361,6 +362,7 @@ internal static bool IsPrimaryOnly(this RedisCommand command) case RedisCommand.BITPOS: case RedisCommand.CLIENT: case RedisCommand.CLUSTER: + case RedisCommand.COMMAND: case RedisCommand.CONFIG: case RedisCommand.DBSIZE: case RedisCommand.DEBUG: diff --git a/src/StackExchange.Redis/Interfaces/IServer.cs b/src/StackExchange.Redis/Interfaces/IServer.cs index 3d9eb8065..7319f7feb 100644 --- a/src/StackExchange.Redis/Interfaces/IServer.cs +++ b/src/StackExchange.Redis/Interfaces/IServer.cs @@ -179,6 +179,41 @@ public partial interface IServer : IRedis /// Task ConfigSetAsync(RedisValue setting, RedisValue value, CommandFlags flags = CommandFlags.None); + /// + /// Returns the number of total commands available in this Redis server. + /// + /// The command flags to use. + /// + long CommandCount(CommandFlags flags = CommandFlags.None); + + /// + Task CommandCountAsync(CommandFlags flags = CommandFlags.None); + + /// + /// Returns list of keys from a full Redis command. + /// + /// The command to get keys from. + /// The command flags to use. + /// + RedisKey[] CommandGetKeys(RedisValue[] command, CommandFlags flags = CommandFlags.None); + + /// + Task CommandGetKeysAsync(RedisValue[] command, CommandFlags flags = CommandFlags.None); + + /// + /// Returns a list of command names available on this Redis server. + /// Only one of the filter options is usable at a time. + /// + /// The module name to filter the command list by. + /// The category to filter the command list by. + /// The pattern to filter the command list by. + /// The command flags to use. + /// + string[] CommandList(RedisValue? moduleName = null, RedisValue? category = null, RedisValue? pattern = null, CommandFlags flags = CommandFlags.None); + + /// + Task CommandListAsync(RedisValue? moduleName = null, RedisValue? category = null, RedisValue? pattern = null, CommandFlags flags = CommandFlags.None); + /// /// Return the number of keys in the database. /// diff --git a/src/StackExchange.Redis/Message.cs b/src/StackExchange.Redis/Message.cs index f44a395d5..e9c35925a 100644 --- a/src/StackExchange.Redis/Message.cs +++ b/src/StackExchange.Redis/Message.cs @@ -473,6 +473,7 @@ internal static bool RequiresDatabase(RedisCommand command) case RedisCommand.BGSAVE: case RedisCommand.CLIENT: case RedisCommand.CLUSTER: + case RedisCommand.COMMAND: case RedisCommand.CONFIG: case RedisCommand.DISCARD: case RedisCommand.ECHO: diff --git a/src/StackExchange.Redis/PublicAPI.Shipped.txt b/src/StackExchange.Redis/PublicAPI.Shipped.txt index 0b4924949..9f7168a51 100644 --- a/src/StackExchange.Redis/PublicAPI.Shipped.txt +++ b/src/StackExchange.Redis/PublicAPI.Shipped.txt @@ -980,6 +980,12 @@ StackExchange.Redis.IServer.ConfigRewrite(StackExchange.Redis.CommandFlags flags StackExchange.Redis.IServer.ConfigRewriteAsync(StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IServer.ConfigSet(StackExchange.Redis.RedisValue setting, StackExchange.Redis.RedisValue value, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> void StackExchange.Redis.IServer.ConfigSetAsync(StackExchange.Redis.RedisValue setting, StackExchange.Redis.RedisValue value, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! +StackExchange.Redis.IServer.CommandCount(StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long +StackExchange.Redis.IServer.CommandCountAsync(StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! +StackExchange.Redis.IServer.CommandGetKeys(StackExchange.Redis.RedisValue[]! command, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.RedisKey[]! +StackExchange.Redis.IServer.CommandGetKeysAsync(StackExchange.Redis.RedisValue[]! command, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! +StackExchange.Redis.IServer.CommandList(StackExchange.Redis.RedisValue? moduleName = null, StackExchange.Redis.RedisValue? category = null, StackExchange.Redis.RedisValue? pattern = null, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> string![]! +StackExchange.Redis.IServer.CommandListAsync(StackExchange.Redis.RedisValue? moduleName = null, StackExchange.Redis.RedisValue? category = null, StackExchange.Redis.RedisValue? pattern = null, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IServer.DatabaseCount.get -> int StackExchange.Redis.IServer.DatabaseSize(int database = -1, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long StackExchange.Redis.IServer.DatabaseSizeAsync(int database = -1, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! @@ -1125,6 +1131,7 @@ StackExchange.Redis.LoadedLuaScript.ExecutableScript.get -> string! StackExchange.Redis.LoadedLuaScript.Hash.get -> byte[]! StackExchange.Redis.LoadedLuaScript.OriginalScript.get -> string! StackExchange.Redis.LCSMatchResult +StackExchange.Redis.LCSMatchResult.IsEmpty.get -> bool StackExchange.Redis.LCSMatchResult.LCSMatchResult() -> void StackExchange.Redis.LCSMatchResult.LongestMatchLength.get -> long StackExchange.Redis.LCSMatchResult.Matches.get -> StackExchange.Redis.LCSMatchResult.LCSMatch[]! diff --git a/src/StackExchange.Redis/RawResult.cs b/src/StackExchange.Redis/RawResult.cs index 11ca450af..8a4f4cf88 100644 --- a/src/StackExchange.Redis/RawResult.cs +++ b/src/StackExchange.Redis/RawResult.cs @@ -265,6 +265,9 @@ internal bool GetBoolean() [MethodImpl(MethodImplOptions.AggressiveInlining)] internal string?[]? GetItemsAsStrings() => this.ToArray((in RawResult x) => (string?)x.AsRedisValue()); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal string[]? GetItemsAsStringsNotNullable() => this.ToArray((in RawResult x) => (string)x.AsRedisValue()!); + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal bool[]? GetItemsAsBooleans() => this.ToArray((in RawResult x) => (bool)x.AsRedisValue()); diff --git a/src/StackExchange.Redis/RedisDatabase.cs b/src/StackExchange.Redis/RedisDatabase.cs index 7a4e7af98..e713eb80e 100644 --- a/src/StackExchange.Redis/RedisDatabase.cs +++ b/src/StackExchange.Redis/RedisDatabase.cs @@ -116,7 +116,7 @@ public Task GeoRemoveAsync(RedisKey key, RedisValue member, CommandFlags f var redisValues = new RedisValue[members.Length]; for (var i = 0; i < members.Length; i++) redisValues[i] = members[i]; var msg = Message.Create(Database, flags, RedisCommand.GEOHASH, key, redisValues); - return ExecuteSync(msg, ResultProcessor.StringArray, defaultValue: Array.Empty()); + return ExecuteSync(msg, ResultProcessor.NullableStringArray, defaultValue: Array.Empty()); } public Task GeoHashAsync(RedisKey key, RedisValue[] members, CommandFlags flags = CommandFlags.None) @@ -125,7 +125,7 @@ public Task GeoRemoveAsync(RedisKey key, RedisValue member, CommandFlags f var redisValues = new RedisValue[members.Length]; for (var i = 0; i < members.Length; i++) redisValues[i] = members[i]; var msg = Message.Create(Database, flags, RedisCommand.GEOHASH, key, redisValues); - return ExecuteAsync(msg, ResultProcessor.StringArray, defaultValue: Array.Empty()); + return ExecuteAsync(msg, ResultProcessor.NullableStringArray, defaultValue: Array.Empty()); } public string? GeoHash(RedisKey key, RedisValue member, CommandFlags flags = CommandFlags.None) @@ -829,37 +829,37 @@ public Task KeyExistsAsync(RedisKey[] keys, CommandFlags flags = CommandFl return ExecuteAsync(msg, ResultProcessor.Int64); } - public bool KeyExpire(RedisKey key, TimeSpan? expiry, CommandFlags flags) => + public bool KeyExpire(RedisKey key, TimeSpan? expiry, CommandFlags flags = CommandFlags.None) => KeyExpire(key, expiry, ExpireWhen.Always, flags); - public bool KeyExpire(RedisKey key, DateTime? expiry, CommandFlags flags) => + public bool KeyExpire(RedisKey key, DateTime? expiry, CommandFlags flags = CommandFlags.None) => KeyExpire(key, expiry, ExpireWhen.Always, flags); - public bool KeyExpire(RedisKey key, TimeSpan? expiry, ExpireWhen when = ExpireWhen.Always, CommandFlags flags = CommandFlags.None) + public bool KeyExpire(RedisKey key, TimeSpan? expiry, ExpireWhen when, CommandFlags flags = CommandFlags.None) { var msg = GetExpiryMessage(key, flags, expiry, when, out ServerEndPoint? server); return ExecuteSync(msg, ResultProcessor.Boolean, server: server); } - public bool KeyExpire(RedisKey key, DateTime? expiry, ExpireWhen when = ExpireWhen.Always, CommandFlags flags = CommandFlags.None) + public bool KeyExpire(RedisKey key, DateTime? expiry, ExpireWhen when, CommandFlags flags = CommandFlags.None) { var msg = GetExpiryMessage(key, flags, expiry, when, out ServerEndPoint? server); return ExecuteSync(msg, ResultProcessor.Boolean, server: server); } - public Task KeyExpireAsync(RedisKey key, TimeSpan? expiry, CommandFlags flags) => + public Task KeyExpireAsync(RedisKey key, TimeSpan? expiry, CommandFlags flags = CommandFlags.None) => KeyExpireAsync(key, expiry, ExpireWhen.Always, flags); - public Task KeyExpireAsync(RedisKey key, DateTime? expiry, CommandFlags flags) => + public Task KeyExpireAsync(RedisKey key, DateTime? expiry, CommandFlags flags = CommandFlags.None) => KeyExpireAsync(key, expiry, ExpireWhen.Always, flags); - public Task KeyExpireAsync(RedisKey key, TimeSpan? expiry, ExpireWhen when = ExpireWhen.Always, CommandFlags flags = CommandFlags.None) + public Task KeyExpireAsync(RedisKey key, TimeSpan? expiry, ExpireWhen when, CommandFlags flags = CommandFlags.None) { var msg = GetExpiryMessage(key, flags, expiry, when, out ServerEndPoint? server); return ExecuteAsync(msg, ResultProcessor.Boolean, server: server); } - public Task KeyExpireAsync(RedisKey key, DateTime? expire, ExpireWhen when = ExpireWhen.Always, CommandFlags flags = CommandFlags.None) + public Task KeyExpireAsync(RedisKey key, DateTime? expire, ExpireWhen when, CommandFlags flags = CommandFlags.None) { var msg = GetExpiryMessage(key, flags, expire, when, out ServerEndPoint? server); return ExecuteAsync(msg, ResultProcessor.Boolean, server: server); @@ -1470,13 +1470,13 @@ public Task StringLongestCommonSubsequenceLengthAsync(RedisKey key1, Redis public LCSMatchResult StringLongestCommonSubsequenceWithMatches(RedisKey key1, RedisKey key2, long minSubMatchLength = 0, CommandFlags flags = CommandFlags.None) { var msg = Message.Create(Database, flags, RedisCommand.LCS, key1, key2, RedisLiterals.IDX, RedisLiterals.MINMATCHLEN, minSubMatchLength, RedisLiterals.WITHMATCHLEN); - return ExecuteSync(msg, ResultProcessor.LCSMatchResult, defaultValue: LCSMatchResult.Null); + return ExecuteSync(msg, ResultProcessor.LCSMatchResult); } public Task StringLongestCommonSubsequenceWithMatchesAsync(RedisKey key1, RedisKey key2, long minSubMatchLength = 0, CommandFlags flags = CommandFlags.None) { var msg = Message.Create(Database, flags, RedisCommand.LCS, key1, key2, RedisLiterals.IDX, RedisLiterals.MINMATCHLEN, minSubMatchLength, RedisLiterals.WITHMATCHLEN); - return ExecuteAsync(msg, ResultProcessor.LCSMatchResult, defaultValue: LCSMatchResult.Null); + return ExecuteAsync(msg, ResultProcessor.LCSMatchResult); } public long Publish(RedisChannel channel, RedisValue message, CommandFlags flags = CommandFlags.None) diff --git a/src/StackExchange.Redis/RedisLiterals.cs b/src/StackExchange.Redis/RedisLiterals.cs index 846969d15..9747a141a 100644 --- a/src/StackExchange.Redis/RedisLiterals.cs +++ b/src/StackExchange.Redis/RedisLiterals.cs @@ -41,6 +41,7 @@ internal static class RedisLiterals // unlike primary commands, these do not get altered by the command-map; we may as // well compute the bytes once and share them public static readonly RedisValue + ACLCAT = "ACLCAT", ADDR = "ADDR", AFTER = "AFTER", AGGREGATE = "AGGREGATE", @@ -64,9 +65,11 @@ public static readonly RedisValue EX = "EX", EXAT = "EXAT", EXISTS = "EXISTS", + FILTERBY = "FILTERBY", FLUSH = "FLUSH", FREQ = "FREQ", GET = "GET", + GETKEYS = "GETKEYS", GETNAME = "GETNAME", GT = "GT", HISTORY = "HISTORY", @@ -88,6 +91,7 @@ public static readonly RedisValue MAXLEN = "MAXLEN", MIN = "MIN", MINMATCHLEN = "MINMATCHLEN", + MODULE = "MODULE", NODES = "NODES", NOSAVE = "NOSAVE", NOT = "NOT", @@ -96,6 +100,7 @@ public static readonly RedisValue NX = "NX", OBJECT = "OBJECT", OR = "OR", + PATTERN = "PATTERN", PAUSE = "PAUSE", PERSIST = "PERSIST", PING = "PING", diff --git a/src/StackExchange.Redis/RedisServer.cs b/src/StackExchange.Redis/RedisServer.cs index cf094d1fd..dea6d586d 100644 --- a/src/StackExchange.Redis/RedisServer.cs +++ b/src/StackExchange.Redis/RedisServer.cs @@ -204,6 +204,79 @@ public Task ConfigSetAsync(RedisValue setting, RedisValue value, CommandFlags fl return task; } + public long CommandCount(CommandFlags flags = CommandFlags.None) + { + var msg = Message.Create(-1, flags, RedisCommand.COMMAND, RedisLiterals.COUNT); + return ExecuteSync(msg, ResultProcessor.Int64); + } + + public Task CommandCountAsync(CommandFlags flags = CommandFlags.None) + { + var msg = Message.Create(-1, flags, RedisCommand.COMMAND, RedisLiterals.COUNT); + return ExecuteAsync(msg, ResultProcessor.Int64); + } + + public RedisKey[] CommandGetKeys(RedisValue[] command, CommandFlags flags = CommandFlags.None) + { + var msg = Message.Create(-1, flags, RedisCommand.COMMAND, AddValueToArray(RedisLiterals.GETKEYS, command)); + return ExecuteSync(msg, ResultProcessor.RedisKeyArray, defaultValue: Array.Empty()); + } + + public Task CommandGetKeysAsync(RedisValue[] command, CommandFlags flags = CommandFlags.None) + { + var msg = Message.Create(-1, flags, RedisCommand.COMMAND, AddValueToArray(RedisLiterals.GETKEYS, command)); + return ExecuteAsync(msg, ResultProcessor.RedisKeyArray, defaultValue: Array.Empty()); + } + + public string[] CommandList(RedisValue? moduleName = null, RedisValue? category = null, RedisValue? pattern = null, CommandFlags flags = CommandFlags.None) + { + var msg = GetCommandListMessage(moduleName, category, pattern, flags); + return ExecuteSync(msg, ResultProcessor.StringArray, defaultValue: Array.Empty()); + } + + public Task CommandListAsync(RedisValue? moduleName = null, RedisValue? category = null, RedisValue? pattern = null, CommandFlags flags = CommandFlags.None) + { + var msg = GetCommandListMessage(moduleName, category, pattern, flags); + return ExecuteAsync(msg, ResultProcessor.StringArray, defaultValue: Array.Empty()); + } + + private Message GetCommandListMessage(RedisValue? moduleName = null, RedisValue? category = null, RedisValue? pattern = null, CommandFlags flags = CommandFlags.None) + { + if (moduleName == null && category == null && pattern == null) + { + return Message.Create(-1, flags, RedisCommand.COMMAND, RedisLiterals.LIST); + } + + else if (moduleName != null && category == null && pattern == null) + { + return Message.Create(-1, flags, RedisCommand.COMMAND, MakeArray(RedisLiterals.LIST, RedisLiterals.FILTERBY, RedisLiterals.MODULE, (RedisValue)moduleName)); + } + + else if (moduleName == null && category != null && pattern == null) + { + return Message.Create(-1, flags, RedisCommand.COMMAND, MakeArray(RedisLiterals.LIST, RedisLiterals.FILTERBY, RedisLiterals.ACLCAT, (RedisValue)category)); + } + + else if (moduleName == null && category == null && pattern != null) + { + return Message.Create(-1, flags, RedisCommand.COMMAND, MakeArray(RedisLiterals.LIST, RedisLiterals.FILTERBY, RedisLiterals.PATTERN, (RedisValue)pattern)); + } + + else + throw new ArgumentException("More then one filter is not allwed"); + } + + private RedisValue[] AddValueToArray(RedisValue val, RedisValue[] arr) + { + var result = new RedisValue[arr.Length + 1]; + var i = 0; + result[i++] = val; + foreach (var item in arr) result[i++] = item; + return result; + } + + private RedisValue[] MakeArray(params RedisValue[] redisValues) { return redisValues; } + public long DatabaseSize(int database = -1, CommandFlags flags = CommandFlags.None) { var msg = Message.Create(multiplexer.ApplyDefaultDatabase(database), flags, RedisCommand.DBSIZE); diff --git a/src/StackExchange.Redis/ResultProcessor.cs b/src/StackExchange.Redis/ResultProcessor.cs index 8b15a8762..17b6d383e 100644 --- a/src/StackExchange.Redis/ResultProcessor.cs +++ b/src/StackExchange.Redis/ResultProcessor.cs @@ -92,6 +92,9 @@ public static readonly ResultProcessor Int64Array = new Int64ArrayProcessor(); public static readonly ResultProcessor + NullableStringArray = new NullableStringArrayProcessor(); + + public static readonly ResultProcessor StringArray = new StringArrayProcessor(); public static readonly ResultProcessor @@ -1394,7 +1397,7 @@ protected override bool SetResultCore(PhysicalConnection connection, Message mes } } - private sealed class StringArrayProcessor : ResultProcessor + private sealed class NullableStringArrayProcessor : ResultProcessor { protected override bool SetResultCore(PhysicalConnection connection, Message message, in RawResult result) { @@ -1410,6 +1413,21 @@ protected override bool SetResultCore(PhysicalConnection connection, Message mes } } + private sealed class StringArrayProcessor : ResultProcessor + { + protected override bool SetResultCore(PhysicalConnection connection, Message message, in RawResult result) + { + switch (result.Type) + { + case ResultType.MultiBulk: + var arr = result.GetItemsAsStringsNotNullable()!; + SetResult(message, arr); + return true; + } + return false; + } + } + private sealed class BooleanArrayProcessor : ResultProcessor { protected override bool SetResultCore(PhysicalConnection connection, Message message, in RawResult result) diff --git a/tests/StackExchange.Redis.Tests/Databases.cs b/tests/StackExchange.Redis.Tests/Databases.cs index 1dfffb4e0..bacce1cd7 100644 --- a/tests/StackExchange.Redis.Tests/Databases.cs +++ b/tests/StackExchange.Redis.Tests/Databases.cs @@ -1,4 +1,5 @@ -using System.Threading.Tasks; +using System; +using System.Threading.Tasks; using Xunit; using Xunit.Abstractions; @@ -7,7 +8,65 @@ namespace StackExchange.Redis.Tests; [Collection(SharedConnectionFixture.Key)] public class Databases : TestBase { - public Databases(ITestOutputHelper output, SharedConnectionFixture fixture) : base (output, fixture) { } + public Databases(ITestOutputHelper output, SharedConnectionFixture fixture) : base(output, fixture) { } + + [Fact] + public async Task CommandCount() + { + using var conn = Create(); + var server = GetAnyPrimary(conn); + var count = server.CommandCount(); + Assert.True(count > 100); + + count = await server.CommandCountAsync(); + Assert.True(count > 100); + } + + [Fact] + public async Task CommandGetKeys() + { + using var conn = Create(); + var server = GetAnyPrimary(conn); + + RedisValue[] command = { "MSET", "a", "b", "c", "d", "e", "f" }; + + RedisKey[] keys = server.CommandGetKeys(command); + RedisKey[] expected = { "a", "c", "e" }; + Assert.Equal(keys, expected); + + keys = await server.CommandGetKeysAsync(command); + Assert.Equal(keys, expected); + } + + [Fact] + public async Task CommandList() + { + using var conn = Create(require: RedisFeatures.v7_0_0_rc1); + var server = GetAnyPrimary(conn); + + var commands = server.CommandList(); + Assert.True(commands.Length > 100); + commands = await server.CommandListAsync(); + Assert.True(commands.Length > 100); + + commands = server.CommandList(moduleName: "JSON"); + Assert.Empty(commands); + commands = await server.CommandListAsync(moduleName: "JSON"); + Assert.Empty(commands); + + commands = server.CommandList(category: "admin"); + Assert.True(commands.Length > 10); + commands = await server.CommandListAsync(category: "admin"); + Assert.True(commands.Length > 10); + + commands = server.CommandList(pattern: "a*"); + Assert.True(commands.Length > 10); + commands = await server.CommandListAsync(pattern: "a*"); + Assert.True(commands.Length > 10); + + Assert.Throws(() => server.CommandList(moduleName: "JSON", pattern: "a*")); + await Assert.ThrowsAsync(() => server.CommandListAsync(moduleName: "JSON", pattern: "a*")); + } [Fact] public async Task CountKeys() diff --git a/tests/StackExchange.Redis.Tests/Strings.cs b/tests/StackExchange.Redis.Tests/Strings.cs index 54dfcce0d..034036703 100644 --- a/tests/StackExchange.Redis.Tests/Strings.cs +++ b/tests/StackExchange.Redis.Tests/Strings.cs @@ -702,7 +702,7 @@ public void LongestCommonSubsequence() // Default value stringMatchResult = db.StringLongestCommonSubsequenceWithMatches(key1, key2, flags: CommandFlags.FireAndForget); - Assert.Equal(stringMatchResult, LCSMatchResult.Null); + Assert.True(stringMatchResult.IsEmpty); } [Fact] @@ -742,7 +742,7 @@ public async Task LongestCommonSubsequenceAsync() // Default value stringMatchResult = await db.StringLongestCommonSubsequenceWithMatchesAsync(key1, key2, flags: CommandFlags.FireAndForget); - Assert.Equal(stringMatchResult, LCSMatchResult.Null); + Assert.True(stringMatchResult.IsEmpty); } private static byte[] Encode(string value) => Encoding.UTF8.GetBytes(value);