ably-rest 0.7.5 → 0.8.1

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 (76) hide show
  1. checksums.yaml +8 -8
  2. data/.travis.yml +2 -0
  3. data/README.md +41 -15
  4. data/SPEC.md +654 -518
  5. data/lib/submodules/ably-ruby/.gitignore +1 -0
  6. data/lib/submodules/ably-ruby/.gitmodules +3 -0
  7. data/lib/submodules/ably-ruby/README.md +54 -26
  8. data/lib/submodules/ably-ruby/SPEC.md +468 -322
  9. data/lib/submodules/ably-ruby/ably.gemspec +4 -2
  10. data/lib/submodules/ably-ruby/lib/ably/auth.rb +185 -131
  11. data/lib/submodules/ably-ruby/lib/ably/models/message.rb +1 -1
  12. data/lib/submodules/ably-ruby/lib/ably/models/paginated_resource.rb +31 -44
  13. data/lib/submodules/ably-ruby/lib/ably/models/presence_message.rb +2 -2
  14. data/lib/submodules/ably-ruby/lib/ably/models/protocol_message.rb +1 -2
  15. data/lib/submodules/ably-ruby/lib/ably/models/stat.rb +67 -24
  16. data/lib/submodules/ably-ruby/lib/ably/models/stats_types.rb +131 -0
  17. data/lib/submodules/ably-ruby/lib/ably/models/token_details.rb +101 -0
  18. data/lib/submodules/ably-ruby/lib/ably/models/token_request.rb +108 -0
  19. data/lib/submodules/ably-ruby/lib/ably/modules/async_wrapper.rb +3 -2
  20. data/lib/submodules/ably-ruby/lib/ably/modules/http_helpers.rb +1 -1
  21. data/lib/submodules/ably-ruby/lib/ably/modules/message_emitter.rb +2 -2
  22. data/lib/submodules/ably-ruby/lib/ably/realtime.rb +3 -7
  23. data/lib/submodules/ably-ruby/lib/ably/realtime/channel.rb +32 -5
  24. data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_manager.rb +1 -0
  25. data/lib/submodules/ably-ruby/lib/ably/realtime/client.rb +4 -8
  26. data/lib/submodules/ably-ruby/lib/ably/realtime/connection.rb +5 -3
  27. data/lib/submodules/ably-ruby/lib/ably/realtime/presence.rb +12 -1
  28. data/lib/submodules/ably-ruby/lib/ably/rest.rb +3 -7
  29. data/lib/submodules/ably-ruby/lib/ably/rest/channel.rb +13 -10
  30. data/lib/submodules/ably-ruby/lib/ably/rest/client.rb +19 -20
  31. data/lib/submodules/ably-ruby/lib/ably/rest/presence.rb +14 -12
  32. data/lib/submodules/ably-ruby/lib/ably/version.rb +1 -1
  33. data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_history_spec.rb +74 -23
  34. data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_spec.rb +3 -3
  35. data/lib/submodules/ably-ruby/spec/acceptance/realtime/client_spec.rb +18 -18
  36. data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_failures_spec.rb +5 -5
  37. data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_spec.rb +12 -12
  38. data/lib/submodules/ably-ruby/spec/acceptance/realtime/message_spec.rb +5 -5
  39. data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_history_spec.rb +56 -13
  40. data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_spec.rb +8 -8
  41. data/lib/submodules/ably-ruby/spec/acceptance/realtime/stats_spec.rb +1 -1
  42. data/lib/submodules/ably-ruby/spec/acceptance/realtime/time_spec.rb +1 -1
  43. data/lib/submodules/ably-ruby/spec/acceptance/rest/auth_spec.rb +262 -158
  44. data/lib/submodules/ably-ruby/spec/acceptance/rest/base_spec.rb +11 -9
  45. data/lib/submodules/ably-ruby/spec/acceptance/rest/channel_spec.rb +28 -21
  46. data/lib/submodules/ably-ruby/spec/acceptance/rest/channels_spec.rb +1 -1
  47. data/lib/submodules/ably-ruby/spec/acceptance/rest/client_spec.rb +30 -27
  48. data/lib/submodules/ably-ruby/spec/acceptance/rest/encoders_spec.rb +1 -1
  49. data/lib/submodules/ably-ruby/spec/acceptance/rest/message_spec.rb +10 -10
  50. data/lib/submodules/ably-ruby/spec/acceptance/rest/presence_spec.rb +93 -56
  51. data/lib/submodules/ably-ruby/spec/acceptance/rest/stats_spec.rb +50 -45
  52. data/lib/submodules/ably-ruby/spec/acceptance/rest/time_spec.rb +1 -1
  53. data/lib/submodules/ably-ruby/spec/rspec_config.rb +3 -2
  54. data/lib/submodules/ably-ruby/spec/shared/client_initializer_behaviour.rb +36 -28
  55. data/lib/submodules/ably-ruby/spec/spec_helper.rb +3 -0
  56. data/lib/submodules/ably-ruby/spec/support/api_helper.rb +3 -3
  57. data/lib/submodules/ably-ruby/spec/support/markdown_spec_formatter.rb +1 -1
  58. data/lib/submodules/ably-ruby/spec/support/test_app.rb +20 -33
  59. data/lib/submodules/ably-ruby/spec/unit/auth_spec.rb +18 -1
  60. data/lib/submodules/ably-ruby/spec/unit/models/paginated_resource_spec.rb +81 -72
  61. data/lib/submodules/ably-ruby/spec/unit/models/stats_spec.rb +289 -0
  62. data/lib/submodules/ably-ruby/spec/unit/models/token_details_spec.rb +111 -0
  63. data/lib/submodules/ably-ruby/spec/unit/models/token_request_spec.rb +110 -0
  64. data/lib/submodules/ably-ruby/spec/unit/modules/async_wrapper_spec.rb +1 -1
  65. data/lib/submodules/ably-ruby/spec/unit/realtime/client_spec.rb +1 -1
  66. data/lib/submodules/ably-ruby/spec/unit/realtime/realtime_spec.rb +1 -1
  67. data/lib/submodules/ably-ruby/spec/unit/rest/channel_spec.rb +1 -1
  68. data/lib/submodules/ably-ruby/spec/unit/rest/client_spec.rb +8 -8
  69. data/lib/submodules/ably-ruby/spec/unit/rest/rest_spec.rb +1 -1
  70. data/lib/submodules/ably-ruby/spec/unit/util/crypto_spec.rb +1 -1
  71. metadata +9 -7
  72. data/lib/submodules/ably-ruby/lib/ably/models/token.rb +0 -74
  73. data/lib/submodules/ably-ruby/spec/resources/crypto-data-128.json +0 -56
  74. data/lib/submodules/ably-ruby/spec/resources/crypto-data-256.json +0 -56
  75. data/lib/submodules/ably-ruby/spec/unit/models/stat_spec.rb +0 -113
  76. data/lib/submodules/ably-ruby/spec/unit/models/token_spec.rb +0 -86
@@ -4,7 +4,7 @@ require 'shared/protocol_msgbus_behaviour'
4
4
  describe Ably::Auth do
5
5
  let(:client) { double('client').as_null_object }
6
6
  let(:client_id) { nil }
7
- let(:options) { { api_key: 'appid.keyuid:keysecret', client_id: client_id } }
7
+ let(:options) { { key: 'appid.keyuid:keysecret', client_id: client_id } }
8
8
 
9
9
  subject do
10
10
  Ably::Auth.new(client, options)
@@ -65,4 +65,21 @@ describe Ably::Auth do
65
65
  end
66
66
  end
67
67
  end
68
+
69
+ context 'defaults' do
70
+ let(:one_hour) { 60 * 60 }
71
+ let(:all_capabilities) { { "*" => ["*"] } }
72
+
73
+ it 'should default TTL to 1 hour' do
74
+ expect(Ably::Auth::TOKEN_DEFAULTS.fetch(:ttl)).to eql(one_hour)
75
+ end
76
+
77
+ it 'should default capability to all' do
78
+ expect(Ably::Auth::TOKEN_DEFAULTS.fetch(:capability)).to eql(all_capabilities)
79
+ end
80
+
81
+ it 'should only have defaults for :ttl and :capability' do
82
+ expect(Ably::Auth::TOKEN_DEFAULTS.keys).to contain_exactly(:ttl, :capability)
83
+ end
84
+ end
68
85
  end
@@ -5,7 +5,7 @@ describe Ably::Models::PaginatedResource do
5
5
  let(:paginated_resource_class) { Ably::Models::PaginatedResource }
6
6
  let(:headers) { Hash.new }
7
7
  let(:client) do
8
- instance_double('Ably::Rest::Client', logger: true).tap do |client|
8
+ instance_double('Ably::Rest::Client', logger: Ably::Models::NilLogger.new).tap do |client|
9
9
  allow(client).to receive(:get).and_return(http_response)
10
10
  end
11
11
  end
@@ -27,57 +27,54 @@ describe Ably::Models::PaginatedResource do
27
27
  let(:first_paged_request) { paginated_resource_class.new(http_response, full_url, client, paginated_resource_options) }
28
28
  subject { first_paged_request }
29
29
 
30
- it 'returns correct length from body' do
31
- expect(subject.length).to eql(body.length)
32
- end
33
-
34
- it 'supports alias methods for length' do
35
- expect(subject.count).to eql(subject.length)
36
- expect(subject.size).to eql(subject.length)
37
- end
38
-
39
- it 'is Enumerable' do
40
- expect(subject).to be_kind_of(Enumerable)
41
- end
30
+ context '#items' do
31
+ it 'returns correct length from body' do
32
+ expect(subject.items.length).to eql(body.length)
33
+ end
42
34
 
43
- it 'is iterable' do
44
- expect(subject.map { |d| d }).to eql(body)
45
- end
35
+ it 'is Enumerable' do
36
+ expect(subject.items).to be_kind_of(Enumerable)
37
+ end
46
38
 
47
- context '#each' do
48
- it 'returns an enumerator' do
49
- expect(subject.each).to be_a(Enumerator)
39
+ it 'is iterable' do
40
+ expect(subject.items.map { |d| d }).to eql(body)
50
41
  end
51
42
 
52
- it 'yields each item' do
53
- items = []
54
- subject.each do |item|
55
- items << item
43
+ context '#each' do
44
+ it 'returns an enumerator' do
45
+ expect(subject.items.each).to be_a(Enumerator)
46
+ end
47
+
48
+ it 'yields each item' do
49
+ items = []
50
+ subject.items.each do |item|
51
+ items << item
52
+ end
53
+ expect(items).to eq(body)
56
54
  end
57
- expect(items).to eq(body)
58
55
  end
59
- end
60
56
 
61
- it 'provides [] accessor method' do
62
- expect(subject[0][:id]).to eql(body[0][:id])
63
- expect(subject[1][:id]).to eql(body[1][:id])
64
- expect(subject[2]).to be_nil
65
- end
57
+ it 'provides [] accessor method' do
58
+ expect(subject.items[0][:id]).to eql(body[0][:id])
59
+ expect(subject.items[1][:id]).to eql(body[1][:id])
60
+ expect(subject.items[2]).to be_nil
61
+ end
66
62
 
67
- specify '#first gets the first item in page' do
68
- expect(subject.first[:id]).to eql(body[0][:id])
69
- end
63
+ specify '#first gets the first item in page' do
64
+ expect(subject.items.first[:id]).to eql(body[0][:id])
65
+ end
70
66
 
71
- specify '#last gets the last item in page' do
72
- expect(subject.last[:id]).to eql(body[1][:id])
73
- end
67
+ specify '#last gets the last item in page' do
68
+ expect(subject.items.last[:id]).to eql(body[1][:id])
69
+ end
74
70
 
75
- context 'with coercion', :api_private do
76
- let(:paginated_resource_options) { { coerce_into: 'OpenStruct' } }
71
+ context 'with coercion', :api_private do
72
+ let(:paginated_resource_options) { { coerce_into: 'OpenStruct' } }
77
73
 
78
- it 'returns coerced objects' do
79
- expect(subject.first).to be_a(OpenStruct)
80
- expect(subject.first.id).to eql(body.first[:id])
74
+ it 'returns coerced objects' do
75
+ expect(subject.items.first).to be_a(OpenStruct)
76
+ expect(subject.items.first.id).to eql(body.first[:id])
77
+ end
81
78
  end
82
79
  end
83
80
 
@@ -90,7 +87,7 @@ describe Ably::Models::PaginatedResource do
90
87
  }
91
88
  end
92
89
  let(:paged_client) do
93
- instance_double('Ably::Rest::Client', logger: true).tap do |client|
90
+ instance_double('Ably::Rest::Client', logger: Ably::Models::NilLogger.new).tap do |client|
94
91
  allow(client).to receive(:get).and_return(http_response_page2)
95
92
  end
96
93
  end
@@ -116,15 +113,15 @@ describe Ably::Models::PaginatedResource do
116
113
  end
117
114
 
118
115
  it 'calls the block for each resource after retrieving the resources' do
119
- expect(subject[0][:added_attribute_from_block]).to eql("id:#{body[0][:id]}")
116
+ expect(subject.items[0][:added_attribute_from_block]).to eql("id:#{body[0][:id]}")
120
117
  end
121
118
 
122
119
  it 'calls the block for each resource on second page after retrieving the resources' do
123
- page_1_first_id = subject[0][:id]
124
- next_page = subject.next_page
120
+ page_1_first_id = subject.items[0][:id]
121
+ next_page = subject.next
125
122
 
126
- expect(next_page[0][:added_attribute_from_block]).to eql("id:#{body_page2[0][:id]}")
127
- expect(next_page[0][:id]).to_not eql(page_1_first_id)
123
+ expect(next_page.items[0][:added_attribute_from_block]).to eql("id:#{body_page2[0][:id]}")
124
+ expect(next_page.items[0][:id]).to_not eql(page_1_first_id)
128
125
  end
129
126
  end
130
127
 
@@ -136,17 +133,17 @@ describe Ably::Models::PaginatedResource do
136
133
  paginated_resource_class.new(http_response, full_url, paged_client, async_blocking_operations: true)
137
134
  end
138
135
 
139
- context '#next_page' do
136
+ context '#next' do
140
137
  it 'returns a SafeDeferrable that catches exceptions in callbacks and logs them' do
141
138
  run_reactor do
142
- expect(subject.next_page).to be_a(Ably::Util::SafeDeferrable)
139
+ expect(subject.next).to be_a(Ably::Util::SafeDeferrable)
143
140
  stop_reactor
144
141
  end
145
142
  end
146
143
 
147
144
  it 'allows a success callback block to be added' do
148
145
  run_reactor do
149
- subject.next_page do |paginated_resource|
146
+ subject.next do |paginated_resource|
150
147
  expect(paginated_resource).to be_a(Ably::Models::PaginatedResource)
151
148
  stop_reactor
152
149
  end
@@ -154,11 +151,11 @@ describe Ably::Models::PaginatedResource do
154
151
  end
155
152
  end
156
153
 
157
- context '#first_page' do
154
+ context '#first' do
158
155
  it 'calls the errback callback when first page headers are missing' do
159
156
  run_reactor do
160
- subject.next_page do |paginated_resource|
161
- deferrable = subject.first_page
157
+ subject.next do |paginated_resource|
158
+ deferrable = subject.first
162
159
  deferrable.errback do |error|
163
160
  expect(error).to be_a(Ably::Exceptions::InvalidPageError)
164
161
  stop_reactor
@@ -173,23 +170,27 @@ describe Ably::Models::PaginatedResource do
173
170
 
174
171
  context 'with non paged http response' do
175
172
  it 'is the first page' do
176
- expect(subject).to be_first_page
173
+ expect(subject).to be_first
177
174
  end
178
175
 
179
176
  it 'is the last page' do
180
- expect(subject).to be_last_page
177
+ expect(subject).to be_last
178
+ end
179
+
180
+ it 'does not have next page' do
181
+ expect(subject).to_not have_next
181
182
  end
182
183
 
183
184
  it 'does not support pagination' do
184
185
  expect(subject.supports_pagination?).to_not eql(true)
185
186
  end
186
187
 
187
- it 'raises an exception when accessing next page' do
188
- expect { subject.next_page }.to raise_error Ably::Exceptions::InvalidPageError, /Paging header link next/
188
+ it 'returns nil when accessing next page' do
189
+ expect(subject.next).to be_nil
189
190
  end
190
191
 
191
- it 'raises an exception when accessing first page' do
192
- expect { subject.first_page }.to raise_error Ably::Exceptions::InvalidPageError, /Paging header link first/
192
+ it 'returns nil when accessing first page' do
193
+ expect(subject.first).to be_nil
193
194
  end
194
195
  end
195
196
 
@@ -207,11 +208,15 @@ describe Ably::Models::PaginatedResource do
207
208
  end
208
209
 
209
210
  it 'is the first page' do
210
- expect(subject).to be_first_page
211
+ expect(subject).to be_first
212
+ end
213
+
214
+ it 'has next page' do
215
+ expect(subject).to have_next
211
216
  end
212
217
 
213
218
  it 'is not the last page' do
214
- expect(subject).to_not be_last_page
219
+ expect(subject).to_not be_last
215
220
  end
216
221
 
217
222
  it 'supports pagination' do
@@ -236,7 +241,7 @@ describe Ably::Models::PaginatedResource do
236
241
  headers: next_headers
237
242
  })
238
243
  end
239
- let(:subject) { first_paged_request.next_page }
244
+ let(:subject) { first_paged_request.next }
240
245
 
241
246
  before do
242
247
  expect(client).to receive(:get).with("#{base_url}/history?index=1").and_return(next_http_response).once
@@ -247,39 +252,43 @@ describe Ably::Models::PaginatedResource do
247
252
  end
248
253
 
249
254
  it 'retrieves the next page of results' do
250
- expect(subject.length).to eql(next_body.length)
251
- expect(subject[0][:id]).to eql(next_body[0][:id])
255
+ expect(subject.items.length).to eql(next_body.length)
256
+ expect(subject.items[0][:id]).to eql(next_body[0][:id])
252
257
  end
253
258
 
254
259
  it 'is not the first page' do
255
- expect(subject).to_not be_first_page
260
+ expect(subject).to_not be_first
261
+ end
262
+
263
+ it 'does not have a next page' do
264
+ expect(subject).to_not have_next
256
265
  end
257
266
 
258
267
  it 'is the last page' do
259
- expect(subject).to be_last_page
268
+ expect(subject).to be_last
260
269
  end
261
270
 
262
- it 'raises an exception if trying to access the last page when it is the last page' do
263
- expect(subject).to be_last_page
264
- expect { subject.next_page }.to raise_error Ably::Exceptions::InvalidPageError, /There are no more pages/
271
+ it 'returns nil when trying to access the last page when it is the last page' do
272
+ expect(subject).to be_last
273
+ expect(subject.next).to be_nil
265
274
  end
266
275
 
267
276
  context 'and then first page' do
268
277
  before do
269
278
  expect(client).to receive(:get).with("#{base_url}/history?index=0").and_return(http_response).once
270
279
  end
271
- subject { first_paged_request.next_page.first_page }
280
+ subject { first_paged_request.next.first }
272
281
 
273
282
  it 'returns a PaginatedResource' do
274
283
  expect(subject).to be_a(paginated_resource_class)
275
284
  end
276
285
 
277
286
  it 'retrieves the first page of results' do
278
- expect(subject.length).to eql(body.length)
287
+ expect(subject.items.length).to eql(body.length)
279
288
  end
280
289
 
281
290
  it 'is the first page' do
282
- expect(subject).to be_first_page
291
+ expect(subject).to be_first
283
292
  end
284
293
  end
285
294
  end
@@ -0,0 +1,289 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+ require 'shared/model_behaviour'
4
+
5
+ describe Ably::Models::Stats do
6
+ include Ably::Modules::Conversions
7
+
8
+ subject { Ably::Models::Stats }
9
+
10
+ %w(all persisted).each do |attribute|
11
+ context "##{attribute} stats" do
12
+ let(:data) do
13
+ { attribute.to_sym => { messages: { count: 5 }, all: { data: 10 } } }
14
+ end
15
+ subject { Ably::Models::Stats.new(data.merge(interval_id: '2004-02')).public_send(attribute) }
16
+
17
+ it 'returns a MessageTypes object' do
18
+ expect(subject).to be_a(Ably::Models::Stats::MessageTypes)
19
+ end
20
+
21
+ it 'returns value for message counts' do
22
+ expect(subject.messages.count).to eql(5)
23
+ end
24
+
25
+ it 'returns value for all data transferred' do
26
+ expect(subject.all.data).to eql(10)
27
+ end
28
+
29
+ it 'returns zero for empty values' do
30
+ expect(subject.presence.count).to eql(0)
31
+ end
32
+
33
+ it 'raises an exception for unknown attributes' do
34
+ expect { subject.unknown }.to raise_error NoMethodError
35
+ end
36
+
37
+ %w(all presence messages).each do |type|
38
+ context "##{type}" do
39
+ it 'is a MessageCount object' do
40
+ expect(subject.public_send(type)).to be_a(Ably::Models::Stats::MessageCount)
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ %w(inbound outbound).each do |direction|
48
+ context "##{direction} stats" do
49
+ let(:data) do
50
+ {
51
+ direction.to_sym => {
52
+ realtime: { messages: { count: 5 }, presence: { data: 10 } },
53
+ all: { messages: { count: 25 }, presence: { data: 210 } }
54
+ }
55
+ }
56
+ end
57
+ subject { Ably::Models::Stats.new(data.merge(interval_id: '2004-02')).public_send(direction) }
58
+
59
+ it 'returns a MessageTraffic object' do
60
+ expect(subject).to be_a(Ably::Models::Stats::MessageTraffic)
61
+ end
62
+
63
+ it 'returns value for realtime message counts' do
64
+ expect(subject.realtime.messages.count).to eql(5)
65
+ end
66
+
67
+ it 'returns value for all presence data' do
68
+ expect(subject.all.presence.data).to eql(210)
69
+ end
70
+
71
+ it 'raises an exception for unknown attributes' do
72
+ expect { subject.unknown }.to raise_error NoMethodError
73
+ end
74
+
75
+ %w(realtime rest webhook all).each do |type|
76
+ context "##{type}" do
77
+ it 'is a MessageTypes object' do
78
+ expect(subject.public_send(type)).to be_a(Ably::Models::Stats::MessageTypes)
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
84
+
85
+ context '#connections stats' do
86
+ let(:data) do
87
+ { connections: { tls: { opened: 5 }, all: { peak: 10 } } }
88
+ end
89
+ subject { Ably::Models::Stats.new(data.merge(interval_id: '2004-02')).connections }
90
+
91
+ it 'returns a ConnectionTypes object' do
92
+ expect(subject).to be_a(Ably::Models::Stats::ConnectionTypes)
93
+ end
94
+
95
+ it 'returns value for tls opened counts' do
96
+ expect(subject.tls.opened).to eql(5)
97
+ end
98
+
99
+ it 'returns value for all peak connections' do
100
+ expect(subject.all.peak).to eql(10)
101
+ end
102
+
103
+ it 'returns zero for empty values' do
104
+ expect(subject.all.refused).to eql(0)
105
+ end
106
+
107
+ it 'raises an exception for unknown attributes' do
108
+ expect { subject.unknown }.to raise_error NoMethodError
109
+ end
110
+
111
+ %w(tls plain all).each do |type|
112
+ context "##{type}" do
113
+ it 'is a ResourceCount object' do
114
+ expect(subject.public_send(type)).to be_a(Ably::Models::Stats::ResourceCount)
115
+ end
116
+ end
117
+ end
118
+ end
119
+
120
+ context '#channels stats' do
121
+ let(:data) do
122
+ { channels: { opened: 5, peak: 10 } }
123
+ end
124
+ subject { Ably::Models::Stats.new(data.merge(interval_id: '2004-02')).channels }
125
+
126
+ it 'returns a ResourceCount object' do
127
+ expect(subject).to be_a(Ably::Models::Stats::ResourceCount)
128
+ end
129
+
130
+ it 'returns value for opened counts' do
131
+ expect(subject.opened).to eql(5)
132
+ end
133
+
134
+ it 'returns value for peak channels' do
135
+ expect(subject.peak).to eql(10)
136
+ end
137
+
138
+ it 'returns zero for empty values' do
139
+ expect(subject.refused).to eql(0)
140
+ end
141
+
142
+ it 'raises an exception for unknown attributes' do
143
+ expect { subject.unknown }.to raise_error NoMethodError
144
+ end
145
+
146
+ %w(opened peak mean min refused).each do |type|
147
+ context "##{type}" do
148
+ it 'is a Integer object' do
149
+ expect(subject.public_send(type)).to be_a(Integer)
150
+ end
151
+ end
152
+ end
153
+ end
154
+
155
+ %w(api_requests token_requests).each do |request_type|
156
+ context "##{request_type} stats" do
157
+ let(:data) do
158
+ {
159
+ request_type.to_sym => { succeeded: 5, failed: 10 }
160
+ }
161
+ end
162
+ subject { Ably::Models::Stats.new(data.merge(interval_id: '2004-02')).public_send(request_type) }
163
+
164
+ it 'returns a RequestCount object' do
165
+ expect(subject).to be_a(Ably::Models::Stats::RequestCount)
166
+ end
167
+
168
+ it 'returns value for succeeded' do
169
+ expect(subject.succeeded).to eql(5)
170
+ end
171
+
172
+ it 'returns value for failed' do
173
+ expect(subject.failed).to eql(10)
174
+ end
175
+
176
+ it 'raises an exception for unknown attributes' do
177
+ expect { subject.unknown }.to raise_error NoMethodError
178
+ end
179
+
180
+ %w(succeeded failed refused).each do |type|
181
+ context "##{type}" do
182
+ it 'is a Integer object' do
183
+ expect(subject.public_send(type)).to be_a(Integer)
184
+ end
185
+ end
186
+ end
187
+ end
188
+ end
189
+
190
+ describe '#interval_granularity' do
191
+ subject { Ably::Models::Stats.new(interval_id: '2004-02') }
192
+
193
+ it 'returns the granularity of the interval_id' do
194
+ expect(subject.interval_granularity).to eq(:month)
195
+ end
196
+ end
197
+
198
+ describe '#interval_time' do
199
+ subject { Ably::Models::Stats.new(interval_id: '2004-02-01:05:06') }
200
+
201
+ it 'returns a Time object representing the start of the interval' do
202
+ expect(subject.interval_time.to_i).to eql(Time.new(2004, 02, 01, 05, 06, 00, '+00:00').to_i)
203
+ end
204
+ end
205
+
206
+ context 'class methods' do
207
+ describe '#to_interval_id' do
208
+ context 'when time zone of time argument is UTC' do
209
+ it 'converts time 2014-02-03:05:06 with granularity :month into 2014-02' do
210
+ expect(subject.to_interval_id(Time.new(2014, 2, 1, 0, 0, 0, '+00:00'), :month)).to eql('2014-02')
211
+ end
212
+
213
+ it 'converts time 2014-02-03:05:06 with granularity :day into 2014-02-03' do
214
+ expect(subject.to_interval_id(Time.new(2014, 2, 3, 0, 0, 0, '+00:00'), :day)).to eql('2014-02-03')
215
+ end
216
+
217
+ it 'converts time 2014-02-03:05:06 with granularity :hour into 2014-02-03:05' do
218
+ expect(subject.to_interval_id(Time.new(2014, 2, 3, 5, 0, 0, '+00:00'), :hour)).to eql('2014-02-03:05')
219
+ end
220
+
221
+ it 'converts time 2014-02-03:05:06 with granularity :minute into 2014-02-03:05:06' do
222
+ expect(subject.to_interval_id(Time.new(2014, 2, 3, 5, 6, 0, '+00:00'), :minute)).to eql('2014-02-03:05:06')
223
+ end
224
+
225
+ it 'fails with invalid granularity' do
226
+ expect { subject.to_interval_id(Time.now, :invalid) }.to raise_error KeyError
227
+ end
228
+
229
+ it 'fails with invalid time' do
230
+ expect { subject.to_interval_id(nil, :month) }.to raise_error ArgumentError
231
+ end
232
+ end
233
+
234
+ context 'when time zone of time argument is +02:00' do
235
+ it 'converts time 2014-02-03:06 with granularity :hour into 2014-02-03:04 at UTC +00:00' do
236
+ expect(subject.to_interval_id(Time.new(2014, 2, 3, 6, 0, 0, '+02:00'), :hour)).to eql('2014-02-03:04')
237
+ end
238
+ end
239
+ end
240
+
241
+ describe '#from_interval_id' do
242
+ it 'converts a month interval_id 2014-02 into a Time object in UTC 0' do
243
+ expect(subject.from_interval_id('2014-02')).to eql(Time.gm(2014, 2))
244
+ expect(subject.from_interval_id('2014-02').utc_offset).to eql(0)
245
+ end
246
+
247
+ it 'converts a day interval_id 2014-02-03 into a Time object in UTC 0' do
248
+ expect(subject.from_interval_id('2014-02-03')).to eql(Time.gm(2014, 2, 3))
249
+ expect(subject.from_interval_id('2014-02-03').utc_offset).to eql(0)
250
+ end
251
+
252
+ it 'converts an hour interval_id 2014-02-03:05 into a Time object in UTC 0' do
253
+ expect(subject.from_interval_id('2014-02-03:05')).to eql(Time.gm(2014, 2, 3, 5))
254
+ expect(subject.from_interval_id('2014-02-03:05').utc_offset).to eql(0)
255
+ end
256
+
257
+ it 'converts a minute interval_id 2014-02-03:05:06 into a Time object in UTC 0' do
258
+ expect(subject.from_interval_id('2014-02-03:05:06')).to eql(Time.gm(2014, 2, 3, 5, 6))
259
+ expect(subject.from_interval_id('2014-02-03:05:06').utc_offset).to eql(0)
260
+ end
261
+
262
+ it 'fails with an invalid interval_id 14-20' do
263
+ expect { subject.from_interval_id('14-20') }.to raise_error ArgumentError
264
+ end
265
+ end
266
+
267
+ describe '#granularity_from_interval_id' do
268
+ it 'returns a :month interval_id for 2014-02' do
269
+ expect(subject.granularity_from_interval_id('2014-02')).to eq(:month)
270
+ end
271
+
272
+ it 'returns a :day interval_id for 2014-02-03' do
273
+ expect(subject.granularity_from_interval_id('2014-02-03')).to eq(:day)
274
+ end
275
+
276
+ it 'returns a :hour interval_id for 2014-02-03:05' do
277
+ expect(subject.granularity_from_interval_id('2014-02-03:05')).to eq(:hour)
278
+ end
279
+
280
+ it 'returns a :minute interval_id for 2014-02-03:05:06' do
281
+ expect(subject.granularity_from_interval_id('2014-02-03:05:06')).to eq(:minute)
282
+ end
283
+
284
+ it 'fails with an invalid interval_id 14-20' do
285
+ expect { subject.granularity_from_interval_id('14-20') }.to raise_error ArgumentError
286
+ end
287
+ end
288
+ end
289
+ end