require 'test_helper'

class ProxyTest < ActiveSupport::TestCase
  def setup
    @account = FactoryGirl.create(:simple_account)
    @service = FactoryGirl.create(:simple_service, account: @account)
    @proxy   = @service.proxy
  end

  def teardown
    ::WebMock.reset!
  end

  class NoContextNecessary < ActiveSupport::TestCase

    def test_set_sandbox_endpoint_callback
      Proxy.any_instance.expects(:set_sandbox_endpoint).at_least_once
      FactoryGirl.create(:simple_proxy)
    end

    def test_set_production_endpoint_callback
      Proxy.any_instance.expects(:set_production_endpoint).at_least_once
      FactoryGirl.create(:simple_proxy)
    end
  end

  def test_deploy_production
    @proxy.expects(:ready_to_deploy?).returns(true).once

    @account.expects(:provider_can_use?).returns(true)
    @proxy.expects(:deploy_production_v2)
    @proxy.deploy_production

    @account.expects(:provider_can_use?).returns(false)
    @account.expects(:deploy_production_apicast)
    @proxy.deploy_production
  end

  test 'hosts' do
    @proxy.endpoint = 'http://foobar.example.com:3000/path'
    @proxy.sandbox_endpoint = 'http://example.com:8080'

    Proxy.stubs(:config).returns(
      sandbox_endpoint: 'http://sandbox.example.com',
      hosted_proxy_endpoint: 'http://hosted.example.com'
    )

    assert_equal %W(foobar.example.com example.com), @proxy.hosts
  end

  test 'hosts with invalid urls' do
    @proxy.endpoint = 'some#invalid /host'
    @proxy.sandbox_endpoint = '$other invalid host'
    Proxy.stubs(:config).returns({})

    assert_equal %w(localhost), @proxy.hosts

    @proxy.sandbox_endpoint = 'http://example.com'

    assert_equal %w(localhost example.com), @proxy.hosts
  end

  test 'backend' do
    proxy_config = System::Application.config.three_scale.sandbox_proxy
    proxy_config.stubs(backend_scheme: 'https', backend_host: 'foobar.com:4400')
    assert_equal({ endpoint: 'https://foobar.com:4400', host: 'foobar.com' }, @proxy.backend)
  end

  test "proxy_enabled makes sure there's at least 1 proxy rule" do
    @proxy.api_backend = 'https://lala.com:3'
    @proxy.save!
    assert_equal 1, @proxy.proxy_rules.size
  end

  test 'api_backend defaults to echo API' do
    assert_equal 'https://echo-api.3scale.net:443', @proxy.api_backend
  end

  test 'proxy api backend formats ok' do
    ['http://foo.com:9', 'https://lala.com:3'].each do |endpoint|
      @proxy.api_backend = endpoint
      @proxy.endpoint = endpoint
      @proxy.secret_token = '123'
      assert @proxy.valid?, @proxy.errors.full_messages.to_sentence
    end
  end

  test 'hostname_rewrite_for_sandbox' do
    @proxy.api_backend = 'https://echo-api.3scale.net:443'
    assert_equal 'echo-api.3scale.net', @proxy.hostname_rewrite_for_sandbox

    @proxy.hostname_rewrite = 'my-own.api.com'
    assert_equal 'my-own.api.com', @proxy.hostname_rewrite_for_sandbox

    # this is actually an invalid state and should not appear
    @proxy.api_backend = nil
    @proxy.hostname_rewrite = ''
    assert_equal 'none', @proxy.hostname_rewrite_for_sandbox
  end

  test 'oauth login url formats ok' do
    @proxy.api_backend = "https://foo.com:89"
    @proxy.secret_token = "lskdjfkljdsf"
    ['https://foobar.com?baz', 'https://www.lala.com', 'localhost'].each do |login_url|
      @proxy.oauth_login_url = login_url
      assert @proxy.valid?, "#{@proxy.errors.full_messages.to_sentence} - #{login_url}"
    end
  end

  test 'oauth login url formats invalid' do
    ['lalal dsa' , 'https://fdsa --fdsa', "foo\nbar", "'", '"',
     "https://foo.com?scope=fdsa","https://foo.com?state=fdsa",
     'https://foo.com?tok%0x3dfoo', "https://foo.com?tok=fdsa",
     "http://www.voo.com", "foo"].each do |login_url|
      @proxy.oauth_login_url = login_url
      @proxy.valid?
      assert @proxy.errors[:oauth_login_url], "no errors found on - #{login_url}".presence
    end
  end

  test 'api_test_path formats valid' do
    [  '/', '/i/m/a/lumberjack/42', '/~stuff', '/!not_-here']. each do |path|
      @proxy.api_test_path = path
      @proxy.valid?
      assert_empty @proxy.errors[:api_test_path], "errors found on - #{path}"
    end
  end

  test 'api_test_path formats invalid' do
    [ "http://foo_bar.com:32/fdsa" ].each do |path|
      @proxy.api_test_path = path
      @proxy.valid?
      assert @proxy.errors[:api_test_path], "errors not found on - #{path}".presence
    end
  end


  test 'proxy api backend formats invalid' do
    ['foo.com:9', "fdsfas", "ssh://foo.com:39",
     "http://foo_bar.com:32/fdsa", "http://foo.com:32/fdsa" ].each do |endpoint|

      @proxy.api_backend = endpoint
      @proxy.update_attributes endpoint: endpoint
      @proxy.secret_token = '123'
      @proxy.valid?

      # Endpoints are rewritten before validation now to do the
      # migration from old sandbox proxies to new ones


      # @assert @proxy.errors[:endpoint], "no endpoint errors found on - #{endpoint}".presence

      assert @proxy.errors[:api_backend], "no api_backend errors found on - #{endpoint}".presence
    end
  end

  test '#endpoint_port' do
    proxy = FactoryGirl.build_stubbed(:proxy)

    proxy.endpoint = nil
    assert_equal 80, proxy.endpoint_port

    proxy.endpoint = ''
    assert_equal 80, proxy.endpoint_port

    proxy.endpoint = 'foo bar invalid'
    assert_equal 80, proxy.endpoint_port

    proxy.endpoint = 'http://example.com/foo'
    assert_equal 80, proxy.endpoint_port

    proxy.endpoint = 'http://example.com:8080/foo'
    assert_equal 8080, proxy.endpoint_port

    proxy.endpoint = 'https://example.com/bar'
    assert_equal 443, proxy.endpoint_port

    proxy.endpoint = 'https://example.com:4433/bar'
    assert_equal 4433, proxy.endpoint_port
  end

  test 'proxy api backend auto port 80' do
    endpoint = 'http://foo.com'
    @proxy.api_backend = endpoint
    @proxy.update_attributes endpoint: endpoint
    @proxy.secret_token = '123'
    @proxy.valid?

    assert @proxy.api_backend = 'http://foo.com:80'
    assert @proxy.errors[:api_backend].blank?, "no api_backend errors found on - #{endpoint}"
  end

  test 'proxy api backend auto port 443' do
    endpoint = 'https://foo.com'
    @proxy.api_backend = endpoint
    @proxy.update_attributes endpoint: endpoint
    @proxy.secret_token = '123'
    @proxy.valid?

    assert @proxy.api_backend = 'https://foo.com:443'
    assert @proxy.errors[:api_backend].blank?, "no api_backend errors found on - #{endpoint}"
  end

  test 'proxy_endpoint is unique' do
    @proxy.update_attributes(endpoint: 'http://foo:80', api_backend: 'http://A:1', secret_token: 'fdsa')
    p2 = Factory(:proxy, endpoint: 'http://foo:80', service: @service, api_backend: 'http://B:1', secret_token: 'fdsafsda')
  end

  test 'credentials names should allow good params' do
    m = Factory(:metric, service: @service)

    %W[ foo bar-baz bar_baz].each do |w|
      @proxy.auth_app_id = w
      @proxy.auth_user_key= w
      @proxy.auth_app_key = w

      # to trigger the validations
      @proxy.valid?

      assert @proxy.errors[:auth_app_id].blank?
      assert @proxy.errors[:auth_user_key].blank?
      assert @proxy.errors[:auth_app_key].blank?
    end
  end

  test 'credentials names cannot have strange params' do
    m = Factory(:metric, service: @service)

    %W|foo/bar {fdsa} fda [fdsa]|.each do |w|
      @proxy.auth_app_id = w
      @proxy.auth_user_key= w
      @proxy.auth_app_key = w

      @proxy.valid?

      assert @proxy.errors[:auth_app_id],   "auth_app_id".presence
      assert @proxy.errors[:auth_user_key], "auth_user_key".presence
      assert @proxy.errors[:auth_app_key],  "auth_app_key".presence
    end
  end

  test 'sandbox_endpoint set on creation' do
    assert_match %r{^https://}, @proxy.sandbox_endpoint
  end

  test 'api_test_path' do
    assert_equal '/', @proxy.api_test_path
  end

  test 'protected domains' do
    Rails.stubs(:env).returns(ActiveSupport::StringInquirer.new('production'))
    %W(localhost 127.0.0.1).each do |b|
      @proxy.api_backend = "http://#{b}:80"
      @proxy.valid?
      assert @proxy.errors[:api_backend],  "#{b} is protected".presence
    end
  end

  test 'allowed domains' do
    Rails.stubs(:env).returns(ActiveSupport::StringInquirer.new('production'))
    %W(api.twitter.com).each do |b|
      @proxy.update_attributes(api_backend: "http://#{b}:80")
      assert @proxy.errors[:api_backend].blank?
    end
  end

  test 'sandbox_deployed? when last proxy log entry says so' do
    proxy = FactoryGirl.create(:proxy, created_at: Time.now)
    refute proxy.sandbox_deployed?
    FactoryGirl.create(:proxy_log, provider: proxy.service.account, status: 'Deployed successfully.', created_at: Time.now - 1.minute)
    refute proxy.sandbox_deployed?
    FactoryGirl.create(:proxy_log, provider: proxy.service.account, status: 'Deployed successfully.', created_at: Time.now + 1.minute)
    assert proxy.sandbox_deployed?
    FactoryGirl.create(:proxy_log, provider: proxy.service.account, status: 'Deploy failed.', created_at: Time.now + 2.minutes)
    refute proxy.sandbox_deployed?
  end

  test 'send_api_test_request!' do
    proxy = FactoryGirl.build_stubbed(:proxy,
                                      api_backend: 'http://api-sentiment.3scale.net:80',
                                      sandbox_endpoint: 'http://proxy:80',
                                      api_test_path: '/v1/word/stuff.json',
                                      secret_token: '123')
    stub_request(:get, 'http://proxy/v1/word/stuff.json?user_key=USER_KEY')
        .to_return(status: 201, body: '', headers: {})

    analytics.expects(:track).with('Sandbox Proxy Test Request', has_entries(success: true, uri: 'http://proxy/v1/word/stuff.json', status: 201))
    assert proxy.send_api_test_request!
  end

  test 'send_api_test_request! with oauth' do
    proxy = FactoryGirl.build_stubbed(:proxy,
                                      api_backend: 'http://api-sentiment.3scale.net:80',
                                      sandbox_endpoint: 'http://proxy:80',
                                      api_test_path: '/v1/word/stuff.json',
                                      secret_token: '123')
    proxy.service.backend_version = 'oauth'
    proxy.api_test_success = true
    proxy.send_api_test_request!

    assert_nil proxy.api_test_success
  end

  test 'save_and_deploy' do
    proxy = FactoryGirl.build(:proxy, api_backend: 'http://example.com', api_test_path: '/path')
    ::ProviderProxyDeploymentService.any_instance.expects(:deploy).with(proxy).returns(true)

    analytics.expects(:track).with('Sandbox Proxy Deploy', success: true)
    analytics.expects(:track).with('Sandbox Proxy updated',
                                   api_backend: 'http://example.com:80',
                                   api_test_path: '/path',
                                   success: true)
    Logic::RollingUpdates.stubs(skipped?: true)

    assert proxy.save_and_deploy
    assert proxy.persisted?
  end

  test 'authentication_params_for_proxy' do
    @proxy.service.update_attribute(:backend_version, '1')
    assert_equal({ 'user_key' => 'USER_KEY' }, @proxy.reload.authentication_params_for_proxy)

    @proxy.update_attribute(:auth_user_key, 'my-auth')
    assert_equal({ 'my-auth' => 'USER_KEY' }, @proxy.authentication_params_for_proxy)
    assert_equal({ 'user_key' => 'USER_KEY' }, @proxy.authentication_params_for_proxy(original_names: true))

    @proxy.service.update_attribute(:backend_version, '2')
    assert_equal({"app_id"=>"APP_ID", "app_key"=>"APP_KEY"}, @proxy.reload.authentication_params_for_proxy)
  end

  def test_set_correct_endpoint
    @proxy.set_correct_endpoint('on_premise')
    assert_nil @proxy.endpoint

    @proxy.set_correct_endpoint('on_3scale')
    assert_not_nil @proxy.endpoint

    assert_nil @proxy.set_correct_endpoint('non-existing-ssssomething')
    assert_not_nil @proxy.endpoint
  end

  def analytics
    ThreeScale::Analytics::UserTracking.any_instance
  end
end
