/spec/unit/action_cable_client_spec.rb

https://gitlab.com/NullVoxPopuli/action_cable_client · Ruby · 260 lines · 200 code · 56 blank · 4 comment · 0 complexity · a591dc1bf49ada751f49c97b631a8e7b MD5 · raw file

  1. # frozen_string_literal: true
  2. require 'spec_helper'
  3. require 'ostruct'
  4. describe ActionCableClient do
  5. context 'with empty WebSocketClient' do
  6. let!(:websocket_client) do
  7. websocket_client_class = class_double(WebSocket::EventMachine::Client).as_stubbed_const
  8. websocket_client = instance_double(WebSocket::EventMachine::Client)
  9. allow(websocket_client_class).to receive(:connect).and_return websocket_client
  10. allow(websocket_client).to receive(:onclose) do |&block|
  11. @websocket_client_onclose_block = block
  12. end
  13. websocket_client
  14. end
  15. let(:host) { 'hostname' }
  16. let(:port) { 1234 }
  17. before(:each) do
  18. @client = ActionCableClient.new("ws://#{host}:#{port}")
  19. allow(@client).to receive(:send_msg) {}
  20. end
  21. context '#handle_received_message' do
  22. context 'is a ping' do
  23. let(:hash) { { 'type' => 'ping', 'message' => 1_461_845_503 } }
  24. let(:message) { hash.to_json }
  25. it 'nothing is yielded' do
  26. expect do |b|
  27. @client.send(:handle_received_message, message, &b)
  28. end.to_not yield_with_args
  29. end
  30. it 'calls _pinged_callback' do
  31. result = nil
  32. @client.pinged do |data|
  33. result = data
  34. end
  35. @client.send(:handle_received_message, message)
  36. expect(result).to eq(hash)
  37. end
  38. end
  39. context 'is not a ping' do
  40. let(:hash) { { 'identifier' => 'notaping', 'type' => 'message' } }
  41. let(:message) { hash.to_json }
  42. it 'yields whatever' do
  43. expect do |b|
  44. @client._subscribed = true
  45. @client.send(:handle_received_message, message, &b)
  46. end.to yield_with_args(hash)
  47. end
  48. it 'does not call _pinged_callback' do
  49. expect(@client).to_not receive(:_pinged_callback)
  50. @client.send(:handle_received_message, message)
  51. end
  52. end
  53. context 'is a welcome' do
  54. let(:hash) { { 'type' => 'welcome' } }
  55. let(:message) { hash.to_json }
  56. it 'calls _connected_callback' do
  57. result = nil
  58. @client.connected do |data|
  59. result = data
  60. end
  61. @client.send(:handle_received_message, message)
  62. expect(result).to eq(hash)
  63. end
  64. it 'subscribes' do
  65. expect(@client).to receive(:subscribe)
  66. @client.send(:handle_received_message, message)
  67. end
  68. end
  69. context 'is a rejection' do
  70. let(:hash) { { 'type' => 'reject_subscription' } }
  71. let(:message) { hash.to_json }
  72. it 'calls _rejected_callback' do
  73. result = nil
  74. @client.rejected do |data|
  75. result = data
  76. end
  77. @client.send(:handle_received_message, message)
  78. expect(result).to eq(hash)
  79. end
  80. end
  81. context 'empty messages are ignored' do
  82. let(:message) { '' }
  83. it 'dont yield' do
  84. expect do |b|
  85. @client._subscribed = true
  86. @client.send(:handle_received_message, message, &b)
  87. end.not_to yield_with_args
  88. end
  89. end
  90. end
  91. context '#perform' do
  92. it 'does not add to the queue' do
  93. @client.perform('action', {})
  94. expect(@client.message_queue.count).to eq 0
  95. end
  96. it 'dispatches the message' do
  97. expect(@client).to receive(:dispatch_message) {}
  98. @client.perform('action', {})
  99. end
  100. end
  101. context '#dispatch_message' do
  102. it 'does not send if not subscribed' do
  103. @client._subscribed = false
  104. expect(@client).to_not receive(:send_msg)
  105. @client.send(:dispatch_message, 'action', {})
  106. end
  107. it 'calls sends when subscribed' do
  108. @client._subscribed = true
  109. expect(@client).to receive(:send_msg) {}
  110. @client.send(:dispatch_message, 'action', {})
  111. end
  112. end
  113. context '#subscribe' do
  114. it 'sends a message' do
  115. expect(@client).to receive(:send_msg) {}
  116. @client.send(:subscribe)
  117. end
  118. end
  119. context '#subscribed' do
  120. it 'sets the callback' do
  121. expect(@client._subscribed_callback).to eq nil
  122. @client.subscribed {}
  123. expect(@client._subscribed_callback).to_not eq nil
  124. end
  125. it 'once the callback is set, receiving a subscription confirmation invokes the callback' do
  126. callback_called = false
  127. @client.subscribed do
  128. callback_called = true
  129. end
  130. expect(@client).to receive(:_subscribed_callback).and_call_original
  131. message = { 'identifier' => 'ping', 'type' => 'confirm_subscription' }
  132. @client.send(:check_for_subscribe_confirmation, message)
  133. expect(callback_called).to eq true
  134. end
  135. end
  136. context '#connected' do
  137. it 'sets the callback' do
  138. expect(@client._connected_callback).to eq(nil)
  139. @client.connected {}
  140. expect(@client._connected_callback).to_not eq(nil)
  141. end
  142. end
  143. context '#disconnected' do
  144. it 'sets subscribed to false' do
  145. @client._subscribed = true
  146. @websocket_client_onclose_block.call
  147. expect(@client._subscribed).to be false
  148. end
  149. it 'sets the callback' do
  150. expect(@client._disconnected_callback).to eq(nil)
  151. @client.disconnected {}
  152. expect(@client._disconnected_callback).to_not eq(nil)
  153. end
  154. end
  155. context '#pinged' do
  156. it 'sets the callback' do
  157. expect(@client._pinged_callback).to eq(nil)
  158. @client.pinged {}
  159. expect(@client._pinged_callback).to_not eq(nil)
  160. end
  161. end
  162. context '#check_for_subscribe_confirmation' do
  163. it 'is a subscribtion confirmation' do
  164. msg = { 'identifier' => '{"channel":"MeshRelayChannel"}', 'type' => 'confirm_subscription' }
  165. @client.send(:check_for_subscribe_confirmation, msg)
  166. expect(@client.subscribed?).to eq true
  167. end
  168. end
  169. context '#is_ping?' do
  170. it 'is a ping' do
  171. msg = { 'type' => 'ping', 'message' => 1_461_845_611 }
  172. result = @client.send(:is_ping?, msg)
  173. expect(result).to eq true
  174. end
  175. it 'is not a ping when it is a confirmation' do
  176. msg = { 'identifier' => '{"channel":"MeshRelayChannel"}', 'type' => 'confirm_subscription' }
  177. result = @client.send(:is_ping?, msg)
  178. expect(result).to eq false
  179. end
  180. it 'is not a ping' do
  181. msg = { 'identifier' => 'notping', 'message' => 1_460_201_942 }
  182. result = @client.send(:is_ping?, msg)
  183. expect(result).to eq false
  184. end
  185. end
  186. context '#reconnect!' do
  187. before do
  188. allow(EventMachine).to receive(:reconnect)
  189. allow(websocket_client).to receive(:post_init)
  190. end
  191. it 'asks EventMachine to reconnect on same host and port' do
  192. expect(EventMachine).to receive(:reconnect).with(host, port, websocket_client)
  193. @client.reconnect!
  194. end
  195. it 'fires EventMachine::WebSocket::Client #post_init' do
  196. # NOTE: in some cases, its required. Have a look to
  197. # https://github.com/imanel/websocket-eventmachine-client/issues/14
  198. # https://github.com/eventmachine/eventmachine/issues/218
  199. expect(websocket_client).to receive(:post_init)
  200. @client.reconnect!
  201. end
  202. end
  203. end
  204. end