ably-rest 0.7.5 → 0.8.1

Sign up to get free protection for your applications and to get access to all the features.
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