require 'spec_helper' require 'routemaster/client' require 'routemaster/topic' require 'webmock/rspec' describe Routemaster::Client do let(:options) do { url: 'https://bus.example.com', uuid: 'john_doe' } end subject { described_class.new(options) } before do @stub_pulse = stub_request(:get, %r{^https://#{options[:uuid]}:x@bus.example.com/pulse$}).with(status: 200) end describe '#initialize' do it 'passes with valid arguments' do expect { subject }.not_to raise_error end it 'fails with a non-SSL URL' do options[:url].sub!(/https/, 'http') expect { subject }.to raise_error(ArgumentError) end it 'fails with a bad URL' do options[:url].replace('foobar') expect { subject }.to raise_error(ArgumentError) end it 'fails with a bad client id' do options[:uuid].replace('123 $%') expect { subject }.to raise_error(ArgumentError) end context 'when connection fails' do before do stub_request(:any, %r{^https://#{options[:uuid]}:x@bus.example.com}). to_raise(Faraday::ConnectionFailed) end it 'fails' do expect { subject }.to raise_error end it 'passes if :lazy' do options[:lazy] = true expect { subject }.not_to raise_error end end it 'fails if it does not get a successful heartbeat from the app' do @stub_pulse.to_return(status: 500) expect { subject }.to raise_error end it 'fails if the timeout value is not an integer' do options[:timeout] = 'timeout' expect { subject }.to raise_error end end shared_examples 'an event sender' do let(:callback) { 'https://app.example.com/widgets/123' } let(:topic) { 'widgets' } let(:perform) { subject.send(event, topic, callback) } before do @stub = stub_request( :post, "https://#{options[:uuid]}:x@bus.example.com/topics/widgets" ).with(status: 200) end it 'sends the event' do perform expect(@stub).to have_been_requested end it 'sends a JSON payload' do @stub.with do |req| expect(req.headers['Content-Type']).to eq('application/json') end perform end it 'fails with a bad callback URL' do callback.replace 'http.foo.bar' expect { perform }.to raise_error end it 'fails with a non-SSL URL' do callback.replace 'http://example.com' expect { perform }.to raise_error end it 'fails with a bad topic name' do topic.replace 'foo123$bar' expect { perform }.to raise_error end it 'fails when an non-success HTTP status is returned' do @stub.to_return(status: 500) expect { perform }.to raise_error(RuntimeError) end it 'fails when the timeout is reached' do @stub.to_timeout expect { perform }.to raise_error(Faraday::TimeoutError) end context 'with explicit timestamp' do let(:timestamp) { Time.now.to_f } let(:perform) { subject.send(event, topic, callback, timestamp) } before do @stub = stub_request( :post, "https://#{options[:uuid]}:x@bus.example.com/topics/widgets" ).with(body: { type: anything, url: callback, timestamp: timestamp }, status: 200) end it 'sends the event' do perform expect(@stub).to have_been_requested end context 'with bad timestamp' do let(:timestamp) { 'foo' } it 'fails with non-numeric timestamp' do expect { perform }.to raise_error end end end end describe '#created' do let(:event) { 'created' } it_behaves_like 'an event sender' end describe '#updated' do let(:event) { 'updated' } it_behaves_like 'an event sender' end describe '#deleted' do let(:event) { 'deleted' } it_behaves_like 'an event sender' end describe '#noop' do let(:event) { 'noop' } it_behaves_like 'an event sender' end describe '#subscribe' do let(:perform) { subject.subscribe(subscribe_options) } let(:subscribe_options) {{ topics: %w(widgets kitten), callback: 'https://app.example.com/events', timeout: 60_000, max: 500 }} before do @stub = stub_request( :post, %r{^https://#{options[:uuid]}:x@bus.example.com/subscription$} ).with { |r| r.headers['Content-Type'] == 'application/json' && JSON.parse(r.body).all? { |k,v| subscribe_options[k.to_sym] == v } } end it 'passes with correct arguments' do expect { perform }.not_to raise_error expect(@stub).to have_been_requested end it 'fails with a bad callback' do subscribe_options[:callback] = 'http://example.com' expect { perform }.to raise_error(ArgumentError) end it 'fails with a bad timeout' do subscribe_options[:timeout] = -5 expect { perform }.to raise_error(ArgumentError) end it 'fails with a bad max number of events' do subscribe_options[:max] = 1_000_000 expect { perform }.to raise_error(ArgumentError) end it 'fails with a bad topic list' do subscribe_options[:topics] = ['widgets', 'foo123$%bar'] expect { perform }.to raise_error(ArgumentError) end it 'fails on HTTP error' do @stub.to_return(status: 500) expect { perform }.to raise_error(RuntimeError) end it 'accepts a uuid' do subscribe_options[:uuid] = 'hello' expect { perform }.not_to raise_error end end describe '#monitor_topics' do let(:perform) { subject.monitor_topics } let(:expected_result) do [ { name: 'widgets', publisher: 'demo', events: 12589 } ] end before do @stub = stub_request( :get, %r{^https://#{options[:uuid]}:x@bus.example.com/topics$} ).with { |r| r.headers['Content-Type'] == 'application/json' }.to_return { { status: 200, body: expected_result.to_json } } end it 'expects a collection of topics' do expect(perform.map(&:attributes)).to eql(expected_result) end end describe '#monitor_subscriptions' do it 'passes' end end