# frozen_string_literal: true

require_relative "./helper"
require "sidekiq/web/csrf_protection"

describe "Csrf" do
  def session
    @session ||= {}
  end

  def env(method = :get, form_hash = {}, rack_session = session)
    imp = StringIO.new
    {
      "REQUEST_METHOD" => method.to_s.upcase,
      "rack.session" => rack_session,
      "rack.logger" => ::Logger.new(@logio ||= StringIO.new),
      "rack.input" => imp,
      "rack.request.form_input" => imp,
      "rack.request.form_hash" => form_hash
    }
  end

  def call(env, &block)
    Sidekiq::Web::CsrfProtection.new(block).call(env)
  end

  it "get" do
    ok = [200, {}, ["OK"]]
    first = 1
    second = 1
    result = call(env) do |envy|
      refute_nil envy[:csrf_token]
      assert_equal 88, envy[:csrf_token].size
      first = envy[:csrf_token]
      ok
    end
    assert_equal ok, result

    result = call(env) do |envy|
      refute_nil envy[:csrf_token]
      assert_equal 88, envy[:csrf_token].size
      second = envy[:csrf_token]
      ok
    end
    assert_equal ok, result

    # verify masked token changes on every valid request
    refute_equal first, second
  end

  it "bad post" do
    result = call(env(:post)) do
      raise "Shouldnt be called"
    end
    refute_nil result
    assert_equal 403, result[0]
    assert_equal ["Forbidden"], result[2]

    @logio.rewind
    assert_match(/attack prevented/, @logio.string)
  end

  it "succeeds with good token" do
    # Make a GET to set up the session with a good token
    goodtoken = call(env) do |envy|
      envy[:csrf_token]
    end
    assert goodtoken

    # Make a POST with the known good token
    result = call(env(:post, "authenticity_token" => goodtoken)) do
      [200, {}, ["OK"]]
    end
    refute_nil result
    assert_equal 200, result[0]
    assert_equal ["OK"], result[2]
  end

  it "fails with bad token" do
    # Make a POST with a known bad token
    result = call(env(:post, "authenticity_token" => "N0QRBD34tU61d7fi+0ZaF/35JLW/9K+8kk8dc1TZoK/0pTl7GIHap5gy7BWGsoKlzbMLRp1yaDpCDFwTJtxWAg==")) do
      raise "shouldnt be called"
    end
    refute_nil result
    assert_equal 403, result[0]
    assert_equal ["Forbidden"], result[2]
  end

  it "empty session post" do
    # Make a GET to set up the session with a good token
    goodtoken = call(env) do |envy|
      envy[:csrf_token]
    end
    assert goodtoken

    # Make a POST with an empty session data and good token
    result = call(env(:post, {"authenticity_token" => goodtoken}, {})) do
      raise "shouldnt be called"
    end
    refute_nil result
    assert_equal 403, result[0]
    assert_equal ["Forbidden"], result[2]
  end

  it "empty csrf session post" do
    # Make a GET to set up the session with a good token
    goodtoken = call(env) do |envy|
      envy[:csrf_token]
    end
    assert goodtoken

    # Make a POST without csrf session data and good token
    result = call(env(:post, {"authenticity_token" => goodtoken}, {"session_id" => "foo"})) do
      raise "shouldnt be called"
    end
    refute_nil result
    assert_equal 403, result[0]
    assert_equal ["Forbidden"], result[2]
  end
end
