ably 0.7.5 → 0.7.6

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