spec/slack/real_time/client_spec.rb in slack-ruby-client-0.14.4 vs spec/slack/real_time/client_spec.rb in slack-ruby-client-0.14.5

- old
+ new

@@ -1,74 +1,91 @@ +# frozen_string_literal: true require 'spec_helper' -RSpec.describe Slack::RealTime::Client do +RSpec.describe Slack::RealTime::Client do # rubocop:disable Metrics/BlockLength let(:ws) { double(Slack::RealTime::Concurrency::Mock::WebSocket, on: true) } + before do @token = ENV.delete('SLACK_API_TOKEN') Slack::Config.reset Slack::RealTime::Config.reset Slack::RealTime.configure do |config| config.concurrency = Slack::RealTime::Concurrency::Mock end end + after do ENV['SLACK_API_TOKEN'] = @token if @token end + context 'token' do before do Slack.configure do |config| config.token = 'global default' end end + it 'defaults token to global default' do - client = Slack::RealTime::Client.new + client = described_class.new expect(client.token).to eq 'global default' expect(client.web_client.token).to eq 'global default' end context 'with real time config' do before do - Slack::RealTime::Client.configure do |config| + described_class.configure do |config| config.token = 'custom real time token' end end + it 'overrides token to real time config' do - client = Slack::RealTime::Client.new + client = described_class.new expect(client.token).to eq 'custom real time token' expect(client.web_client.token).to eq 'custom real time token' end it 'overrides token to specific token' do - client = Slack::RealTime::Client.new(token: 'local token') + client = described_class.new(token: 'local token') expect(client.token).to eq 'local token' expect(client.web_client.token).to eq 'local token' end end end + context 'websocket_ping_timer' do context 'with defaults' do - let(:client) { Slack::RealTime::Client.new } + let(:client) { described_class.new } + it 'defaults to websocket_ping / 2' do expect(client.websocket_ping_timer).to eq 15 end end + context 'with websocket_ping value changed' do - let(:client) { Slack::RealTime::Client.new(websocket_ping: 22) } + let(:client) { described_class.new(websocket_ping: 22) } + it 'defaults to websocket_ping / 2' do expect(client.websocket_ping_timer).to eq 11 end end end - context 'client with a full store', vcr: { cassette_name: 'web/rtm_start', allow_playback_repeats: true } do - let(:client) { Slack::RealTime::Client.new(store_class: Slack::RealTime::Stores::Store) } + + context 'client with a full store', + vcr: { cassette_name: 'web/rtm_start', allow_playback_repeats: true } do + let(:client) { described_class.new(store_class: Slack::RealTime::Stores::Store) } let(:url) { 'wss://ms173.slack-msgs.com/websocket/lqcUiAvrKTP-uuid=' } + describe '#start!' do let(:socket) { double(Slack::RealTime::Socket, connected?: true) } + before do - allow(Slack::RealTime::Socket).to receive(:new).with(url, ping: 30, logger: Slack::Logger.default).and_return(socket) + allow(Slack::RealTime::Socket).to( + receive(:new).with(url, ping: 30, logger: Slack::Logger.default).and_return(socket) + ) allow(socket).to receive(:connect!) allow(socket).to receive(:start_sync) client.start! end + describe 'properties provided upon connection' do it 'sets url' do expect(client.url).to eq url end it 'sets team' do @@ -99,13 +116,16 @@ end it 'sets groups' do expect(client.groups.count).to eq 1 end it 'includes team name in to_s' do - expect(client.to_s).to eq "id=#{client.team.id}, name=#{client.team.name}, domain=#{client.team.domain}" + expect(client.to_s).to eq( + "id=#{client.team.id}, name=#{client.team.name}, domain=#{client.team.domain}" + ) end end + it 'uses web client to fetch url' do expect(client.web_client).to be_a Slack::Web::Client end it 'remembers socket' do expect(client.instance_variable_get('@socket')).to eq socket @@ -115,26 +135,29 @@ client.start! end.to raise_error Slack::RealTime::Client::ClientAlreadyStartedError end describe '#stop!' do before do - expect(socket).to receive(:disconnect!) + allow(socket).to receive(:disconnect!) client.stop! end + it 'cannot be invoked twice' do client.instance_variable_set('@socket', nil) # caused by a :close callback expect do client.stop! end.to raise_error Slack::RealTime::Client::ClientNotStartedError end end + describe '#next_id' do it 'increments' do previous_id = client.send(:next_id) expect(client.send(:next_id)).to eq previous_id + 1 end end + context 'subclassed' do it 'runs event handlers' do event = Slack::RealTime::Event.new( 'type' => 'team_rename', 'name' => 'New Team Name Inc.' @@ -142,18 +165,23 @@ client.send(:dispatch, event) expect(client.store.team.name).to eq 'New Team Name Inc.' end end end + describe '#start_async' do let(:socket) { double(Slack::RealTime::Socket, connected?: true) } + before do - allow(Slack::RealTime::Socket).to receive(:new).with(url, ping: 30, logger: Slack::Logger.default).and_return(socket) + allow(Slack::RealTime::Socket).to( + receive(:new).with(url, ping: 30, logger: Slack::Logger.default).and_return(socket) + ) allow(socket).to receive(:connect!) allow(socket).to receive(:start_async) client.start_async end + describe '#run_ping!' do it 'sends ping messages when the websocket connection is idle' do allow(socket).to receive(:time_since_last_message).and_return(30) expect(socket).to receive(:send_data).with('{"type":"ping","id":1}') client.run_ping! @@ -163,38 +191,66 @@ allow(socket).to receive(:connected?).and_return(true) expect(socket).to receive(:close) expect(socket).to receive(:restart_async) client.run_ping! end - [EOFError, Errno::ECONNRESET, Errno::EPIPE, Faraday::ClientError].each do |err| + [ + EOFError, + Errno::ECONNRESET, + Errno::EPIPE, + Faraday::ClientError, + Slack::Web::Api::Errors::SlackError + ].each do |err| context "raising #{err}" do it 'does not terminate the ping worker' do allow(socket).to receive(:time_since_last_message) { raise err } - expect(socket).to_not receive(:send_data) + expect(socket).not_to receive(:send_data) client.run_ping! end end end + context 'raising Slack::Web::Api::Errors::SlackError' do + %w[invalid_auth account_inactive].each do |code| + context code do + it 'does not terminate the ping worker' do + allow(socket).to receive(:time_since_last_message) { + raise Slack::Web::Api::Errors::SlackError, code + } + expect(socket).not_to receive(:send_data) + expect do + client.run_ping! + end.to raise_error Slack::Web::Api::Errors::SlackError, code + end + end + end + end end end + describe 'to_s' do it 'defaults to class instance' do expect(client.to_s).to match(/^#<Slack::RealTime::Client:0x\h+>$/) end end end + context 'client with starter store', vcr: { cassette_name: 'web/rtm_connect' } do - let(:client) { Slack::RealTime::Client.new(store_class: Slack::RealTime::Stores::Starter) } + let(:client) { described_class.new(store_class: Slack::RealTime::Stores::Starter) } let(:url) { 'wss://mpmulti-w5tz.slack-msgs.com/websocket/uid' } + describe '#start!' do let(:socket) { double(Slack::RealTime::Socket, connected?: true) } + before do - allow(Slack::RealTime::Socket).to receive(:new).with(url, ping: 30, logger: Slack::Logger.default).and_return(socket) + allow(Slack::RealTime::Socket).to( + receive(:new).with(url, ping: 30, logger: Slack::Logger.default).and_return(socket) + ) allow(socket).to receive(:connect!) allow(socket).to receive(:start_sync) client.start! end + describe 'properties provided upon connection' do it 'sets url' do expect(client.url).to eq url end it 'sets team' do @@ -220,13 +276,16 @@ end it 'no groups' do expect(client.groups).to be_nil end it 'includes team name in to_s' do - expect(client.to_s).to eq "id=#{client.team.id}, name=#{client.team.name}, domain=#{client.team.domain}" + expect(client.to_s).to eq( + "id=#{client.team.id}, name=#{client.team.name}, domain=#{client.team.domain}" + ) end end + it 'uses web client to fetch url' do expect(client.web_client).to be_a Slack::Web::Client end it 'remembers socket' do expect(client.instance_variable_get('@socket')).to eq socket @@ -236,40 +295,44 @@ client.start! end.to raise_error Slack::RealTime::Client::ClientAlreadyStartedError end describe '#stop!' do before do - expect(socket).to receive(:disconnect!) + allow(socket).to receive(:disconnect!) client.stop! end + it 'cannot be invoked twice' do client.instance_variable_set('@socket', nil) # caused by a :close callback expect do client.stop! end.to raise_error Slack::RealTime::Client::ClientNotStartedError end end + describe '#next_id' do it 'increments' do previous_id = client.send(:next_id) expect(client.send(:next_id)).to eq previous_id + 1 end end end end + context 'client with nil store', vcr: { cassette_name: 'web/rtm_connect' } do - let(:client) { Slack::RealTime::Client.new(store_class: nil) } + let(:client) { described_class.new(store_class: nil) } let(:url) { 'wss://mpmulti-w5tz.slack-msgs.com/websocket/uid' } + it 'sets store to nil' do expect(client.store).to be nil end it "doesn't handle events" do event = Slack::RealTime::Event.new( 'type' => 'team_rename', 'name' => 'New Team Name Inc.' ) - expect(client).to_not receive(:run_handlers) + expect(client).not_to receive(:run_handlers) client.send(:dispatch, event) end it 'self' do expect(client.self).to be nil end @@ -280,12 +343,14 @@ it 'defaults to class instance' do expect(client.to_s).to match(/^#<Slack::RealTime::Client:0x\h+>$/) end end end + context 'client with defaults' do - let(:client) { Slack::RealTime::Client.new } + let(:client) { described_class.new } + describe '#initialize' do it 'sets ping' do expect(client.websocket_ping).to eq 30 end it "doesn't set proxy" do @@ -301,10 +366,11 @@ it "sets #{key}" do expect(client.send(key)).to eq Slack::RealTime::Config.send(key) end end end + describe '#run_ping?' do it 'returns true when websocket_ping is greater than 0' do client.websocket_ping = 30 expect(client.run_ping?).to be true end @@ -314,66 +380,78 @@ client.websocket_ping = nil expect(client.run_ping?).to be false end end end + context 'with custom settings' do describe '#initialize' do Slack::RealTime::Config::ATTRIBUTES.each do |key| - context key do - let(:client) { Slack::RealTime::Client.new(key => 'custom') } + context key.to_s do + let(:client) { described_class.new(key => 'custom') } + it "sets #{key}" do - expect(client.send(key)).to_not eq Slack::RealTime::Config.send(key) + expect(client.send(key)).not_to eq Slack::RealTime::Config.send(key) expect(client.send(key)).to eq 'custom' end end end end + describe 'logger accessor' do - let(:client) { Slack::RealTime::Client.new } + let(:client) { described_class.new } + it 'exposes public logger' do expect(client.logger).to be_a(::Logger) end it 'exposes public logger=' do expect { client.logger = nil }.not_to raise_error(NoMethodError) end end end + context 'global config' do after do - Slack::RealTime::Client.config.reset + described_class.config.reset end + let(:url) { 'wss://ms173.slack-msgs.com/websocket/lqcUiAvrKTP-uuid=' } - let(:client) { Slack::RealTime::Client.new } + let(:client) { described_class.new } + context 'ping' do before do - Slack::RealTime::Client.configure do |config| + described_class.configure do |config| config.websocket_ping = 15 end end + describe '#initialize' do it 'sets ping' do expect(client.websocket_ping).to eq 15 end it 'creates a connection with custom ping', vcr: { cassette_name: 'web/rtm_start' } do - expect(Slack::RealTime::Concurrency::Mock::WebSocket).to receive(:new).with(url, nil, ping: 15).and_return(ws) + expect(Slack::RealTime::Concurrency::Mock::WebSocket).to( + receive(:new).with(url, nil, ping: 15).and_return(ws) + ) client.start! end it 'sets start_options' do expect(client.start_options).to eq(request: { timeout: 180 }) end end end + context 'proxy' do before do - Slack::RealTime::Client.configure do |config| + described_class.configure do |config| config.websocket_proxy = { origin: 'http://username:password@proxy.example.com', headers: { 'User-Agent' => 'ruby' } } end end + describe '#initialize' do it 'sets proxy' do expect(client.websocket_proxy).to eq( origin: 'http://username:password@proxy.example.com', headers: { 'User-Agent' => 'ruby' } @@ -391,114 +469,134 @@ ).and_return(ws) client.start! end end end + context 'start_options' do before do - Slack::RealTime::Client.configure do |config| + described_class.configure do |config| config.start_options = { simple_latest: true } end end + describe '#initialize' do it 'sets start_options' do expect(client.start_options).to eq(simple_latest: true) end context 'start!' do let(:socket) { double(Slack::RealTime::Socket, connected?: true) } + before do allow(Slack::RealTime::Socket).to receive(:new).and_return(socket) allow(socket).to receive(:connect!) allow(socket).to receive(:start_sync) end + it 'calls rtm_start with start options', vcr: { cassette_name: 'web/rtm_start' } do - expect(client.web_client).to receive(:rtm_start).with(simple_latest: true).and_call_original + expect(client.web_client).to( + receive(:rtm_start).with(simple_latest: true).and_call_original + ) client.start! end end end end + context 'store_class' do context 'starter' do before do - Slack::RealTime::Client.configure do |config| + described_class.configure do |config| config.store_class = Slack::RealTime::Stores::Starter end end + describe '#initialize' do it 'can be overriden explicitly' do - client = Slack::RealTime::Client.new(store_class: Slack::RealTime::Store) + client = described_class.new(store_class: Slack::RealTime::Store) expect(client.send(:store_class)).to eq Slack::RealTime::Store end it 'sets store_class' do expect(client.send(:store_class)).to eq(Slack::RealTime::Stores::Starter) end context 'start!' do let(:socket) { double(Slack::RealTime::Socket, connected?: true) } + before do allow(Slack::RealTime::Socket).to receive(:new).and_return(socket) allow(socket).to receive(:connect!) allow(socket).to receive(:start_sync) end + it 'instantiates the correct store class', vcr: { cassette_name: 'web/rtm_connect' } do client.start! expect(client.store).to be_a Slack::RealTime::Stores::Starter end end end end + context 'store' do before do - Slack::RealTime::Client.configure do |config| + described_class.configure do |config| config.store_class = Slack::RealTime::Stores::Store end end + describe '#initialize' do context 'start!' do let(:socket) { double(Slack::RealTime::Socket, connected?: true) } + before do allow(Slack::RealTime::Socket).to receive(:new).and_return(socket) allow(socket).to receive(:connect!) allow(socket).to receive(:start_sync) end + it 'calls rtm_start and not rtm_connect', vcr: { cassette_name: 'web/rtm_start' } do expect(client.web_client).to receive(:rtm_start).and_call_original client.start! end end end end end + context 'start_method' do describe '#initialize' do it 'can be overriden explicitly' do - client = Slack::RealTime::Client.new(start_method: :overriden) + client = described_class.new(start_method: :overriden) expect(client.send(:start_method)).to eq :overriden end context 'with start_method' do before do - Slack::RealTime::Client.configure do |config| + described_class.configure do |config| config.start_method = :overriden end end + it 'sets start_method' do expect(client.send(:start_method)).to eq :overriden end it 'calls the overriden method' do expect(client.web_client).to receive(:overriden).and_raise('overriden') expect do client.start! end.to raise_error RuntimeError, 'overriden' end end + context 'start!' do let(:socket) { double(Slack::RealTime::Socket, connected?: true) } + before do allow(Slack::RealTime::Socket).to receive(:new).and_return(socket) allow(socket).to receive(:connect!) allow(socket).to receive(:start_sync) end - it 'defaults to :rtm_start when using full store', vcr: { cassette_name: 'web/rtm_start' } do + + it 'defaults to :rtm_start when using full store', + vcr: { cassette_name: 'web/rtm_start' } do expect(client.web_client).to receive(:rtm_start).and_call_original client.start! end end end