# encoding: UTF-8 require "helper" class TestTransactions < Test::Unit::TestCase include Helper::Client def test_multi_discard r.multi assert_equal "QUEUED", r.set("foo", "1") assert_equal "QUEUED", r.get("foo") r.discard assert_equal nil, r.get("foo") end def test_multi_exec_with_a_block r.multi do |multi| multi.set "foo", "s1" end assert_equal "s1", r.get("foo") end def test_multi_exec_with_a_block_doesn_t_return_replies_for_multi_and_exec r1, r2, nothing_else = r.multi do |multi| multi.set "foo", "s1" multi.get "foo" end assert_equal "OK", r1 assert_equal "s1", r2 assert_equal nil, nothing_else end def test_assignment_inside_multi_exec_block r.multi do |m| @first = m.sadd("foo", 1) @second = m.sadd("foo", 1) end assert_equal true, @first.value assert_equal false, @second.value end # Although we could support accessing the values in these futures, # it doesn't make a lot of sense. def test_assignment_inside_multi_exec_block_with_delayed_command_errors assert_raise(Redis::CommandError) do r.multi do |m| @first = m.set("foo", "s1") @second = m.incr("foo") # not an integer @third = m.lpush("foo", "value") # wrong kind of value end end assert_equal "OK", @first.value assert_raise(Redis::CommandError) { @second.value } assert_raise(Redis::FutureNotReady) { @third.value } end def test_assignment_inside_multi_exec_block_with_immediate_command_errors assert_raise(Redis::CommandError) do r.multi do |m| m.doesnt_exist @first = m.sadd("foo", 1) m.doesnt_exist @second = m.sadd("foo", 1) m.doesnt_exist end end assert_raise(Redis::FutureNotReady) { @first.value } assert_raise(Redis::FutureNotReady) { @second.value } end def test_raise_immediate_errors_in_multi_exec assert_raise(RuntimeError) do r.multi do |multi| multi.set "bar", "s2" raise "Some error" multi.set "baz", "s3" end end assert_equal nil, r.get("bar") assert_equal nil, r.get("baz") end def test_transformed_replies_as_return_values_for_multi_exec_block info, _ = r.multi do |m| r.info end assert info.kind_of?(Hash) end def test_transformed_replies_inside_multi_exec_block r.multi do |m| @info = r.info end assert @info.value.kind_of?(Hash) end def test_raise_command_errors_in_multi_exec assert_raise(Redis::CommandError) do r.multi do |m| m.set("foo", "s1") m.incr("foo") # not an integer m.lpush("foo", "value") # wrong kind of value end end assert_equal "s1", r.get("foo") end def test_raise_command_errors_when_accessing_futures_after_multi_exec begin r.multi do |m| m.set("foo", "s1") @counter = m.incr("foo") # not an integer end rescue Exception # Not gonna deal with it end # We should test for Redis::Error here, but hiredis doesn't yet do # custom error classes. err = nil begin @counter.value rescue => err end assert err.kind_of?(RuntimeError) end def test_multi_with_a_block_yielding_the_client r.multi do |multi| multi.set "foo", "s1" end assert_equal "s1", r.get("foo") end def test_raise_command_error_when_exec_fails redis_mock(:exec => lambda { |*_| "-ERROR" }) do |redis| assert_raise(Redis::CommandError) do redis.multi do |m| m.set "foo", "s1" end end end end def test_watch res = r.watch "foo" assert_equal "OK", res end def test_watch_with_an_unmodified_key r.watch "foo" r.multi do |multi| multi.set "foo", "s1" end assert_equal "s1", r.get("foo") end def test_watch_with_an_unmodified_key_passed_as_array r.watch ["foo", "bar"] r.multi do |multi| multi.set "foo", "s1" end assert_equal "s1", r.get("foo") end def test_watch_with_a_modified_key r.watch "foo" r.set "foo", "s1" res = r.multi do |multi| multi.set "foo", "s2" end assert_equal nil, res assert_equal "s1", r.get("foo") end def test_watch_with_a_modified_key_passed_as_array r.watch ["foo", "bar"] r.set "foo", "s1" res = r.multi do |multi| multi.set "foo", "s2" end assert_equal nil, res assert_equal "s1", r.get("foo") end def test_watch_with_a_block_and_an_unmodified_key result = r.watch "foo" do |rd| assert_same r, rd rd.multi do |multi| multi.set "foo", "s1" end end assert_equal ["OK"], result assert_equal "s1", r.get("foo") end def test_watch_with_a_block_and_a_modified_key result = r.watch "foo" do |rd| assert_same r, rd rd.set "foo", "s1" rd.multi do |multi| multi.set "foo", "s2" end end assert_equal nil, result assert_equal "s1", r.get("foo") end def test_watch_with_a_block_that_raises_an_exception r.set("foo", "s1") begin r.watch "foo" do raise "test" end rescue RuntimeError end r.set("foo", "s2") # If the watch was still set from within the block above, this multi/exec # would fail. This proves that raising an exception above unwatches. r.multi do |multi| multi.set "foo", "s3" end assert_equal "s3", r.get("foo") end def test_unwatch_with_a_modified_key r.watch "foo" r.set "foo", "s1" r.unwatch r.multi do |multi| multi.set "foo", "s2" end assert_equal "s2", r.get("foo") end end