require 'spec_helper'

describe 'VMC::Client' do
  include WebMock::API

  before(:all) do
    @target = VMC::DEFAULT_TARGET
    @local_target = VMC::DEFAULT_LOCAL_TARGET
    @user = 'derek@gmail.com'
    @password = 'foo'
    @auth_token = spec_asset('sample_token.txt')
  end

  before(:each) do
    # make sure these get cleared so we don't have tests pass that shouldn't
    RestClient.proxy = nil
    ENV['http_proxy'] = nil
    ENV['https_proxy'] = nil
  end

  it 'should report its version' do
    VMC::Client.version.should =~ /\d.\d.\d/
  end

  it 'should default to local target' do
    client = VMC::Client.new
    client.target.should == VMC::DEFAULT_TARGET
  end

  it 'should default to use secure protocol' do
    client = VMC::Client.new
    client.target.match(/^https/)
  end

  it 'should normalize target with no scheme' do
    client = VMC::Client.new('api.cloudfoundry.com')
    client.target.should == 'https://api.cloudfoundry.com'
  end

  it 'should properly initialize with auth_token' do
    client = VMC::Client.new(@target, @auth_token)
    client.target.should     == @target
    client.auth_token.should == @auth_token
  end

  it 'should allow login correctly and return an auth_token' do
    login_path = "#{@local_target}/users/#{@user}/tokens"
    stub_request(:post, login_path).to_return(File.new(spec_asset('login_success.txt')))
    client = VMC::Client.new(@local_target)
    auth_token = client.login(@user, @password)
    client.target.should == @local_target
    client.user.should == @user
    client.auth_token.should be
    auth_token.should be
    auth_token.should == client.auth_token
  end

  it 'should raise exception if login fails' do
    login_path = "#{@local_target}/users/#{@user}/tokens"
    stub_request(:post, login_path).to_return(File.new(spec_asset('login_fail.txt')))
    client = VMC::Client.new(@local_target)
    expect { client.login(@user, @password) }.to raise_error(VMC::Client::TargetError)
  end

  it 'should allow admin users to proxy for others' do
    proxy = 'vadim@gmail.com'
    client = VMC::Client.new(@target)
    client.proxy_for(proxy)
    client.proxy.should == proxy
  end

  it 'should properly get info for valid target cloud' do
    info_path = "#{@local_target}/#{VMC::INFO_PATH}"
    stub_request(:get, info_path).to_return(File.new(spec_asset('info_return.txt')))
    client = VMC::Client.new(@local_target)
    info = client.info
    a_request(:get, info_path).should have_been_made.once
    info.should have_key :support
    info.should have_key :description
    info.should have_key :name
    info.should have_key :version
    info.should have_key :build
  end

  it 'should raise and exception for a bad target' do
    info_path = "#{@local_target}/#{VMC::INFO_PATH}"
    stub_request(:get, info_path).to_return(File.new(spec_asset('info_return_bad.txt')))
    client = VMC::Client.new(@local_target)
    expect {info = client.info}.to raise_error(VMC::Client::BadResponse)
    a_request(:get, info_path).should have_been_made.once
  end

  it 'should have target_valid? return true for a good target' do
    info_path = "#{@local_target}/#{VMC::INFO_PATH}"
    stub_request(:get, info_path).to_return(File.new(spec_asset('info_return.txt')))
    client = VMC::Client.new(@local_target)
    client.target_valid?.should be_true
  end

  it 'should have target_valid? return false for a bad target' do
    info_path = "#{@local_target}/#{VMC::INFO_PATH}"
    stub_request(:get, info_path).to_return(File.new(spec_asset('info_return_bad.txt')))
    client = VMC::Client.new(@local_target)
    client.target_valid?.should be_false
  end

  it 'should respond ok if properly logged in' do
    login_path = "#{@local_target}/users/#{@user}/tokens"
    stub_request(:post, login_path).to_return(File.new(spec_asset('login_success.txt')))
    info_path = "#{@local_target}/#{VMC::INFO_PATH}"
    stub_request(:get, info_path).to_return(File.new(spec_asset('info_authenticated.txt')))
    client = VMC::Client.new(@local_target)
    client.login(@user, @password)
    client.logged_in?.should be_true
  end

  it 'should fail when trying to change password unless logged in' do
    login_path = "#{@local_target}/users/#{@user}/tokens"
    stub_request(:post, login_path).to_return(File.new(spec_asset('login_success.txt')))
    user_info_path = "#{@local_target}/users/#{@user}"
    stub_request(:get, user_info_path).to_return(File.new(spec_asset('user_info.txt')))
    stub_request(:put, user_info_path)
    client = VMC::Client.new(@local_target)
    client.login(@user, @password)
    client.change_password('bar')
  end

  it 'should get a proper list of apps' do
    info_path = "#{@local_target}/#{VMC::INFO_PATH}"
    stub_request(:get, info_path).to_return(File.new(spec_asset('info_authenticated.txt')))
    apps_path = "#{@local_target}/#{VMC::APPS_PATH}"
    stub_request(:get, apps_path).to_return(File.new(spec_asset('app_listings.txt')))
    client = VMC::Client.new(@local_target, @auth_token)
    apps = client.apps
    apps.should have(1).items
    app = apps.first
    app.should have_key :state
    app.should have_key :uris
    app.should have_key :name
    app.should have_key :services
    app.should have_key :instances
  end

  it 'should get a proper list of users' do
    info_path = "#{@local_target}/#{VMC::INFO_PATH}"
    stub_request(:get, info_path).to_return(File.new(spec_asset('info_authenticated.txt')))
    users_path = "#{@local_target}/#{VMC::USERS_PATH}"
    stub_request(:get, users_path).to_return(File.new(spec_asset('list_users.txt')))
    client = VMC::Client.new(@local_target, @auth_token)
    users = client.users
    users.should have(4).items
    user = users.first
    user.should have_key :email
    user.should have_key :admin
    user.should have_key :apps
  end

  it 'should get a proper list of services' do
    info_path = "#{@local_target}/#{VMC::INFO_PATH}"
    stub_request(:get, info_path).to_return(File.new(spec_asset('info_authenticated.txt')))
    services_path = "#{@local_target}/#{VMC::Client.path(VMC::GLOBAL_SERVICES_PATH)}"
    stub_request(:get, services_path).to_return(File.new(spec_asset('global_service_listings.txt')))
    client = VMC::Client.new(@local_target, @auth_token)
    services = client.services_info
    services.should have(2).items
    # FIXME, add in more details.
  end

  it 'should get a proper list of provisioned services' do
    info_path = "#{@local_target}/#{VMC::INFO_PATH}"
    stub_request(:get, info_path).to_return(File.new(spec_asset('info_authenticated.txt')))
    services_path = "#{@local_target}/#{VMC::SERVICES_PATH}"
    stub_request(:get, services_path).to_return(File.new(spec_asset('service_listings.txt')))
    client = VMC::Client.new(@local_target, @auth_token)
    app_services = client.services
    app_services.should have(1).items
    redis = app_services.first
    redis.should have_key :type
    redis.should have_key :vendor
  end

  it 'should raise when trying to create an app with no manifest' do
    info_path = "#{@local_target}/#{VMC::INFO_PATH}"
    stub_request(:get, info_path).to_return(File.new(spec_asset('info_authenticated.txt')))
    app_path = "#{@local_target}/#{VMC::APPS_PATH}"
    stub_request(:post, app_path).to_return(File.new(spec_asset('bad_create_app.txt')))
    client = VMC::Client.new(@local_target, @auth_token)
    expect { client.create_app('foo') }.to  raise_error(VMC::Client::NotFound)
  end

  it 'should create an app with a simple manifest' do
    info_path = "#{@local_target}/#{VMC::INFO_PATH}"
    stub_request(:get, info_path).to_return(File.new(spec_asset('info_authenticated.txt')))
    app_path = "#{@local_target}/#{VMC::APPS_PATH}"
    stub_request(:post, app_path).to_return(File.new(spec_asset('good_create_app.txt')))
    client = VMC::Client.new(@local_target, @auth_token)
    manifest = {
      :name => 'foo',
      :uris => ['foo.vcap.me'],
      :instances => 1,
      :staging => { :model => 'nodejs/1.0' },
      :resources => { :memory => 64 }
    }
    client.create_app('foo', manifest)
  end

  it 'should allow us to delete an app we created' do
    info_path = "#{@local_target}/#{VMC::INFO_PATH}"
    stub_request(:get, info_path).to_return(File.new(spec_asset('info_authenticated.txt')))
    app_path = "#{@local_target}/#{VMC::APPS_PATH}/foo"
    stub_request(:delete, app_path).to_return(File.new(spec_asset('delete_app.txt')))
    client = VMC::Client.new(@local_target, @auth_token)
    client.delete_app('foo')
  end

  it 'should provision a service' do
    info_path = "#{@local_target}/#{VMC::INFO_PATH}"
    stub_request(:get, info_path).to_return(File.new(spec_asset('info_authenticated.txt')))
    global_services_path = "#{@local_target}/#{VMC::Client.path(VMC::GLOBAL_SERVICES_PATH)}"
    stub_request(:get, global_services_path).to_return(File.new(spec_asset('global_service_listings.txt')))
    services_path = "#{@local_target}/#{VMC::SERVICES_PATH}"
    stub_request(:post, services_path).to_return(File.new(spec_asset('good_create_service.txt')))
    client = VMC::Client.new(@local_target, @auth_token)
    client.create_service('aws', 'redis', 'foo')
  end

  it 'should complain if we try to provision a service that already exists with same name' do
    info_path = "#{@local_target}/#{VMC::INFO_PATH}"
    stub_request(:get, info_path).to_return(File.new(spec_asset('info_authenticated.txt')))
    global_services_path = "#{@local_target}/#{VMC::Client.path(VMC::GLOBAL_SERVICES_PATH)}"
    stub_request(:get, global_services_path).to_return(File.new(spec_asset('global_service_listings.txt')))
    services_path = "#{@local_target}/#{VMC::SERVICES_PATH}"
    stub_request(:post, services_path).to_return(File.new(spec_asset('service_already_exists.txt')))
    client = VMC::Client.new(@local_target, @auth_token)
    expect { client.create_service('aws','redis', 'foo') }.to raise_error(VMC::Client::NotFound)
  end

  it 'should complain if we try to provision a service that does not exist' do
    info_path = "#{@local_target}/#{VMC::INFO_PATH}"
    stub_request(:get, info_path).to_return(File.new(spec_asset('info_authenticated.txt')))
    global_services_path = "#{@local_target}/#{VMC::Client.path(VMC::GLOBAL_SERVICES_PATH)}"
    stub_request(:get, global_services_path).to_return(File.new(spec_asset('global_service_listings.txt')))
    services_path = "#{@local_target}/#{VMC::SERVICES_PATH}"
    stub_request(:post, services_path).to_return(File.new(spec_asset('service_not_found.txt')))
    client = VMC::Client.new(@local_target, @auth_token)
    expect { client.create_service('aws', 'redis', 'foo') }.to raise_error(VMC::Client::NotFound)
  end

  it 'should allow us to delete a provisioned service' do
    info_path = "#{@local_target}/#{VMC::INFO_PATH}"
    stub_request(:get, info_path).to_return(File.new(spec_asset('info_authenticated.txt')))
    services_path = "#{@local_target}/#{VMC::SERVICES_PATH}"
    stub_request(:get, services_path).to_return(File.new(spec_asset('service_listings.txt')))
    services_path = "#{@local_target}/#{VMC::SERVICES_PATH}/redis-7ed7da9"
    stub_request(:delete, services_path)
    client = VMC::Client.new(@local_target, @auth_token)
    client.delete_service('redis-7ed7da9')
  end

  it 'should bind a service to an app' do
    info_path = "#{@local_target}/#{VMC::INFO_PATH}"
    stub_request(:get, info_path).to_return(File.new(spec_asset('info_authenticated.txt')))
    app_path = "#{@local_target}/#{VMC::APPS_PATH}/foo"
    stub_request(:get, app_path).to_return(File.new(spec_asset('app_info.txt')))
    stub_request(:put, app_path)
    services_path = "#{@local_target}/#{VMC::SERVICES_PATH}"
    # XXX - client only tests that we are not referencing a known service on another infrastructure.
    stub_request(:get, services_path).to_return(File.new(spec_asset('service_listings.txt')))
    client = VMC::Client.new(@local_target, @auth_token)
    client.bind_service('my-redis', 'foo')
    a_request(:get, app_path).should have_been_made.once
    a_request(:put, app_path).should have_been_made.once
  end

  it 'should unbind an existing service from an app' do
    info_path = "#{@local_target}/#{VMC::INFO_PATH}"
    stub_request(:get, info_path).to_return(File.new(spec_asset('info_authenticated.txt')))
    app_path = "#{@local_target}/#{VMC::APPS_PATH}/foo"
    stub_request(:get, app_path).to_return(File.new(spec_asset('app_info.txt')))
    stub_request(:put, app_path)
    client = VMC::Client.new(@local_target, @auth_token)
    client.unbind_service('my-redis', 'foo')
    a_request(:get, app_path).should have_been_made.once
    a_request(:put, app_path).should have_been_made.once
  end

  it 'should set a proxy if one is set' do
    target = "http://nonlocal.domain.com"
    info_path = "#{target}/#{VMC::INFO_PATH}"
    stub_request(:get, info_path).to_return(File.new(spec_asset('info_return.txt')))
    proxy = 'http://proxy.vmware.com:3128'
    ENV['http_proxy'] = proxy
    client = VMC::Client.new(target)
    info = client.info
    RestClient.proxy.should == proxy
  end

  it 'should not set a proxy when accessing localhost' do
    info_path = "#{@local_target}/#{VMC::INFO_PATH}"
    stub_request(:get, info_path).to_return(File.new(spec_asset('info_return.txt')))
    proxy = 'http://proxy.vmware.com:3128'
    ENV['http_proxy'] = proxy
    client = VMC::Client.new(@local_target)
    info = client.info
    RestClient.proxy.should == nil
  end

  it 'should use a secure proxy over a normal proxy if one is set' do
    info_path = "#{@target}/#{VMC::INFO_PATH}"
    stub_request(:get, info_path).to_return(File.new(spec_asset('info_return.txt')))
    proxy = 'http://proxy.vmware.com:3128'
    secure_proxy = 'http://secure-proxy.vmware.com:3128'
    ENV['http_proxy'] = proxy
    ENV['https_proxy'] = secure_proxy
    client = VMC::Client.new(@target)
    info = client.info
    RestClient.proxy.should == secure_proxy
  end

  it 'should not use a secure proxy for non-secure site' do
    target = "http://nonlocal.domain.com"
    info_path = "#{target}/#{VMC::INFO_PATH}"
    stub_request(:get, info_path).to_return(File.new(spec_asset('info_return.txt')))
    proxy = 'http://proxy.vmware.com:3128'
    secure_proxy = 'http://secure-proxy.vmware.com:3128'
    ENV['http_proxy'] = proxy
    ENV['https_proxy'] = secure_proxy
    client = VMC::Client.new(target)
    info = client.info
    RestClient.proxy.should == proxy
  end

  it 'should fail when there is a service gateway failure' do
    info_path = "#{@local_target}/#{VMC::INFO_PATH}"
    stub_request(:get, info_path).to_return(File.new(spec_asset('info_authenticated.txt')))
    global_services_path = "#{@local_target}/#{VMC::Client.path(VMC::GLOBAL_SERVICES_PATH)}"
    stub_request(:get, global_services_path).to_return(File.new(spec_asset('global_service_listings.txt')))
    services_path = "#{@local_target}/#{VMC::SERVICES_PATH}"
    # A service gateway failure will typically happen when provisioning a new service instance -
    # e.g. provisioning too many instances of mysql service.
    stub_request(:post, services_path).to_return(File.new(spec_asset('service_gateway_fail.txt')))
    client = VMC::Client.new(@local_target, @auth_token)
    expect { client.create_service('aws', 'mysql', 'foo') }.to raise_error(VMC::Client::TargetError)
  end

  # WebMock.allow_net_connect!

end
