ably 0.6.2 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
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