Commit cad91c7b authored by Volodymyr Kostochka's avatar Volodymyr Kostochka Committed by Marc Gravell

Added: additional transaction conditions (#994)

* Added: additional transaction conditions

* Fixed: failed tests
Added: tests for new transaction conditions

* Conflicts resolving
parent 0ff83549
...@@ -22,7 +22,7 @@ public static Condition HashEqual(RedisKey key, RedisValue hashField, RedisValue ...@@ -22,7 +22,7 @@ public static Condition HashEqual(RedisKey key, RedisValue hashField, RedisValue
{ {
if (hashField.IsNull) throw new ArgumentNullException(nameof(hashField)); if (hashField.IsNull) throw new ArgumentNullException(nameof(hashField));
if (value.IsNull) return HashNotExists(key, hashField); if (value.IsNull) return HashNotExists(key, hashField);
return new EqualsCondition(key, hashField, true, value); return new EqualsCondition(key, RedisType.Hash, hashField, true, value);
} }
/// <summary> /// <summary>
...@@ -46,7 +46,7 @@ public static Condition HashNotEqual(RedisKey key, RedisValue hashField, RedisVa ...@@ -46,7 +46,7 @@ public static Condition HashNotEqual(RedisKey key, RedisValue hashField, RedisVa
{ {
if (hashField.IsNull) throw new ArgumentNullException(nameof(hashField)); if (hashField.IsNull) throw new ArgumentNullException(nameof(hashField));
if (value.IsNull) return HashExists(key, hashField); if (value.IsNull) return HashExists(key, hashField);
return new EqualsCondition(key, hashField, false, value); return new EqualsCondition(key, RedisType.Hash, hashField, false, value);
} }
/// <summary> /// <summary>
...@@ -110,7 +110,7 @@ public static Condition HashNotExists(RedisKey key, RedisValue hashField) ...@@ -110,7 +110,7 @@ public static Condition HashNotExists(RedisKey key, RedisValue hashField)
public static Condition StringEqual(RedisKey key, RedisValue value) public static Condition StringEqual(RedisKey key, RedisValue value)
{ {
if (value.IsNull) return KeyNotExists(key); if (value.IsNull) return KeyNotExists(key);
return new EqualsCondition(key, RedisValue.Null, true, value); return new EqualsCondition(key, RedisType.Hash, RedisValue.Null, true, value);
} }
/// <summary> /// <summary>
...@@ -121,7 +121,7 @@ public static Condition StringEqual(RedisKey key, RedisValue value) ...@@ -121,7 +121,7 @@ public static Condition StringEqual(RedisKey key, RedisValue value)
public static Condition StringNotEqual(RedisKey key, RedisValue value) public static Condition StringNotEqual(RedisKey key, RedisValue value)
{ {
if (value.IsNull) return KeyExists(key); if (value.IsNull) return KeyExists(key);
return new EqualsCondition(key, RedisValue.Null, false, value); return new EqualsCondition(key, RedisType.Hash, RedisValue.Null, false, value);
} }
/// <summary> /// <summary>
...@@ -257,6 +257,52 @@ public static Condition StringNotEqual(RedisKey key, RedisValue value) ...@@ -257,6 +257,52 @@ public static Condition StringNotEqual(RedisKey key, RedisValue value)
/// <param name="member">The member the sorted set must not contain.</param> /// <param name="member">The member the sorted set must not contain.</param>
public static Condition SortedSetNotContains(RedisKey key, RedisValue member) => new ExistsCondition(key, RedisType.SortedSet, member, false); public static Condition SortedSetNotContains(RedisKey key, RedisValue member) => new ExistsCondition(key, RedisType.SortedSet, member, false);
/// <summary>
/// Enforces that the given sorted set member must have the specified score.
/// </summary>
/// <param name="key">The key of the sorted set to check.</param>
/// <param name="member">The member the sorted set to check.</param>
/// <param name="score">The score that member must have.</param>
public static Condition SortedSetEqual(RedisKey key, RedisValue member, RedisValue score) => new EqualsCondition(key, RedisType.SortedSet, member, true, score);
/// <summary>
/// Enforces that the given sorted set member must not have the specified score.
/// </summary>
/// <param name="key">The key of the sorted set to check.</param>
/// <param name="member">The member the sorted set to check.</param>
/// <param name="score">The score that member must not have.</param>
public static Condition SortedSetNotEqual(RedisKey key, RedisValue member, RedisValue score) => new EqualsCondition(key, RedisType.SortedSet, member, false, score);
/// <summary>
/// Enforces that the given sorted set must have the given score.
/// </summary>
/// <param name="key">The key of the sorted set to check.</param>
/// <param name="score">The score that the sorted set must have.</param>
public static Condition SortedSetScoreExists(RedisKey key, RedisValue score) => new SortedSetScoreCondition(key, score, false, 0);
/// <summary>
/// Enforces that the given sorted set must not have the given score.
/// </summary>
/// <param name="key">The key of the sorted set to check.</param>
/// <param name="score">The score that the sorted set must not have.</param>
public static Condition SortedSetScoreNotExists(RedisKey key, RedisValue score) => new SortedSetScoreCondition(key, score, true, 0);
/// <summary>
/// Enforces that the given sorted set must have the specified count of the given score.
/// </summary>
/// <param name="key">The key of the sorted set to check.</param>
/// <param name="score">The score that the sorted set must have.</param>
/// <param name="count">The number of members which sorted set must have.</param>
public static Condition SortedSetScoreExists(RedisKey key, RedisValue score, RedisValue count) => new SortedSetScoreCondition(key, score, true, count);
/// <summary>
/// Enforces that the given sorted set must not have the specified count of the given score.
/// </summary>
/// <param name="key">The key of the sorted set to check.</param>
/// <param name="score">The score that the sorted set must not have.</param>
/// <param name="count">The number of members which sorted set must not have.</param>
public static Condition SortedSetScoreNotExists(RedisKey key, RedisValue score, RedisValue count) => new SortedSetScoreCondition(key, score, false, count);
internal abstract void CheckCommands(CommandMap commandMap); internal abstract void CheckCommands(CommandMap commandMap);
internal abstract IEnumerable<Message> CreateMessages(int db, ResultBox resultBox); internal abstract IEnumerable<Message> CreateMessages(int db, ResultBox resultBox);
...@@ -273,6 +319,11 @@ public static Message CreateMessage(Condition condition, int db, CommandFlags fl ...@@ -273,6 +319,11 @@ public static Message CreateMessage(Condition condition, int db, CommandFlags fl
return new ConditionMessage(condition, db, flags, command, key, value); return new ConditionMessage(condition, db, flags, command, key, value);
} }
public static Message CreateMessage(Condition condition, int db, CommandFlags flags, RedisCommand command, RedisKey key, RedisValue value, RedisValue value1)
{
return new ConditionMessage(condition, db, flags, command, key, value, value1);
}
protected override bool SetResultCore(PhysicalConnection connection, Message message, in RawResult result) protected override bool SetResultCore(PhysicalConnection connection, Message message, in RawResult result)
{ {
connection?.BridgeCouldBeNull?.Multiplexer?.OnTransactionLog($"condition '{message.CommandAndKey}' got '{result.ToString()}'"); connection?.BridgeCouldBeNull?.Multiplexer?.OnTransactionLog($"condition '{message.CommandAndKey}' got '{result.ToString()}'");
...@@ -289,7 +340,8 @@ protected override bool SetResultCore(PhysicalConnection connection, Message mes ...@@ -289,7 +340,8 @@ protected override bool SetResultCore(PhysicalConnection connection, Message mes
private class ConditionMessage : Message.CommandKeyBase private class ConditionMessage : Message.CommandKeyBase
{ {
public readonly Condition Condition; public readonly Condition Condition;
private RedisValue value; private readonly RedisValue value;
private readonly RedisValue value1;
public ConditionMessage(Condition condition, int db, CommandFlags flags, RedisCommand command, RedisKey key, RedisValue value) public ConditionMessage(Condition condition, int db, CommandFlags flags, RedisCommand command, RedisKey key, RedisValue value)
: base(db, flags, command, key) : base(db, flags, command, key)
...@@ -298,6 +350,12 @@ public ConditionMessage(Condition condition, int db, CommandFlags flags, RedisCo ...@@ -298,6 +350,12 @@ public ConditionMessage(Condition condition, int db, CommandFlags flags, RedisCo
this.value = value; // note no assert here this.value = value; // note no assert here
} }
public ConditionMessage(Condition condition, int db, CommandFlags flags, RedisCommand command, RedisKey key, RedisValue value, RedisValue value1)
: this(condition, db, flags, command, key, value)
{
this.value1 = value1; // note no assert here
}
protected override void WriteImpl(PhysicalConnection physical) protected override void WriteImpl(PhysicalConnection physical)
{ {
if (value.IsNull) if (value.IsNull)
...@@ -307,12 +365,16 @@ protected override void WriteImpl(PhysicalConnection physical) ...@@ -307,12 +365,16 @@ protected override void WriteImpl(PhysicalConnection physical)
} }
else else
{ {
physical.WriteHeader(command, 2); physical.WriteHeader(command, value1.IsNull ? 2 : 3);
physical.Write(Key); physical.Write(Key);
physical.WriteBulkString(value); physical.WriteBulkString(value);
if (!value1.IsNull)
{
physical.WriteBulkString(value1);
}
} }
} }
public override int ArgCount => value.IsNull ? 1 : 2; public override int ArgCount => value.IsNull ? 1 : value1.IsNull ? 2 : 3;
} }
} }
...@@ -410,39 +472,52 @@ internal class EqualsCondition : Condition ...@@ -410,39 +472,52 @@ internal class EqualsCondition : Condition
{ {
internal override Condition MapKeys(Func<RedisKey, RedisKey> map) internal override Condition MapKeys(Func<RedisKey, RedisKey> map)
{ {
return new EqualsCondition(map(key), hashField, expectedEqual, expectedValue); return new EqualsCondition(map(key), type, memberName, expectedEqual, expectedValue);
} }
private readonly bool expectedEqual; private readonly bool expectedEqual;
private readonly RedisValue hashField, expectedValue; private readonly RedisValue memberName, expectedValue;
private readonly RedisKey key; private readonly RedisKey key;
public EqualsCondition(RedisKey key, RedisValue hashField, bool expectedEqual, RedisValue expectedValue) private readonly RedisType type;
private readonly RedisCommand cmd;
public EqualsCondition(RedisKey key, RedisType type, RedisValue memberName, bool expectedEqual, RedisValue expectedValue)
{ {
if (key.IsNull) throw new ArgumentException("key"); if (key.IsNull) throw new ArgumentException("key");
this.key = key; this.key = key;
this.hashField = hashField; this.memberName = memberName;
this.expectedEqual = expectedEqual; this.expectedEqual = expectedEqual;
this.expectedValue = expectedValue; this.expectedValue = expectedValue;
this.type = type;
switch (type)
{
case RedisType.Hash:
cmd = memberName.IsNull ? RedisCommand.GET : RedisCommand.HGET;
break;
case RedisType.SortedSet:
cmd = RedisCommand.ZSCORE;
break;
default:
throw new ArgumentException(nameof(type));
}
} }
public override string ToString() public override string ToString()
{ {
return (hashField.IsNull ? key.ToString() : ((string)key) + " > " + hashField) return (memberName.IsNull ? key.ToString() : ((string)key) + " " + type + " > " + memberName)
+ (expectedEqual ? " == " : " != ") + (expectedEqual ? " == " : " != ")
+ expectedValue; + expectedValue;
} }
internal override void CheckCommands(CommandMap commandMap) internal override void CheckCommands(CommandMap commandMap) => commandMap.AssertAvailable(cmd);
{
commandMap.AssertAvailable(hashField.IsNull ? RedisCommand.GET : RedisCommand.HGET);
}
internal sealed override IEnumerable<Message> CreateMessages(int db, ResultBox resultBox) internal sealed override IEnumerable<Message> CreateMessages(int db, ResultBox resultBox)
{ {
yield return Message.Create(db, CommandFlags.None, RedisCommand.WATCH, key); yield return Message.Create(db, CommandFlags.None, RedisCommand.WATCH, key);
var cmd = hashField.IsNull ? RedisCommand.GET : RedisCommand.HGET; var message = ConditionProcessor.CreateMessage(this, db, CommandFlags.None, cmd, key, memberName);
var message = ConditionProcessor.CreateMessage(this, db, CommandFlags.None, cmd, key, hashField);
message.SetSource(ConditionProcessor.Default, resultBox); message.SetSource(ConditionProcessor.Default, resultBox);
yield return message; yield return message;
} }
...@@ -454,19 +529,38 @@ internal override int GetHashSlot(ServerSelectionStrategy serverSelectionStrateg ...@@ -454,19 +529,38 @@ internal override int GetHashSlot(ServerSelectionStrategy serverSelectionStrateg
internal override bool TryValidate(in RawResult result, out bool value) internal override bool TryValidate(in RawResult result, out bool value)
{ {
switch (result.Type) switch (type)
{ {
case ResultType.BulkString: case RedisType.SortedSet:
case ResultType.SimpleString: var parsedValue = RedisValue.Null;
case ResultType.Integer: if (!result.IsNull)
var parsed = result.AsRedisValue(); {
value = (parsed == expectedValue) == expectedEqual; if (result.TryGetDouble(out var val))
ConnectionMultiplexer.TraceWithoutContext("actual: " + (string)parsed + "; expected: " + (string)expectedValue + {
"; wanted: " + (expectedEqual ? "==" : "!=") + "; voting: " + value); parsedValue = val;
}
}
value = (parsedValue == expectedValue) == expectedEqual;
ConnectionMultiplexer.TraceWithoutContext("actual: " + (string)parsedValue + "; expected: " + (string)expectedValue +
"; wanted: " + (expectedEqual ? "==" : "!=") + "; voting: " + value);
return true; return true;
default:
switch (result.Type)
{
case ResultType.BulkString:
case ResultType.SimpleString:
case ResultType.Integer:
var parsed = result.AsRedisValue();
value = (parsed == expectedValue) == expectedEqual;
ConnectionMultiplexer.TraceWithoutContext("actual: " + (string)parsed + "; expected: " + (string)expectedValue +
"; wanted: " + (expectedEqual ? "==" : "!=") + "; voting: " + value);
return true;
}
value = false;
return false;
} }
value = false;
return false;
} }
} }
...@@ -631,6 +725,65 @@ internal override bool TryValidate(in RawResult result, out bool value) ...@@ -631,6 +725,65 @@ internal override bool TryValidate(in RawResult result, out bool value)
return false; return false;
} }
} }
internal class SortedSetScoreCondition : Condition
{
internal override Condition MapKeys(Func<RedisKey, RedisKey> map)
{
return new SortedSetScoreCondition(map(key), sortedSetScore, expectedEqual, expectedValue);
}
private readonly bool expectedEqual;
private readonly RedisValue sortedSetScore, expectedValue;
private readonly RedisKey key;
public SortedSetScoreCondition(RedisKey key, RedisValue sortedSetScore, bool expectedEqual, RedisValue expectedValue)
{
if (key.IsNull)
{
throw new ArgumentException("key");
}
this.key = key;
this.sortedSetScore = sortedSetScore;
this.expectedEqual = expectedEqual;
this.expectedValue = expectedValue;
}
public override string ToString()
{
return key.ToString() + (expectedEqual ? " contains " : " not contains ") + expectedValue + " members with score: " + sortedSetScore;
}
internal override void CheckCommands(CommandMap commandMap) => commandMap.AssertAvailable(RedisCommand.ZCOUNT);
internal sealed override IEnumerable<Message> CreateMessages(int db, ResultBox resultBox)
{
yield return Message.Create(db, CommandFlags.None, RedisCommand.WATCH, key);
var message = ConditionProcessor.CreateMessage(this, db, CommandFlags.None, RedisCommand.ZCOUNT, key, sortedSetScore, sortedSetScore);
message.SetSource(ConditionProcessor.Default, resultBox);
yield return message;
}
internal override int GetHashSlot(ServerSelectionStrategy serverSelectionStrategy) => serverSelectionStrategy.HashSlot(key);
internal override bool TryValidate(in RawResult result, out bool value)
{
switch (result.Type)
{
case ResultType.Integer:
var parsedValue = result.AsRedisValue();
value = (parsedValue == expectedValue) == expectedEqual;
ConnectionMultiplexer.TraceWithoutContext("actual: " + (string)parsedValue + "; expected: " + (string)expectedValue + "; wanted: " + (expectedEqual ? "==" : "!=") + "; voting: " + value);
return true;
}
value = false;
return false;
}
}
} }
/// <summary> /// <summary>
......
...@@ -20,14 +20,14 @@ public TransactionWrapperTests() ...@@ -20,14 +20,14 @@ public TransactionWrapperTests()
public void AddCondition_HashEqual() public void AddCondition_HashEqual()
{ {
wrapper.AddCondition(Condition.HashEqual("key", "field", "value")); wrapper.AddCondition(Condition.HashEqual("key", "field", "value"));
mock.Verify(_ => _.AddCondition(It.Is<Condition>(value => "prefix:key > field == value" == value.ToString()))); mock.Verify(_ => _.AddCondition(It.Is<Condition>(value => "prefix:key Hash > field == value" == value.ToString())));
} }
[Fact] [Fact]
public void AddCondition_HashNotEqual() public void AddCondition_HashNotEqual()
{ {
wrapper.AddCondition(Condition.HashNotEqual("key", "field", "value")); wrapper.AddCondition(Condition.HashNotEqual("key", "field", "value"));
mock.Verify(_ => _.AddCondition(It.Is<Condition>(value => "prefix:key > field != value" == value.ToString()))); mock.Verify(_ => _.AddCondition(It.Is<Condition>(value => "prefix:key Hash > field != value" == value.ToString())));
} }
[Fact] [Fact]
...@@ -72,6 +72,48 @@ public void AddCondition_StringNotEqual() ...@@ -72,6 +72,48 @@ public void AddCondition_StringNotEqual()
mock.Verify(_ => _.AddCondition(It.Is<Condition>(value => "prefix:key != value" == value.ToString()))); mock.Verify(_ => _.AddCondition(It.Is<Condition>(value => "prefix:key != value" == value.ToString())));
} }
[Fact]
public void AddCondition_SortedSetEqual()
{
wrapper.AddCondition(Condition.SortedSetEqual("key", "member", "score"));
mock.Verify(_ => _.AddCondition(It.Is<Condition>(value => "prefix:key SortedSet > member == score" == value.ToString())));
}
[Fact]
public void AddCondition_SortedSetNotEqual()
{
wrapper.AddCondition(Condition.SortedSetNotEqual("key", "member", "score"));
mock.Verify(_ => _.AddCondition(It.Is<Condition>(value => "prefix:key SortedSet > member != score" == value.ToString())));
}
[Fact]
public void AddCondition_SortedSetScoreExists()
{
wrapper.AddCondition(Condition.SortedSetScoreExists("key", "score"));
mock.Verify(_ => _.AddCondition(It.Is<Condition>(value => "prefix:key not contains 0 members with score: score" == value.ToString())));
}
[Fact]
public void AddCondition_SortedSetScoreNotExists()
{
wrapper.AddCondition(Condition.SortedSetScoreNotExists("key", "score"));
mock.Verify(_ => _.AddCondition(It.Is<Condition>(value => "prefix:key contains 0 members with score: score" == value.ToString())));
}
[Fact]
public void AddCondition_SortedSetScoreCountExists()
{
wrapper.AddCondition(Condition.SortedSetScoreExists("key", "score", "count"));
mock.Verify(_ => _.AddCondition(It.Is<Condition>(value => "prefix:key contains count members with score: score" == value.ToString())));
}
[Fact]
public void AddCondition_SortedSetScoreCountNotExists()
{
wrapper.AddCondition(Condition.SortedSetScoreNotExists("key", "score", "count"));
mock.Verify(_ => _.AddCondition(It.Is<Condition>(value => "prefix:key not contains count members with score: score" == value.ToString())));
}
[Fact] [Fact]
public void ExecuteAsync() public void ExecuteAsync()
{ {
......
...@@ -748,6 +748,184 @@ public async Task BasicTranWithSortedSetContainsCondition(bool demandKeyExists, ...@@ -748,6 +748,184 @@ public async Task BasicTranWithSortedSetContainsCondition(bool demandKeyExists,
} }
} }
[Theory]
[InlineData(4D, 4D, true, true)]
[InlineData(4D, 5D, true, false)]
[InlineData(4D, null, true, false)]
[InlineData(null, 5D, true, false)]
[InlineData(null, null, true, true)]
[InlineData(4D, 4D, false, false)]
[InlineData(4D, 5D, false, true)]
[InlineData(4D, null, false, true)]
[InlineData(null, 5D, false, true)]
[InlineData(null, null, false, false)]
public async Task BasicTranWithSortedSetEqualCondition(double? expected, double? value, bool expectEqual, bool expectedTranResult)
{
using (var muxer = Create())
{
RedisKey key = Me(), key2 = Me() + "2";
var db = muxer.GetDatabase();
db.KeyDelete(key, CommandFlags.FireAndForget);
db.KeyDelete(key2, CommandFlags.FireAndForget);
RedisValue member = "member";
if (value != null) db.SortedSetAdd(key2, member, value.Value, flags: CommandFlags.FireAndForget);
Assert.False(db.KeyExists(key));
Assert.Equal(value, db.SortedSetScore(key2, member));
var tran = db.CreateTransaction();
var cond = tran.AddCondition(expectEqual ? Condition.SortedSetEqual(key2, member, expected) : Condition.SortedSetNotEqual(key2, member, expected));
var incr = tran.StringIncrementAsync(key);
var exec = tran.ExecuteAsync();
var get = db.StringGet(key);
Assert.Equal(expectedTranResult, await exec);
if (expectEqual == (value == expected))
{
Assert.True(await exec, "eq: exec");
Assert.True(cond.WasSatisfied, "eq: was satisfied");
Assert.Equal(1, await incr); // eq: incr
Assert.Equal(1, (long)get); // eq: get
}
else
{
Assert.False(await exec, "neq: exec");
Assert.False(cond.WasSatisfied, "neq: was satisfied");
Assert.Equal(TaskStatus.Canceled, SafeStatus(incr)); // neq: incr
Assert.Equal(0, (long)get); // neq: get
}
}
}
[Theory]
[InlineData(true, true, true, true)]
[InlineData(true, false, true, true)]
[InlineData(false, true, true, true)]
[InlineData(true, true, false, false)]
[InlineData(true, false, false, false)]
[InlineData(false, true, false, false)]
[InlineData(false, false, true, false)]
[InlineData(false, false, false, true)]
public async Task BasicTranWithSortedSetScoreExistsCondition(bool member1HasScore, bool member2HasScore, bool demandScoreExists, bool expectedTranResult)
{
using (var muxer = Create())
{
RedisKey key = Me(), key2 = Me() + "2";
var db = muxer.GetDatabase();
db.KeyDelete(key, CommandFlags.FireAndForget);
db.KeyDelete(key2, CommandFlags.FireAndForget);
const double Score = 4D;
RedisValue member1 = "member1";
RedisValue member2 = "member2";
if (member1HasScore)
{
db.SortedSetAdd(key2, member1, Score, flags: CommandFlags.FireAndForget);
}
if (member2HasScore)
{
db.SortedSetAdd(key2, member2, Score, flags: CommandFlags.FireAndForget);
}
Assert.False(db.KeyExists(key));
Assert.Equal(member1HasScore ? (double?)Score : null, db.SortedSetScore(key2, member1));
Assert.Equal(member2HasScore ? (double?)Score : null, db.SortedSetScore(key2, member2));
var tran = db.CreateTransaction();
var cond = tran.AddCondition(demandScoreExists ? Condition.SortedSetScoreExists(key2, Score) : Condition.SortedSetScoreNotExists(key2, Score));
var incr = tran.StringIncrementAsync(key);
var exec = tran.ExecuteAsync();
var get = db.StringGet(key);
Assert.Equal(expectedTranResult, await exec);
if ((member1HasScore || member2HasScore) == demandScoreExists)
{
Assert.True(await exec, "eq: exec");
Assert.True(cond.WasSatisfied, "eq: was satisfied");
Assert.Equal(1, await incr); // eq: incr
Assert.Equal(1, (long)get); // eq: get
}
else
{
Assert.False(await exec, "neq: exec");
Assert.False(cond.WasSatisfied, "neq: was satisfied");
Assert.Equal(TaskStatus.Canceled, SafeStatus(incr)); // neq: incr
Assert.Equal(0, (long)get); // neq: get
}
}
}
[Theory]
[InlineData(true, true, 2L, true, true)]
[InlineData(true, true, 2L, false, false)]
[InlineData(true, true, 1L, true, false)]
[InlineData(true, true, 1L, false, true)]
[InlineData(true, false, 2L, true, false)]
[InlineData(true, false, 2L, false, true)]
[InlineData(true, false, 1L, true, true)]
[InlineData(true, false, 1L, false, false)]
[InlineData(false, true, 2L, true, false)]
[InlineData(false, true, 2L, false, true)]
[InlineData(false, true, 1L, true, true)]
[InlineData(false, true, 1L, false, false)]
[InlineData(false, false, 2L, true, false)]
[InlineData(false, false, 2L, false, true)]
[InlineData(false, false, 1L, true, false)]
[InlineData(false, false, 1L, false, true)]
public async Task BasicTranWithSortedSetScoreCountExistsCondition(bool member1HasScore, bool member2HasScore, long expectedLength, bool expectEqual, bool expectedTranResult)
{
using (var muxer = Create())
{
RedisKey key = Me(), key2 = Me() + "2";
var db = muxer.GetDatabase();
db.KeyDelete(key, CommandFlags.FireAndForget);
db.KeyDelete(key2, CommandFlags.FireAndForget);
const double Score = 4D;
var length = 0L;
RedisValue member1 = "member1";
RedisValue member2 = "member2";
if (member1HasScore)
{
db.SortedSetAdd(key2, member1, Score, flags: CommandFlags.FireAndForget);
length++;
}
if (member2HasScore)
{
db.SortedSetAdd(key2, member2, Score, flags: CommandFlags.FireAndForget);
length++;
}
Assert.False(db.KeyExists(key));
Assert.Equal(length, db.SortedSetLength(key2, Score, Score));
var tran = db.CreateTransaction();
var cond = tran.AddCondition(expectEqual ? Condition.SortedSetScoreExists(key2, Score, expectedLength) : Condition.SortedSetScoreNotExists(key2, Score, expectedLength));
var incr = tran.StringIncrementAsync(key);
var exec = tran.ExecuteAsync();
var get = db.StringGet(key);
Assert.Equal(expectedTranResult, await exec);
if (expectEqual == (length == expectedLength))
{
Assert.True(await exec, "eq: exec");
Assert.True(cond.WasSatisfied, "eq: was satisfied");
Assert.Equal(1, await incr); // eq: incr
Assert.Equal(1, (long)get); // eq: get
}
else
{
Assert.False(await exec, "neq: exec");
Assert.False(cond.WasSatisfied, "neq: was satisfied");
Assert.Equal(TaskStatus.Canceled, SafeStatus(incr)); // neq: incr
Assert.Equal(0, (long)get); // neq: get
}
}
}
[Theory] [Theory]
[InlineData("five", ComparisonType.Equal, 5L, false)] [InlineData("five", ComparisonType.Equal, 5L, false)]
[InlineData("four", ComparisonType.Equal, 4L, true)] [InlineData("four", ComparisonType.Equal, 4L, true)]
......
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