ably 0.7.5 → 0.7.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +5 -13
  2. data/.gitignore +1 -0
  3. data/.gitmodules +3 -0
  4. data/README.md +46 -22
  5. data/SPEC.md +345 -240
  6. data/ably.gemspec +4 -2
  7. data/lib/ably/auth.rb +18 -14
  8. data/lib/ably/models/message.rb +1 -1
  9. data/lib/ably/models/paginated_resource.rb +31 -44
  10. data/lib/ably/models/presence_message.rb +1 -1
  11. data/lib/ably/models/stat.rb +67 -24
  12. data/lib/ably/models/stats_types.rb +131 -0
  13. data/lib/ably/modules/async_wrapper.rb +3 -2
  14. data/lib/ably/modules/message_emitter.rb +2 -2
  15. data/lib/ably/realtime.rb +1 -1
  16. data/lib/ably/realtime/channel.rb +24 -3
  17. data/lib/ably/realtime/channel/channel_manager.rb +1 -0
  18. data/lib/ably/realtime/client.rb +2 -2
  19. data/lib/ably/realtime/connection.rb +1 -1
  20. data/lib/ably/realtime/presence.rb +12 -1
  21. data/lib/ably/rest.rb +1 -1
  22. data/lib/ably/rest/channel.rb +4 -5
  23. data/lib/ably/rest/client.rb +5 -5
  24. data/lib/ably/rest/presence.rb +2 -2
  25. data/lib/ably/version.rb +1 -1
  26. data/spec/acceptance/realtime/channel_history_spec.rb +74 -23
  27. data/spec/acceptance/realtime/channel_spec.rb +3 -3
  28. data/spec/acceptance/realtime/client_spec.rb +3 -3
  29. data/spec/acceptance/realtime/connection_failures_spec.rb +2 -2
  30. data/spec/acceptance/realtime/connection_spec.rb +4 -4
  31. data/spec/acceptance/realtime/message_spec.rb +5 -5
  32. data/spec/acceptance/realtime/presence_history_spec.rb +56 -13
  33. data/spec/acceptance/realtime/presence_spec.rb +8 -8
  34. data/spec/acceptance/realtime/stats_spec.rb +1 -1
  35. data/spec/acceptance/realtime/time_spec.rb +1 -1
  36. data/spec/acceptance/rest/auth_spec.rb +31 -4
  37. data/spec/acceptance/rest/base_spec.rb +3 -3
  38. data/spec/acceptance/rest/channel_spec.rb +19 -19
  39. data/spec/acceptance/rest/channels_spec.rb +1 -1
  40. data/spec/acceptance/rest/client_spec.rb +9 -6
  41. data/spec/acceptance/rest/encoders_spec.rb +1 -1
  42. data/spec/acceptance/rest/message_spec.rb +10 -10
  43. data/spec/acceptance/rest/presence_spec.rb +81 -51
  44. data/spec/acceptance/rest/stats_spec.rb +46 -41
  45. data/spec/acceptance/rest/time_spec.rb +1 -1
  46. data/spec/shared/client_initializer_behaviour.rb +30 -19
  47. data/spec/spec_helper.rb +3 -0
  48. data/spec/support/markdown_spec_formatter.rb +1 -1
  49. data/spec/support/test_app.rb +11 -24
  50. data/spec/unit/auth_spec.rb +1 -1
  51. data/spec/unit/models/paginated_resource_spec.rb +81 -72
  52. data/spec/unit/models/stats_spec.rb +289 -0
  53. data/spec/unit/modules/async_wrapper_spec.rb +1 -1
  54. data/spec/unit/realtime/client_spec.rb +1 -1
  55. data/spec/unit/realtime/realtime_spec.rb +1 -1
  56. data/spec/unit/rest/channel_spec.rb +1 -1
  57. data/spec/unit/rest/client_spec.rb +8 -8
  58. data/spec/unit/rest/rest_spec.rb +1 -1
  59. data/spec/unit/util/crypto_spec.rb +1 -1
  60. metadata +55 -43
  61. data/spec/resources/crypto-data-128.json +0 -56
  62. data/spec/resources/crypto-data-256.json +0 -56
  63. data/spec/unit/models/stat_spec.rb +0 -113
@@ -15,7 +15,7 @@ describe Ably::Rest::Channels do
15
15
 
16
16
  vary_by_protocol do
17
17
  let(:client) do
18
- Ably::Rest::Client.new(api_key: api_key, environment: environment, protocol: protocol)
18
+ Ably::Rest::Client.new(key: api_key, environment: environment, protocol: protocol)
19
19
  end
20
20
  let(:channel_name) { random_str }
21
21
  let(:options) { { key: 'value' } }
@@ -43,12 +43,15 @@ describe Ably::Rest::Client do
43
43
  Ably::Rest::Client.new(client_options) do
44
44
  @request_index ||= 0
45
45
  @request_index += 1
46
- send("token_request_#{@request_index}")
46
+ send("token_request_#{@request_index > 2 ? 'next' : @request_index}")
47
47
  end
48
48
  end
49
49
  let(:token_request_1) { client.auth.create_token_request(token_request_options.merge(client_id: random_str)) }
50
50
  let(:token_request_2) { client.auth.create_token_request(token_request_options.merge(client_id: random_str)) }
51
51
 
52
+ # If token expires against whilst runnig tests in a slower CI environment then use this token
53
+ let(:token_request_next) { client.auth.create_token_request(token_request_options.merge(client_id: random_str)) }
54
+
52
55
  context 'when expired' do
53
56
  let(:token_request_options) { { key_id: key_id, key_secret: key_secret, ttl: Ably::Models::Token::TOKEN_EXPIRY_BUFFER } }
54
57
 
@@ -79,7 +82,7 @@ describe Ably::Rest::Client do
79
82
  end
80
83
 
81
84
  context 'connection transport' do
82
- let(:client_options) { default_options.merge(api_key: api_key) }
85
+ let(:client_options) { default_options.merge(key: api_key) }
83
86
 
84
87
  context 'for default host' do
85
88
  it "is configured to timeout connection opening in #{connection_retry.fetch(:single_request_open_timeout)} seconds" do
@@ -107,7 +110,7 @@ describe Ably::Rest::Client do
107
110
  let(:publish_block) { proc { client.channel('test').publish('event', 'data') } }
108
111
 
109
112
  context 'configured' do
110
- let(:client_options) { default_options.merge(api_key: api_key) }
113
+ let(:client_options) { default_options.merge(key: api_key) }
111
114
 
112
115
  it 'should make connection attempts to A.ably-realtime.com, B.ably-realtime.com, C.ably-realtime.com, D.ably-realtime.com, E.ably-realtime.com' do
113
116
  hosts = []
@@ -119,7 +122,7 @@ describe Ably::Rest::Client do
119
122
  end
120
123
 
121
124
  context 'when environment is NOT production' do
122
- let(:client_options) { default_options.merge(environment: 'sandbox', api_key: api_key) }
125
+ let(:client_options) { default_options.merge(environment: 'sandbox', key: api_key) }
123
126
  let!(:default_host_request_stub) do
124
127
  stub_request(:post, "https://#{api_key}@#{environment}-#{Ably::Rest::Client::DOMAIN}#{path}").to_return do
125
128
  raise Faraday::TimeoutError.new('timeout error message')
@@ -135,7 +138,7 @@ describe Ably::Rest::Client do
135
138
  let(:custom_hosts) { %w(A.ably-realtime.com B.ably-realtime.com) }
136
139
  let(:max_attempts) { 2 }
137
140
  let(:cumulative_timeout) { 0.5 }
138
- let(:client_options) { default_options.merge(environment: nil, api_key: api_key) }
141
+ let(:client_options) { default_options.merge(environment: nil, key: api_key) }
139
142
 
140
143
  before do
141
144
  stub_const 'Ably::FALLBACK_HOSTS', custom_hosts
@@ -209,7 +212,7 @@ describe Ably::Rest::Client do
209
212
 
210
213
  context 'with a custom host' do
211
214
  let(:custom_host) { 'host.does.not.exist' }
212
- let(:client_options) { default_options.merge(api_key: api_key, rest_host: custom_host) }
215
+ let(:client_options) { default_options.merge(key: api_key, rest_host: custom_host) }
213
216
  let(:capability) { { :foo => ["publish"] } }
214
217
 
215
218
  context 'that does not exist' do
@@ -3,7 +3,7 @@ require 'spec_helper'
3
3
  require 'base64'
4
4
 
5
5
  describe Ably::Models::MessageEncoders do
6
- let(:default_client_options) { { api_key: api_key, environment: environment } }
6
+ let(:default_client_options) { { key: api_key, environment: environment } }
7
7
  let(:client) { Ably::Rest::Client.new(default_client_options.merge(protocol: protocol)) }
8
8
  let(:channel_options) { {} }
9
9
  let(:channel) { client.channel('test', channel_options) }
@@ -6,7 +6,7 @@ describe Ably::Rest::Channel, 'messages' do
6
6
  include Ably::Modules::Conversions
7
7
 
8
8
  vary_by_protocol do
9
- let(:default_client_options) { { api_key: api_key, environment: environment, protocol: protocol } }
9
+ let(:default_client_options) { { key: api_key, environment: environment, protocol: protocol } }
10
10
  let(:client_options) { default_client_options }
11
11
  let(:client) { Ably::Rest::Client.new(client_options) }
12
12
  let(:other_client) { Ably::Rest::Client.new(client_options) }
@@ -17,7 +17,7 @@ describe Ably::Rest::Channel, 'messages' do
17
17
 
18
18
  it 'is converted into UTF_8' do
19
19
  channel.publish message_name, 'example'
20
- message = channel.history.first
20
+ message = channel.history.items.first
21
21
  expect(message.name.encoding).to eql(Encoding::UTF_8)
22
22
  expect(message.name.encode(Encoding::ASCII_8BIT)).to eql(message_name)
23
23
  end
@@ -80,13 +80,13 @@ describe Ably::Rest::Channel, 'messages' do
80
80
  it 'sends and retrieves messages that are encrypted & decrypted by the Ably library' do
81
81
  encrypted_channel.publish 'example', encoded_data_decoded
82
82
 
83
- message = encrypted_channel.history.first
83
+ message = encrypted_channel.history.items.first
84
84
  expect(message.data).to eql(encoded_data_decoded)
85
85
  expect(message.encoding).to be_nil
86
86
  end
87
87
  end
88
88
 
89
- resources_root = File.expand_path('../../../resources', __FILE__)
89
+ resources_root = File.expand_path('../../../../lib/submodules/ably-common/test-resources', __FILE__)
90
90
 
91
91
  def self.add_tests_for_data(data)
92
92
  data['items'].each_with_index do |item, index|
@@ -115,7 +115,7 @@ describe Ably::Rest::Channel, 'messages' do
115
115
  encrypted_channel.publish index.to_s, "#{index}-#{data}"
116
116
  end
117
117
 
118
- messages = encrypted_channel.history
118
+ messages = encrypted_channel.history.items
119
119
 
120
120
  expect(messages.count).to eql(message_count)
121
121
  messages.each do |message|
@@ -140,7 +140,7 @@ describe Ably::Rest::Channel, 'messages' do
140
140
  specify "delivers a #{payload_description} payload to the receiver" do
141
141
  encrypted_channel.publish 'example', payload
142
142
 
143
- message = other_client_channel.history.first
143
+ message = other_client_channel.history.items.first
144
144
  expect(message.data).to eql(payload)
145
145
  expect(message.encoding).to be_nil
146
146
  end
@@ -156,7 +156,7 @@ describe Ably::Rest::Channel, 'messages' do
156
156
  it 'does not attempt to decrypt the message' do
157
157
  unencrypted_channel.publish 'example', payload
158
158
 
159
- message = other_client_encrypted_channel.history.first
159
+ message = other_client_encrypted_channel.history.items.first
160
160
  expect(message.data).to eql(payload)
161
161
  expect(message.encoding).to be_nil
162
162
  end
@@ -175,7 +175,7 @@ describe Ably::Rest::Channel, 'messages' do
175
175
  end
176
176
 
177
177
  it 'retrieves the message that remains encrypted with an encrypted encoding attribute' do
178
- message = other_client_unencrypted_channel.history.first
178
+ message = other_client_unencrypted_channel.history.items.first
179
179
  expect(message.data).to_not eql(payload)
180
180
  expect(message.encoding).to match(/^cipher\+aes-256-cbc/)
181
181
  end
@@ -202,7 +202,7 @@ describe Ably::Rest::Channel, 'messages' do
202
202
  end
203
203
 
204
204
  it 'retrieves the message that remains encrypted with an encrypted encoding attribute' do
205
- message = encrypted_channel_client2.history.first
205
+ message = encrypted_channel_client2.history.items.first
206
206
  expect(message.data).to_not eql(payload)
207
207
  expect(message.encoding).to match(/^cipher\+aes-256-cbc/)
208
208
  end
@@ -229,7 +229,7 @@ describe Ably::Rest::Channel, 'messages' do
229
229
  end
230
230
 
231
231
  it 'retrieves the message that remains encrypted with an encrypted encoding attribute' do
232
- message = encrypted_channel_client2.history.first
232
+ message = encrypted_channel_client2.history.items.first
233
233
  expect(message.data).to_not eql(payload)
234
234
  expect(message.encoding).to match(/^cipher\+aes-256-cbc/)
235
235
  end
@@ -5,7 +5,7 @@ describe Ably::Rest::Presence do
5
5
  include Ably::Modules::Conversions
6
6
 
7
7
  vary_by_protocol do
8
- let(:default_options) { { api_key: api_key, environment: environment, protocol: protocol } }
8
+ let(:default_options) { { key: api_key, environment: environment, protocol: protocol } }
9
9
  let(:client_options) { default_options }
10
10
  let(:client) do
11
11
  Ably::Rest::Client.new(client_options)
@@ -16,88 +16,93 @@ describe Ably::Rest::Presence do
16
16
  IdiomaticRubyWrapper(fixture, stop_at: [:data])
17
17
  end
18
18
  end
19
+ let(:non_encoded_fixtures) { fixtures.reject { |fixture| fixture['encoding'] } }
20
+
21
+ # Encrypted fixtures need encryption details or an error will be raised
22
+ let(:cipher_details) { TestApp::APP_SPEC_CIPHER }
23
+ let(:algorithm) { cipher_details.fetch('algorithm').upcase }
24
+ let(:mode) { cipher_details.fetch('mode').upcase }
25
+ let(:key_length) { cipher_details.fetch('keylength') }
26
+ let(:secret_key) { Base64.decode64(cipher_details.fetch('key')) }
27
+ let(:iv) { Base64.decode64(cipher_details.fetch('iv')) }
28
+
29
+ let(:cipher_options) { { key: secret_key, algorithm: algorithm, mode: mode, key_length: key_length, iv: iv } }
30
+ let(:fixtures_channel) { client.channel('persisted:presence_fixtures', encrypted: true, cipher_params: cipher_options, iv: iv) }
19
31
 
20
32
  context 'tested against presence fixture data set up in test app' do
21
- describe '#get' do
22
- before(:context) do
23
- # When this test is run as a part of a test suite, the presence data injected in the test app may have expired
24
- reload_test_app
25
- end
33
+ before(:context) do
34
+ # When this test is run as a part of a test suite, the presence data injected in the test app may have expired
35
+ reload_test_app
36
+ end
26
37
 
27
- let(:channel) { client.channel('persisted:presence_fixtures') }
28
- let(:presence) { channel.presence.get }
38
+ describe '#get' do
39
+ let(:presence_page) { fixtures_channel.presence.get }
29
40
 
30
41
  it 'returns current members on the channel with their action set to :present' do
31
- expect(presence.size).to eql(4)
42
+ expect(presence_page.items.size).to eql(fixtures.count)
32
43
 
33
- fixtures.each do |fixture|
34
- presence_message = presence.find { |client| client.client_id == fixture[:client_id] }
44
+ non_encoded_fixtures.each do |fixture|
45
+ presence_message = presence_page.items.find { |client| client.client_id == fixture[:client_id] }
35
46
  expect(presence_message.data).to eq(fixture[:data])
36
47
  expect(presence_message.action).to eq(:present)
37
48
  end
38
49
  end
39
50
 
40
51
  context 'with :limit option' do
41
- let(:page_size) { 2 }
42
- let(:presence) { channel.presence.get(limit: page_size) }
52
+ let(:page_size) { 3 }
53
+ let(:presence_page) { fixtures_channel.presence.get(limit: page_size) }
43
54
 
44
55
  it 'returns a paged response limiting number of members per page' do
45
- expect(presence.size).to eql(2)
46
- next_page = presence.next_page
47
- expect(next_page.size).to eql(2)
48
- expect(next_page).to be_last_page
56
+ expect(presence_page.items.size).to eql(page_size)
57
+ next_page = presence_page.next
58
+ expect(next_page.items.size).to eql(page_size)
59
+ expect(next_page).to be_last
49
60
  end
50
61
  end
51
62
  end
52
63
 
53
64
  describe '#history' do
54
- before(:context) do
55
- # When this test is run as a part of a test suite, the presence data injected in the test app may have expired
56
- reload_test_app
57
- end
58
-
59
- let(:channel) { client.channel('persisted:presence_fixtures') }
60
- let(:presence_history) { channel.presence.history }
65
+ let(:history_page) { fixtures_channel.presence.history }
61
66
 
62
67
  it 'returns recent presence activity' do
63
- expect(presence_history.size).to eql(4)
68
+ expect(history_page.items.size).to eql(fixtures.count)
64
69
 
65
- fixtures.each do |fixture|
66
- presence_message = presence_history.find { |client| client.client_id == fixture['clientId'] }
70
+ non_encoded_fixtures.each do |fixture|
71
+ presence_message = history_page.items.find { |client| client.client_id == fixture['clientId'] }
67
72
  expect(presence_message.data).to eq(fixture[:data])
68
73
  end
69
74
  end
70
75
 
71
76
  context 'with options' do
72
- let(:page_size) { 2 }
77
+ let(:page_size) { 3 }
73
78
 
74
79
  context 'direction: :forwards' do
75
- let(:presence_history) { channel.presence.history(direction: :forwards) }
76
- let(:paged_history_forward) { channel.presence.history(limit: page_size, direction: :forwards) }
80
+ let(:history_page) { fixtures_channel.presence.history(direction: :forwards) }
81
+ let(:paged_history_forward) { fixtures_channel.presence.history(limit: page_size, direction: :forwards) }
77
82
 
78
83
  it 'returns recent presence activity forwards with most recent history last' do
79
84
  expect(paged_history_forward).to be_a(Ably::Models::PaginatedResource)
80
- expect(paged_history_forward.size).to eql(2)
85
+ expect(paged_history_forward.items.size).to eql(page_size)
81
86
 
82
- next_page = paged_history_forward.next_page
87
+ next_page = paged_history_forward.next
83
88
 
84
- expect(paged_history_forward.first.id).to eql(presence_history.first.id)
85
- expect(next_page.first.id).to eql(presence_history[page_size].id)
89
+ expect(paged_history_forward.items.first.id).to eql(history_page.items.first.id)
90
+ expect(next_page.items.first.id).to eql(history_page.items[page_size].id)
86
91
  end
87
92
  end
88
93
 
89
94
  context 'direction: :backwards' do
90
- let(:presence_history) { channel.presence.history(direction: :backwards) }
91
- let(:paged_history_backward) { channel.presence.history(limit: page_size, direction: :backwards) }
95
+ let(:history_page) { fixtures_channel.presence.history(direction: :backwards) }
96
+ let(:paged_history_backward) { fixtures_channel.presence.history(limit: page_size, direction: :backwards) }
92
97
 
93
98
  it 'returns recent presence activity backwards with most recent history first' do
94
99
  expect(paged_history_backward).to be_a(Ably::Models::PaginatedResource)
95
- expect(paged_history_backward.size).to eql(2)
100
+ expect(paged_history_backward.items.size).to eql(page_size)
96
101
 
97
- next_page = paged_history_backward.next_page
102
+ next_page = paged_history_backward.next
98
103
 
99
- expect(paged_history_backward.first.id).to eql(presence_history.first.id)
100
- expect(next_page.first.id).to eql(presence_history[page_size].id)
104
+ expect(paged_history_backward.items.first.id).to eql(history_page.items.first.id)
105
+ expect(next_page.items.first.id).to eql(history_page.items[page_size].id)
101
106
  end
102
107
  end
103
108
  end
@@ -117,7 +122,7 @@ describe Ably::Rest::Presence do
117
122
  end
118
123
  end
119
124
  let(:client) do
120
- Ably::Rest::Client.new(api_key: "#{user}:#{secret}")
125
+ Ably::Rest::Client.new(key: "#{user}:#{secret}")
121
126
  end
122
127
 
123
128
  [:start, :end].each do |option|
@@ -154,7 +159,32 @@ describe Ably::Rest::Presence do
154
159
  end
155
160
  end
156
161
 
157
- describe 'decoding', :webmock do
162
+ describe 'decoding' do
163
+ context 'with encoded fixture data' do
164
+ let(:decoded_client_id) { 'client_decoded' }
165
+ let(:encoded_client_id) { 'client_encoded' }
166
+
167
+ def message(client_id, messages)
168
+ messages.items.find { |message| message.client_id == client_id }
169
+ end
170
+
171
+ describe '#history' do
172
+ let(:history) { fixtures_channel.presence.history }
173
+ it 'decodes encoded and encryped presence fixture data automatically' do
174
+ expect(message(decoded_client_id, history).data).to eql(message(encoded_client_id, history).data)
175
+ end
176
+ end
177
+
178
+ describe '#get' do
179
+ let(:present) { fixtures_channel.presence.get }
180
+ it 'decodes encoded and encryped presence fixture data automatically' do
181
+ expect(message(decoded_client_id, present).data).to eql(message(encoded_client_id, present).data)
182
+ end
183
+ end
184
+ end
185
+ end
186
+
187
+ describe 'decoding permutations using mocked #history', :webmock do
158
188
  let(:user) { 'appid.keyuid' }
159
189
  let(:secret) { random_str(8) }
160
190
  let(:endpoint) do
@@ -164,7 +194,7 @@ describe Ably::Rest::Presence do
164
194
  end
165
195
  end
166
196
  let(:client) do
167
- Ably::Rest::Client.new(client_options.merge(api_key: "#{user}:#{secret}"))
197
+ Ably::Rest::Client.new(client_options.merge(key: "#{user}:#{secret}"))
168
198
  end
169
199
 
170
200
  let(:data) { random_str(32) }
@@ -204,9 +234,9 @@ describe Ably::Rest::Presence do
204
234
  end
205
235
 
206
236
  it 'automaticaly decodes presence messages' do
207
- present = presence.get
208
- expect(present.first.encoding).to be_nil
209
- expect(present.first.data).to eql(data)
237
+ present_page = presence.get
238
+ expect(present_page.items.first.encoding).to be_nil
239
+ expect(present_page.items.first.data).to eql(data)
210
240
  end
211
241
  end
212
242
 
@@ -221,9 +251,9 @@ describe Ably::Rest::Presence do
221
251
  end
222
252
 
223
253
  it 'automaticaly decodes presence messages' do
224
- history = presence.history
225
- expect(history.first.encoding).to be_nil
226
- expect(history.first.data).to eql(data)
254
+ history_page = presence.history
255
+ expect(history_page.items.first.encoding).to be_nil
256
+ expect(history_page.items.first.data).to eql(data)
227
257
  end
228
258
  end
229
259
  end
@@ -245,7 +275,7 @@ describe Ably::Rest::Presence do
245
275
  stub_request(:get, "#{endpoint}/channels/#{CGI.escape(channel_name)}/presence").
246
276
  to_return(:body => serialized_encoded_message_with_invalid_encoding, :headers => { 'Content-Type' => content_type })
247
277
  }
248
- let(:presence_message) { presence.get.first }
278
+ let(:presence_message) { presence.get.items.first }
249
279
 
250
280
  after do
251
281
  expect(get_stub).to have_been_requested
@@ -269,7 +299,7 @@ describe Ably::Rest::Presence do
269
299
  stub_request(:get, "#{endpoint}/channels/#{CGI.escape(channel_name)}/presence/history").
270
300
  to_return(:body => serialized_encoded_message_with_invalid_encoding, :headers => { 'Content-Type' => content_type })
271
301
  }
272
- let(:presence_message) { presence.history.first }
302
+ let(:presence_message) { presence.history.items.first }
273
303
 
274
304
  after do
275
305
  expect(history_stub).to have_been_requested
@@ -9,17 +9,17 @@ describe Ably::Rest::Client, '#stats' do
9
9
 
10
10
  STATS_FIXTURES = [
11
11
  {
12
- intervalId: Ably::Models::Stat.to_interval_id(LAST_INTERVAL - 120, :minute),
12
+ intervalId: Ably::Models::Stats.to_interval_id(LAST_INTERVAL - 120, :minute),
13
13
  inbound: { realtime: { messages: { count: 50, data: 5000 } } },
14
14
  outbound: { realtime: { messages: { count: 20, data: 2000 } } }
15
15
  },
16
16
  {
17
- intervalId: Ably::Models::Stat.to_interval_id(LAST_INTERVAL - 60, :minute),
17
+ intervalId: Ably::Models::Stats.to_interval_id(LAST_INTERVAL - 60, :minute),
18
18
  inbound: { realtime: { messages: { count: 60, data: 6000 } } },
19
19
  outbound: { realtime: { messages: { count: 10, data: 1000 } } }
20
20
  },
21
21
  {
22
- intervalId: Ably::Models::Stat.to_interval_id(LAST_INTERVAL, :minute),
22
+ intervalId: Ably::Models::Stats.to_interval_id(LAST_INTERVAL, :minute),
23
23
  inbound: { realtime: { messages: { count: 70, data: 7000 } } },
24
24
  outbound: { realtime: { messages: { count: 40, data: 4000 } } },
25
25
  persisted: { presence: { count: 20, data: 2000 } },
@@ -36,7 +36,7 @@ describe Ably::Rest::Client, '#stats' do
36
36
  end
37
37
 
38
38
  vary_by_protocol do
39
- let(:client) { Ably::Rest::Client.new(api_key: api_key, environment: environment, protocol: protocol) }
39
+ let(:client) { Ably::Rest::Client.new(key: api_key, environment: environment, protocol: protocol) }
40
40
 
41
41
  describe 'fetching application stats' do
42
42
  context 'by minute' do
@@ -45,55 +45,60 @@ describe Ably::Rest::Client, '#stats' do
45
45
 
46
46
  context 'with :from set to last interval and :limit set to 1' do
47
47
  let(:subject) { client.stats(start: as_since_epoch(LAST_INTERVAL), by: :minute, limit: 1) }
48
- let(:stat) { subject.first}
48
+ let(:stat) { subject.items.first }
49
49
 
50
50
  it 'retrieves only one stat' do
51
- expect(subject.count).to eql(1)
51
+ expect(subject.items.count).to eql(1)
52
+ end
53
+
54
+ it 'returns zero value for any missing metrics' do
55
+ expect(stat.channels.refused).to eql(0)
56
+ expect(stat.outbound.webhook.all.count).to eql(0)
52
57
  end
53
58
 
54
59
  it 'returns all aggregated message data' do
55
- expect(stat.all[:messages][:count]).to eql(70 + 40) # inbound + outbound
56
- expect(stat.all[:messages][:data]).to eql(7000 + 4000) # inbound + outbound
60
+ expect(stat.all.messages.count).to eql(70 + 40) # inbound + outbound
61
+ expect(stat.all.messages.data).to eql(7000 + 4000) # inbound + outbound
57
62
  end
58
63
 
59
64
  it 'returns inbound realtime all data' do
60
- expect(stat.inbound[:realtime][:all][:count]).to eql(70)
61
- expect(stat.inbound[:realtime][:all][:data]).to eql(7000)
65
+ expect(stat.inbound.realtime.all.count).to eql(70)
66
+ expect(stat.inbound.realtime.all.data).to eql(7000)
62
67
  end
63
68
 
64
69
  it 'returns inbound realtime message data' do
65
- expect(stat.inbound[:realtime][:messages][:count]).to eql(70)
66
- expect(stat.inbound[:realtime][:messages][:data]).to eql(7000)
70
+ expect(stat.inbound.realtime.messages.count).to eql(70)
71
+ expect(stat.inbound.realtime.messages.data).to eql(7000)
67
72
  end
68
73
 
69
74
  it 'returns outbound realtime all data' do
70
- expect(stat.outbound[:realtime][:all][:count]).to eql(40)
71
- expect(stat.outbound[:realtime][:all][:data]).to eql(4000)
75
+ expect(stat.outbound.realtime.all.count).to eql(40)
76
+ expect(stat.outbound.realtime.all.data).to eql(4000)
72
77
  end
73
78
 
74
79
  it 'returns persisted presence all data' do
75
- expect(stat.persisted[:all][:count]).to eql(20)
76
- expect(stat.persisted[:all][:data]).to eql(2000)
80
+ expect(stat.persisted.all.count).to eql(20)
81
+ expect(stat.persisted.all.data).to eql(2000)
77
82
  end
78
83
 
79
84
  it 'returns connections all data' do
80
- expect(stat.connections[:tls][:peak]).to eql(20)
81
- expect(stat.connections[:tls][:opened]).to eql(10)
85
+ expect(stat.connections.tls.peak).to eql(20)
86
+ expect(stat.connections.tls.opened).to eql(10)
82
87
  end
83
88
 
84
89
  it 'returns channels all data' do
85
- expect(stat.channels[:peak]).to eql(50)
86
- expect(stat.channels[:opened]).to eql(30)
90
+ expect(stat.channels.peak).to eql(50)
91
+ expect(stat.channels.opened).to eql(30)
87
92
  end
88
93
 
89
94
  it 'returns api_requests data' do
90
- expect(stat.api_requests[:succeeded]).to eql(50)
91
- expect(stat.api_requests[:failed]).to eql(10)
95
+ expect(stat.api_requests.succeeded).to eql(50)
96
+ expect(stat.api_requests.failed).to eql(10)
92
97
  end
93
98
 
94
99
  it 'returns token_requests data' do
95
- expect(stat.token_requests[:succeeded]).to eql(60)
96
- expect(stat.token_requests[:failed]).to eql(20)
100
+ expect(stat.token_requests.succeeded).to eql(60)
101
+ expect(stat.token_requests.failed).to eql(20)
97
102
  end
98
103
 
99
104
  it 'returns stat objects with #interval_granularity equal to :minute' do
@@ -112,34 +117,34 @@ describe Ably::Rest::Client, '#stats' do
112
117
  context 'with :start set to first interval, :limit set to 1 and direction :forwards' do
113
118
  let(:first_interval) { LAST_INTERVAL - 120 }
114
119
  let(:subject) { client.stats(start: as_since_epoch(first_interval), by: :minute, direction: :forwards, limit: 1) }
115
- let(:stat) { subject.first}
120
+ let(:stat) { subject.items.first }
116
121
 
117
122
  it 'returns the first interval stats as stats are provided forwards from :start' do
118
- expect(stat.inbound[:realtime][:all][:count]).to eql(first_inbound_realtime_count)
123
+ expect(stat.inbound.realtime.all.count).to eql(first_inbound_realtime_count)
119
124
  end
120
125
 
121
126
  it 'returns 3 pages of stats' do
122
- expect(subject).to be_first_page
123
- expect(subject).to_not be_last_page
124
- page3 = subject.next_page.next_page
125
- expect(page3).to be_last_page
126
- expect(page3.first.inbound[:realtime][:all][:count]).to eql(last_inbound_realtime_count)
127
+ expect(subject).to be_first
128
+ expect(subject).to_not be_last
129
+ page3 = subject.next.next
130
+ expect(page3).to be_last
131
+ expect(page3.items.first.inbound.realtime.all.count).to eql(last_inbound_realtime_count)
127
132
  end
128
133
  end
129
134
 
130
135
  context 'with :end set to last interval, :limit set to 1 and direction :backwards' do
131
136
  let(:subject) { client.stats(:end => as_since_epoch(LAST_INTERVAL), by: :minute, direction: :backwards, limit: 1) }
132
- let(:stat) { subject.first}
137
+ let(:stat) { subject.items.first }
133
138
 
134
139
  it 'returns the 3rd interval stats first as stats are provided backwards from :end' do
135
- expect(stat.inbound[:realtime][:all][:count]).to eql(last_inbound_realtime_count)
140
+ expect(stat.inbound.realtime.all.count).to eql(last_inbound_realtime_count)
136
141
  end
137
142
 
138
143
  it 'returns 3 pages of stats' do
139
- expect(subject).to be_first_page
140
- expect(subject).to_not be_last_page
141
- page3 = subject.next_page.next_page
142
- expect(page3.first.inbound[:realtime][:all][:count]).to eql(first_inbound_realtime_count)
144
+ expect(subject).to be_first
145
+ expect(subject).to_not be_last
146
+ page3 = subject.next.next
147
+ expect(page3.items.first.inbound.realtime.all.count).to eql(first_inbound_realtime_count)
143
148
  end
144
149
  end
145
150
  end
@@ -147,7 +152,7 @@ describe Ably::Rest::Client, '#stats' do
147
152
  [:hour, :day, :month].each do |interval|
148
153
  context "by #{interval}" do
149
154
  let(:subject) { client.stats(start: as_since_epoch(LAST_INTERVAL), by: interval, direction: 'forwards', limit: 1) }
150
- let(:stat) { subject.first }
155
+ let(:stat) { subject.items.first }
151
156
  let(:aggregate_messages_count) do
152
157
  STATS_FIXTURES.inject(0) do |sum, fixture|
153
158
  sum + fixture[:inbound][:realtime][:messages][:count] + fixture[:outbound][:realtime][:messages][:count]
@@ -160,10 +165,10 @@ describe Ably::Rest::Client, '#stats' do
160
165
  end
161
166
 
162
167
  it 'should aggregate the stats for that period' do
163
- expect(subject.count).to eql(1)
168
+ expect(subject.items.count).to eql(1)
164
169
 
165
- expect(stat.all[:messages][:count]).to eql(aggregate_messages_count)
166
- expect(stat.all[:messages][:data]).to eql(aggregate_messages_data)
170
+ expect(stat.all.messages.count).to eql(aggregate_messages_count)
171
+ expect(stat.all.messages.data).to eql(aggregate_messages_data)
167
172
  end
168
173
  end
169
174
  end