def dummy_strategy_factory(app_handle, env_handle, usable, options_receiver = []) Class.new do attr_reader :options define_method(:initialize) { |options| options_receiver << options } define_method(:app_handle) { app_handle } define_method(:env_handle) { env_handle } define_method(:usable?) { usable } def explain '(options from dummy)' end end end describe Aptible::CLI::Agent do before { subject.stub(:ask) } before { subject.stub(:save_token) } before { subject.stub(:fetch_token) { double 'token' } } before { subject.stub(:attach_to_operation_logs) } let!(:account) { Fabricate(:account) } let!(:app) { Fabricate(:app, handle: 'hello', account: account) } let!(:service) { Fabricate(:service, app: app) } let(:op) { Fabricate(:operation, status: 'succeeded', resource: app) } describe '#apps:scale' do before do allow(Aptible::Api::App).to receive(:all) { [app] } allow(Aptible::Api::Account).to receive(:all) { [account] } end it 'should pass given correct parameters' do allow(subject).to receive(:options) do { app: 'hello', environment: 'foobar' } end expect(service).to receive(:create_operation!) { op } expect(subject).to receive(:environment_from_handle) .with('foobar') .and_return(account) expect(subject).to receive(:apps_from_handle).and_return([app]) subject.send('apps:scale', 'web', 3) end it 'should pass container size param to operation if given' do allow(subject).to receive(:options) do { app: 'hello', size: 90210, environment: 'foobar' } end expect(service).to receive(:create_operation!) .with(type: 'scale', container_count: 3, container_size: 90210) .and_return(op) expect(subject).to receive(:environment_from_handle) .with('foobar') .and_return(account) expect(subject).to receive(:apps_from_handle).and_return([app]) subject.send('apps:scale', 'web', 3) end it 'should fail if environment is non-existent' do allow(subject).to receive(:options) do { environment: 'foo', app: 'web' } end allow(Aptible::Api::Account).to receive(:all) { [] } allow(service).to receive(:create_operation!) { op } expect do subject.send('apps:scale', 'web', 3) end.to raise_error(Thor::Error) end it 'should fail if app is non-existent' do expect do subject.send('apps:scale', 'web', 3) end.to raise_error(Thor::Error) end it 'should fail if number is not a valid number' do allow(subject).to receive(:options) { { app: 'hello' } } allow(service).to receive(:create_operation) { op } expect do subject.send('apps:scale', 'web', 'potato') end.to raise_error(ArgumentError) end it 'should fail if the service does not exist' do allow(subject).to receive(:options) do { app: 'hello', environment: 'foobar' } end expect(subject).to receive(:environment_from_handle) .with('foobar') .and_return(account) expect(subject).to receive(:apps_from_handle).and_return([app]) expect do subject.send('apps:scale', 'potato', 1) end.to raise_error(Thor::Error, /Service.* potato.* does not exist/) end context 'no service' do before { app.services = [] } it 'should fail if the app has no services' do allow(subject).to receive(:options) do { app: 'hello', environment: 'foobar' } end expect(subject).to receive(:environment_from_handle) .with('foobar') .and_return(account) expect(subject).to receive(:apps_from_handle).and_return([app]) expect do subject.send('apps:scale', 'web', 1) end.to raise_error(Thor::Error, /deploy the app first/) end end end describe '#ensure_app' do it 'fails if no usable strategy is found' do strategies = [dummy_strategy_factory(nil, nil, false)] subject.stub(:handle_strategies) { strategies } expect { subject.ensure_app }.to raise_error(/Could not find app/) end it 'fails if an environment is specified but not found' do strategies = [dummy_strategy_factory('hello', 'aptible', true)] subject.stub(:handle_strategies) { strategies } expect(subject).to receive(:environment_from_handle).and_return(nil) expect { subject.ensure_app }.to raise_error(/Could not find environment/) end context 'with apps' do let(:apps) { [app] } before do account.apps = apps allow(Aptible::Api::App).to receive(:all).and_return(apps) end it 'scopes the app search to an environment if provided' do strategies = [dummy_strategy_factory('hello', 'aptible', true)] subject.stub(:handle_strategies) { strategies } expect(subject).to receive(:environment_from_handle).with('aptible') .and_return(account) expect(subject.ensure_app).to eq(apps.first) end it 'does not scope the app search to an environment if not provided' do strategies = [dummy_strategy_factory('hello', nil, true)] subject.stub(:handle_strategies) { strategies } expect(subject.ensure_app).to eq(apps.first) end it 'fails if no app is found' do apps.pop strategies = [dummy_strategy_factory('hello', nil, true)] subject.stub(:handle_strategies) { strategies } expect { subject.ensure_app }.to raise_error(/not find app hello/) end it 'explains the strategy when it fails' do apps.pop strategies = [dummy_strategy_factory('hello', nil, true)] subject.stub(:handle_strategies) { strategies } expect { subject.ensure_app }.to raise_error(/from dummy/) end it 'indicates the environment when the app search was scoped' do apps.pop strategies = [dummy_strategy_factory('hello', 'aptible', true)] subject.stub(:handle_strategies) { strategies } expect(subject).to receive(:environment_from_handle).with('aptible') .and_return(account) expect { subject.ensure_app }.to raise_error(/in environment aptible/) end it 'fails if multiple apps are found' do apps << Fabricate(:app, handle: 'hello') strategies = [dummy_strategy_factory('hello', nil, true)] subject.stub(:handle_strategies) { strategies } expect { subject.ensure_app }.to raise_error(/Multiple apps/) end it 'falls back to another strategy when the first one is unusable' do strategies = [ dummy_strategy_factory('hello', nil, false), dummy_strategy_factory('hello', nil, true) ] subject.stub(:handle_strategies) { strategies } expect(subject.ensure_app).to eq(apps.first) end it 'passes options to the strategy' do receiver = [] strategies = [dummy_strategy_factory('hello', nil, false, receiver)] subject.stub(:handle_strategies) { strategies } options = { app: 'foo', environment: 'bar' } expect { subject.ensure_app options }.to raise_error(/not find app/) expect(receiver).to eq([options]) end end end end