require 'helper' require 'flipper/adapters/http' require 'flipper/adapters/pstore' require 'flipper/spec/shared_adapter_specs' require 'rack/handler/webrick' FLIPPER_SPEC_API_PORT = ENV.fetch('FLIPPER_SPEC_API_PORT', 9001).to_i RSpec.describe Flipper::Adapters::Http do context 'adapter' do subject do described_class.new(url: "http://localhost:#{FLIPPER_SPEC_API_PORT}") end before :all do dir = FlipperRoot.join('tmp').tap(&:mkpath) log_path = dir.join('flipper_adapters_http_spec.log') @pstore_file = dir.join('flipper.pstore') @pstore_file.unlink if @pstore_file.exist? api_adapter = Flipper::Adapters::PStore.new(@pstore_file) flipper_api = Flipper.new(api_adapter) app = Flipper::Api.app(flipper_api) server_options = { Port: FLIPPER_SPEC_API_PORT, StartCallback: -> { @started = true }, Logger: WEBrick::Log.new(log_path.to_s, WEBrick::Log::INFO), AccessLog: [ [log_path.open('w'), WEBrick::AccessLog::COMBINED_LOG_FORMAT], ], } @server = WEBrick::HTTPServer.new(server_options) @server.mount '/', Rack::Handler::WEBrick, app Thread.new { @server.start } Timeout.timeout(1) { :wait until @started } end after :all do @server.shutdown if @server end before(:each) do @pstore_file.unlink if @pstore_file.exist? end it_should_behave_like 'a flipper adapter' it "can enable and disable unregistered group" do flipper = Flipper.new(subject) expect(flipper[:search].enable_group(:some_made_up_group)).to be(true) expect(flipper[:search].groups_value).to eq(Set["some_made_up_group"]) expect(flipper[:search].disable_group(:some_made_up_group)).to be(true) expect(flipper[:search].groups_value).to eq(Set.new) end end it "sends default headers" do headers = { 'Accept' => 'application/json', 'Content-Type' => 'application/json', 'User-Agent' => "Flipper HTTP Adapter v#{Flipper::VERSION}", } stub_request(:get, "http://app.com/flipper/features/feature_panel") .with(headers: headers) .to_return(status: 404, body: "", headers: {}) adapter = described_class.new(url: 'http://app.com/flipper') adapter.get(flipper[:feature_panel]) end describe "#get" do it "raises error when not successful response" do stub_request(:get, "http://app.com/flipper/features/feature_panel") .to_return(status: 503, body: "", headers: {}) adapter = described_class.new(url: 'http://app.com/flipper') expect do adapter.get(flipper[:feature_panel]) end.to raise_error(Flipper::Adapters::Http::Error) end end describe "#get_multi" do it "raises error when not successful response" do stub_request(:get, "http://app.com/flipper/features?keys=feature_panel") .to_return(status: 503, body: "", headers: {}) adapter = described_class.new(url: 'http://app.com/flipper') expect do adapter.get_multi([flipper[:feature_panel]]) end.to raise_error(Flipper::Adapters::Http::Error) end end describe "#get_all" do it "raises error when not successful response" do stub_request(:get, "http://app.com/flipper/features") .to_return(status: 503, body: "", headers: {}) adapter = described_class.new(url: 'http://app.com/flipper') expect do adapter.get_all end.to raise_error(Flipper::Adapters::Http::Error) end end describe "#features" do it "raises error when not successful response" do stub_request(:get, "http://app.com/flipper/features") .to_return(status: 503, body: "", headers: {}) adapter = described_class.new(url: 'http://app.com/flipper') expect do adapter.features end.to raise_error(Flipper::Adapters::Http::Error) end end describe 'configuration' do let(:debug_output) { object_double($stderr) } let(:options) do { url: 'http://app.com/mount-point', headers: { 'X-Custom-Header' => 'foo' }, basic_auth_username: 'username', basic_auth_password: 'password', read_timeout: 100, open_timeout: 40, write_timeout: 40, debug_output: debug_output, } end subject { described_class.new(options) } let(:feature) { flipper[:feature_panel] } before do stub_request(:get, %r{\Ahttp://app.com*}).to_return(body: fixture_file('feature.json')) end it 'allows client to set request headers' do subject.get(feature) expect( a_request(:get, 'http://app.com/mount-point/features/feature_panel') .with(headers: { 'X-Custom-Header' => 'foo' }) ).to have_been_made.once end it 'allows client to set basic auth' do subject.get(feature) expect( a_request(:get, 'http://app.com/mount-point/features/feature_panel') .with(basic_auth: %w(username password)) ).to have_been_made.once end it 'allows client to set debug output' do user_agent = Net::HTTP.new("app.com") allow(Net::HTTP).to receive(:new).and_return(user_agent) expect(user_agent).to receive(:set_debug_output).with(debug_output) subject.get(feature) end end def fixture_file(name) fixtures_path = File.expand_path('../../../fixtures', __FILE__) File.new(fixtures_path + '/' + name) end end