require 'redis'
require 'uncomplicated_mutex'
require 'minitest'
require 'minitest/autorun'

class TestUncomplicatedMutex < Minitest::Test
  class SlowObject
    attr_accessor :id
    def initialize
      @id = Time.now.to_i
    end
  end

  def setup
    @obj = SlowObject.new
    @redis = Redis.new
    default_opts = { timeout: 1, ticks: 10, redis: @redis }
    @mutex1 = UncomplicatedMutex.new(@obj, default_opts)
    @mutex2 = UncomplicatedMutex.new(@obj, default_opts)
    @lock_name = @mutex1.lock_name
  end

  def test_mutex_works
    @mutex1.lock do
      assert_equal @redis.exists(@lock_name), true
    end
    assert_equal @redis.exists(@lock_name), false
  end

  def test_sequential_access
    @redis.set('lock:testvalue', 1)
    @mutex1.lock do
      @redis.set('lock:testvalue', 2)
    end
    @mutex2.lock do
      assert_equal(@redis.get('lock:testvalue'), '2')
      @redis.del('lock:testvalue')
    end
    assert_equal @redis.exists('lock:testvalue'), false
  end

  def test_exception_is_thrown
    begin
      @redis.set(@lock_name, 1)
      UncomplicatedMutex.new(@obj, { timeout: 1, fail_on_timeout: true, ticks: 10 }).lock do
        sleep 2
      end
    rescue UncomplicatedMutex::MutexTimeout
      pass "Exception thrown"
    else
      flunk "Exception was not thrown"
    ensure
      @redis.del(@lock_name)
    end
  end

  def test_exception_is_not_thrown
    begin
      @redis.set(@lock_name, 1)
      @mutex2.lock do
        sleep 1.05
      end
    rescue UncomplicatedMutex::MutexTimeout
      flunk "Exception thrown"
    else
      pass "Exception was not thrown"
    ensure
      @redis.del(@lock_name)
    end
  end

  def test_lock_is_not_overwritten
    @mutex1.lock do
      @redis.set(@lock_name, 'abc123')
    end
    assert_equal(@redis.get(@lock_name), 'abc123')
    @redis.del(@lock_name)
  end
end