ably 0.6.2 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +1 -0
  3. data/.ruby-version.old +1 -0
  4. data/.travis.yml +0 -2
  5. data/Rakefile +22 -4
  6. data/SPEC.md +1676 -0
  7. data/ably.gemspec +1 -1
  8. data/lib/ably.rb +0 -8
  9. data/lib/ably/auth.rb +54 -46
  10. data/lib/ably/exceptions.rb +19 -5
  11. data/lib/ably/logger.rb +1 -1
  12. data/lib/ably/models/error_info.rb +1 -1
  13. data/lib/ably/models/idiomatic_ruby_wrapper.rb +11 -9
  14. data/lib/ably/models/message.rb +15 -12
  15. data/lib/ably/models/message_encoders/base.rb +6 -5
  16. data/lib/ably/models/message_encoders/base64.rb +1 -0
  17. data/lib/ably/models/message_encoders/cipher.rb +6 -3
  18. data/lib/ably/models/message_encoders/json.rb +1 -0
  19. data/lib/ably/models/message_encoders/utf8.rb +2 -9
  20. data/lib/ably/models/nil_logger.rb +20 -0
  21. data/lib/ably/models/paginated_resource.rb +5 -2
  22. data/lib/ably/models/presence_message.rb +21 -12
  23. data/lib/ably/models/protocol_message.rb +22 -6
  24. data/lib/ably/modules/ably.rb +11 -0
  25. data/lib/ably/modules/async_wrapper.rb +2 -0
  26. data/lib/ably/modules/conversions.rb +23 -3
  27. data/lib/ably/modules/encodeable.rb +2 -1
  28. data/lib/ably/modules/enum.rb +2 -0
  29. data/lib/ably/modules/event_emitter.rb +7 -1
  30. data/lib/ably/modules/event_machine_helpers.rb +2 -0
  31. data/lib/ably/modules/http_helpers.rb +2 -0
  32. data/lib/ably/modules/model_common.rb +12 -2
  33. data/lib/ably/modules/state_emitter.rb +76 -0
  34. data/lib/ably/modules/state_machine.rb +53 -0
  35. data/lib/ably/modules/statesman_monkey_patch.rb +33 -0
  36. data/lib/ably/modules/uses_state_machine.rb +74 -0
  37. data/lib/ably/realtime.rb +4 -2
  38. data/lib/ably/realtime/channel.rb +51 -58
  39. data/lib/ably/realtime/channel/channel_manager.rb +91 -0
  40. data/lib/ably/realtime/channel/channel_state_machine.rb +68 -0
  41. data/lib/ably/realtime/client.rb +70 -26
  42. data/lib/ably/realtime/client/incoming_message_dispatcher.rb +31 -13
  43. data/lib/ably/realtime/client/outgoing_message_dispatcher.rb +1 -1
  44. data/lib/ably/realtime/connection.rb +135 -92
  45. data/lib/ably/realtime/connection/connection_manager.rb +216 -33
  46. data/lib/ably/realtime/connection/connection_state_machine.rb +30 -73
  47. data/lib/ably/realtime/models/nil_channel.rb +10 -1
  48. data/lib/ably/realtime/presence.rb +336 -92
  49. data/lib/ably/rest.rb +2 -2
  50. data/lib/ably/rest/channel.rb +13 -4
  51. data/lib/ably/rest/client.rb +138 -38
  52. data/lib/ably/rest/middleware/logger.rb +24 -3
  53. data/lib/ably/rest/presence.rb +12 -7
  54. data/lib/ably/version.rb +1 -1
  55. data/spec/acceptance/realtime/channel_history_spec.rb +101 -85
  56. data/spec/acceptance/realtime/channel_spec.rb +461 -120
  57. data/spec/acceptance/realtime/client_spec.rb +119 -0
  58. data/spec/acceptance/realtime/connection_failures_spec.rb +499 -0
  59. data/spec/acceptance/realtime/connection_spec.rb +571 -97
  60. data/spec/acceptance/realtime/message_spec.rb +347 -333
  61. data/spec/acceptance/realtime/presence_history_spec.rb +35 -40
  62. data/spec/acceptance/realtime/presence_spec.rb +769 -239
  63. data/spec/acceptance/realtime/stats_spec.rb +14 -22
  64. data/spec/acceptance/realtime/time_spec.rb +16 -20
  65. data/spec/acceptance/rest/auth_spec.rb +425 -364
  66. data/spec/acceptance/rest/base_spec.rb +108 -176
  67. data/spec/acceptance/rest/channel_spec.rb +89 -89
  68. data/spec/acceptance/rest/channels_spec.rb +30 -32
  69. data/spec/acceptance/rest/client_spec.rb +273 -0
  70. data/spec/acceptance/rest/encoders_spec.rb +185 -0
  71. data/spec/acceptance/rest/message_spec.rb +186 -163
  72. data/spec/acceptance/rest/presence_spec.rb +150 -111
  73. data/spec/acceptance/rest/stats_spec.rb +45 -40
  74. data/spec/acceptance/rest/time_spec.rb +8 -10
  75. data/spec/rspec_config.rb +10 -1
  76. data/spec/shared/client_initializer_behaviour.rb +212 -0
  77. data/spec/{support/model_helper.rb → shared/model_behaviour.rb} +6 -6
  78. data/spec/{support/protocol_msgbus_helper.rb → shared/protocol_msgbus_behaviour.rb} +1 -1
  79. data/spec/spec_helper.rb +9 -0
  80. data/spec/support/api_helper.rb +11 -0
  81. data/spec/support/event_machine_helper.rb +101 -3
  82. data/spec/support/markdown_spec_formatter.rb +90 -0
  83. data/spec/support/private_api_formatter.rb +36 -0
  84. data/spec/support/protocol_helper.rb +32 -0
  85. data/spec/support/random_helper.rb +15 -0
  86. data/spec/support/test_app.rb +4 -0
  87. data/spec/unit/auth_spec.rb +68 -0
  88. data/spec/unit/logger_spec.rb +77 -66
  89. data/spec/unit/models/error_info_spec.rb +1 -1
  90. data/spec/unit/models/idiomatic_ruby_wrapper_spec.rb +2 -3
  91. data/spec/unit/models/message_encoders/base64_spec.rb +2 -2
  92. data/spec/unit/models/message_encoders/cipher_spec.rb +2 -2
  93. data/spec/unit/models/message_encoders/utf8_spec.rb +2 -46
  94. data/spec/unit/models/message_spec.rb +160 -15
  95. data/spec/unit/models/paginated_resource_spec.rb +29 -27
  96. data/spec/unit/models/presence_message_spec.rb +163 -20
  97. data/spec/unit/models/protocol_message_spec.rb +43 -8
  98. data/spec/unit/modules/async_wrapper_spec.rb +2 -3
  99. data/spec/unit/modules/conversions_spec.rb +1 -1
  100. data/spec/unit/modules/enum_spec.rb +2 -3
  101. data/spec/unit/modules/event_emitter_spec.rb +62 -5
  102. data/spec/unit/modules/state_emitter_spec.rb +283 -0
  103. data/spec/unit/realtime/channel_spec.rb +107 -2
  104. data/spec/unit/realtime/channels_spec.rb +1 -0
  105. data/spec/unit/realtime/client_spec.rb +8 -48
  106. data/spec/unit/realtime/connection_spec.rb +3 -3
  107. data/spec/unit/realtime/incoming_message_dispatcher_spec.rb +2 -2
  108. data/spec/unit/realtime/presence_spec.rb +13 -4
  109. data/spec/unit/realtime/realtime_spec.rb +0 -11
  110. data/spec/unit/realtime/websocket_transport_spec.rb +2 -2
  111. data/spec/unit/rest/channel_spec.rb +109 -0
  112. data/spec/unit/rest/channels_spec.rb +4 -3
  113. data/spec/unit/rest/client_spec.rb +30 -125
  114. data/spec/unit/rest/rest_spec.rb +10 -0
  115. data/spec/unit/util/crypto_spec.rb +10 -5
  116. data/spec/unit/util/pub_sub_spec.rb +5 -5
  117. metadata +44 -12
  118. data/spec/integration/modules/state_emitter_spec.rb +0 -80
  119. data/spec/integration/rest/auth.rb +0 -9
@@ -1,5 +1,6 @@
1
+ # encoding: utf-8
1
2
  require 'spec_helper'
2
- require 'support/protocol_msgbus_helper'
3
+ require 'shared/protocol_msgbus_behaviour'
3
4
 
4
5
  describe Ably::Realtime::Channel do
5
6
  let(:client) { double('client').as_null_object }
@@ -9,6 +10,110 @@ describe Ably::Realtime::Channel do
9
10
  Ably::Realtime::Channel.new(client, channel_name)
10
11
  end
11
12
 
13
+ describe '#initializer' do
14
+ let(:channel_name) { random_str.encode(encoding) }
15
+
16
+ context 'as UTF_8 string' do
17
+ let(:encoding) { Encoding::UTF_8 }
18
+
19
+ it 'is permitted' do
20
+ expect(subject.name).to eql(channel_name)
21
+ end
22
+
23
+ it 'remains as UTF-8' do
24
+ expect(subject.name.encoding).to eql(encoding)
25
+ end
26
+ end
27
+
28
+ context 'as SHIFT_JIS string' do
29
+ let(:encoding) { Encoding::SHIFT_JIS }
30
+
31
+ it 'gets converted to UTF-8' do
32
+ expect(subject.name.encoding).to eql(Encoding::UTF_8)
33
+ end
34
+
35
+ it 'is compatible with original encoding' do
36
+ expect(subject.name.encode(encoding)).to eql(channel_name)
37
+ end
38
+ end
39
+
40
+ context 'as ASCII_8BIT string' do
41
+ let(:encoding) { Encoding::ASCII_8BIT }
42
+
43
+ it 'gets converted to UTF-8' do
44
+ expect(subject.name.encoding).to eql(Encoding::UTF_8)
45
+ end
46
+
47
+ it 'is compatible with original encoding' do
48
+ expect(subject.name.encode(encoding)).to eql(channel_name)
49
+ end
50
+ end
51
+
52
+ context 'as Integer' do
53
+ let(:channel_name) { 1 }
54
+
55
+ it 'raises an argument error' do
56
+ expect { subject }.to raise_error ArgumentError, /must be a String/
57
+ end
58
+ end
59
+
60
+ context 'as Nil' do
61
+ let(:channel_name) { nil }
62
+
63
+ it 'raises an argument error' do
64
+ expect { subject }.to raise_error ArgumentError, /must be a String/
65
+ end
66
+ end
67
+ end
68
+
69
+ describe '#publish name argument' do
70
+ let(:encoded_value) { random_str.encode(encoding) }
71
+
72
+ before do
73
+ allow(subject).to receive(:create_message).and_return('message_stubbed')
74
+ end
75
+
76
+ context 'as UTF_8 string' do
77
+ let(:encoding) { Encoding::UTF_8 }
78
+
79
+ it 'is permitted' do
80
+ expect(subject.publish(encoded_value, 'data')).to eql('message_stubbed')
81
+ end
82
+ end
83
+
84
+ context 'as SHIFT_JIS string' do
85
+ let(:encoding) { Encoding::SHIFT_JIS }
86
+
87
+ it 'is permitted' do
88
+ expect(subject.publish(encoded_value, 'data')).to eql('message_stubbed')
89
+ end
90
+ end
91
+
92
+ context 'as ASCII_8BIT string' do
93
+ let(:encoding) { Encoding::ASCII_8BIT }
94
+
95
+ it 'is permitted' do
96
+ expect(subject.publish(encoded_value, 'data')).to eql('message_stubbed')
97
+ end
98
+ end
99
+
100
+ context 'as Integer' do
101
+ let(:encoded_value) { 1 }
102
+
103
+ it 'raises an argument error' do
104
+ expect { subject.publish(encoded_value, 'data') }.to raise_error ArgumentError, /must be a String/
105
+ end
106
+ end
107
+
108
+ context 'as Nil' do
109
+ let(:encoded_value) { nil }
110
+
111
+ it 'raises an argument error' do
112
+ expect { subject.publish(encoded_value, 'data') }.to raise_error ArgumentError, /must be a String/
113
+ end
114
+ end
115
+ end
116
+
12
117
  describe 'callbacks' do
13
118
  specify 'are supported for valid STATE events' do
14
119
  state = nil
@@ -23,7 +128,7 @@ describe Ably::Realtime::Channel do
23
128
  end
24
129
  end
25
130
 
26
- context 'msgbus' do
131
+ context 'msgbus', :api_private do
27
132
  let(:message) do
28
133
  Ably::Models::Message.new({
29
134
  'name' => 'test',
@@ -1,3 +1,4 @@
1
+ # encoding: utf-8
1
2
  require 'spec_helper'
2
3
 
3
4
  describe Ably::Realtime::Channels do
@@ -1,70 +1,30 @@
1
+ # encoding: utf-8
1
2
  require 'spec_helper'
2
- require 'support/protocol_msgbus_helper'
3
+ require 'shared/client_initializer_behaviour'
3
4
 
4
5
  describe Ably::Realtime::Client do
5
- let(:client_options) { 'appid.keyuid:keysecret' }
6
6
  subject do
7
7
  Ably::Realtime::Client.new(client_options)
8
8
  end
9
9
 
10
- context 'delegation to the Rest Client' do
11
- let(:options) { { arbitrary: 'value' } }
10
+ it_behaves_like 'a client initializer'
11
+
12
+ context 'delegation to the REST Client' do
13
+ let(:client_options) { { api_key: 'appid.keyuid:keysecret' } }
12
14
 
13
15
  it 'passes on the options to the initializer' do
14
- rest_client = instance_double('Ably::Rest::Client', auth: instance_double('Ably::Auth'), options: {})
16
+ rest_client = instance_double('Ably::Rest::Client', auth: instance_double('Ably::Auth'), options: client_options)
15
17
  expect(Ably::Rest::Client).to receive(:new).with(client_options).and_return(rest_client)
16
18
  subject
17
19
  end
18
20
 
19
21
  context 'for attribute' do
20
- [:environment, :use_tls?, :log_level].each do |attribute|
22
+ [:environment, :use_tls?, :log_level, :custom_host].each do |attribute|
21
23
  specify "##{attribute}" do
22
24
  expect(subject.rest_client).to receive(attribute)
23
25
  subject.public_send attribute
24
26
  end
25
27
  end
26
28
  end
27
-
28
- context 'logger' do
29
- context 'defaults' do
30
- let(:logger) { subject.logger }
31
-
32
- subject { Ably::Realtime::Client.new(client_options) }
33
-
34
- it 'uses default Ruby Logger by default' do
35
- expect(subject.logger.logger).to be_a(::Logger)
36
- end
37
-
38
- it 'defaults to Logger::ERROR log level' do
39
- expect(subject.logger.log_level).to eql(::Logger::ERROR)
40
- end
41
-
42
- it 'returns the connection ID' do
43
- allow(subject).to receive_message_chain(:connection, :id).and_return('AAA')
44
- expect(logger.logger.formatter.call(0, Time.now, '', 'unique_message')).to match(/AAA/)
45
- end
46
- end
47
-
48
- context 'with custom logger and log_level' do
49
- let(:custom_logger) do
50
- Class.new do
51
- extend Forwardable
52
- def initialize
53
- @logger = Logger.new(STDOUT)
54
- end
55
- def_delegators :@logger, :fatal, :error, :warn, :info, :debug, :level, :level=
56
- end
57
- end
58
- subject { Ably::Realtime::Client.new(api_key: 'appid.keyuid:keysecret', logger: custom_logger.new, log_level: :debug) }
59
-
60
- it 'uses the custom logger' do
61
- expect(subject.logger.logger.class).to eql(custom_logger)
62
- end
63
-
64
- it 'sets the custom log level' do
65
- expect(subject.logger.log_level).to eql(Logger::DEBUG)
66
- end
67
- end
68
- end
69
29
  end
70
30
  end
@@ -1,6 +1,5 @@
1
1
  require 'spec_helper'
2
- require 'support/protocol_msgbus_helper'
3
- require 'support/event_machine_helper'
2
+ require 'shared/protocol_msgbus_behaviour'
4
3
 
5
4
  describe Ably::Realtime::Connection do
6
5
  let(:client) { instance_double('Ably::Realtime::Client', logger: double('logger').as_null_object) }
@@ -10,8 +9,9 @@ describe Ably::Realtime::Connection do
10
9
  end
11
10
 
12
11
  before do
13
- expect(EventMachine::Timer).to receive(:new) # Connection Manager #initializer
14
12
  expect(EventMachine).to receive(:next_tick) # non_blocking_loop_while for delivery of messages async
13
+ subject.__incoming_protocol_msgbus__.off
14
+ subject.__outgoing_protocol_msgbus__.off
15
15
  end
16
16
 
17
17
  describe 'callbacks' do
@@ -1,11 +1,11 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Ably::Realtime::Client::IncomingMessageDispatcher do
3
+ describe Ably::Realtime::Client::IncomingMessageDispatcher, :api_private do
4
4
  let(:msgbus) do
5
5
  Ably::Util::PubSub.new
6
6
  end
7
7
  let(:connection) do
8
- instance_double('Ably::Realtime::Connection', __incoming_protocol_msgbus__: msgbus, update_connection_id: true, id: nil)
8
+ instance_double('Ably::Realtime::Connection', __incoming_protocol_msgbus__: msgbus, update_connection_id_and_key: true, id: nil)
9
9
  end
10
10
  let(:client) do
11
11
  instance_double('Ably::Realtime::Client', channels: {})
@@ -1,5 +1,6 @@
1
+ # encoding: utf-8
1
2
  require 'spec_helper'
2
- require 'support/protocol_msgbus_helper'
3
+ require 'shared/protocol_msgbus_behaviour'
3
4
 
4
5
  describe Ably::Realtime::Presence do
5
6
  let(:channel) { double('Ably::Realtime::Channel').as_null_object }
@@ -22,11 +23,11 @@ describe Ably::Realtime::Presence do
22
23
  end
23
24
  end
24
25
 
25
- context 'msgbus' do
26
+ context 'msgbus', :api_private do
26
27
  let(:message) do
27
28
  Ably::Models::PresenceMessage.new({
28
29
  'action' => 0,
29
- 'member_id' => SecureRandom.hex,
30
+ 'connection_id' => random_str,
30
31
  }, instance_double('Ably::Models::ProtocolMessage'))
31
32
  end
32
33
  let(:msgbus) { subject.__incoming_msgbus__ }
@@ -48,10 +49,14 @@ describe Ably::Realtime::Presence do
48
49
  let(:message_history) { Hash.new { |hash, key| hash[key] = 0 } }
49
50
  let(:presence_action) { Ably::Models::PresenceMessage::ACTION.Enter }
50
51
  let(:message) do
51
- instance_double('Ably::Models::PresenceMessage', action: presence_action, member_id: SecureRandom.hex, decode: true)
52
+ instance_double('Ably::Models::PresenceMessage', action: presence_action, connection_id: random_str, decode: true, member_key: random_str)
52
53
  end
53
54
 
54
55
  context '#subscribe' do
56
+ before do
57
+ subject.sync_completed
58
+ end
59
+
55
60
  specify 'to all presence state actions' do
56
61
  subject.subscribe { |message| message_history[:received] += 1}
57
62
  subject.__incoming_msgbus__.publish(:presence, message)
@@ -67,6 +72,10 @@ describe Ably::Realtime::Presence do
67
72
  end
68
73
 
69
74
  context '#unsubscribe' do
75
+ before do
76
+ subject.sync_completed
77
+ end
78
+
70
79
  let(:callback) do
71
80
  Proc.new { |message| message_history[:received] += 1 }
72
81
  end
@@ -6,15 +6,4 @@ describe Ably::Realtime do
6
6
  specify 'constructor returns an Ably::Realtime::Client' do
7
7
  expect(Ably::Realtime.new(options)).to be_instance_of(Ably::Realtime::Client)
8
8
  end
9
-
10
- describe Ably::Realtime::Client do
11
- describe 'initializing the client' do
12
- it 'should disallow an invalid key' do
13
- expect { Ably::Realtime::Client.new({}) }.to raise_error(ArgumentError, /api_key is missing/)
14
- expect { Ably::Realtime::Client.new(api_key: 'invalid') }.to raise_error(ArgumentError, /api_key is invalid/)
15
- expect { Ably::Realtime::Client.new(api_key: 'invalid:asdad') }.to raise_error(ArgumentError, /api_key is invalid/)
16
- expect { Ably::Realtime::Client.new(api_key: 'appid.keyuid:keysecret') }.to_not raise_error
17
- end
18
- end
19
- end
20
9
  end
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
- require 'support/protocol_msgbus_helper'
2
+ require 'shared/protocol_msgbus_behaviour'
3
3
 
4
- describe Ably::Realtime::Connection::WebsocketTransport do
4
+ describe Ably::Realtime::Connection::WebsocketTransport, :api_private do
5
5
  let(:client_ignored) { double('Ably::Realtime::Client').as_null_object }
6
6
  let(:connection) { instance_double('Ably::Realtime::Connection', client: client_ignored, id: nil) }
7
7
  let(:url) { 'http://ably.io/' }
@@ -0,0 +1,109 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe Ably::Rest::Channels do
5
+ let(:client) { instance_double('Ably::Rest::Client', encoders: [], post: instance_double('Faraday::Response', status: 201)) }
6
+ let(:channel_name) { 'unique' }
7
+
8
+ subject { Ably::Rest::Channel.new(client, channel_name) }
9
+
10
+ describe '#initializer' do
11
+ let(:channel_name) { random_str.encode(encoding) }
12
+
13
+ context 'as UTF_8 string' do
14
+ let(:encoding) { Encoding::UTF_8 }
15
+
16
+ it 'is permitted' do
17
+ expect(subject.name).to eql(channel_name)
18
+ end
19
+
20
+ it 'remains as UTF-8' do
21
+ expect(subject.name.encoding).to eql(encoding)
22
+ end
23
+ end
24
+
25
+ context 'as SHIFT_JIS string' do
26
+ let(:encoding) { Encoding::SHIFT_JIS }
27
+
28
+ it 'gets converted to UTF-8' do
29
+ expect(subject.name.encoding).to eql(Encoding::UTF_8)
30
+ end
31
+
32
+ it 'is compatible with original encoding' do
33
+ expect(subject.name.encode(encoding)).to eql(channel_name)
34
+ end
35
+ end
36
+
37
+ context 'as ASCII_8BIT string' do
38
+ let(:encoding) { Encoding::ASCII_8BIT }
39
+
40
+ it 'gets converted to UTF-8' do
41
+ expect(subject.name.encoding).to eql(Encoding::UTF_8)
42
+ end
43
+
44
+ it 'is compatible with original encoding' do
45
+ expect(subject.name.encode(encoding)).to eql(channel_name)
46
+ end
47
+ end
48
+
49
+ context 'as Integer' do
50
+ let(:channel_name) { 1 }
51
+
52
+ it 'raises an argument error' do
53
+ expect { subject }.to raise_error ArgumentError, /must be a String/
54
+ end
55
+ end
56
+
57
+ context 'as Nil' do
58
+ let(:channel_name) { nil }
59
+
60
+ it 'raises an argument error' do
61
+ expect { subject }.to raise_error ArgumentError, /must be a String/
62
+ end
63
+ end
64
+ end
65
+
66
+ describe '#publish name argument' do
67
+ let(:encoded_value) { random_str.encode(encoding) }
68
+
69
+ context 'as UTF_8 string' do
70
+ let(:encoding) { Encoding::UTF_8 }
71
+
72
+ it 'is permitted' do
73
+ expect(subject.publish(encoded_value, 'data')).to eql(true)
74
+ end
75
+ end
76
+
77
+ context 'as SHIFT_JIS string' do
78
+ let(:encoding) { Encoding::SHIFT_JIS }
79
+
80
+ it 'is permitted' do
81
+ expect(subject.publish(encoded_value, 'data')).to eql(true)
82
+ end
83
+ end
84
+
85
+ context 'as ASCII_8BIT string' do
86
+ let(:encoding) { Encoding::ASCII_8BIT }
87
+
88
+ it 'is permitted' do
89
+ expect(subject.publish(encoded_value, 'data')).to eql(true)
90
+ end
91
+ end
92
+
93
+ context 'as Integer' do
94
+ let(:encoded_value) { 1 }
95
+
96
+ it 'raises an argument error' do
97
+ expect { subject.publish(encoded_value, 'data') }.to raise_error ArgumentError, /must be a String/
98
+ end
99
+ end
100
+
101
+ context 'as Nil' do
102
+ let(:encoded_value) { nil }
103
+
104
+ it 'raises an argument error' do
105
+ expect { subject.publish(encoded_value, 'data') }.to raise_error ArgumentError, /must be a String/
106
+ end
107
+ end
108
+ end
109
+ end
@@ -1,9 +1,10 @@
1
+ # encoding: utf-8
1
2
  require 'spec_helper'
2
3
 
3
4
  describe Ably::Rest::Channels do
4
- let(:client) { instance_double('Ably::Rest::Client') }
5
- let(:channel_name) { 'unique' }
6
- let(:options) { { 'bizarre' => 'value' } }
5
+ let(:client) { instance_double('Ably::Rest::Client') }
6
+ let(:channel_name) { 'unique'.encode(Encoding::UTF_8) }
7
+ let(:options) { { 'bizarre' => 'value' } }
7
8
 
8
9
  subject { Ably::Rest::Channels.new(client) }
9
10
 
@@ -1,145 +1,50 @@
1
+ # encoding: utf-8
1
2
  require 'spec_helper'
3
+ require 'shared/client_initializer_behaviour'
2
4
 
3
- describe Ably::Rest do
4
- let(:options) { { api_key: 'app.key:secret' } }
5
-
6
- specify 'constructor returns an Ably::Rest::Client' do
7
- expect(Ably::Rest.new(options)).to be_instance_of(Ably::Rest::Client)
5
+ describe Ably::Rest::Client do
6
+ subject do
7
+ Ably::Rest::Client.new(client_options)
8
8
  end
9
9
 
10
- describe Ably::Rest::Client do
11
- describe 'initializing the client' do
12
- it 'should disallow an invalid key' do
13
- expect { Ably::Rest::Client.new({}) }.to raise_error(ArgumentError, /api_key is missing/)
14
- expect { Ably::Rest::Client.new(api_key: 'invalid') }.to raise_error(ArgumentError, /api_key is invalid/)
15
- expect { Ably::Rest::Client.new(api_key: 'invalid:asdad') }.to raise_error(ArgumentError, /api_key is invalid/)
16
- expect { Ably::Rest::Client.new(api_key: 'appid.keyuid:keysecret') }.to_not raise_error
17
- end
18
-
19
- it 'should disallow api_key and key_id' do
20
- expect { Ably::Rest::Client.new(api_key: 'valid', key_id: 'invalid') }.to raise_error(ArgumentError, /api_key and key_id or key_secret are mutually exclusive/)
21
- end
22
-
23
- it 'should disallow api_key and key_secret' do
24
- expect { Ably::Rest::Client.new(api_key: 'valid', key_secret: 'invalid') }.to raise_error(ArgumentError, /api_key and key_id or key_secret are mutually exclusive/)
25
- end
26
-
27
- context 'using key_id and key_secret' do
28
- let(:client) { Ably::Rest::Client.new(key_id: 'id', key_secret: 'secret') }
29
-
30
- it 'should allow key_id and key_secret in place of api_key' do
31
- expect(client.auth.api_key).to eql('id:secret')
32
- end
33
- end
34
-
35
- context 'with a string key instead of options' do
36
- let(:options) { 'app.key:secret' }
37
- subject { Ably::Rest::Client.new(options) }
38
-
39
- it 'should set the api_key' do
40
- expect(subject.auth.api_key).to eql(options)
41
- end
42
-
43
- it 'should set the key_id' do
44
- expect(subject.auth.key_id).to eql('app.key')
45
- end
46
-
47
- it 'should set the key_secret' do
48
- expect(subject.auth.key_secret).to eql('secret')
49
- end
50
- end
51
-
52
- context 'with a client_id' do
53
- it "should require a valid key" do
54
- expect { Ably::Rest::Client.new(client_id: 'valid') }.to raise_error(ArgumentError, /client_id cannot be provided without a complete API key/)
55
- end
56
- end
57
-
58
- it 'should default to the production REST end point' do
59
- expect(Ably::Rest::Client.new(api_key: 'appid.keyuid:keysecret').endpoint.to_s).to eql('https://rest.ably.io')
60
- end
61
-
62
- it 'should allow an environment to be set' do
63
- expect(Ably::Rest::Client.new(api_key: 'appid.keyuid:keysecret', environment: 'sandbox').endpoint.to_s).to eql('https://sandbox-rest.ably.io')
64
- end
65
-
66
- context 'with TLS disabled' do
67
- let(:client) { Ably::Rest::Client.new(api_key: 'appid.keyid:secret', tls: false) }
68
-
69
- it 'uses plain text' do
70
- expect(client.use_tls?).to eql(false)
71
- end
72
-
73
- it 'uses HTTP' do
74
- expect(client.endpoint.to_s).to eql('http://rest.ably.io')
75
- end
76
-
77
- it 'fails when authenticating with basic auth' do
78
- expect { client.channel('a').publish('event', 'message') }.to raise_error(Ably::Exceptions::InsecureRequestError)
79
- end
80
- end
81
-
82
- context 'with no TLS option provided' do
83
- let(:client) { Ably::Rest::Client.new(api_key: 'appid.keyid:secret') }
84
-
85
- it 'defaults to TLS' do
86
- expect(client.use_tls?).to eql(true)
87
- end
88
- end
10
+ it_behaves_like 'a client initializer'
89
11
 
90
- context 'with alternative environment' do
91
- let(:client) { Ably::Rest::Client.new(api_key: 'appid.keyid:secret', environment: 'sandbox') }
12
+ context 'initializer options' do
13
+ context 'TLS' do
14
+ context 'disabled' do
15
+ let(:client_options) { { api_key: 'appid.keyuid:keysecret', tls: false } }
92
16
 
93
- it 'should alter the endpoint' do
94
- expect(client.endpoint.to_s).to eql('https://sandbox-rest.ably.io')
17
+ it 'fails for any operation with basic auth and attempting to send an API key over a non-secure connection' do
18
+ expect { subject.channel('a').publish('event', 'message') }.to raise_error(Ably::Exceptions::InsecureRequestError)
95
19
  end
96
20
  end
21
+ end
97
22
 
98
- context 'delegators' do
99
- subject { Ably::Rest::Client.new(options) }
100
-
101
- it 'should delegate :client_id to .auth' do
102
- expect(subject.auth).to receive(:client_id).and_return('john')
103
- expect(subject.client_id).to eql('john')
104
- end
23
+ context ':use_token_auth' do
24
+ context 'set to false' do
25
+ context 'with an api_key with :tls => false' do
26
+ let(:client_options) { { use_token_auth: false, api_key: 'appid.keyuid:keysecret', tls: false } }
105
27
 
106
- it 'should delegate :auth_options to .auth' do
107
- expect(subject.auth).to receive(:auth_options).and_return({ option: 1 })
108
- expect(subject.auth_options).to eql({ option: 1 })
28
+ it 'fails for any operation with basic auth and attempting to send an API key over a non-secure connection' do
29
+ expect { subject.channel('a').publish('event', 'message') }.to raise_error(Ably::Exceptions::InsecureRequestError)
30
+ end
109
31
  end
110
- end
111
32
 
112
- context 'logger' do
113
- context 'defaults' do
114
- subject { Ably::Rest::Client.new(options) }
33
+ context 'without an api_key' do
34
+ let(:client_options) { { use_token_auth: false } }
115
35
 
116
- it 'uses default Ruby Logger by default' do
117
- expect(subject.logger.logger).to be_a(::Logger)
118
- end
119
-
120
- it 'defaults to Logger::ERROR log level' do
121
- expect(subject.logger.log_level).to eql(::Logger::ERROR)
36
+ it 'fails as an api_key is required if not using token auth' do
37
+ expect { subject.channel('a').publish('event', 'message') }.to raise_error(ArgumentError)
122
38
  end
123
39
  end
40
+ end
124
41
 
125
- context 'with custom logger and log_level' do
126
- let(:custom_logger) do
127
- Class.new do
128
- extend Forwardable
129
- def initialize
130
- @logger = Logger.new(STDOUT)
131
- end
132
- def_delegators :@logger, :fatal, :error, :warn, :info, :debug, :level, :level=
133
- end
134
- end
135
- subject { Ably::Rest::Client.new(options.merge(logger: custom_logger.new, log_level: :debug)) }
136
-
137
- it 'uses the custom logger' do
138
- expect(subject.logger.logger.class).to eql(custom_logger)
139
- end
42
+ context 'set to true' do
43
+ context 'without an api_key or token_id' do
44
+ let(:client_options) { { use_token_auth: true, api_key: true } }
140
45
 
141
- it 'sets the custom log level' do
142
- expect(subject.logger.log_level).to eql(Logger::DEBUG)
46
+ it 'fails as an api_key is required to issue tokens' do
47
+ expect { subject.channel('a').publish('event', 'message') }.to raise_error(ArgumentError)
143
48
  end
144
49
  end
145
50
  end