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.
- checksums.yaml +4 -4
- data/.rspec +1 -0
- data/.ruby-version.old +1 -0
- data/.travis.yml +0 -2
- data/Rakefile +22 -4
- data/SPEC.md +1676 -0
- data/ably.gemspec +1 -1
- data/lib/ably.rb +0 -8
- data/lib/ably/auth.rb +54 -46
- data/lib/ably/exceptions.rb +19 -5
- data/lib/ably/logger.rb +1 -1
- data/lib/ably/models/error_info.rb +1 -1
- data/lib/ably/models/idiomatic_ruby_wrapper.rb +11 -9
- data/lib/ably/models/message.rb +15 -12
- data/lib/ably/models/message_encoders/base.rb +6 -5
- data/lib/ably/models/message_encoders/base64.rb +1 -0
- data/lib/ably/models/message_encoders/cipher.rb +6 -3
- data/lib/ably/models/message_encoders/json.rb +1 -0
- data/lib/ably/models/message_encoders/utf8.rb +2 -9
- data/lib/ably/models/nil_logger.rb +20 -0
- data/lib/ably/models/paginated_resource.rb +5 -2
- data/lib/ably/models/presence_message.rb +21 -12
- data/lib/ably/models/protocol_message.rb +22 -6
- data/lib/ably/modules/ably.rb +11 -0
- data/lib/ably/modules/async_wrapper.rb +2 -0
- data/lib/ably/modules/conversions.rb +23 -3
- data/lib/ably/modules/encodeable.rb +2 -1
- data/lib/ably/modules/enum.rb +2 -0
- data/lib/ably/modules/event_emitter.rb +7 -1
- data/lib/ably/modules/event_machine_helpers.rb +2 -0
- data/lib/ably/modules/http_helpers.rb +2 -0
- data/lib/ably/modules/model_common.rb +12 -2
- data/lib/ably/modules/state_emitter.rb +76 -0
- data/lib/ably/modules/state_machine.rb +53 -0
- data/lib/ably/modules/statesman_monkey_patch.rb +33 -0
- data/lib/ably/modules/uses_state_machine.rb +74 -0
- data/lib/ably/realtime.rb +4 -2
- data/lib/ably/realtime/channel.rb +51 -58
- data/lib/ably/realtime/channel/channel_manager.rb +91 -0
- data/lib/ably/realtime/channel/channel_state_machine.rb +68 -0
- data/lib/ably/realtime/client.rb +70 -26
- data/lib/ably/realtime/client/incoming_message_dispatcher.rb +31 -13
- data/lib/ably/realtime/client/outgoing_message_dispatcher.rb +1 -1
- data/lib/ably/realtime/connection.rb +135 -92
- data/lib/ably/realtime/connection/connection_manager.rb +216 -33
- data/lib/ably/realtime/connection/connection_state_machine.rb +30 -73
- data/lib/ably/realtime/models/nil_channel.rb +10 -1
- data/lib/ably/realtime/presence.rb +336 -92
- data/lib/ably/rest.rb +2 -2
- data/lib/ably/rest/channel.rb +13 -4
- data/lib/ably/rest/client.rb +138 -38
- data/lib/ably/rest/middleware/logger.rb +24 -3
- data/lib/ably/rest/presence.rb +12 -7
- data/lib/ably/version.rb +1 -1
- data/spec/acceptance/realtime/channel_history_spec.rb +101 -85
- data/spec/acceptance/realtime/channel_spec.rb +461 -120
- data/spec/acceptance/realtime/client_spec.rb +119 -0
- data/spec/acceptance/realtime/connection_failures_spec.rb +499 -0
- data/spec/acceptance/realtime/connection_spec.rb +571 -97
- data/spec/acceptance/realtime/message_spec.rb +347 -333
- data/spec/acceptance/realtime/presence_history_spec.rb +35 -40
- data/spec/acceptance/realtime/presence_spec.rb +769 -239
- data/spec/acceptance/realtime/stats_spec.rb +14 -22
- data/spec/acceptance/realtime/time_spec.rb +16 -20
- data/spec/acceptance/rest/auth_spec.rb +425 -364
- data/spec/acceptance/rest/base_spec.rb +108 -176
- data/spec/acceptance/rest/channel_spec.rb +89 -89
- data/spec/acceptance/rest/channels_spec.rb +30 -32
- data/spec/acceptance/rest/client_spec.rb +273 -0
- data/spec/acceptance/rest/encoders_spec.rb +185 -0
- data/spec/acceptance/rest/message_spec.rb +186 -163
- data/spec/acceptance/rest/presence_spec.rb +150 -111
- data/spec/acceptance/rest/stats_spec.rb +45 -40
- data/spec/acceptance/rest/time_spec.rb +8 -10
- data/spec/rspec_config.rb +10 -1
- data/spec/shared/client_initializer_behaviour.rb +212 -0
- data/spec/{support/model_helper.rb → shared/model_behaviour.rb} +6 -6
- data/spec/{support/protocol_msgbus_helper.rb → shared/protocol_msgbus_behaviour.rb} +1 -1
- data/spec/spec_helper.rb +9 -0
- data/spec/support/api_helper.rb +11 -0
- data/spec/support/event_machine_helper.rb +101 -3
- data/spec/support/markdown_spec_formatter.rb +90 -0
- data/spec/support/private_api_formatter.rb +36 -0
- data/spec/support/protocol_helper.rb +32 -0
- data/spec/support/random_helper.rb +15 -0
- data/spec/support/test_app.rb +4 -0
- data/spec/unit/auth_spec.rb +68 -0
- data/spec/unit/logger_spec.rb +77 -66
- data/spec/unit/models/error_info_spec.rb +1 -1
- data/spec/unit/models/idiomatic_ruby_wrapper_spec.rb +2 -3
- data/spec/unit/models/message_encoders/base64_spec.rb +2 -2
- data/spec/unit/models/message_encoders/cipher_spec.rb +2 -2
- data/spec/unit/models/message_encoders/utf8_spec.rb +2 -46
- data/spec/unit/models/message_spec.rb +160 -15
- data/spec/unit/models/paginated_resource_spec.rb +29 -27
- data/spec/unit/models/presence_message_spec.rb +163 -20
- data/spec/unit/models/protocol_message_spec.rb +43 -8
- data/spec/unit/modules/async_wrapper_spec.rb +2 -3
- data/spec/unit/modules/conversions_spec.rb +1 -1
- data/spec/unit/modules/enum_spec.rb +2 -3
- data/spec/unit/modules/event_emitter_spec.rb +62 -5
- data/spec/unit/modules/state_emitter_spec.rb +283 -0
- data/spec/unit/realtime/channel_spec.rb +107 -2
- data/spec/unit/realtime/channels_spec.rb +1 -0
- data/spec/unit/realtime/client_spec.rb +8 -48
- data/spec/unit/realtime/connection_spec.rb +3 -3
- data/spec/unit/realtime/incoming_message_dispatcher_spec.rb +2 -2
- data/spec/unit/realtime/presence_spec.rb +13 -4
- data/spec/unit/realtime/realtime_spec.rb +0 -11
- data/spec/unit/realtime/websocket_transport_spec.rb +2 -2
- data/spec/unit/rest/channel_spec.rb +109 -0
- data/spec/unit/rest/channels_spec.rb +4 -3
- data/spec/unit/rest/client_spec.rb +30 -125
- data/spec/unit/rest/rest_spec.rb +10 -0
- data/spec/unit/util/crypto_spec.rb +10 -5
- data/spec/unit/util/pub_sub_spec.rb +5 -5
- metadata +44 -12
- data/spec/integration/modules/state_emitter_spec.rb +0 -80
- 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
|
5
|
-
|
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
|
-
|
18
|
-
|
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
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
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
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
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
|
-
|
33
|
-
|
34
|
-
|
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
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
33
|
+
options = [
|
34
|
+
{ protocol: :json },
|
35
|
+
{ use_binary_protocol: false }
|
36
|
+
].each do |client_option|
|
42
37
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
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
|
-
|
49
|
-
|
50
|
-
|
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
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
50
|
+
options = [
|
51
|
+
{ protocol: :msgpack },
|
52
|
+
{ use_binary_protocol: true }
|
53
|
+
].each do |client_option|
|
59
54
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
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
|
-
|
66
|
-
|
67
|
-
|
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
|
-
|
75
|
-
|
76
|
-
|
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
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
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
|
-
|
153
|
-
|
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
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
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 '
|
171
|
-
expect { client.
|
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
|
-
|
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
|
-
|
101
|
+
stub_request(:get, "#{client.endpoint}/time").
|
102
|
+
to_return(:status => 500, :headers => { 'Content-Type' => 'application/json' })
|
184
103
|
end
|
185
104
|
|
186
|
-
it '
|
187
|
-
expect { client.
|
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
|
-
|
194
|
-
let(:
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
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
|
-
|
208
|
-
|
209
|
-
|
138
|
+
context 'when auth#token_renewable?' do
|
139
|
+
before do
|
140
|
+
client.auth.authorise
|
141
|
+
end
|
210
142
|
|
211
|
-
|
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
|
-
|
214
|
-
expect(
|
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 '
|
219
|
-
let(:
|
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
|
-
|
228
|
-
|
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
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
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
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
12
|
+
describe '#publish' do
|
13
|
+
let(:channel) { client.channel('test') }
|
14
|
+
let(:event) { 'foo' }
|
15
|
+
let(:message) { 'woop!' }
|
17
16
|
|
18
|
-
|
19
|
-
|
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
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
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
|
-
|
35
|
-
|
36
|
-
|
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
|
-
|
41
|
-
|
39
|
+
it 'should return the current message history for the channel' do
|
40
|
+
actual_history = channel.history
|
42
41
|
|
43
|
-
|
42
|
+
expect(actual_history.size).to eql(3)
|
44
43
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
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
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
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
|
-
|
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
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
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
|
-
|
72
|
-
|
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
|
-
|
75
|
-
|
76
|
-
|
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
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
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
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
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
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
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
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
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
|
-
|
108
|
-
|
109
|
-
|
108
|
+
before do
|
109
|
+
channel.history(options)
|
110
|
+
end
|
110
111
|
|
111
|
-
|
112
|
-
|
113
|
-
|
112
|
+
context 'with milliseconds since epoch value' do
|
113
|
+
let(:milliseconds) { as_since_epoch(Time.now) }
|
114
|
+
let(:options) { { option => milliseconds } }
|
114
115
|
|
115
|
-
|
116
|
-
|
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
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
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
|
-
|
126
|
-
|
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
|