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,12 +1,8 @@
1
+ # encoding: utf-8
1
2
  require 'spec_helper'
2
- require 'securerandom'
3
3
 
4
- describe 'REST' do
5
- let(:client) do
6
- Ably::Rest::Client.new(api_key: api_key, environment: environment)
7
- end
8
-
9
- describe 'protocol' do
4
+ describe Ably::Rest do
5
+ describe 'transport protocol' do
10
6
  include Ably::Modules::Conversions
11
7
 
12
8
  let(:client_options) { {} }
@@ -14,218 +10,154 @@ describe 'REST' do
14
10
  Ably::Rest::Client.new(client_options.merge(api_key: 'appid.keyuid:keysecret'))
15
11
  end
16
12
 
17
- context 'transport' do
18
- let(:now) { Time.now - 1000 }
19
- let(:body_value) { [as_since_epoch(now)] }
13
+ let(:now) { Time.now - 1000 }
14
+ let(:body_value) { [as_since_epoch(now)] }
20
15
 
21
- before do
22
- stub_request(:get, "#{client.endpoint}/time").
23
- with(:headers => { 'Accept' => mime }).
24
- to_return(:status => 200, :body => request_body, :headers => { 'Content-Type' => mime })
25
- end
16
+ before do
17
+ stub_request(:get, "#{client.endpoint}/time").
18
+ with(:headers => { 'Accept' => mime }).
19
+ to_return(:status => 200, :body => request_body, :headers => { 'Content-Type' => mime })
20
+ end
26
21
 
27
- context 'when protocol is not defined it defaults to :msgpack' do
28
- let(:client_options) { { } }
29
- let(:mime) { 'application/x-msgpack' }
30
- let(:request_body) { body_value.to_msgpack }
22
+ context 'when protocol is not defined it defaults to :msgpack' do
23
+ let(:client_options) { { } }
24
+ let(:mime) { 'application/x-msgpack' }
25
+ let(:request_body) { body_value.to_msgpack }
31
26
 
32
- it 'uses MsgPack', webmock: true do
33
- expect(client.protocol).to eql(:msgpack)
34
- expect(client.time).to be_within(1).of(now)
35
- end
27
+ it 'uses MsgPack', :webmock do
28
+ expect(client.protocol).to eql(:msgpack)
29
+ expect(client.time).to be_within(1).of(now)
36
30
  end
31
+ end
37
32
 
38
- options = [
39
- { protocol: :json },
40
- { use_binary_protocol: false }
41
- ].each do |client_option|
33
+ options = [
34
+ { protocol: :json },
35
+ { use_binary_protocol: false }
36
+ ].each do |client_option|
42
37
 
43
- context "when option #{client_option} is used" do
44
- let(:client_options) { client_option }
45
- let(:mime) { 'application/json' }
46
- let(:request_body) { body_value.to_json }
38
+ context "when option #{client_option} is used" do
39
+ let(:client_options) { client_option }
40
+ let(:mime) { 'application/json' }
41
+ let(:request_body) { body_value.to_json }
47
42
 
48
- it 'uses JSON', webmock: true do
49
- expect(client.protocol).to eql(:json)
50
- expect(client.time).to be_within(1).of(now)
51
- end
43
+ it 'uses JSON', :webmock do
44
+ expect(client.protocol).to eql(:json)
45
+ expect(client.time).to be_within(1).of(now)
52
46
  end
53
47
  end
48
+ end
54
49
 
55
- options = [
56
- { protocol: :json },
57
- { use_binary_protocol: false }
58
- ].each do |client_option|
50
+ options = [
51
+ { protocol: :msgpack },
52
+ { use_binary_protocol: true }
53
+ ].each do |client_option|
59
54
 
60
- context "when option #{client_option} is used" do
61
- let(:client_options) { { protocol: :msgpack } }
62
- let(:mime) { 'application/x-msgpack' }
63
- let(:request_body) { body_value.to_msgpack }
55
+ context "when option #{client_option} is used" do
56
+ let(:client_options) { client_option }
57
+ let(:mime) { 'application/x-msgpack' }
58
+ let(:request_body) { body_value.to_msgpack }
64
59
 
65
- it 'uses MsgPack', webmock: true do
66
- expect(client.protocol).to eql(:msgpack)
67
- expect(client.time).to be_within(1).of(now)
68
- end
60
+ it 'uses MsgPack', :webmock do
61
+ expect(client.protocol).to eql(:msgpack)
62
+ expect(client.time).to be_within(1).of(now)
69
63
  end
70
64
  end
71
65
  end
72
66
  end
73
67
 
74
- describe 'invalid requests in middleware' do
75
- it 'should raise an InvalidRequest exception with a valid message' do
76
- invalid_client = Ably::Rest::Client.new(api_key: 'appid.keyuid:keysecret', environment: environment)
77
- expect { invalid_client.channel('test').publish('foo', 'choo') }.to raise_error do |error|
78
- expect(error).to be_a(Ably::Exceptions::InvalidToken)
79
- expect(error.message).to match(/invalid credentials/)
80
- expect(error.code).to eql(40100)
81
- expect(error.status).to eql(401)
82
- end
83
- end
84
-
85
- describe 'server error with JSON response', webmock: true do
86
- let(:error_response) { '{ "error": { "statusCode": 500, "code": 50000, "message": "Internal error" } }' }
87
-
88
- before do
89
- stub_request(:get, "#{client.endpoint}/time").
90
- to_return(:status => 500, :body => error_response, :headers => { 'Content-Type' => 'application/json' })
91
- end
92
-
93
- it 'should raise a ServerError exception' do
94
- expect { client.time }.to raise_error(Ably::Exceptions::ServerError, /Internal error/)
95
- end
96
- end
97
-
98
- describe 'server error', webmock: true do
99
- before do
100
- stub_request(:get, "#{client.endpoint}/time").
101
- to_return(:status => 500, :headers => { 'Content-Type' => 'application/json' })
102
- end
103
-
104
- it 'should raise a ServerError exception' do
105
- expect { client.time }.to raise_error(Ably::Exceptions::ServerError, /Unknown/)
106
- end
68
+ vary_by_protocol do
69
+ let(:client) do
70
+ Ably::Rest::Client.new(api_key: api_key, environment: environment, protocol: protocol)
107
71
  end
108
- end
109
-
110
- describe 'authentication failure', webmock: true do
111
- let(:token_1) { { id: SecureRandom.hex } }
112
- let(:token_2) { { id: SecureRandom.hex } }
113
- let(:channel) { 'channelname' }
114
72
 
115
- before do
116
- @token_requests = 0
117
- @publish_attempts = 0
118
-
119
- stub_request(:post, "#{client.endpoint}/keys/#{key_id}/requestToken").to_return do
120
- @token_requests += 1
121
- {
122
- :body => { access_token: send("token_#{@token_requests}").merge(expires: Time.now.to_i + 3600) }.to_json,
123
- :headers => { 'Content-Type' => 'application/json' }
124
- }
125
- end
126
-
127
- stub_request(:post, "#{client.endpoint}/channels/#{channel}/publish").to_return do
128
- @publish_attempts += 1
129
- if [1, 3].include?(@publish_attempts)
130
- { status: 201, :body => '[]', :headers => { 'Content-Type' => 'application/json' } }
131
- else
132
- raise Ably::Exceptions::InvalidRequest.new('Authentication failure', 401, 40140)
73
+ describe 'failed requests' do
74
+ context 'due to invalid Auth' do
75
+ it 'should raise an InvalidRequest exception with a valid error message and code' do
76
+ invalid_client = Ably::Rest::Client.new(api_key: 'appid.keyuid:keysecret', environment: environment)
77
+ expect { invalid_client.channel('test').publish('foo', 'choo') }.to raise_error do |error|
78
+ expect(error).to be_a(Ably::Exceptions::InvalidRequest)
79
+ expect(error.message).to match(/invalid credentials/)
80
+ expect(error.code).to eql(40100)
81
+ expect(error.status).to eql(401)
82
+ end
133
83
  end
134
84
  end
135
- end
136
-
137
- context 'when auth#token_renewable?' do
138
- before do
139
- client.auth.authorise
140
- end
141
-
142
- it 'should automatically reissue a token' do
143
- client.channel(channel).publish('evt', 'msg')
144
- expect(@publish_attempts).to eql(1)
145
-
146
- client.channel(channel).publish('evt', 'msg')
147
- expect(@publish_attempts).to eql(3)
148
- expect(@token_requests).to eql(2)
149
- end
150
- end
151
85
 
152
- context 'when NOT auth#token_renewable?' do
153
- let(:client) { Ably::Rest::Client.new(token_id: 'token ID cannot be used to create a new token', environment: environment) }
154
- it 'should raise the exception' do
155
- client.channel(channel).publish('evt', 'msg')
156
- expect(@publish_attempts).to eql(1)
157
- expect { client.channel(channel).publish('evt', 'msg') }.to raise_error Ably::Exceptions::InvalidToken
158
- expect(@token_requests).to eql(0)
159
- end
160
- end
161
- end
86
+ describe 'server error with JSON error response body', :webmock do
87
+ let(:error_response) { '{ "error": { "statusCode": 500, "code": 50000, "message": "Internal error" } }' }
162
88
 
163
- describe Ably::Rest::Client do
164
- context '#initialize' do
165
- context 'with an auth block' do
166
- let(:client) { Ably::Rest::Client.new(environment: environment) { token_request } }
167
- let(:token_request) { client.auth.create_token_request(key_id: key_id, key_secret: key_secret, client_id: client_id) }
168
- let(:client_id) { 'unique_client_id' }
89
+ before do
90
+ stub_request(:get, "#{client.endpoint}/time").
91
+ to_return(:status => 500, :body => error_response, :headers => { 'Content-Type' => 'application/json' })
92
+ end
169
93
 
170
- it 'calls the block to get a new token' do
171
- expect { client.channel('channel_name').publish('event', 'message') }.to change { client.auth.current_token }
172
- expect(client.auth.current_token.client_id).to eql(client_id)
94
+ it 'should raise a ServerError exception' do
95
+ expect { client.time }.to raise_error(Ably::Exceptions::ServerError, /Internal error/)
173
96
  end
174
97
  end
175
98
 
176
- context 'with an auth URL' do
177
- let(:client) { Ably::Rest::Client.new(environment: environment, auth_url: token_request_url, auth_method: :get) }
178
- let(:token_request_url) { 'http://get.token.request.com/' }
179
- let(:token_request) { client.auth.create_token_request(key_id: key_id, key_secret: key_secret, client_id: client_id) }
180
- let(:client_id) { 'unique_client_id' }
181
-
99
+ describe '500 server error without a valid JSON response body', :webmock do
182
100
  before do
183
- allow(client.auth).to receive(:token_request_from_auth_url).with(token_request_url, :auth_method => :get).and_return(token_request)
101
+ stub_request(:get, "#{client.endpoint}/time").
102
+ to_return(:status => 500, :headers => { 'Content-Type' => 'application/json' })
184
103
  end
185
104
 
186
- it 'sends an HTTP request to get a new token' do
187
- expect { client.channel('channel_name').publish('event', 'message') }.to change { client.auth.current_token }
188
- expect(client.auth.current_token.client_id).to eql(client_id)
105
+ it 'should raise a ServerError exception' do
106
+ expect { client.time }.to raise_error(Ably::Exceptions::ServerError, /Unknown/)
189
107
  end
190
108
  end
191
109
  end
192
110
 
193
- context 'token expiry' do
194
- let(:client) do
195
- Ably::Rest::Client.new(environment: environment) do
196
- @request_index ||= 0
197
- @request_index += 1
198
- send("token_request_#{@request_index}")
111
+ describe 'token authentication failures', :webmock do
112
+ let(:token_1) { { id: random_str } }
113
+ let(:token_2) { { id: random_str } }
114
+ let(:channel) { 'channelname' }
115
+
116
+ before do
117
+ @token_requests = 0
118
+ @publish_attempts = 0
119
+
120
+ stub_request(:post, "#{client.endpoint}/keys/#{key_id}/requestToken").to_return do
121
+ @token_requests += 1
122
+ {
123
+ :body => { access_token: send("token_#{@token_requests}").merge(expires: Time.now.to_i + 3600) }.to_json,
124
+ :headers => { 'Content-Type' => 'application/json' }
125
+ }
126
+ end
127
+
128
+ stub_request(:post, "#{client.endpoint}/channels/#{channel}/publish").to_return do
129
+ @publish_attempts += 1
130
+ if [1, 3].include?(@publish_attempts)
131
+ { status: 201, :body => '[]', :headers => { 'Content-Type' => 'application/json' } }
132
+ else
133
+ raise Ably::Exceptions::InvalidRequest.new('Authentication failure', 401, 40140)
134
+ end
199
135
  end
200
136
  end
201
- let(:token_request_1) { client.auth.create_token_request(token_request_options.merge(client_id: SecureRandom.hex)) }
202
- let(:token_request_2) { client.auth.create_token_request(token_request_options.merge(client_id: SecureRandom.hex)) }
203
-
204
- context 'when expired' do
205
- let(:token_request_options) { { key_id: key_id, key_secret: key_secret, ttl: Ably::Models::Token::TOKEN_EXPIRY_BUFFER } }
206
137
 
207
- it 'creates a new token automatically when the old token expires' do
208
- expect { client.channel('channel_name').publish('event', 'message') }.to change { client.auth.current_token }
209
- expect(client.auth.current_token.client_id).to eql(token_request_1[:client_id])
138
+ context 'when auth#token_renewable?' do
139
+ before do
140
+ client.auth.authorise
141
+ end
210
142
 
211
- sleep 1
143
+ it 'should automatically reissue a token' do
144
+ client.channel(channel).publish('evt', 'msg')
145
+ expect(@publish_attempts).to eql(1)
212
146
 
213
- expect { client.channel('channel_name').publish('event', 'message') }.to change { client.auth.current_token }
214
- expect(client.auth.current_token.client_id).to eql(token_request_2[:client_id])
147
+ client.channel(channel).publish('evt', 'msg')
148
+ expect(@publish_attempts).to eql(3)
149
+ expect(@token_requests).to eql(2)
215
150
  end
216
151
  end
217
152
 
218
- context 'token authentication with long expiry token' do
219
- let(:token_request_options) { { key_id: key_id, key_secret: key_secret, ttl: 3600 } }
220
-
221
- it 'creates a new token automatically when the old token expires' do
222
- expect { client.channel('channel_name').publish('event', 'message') }.to change { client.auth.current_token }
223
- expect(client.auth.current_token.client_id).to eql(token_request_1[:client_id])
224
-
225
- sleep 1
153
+ context 'when NOT auth#token_renewable?' do
154
+ let(:client) { Ably::Rest::Client.new(token_id: 'token ID cannot be used to create a new token', environment: environment, protocol: protocol) }
226
155
 
227
- expect { client.channel('channel_name').publish('event', 'message') }.to_not change { client.auth.current_token }
228
- expect(client.auth.current_token.client_id).to eql(token_request_1[:client_id])
156
+ it 'should raise an InvalidToken exception' do
157
+ client.channel(channel).publish('evt', 'msg')
158
+ expect(@publish_attempts).to eql(1)
159
+ expect { client.channel(channel).publish('evt', 'msg') }.to raise_error Ably::Exceptions::InvalidToken
160
+ expect(@token_requests).to eql(0)
229
161
  end
230
162
  end
231
163
  end
@@ -1,130 +1,130 @@
1
+ # encoding: utf-8
1
2
  require 'spec_helper'
2
- require 'securerandom'
3
3
 
4
4
  describe Ably::Rest::Channel do
5
5
  include Ably::Modules::Conversions
6
6
 
7
- [:msgpack, :json].each do |protocol|
8
- context "over #{protocol}" do
9
- let(:client) do
10
- Ably::Rest::Client.new(api_key: api_key, environment: environment, protocol: protocol)
11
- end
7
+ vary_by_protocol do
8
+ let(:client) do
9
+ Ably::Rest::Client.new(api_key: api_key, environment: environment, protocol: protocol)
10
+ end
12
11
 
13
- describe 'publishing messages' do
14
- let(:channel) { client.channel('test') }
15
- let(:event) { 'foo' }
16
- let(:message) { 'woop!' }
12
+ describe '#publish' do
13
+ let(:channel) { client.channel('test') }
14
+ let(:event) { 'foo' }
15
+ let(:message) { 'woop!' }
17
16
 
18
- it 'should publish the message ok' do
19
- expect(channel.publish(event, message)).to eql(true)
20
- end
17
+ it 'should publish the message adn return true indicating success' do
18
+ expect(channel.publish(event, message)).to eql(true)
21
19
  end
20
+ end
22
21
 
23
- describe 'fetching channel history' do
24
- let(:channel) { client.channel("persisted:#{SecureRandom.hex(4)}") }
25
- let(:expected_history) do
26
- [
27
- { :name => 'test1', :data => 'foo' },
28
- { :name => 'test2', :data => 'bar' },
29
- { :name => 'test3', :data => 'baz' }
30
- ]
31
- end
32
- let!(:before_published) { client.time }
22
+ describe '#history' do
23
+ let(:channel) { client.channel("persisted:#{random_str(4)}") }
24
+ let(:expected_history) do
25
+ [
26
+ { :name => 'test1', :data => 'foo' },
27
+ { :name => 'test2', :data => 'bar' },
28
+ { :name => 'test3', :data => 'baz' }
29
+ ]
30
+ end
31
+ let!(:before_published) { client.time }
33
32
 
34
- before(:each) do
35
- expected_history.each do |message|
36
- channel.publish(message[:name], message[:data]) || raise('Unable to publish message')
37
- end
33
+ before(:each) do
34
+ expected_history.each do |message|
35
+ channel.publish(message[:name], message[:data]) || raise('Unable to publish message')
38
36
  end
37
+ end
39
38
 
40
- it 'should return all the history for the channel' do
41
- actual_history = channel.history
39
+ it 'should return the current message history for the channel' do
40
+ actual_history = channel.history
42
41
 
43
- expect(actual_history.size).to eql(3)
42
+ expect(actual_history.size).to eql(3)
44
43
 
45
- expected_history.each do |message|
46
- message_name, message_data = message[:name], message[:data]
47
- matching_message = actual_history.find { |message| message.name == message_name && message.data == message_data }
48
- expect(matching_message).to be_a(Ably::Models::Message)
49
- end
44
+ expected_history.each do |message|
45
+ message_name, message_data = message[:name], message[:data]
46
+ matching_message = actual_history.find { |message| message.name == message_name && message.data == message_data }
47
+ expect(matching_message).to be_a(Ably::Models::Message)
50
48
  end
49
+ end
51
50
 
52
- context 'timestamps' do
53
- it 'should be greater than the time before the messages were published' do
54
- channel.history.each do |message|
55
- expect(before_published.to_f).to be < message.timestamp.to_f
56
- end
51
+ context 'message timestamps' do
52
+ it 'should all be after the messages were published' do
53
+ channel.history.each do |message|
54
+ expect(before_published.to_f).to be < message.timestamp.to_f
57
55
  end
58
56
  end
57
+ end
59
58
 
60
- it 'should return messages with unique IDs' do
59
+ context 'message IDs' do
60
+ it 'should be unique' do
61
61
  message_ids = channel.history.map(&:id).compact
62
62
  expect(message_ids.count).to eql(3)
63
63
  expect(message_ids.uniq.count).to eql(3)
64
64
  end
65
+ end
65
66
 
66
- it 'should return paged history' do
67
- page_1 = channel.history(limit: 1)
68
- page_2 = page_1.next_page
69
- page_3 = page_2.next_page
67
+ it 'should return paged history using the PaginatedResource model' do
68
+ page_1 = channel.history(limit: 1)
69
+ page_2 = page_1.next_page
70
+ page_3 = page_2.next_page
70
71
 
71
- all_items = [page_1[0].id, page_2[0].id, page_3[0].id]
72
- expect(all_items.uniq).to eql(all_items)
72
+ all_items = [page_1[0].id, page_2[0].id, page_3[0].id]
73
+ expect(all_items.uniq).to eql(all_items)
73
74
 
74
- expect(page_1.size).to eql(1)
75
- expect(page_1).to_not be_last_page
76
- expect(page_1).to be_first_page
75
+ expect(page_1.size).to eql(1)
76
+ expect(page_1).to_not be_last_page
77
+ expect(page_1).to be_first_page
77
78
 
78
- # Page 2
79
- expect(page_2.size).to eql(1)
80
- expect(page_2).to_not be_last_page
81
- expect(page_2).to_not be_first_page
79
+ # Page 2
80
+ expect(page_2.size).to eql(1)
81
+ expect(page_2).to_not be_last_page
82
+ expect(page_2).to_not be_first_page
82
83
 
83
- # Page 3
84
- expect(page_3.size).to eql(1)
85
- expect(page_3).to be_last_page
86
- expect(page_3).to_not be_first_page
87
- end
84
+ # Page 3
85
+ expect(page_3.size).to eql(1)
86
+ expect(page_3).to be_last_page
87
+ expect(page_3).to_not be_first_page
88
88
  end
89
+ end
89
90
 
90
- describe 'history options' do
91
- let(:channel_name) { "persisted:#{SecureRandom.hex(4)}" }
92
- let(:channel) { client.channel(channel_name) }
93
- let(:endpoint) do
94
- client.endpoint.tap do |client_end_point|
95
- client_end_point.user = key_id
96
- client_end_point.password = key_secret
97
- end
91
+ describe '#history option' do
92
+ let(:channel_name) { "persisted:#{random_str(4)}" }
93
+ let(:channel) { client.channel(channel_name) }
94
+ let(:endpoint) do
95
+ client.endpoint.tap do |client_end_point|
96
+ client_end_point.user = key_id
97
+ client_end_point.password = key_secret
98
98
  end
99
+ end
99
100
 
100
- [:start, :end].each do |option|
101
- describe ":#{option}", webmock: true do
102
- let!(:history_stub) {
103
- stub_request(:get, "#{endpoint}/channels/#{CGI.escape(channel_name)}/messages?live=true&#{option}=#{milliseconds}").
104
- to_return(:body => '{}', :headers => { 'Content-Type' => 'application/json' })
105
- }
101
+ [:start, :end].each do |option|
102
+ describe ":#{option}", :webmock do
103
+ let!(:history_stub) {
104
+ stub_request(:get, "#{endpoint}/channels/#{CGI.escape(channel_name)}/messages?#{option}=#{milliseconds}").
105
+ to_return(:body => '{}', :headers => { 'Content-Type' => 'application/json' })
106
+ }
106
107
 
107
- before do
108
- channel.history(options)
109
- end
108
+ before do
109
+ channel.history(options)
110
+ end
110
111
 
111
- context 'with milliseconds since epoch' do
112
- let(:milliseconds) { as_since_epoch(Time.now) }
113
- let(:options) { { option => milliseconds } }
112
+ context 'with milliseconds since epoch value' do
113
+ let(:milliseconds) { as_since_epoch(Time.now) }
114
+ let(:options) { { option => milliseconds } }
114
115
 
115
- specify 'are left unchanged' do
116
- expect(history_stub).to have_been_requested
117
- end
116
+ it 'uses this value in the history request' do
117
+ expect(history_stub).to have_been_requested
118
118
  end
119
+ end
119
120
 
120
- context 'with Time' do
121
- let(:time) { Time.now }
122
- let(:milliseconds) { as_since_epoch(time) }
123
- let(:options) { { option => time } }
121
+ context 'with a Time object value' do
122
+ let(:time) { Time.now }
123
+ let(:milliseconds) { as_since_epoch(time) }
124
+ let(:options) { { option => time } }
124
125
 
125
- specify 'are left unchanged' do
126
- expect(history_stub).to have_been_requested
127
- end
126
+ it 'converts the value to milliseconds since epoch in the hisotry request' do
127
+ expect(history_stub).to have_been_requested
128
128
  end
129
129
  end
130
130
  end