# frozen_string_literal: true

require_relative 'test_helper'

module Dynflow
  module MiddlewareTest
    describe 'Middleware' do
      let(:world) { WorldFactory.create_world }
      let(:log) { Support::MiddlewareExample::LogMiddleware.log }

      before do
        Support::MiddlewareExample::LogMiddleware.reset_log
      end

      it "wraps the action method calls" do
        delay = world.delay(Support::MiddlewareExample::LoggingAction, { :start_at => Time.now.utc - 60 }, {})
        plan = world.persistence.load_delayed_plan delay.execution_plan_id
        plan.plan
        plan.execute.future.wait
        _(log).must_equal %w[LogMiddleware::before_delay
                             delay
                             LogMiddleware::after_delay
                             LogMiddleware::before_plan_phase
                             LogMiddleware::before_plan
                             plan
                             LogMiddleware::after_plan
                             LogMiddleware::after_plan_phase
                             LogMiddleware::before_run
                             run
                             LogMiddleware::after_run
                             LogMiddleware::before_finalize_phase
                             LogMiddleware::before_finalize
                             finalize
                             LogMiddleware::after_finalize
                             LogMiddleware::after_finalize_phase]
      end

      it "inherits the middleware" do
        world.trigger(Support::MiddlewareExample::SubAction, {}).finished.wait
        _(log).must_equal %w[LogRunMiddleware::before_run
                             AnotherLogRunMiddleware::before_run
                             run
                             AnotherLogRunMiddleware::after_run
                             LogRunMiddleware::after_run]
      end

      describe "world.middleware" do
        let(:world_with_middleware) do
          WorldFactory.create_world.tap do |world|
            world.middleware.use(Support::MiddlewareExample::AnotherLogRunMiddleware)
          end
        end

        it "puts the middleware to the beginning of the stack" do
          world_with_middleware.trigger(Support::MiddlewareExample::Action, {}).finished.wait
          _(log).must_equal %w[AnotherLogRunMiddleware::before_run
                               LogRunMiddleware::before_run
                               run
                               LogRunMiddleware::after_run
                               AnotherLogRunMiddleware::after_run]
        end
      end

      describe "rules" do
        describe "before" do
          specify do
            world.trigger(Support::MiddlewareExample::SubActionBeforeRule, {}).finished.wait
            _(log).must_equal %w[AnotherLogRunMiddleware::before_run
                                 LogRunMiddleware::before_run
                                 run
                                 LogRunMiddleware::after_run
                                 AnotherLogRunMiddleware::after_run]
          end
        end

        describe "after" do
          let(:world_with_middleware) do
            WorldFactory.create_world.tap do |world|
              world.middleware.use(Support::MiddlewareExample::AnotherLogRunMiddleware,
                after: Support::MiddlewareExample::LogRunMiddleware)
            end
          end

          specify do
            world_with_middleware.trigger(Support::MiddlewareExample::Action, {}).finished.wait
            _(log).must_equal %w[LogRunMiddleware::before_run
                                 AnotherLogRunMiddleware::before_run
                                 run
                                 AnotherLogRunMiddleware::after_run
                                 LogRunMiddleware::after_run]
          end
        end

        describe "replace" do
          specify do
            world.trigger(Support::MiddlewareExample::SubActionReplaceRule, {}).finished.wait
            _(log).must_equal %w[AnotherLogRunMiddleware::before_run
                                 run
                                 AnotherLogRunMiddleware::after_run]
          end
        end

        describe "remove" do
          specify do
            world.trigger(Support::MiddlewareExample::SubActionDoNotUseRule, {}).finished.wait
            _(log).must_equal %w[run]
          end
        end
      end

      it "allows access the running action" do
        world = WorldFactory.create_world
        world.middleware.use(Support::MiddlewareExample::ObservingMiddleware,
          replace: Support::MiddlewareExample::LogRunMiddleware)
        world.trigger(Support::MiddlewareExample::Action, message: 'hello').finished.wait
        _(log).must_equal %w[input#message:hello
                             run
                             output#message:finished]
      end

      it "allows modification of the running action when delaying execution" do
        world = WorldFactory.create_world
        world.middleware.use(Support::MiddlewareExample::AnotherObservingMiddleware,
          replace: Support::MiddlewareExample::LogRunMiddleware)
        delay = world.delay(Support::MiddlewareExample::Action, { :start_at => Time.now - 60 })
        plan = world.persistence.load_delayed_plan delay.execution_plan_id
        plan.plan
        plan.execute.future.wait
        _(log).must_equal ["delay#set-input:#{world.id}",
                           "plan#input:#{world.id}",
                           "input#message:#{world.id}",
                           'run',
                           'output#message:finished']
      end

      describe 'Presnet middleware' do
        let(:world_with_middleware) do
          WorldFactory.create_world.tap do |world|
            world.middleware.use(Support::MiddlewareExample::FilterSensitiveData)
          end
        end

        let :execution_plan do
          result = world.trigger(Support::CodeWorkflowExample::IncomingIssue, issue_data)
          _(result).must_be :planned?
          result.finished.value
        end

        let :execution_plan_2 do
          result = world.trigger(Support::MiddlewareExample::SecretAction)
          _(result).must_be :planned?
          result.finished.value
        end

        let :filtered_execution_plan do
          world_with_middleware.persistence.load_execution_plan(execution_plan.id)
        end

        let :issue_data do
          { 'author' => 'Harry Potter', 'text' => 'Lord Voldemort is comming' }
        end

        let :presenter do
          filtered_execution_plan.root_plan_step.action filtered_execution_plan
        end

        let :presenter_2 do
          execution_plan_2.root_plan_step.action execution_plan_2
        end

        let :presenter_without_middleware do
          execution_plan.root_plan_step.action execution_plan
        end

        it 'filters the data ===' do
          _(presenter.input['text']).must_equal('You-Know-Who is comming')
          _(presenter_2.output['spell']).must_equal('***')
        end

        it "doesn't affect stored data" do
          _(presenter.input['text']).must_equal('You-Know-Who is comming')
          _(presenter_without_middleware.input['text']).must_equal('Lord Voldemort is comming')
        end
      end
    end
  end
end
