csharp/2881099/csredis/test/CSRedisCore.Tests/Resp3HelperTests.cs

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);
	}
}