CSRedisCore.Tests
Resp3HelperTests.cs
using CSRedis.Internal;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Xunit;
namespace CSRedisCore.Tests {
public clast Resp3HelperTests {
public clast RedisSocket : IDisposable
{
Socket _socket;
public NetworkStream Stream { get; }
public RedisSocket(Socket socket)
{
_socket = socket;
Stream = new NetworkStream(_socket, true);
}
public void Dispose()
{
_socket.Shutdown(SocketShutdown.Both);
_socket.Close();
_socket.Dispose();
}
public static RedisSocket GetRedisSocket()
{
var endpoint = new IPEndPoint(IPAddress.Parse("192.168.164.10"), 6379);
var _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_socket.Connect(endpoint);
return new RedisSocket(_socket);
}
}
static object[] PrepareCmd(string cmd, string subcmd = null, params object[] parms)
{
if (string.IsNullOrWhiteSpace(cmd)) throw new ArgumentNullException("Redis command not is null or empty.");
object[] args = null;
if (parms?.Any() != true)
{
if (string.IsNullOrWhiteSpace(subcmd) == false) args = new object[] { cmd, subcmd };
else args = cmd.Split(' ').Where(a => string.IsNullOrWhiteSpace(a) == false).ToArray();
}
else
{
var issubcmd = string.IsNullOrWhiteSpace(subcmd) == false;
args = new object[parms.Length + 1 + (issubcmd ? 1 : 0)];
var argsIdx = 0;
args[argsIdx++] = cmd;
if (issubcmd) args[argsIdx++] = subcmd;
foreach (var prm in parms) args[argsIdx++] = prm;
}
return args;
}
static Resp3Helper.ReadResult ExecCmd(string cmd, string subcmd = null, params object[] parms)
{
var args = PrepareCmd(cmd, subcmd, parms);
using (var rds = RedisSocket.GetRedisSocket())
{
Resp3Helper.Write(rds.Stream, args, true);
var rt = Resp3Helper.Read(rds.Stream);
return rt;
}
}
static ExecCmdListenResult ExecCmdListen(Action ondata, string cmd, string subcmd = null, params object[] parms)
{
var args = PrepareCmd(cmd, subcmd, parms);
var rds = RedisSocket.GetRedisSocket();
Resp3Helper.Write(rds.Stream, args, true);
var rd = Resp3Helper.Read(rds.Stream);
var rt = new ExecCmdListenResult { rds = rds };
new Thread(() =>
{
ondata?.Invoke(rt, rd.Value);
while (rt._running)
{
try
{
ondata?.Invoke(rt, Resp3Helper.Read(rds.Stream).Value);
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}).Start();
return rt;
}
public clast ExecCmdListenResult : IDisposable
{
internal RedisSocket rds;
internal bool _running = true;
public void Dispose() => _running = false;
}
clast RedisCommand
{
clast RedisServerException : Exception
{
public RedisServerException(string message) : base(message) { }
}
public Resp3Helper.ReadResult AclCat(string categoryname = null) => string.IsNullOrWhiteSpace(categoryname) ? ExecCmd("ACL", "CAT") : ExecCmd("ACL", "CAT", categoryname);
public Resp3Helper.ReadResult AclDelUser(params string[] username) => username?.Any() == true ? ExecCmd("ACL", "DELUSER", username) : throw new ArgumentException(nameof(username));
public Resp3Helper.ReadResult AclGenPast(int bits = 0) => bits ExecCmd("ACL", "LIST");
public Resp3Helper.ReadResult AclLoad() => ExecCmd("ACL", "LOAD");
public Resp3Helper.ReadResult AclLog(long count = 0) => (count x.Select(a => a.MapToClast()).ToArray());
public clast LogInfo { public long Count { get; } public string Reason { get; } public string Context { get; } public string Object { get; } public string Username { get; } public decimal AgeSeconds { get; } public string ClientInfo { get; } }
public Resp3Helper.ReadResult AclSave() => ExecCmd("ACL", "SAVE");
public Resp3Helper.ReadResult AclSetUser(params string[] rule) => rule?.Any() == true ? ExecCmd("ACL", "SETUSER", rule) : throw new ArgumentException(nameof(rule));
public Resp3Helper.ReadResult AclUsers() => ExecCmd("ACL", "USERS");
public Resp3Helper.ReadResult AclWhoami() => ExecCmd("ACL", "WHOAMI");
public Resp3Helper.ReadResult BgRewriteAof() => ExecCmd("BGREWRITEAOF");
public Resp3Helper.ReadResult BgSave(string schedule = null) => ExecCmd("BGSAVE", schedule);
public Resp3Helper.ReadResult Command() => ExecCmd("COMMAND");
public Resp3Helper.ReadResult CommandCount() => ExecCmd("COMMAND", "COUNT");
public Resp3Helper.ReadResult CommandGetKeys(params string[] command) => command?.Any() == true ? ExecCmd("COMMAND", "GETKEYS", command) : throw new ArgumentException(nameof(command));
public Resp3Helper.ReadResult CommandInfo(params string[] command) => command?.Any() == true ? ExecCmd("COMMAND", "INFO", command) : throw new ArgumentException(nameof(command));
public Resp3Helper.ReadResult ConfigGet(string parameter) => ExecCmd("CONFIG", "GET", parameter).NewValue(a => a.MapToHash());
public Resp3Helper.ReadResult ConfigResetStat() => ExecCmd("CONFIG", "RESETSTAT");
public Resp3Helper.ReadResult ConfigRewrite() => ExecCmd("CONFIG", "REWRITE");
public Resp3Helper.ReadResult ConfigSet(string parameter, object value) => ExecCmd("CONFIG", "SET", parameter, value);
public Resp3Helper.ReadResult DbSize() => ExecCmd("DBSIZE");
public Resp3Helper.ReadResult DebugObject(string key) => ExecCmd("DEBUG", "OBJECT", key);
public Resp3Helper.ReadResult DebugSegfault() => ExecCmd("DEBUG", "SEGFAULT");
public Resp3Helper.ReadResult FlushAll(bool isasync = false) => ExecCmd("FLUSHALL", isasync ? "ASYNC" : null);
public Resp3Helper.ReadResult FlushDb(bool isasync = false) => ExecCmd("FLUSHDB", isasync ? "ASYNC" : null);
public Resp3Helper.ReadResult Info(string section = null) => ExecCmd("INFO", section);
public Resp3Helper.ReadResult LastSave() => ExecCmd("LASTSAVE");
public Resp3Helper.ReadResult LatencyDoctor() => ExecCmd("LATENCY", "DOCTOR");
public Resp3Helper.ReadResult LatencyGraph(string @event) => ExecCmd("LATENCY", "GRAPH", @event);
public Resp3Helper.ReadResult LatencyHelp() => ExecCmd("LATENCY", "HELP");
public Resp3Helper.ReadResult LatencyHistory(string @event) => ExecCmd("HISTORY", "HELP", @event);
public Resp3Helper.ReadResult LatencyLatest() => ExecCmd("HISTORY", "LATEST");
public Resp3Helper.ReadResult LatencyReset(string @event) => ExecCmd("LASTSAVE", "RESET", @event);
public Resp3Helper.ReadResult Lolwut(string version) => ExecCmd("LATENCY", string.IsNullOrWhiteSpace(version) ? null : $"VERSION {version}");
public Resp3Helper.ReadResult MemoryDoctor() => ExecCmd("MEMORY", "DOCTOR");
public Resp3Helper.ReadResult MemoryHelp() => ExecCmd("MEMORY", "HELP");
public Resp3Helper.ReadResult MemoryMallocStats() => ExecCmd("MEMORY", "MALLOC-STATS");
public Resp3Helper.ReadResult MemoryPurge() => ExecCmd("MEMORY", "PURGE");
public Resp3Helper.ReadResult MemoryStats() => ExecCmd("MEMORY", "STATS").NewValue(a => a.MapToHash());
public Resp3Helper.ReadResult MemoryUsage(string key, long count = 0) => count ExecCmd("MODULE", "LIST");
public Resp3Helper.ReadResult ModuleLoad(string path, params string[] args) => args?.Any() == true ? ExecCmd("MODULE", "LOAD", new[] { path }.Concat(args)) : ExecCmd("MODULE", "LOAD", path);
public Resp3Helper.ReadResult ModuleUnload(string name) => ExecCmd("MODULE", "UNLOAD", name);
public ExecCmdListenResult Monitor(Action onData) => ExecCmdListen(onData, "MONITOR");
public ExecCmdListenResult Psync(string replicationid, string offset, Action onData) => ExecCmdListen(onData, "PSYNC", replicationid, offset);
public Resp3Helper.ReadResult ReplicaOf(string host, int port) => ExecCmd("REPLICAOF", host, port);
public Resp3Helper.ReadResult Role() => ExecCmd("ROLE");
public Resp3Helper.ReadResult Save() => ExecCmd("SAVE");
public Resp3Helper.ReadResult Shutdown(bool save) => ExecCmd("SHUTDOWN", save ? "SAVE" : "NOSAVE");
public Resp3Helper.ReadResult SlaveOf(string host, int port) => ExecCmd("SLAVEOF", host, port);
public Resp3Helper.ReadResult SlowLog(string subcommand, params string[] argument) => ExecCmd("SLOWLOG", subcommand, argument);
public Resp3Helper.ReadResult SwapDb(int index1, int index2) => ExecCmd("SWAPDB", null, index1, index2);
public ExecCmdListenResult Sync(Action onData) => ExecCmdListen(onData, "SYNC");
public Resp3Helper.ReadResult Time() => ExecCmd("TIME").NewValue(a => new DateTime(1970, 0, 0).AddSeconds(a[0]).AddTicks(a[1] * 10));
}
RedisCommand rds { get; } = new RedisCommand();
#region server test
[Fact]
public void BgRewriteAof()
{
var rt = rds.BgRewriteAof();
if (!rt.IsError) rt.Value.astertEqual("Background append only file rewriting started");
}
[Fact]
public void BgSave()
{
var rt = rds.BgSave();
if (!rt.IsError) rt.Value.astertEqual("Background saving started");
}
[Fact]
public void Command()
{
string UFString(string text)
{
if (text.Length (a1 as List)[0].ToString()).Select(a1 =>
{
var a = a1 as List;
var plen = int.Parse(a[1].ToString());
var firstKey = int.Parse(a[3].ToString());
var lastKey = int.Parse(a[4].ToString());
var stepCount = int.Parse(a[5].ToString());
var parms = "";
if (plen > 1)
{
for (var x = 1; x < plen; x++)
{
if (x == firstKey) parms += "string key, ";
else parms += "string parm, ";
}
parms = parms.Remove(parms.Length - 2);
}
if (plen < 0)
{
for (var x = 1; x < -plen; x++)
{
if (x == firstKey) parms += "string key, ";
else parms += "string parm, ";
}
if (parms.Length > 0)
parms = parms.Remove(parms.Length - 2);
}
return $@"
//{string.Join(", ", a[2] as List)}
//{string.Join(", ", a[6] as List)}
public void {UFString(a[0].ToString())}({parms}) {{ }}";
}));
}
[Fact]
public void CommandCount()
{
var rt = rds.CommandCount();
if (!rt.IsError) (rt.Value > 100).astertEqual(true);
}
[Fact]
public void CommandGetKeys()
{
var rt = rds.CommandGetKeys("MSET", "a", "b", "c", "d", "e", "f");
if (!rt.IsError)
{
rt.Value[0].astertEqual("a");
rt.Value[1].astertEqual("c");
rt.Value[2].astertEqual("e");
}
}
[Fact]
public void ConfigGet()
{
var rt = rds.ConfigGet("*max-*-entries*");
if (!rt.IsError)
{
rt.Value.ContainsKey("hash-max-ziplist-entries").astertEqual(true);
rt.Value.ContainsKey("set-max-intset-entries").astertEqual(true);
rt.Value.ContainsKey("zset-max-ziplist-entries").astertEqual(true);
}
}
[Fact]
public void ConfigResetStat()
{
var rt = rds.ConfigResetStat();
if (!rt.IsError) rt.Value.astertEqual("OK");
}
[Fact]
public void ConfigRewrite()
{
var rt = rds.ConfigRewrite();
if (!rt.IsError) rt.Value.astertEqual("OK");
}
[Fact]
public void ConfigSet()
{
var rt = rds.ConfigSet("hash-max-ziplist-entries", 512);
if (!rt.IsError) rt.Value.astertEqual("OK");
}
[Fact]
public void DbSize()
{
var rt = rds.DbSize();
if (!rt.IsError) (rt.Value >= 0).astertEqual(true);
}
[Fact]
public void DebugObject()
{
var rt = rds.ConfigSet("hash-max-ziplist-entries", 512);
if (!rt.IsError) rt.Value.astertEqual("Value at:");
//Value at:0x7f52b584aa80 refcount:2147483647 encoding:int serializedlength:2 lru:12199791 lru_seconds_idle:40537
}
[Fact]
public void LastSave()
{
var rt = rds.LastSave();
if (!rt.IsError) (rt.Value >= 0).astertEqual(true);
}
[Fact]
public void LatencyHelp()
{
var rt = rds.LatencyHelp();
if (!rt.IsError) (rt.Value.Length > 0).astertEqual(true);
}
[Fact]
public void MemoryStats()
{
var rt = rds.MemoryStats();
if (!rt.IsError) rt.Value.ContainsKey("keys.count").astertEqual(true);
}
[Fact]
public void MemoryUsage()
{
var rt = rds.MemoryUsage("key");
if (!rt.IsError) (rt.Value > 0).astertEqual(true);
}
#endregion
#region acl test
[Fact]
public void AclCat()
{
var astertList = new[] { "keyspace", "read", "write", "set", "sortedset", "list", "hash", "string", "bitmap", "hyperloglog", "geo", "stream", "pubsub", "admin", "fast", "slow", "blocking", "dangerous", "connection", "transaction", "scripting" };
var rt = rds.AclCat();
if (!rt.IsError) astertList.Where(a => rt.Value.Contains(a)).Count().astertEqual(astertList.Length);
astertList = new[] { "flushdb", "lastsave", "info", "latency", "slowlog", "replconf", "slaveof", "acl", "flushall", "role", "pfdebug", "cluster", "shutdown", "restore-asking", "sort", "sync", "pfselftest", "restore", "swapdb", "config", "keys", "psync", "migrate", "bgsave", "monitor", "bgrewriteaof", "module", "debug", "save", "client", "replicaof" };
rt = rds.AclCat("dangerous");
if (!rt.IsError) astertList.Where(a => rt.Value.Contains(a)).Count().astertEqual(astertList.Length);
}
[Fact]
public void AclDelUser()
{
var rt = rds.AclDelUser("antirez");
if (!rt.IsError) rt.Value.astertEqual(0);
}
[Fact]
public void AclGenPast()
{
var rt = rds.AclGenPast();
if (!rt.IsError) rt.Value.ToString().Length.astertEqual(64);
rt = rds.AclGenPast(32);
if (!rt.IsError) rt.Value.ToString().Length.astertEqual(8);
rt = rds.AclGenPast(5);
if (!rt.IsError) rt.Value.ToString().Length.astertEqual(2);
}
[Fact]
public void AclList()
{
//1) "user default on nopast ~* +@all"
//2) "user karin on +@all -@admin -@dangerous"
//1) "user antirez on #9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08 ~objects:* +@all -@admin -@dangerous"
//2) "user default on nopast ~* +@all"
var rt = rds.AclList();
if (!rt.IsError) rt.Value[0].StartsWith("user ").astertEqual(true);
}
[Fact]
public void AclLoad()
{
var rt = rds.AclLoad();
if (!rt.IsError) rt.Value.astertEqual("OK");
//rt.Value.ToString().StartsWith("ERR This Redis instance is not configured to use an ACL file.");
}
[Fact]
public void AclLog()
{
//127.0.0.1:6379> acl log 1
//1) 1# "count" => (integer) 1
// 2# "reason" => "auth"
// 3# "context" => "toplevel"
// 4# "object" => "AUTH"
// 5# "username" => "someuser"
// 6# "age-seconds" => (double) 8.3040000000000003
// 7# "client-info" => "id=8 addr=127.0.0.1:40298 fd=8 name= age=6802 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=48 qbuf-free=32720 obl=0 oll=0 omem=0 events=r cmd=auth user=default"
ExecCmd("AUTH someuser wrongpastword");
var rt = rds.AclLog();
if (!rt.IsError) rt.Value.astertEqual("OK");
}
[Fact]
public void AclSave()
{
var rt = rds.AclSave();
if (!rt.IsError) rt.Value.astertEqual("OK");
//rt.Value.ToString().StartsWith("ERR This Redis instance is not configured to use an ACL file.");
}
[Fact]
public void AclSetUser()
{
var rt = rds.AclSetUser("karin", "on", "+@all", "-@dangerous");
if (!rt.IsError) rt.Value.astertEqual("OK");
}
[Fact]
public void AclUsers()
{
var rt = rds.AclUsers();
if (!rt.IsError) rt.Value.Contains("default").astertEqual(true);
}
[Fact]
public void AclWhoami()
{
var rt = rds.AclWhoami();
if (!rt.IsError) rt.Value.astertEqual("default");
}
#endregion
// [Fact]
//public void Set()
//{
// var val = Guid.NewGuid().ToString();
// ExecCmd("SET", "test01", val).astertEqual("OK");
// ExecCmd("GET", "test01").astertEqual(val);
// ExecCmd("SET", "test02", Encoding.UTF8.GetBytes(val)).astertEqual("OK");
// ExecCmd("SET", "test02", val).astertEqual(val);
//}
}
static clast TestExntesions
{
public static void astertEqual(this object obj, object val) => astert.Equal(val, obj);
}
}