# frozen_string_literal: true require_relative 'test_helper' require 'mocha/minitest' require 'minitest/stub_const' require 'ostruct' require 'sidekiq' require 'dynflow/executors/sidekiq/core' module Dynflow module RedisLockingTest describe Executors::Sidekiq::RedisLocking do class Orchestrator include Executors::Sidekiq::RedisLocking attr_accessor :logger def initialize(world, logger) @world = world @logger = logger end end class Logger attr_reader :logs def initialize @logs = [] end [:info, :error, :fatal].each do |key| define_method key do |message| @logs << [key, message] end end end after do ::Sidekiq.redis do |conn| conn.del Executors::Sidekiq::RedisLocking::REDIS_LOCK_KEY end end def redis_orchestrator_id ::Sidekiq.redis do |conn| conn.get Executors::Sidekiq::RedisLocking::REDIS_LOCK_KEY end end let(:world) { OpenStruct.new(:id => '12345') } let(:world2) { OpenStruct.new(:id => '67890') } let(:orchestrator) { Orchestrator.new(world, Logger.new) } let(:orchestrator2) { Orchestrator.new(world2, Logger.new) } it 'acquires the lock when it is not taken' do orchestrator.wait_for_orchestrator_lock logs = orchestrator.logger.logs _(redis_orchestrator_id).must_equal world.id _(logs).must_equal [[:info, 'Acquired orchestrator lock, entering active mode.']] end it 'reacquires the lock if it was lost' do orchestrator.reacquire_orchestrator_lock logs = orchestrator.logger.logs _(redis_orchestrator_id).must_equal world.id _(logs).must_equal [[:error, 'The orchestrator lock was lost, reacquired']] end it 'terminates the process if lock was stolen' do orchestrator.wait_for_orchestrator_lock Process.expects(:kill) orchestrator2.reacquire_orchestrator_lock logs = orchestrator2.logger.logs _(redis_orchestrator_id).must_equal world.id _(logs).must_equal [[:fatal, 'The orchestrator lock was stolen by 12345, aborting.']] end it 'polls for the lock availability' do Executors::Sidekiq::RedisLocking.stub_const(:REDIS_LOCK_TTL, 1) do Executors::Sidekiq::RedisLocking.stub_const(:REDIS_LOCK_POLL_INTERVAL, 0.5) do orchestrator.wait_for_orchestrator_lock _(redis_orchestrator_id).must_equal world.id orchestrator2.wait_for_orchestrator_lock end end _(redis_orchestrator_id).must_equal world2.id passive, active = orchestrator2.logger.logs _(passive).must_equal [:info, 'Orchestrator lock already taken, entering passive mode.'] _(active).must_equal [:info, 'Acquired orchestrator lock, entering active mode.'] end end end end