require 'rails_helper'

shared_examples_for AgentControllerConcern do
  describe 'preconditions' do
    it 'must be satisfied for these shared examples' do
      expect(agent.user).to eq(users(:bob))
      expect(agent.control_action).to eq('run')
    end
  end

  describe 'validation' do
    describe 'of action' do
      it 'should allow certain values' do
        ['run', 'enable', 'disable', '{{ action }}'].each { |action|
          agent.options['action'] = action
          expect(agent).to be_valid
        }
      end

      it 'should disallow obviously bad values' do
        ['delete', nil, 1, true].each { |action|
          agent.options['action'] = action
          expect(agent).not_to be_valid
        }
      end

      it "should accept 'run' if all target agents are schedulable" do
        agent.control_targets = [agents(:bob_website_agent)]
        expect(agent).to be_valid
      end

      it "should reject 'run' if targets include an unschedulable agent" do
        agent.control_targets = [agents(:bob_notifier_agent)]
        expect(agent).not_to be_valid
      end

      it "should not reject 'enable' or 'disable' no matter if targets include an unschedulable agent" do
        %w[enable disable].each { |action|
          agent.options['action'] = action
          agent.control_targets = [agents(:bob_notifier_agent)]
          expect(agent).to be_valid
        }
      end

      it "should ensure that 'configure_options' exists in options when the action is 'configure'" do
        agent.options['action'] = 'configure'
        expect(agent).not_to be_valid
        agent.options['configure_options'] = {}
        expect(agent).not_to be_valid
        agent.options['configure_options'] = { 'key' => 'value' }
        expect(agent).to be_valid
      end
    end
  end

  describe 'control_action' do
    it "returns options['action']" do
      expect(agent.control_action).to eq('run')

      %w[run enable disable].each { |action|
        agent.options['action'] = action
        expect(agent.control_action).to eq(action)
      }
    end

    it 'returns the result of interpolation' do
      expect(agent.control_action).to eq('run')

      agent.options['action'] = '{{ "enable" }}'
      expect(agent.control_action).to eq('enable')
    end
  end

  describe 'control!' do
    before do
      agent.control_targets = [agents(:bob_website_agent), agents(:bob_status_agent)]
      agent.save!
    end

    it 'should run targets' do
      control_target_ids = agent.control_targets.map(&:id)
      stub(Agent).async_check(anything) { |id|
        control_target_ids.delete(id)
      }

      agent.control!
      expect(control_target_ids).to be_empty
    end

    it 'should not run disabled targets' do
      control_target_ids = agent.control_targets.map(&:id)
      stub(Agent).async_check(anything) { |id|
        control_target_ids.delete(id)
      }

      agent.control_targets.last.update!(disabled: true)

      agent.control!
      expect(control_target_ids).to eq [agent.control_targets.last.id]
    end

    it 'should enable targets' do
      agent.options['action'] = 'disable'
      agent.save!
      agent.control_targets.first.update!(disabled: true)

      agent.control!
      expect(agent.control_targets.reload).to all(be_disabled)
    end

    it 'should disable targets' do
      agent.options['action'] = 'enable'
      agent.save!
      agent.control_targets.first.update!(disabled: true)

      agent.control!
      expect(agent.control_targets.reload).to all(satisfy { |a| !a.disabled? })
    end

    it 'should configure targets' do
      agent.options['action'] = 'configure'
      agent.options['configure_options'] = { 'url' => 'http://some-new-url.com/{{"something" | upcase}}' }
      agent.save!
      old_options = agents(:bob_website_agent).options

      agent.control!

      expect(agent.control_targets.reload).to all(satisfy { |a| a.options['url'] == 'http://some-new-url.com/SOMETHING' })
      expect(agents(:bob_website_agent).reload.options).to eq(old_options.merge('url' => 'http://some-new-url.com/SOMETHING'))
    end

    it 'should configure targets with nested objects' do
      agent.control_targets = [
        agents(:bob_status_agent), # does not support a `template` option, but anyway
        agents(:bob_data_output_agent)
      ]
      agent.options['action'] = 'configure'
      agent.options['configure_options'] = {
        template: {
          item: {
            title: 'changed'
          }
        }
      }
      agent.save!
      old_options = agents(:bob_data_output_agent).options

      agent.control!

      expect(agent.control_targets.reload).to all(satisfy { |a| a.options['template'] && a.options['template']['item'] && (a.options['template']['item']['title'] == 'changed') })
      expect(agents(:bob_data_output_agent).reload.options).to eq(old_options.deep_merge(agent.options['configure_options']))
    end
  end
end
