# frozen_string_literal: true

require "spec_helper"

# Based on https://github.com/rails/rails/pull/45115/files#diff-35ef6d1bd8b8d3b037ec819a704cd78db55db916a57abfc2859882826fc679b6
RSpec.describe ContentSecurityPolicyPatch, feature_category: :shared do
  include Rack::Test::Methods

  let(:routes) do
    ActionDispatch::Routing::RouteSet.new.tap do |routes|
      routes.draw do
        # Using Testing module defined below
        scope module: "testing" do
          get "/", to: "policy#index"
        end
      end
    end
  end

  let(:csp) do
    ActionDispatch::ContentSecurityPolicy.new do |p|
      p.default_src -> { :self }
      p.script_src -> { :https }
    end
  end

  let(:policy_middleware) do
    Module.new do
      def self.new(app, policy)
        ->(env) do
          env["action_dispatch.content_security_policy"] = policy

          app.call(env)
        end
      end
    end
  end

  subject(:app) do
    build_app(routes) do |middleware|
      middleware.use policy_middleware, csp
      middleware.use ActionDispatch::ContentSecurityPolicy::Middleware
    end
  end

  def setup_controller
    application_controller = Class.new(ActionController::Base) do # rubocop:disable Rails/ApplicationController
      helper_method :sky_is_blue?
      def sky_is_blue?
        true
      end
    end

    policy_controller = Class.new(application_controller) do
      extend ContentSecurityPolicyPatch

      content_security_policy_with_context do |p|
        p.default_src "https://example.com"
        p.script_src "https://example.com" if helpers.sky_is_blue?
      end

      def index
        head :ok
      end
    end

    stub_const("Testing::ApplicationController", application_controller)
    stub_const("Testing::PolicyController", policy_controller)
  end

  def build_app(routes)
    stack = ActionDispatch::MiddlewareStack.new do |middleware|
      middleware.use ActionDispatch::DebugExceptions
      middleware.use ActionDispatch::ActionableExceptions
      middleware.use ActionDispatch::Callbacks
      middleware.use ActionDispatch::Cookies
      middleware.use ActionDispatch::Flash
      middleware.use Rack::MethodOverride
      middleware.use Rack::Head

      yield(middleware) if block_given?
    end

    app = stack.build(routes)

    ->(env) { app.call(env) }
  end

  it "calls helper method" do
    setup_controller

    response = get "/"

    csp_header = response.headers["Content-Security-Policy"]

    expect(csp_header).to include "default-src https://example.com"
    expect(csp_header).to include "script-src https://example.com"
  end

  it "does not emit any warnings" do
    expect { setup_controller }.not_to output.to_stderr
  end

  context "with Rails version 7.2" do
    before do
      version = Gem::Version.new("7.2.0")
      allow(Rails).to receive(:gem_version).and_return(version)
    end

    it "does not emit any warnings" do
      expect { setup_controller }.not_to output.to_stderr
    end
  end
end
