Commit 67d23ca1 authored by Nick Craver's avatar Nick Craver

Tests host:port cleanup

There were several mismatches here...this should help avoid future ones.
parent 72b1cbab
......@@ -8,7 +8,7 @@ public class AsyncTests : TestBase
{
public AsyncTests(ITestOutputHelper output) : base (output) { }
protected override string GetConfiguration() => TestConfig.Current.MasterServer + ":" + TestConfig.Current.MasterPort;
protected override string GetConfiguration() => TestConfig.Current.MasterServerAndPort;
#if DEBUG // IRedisServerDebug and AllowConnect are only available if DEBUG is defined
[Fact]
......
......@@ -7,7 +7,7 @@ namespace StackExchange.Redis.Tests
{
public class ConnectionShutdown : TestBase
{
protected override string GetConfiguration() => TestConfig.Current.MasterServer + ":" + TestConfig.Current.MasterPort;
protected override string GetConfiguration() => TestConfig.Current.MasterServerAndPort;
public ConnectionShutdown(ITestOutputHelper output) : base (output) { }
[Fact(Skip="Unfriendly")]
......@@ -53,4 +53,4 @@ public void ShutdownRaisesConnectionFailedAndRestore()
}
}
}
}
\ No newline at end of file
}
......@@ -42,13 +42,16 @@ public class Config
public string MasterServer { get; set; } = "127.0.0.1";
public int MasterPort { get; set; } = 6379;
public string MasterServerAndPort => MasterServer + ":" + MasterPort.ToString();
public string SlaveServer { get; set; } = "127.0.0.1";
public int SlavePort { get; set; } = 6380;
public string SlaveServerAndPort => SlaveServer + ":" + SlavePort.ToString();
public string SecureServer { get; set; } = "127.0.0.1";
public int SecurePort { get; set; } = 6381;
public string SecurePassword { get; set; } = "changeme";
public string SecureServerAndPort => SecureServer + ":" + SecurePort.ToString();
public string IPv4Server { get; set; } = "127.0.0.1";
public int IPv4Port { get; set; } = 6379;
......
......@@ -28,7 +28,7 @@ public void ConfigurationOptions_UnspecifiedDefaultDb()
var log = new StringWriter();
try
{
using (var conn = ConnectionMultiplexer.Connect($"{TestConfig.Current.MasterServer}:{TestConfig.Current.MasterPort}", log)) {
using (var conn = ConnectionMultiplexer.Connect(TestConfig.Current.MasterServerAndPort, log)) {
var db = conn.GetDatabase();
Assert.Equal(0, db.Database);
}
......@@ -45,7 +45,7 @@ public void ConfigurationOptions_SpecifiedDefaultDb()
var log = new StringWriter();
try
{
using (var conn = ConnectionMultiplexer.Connect($"{TestConfig.Current.MasterServer}:{TestConfig.Current.MasterPort},defaultDatabase=3", log)) {
using (var conn = ConnectionMultiplexer.Connect($"{TestConfig.Current.MasterServerAndPort},defaultDatabase=3", log)) {
var db = conn.GetDatabase();
Assert.Equal(3, db.Database);
}
......
using System;
using System.Linq;
using Xunit;
using Xunit.Abstractions;
namespace StackExchange.Redis.Tests.Issues
{
public class Issue182 : TestBase
{
protected override string GetConfiguration() => $"{TestConfig.Current.MasterServer}:{TestConfig.Current.MasterPort},responseTimeout=10000";
public Issue182(ITestOutputHelper output) : base (output) { }
[FactLongRunning]
public void SetMembers()
{
using (var conn = Create())
{
conn.ConnectionFailed += (s, a) =>
{
Output.WriteLine(a.FailureType.ToString());
Output.WriteLine(a.Exception.Message);
Output.WriteLine(a.Exception.StackTrace);
};
var db = conn.GetDatabase();
var key = Me();
const int count = (int)5e6;
db.KeyDeleteAsync(key).Wait();
foreach (var _ in Enumerable.Range(0, count))
db.SetAdd(key, Guid.NewGuid().ToByteArray(), CommandFlags.FireAndForget);
Assert.Equal(count, db.SetLengthAsync(key).Result); // SCARD for set
var task = db.SetMembersAsync(key);
task.Wait();
Assert.Equal(count, task.Result.Length); // SMEMBERS result length
}
}
[FactLongRunning]
public void SetUnion()
{
using (var conn = Create())
{
var db = conn.GetDatabase();
var key1 = Me() + ":1";
var key2 = Me() + ":2";
var dstkey = Me() + ":dst";
db.KeyDeleteAsync(key1).Wait();
db.KeyDeleteAsync(key2).Wait();
db.KeyDeleteAsync(dstkey).Wait();
const int count = (int)5e6;
foreach (var _ in Enumerable.Range(0, count))
{
db.SetAdd(key1, Guid.NewGuid().ToByteArray(), CommandFlags.FireAndForget);
db.SetAdd(key2, Guid.NewGuid().ToByteArray(), CommandFlags.FireAndForget);
}
Assert.Equal(count, db.SetLengthAsync(key1).Result); // SCARD for set 1
Assert.Equal(count, db.SetLengthAsync(key2).Result); // SCARD for set 2
db.SetCombineAndStoreAsync(SetOperation.Union, dstkey, key1, key2).Wait();
var dstLen = db.SetLength(dstkey);
Assert.Equal(count * 2, dstLen); // SCARD for destination set
}
}
}
}
\ No newline at end of file
using System;
using System.Linq;
using Xunit;
using Xunit.Abstractions;
namespace StackExchange.Redis.Tests.Issues
{
public class Issue182 : TestBase
{
protected override string GetConfiguration() => $"{TestConfig.Current.MasterServerAndPort},responseTimeout=10000";
public Issue182(ITestOutputHelper output) : base (output) { }
[FactLongRunning]
public void SetMembers()
{
using (var conn = Create())
{
conn.ConnectionFailed += (s, a) =>
{
Output.WriteLine(a.FailureType.ToString());
Output.WriteLine(a.Exception.Message);
Output.WriteLine(a.Exception.StackTrace);
};
var db = conn.GetDatabase();
var key = Me();
const int count = (int)5e6;
db.KeyDeleteAsync(key).Wait();
foreach (var _ in Enumerable.Range(0, count))
db.SetAdd(key, Guid.NewGuid().ToByteArray(), CommandFlags.FireAndForget);
Assert.Equal(count, db.SetLengthAsync(key).Result); // SCARD for set
var task = db.SetMembersAsync(key);
task.Wait();
Assert.Equal(count, task.Result.Length); // SMEMBERS result length
}
}
[FactLongRunning]
public void SetUnion()
{
using (var conn = Create())
{
var db = conn.GetDatabase();
var key1 = Me() + ":1";
var key2 = Me() + ":2";
var dstkey = Me() + ":dst";
db.KeyDeleteAsync(key1).Wait();
db.KeyDeleteAsync(key2).Wait();
db.KeyDeleteAsync(dstkey).Wait();
const int count = (int)5e6;
foreach (var _ in Enumerable.Range(0, count))
{
db.SetAdd(key1, Guid.NewGuid().ToByteArray(), CommandFlags.FireAndForget);
db.SetAdd(key2, Guid.NewGuid().ToByteArray(), CommandFlags.FireAndForget);
}
Assert.Equal(count, db.SetLengthAsync(key1).Result); // SCARD for set 1
Assert.Equal(count, db.SetLengthAsync(key2).Result); // SCARD for set 2
db.SetCombineAndStoreAsync(SetOperation.Union, dstkey, key1, key2).Wait();
var dstLen = db.SetLength(dstkey);
Assert.Equal(count * 2, dstLen); // SCARD for destination set
}
}
}
}
......@@ -29,7 +29,7 @@ public void DefaultValue_IsTrue()
[Fact]
public void PreserveAsyncOrder_SetConnectionMultiplexerProperty()
{
var multiplexer = ConnectionMultiplexer.Connect(TestConfig.Current.MasterServer + ":" + TestConfig.Current.MasterPort + ",preserveAsyncOrder=false");
var multiplexer = ConnectionMultiplexer.Connect(TestConfig.Current.MasterServerAndPort + ",preserveAsyncOrder=false");
Assert.False(multiplexer.PreserveAsyncOrder);
}
}
......
......@@ -7,7 +7,7 @@ namespace StackExchange.Redis.Tests.Issues
{
public class SO25567566 : TestBase
{
protected override string GetConfiguration() => $"{TestConfig.Current.MasterServer}:{TestConfig.Current.MasterPort}";
protected override string GetConfiguration() => TestConfig.Current.MasterServerAndPort;
public SO25567566(ITestOutputHelper output) : base(output) { }
[FactLongRunning]
......@@ -70,4 +70,4 @@ private async Task<string> DoStuff(ConnectionMultiplexer conn)
}
}
}
}
\ No newline at end of file
}
......@@ -34,7 +34,7 @@ public void RandomKey()
using (var conn = Create(allowAdmin: true))
{
var db = conn.GetDatabase();
conn.GetServer(TestConfig.Current.MasterServer, TestConfig.Current.MasterPort).FlushDatabase();
conn.GetServer(TestConfig.Current.MasterServerAndPort).FlushDatabase();
string anyKey = db.KeyRandom();
Assert.Null(anyKey);
......
......@@ -9,7 +9,7 @@ namespace StackExchange.Redis.Tests
{
public class Locking : TestBase
{
protected override string GetConfiguration() => TestConfig.Current.MasterServer + ":" + TestConfig.Current.MasterPort;
protected override string GetConfiguration() => TestConfig.Current.MasterServerAndPort;
public Locking(ITestOutputHelper output) : base (output) { }
public enum TestMode
......
......@@ -12,7 +12,7 @@ namespace StackExchange.Redis.Tests
public class MultiMaster : TestBase
{
protected override string GetConfiguration() =>
TestConfig.Current.MasterServer + ":" + TestConfig.Current.SecurePort + "," + TestConfig.Current.MasterServer + ":" + TestConfig.Current.MasterPort + ",password=" + TestConfig.Current.SecurePassword;
TestConfig.Current.MasterServerAndPort + "," + TestConfig.Current.SecureServerAndPort + ",password=" + TestConfig.Current.SecurePassword;
public MultiMaster(ITestOutputHelper output) : base (output) { }
......@@ -39,8 +39,8 @@ public async Task DeslaveGoesToPrimary()
ConfigurationOptions config = GetMasterSlaveConfig();
using (var conn = ConnectionMultiplexer.Connect(config))
{
var primary = conn.GetServer(TestConfig.Current.MasterServer, TestConfig.Current.MasterPort);
var secondary = conn.GetServer(TestConfig.Current.SlaveServer, TestConfig.Current.SlavePort);
var primary = conn.GetServer(TestConfig.Current.MasterServerAndPort);
var secondary = conn.GetServer(TestConfig.Current.SlaveServerAndPort);
primary.Ping();
secondary.Ping();
......@@ -58,7 +58,7 @@ public async Task DeslaveGoesToPrimary()
conn.Configure(writer);
string log = writer.ToString();
Assert.True(log.Contains("tie-break is unanimous at " + TestConfig.Current.MasterServer + ":" + TestConfig.Current.MasterPort), "unanimous");
Assert.True(log.Contains("tie-break is unanimous at " + TestConfig.Current.MasterServerAndPort), "unanimous");
}
// k, so we know everyone loves 6379; is that what we get?
......@@ -89,8 +89,8 @@ public async Task DeslaveGoesToPrimary()
// server topology changes from failures to recognize those changes
using (var conn2 = ConnectionMultiplexer.Connect(config))
{
var primary2 = conn.GetServer(TestConfig.Current.MasterServer, TestConfig.Current.MasterPort);
var secondary2 = conn.GetServer(TestConfig.Current.SlaveServer, TestConfig.Current.SlavePort);
var primary2 = conn.GetServer(TestConfig.Current.MasterServerAndPort);
var secondary2 = conn.GetServer(TestConfig.Current.SlaveServerAndPort);
Writer.WriteLine($"Check: {primary2.EndPoint}: {primary2.ServerType}, Mode: {(primary2.IsSlave ? "Slave" : "Master")}");
Writer.WriteLine($"Check: {secondary2.EndPoint}: {secondary2.ServerType}, Mode: {(secondary2.IsSlave ? "Slave" : "Master")}");
......@@ -143,15 +143,15 @@ public void TestMultiNoTieBreak()
public static IEnumerable<object[]> GetConnections()
{
yield return new object[] { TestConfig.Current.MasterServer + ":" + TestConfig.Current.MasterPort, TestConfig.Current.MasterServer + ":" + TestConfig.Current.MasterPort, TestConfig.Current.MasterServer + ":" + TestConfig.Current.MasterPort };
yield return new object[] { TestConfig.Current.MasterServer + ":" + TestConfig.Current.SecurePort, TestConfig.Current.MasterServer + ":" + TestConfig.Current.SecurePort, TestConfig.Current.MasterServer + ":" + TestConfig.Current.SecurePort };
yield return new object[] { TestConfig.Current.MasterServer + ":" + TestConfig.Current.SecurePort, TestConfig.Current.MasterServer + ":" + TestConfig.Current.MasterPort, null };
yield return new object[] { TestConfig.Current.MasterServer + ":" + TestConfig.Current.MasterPort, TestConfig.Current.MasterServer + ":" + TestConfig.Current.SecurePort, null };
yield return new object[] { null, TestConfig.Current.MasterServer + ":" + TestConfig.Current.MasterPort, TestConfig.Current.MasterServer + ":" + TestConfig.Current.MasterPort };
yield return new object[] { TestConfig.Current.MasterServer + ":" + TestConfig.Current.MasterPort, null, TestConfig.Current.MasterServer + ":" + TestConfig.Current.MasterPort };
yield return new object[] { null, TestConfig.Current.MasterServer + ":" + TestConfig.Current.SecurePort, TestConfig.Current.MasterServer + ":" + TestConfig.Current.SecurePort };
yield return new object[] { TestConfig.Current.MasterServer + ":" + TestConfig.Current.SecurePort, null, TestConfig.Current.MasterServer + ":" + TestConfig.Current.SecurePort };
yield return new object[] { TestConfig.Current.MasterServerAndPort, TestConfig.Current.MasterServerAndPort, TestConfig.Current.MasterServerAndPort };
yield return new object[] { TestConfig.Current.SecureServerAndPort, TestConfig.Current.SecureServerAndPort, TestConfig.Current.SecureServerAndPort };
yield return new object[] { TestConfig.Current.SecureServerAndPort, TestConfig.Current.MasterServerAndPort, null };
yield return new object[] { TestConfig.Current.MasterServerAndPort, TestConfig.Current.SecureServerAndPort, null };
yield return new object[] { null, TestConfig.Current.MasterServerAndPort, TestConfig.Current.MasterServerAndPort };
yield return new object[] { TestConfig.Current.MasterServerAndPort, null, TestConfig.Current.MasterServerAndPort };
yield return new object[] { null, TestConfig.Current.SecureServerAndPort, TestConfig.Current.SecureServerAndPort };
yield return new object[] { TestConfig.Current.SecureServerAndPort, null, TestConfig.Current.SecureServerAndPort };
yield return new object[] { null, null, null };
}
......@@ -160,11 +160,11 @@ public void TestMultiWithTiebreak(string a, string b, string elected)
{
const string TieBreak = "__tie__";
// set the tie-breakers to the expected state
using (var aConn = ConnectionMultiplexer.Connect(TestConfig.Current.MasterServer + ":" + TestConfig.Current.MasterPort))
using (var aConn = ConnectionMultiplexer.Connect(TestConfig.Current.MasterServerAndPort))
{
aConn.GetDatabase().StringSet(TieBreak, a);
}
using (var aConn = ConnectionMultiplexer.Connect(TestConfig.Current.MasterServer + ":" + TestConfig.Current.SecurePort + ",password=" + TestConfig.Current.SecurePassword))
using (var aConn = ConnectionMultiplexer.Connect(TestConfig.Current.SecureServerAndPort + ",password=" + TestConfig.Current.SecurePassword))
{
aConn.GetDatabase().StringSet(TieBreak, b);
}
......
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Xunit;
using Xunit.Abstractions;
......@@ -13,7 +14,7 @@ public class PubSubNonParallel : TestBase
[Theory]
[InlineData(false)]
[InlineData(true)]
public void SubscriptionsSurviveMasterSwitch(bool useSharedSocketManager)
public async Task SubscriptionsSurviveMasterSwitchAsync(bool useSharedSocketManager)
{
using (var a = Create(allowAdmin: true, useSharedSocketManager: useSharedSocketManager))
using (var b = Create(allowAdmin: true, useSharedSocketManager: useSharedSocketManager))
......@@ -42,10 +43,10 @@ public void SubscriptionsSurviveMasterSwitch(bool useSharedSocketManager)
Interlocked.Increment(ref bCount);
});
Assert.False(a.GetServer(TestConfig.Current.MasterServer, TestConfig.Current.MasterPort).IsSlave, TestConfig.Current.MasterPort + " is master via a");
Assert.True(a.GetServer(TestConfig.Current.MasterServer, TestConfig.Current.SlavePort).IsSlave, TestConfig.Current.SlavePort + " is slave via a");
Assert.False(b.GetServer(TestConfig.Current.MasterServer, TestConfig.Current.MasterPort).IsSlave, TestConfig.Current.MasterPort + " is master via b");
Assert.True(b.GetServer(TestConfig.Current.MasterServer, TestConfig.Current.SlavePort).IsSlave, TestConfig.Current.SlavePort + " is slave via b");
Assert.False(a.GetServer(TestConfig.Current.MasterServerAndPort).IsSlave, $"{TestConfig.Current.MasterServerAndPort} should be master via a");
Assert.True(a.GetServer(TestConfig.Current.SlaveServerAndPort).IsSlave, $"{TestConfig.Current.SlaveServerAndPort} should be slave via a");
Assert.False(b.GetServer(TestConfig.Current.MasterServerAndPort).IsSlave, $"{TestConfig.Current.MasterServerAndPort} should be master via b");
Assert.True(b.GetServer(TestConfig.Current.SlaveServerAndPort).IsSlave, $"{TestConfig.Current.SlaveServerAndPort} should be slave via b");
var epA = subA.SubscribedEndpoint(channel);
var epB = subB.SubscribedEndpoint(channel);
......@@ -68,18 +69,18 @@ public void SubscriptionsSurviveMasterSwitch(bool useSharedSocketManager)
Output.WriteLine("Changing master...");
using (var sw = new StringWriter())
{
a.GetServer(TestConfig.Current.MasterServer, TestConfig.Current.SlavePort).MakeMaster(ReplicationChangeOptions.All, sw);
a.GetServer(TestConfig.Current.SlaveServerAndPort).MakeMaster(ReplicationChangeOptions.All, sw);
Output.WriteLine(sw.ToString());
}
subA.Ping();
subB.Ping();
Output.WriteLine("Pausing...");
Thread.Sleep(2000);
await Task.Delay(4000).ForAwait();
Assert.True(a.GetServer(TestConfig.Current.MasterServer, TestConfig.Current.MasterPort).IsSlave, TestConfig.Current.MasterPort + " is slave via a");
Assert.False(a.GetServer(TestConfig.Current.MasterServer, TestConfig.Current.SlavePort).IsSlave, TestConfig.Current.SlavePort + " is master via a");
Assert.True(b.GetServer(TestConfig.Current.MasterServer, TestConfig.Current.MasterPort).IsSlave, TestConfig.Current.MasterPort + " is slave via b");
Assert.False(b.GetServer(TestConfig.Current.MasterServer, TestConfig.Current.SlavePort).IsSlave, TestConfig.Current.SlavePort + " is master via b");
Assert.True(a.GetServer(TestConfig.Current.MasterServerAndPort).IsSlave, $"{TestConfig.Current.MasterServerAndPort} should be a slave via a");
Assert.False(a.GetServer(TestConfig.Current.SlaveServerAndPort).IsSlave, $"{TestConfig.Current.SlaveServerAndPort} should be a master via a");
Assert.True(b.GetServer(TestConfig.Current.MasterServerAndPort).IsSlave, $"{TestConfig.Current.MasterServerAndPort} should be a slave via b");
Assert.False(b.GetServer(TestConfig.Current.SlaveServerAndPort).IsSlave, $"{TestConfig.Current.SlaveServerAndPort} should be a master via b");
Output.WriteLine("Pause complete");
var counters = a.GetCounters();
......@@ -88,6 +89,7 @@ public void SubscriptionsSurviveMasterSwitch(bool useSharedSocketManager)
Output.WriteLine("b outstanding: " + counters.TotalOutstanding);
subA.Ping();
subB.Ping();
await Task.Delay(2000).ForAwait();
epA = subA.SubscribedEndpoint(channel);
epB = subB.SubscribedEndpoint(channel);
Output.WriteLine("a: " + EndPointCollection.ToString(epA));
......@@ -108,7 +110,7 @@ public void SubscriptionsSurviveMasterSwitch(bool useSharedSocketManager)
Output.WriteLine("Restoring configuration...");
try
{
a.GetServer(TestConfig.Current.MasterServer, TestConfig.Current.MasterPort).MakeMaster(ReplicationChangeOptions.All);
a.GetServer(TestConfig.Current.MasterServerAndPort).MakeMaster(ReplicationChangeOptions.All);
}
catch
{ }
......
using System;
using System.Diagnostics;
using System.Linq;
using Xunit;
using Xunit.Abstractions;
namespace StackExchange.Redis.Tests
{
public class Scripting : TestBase
{
public Scripting(ITestOutputHelper output) : base (output) { }
[Fact]
public void TestBasicScripting()
{
using (var conn = Create())
{
Skip.IfMissingFeature(conn, nameof(RedisFeatures.Scripting), f => f.Scripting);
RedisValue newId = Guid.NewGuid().ToString();
RedisKey custKey = Me();
var db = conn.GetDatabase();
db.KeyDelete(custKey);
db.HashSet(custKey, "id", 123);
var wasSet = (bool)db.ScriptEvaluate("if redis.call('hexists', KEYS[1], 'UniqueId') then return redis.call('hset', KEYS[1], 'UniqueId', ARGV[1]) else return 0 end",
new RedisKey[] { custKey }, new RedisValue[] { newId });
Assert.True(wasSet);
wasSet = (bool)db.ScriptEvaluate("if redis.call('hexists', KEYS[1], 'UniqueId') then return redis.call('hset', KEYS[1], 'UniqueId', ARGV[1]) else return 0 end",
new RedisKey[] { custKey }, new RedisValue[] { newId });
Assert.False(wasSet);
}
}
[Theory]
[InlineData(true)]
[InlineData(false)]
public void CheckLoads(bool async)
{
using (var conn0 = Create(allowAdmin: true))
using (var conn1 = Create(allowAdmin: true))
{
Skip.IfMissingFeature(conn0, nameof(RedisFeatures.Scripting), f => f.Scripting);
// note that these are on different connections (so we wouldn't expect
// the flush to drop the local cache - assume it is a surprise!)
var server = conn0.GetServer(TestConfig.Current.MasterServer, TestConfig.Current.MasterPort);
var db = conn1.GetDatabase();
const string script = "return 1;";
// start empty
server.ScriptFlush();
Assert.False(server.ScriptExists(script));
// run once, causes to be cached
Assert.True((bool)db.ScriptEvaluate(script));
Assert.True(server.ScriptExists(script));
// can run again
Assert.True((bool)db.ScriptEvaluate(script));
// ditch the scripts; should no longer exist
db.Ping();
server.ScriptFlush();
Assert.False(server.ScriptExists(script));
db.Ping();
if (async)
{
// now: fails the first time
try
{
db.Wait(db.ScriptEvaluateAsync(script));
Assert.True(false, "ScriptEvaluateAsync should fail");
}
catch (AggregateException ex)
{
Assert.Single(ex.InnerExceptions);
Assert.IsType<RedisServerException>(ex.InnerExceptions[0]);
Assert.Equal("NOSCRIPT No matching script. Please use EVAL.", ex.InnerExceptions[0].Message);
}
}
else
{
// just works; magic
Assert.True((bool)db.ScriptEvaluate(script));
}
// but gets marked as unloaded, so we can use it again...
Assert.True((bool)db.ScriptEvaluate(script));
// which will cause it to be cached
Assert.True(server.ScriptExists(script));
}
}
[Fact]
public void CompareScriptToDirect()
{
const string Script = "return redis.call('incr', KEYS[1])";
using (var conn = Create(allowAdmin: true))
{
Skip.IfMissingFeature(conn, nameof(RedisFeatures.Scripting), f => f.Scripting);
var server = conn.GetServer(TestConfig.Current.MasterServer, TestConfig.Current.MasterPort);
server.FlushAllDatabases();
server.ScriptFlush();
server.ScriptLoad(Script);
var db = conn.GetDatabase();
db.Ping(); // k, we're all up to date now; clean db, minimal script cache
// we're using a pipeline here, so send 1000 messages, but for timing: only care about the last
const int LOOP = 5000;
RedisKey key = "foo";
RedisKey[] keys = new[] { key }; // script takes an array
// run via script
db.KeyDelete(key);
CollectGarbage();
var watch = Stopwatch.StartNew();
for (int i = 1; i < LOOP; i++) // the i=1 is to do all-but-one
{
db.ScriptEvaluate(Script, keys, flags: CommandFlags.FireAndForget);
}
var scriptResult = db.ScriptEvaluate(Script, keys); // last one we wait for (no F+F)
watch.Stop();
TimeSpan scriptTime = watch.Elapsed;
// run via raw op
db.KeyDelete(key);
CollectGarbage();
watch = Stopwatch.StartNew();
for (int i = 1; i < LOOP; i++) // the i=1 is to do all-but-one
{
db.StringIncrement(key, flags: CommandFlags.FireAndForget);
}
var directResult = db.StringIncrement(key); // last one we wait for (no F+F)
watch.Stop();
TimeSpan directTime = watch.Elapsed;
Assert.Equal(LOOP, (long)scriptResult);
Assert.Equal(LOOP, directResult);
Output.WriteLine("script: {0}ms; direct: {1}ms",
scriptTime.TotalMilliseconds,
directTime.TotalMilliseconds);
}
}
[Fact]
public void TestCallByHash()
{
const string Script = "return redis.call('incr', KEYS[1])";
using (var conn = Create(allowAdmin: true))
{
Skip.IfMissingFeature(conn, nameof(RedisFeatures.Scripting), f => f.Scripting);
var server = conn.GetServer(TestConfig.Current.MasterServer, TestConfig.Current.MasterPort);
server.FlushAllDatabases();
server.ScriptFlush();
byte[] hash = server.ScriptLoad(Script);
var db = conn.GetDatabase();
RedisKey[] keys = { Me() };
string hexHash = string.Concat(hash.Select(x => x.ToString("X2")));
Assert.Equal("2BAB3B661081DB58BD2341920E0BA7CF5DC77B25", hexHash);
db.ScriptEvaluate(hexHash, keys);
db.ScriptEvaluate(hash, keys);
var count = (int)db.StringGet(keys)[0];
Assert.Equal(2, count);
}
}
[Fact]
public void SimpleLuaScript()
{
const string Script = "return @ident";
using (var conn = Create(allowAdmin: true))
{
Skip.IfMissingFeature(conn, nameof(RedisFeatures.Scripting), f => f.Scripting);
var server = conn.GetServer(TestConfig.Current.MasterServer, TestConfig.Current.MasterPort);
server.FlushAllDatabases();
server.ScriptFlush();
var prepared = LuaScript.Prepare(Script);
var db = conn.GetDatabase();
{
var val = prepared.Evaluate(db, new { ident = "hello" });
Assert.Equal("hello", (string)val);
}
{
var val = prepared.Evaluate(db, new { ident = 123 });
Assert.Equal(123, (int)val);
}
{
var val = prepared.Evaluate(db, new { ident = 123L });
Assert.Equal(123L, (long)val);
}
{
var val = prepared.Evaluate(db, new { ident = 1.1 });
Assert.Equal(1.1, (double)val);
}
{
var val = prepared.Evaluate(db, new { ident = true });
Assert.True((bool)val);
}
{
var val = prepared.Evaluate(db, new { ident = new byte[] { 4, 5, 6 } });
Assert.True(new byte[] { 4, 5, 6 }.SequenceEqual((byte[])val));
}
}
}
[Fact]
public void LuaScriptWithKeys()
{
const string Script = "redis.call('set', @key, @value)";
using (var conn = Create(allowAdmin: true))
{
Skip.IfMissingFeature(conn, nameof(RedisFeatures.Scripting), f => f.Scripting);
var server = conn.GetServer(TestConfig.Current.MasterServer, TestConfig.Current.MasterPort);
server.FlushAllDatabases();
server.ScriptFlush();
var script = LuaScript.Prepare(Script);
var db = conn.GetDatabase();
var p = new { key = (RedisKey)"testkey", value = 123 };
script.Evaluate(db, p);
var val = db.StringGet("testkey");
Assert.Equal(123, (int)val);
// no super clean way to extract this; so just abuse InternalsVisibleTo
script.ExtractParameters(p, null, out RedisKey[] keys, out RedisValue[] args);
Assert.NotNull(keys);
Assert.Single(keys);
Assert.Equal("testkey", keys[0]);
}
}
[Fact]
public void NoInlineReplacement()
{
const string Script = "redis.call('set', @key, 'hello@example')";
using (var conn = Create(allowAdmin: true))
{
Skip.IfMissingFeature(conn, nameof(RedisFeatures.Scripting), f => f.Scripting);
var server = conn.GetServer(TestConfig.Current.MasterServer, TestConfig.Current.MasterPort);
server.FlushAllDatabases();
server.ScriptFlush();
var script = LuaScript.Prepare(Script);
Assert.Equal("redis.call('set', ARGV[1], 'hello@example')", script.ExecutableScript);
var db = conn.GetDatabase();
var p = new { key = (RedisKey)"key" };
script.Evaluate(db, p);
var val = db.StringGet("key");
Assert.Equal("hello@example", val);
}
}
[Fact]
public void EscapeReplacement()
{
const string Script = "redis.call('set', @key, @@escapeMe)";
var script = LuaScript.Prepare(Script);
Assert.Equal("redis.call('set', ARGV[1], @escapeMe)", script.ExecutableScript);
}
[Fact]
public void SimpleLoadedLuaScript()
{
const string Script = "return @ident";
using (var conn = Create(allowAdmin: true))
{
Skip.IfMissingFeature(conn, nameof(RedisFeatures.Scripting), f => f.Scripting);
var server = conn.GetServer(TestConfig.Current.MasterServer, TestConfig.Current.MasterPort);
server.FlushAllDatabases();
server.ScriptFlush();
var prepared = LuaScript.Prepare(Script);
var loaded = prepared.Load(server);
var db = conn.GetDatabase();
{
var val = loaded.Evaluate(db, new { ident = "hello" });
Assert.Equal("hello", (string)val);
}
{
var val = loaded.Evaluate(db, new { ident = 123 });
Assert.Equal(123, (int)val);
}
{
var val = loaded.Evaluate(db, new { ident = 123L });
Assert.Equal(123L, (long)val);
}
{
var val = loaded.Evaluate(db, new { ident = 1.1 });
Assert.Equal(1.1, (double)val);
}
{
var val = loaded.Evaluate(db, new { ident = true });
Assert.True((bool)val);
}
{
var val = loaded.Evaluate(db, new { ident = new byte[] { 4, 5, 6 } });
Assert.True(new byte[] { 4, 5, 6 }.SequenceEqual((byte[])val));
}
}
}
[Fact]
public void LoadedLuaScriptWithKeys()
{
const string Script = "redis.call('set', @key, @value)";
using (var conn = Create(allowAdmin: true))
{
Skip.IfMissingFeature(conn, nameof(RedisFeatures.Scripting), f => f.Scripting);
var server = conn.GetServer(TestConfig.Current.MasterServer, TestConfig.Current.MasterPort);
server.FlushAllDatabases();
server.ScriptFlush();
var script = LuaScript.Prepare(Script);
var prepared = script.Load(server);
var db = conn.GetDatabase();
var p = new { key = (RedisKey)"testkey", value = 123 };
prepared.Evaluate(db, p);
var val = db.StringGet("testkey");
Assert.Equal(123, (int)val);
// no super clean way to extract this; so just abuse InternalsVisibleTo
prepared.Original.ExtractParameters(p, null, out RedisKey[] keys, out RedisValue[] args);
Assert.NotNull(keys);
Assert.Single(keys);
Assert.Equal("testkey", keys[0]);
}
}
[Fact]
public void PurgeLuaScriptCache()
{
const string Script = "redis.call('set', @PurgeLuaScriptCacheKey, @PurgeLuaScriptCacheValue)";
var first = LuaScript.Prepare(Script);
var fromCache = LuaScript.Prepare(Script);
Assert.True(ReferenceEquals(first, fromCache));
LuaScript.PurgeCache();
var shouldBeNew = LuaScript.Prepare(Script);
Assert.False(ReferenceEquals(first, shouldBeNew));
}
private static void _PurgeLuaScriptOnFinalize(string script)
{
var first = LuaScript.Prepare(script);
var fromCache = LuaScript.Prepare(script);
Assert.True(ReferenceEquals(first, fromCache));
Assert.Equal(1, LuaScript.GetCachedScriptCount());
}
[Fact]
public void PurgeLuaScriptOnFinalize()
{
const string Script = "redis.call('set', @PurgeLuaScriptOnFinalizeKey, @PurgeLuaScriptOnFinalizeValue)";
LuaScript.PurgeCache();
Assert.Equal(0, LuaScript.GetCachedScriptCount());
// This has to be a separate method to guarantee that the created LuaScript objects go out of scope,
// and are thus available to be GC'd
_PurgeLuaScriptOnFinalize(Script);
GC.Collect(2, GCCollectionMode.Forced, blocking: true);
GC.WaitForPendingFinalizers();
Assert.Equal(0, LuaScript.GetCachedScriptCount());
var shouldBeNew = LuaScript.Prepare(Script);
Assert.Equal(1, LuaScript.GetCachedScriptCount());
}
[Fact]
public void IDatabaseLuaScriptConvenienceMethods()
{
const string Script = "redis.call('set', @key, @value)";
using (var conn = Create(allowAdmin: true))
{
Skip.IfMissingFeature(conn, nameof(RedisFeatures.Scripting), f => f.Scripting);
var script = LuaScript.Prepare(Script);
var db = conn.GetDatabase();
db.ScriptEvaluate(script, new { key = (RedisKey)"key", value = "value" });
var val = db.StringGet("key");
Assert.Equal("value", val);
var prepared = script.Load(conn.GetServer(conn.GetEndPoints()[0]));
db.ScriptEvaluate(prepared, new { key = (RedisKey)"key2", value = "value2" });
var val2 = db.StringGet("key2");
Assert.Equal("value2", val2);
}
}
[Fact]
public void IServerLuaScriptConvenienceMethods()
{
const string Script = "redis.call('set', @key, @value)";
using (var conn = Create(allowAdmin: true))
{
Skip.IfMissingFeature(conn, nameof(RedisFeatures.Scripting), f => f.Scripting);
var script = LuaScript.Prepare(Script);
var server = conn.GetServer(conn.GetEndPoints()[0]);
var db = conn.GetDatabase();
var prepared = server.ScriptLoad(script);
db.ScriptEvaluate(prepared, new { key = (RedisKey)"key3", value = "value3" });
var val = db.StringGet("key3");
Assert.Equal("value3", val);
}
}
[Fact]
public void LuaScriptPrefixedKeys()
{
const string Script = "redis.call('set', @key, @value)";
var prepared = LuaScript.Prepare(Script);
var p = new { key = (RedisKey)"key", value = "hello" };
// no super clean way to extract this; so just abuse InternalsVisibleTo
prepared.ExtractParameters(p, "prefix-", out RedisKey[] keys, out RedisValue[] args);
Assert.NotNull(keys);
Assert.Single(keys);
Assert.Equal("prefix-key", keys[0]);
Assert.Equal(2, args.Length);
Assert.Equal("prefix-key", args[0]);
Assert.Equal("hello", args[1]);
}
[Fact]
public void LuaScriptWithWrappedDatabase()
{
const string Script = "redis.call('set', @key, @value)";
using (var conn = Create(allowAdmin: true))
{
Skip.IfMissingFeature(conn, nameof(RedisFeatures.Scripting), f => f.Scripting);
var db = conn.GetDatabase(0);
var wrappedDb = KeyspaceIsolation.DatabaseExtensions.WithKeyPrefix(db, "prefix-");
var prepared = LuaScript.Prepare(Script);
wrappedDb.ScriptEvaluate(prepared, new { key = (RedisKey)"mykey", value = 123 });
var val1 = wrappedDb.StringGet("mykey");
Assert.Equal(123, (int)val1);
var val2 = db.StringGet("prefix-mykey");
Assert.Equal(123, (int)val2);
var val3 = db.StringGet("mykey");
Assert.True(val3.IsNull);
}
}
[Fact]
public void LoadedLuaScriptWithWrappedDatabase()
{
const string Script = "redis.call('set', @key, @value)";
using (var conn = Create(allowAdmin: true))
{
Skip.IfMissingFeature(conn, nameof(RedisFeatures.Scripting), f => f.Scripting);
var db = conn.GetDatabase(0);
var wrappedDb = KeyspaceIsolation.DatabaseExtensions.WithKeyPrefix(db, "prefix2-");
var server = conn.GetServer(conn.GetEndPoints()[0]);
var prepared = LuaScript.Prepare(Script).Load(server);
wrappedDb.ScriptEvaluate(prepared, new { key = (RedisKey)"mykey", value = 123 });
var val1 = wrappedDb.StringGet("mykey");
Assert.Equal(123, (int)val1);
var val2 = db.StringGet("prefix2-mykey");
Assert.Equal(123, (int)val2);
var val3 = db.StringGet("mykey");
Assert.True(val3.IsNull);
}
}
}
}
using System;
using System.Diagnostics;
using System.Linq;
using Xunit;
using Xunit.Abstractions;
namespace StackExchange.Redis.Tests
{
public class Scripting : TestBase
{
public Scripting(ITestOutputHelper output) : base (output) { }
[Fact]
public void TestBasicScripting()
{
using (var conn = Create())
{
Skip.IfMissingFeature(conn, nameof(RedisFeatures.Scripting), f => f.Scripting);
RedisValue newId = Guid.NewGuid().ToString();
RedisKey custKey = Me();
var db = conn.GetDatabase();
db.KeyDelete(custKey);
db.HashSet(custKey, "id", 123);
var wasSet = (bool)db.ScriptEvaluate("if redis.call('hexists', KEYS[1], 'UniqueId') then return redis.call('hset', KEYS[1], 'UniqueId', ARGV[1]) else return 0 end",
new RedisKey[] { custKey }, new RedisValue[] { newId });
Assert.True(wasSet);
wasSet = (bool)db.ScriptEvaluate("if redis.call('hexists', KEYS[1], 'UniqueId') then return redis.call('hset', KEYS[1], 'UniqueId', ARGV[1]) else return 0 end",
new RedisKey[] { custKey }, new RedisValue[] { newId });
Assert.False(wasSet);
}
}
[Theory]
[InlineData(true)]
[InlineData(false)]
public void CheckLoads(bool async)
{
using (var conn0 = Create(allowAdmin: true))
using (var conn1 = Create(allowAdmin: true))
{
Skip.IfMissingFeature(conn0, nameof(RedisFeatures.Scripting), f => f.Scripting);
// note that these are on different connections (so we wouldn't expect
// the flush to drop the local cache - assume it is a surprise!)
var server = conn0.GetServer(TestConfig.Current.MasterServerAndPort);
var db = conn1.GetDatabase();
const string script = "return 1;";
// start empty
server.ScriptFlush();
Assert.False(server.ScriptExists(script));
// run once, causes to be cached
Assert.True((bool)db.ScriptEvaluate(script));
Assert.True(server.ScriptExists(script));
// can run again
Assert.True((bool)db.ScriptEvaluate(script));
// ditch the scripts; should no longer exist
db.Ping();
server.ScriptFlush();
Assert.False(server.ScriptExists(script));
db.Ping();
if (async)
{
// now: fails the first time
try
{
db.Wait(db.ScriptEvaluateAsync(script));
Assert.True(false, "ScriptEvaluateAsync should fail");
}
catch (AggregateException ex)
{
Assert.Single(ex.InnerExceptions);
Assert.IsType<RedisServerException>(ex.InnerExceptions[0]);
Assert.Equal("NOSCRIPT No matching script. Please use EVAL.", ex.InnerExceptions[0].Message);
}
}
else
{
// just works; magic
Assert.True((bool)db.ScriptEvaluate(script));
}
// but gets marked as unloaded, so we can use it again...
Assert.True((bool)db.ScriptEvaluate(script));
// which will cause it to be cached
Assert.True(server.ScriptExists(script));
}
}
[Fact]
public void CompareScriptToDirect()
{
const string Script = "return redis.call('incr', KEYS[1])";
using (var conn = Create(allowAdmin: true))
{
Skip.IfMissingFeature(conn, nameof(RedisFeatures.Scripting), f => f.Scripting);
var server = conn.GetServer(TestConfig.Current.MasterServerAndPort);
server.FlushAllDatabases();
server.ScriptFlush();
server.ScriptLoad(Script);
var db = conn.GetDatabase();
db.Ping(); // k, we're all up to date now; clean db, minimal script cache
// we're using a pipeline here, so send 1000 messages, but for timing: only care about the last
const int LOOP = 5000;
RedisKey key = "foo";
RedisKey[] keys = new[] { key }; // script takes an array
// run via script
db.KeyDelete(key);
CollectGarbage();
var watch = Stopwatch.StartNew();
for (int i = 1; i < LOOP; i++) // the i=1 is to do all-but-one
{
db.ScriptEvaluate(Script, keys, flags: CommandFlags.FireAndForget);
}
var scriptResult = db.ScriptEvaluate(Script, keys); // last one we wait for (no F+F)
watch.Stop();
TimeSpan scriptTime = watch.Elapsed;
// run via raw op
db.KeyDelete(key);
CollectGarbage();
watch = Stopwatch.StartNew();
for (int i = 1; i < LOOP; i++) // the i=1 is to do all-but-one
{
db.StringIncrement(key, flags: CommandFlags.FireAndForget);
}
var directResult = db.StringIncrement(key); // last one we wait for (no F+F)
watch.Stop();
TimeSpan directTime = watch.Elapsed;
Assert.Equal(LOOP, (long)scriptResult);
Assert.Equal(LOOP, directResult);
Output.WriteLine("script: {0}ms; direct: {1}ms",
scriptTime.TotalMilliseconds,
directTime.TotalMilliseconds);
}
}
[Fact]
public void TestCallByHash()
{
const string Script = "return redis.call('incr', KEYS[1])";
using (var conn = Create(allowAdmin: true))
{
Skip.IfMissingFeature(conn, nameof(RedisFeatures.Scripting), f => f.Scripting);
var server = conn.GetServer(TestConfig.Current.MasterServerAndPort);
server.FlushAllDatabases();
server.ScriptFlush();
byte[] hash = server.ScriptLoad(Script);
var db = conn.GetDatabase();
RedisKey[] keys = { Me() };
string hexHash = string.Concat(hash.Select(x => x.ToString("X2")));
Assert.Equal("2BAB3B661081DB58BD2341920E0BA7CF5DC77B25", hexHash);
db.ScriptEvaluate(hexHash, keys);
db.ScriptEvaluate(hash, keys);
var count = (int)db.StringGet(keys)[0];
Assert.Equal(2, count);
}
}
[Fact]
public void SimpleLuaScript()
{
const string Script = "return @ident";
using (var conn = Create(allowAdmin: true))
{
Skip.IfMissingFeature(conn, nameof(RedisFeatures.Scripting), f => f.Scripting);
var server = conn.GetServer(TestConfig.Current.MasterServerAndPort);
server.FlushAllDatabases();
server.ScriptFlush();
var prepared = LuaScript.Prepare(Script);
var db = conn.GetDatabase();
{
var val = prepared.Evaluate(db, new { ident = "hello" });
Assert.Equal("hello", (string)val);
}
{
var val = prepared.Evaluate(db, new { ident = 123 });
Assert.Equal(123, (int)val);
}
{
var val = prepared.Evaluate(db, new { ident = 123L });
Assert.Equal(123L, (long)val);
}
{
var val = prepared.Evaluate(db, new { ident = 1.1 });
Assert.Equal(1.1, (double)val);
}
{
var val = prepared.Evaluate(db, new { ident = true });
Assert.True((bool)val);
}
{
var val = prepared.Evaluate(db, new { ident = new byte[] { 4, 5, 6 } });
Assert.True(new byte[] { 4, 5, 6 }.SequenceEqual((byte[])val));
}
}
}
[Fact]
public void LuaScriptWithKeys()
{
const string Script = "redis.call('set', @key, @value)";
using (var conn = Create(allowAdmin: true))
{
Skip.IfMissingFeature(conn, nameof(RedisFeatures.Scripting), f => f.Scripting);
var server = conn.GetServer(TestConfig.Current.MasterServerAndPort);
server.FlushAllDatabases();
server.ScriptFlush();
var script = LuaScript.Prepare(Script);
var db = conn.GetDatabase();
var p = new { key = (RedisKey)"testkey", value = 123 };
script.Evaluate(db, p);
var val = db.StringGet("testkey");
Assert.Equal(123, (int)val);
// no super clean way to extract this; so just abuse InternalsVisibleTo
script.ExtractParameters(p, null, out RedisKey[] keys, out RedisValue[] args);
Assert.NotNull(keys);
Assert.Single(keys);
Assert.Equal("testkey", keys[0]);
}
}
[Fact]
public void NoInlineReplacement()
{
const string Script = "redis.call('set', @key, 'hello@example')";
using (var conn = Create(allowAdmin: true))
{
Skip.IfMissingFeature(conn, nameof(RedisFeatures.Scripting), f => f.Scripting);
var server = conn.GetServer(TestConfig.Current.MasterServerAndPort);
server.FlushAllDatabases();
server.ScriptFlush();
var script = LuaScript.Prepare(Script);
Assert.Equal("redis.call('set', ARGV[1], 'hello@example')", script.ExecutableScript);
var db = conn.GetDatabase();
var p = new { key = (RedisKey)"key" };
script.Evaluate(db, p);
var val = db.StringGet("key");
Assert.Equal("hello@example", val);
}
}
[Fact]
public void EscapeReplacement()
{
const string Script = "redis.call('set', @key, @@escapeMe)";
var script = LuaScript.Prepare(Script);
Assert.Equal("redis.call('set', ARGV[1], @escapeMe)", script.ExecutableScript);
}
[Fact]
public void SimpleLoadedLuaScript()
{
const string Script = "return @ident";
using (var conn = Create(allowAdmin: true))
{
Skip.IfMissingFeature(conn, nameof(RedisFeatures.Scripting), f => f.Scripting);
var server = conn.GetServer(TestConfig.Current.MasterServerAndPort);
server.FlushAllDatabases();
server.ScriptFlush();
var prepared = LuaScript.Prepare(Script);
var loaded = prepared.Load(server);
var db = conn.GetDatabase();
{
var val = loaded.Evaluate(db, new { ident = "hello" });
Assert.Equal("hello", (string)val);
}
{
var val = loaded.Evaluate(db, new { ident = 123 });
Assert.Equal(123, (int)val);
}
{
var val = loaded.Evaluate(db, new { ident = 123L });
Assert.Equal(123L, (long)val);
}
{
var val = loaded.Evaluate(db, new { ident = 1.1 });
Assert.Equal(1.1, (double)val);
}
{
var val = loaded.Evaluate(db, new { ident = true });
Assert.True((bool)val);
}
{
var val = loaded.Evaluate(db, new { ident = new byte[] { 4, 5, 6 } });
Assert.True(new byte[] { 4, 5, 6 }.SequenceEqual((byte[])val));
}
}
}
[Fact]
public void LoadedLuaScriptWithKeys()
{
const string Script = "redis.call('set', @key, @value)";
using (var conn = Create(allowAdmin: true))
{
Skip.IfMissingFeature(conn, nameof(RedisFeatures.Scripting), f => f.Scripting);
var server = conn.GetServer(TestConfig.Current.MasterServerAndPort);
server.FlushAllDatabases();
server.ScriptFlush();
var script = LuaScript.Prepare(Script);
var prepared = script.Load(server);
var db = conn.GetDatabase();
var p = new { key = (RedisKey)"testkey", value = 123 };
prepared.Evaluate(db, p);
var val = db.StringGet("testkey");
Assert.Equal(123, (int)val);
// no super clean way to extract this; so just abuse InternalsVisibleTo
prepared.Original.ExtractParameters(p, null, out RedisKey[] keys, out RedisValue[] args);
Assert.NotNull(keys);
Assert.Single(keys);
Assert.Equal("testkey", keys[0]);
}
}
[Fact]
public void PurgeLuaScriptCache()
{
const string Script = "redis.call('set', @PurgeLuaScriptCacheKey, @PurgeLuaScriptCacheValue)";
var first = LuaScript.Prepare(Script);
var fromCache = LuaScript.Prepare(Script);
Assert.True(ReferenceEquals(first, fromCache));
LuaScript.PurgeCache();
var shouldBeNew = LuaScript.Prepare(Script);
Assert.False(ReferenceEquals(first, shouldBeNew));
}
private static void _PurgeLuaScriptOnFinalize(string script)
{
var first = LuaScript.Prepare(script);
var fromCache = LuaScript.Prepare(script);
Assert.True(ReferenceEquals(first, fromCache));
Assert.Equal(1, LuaScript.GetCachedScriptCount());
}
[Fact]
public void PurgeLuaScriptOnFinalize()
{
const string Script = "redis.call('set', @PurgeLuaScriptOnFinalizeKey, @PurgeLuaScriptOnFinalizeValue)";
LuaScript.PurgeCache();
Assert.Equal(0, LuaScript.GetCachedScriptCount());
// This has to be a separate method to guarantee that the created LuaScript objects go out of scope,
// and are thus available to be GC'd
_PurgeLuaScriptOnFinalize(Script);
GC.Collect(2, GCCollectionMode.Forced, blocking: true);
GC.WaitForPendingFinalizers();
Assert.Equal(0, LuaScript.GetCachedScriptCount());
var shouldBeNew = LuaScript.Prepare(Script);
Assert.Equal(1, LuaScript.GetCachedScriptCount());
}
[Fact]
public void IDatabaseLuaScriptConvenienceMethods()
{
const string Script = "redis.call('set', @key, @value)";
using (var conn = Create(allowAdmin: true))
{
Skip.IfMissingFeature(conn, nameof(RedisFeatures.Scripting), f => f.Scripting);
var script = LuaScript.Prepare(Script);
var db = conn.GetDatabase();
db.ScriptEvaluate(script, new { key = (RedisKey)"key", value = "value" });
var val = db.StringGet("key");
Assert.Equal("value", val);
var prepared = script.Load(conn.GetServer(conn.GetEndPoints()[0]));
db.ScriptEvaluate(prepared, new { key = (RedisKey)"key2", value = "value2" });
var val2 = db.StringGet("key2");
Assert.Equal("value2", val2);
}
}
[Fact]
public void IServerLuaScriptConvenienceMethods()
{
const string Script = "redis.call('set', @key, @value)";
using (var conn = Create(allowAdmin: true))
{
Skip.IfMissingFeature(conn, nameof(RedisFeatures.Scripting), f => f.Scripting);
var script = LuaScript.Prepare(Script);
var server = conn.GetServer(conn.GetEndPoints()[0]);
var db = conn.GetDatabase();
var prepared = server.ScriptLoad(script);
db.ScriptEvaluate(prepared, new { key = (RedisKey)"key3", value = "value3" });
var val = db.StringGet("key3");
Assert.Equal("value3", val);
}
}
[Fact]
public void LuaScriptPrefixedKeys()
{
const string Script = "redis.call('set', @key, @value)";
var prepared = LuaScript.Prepare(Script);
var p = new { key = (RedisKey)"key", value = "hello" };
// no super clean way to extract this; so just abuse InternalsVisibleTo
prepared.ExtractParameters(p, "prefix-", out RedisKey[] keys, out RedisValue[] args);
Assert.NotNull(keys);
Assert.Single(keys);
Assert.Equal("prefix-key", keys[0]);
Assert.Equal(2, args.Length);
Assert.Equal("prefix-key", args[0]);
Assert.Equal("hello", args[1]);
}
[Fact]
public void LuaScriptWithWrappedDatabase()
{
const string Script = "redis.call('set', @key, @value)";
using (var conn = Create(allowAdmin: true))
{
Skip.IfMissingFeature(conn, nameof(RedisFeatures.Scripting), f => f.Scripting);
var db = conn.GetDatabase(0);
var wrappedDb = KeyspaceIsolation.DatabaseExtensions.WithKeyPrefix(db, "prefix-");
var prepared = LuaScript.Prepare(Script);
wrappedDb.ScriptEvaluate(prepared, new { key = (RedisKey)"mykey", value = 123 });
var val1 = wrappedDb.StringGet("mykey");
Assert.Equal(123, (int)val1);
var val2 = db.StringGet("prefix-mykey");
Assert.Equal(123, (int)val2);
var val3 = db.StringGet("mykey");
Assert.True(val3.IsNull);
}
}
[Fact]
public void LoadedLuaScriptWithWrappedDatabase()
{
const string Script = "redis.call('set', @key, @value)";
using (var conn = Create(allowAdmin: true))
{
Skip.IfMissingFeature(conn, nameof(RedisFeatures.Scripting), f => f.Scripting);
var db = conn.GetDatabase(0);
var wrappedDb = KeyspaceIsolation.DatabaseExtensions.WithKeyPrefix(db, "prefix2-");
var server = conn.GetServer(conn.GetEndPoints()[0]);
var prepared = LuaScript.Prepare(Script).Load(server);
wrappedDb.ScriptEvaluate(prepared, new { key = (RedisKey)"mykey", value = 123 });
var val1 = wrappedDb.StringGet("mykey");
Assert.Equal(123, (int)val1);
var val2 = db.StringGet("prefix2-mykey");
Assert.Equal(123, (int)val2);
var val3 = db.StringGet("mykey");
Assert.True(val3.IsNull);
}
}
}
}
using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Xunit;
using Xunit.Abstractions;
namespace StackExchange.Redis.Tests
{
[Collection(NonParallelCollection.Name)]
public class Secure : TestBase
{
protected override string GetConfiguration() =>
TestConfig.Current.MasterServer + ":" + TestConfig.Current.SecurePort + ",password=" + TestConfig.Current.SecurePassword + ",name=MyClient";
public Secure(ITestOutputHelper output) : base (output) { }
[Theory]
[InlineData(true)]
[InlineData(false)]
public void MassiveBulkOpsFireAndForgetSecure(bool preserveOrder)
{
using (var muxer = Create())
{
muxer.PreserveAsyncOrder = preserveOrder;
#if DEBUG
long oldAlloc = ConnectionMultiplexer.GetResultBoxAllocationCount();
#endif
RedisKey key = "MBOF";
var conn = muxer.GetDatabase();
conn.Ping();
var watch = Stopwatch.StartNew();
for (int i = 0; i <= AsyncOpsQty; i++)
{
conn.StringSet(key, i, flags: CommandFlags.FireAndForget);
}
int val = (int)conn.StringGet(key);
Assert.Equal(AsyncOpsQty, val);
watch.Stop();
Output.WriteLine("{2}: Time for {0} ops: {1}ms ({3}); ops/s: {4}", AsyncOpsQty, watch.ElapsedMilliseconds, Me(),
preserveOrder ? "preserve order" : "any order",
AsyncOpsQty / watch.Elapsed.TotalSeconds);
#if DEBUG
long newAlloc = ConnectionMultiplexer.GetResultBoxAllocationCount();
Output.WriteLine("ResultBox allocations: {0}", newAlloc - oldAlloc);
Assert.True(newAlloc - oldAlloc <= 2, $"NewAllocs: {newAlloc}, OldAllocs: {oldAlloc}");
#endif
}
}
[Fact]
public void CheckConfig()
{
var config = ConfigurationOptions.Parse(GetConfiguration());
foreach (var ep in config.EndPoints)
{
Output.WriteLine(ep.ToString());
}
Assert.Single(config.EndPoints);
Assert.Equal("changeme", config.Password);
}
[Fact]
public void Connect()
{
using (var server = Create())
{
server.GetDatabase().Ping();
}
}
[Theory]
[InlineData("wrong")]
[InlineData("")]
public async Task ConnectWithWrongPassword(string password)
{
var config = ConfigurationOptions.Parse(GetConfiguration());
config.Password = password;
config.ConnectRetry = 0; // we don't want to retry on closed sockets in this case.
var ex = await Assert.ThrowsAsync<RedisConnectionException>(async () =>
{
SetExpectedAmbientFailureCount(-1);
using (var conn = await ConnectionMultiplexer.ConnectAsync(config, Writer).ConfigureAwait(false))
{
conn.GetDatabase().Ping();
}
}).ConfigureAwait(false);
Output.WriteLine("Exception: " + ex.Message);
Assert.Equal("It was not possible to connect to the redis server(s); to create a disconnected multiplexer, disable AbortOnConnectFail. AuthenticationFailure on PING", ex.Message);
}
}
}
using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Xunit;
using Xunit.Abstractions;
namespace StackExchange.Redis.Tests
{
[Collection(NonParallelCollection.Name)]
public class Secure : TestBase
{
protected override string GetConfiguration() =>
TestConfig.Current.SecureServerAndPort + ",password=" + TestConfig.Current.SecurePassword + ",name=MyClient";
public Secure(ITestOutputHelper output) : base (output) { }
[Theory]
[InlineData(true)]
[InlineData(false)]
public void MassiveBulkOpsFireAndForgetSecure(bool preserveOrder)
{
using (var muxer = Create())
{
muxer.PreserveAsyncOrder = preserveOrder;
#if DEBUG
long oldAlloc = ConnectionMultiplexer.GetResultBoxAllocationCount();
#endif
RedisKey key = "MBOF";
var conn = muxer.GetDatabase();
conn.Ping();
var watch = Stopwatch.StartNew();
for (int i = 0; i <= AsyncOpsQty; i++)
{
conn.StringSet(key, i, flags: CommandFlags.FireAndForget);
}
int val = (int)conn.StringGet(key);
Assert.Equal(AsyncOpsQty, val);
watch.Stop();
Output.WriteLine("{2}: Time for {0} ops: {1}ms ({3}); ops/s: {4}", AsyncOpsQty, watch.ElapsedMilliseconds, Me(),
preserveOrder ? "preserve order" : "any order",
AsyncOpsQty / watch.Elapsed.TotalSeconds);
#if DEBUG
long newAlloc = ConnectionMultiplexer.GetResultBoxAllocationCount();
Output.WriteLine("ResultBox allocations: {0}", newAlloc - oldAlloc);
Assert.True(newAlloc - oldAlloc <= 2, $"NewAllocs: {newAlloc}, OldAllocs: {oldAlloc}");
#endif
}
}
[Fact]
public void CheckConfig()
{
var config = ConfigurationOptions.Parse(GetConfiguration());
foreach (var ep in config.EndPoints)
{
Output.WriteLine(ep.ToString());
}
Assert.Single(config.EndPoints);
Assert.Equal("changeme", config.Password);
}
[Fact]
public void Connect()
{
using (var server = Create())
{
server.GetDatabase().Ping();
}
}
[Theory]
[InlineData("wrong")]
[InlineData("")]
public async Task ConnectWithWrongPassword(string password)
{
var config = ConfigurationOptions.Parse(GetConfiguration());
config.Password = password;
config.ConnectRetry = 0; // we don't want to retry on closed sockets in this case.
var ex = await Assert.ThrowsAsync<RedisConnectionException>(async () =>
{
SetExpectedAmbientFailureCount(-1);
using (var conn = await ConnectionMultiplexer.ConnectAsync(config, Writer).ConfigureAwait(false))
{
conn.GetDatabase().Ping();
}
}).ConfigureAwait(false);
Output.WriteLine("Exception: " + ex.Message);
Assert.Equal("It was not possible to connect to the redis server(s); to create a disconnected multiplexer, disable AbortOnConnectFail. AuthenticationFailure on PING", ex.Message);
}
}
}
......@@ -18,7 +18,7 @@ public abstract class TestBase : IDisposable
{
protected ITestOutputHelper Output { get; }
protected TextWriterOutputHelper Writer { get; }
protected virtual string GetConfiguration() => TestConfig.Current.MasterServer + ":" + TestConfig.Current.MasterPort + "," + TestConfig.Current.SlaveServer + ":" + TestConfig.Current.SlavePort;
protected virtual string GetConfiguration() => TestConfig.Current.MasterServerAndPort + "," + TestConfig.Current.SlaveServerAndPort;
protected TestBase(ITestOutputHelper output)
{
......@@ -331,4 +331,4 @@ protected static TimeSpan RunConcurrent(Action work, int threads, int timeout =
return watch.Elapsed;
}
}
}
\ No newline at end of file
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment