# frozen_string_literal: true require 'spec_helper' describe Grape::Middleware::Stack do module StackSpec class FooMiddleware; end class BarMiddleware; end class BlockMiddleware attr_reader :block def initialize(&block) @block = block end end end subject { described_class.new } let(:proc) { -> {} } let(:others) { [[:use, StackSpec::BarMiddleware], [:insert_before, StackSpec::BarMiddleware, StackSpec::BlockMiddleware, proc]] } before do subject.use StackSpec::FooMiddleware end describe '#use' do it 'pushes a middleware class onto the stack' do expect { subject.use StackSpec::BarMiddleware } .to change(subject, :size).by(1) expect(subject.last).to eq(StackSpec::BarMiddleware) end it 'pushes a middleware class with arguments onto the stack' do expect { subject.use StackSpec::BarMiddleware, false, my_arg: 42 } .to change(subject, :size).by(1) expect(subject.last).to eq(StackSpec::BarMiddleware) expect(subject.last.args).to eq([false, { my_arg: 42 }]) end it 'pushes a middleware class with block arguments onto the stack' do expect { subject.use StackSpec::BlockMiddleware, &proc } .to change(subject, :size).by(1) expect(subject.last).to eq(StackSpec::BlockMiddleware) expect(subject.last.args).to eq([]) expect(subject.last.block).to eq(proc) end end describe '#insert' do it 'inserts a middleware class at the integer index' do expect { subject.insert 0, StackSpec::BarMiddleware } .to change(subject, :size).by(1) expect(subject[0]).to eq(StackSpec::BarMiddleware) expect(subject[1]).to eq(StackSpec::FooMiddleware) end end describe '#insert_before' do it 'inserts a middleware before another middleware class' do expect { subject.insert_before StackSpec::FooMiddleware, StackSpec::BarMiddleware } .to change(subject, :size).by(1) expect(subject[0]).to eq(StackSpec::BarMiddleware) expect(subject[1]).to eq(StackSpec::FooMiddleware) end it 'inserts a middleware before an anonymous class given by its superclass' do subject.use Class.new(StackSpec::BlockMiddleware) expect { subject.insert_before StackSpec::BlockMiddleware, StackSpec::BarMiddleware } .to change(subject, :size).by(1) expect(subject[1]).to eq(StackSpec::BarMiddleware) expect(subject[2]).to eq(StackSpec::BlockMiddleware) end it 'raises an error on an invalid index' do expect { subject.insert_before StackSpec::BlockMiddleware, StackSpec::BarMiddleware } .to raise_error(RuntimeError, 'No such middleware to insert before: StackSpec::BlockMiddleware') end end describe '#insert_after' do it 'inserts a middleware after another middleware class' do expect { subject.insert_after StackSpec::FooMiddleware, StackSpec::BarMiddleware } .to change(subject, :size).by(1) expect(subject[1]).to eq(StackSpec::BarMiddleware) expect(subject[0]).to eq(StackSpec::FooMiddleware) end it 'inserts a middleware after an anonymous class given by its superclass' do subject.use Class.new(StackSpec::BlockMiddleware) expect { subject.insert_after StackSpec::BlockMiddleware, StackSpec::BarMiddleware } .to change(subject, :size).by(1) expect(subject[1]).to eq(StackSpec::BlockMiddleware) expect(subject[2]).to eq(StackSpec::BarMiddleware) end it 'raises an error on an invalid index' do expect { subject.insert_after StackSpec::BlockMiddleware, StackSpec::BarMiddleware } .to raise_error(RuntimeError, 'No such middleware to insert after: StackSpec::BlockMiddleware') end end describe '#merge_with' do it 'applies a collection of operations and middlewares' do expect { subject.merge_with(others) } .to change(subject, :size).by(2) expect(subject[0]).to eq(StackSpec::FooMiddleware) expect(subject[1]).to eq(StackSpec::BlockMiddleware) expect(subject[2]).to eq(StackSpec::BarMiddleware) end context 'middleware spec with proc declaration exists' do let(:middleware_spec_with_proc) { [:use, StackSpec::FooMiddleware, proc] } it 'properly forwards spec arguments' do expect(subject).to receive(:use).with(StackSpec::FooMiddleware) subject.merge_with([middleware_spec_with_proc]) end end end describe '#build' do it 'returns a rack builder instance' do expect(subject.build).to be_instance_of(Rack::Builder) end context 'when @others are present' do let(:others) { [[:insert_after, Grape::Middleware::Formatter, StackSpec::BarMiddleware]] } it 'applies the middleware specs stored in @others' do subject.concat others subject.use Grape::Middleware::Formatter subject.build expect(subject[0]).to eq StackSpec::FooMiddleware expect(subject[1]).to eq Grape::Middleware::Formatter expect(subject[2]).to eq StackSpec::BarMiddleware end end end describe '#concat' do it 'adds non :use specs to @others' do expect { subject.concat others }.to change(subject, :others).from([]).to([[others.last]]) end it 'calls +merge_with+ with the :use specs' do expect(subject).to receive(:merge_with).with [[:use, StackSpec::BarMiddleware]] subject.concat others end end end