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,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