ably 0.7.6 → 0.8.0
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.
- checksums.yaml +4 -4
- data/README.md +11 -7
- data/SPEC.md +310 -269
- data/lib/ably/auth.rb +177 -127
- data/lib/ably/models/presence_message.rb +1 -1
- data/lib/ably/models/protocol_message.rb +1 -2
- data/lib/ably/models/token_details.rb +101 -0
- data/lib/ably/models/token_request.rb +108 -0
- data/lib/ably/modules/http_helpers.rb +1 -1
- data/lib/ably/realtime.rb +2 -6
- data/lib/ably/realtime/channel.rb +14 -8
- data/lib/ably/realtime/client.rb +2 -6
- data/lib/ably/realtime/connection.rb +4 -2
- data/lib/ably/rest.rb +2 -6
- data/lib/ably/rest/channel.rb +10 -6
- data/lib/ably/rest/client.rb +15 -16
- data/lib/ably/rest/presence.rb +12 -10
- data/lib/ably/version.rb +1 -1
- data/spec/acceptance/realtime/client_spec.rb +15 -15
- data/spec/acceptance/realtime/connection_failures_spec.rb +3 -3
- data/spec/acceptance/realtime/connection_spec.rb +9 -9
- data/spec/acceptance/rest/auth_spec.rb +248 -172
- data/spec/acceptance/rest/base_spec.rb +8 -6
- data/spec/acceptance/rest/channel_spec.rb +9 -2
- data/spec/acceptance/rest/client_spec.rb +21 -21
- data/spec/acceptance/rest/presence_spec.rb +12 -5
- data/spec/acceptance/rest/stats_spec.rb +4 -4
- data/spec/rspec_config.rb +3 -2
- data/spec/shared/client_initializer_behaviour.rb +21 -24
- data/spec/support/api_helper.rb +3 -3
- data/spec/support/test_app.rb +9 -9
- data/spec/unit/auth_spec.rb +17 -0
- data/spec/unit/models/token_details_spec.rb +111 -0
- data/spec/unit/models/token_request_spec.rb +110 -0
- data/spec/unit/rest/client_spec.rb +1 -1
- metadata +8 -5
- data/lib/ably/models/token.rb +0 -74
- data/spec/unit/models/token_spec.rb +0 -86
@@ -109,18 +109,18 @@ describe Ably::Rest do
|
|
109
109
|
end
|
110
110
|
|
111
111
|
describe 'token authentication failures', :webmock do
|
112
|
-
let(:token_1) { {
|
113
|
-
let(:token_2) { {
|
114
|
-
let(:channel) {
|
112
|
+
let(:token_1) { { token: random_str } }
|
113
|
+
let(:token_2) { { token: random_str } }
|
114
|
+
let(:channel) { random_str }
|
115
115
|
|
116
116
|
before do
|
117
117
|
@token_requests = 0
|
118
118
|
@publish_attempts = 0
|
119
119
|
|
120
|
-
stub_request(:post, "#{client.endpoint}/keys/#{
|
120
|
+
stub_request(:post, "#{client.endpoint}/keys/#{key_name}/requestToken").to_return do
|
121
121
|
@token_requests += 1
|
122
122
|
{
|
123
|
-
:body =>
|
123
|
+
:body => public_send("token_#{@token_requests}").merge(expires: (Time.now.to_i + 60) * 1000).to_json,
|
124
124
|
:headers => { 'Content-Type' => 'application/json' }
|
125
125
|
}
|
126
126
|
end
|
@@ -143,7 +143,9 @@ describe Ably::Rest do
|
|
143
143
|
it 'should automatically reissue a token' do
|
144
144
|
client.channel(channel).publish('evt', 'msg')
|
145
145
|
expect(@publish_attempts).to eql(1)
|
146
|
+
expect(@token_requests).to eql(1)
|
146
147
|
|
148
|
+
# Triggers an authentication 401 failure which should automatically request a new token
|
147
149
|
client.channel(channel).publish('evt', 'msg')
|
148
150
|
expect(@publish_attempts).to eql(3)
|
149
151
|
expect(@token_requests).to eql(2)
|
@@ -151,7 +153,7 @@ describe Ably::Rest do
|
|
151
153
|
end
|
152
154
|
|
153
155
|
context 'when NOT auth#token_renewable?' do
|
154
|
-
let(:client) { Ably::Rest::Client.new(
|
156
|
+
let(:client) { Ably::Rest::Client.new(token: 'token ID cannot be used to create a new token', environment: environment, protocol: protocol) }
|
155
157
|
|
156
158
|
it 'should raise an InvalidToken exception' do
|
157
159
|
client.channel(channel).publish('evt', 'msg')
|
@@ -93,15 +93,22 @@ describe Ably::Rest::Channel do
|
|
93
93
|
let(:channel) { client.channel(channel_name) }
|
94
94
|
let(:endpoint) do
|
95
95
|
client.endpoint.tap do |client_end_point|
|
96
|
-
client_end_point.user =
|
96
|
+
client_end_point.user = key_name
|
97
97
|
client_end_point.password = key_secret
|
98
98
|
end
|
99
99
|
end
|
100
|
+
let(:default_options) do
|
101
|
+
{
|
102
|
+
direction: :backwards,
|
103
|
+
limit: 100
|
104
|
+
}
|
105
|
+
end
|
100
106
|
|
101
107
|
[:start, :end].each do |option|
|
102
108
|
describe ":#{option}", :webmock do
|
103
109
|
let!(:history_stub) {
|
104
|
-
|
110
|
+
query_params = default_options.merge(option => milliseconds).map { |k, v| "#{k}=#{v}" }.join('&')
|
111
|
+
stub_request(:get, "#{endpoint}/channels/#{CGI.escape(channel_name)}/messages?#{query_params}").
|
105
112
|
to_return(:body => '{}', :headers => { 'Content-Type' => 'application/json' })
|
106
113
|
}
|
107
114
|
|
@@ -12,14 +12,14 @@ describe Ably::Rest::Client do
|
|
12
12
|
|
13
13
|
context '#initialize' do
|
14
14
|
let(:client_id) { random_str }
|
15
|
-
let(:token_request) { client.auth.create_token_request(
|
15
|
+
let(:token_request) { client.auth.create_token_request(key_name: key_name, key_secret: key_secret, client_id: client_id) }
|
16
16
|
|
17
|
-
context 'with
|
18
|
-
let(:client) { Ably::Rest::Client.new(client_options
|
17
|
+
context 'with a :auth_callback Proc' do
|
18
|
+
let(:client) { Ably::Rest::Client.new(client_options.merge(auth_callback: Proc.new { token_request })) }
|
19
19
|
|
20
|
-
it 'calls the
|
21
|
-
expect { client.channel('channel_name').publish('event', 'message') }.to change { client.auth.
|
22
|
-
expect(client.auth.
|
20
|
+
it 'calls the auth Proc to get a new token' do
|
21
|
+
expect { client.channel('channel_name').publish('event', 'message') }.to change { client.auth.current_token_details }
|
22
|
+
expect(client.auth.current_token_details.client_id).to eql(client_id)
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
@@ -32,19 +32,19 @@ describe Ably::Rest::Client do
|
|
32
32
|
end
|
33
33
|
|
34
34
|
it 'sends an HTTP request to the provided URL to get a new token' do
|
35
|
-
expect { client.channel('channel_name').publish('event', 'message') }.to change { client.auth.
|
36
|
-
expect(client.auth.
|
35
|
+
expect { client.channel('channel_name').publish('event', 'message') }.to change { client.auth.current_token_details }
|
36
|
+
expect(client.auth.current_token_details.client_id).to eql(client_id)
|
37
37
|
end
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
41
41
|
context 'using tokens' do
|
42
42
|
let(:client) do
|
43
|
-
Ably::Rest::Client.new(client_options
|
43
|
+
Ably::Rest::Client.new(client_options.merge(auth_callback: Proc.new do
|
44
44
|
@request_index ||= 0
|
45
45
|
@request_index += 1
|
46
46
|
send("token_request_#{@request_index > 2 ? 'next' : @request_index}")
|
47
|
-
end
|
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)) }
|
@@ -53,30 +53,30 @@ describe Ably::Rest::Client do
|
|
53
53
|
let(:token_request_next) { client.auth.create_token_request(token_request_options.merge(client_id: random_str)) }
|
54
54
|
|
55
55
|
context 'when expired' do
|
56
|
-
let(:token_request_options) { {
|
56
|
+
let(:token_request_options) { { key_name: key_name, key_secret: key_secret, ttl: Ably::Models::TokenDetails::TOKEN_EXPIRY_BUFFER } }
|
57
57
|
|
58
58
|
it 'creates a new token automatically when the old token expires' do
|
59
|
-
expect { client.channel('channel_name').publish('event', 'message') }.to change { client.auth.
|
60
|
-
expect(client.auth.
|
59
|
+
expect { client.channel('channel_name').publish('event', 'message') }.to change { client.auth.current_token_details }
|
60
|
+
expect(client.auth.current_token_details.client_id).to eql(token_request_1.client_id)
|
61
61
|
|
62
62
|
sleep 1
|
63
63
|
|
64
|
-
expect { client.channel('channel_name').publish('event', 'message') }.to change { client.auth.
|
65
|
-
expect(client.auth.
|
64
|
+
expect { client.channel('channel_name').publish('event', 'message') }.to change { client.auth.current_token_details }
|
65
|
+
expect(client.auth.current_token_details.client_id).to eql(token_request_2.client_id)
|
66
66
|
end
|
67
67
|
end
|
68
68
|
|
69
69
|
context 'when token has not expired' do
|
70
|
-
let(:token_request_options) { {
|
70
|
+
let(:token_request_options) { { key_name: key_name, key_secret: key_secret, ttl: 3600 } }
|
71
71
|
|
72
72
|
it 'reuses the existing token for every request' do
|
73
|
-
expect { client.channel('channel_name').publish('event', 'message') }.to change { client.auth.
|
74
|
-
expect(client.auth.
|
73
|
+
expect { client.channel('channel_name').publish('event', 'message') }.to change { client.auth.current_token_details }
|
74
|
+
expect(client.auth.current_token_details.client_id).to eql(token_request_1.client_id)
|
75
75
|
|
76
76
|
sleep 1
|
77
77
|
|
78
|
-
expect { client.channel('channel_name').publish('event', 'message') }.to_not change { client.auth.
|
79
|
-
expect(client.auth.
|
78
|
+
expect { client.channel('channel_name').publish('event', 'message') }.to_not change { client.auth.current_token_details }
|
79
|
+
expect(client.auth.current_token_details.client_id).to eql(token_request_1.client_id)
|
80
80
|
end
|
81
81
|
end
|
82
82
|
end
|
@@ -245,7 +245,7 @@ describe Ably::Rest::Client do
|
|
245
245
|
end
|
246
246
|
|
247
247
|
context 'that times out', :webmock do
|
248
|
-
let(:path) { '/keys/app_id.
|
248
|
+
let(:path) { '/keys/app_id.key_name/requestToken' }
|
249
249
|
let!(:custom_host_request_stub) do
|
250
250
|
stub_request(:post, "https://#{custom_host}#{path}").to_return do
|
251
251
|
raise Faraday::TimeoutError.new('timeout error message')
|
@@ -124,11 +124,18 @@ describe Ably::Rest::Presence do
|
|
124
124
|
let(:client) do
|
125
125
|
Ably::Rest::Client.new(key: "#{user}:#{secret}")
|
126
126
|
end
|
127
|
+
let(:default_options) do
|
128
|
+
{
|
129
|
+
direction: :backwards,
|
130
|
+
limit: 100
|
131
|
+
}
|
132
|
+
end
|
127
133
|
|
128
134
|
[:start, :end].each do |option|
|
129
135
|
describe ":#{option}", :webmock do
|
130
136
|
let!(:history_stub) {
|
131
|
-
|
137
|
+
query_params = default_options.merge(option => milliseconds).map { |k, v| "#{k}=#{v}" }.join('&')
|
138
|
+
stub_request(:get, "#{endpoint}/channels/#{CGI.escape(channel_name)}/presence/history?#{query_params}").
|
132
139
|
to_return(:body => '{}', :headers => { 'Content-Type' => 'application/json' })
|
133
140
|
}
|
134
141
|
|
@@ -225,7 +232,7 @@ describe Ably::Rest::Presence do
|
|
225
232
|
|
226
233
|
context '#get' do
|
227
234
|
let!(:get_stub) {
|
228
|
-
stub_request(:get, "#{endpoint}/channels/#{CGI.escape(channel_name)}/presence").
|
235
|
+
stub_request(:get, "#{endpoint}/channels/#{CGI.escape(channel_name)}/presence?limit=100").
|
229
236
|
to_return(:body => serialized_encoded_message, :headers => { 'Content-Type' => content_type })
|
230
237
|
}
|
231
238
|
|
@@ -242,7 +249,7 @@ describe Ably::Rest::Presence do
|
|
242
249
|
|
243
250
|
context '#history' do
|
244
251
|
let!(:history_stub) {
|
245
|
-
stub_request(:get, "#{endpoint}/channels/#{CGI.escape(channel_name)}/presence/history").
|
252
|
+
stub_request(:get, "#{endpoint}/channels/#{CGI.escape(channel_name)}/presence/history?direction=backwards&limit=100").
|
246
253
|
to_return(:body => serialized_encoded_message, :headers => { 'Content-Type' => content_type })
|
247
254
|
}
|
248
255
|
|
@@ -272,7 +279,7 @@ describe Ably::Rest::Presence do
|
|
272
279
|
context '#get' do
|
273
280
|
let(:client_options) { default_options.merge(log_level: :fatal) }
|
274
281
|
let!(:get_stub) {
|
275
|
-
stub_request(:get, "#{endpoint}/channels/#{CGI.escape(channel_name)}/presence").
|
282
|
+
stub_request(:get, "#{endpoint}/channels/#{CGI.escape(channel_name)}/presence?limit=100").
|
276
283
|
to_return(:body => serialized_encoded_message_with_invalid_encoding, :headers => { 'Content-Type' => content_type })
|
277
284
|
}
|
278
285
|
let(:presence_message) { presence.get.items.first }
|
@@ -296,7 +303,7 @@ describe Ably::Rest::Presence do
|
|
296
303
|
context '#history' do
|
297
304
|
let(:client_options) { default_options.merge(log_level: :fatal) }
|
298
305
|
let!(:history_stub) {
|
299
|
-
stub_request(:get, "#{endpoint}/channels/#{CGI.escape(channel_name)}/presence/history").
|
306
|
+
stub_request(:get, "#{endpoint}/channels/#{CGI.escape(channel_name)}/presence/history?direction=backwards&limit=100").
|
300
307
|
to_return(:body => serialized_encoded_message_with_invalid_encoding, :headers => { 'Content-Type' => content_type })
|
301
308
|
}
|
302
309
|
let(:presence_message) { presence.history.items.first }
|
@@ -44,7 +44,7 @@ describe Ably::Rest::Client, '#stats' do
|
|
44
44
|
let(:last_inbound_realtime_count) { STATS_FIXTURES.last[:inbound][:realtime][:messages][:count] }
|
45
45
|
|
46
46
|
context 'with :from set to last interval and :limit set to 1' do
|
47
|
-
let(:subject) { client.stats(start: as_since_epoch(LAST_INTERVAL),
|
47
|
+
let(:subject) { client.stats(start: as_since_epoch(LAST_INTERVAL), end: LAST_INTERVAL, unit: :minute, limit: 1) }
|
48
48
|
let(:stat) { subject.items.first }
|
49
49
|
|
50
50
|
it 'retrieves only one stat' do
|
@@ -116,7 +116,7 @@ describe Ably::Rest::Client, '#stats' do
|
|
116
116
|
|
117
117
|
context 'with :start set to first interval, :limit set to 1 and direction :forwards' do
|
118
118
|
let(:first_interval) { LAST_INTERVAL - 120 }
|
119
|
-
let(:subject) { client.stats(start: as_since_epoch(first_interval),
|
119
|
+
let(:subject) { client.stats(start: as_since_epoch(first_interval), end: LAST_INTERVAL, unit: :minute, direction: :forwards, limit: 1) }
|
120
120
|
let(:stat) { subject.items.first }
|
121
121
|
|
122
122
|
it 'returns the first interval stats as stats are provided forwards from :start' do
|
@@ -133,7 +133,7 @@ describe Ably::Rest::Client, '#stats' do
|
|
133
133
|
end
|
134
134
|
|
135
135
|
context 'with :end set to last interval, :limit set to 1 and direction :backwards' do
|
136
|
-
let(:subject) { client.stats(:end => as_since_epoch(LAST_INTERVAL),
|
136
|
+
let(:subject) { client.stats(:end => as_since_epoch(LAST_INTERVAL), end: LAST_INTERVAL, unit: :minute, direction: :backwards, limit: 1) }
|
137
137
|
let(:stat) { subject.items.first }
|
138
138
|
|
139
139
|
it 'returns the 3rd interval stats first as stats are provided backwards from :end' do
|
@@ -151,7 +151,7 @@ describe Ably::Rest::Client, '#stats' do
|
|
151
151
|
|
152
152
|
[:hour, :day, :month].each do |interval|
|
153
153
|
context "by #{interval}" do
|
154
|
-
let(:subject) { client.stats(start: as_since_epoch(LAST_INTERVAL),
|
154
|
+
let(:subject) { client.stats(start: as_since_epoch(LAST_INTERVAL), end: LAST_INTERVAL, unit: interval, direction: 'forwards', limit: 1) }
|
155
155
|
let(:stat) { subject.items.first }
|
156
156
|
let(:aggregate_messages_count) do
|
157
157
|
STATS_FIXTURES.inject(0) do |sum, fixture|
|
data/spec/rspec_config.rb
CHANGED
@@ -32,8 +32,9 @@ RSpec.configure do |config|
|
|
32
32
|
config.before(:example, :webmock) do
|
33
33
|
allow(TestApp).to receive(:instance).and_return(instance_double('TestApp',
|
34
34
|
app_id: 'app_id',
|
35
|
-
|
36
|
-
|
35
|
+
key_name: 'app_id.key_name',
|
36
|
+
key_secret: 'secret',
|
37
|
+
api_key: 'app_id.key_name:secret',
|
37
38
|
environment: 'sandbox'
|
38
39
|
))
|
39
40
|
WebMock.enable!
|
@@ -54,11 +54,11 @@ shared_examples 'a client initializer' do
|
|
54
54
|
end
|
55
55
|
end
|
56
56
|
|
57
|
-
context 'key and
|
58
|
-
let(:client_options) { { key: 'appid.keyuid:keysecret',
|
57
|
+
context 'key and key_name' do
|
58
|
+
let(:client_options) { { key: 'appid.keyuid:keysecret', key_name: 'invalid' } }
|
59
59
|
|
60
60
|
it 'raises an exception' do
|
61
|
-
expect { subject }.to raise_error(ArgumentError, /key and
|
61
|
+
expect { subject }.to raise_error(ArgumentError, /key and key_name or key_secret are mutually exclusive/)
|
62
62
|
end
|
63
63
|
end
|
64
64
|
|
@@ -66,7 +66,7 @@ shared_examples 'a client initializer' do
|
|
66
66
|
let(:client_options) { { key: 'appid.keyuid:keysecret', key_secret: 'invalid' } }
|
67
67
|
|
68
68
|
it 'raises an exception' do
|
69
|
-
expect { subject }.to raise_error(ArgumentError, /key and
|
69
|
+
expect { subject }.to raise_error(ArgumentError, /key and key_name or key_secret are mutually exclusive/)
|
70
70
|
end
|
71
71
|
end
|
72
72
|
|
@@ -89,19 +89,8 @@ shared_examples 'a client initializer' do
|
|
89
89
|
end
|
90
90
|
end
|
91
91
|
|
92
|
-
context '
|
93
|
-
let(:
|
94
|
-
it 'connects to the Ably service' do
|
95
|
-
expect { subject }.to_not raise_error
|
96
|
-
end
|
97
|
-
|
98
|
-
it 'sets the Auth#key' do
|
99
|
-
expect(subject.auth.key).to eql('api_key_id.keyuid:keysecret')
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
context 'key_id and key_secret' do
|
104
|
-
let(:client_options) { { key_id: 'id', key_secret: 'secret' } }
|
92
|
+
context 'key_name and key_secret', api_private: true do
|
93
|
+
let(:client_options) { { key_name: 'id', key_secret: 'secret' } }
|
105
94
|
|
106
95
|
it 'constructs an key' do
|
107
96
|
expect(subject.auth.key).to eql('id:secret')
|
@@ -115,8 +104,8 @@ shared_examples 'a client initializer' do
|
|
115
104
|
expect(subject.auth.key).to eql(client_options)
|
116
105
|
end
|
117
106
|
|
118
|
-
it 'sets the
|
119
|
-
expect(subject.auth.
|
107
|
+
it 'sets the key_name' do
|
108
|
+
expect(subject.auth.key_name).to eql('app.key')
|
120
109
|
end
|
121
110
|
|
122
111
|
it 'sets the key_secret' do
|
@@ -127,16 +116,24 @@ shared_examples 'a client initializer' do
|
|
127
116
|
context 'with a string token key instead of options hash' do
|
128
117
|
let(:client_options) { 'app.kjhkasjhdsakdh127g7g1271' }
|
129
118
|
|
130
|
-
it 'sets the
|
131
|
-
expect(subject.auth.
|
119
|
+
it 'sets the token' do
|
120
|
+
expect(subject.auth.token).to eql(client_options)
|
132
121
|
end
|
133
122
|
end
|
134
123
|
|
135
124
|
context 'with token' do
|
136
|
-
let(:client_options) { {
|
125
|
+
let(:client_options) { { token: 'token' } }
|
126
|
+
|
127
|
+
it 'sets the token' do
|
128
|
+
expect(subject.auth.token).to eql('token')
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
context 'with token_details' do
|
133
|
+
let(:client_options) { { token_details: Ably::Models::TokenDetails.new(token: 'token') } }
|
137
134
|
|
138
|
-
it 'sets the
|
139
|
-
expect(subject.auth.
|
135
|
+
it 'sets the token' do
|
136
|
+
expect(subject.auth.token).to eql('token')
|
140
137
|
end
|
141
138
|
end
|
142
139
|
|
data/spec/support/api_helper.rb
CHANGED
data/spec/support/test_app.rb
CHANGED
@@ -27,31 +27,31 @@ class TestApp
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def app_id
|
30
|
-
@attributes
|
30
|
+
@attributes.fetch('appId')
|
31
31
|
end
|
32
32
|
|
33
33
|
def key
|
34
|
-
@attributes
|
34
|
+
@attributes.fetch('keys').first
|
35
35
|
end
|
36
36
|
|
37
37
|
def restricted_key
|
38
|
-
@attributes
|
38
|
+
@attributes.fetch('keys')[1]
|
39
39
|
end
|
40
40
|
|
41
|
-
def
|
42
|
-
|
41
|
+
def key_name
|
42
|
+
key.fetch('keyName')
|
43
43
|
end
|
44
44
|
|
45
|
-
def
|
46
|
-
key
|
45
|
+
def key_secret
|
46
|
+
key.fetch('keySecret')
|
47
47
|
end
|
48
48
|
|
49
49
|
def api_key
|
50
|
-
|
50
|
+
key.fetch('keyStr')
|
51
51
|
end
|
52
52
|
|
53
53
|
def restricted_api_key
|
54
|
-
|
54
|
+
restricted_key.fetch('keyStr')
|
55
55
|
end
|
56
56
|
|
57
57
|
def delete
|
data/spec/unit/auth_spec.rb
CHANGED
@@ -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
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'shared/model_behaviour'
|
3
|
+
|
4
|
+
describe Ably::Models::TokenDetails do
|
5
|
+
include Ably::Modules::Conversions
|
6
|
+
|
7
|
+
subject { Ably::Models::TokenDetails }
|
8
|
+
|
9
|
+
it_behaves_like 'a model', with_simple_attributes: %w(token key_name client_id) do
|
10
|
+
let(:model_args) { [] }
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
context 'attributes' do
|
15
|
+
let(:capability) { { "value" => random_str } }
|
16
|
+
let(:capability_str) { JSON.dump(capability) }
|
17
|
+
|
18
|
+
context '#capability' do
|
19
|
+
subject { Ably::Models::TokenDetails.new({ capability: capability_str }) }
|
20
|
+
|
21
|
+
it 'retrieves attribute :capability as parsed JSON' do
|
22
|
+
expect(subject.capability).to eql(capability)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
{ :issued => :issued, :expires => :expires }.each do |method_name, attribute|
|
27
|
+
let(:time) { Time.now }
|
28
|
+
context "##{method_name} with :#{method_name} option as milliseconds in constructor" do
|
29
|
+
subject { Ably::Models::TokenDetails.new({ attribute.to_sym => time.to_i * 1000 }) }
|
30
|
+
|
31
|
+
it "retrieves attribute :#{attribute} as Time" do
|
32
|
+
expect(subject.public_send(method_name)).to be_a(Time)
|
33
|
+
expect(subject.public_send(method_name).to_i).to eql(time.to_i)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context "##{method_name} with :#{method_name} option as a Time in constructor" do
|
38
|
+
subject { Ably::Models::TokenDetails.new({ attribute.to_sym => time }) }
|
39
|
+
|
40
|
+
it "retrieves attribute :#{attribute} as Time" do
|
41
|
+
expect(subject.public_send(method_name)).to be_a(Time)
|
42
|
+
expect(subject.public_send(method_name).to_i).to eql(time.to_i)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context "##{method_name} when converted to JSON" do
|
47
|
+
subject { Ably::Models::TokenDetails.new({ attribute.to_sym => time }) }
|
48
|
+
|
49
|
+
it 'is in milliseconds' do
|
50
|
+
expect(JSON.parse(JSON.dump(subject))[convert_to_mixed_case(attribute)]).to eql((time.to_f * 1000).round)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context '#expired?' do
|
56
|
+
let(:expire_time) { Time.now + Ably::Models::TokenDetails::TOKEN_EXPIRY_BUFFER }
|
57
|
+
|
58
|
+
context 'once grace period buffer has passed' do
|
59
|
+
subject { Ably::Models::TokenDetails.new(expires: expire_time - 1) }
|
60
|
+
|
61
|
+
it 'is true' do
|
62
|
+
expect(subject.expired?).to eql(true)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context 'within grace period buffer' do
|
67
|
+
subject { Ably::Models::TokenDetails.new(expires: expire_time + 1) }
|
68
|
+
|
69
|
+
it 'is false' do
|
70
|
+
expect(subject.expired?).to eql(false)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context '==' do
|
77
|
+
let(:token_attributes) { { token: 'unique' } }
|
78
|
+
|
79
|
+
it 'is true when attributes are the same' do
|
80
|
+
new_token = -> { Ably::Models::TokenDetails.new(token_attributes) }
|
81
|
+
expect(new_token[]).to eq(new_token[])
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'is false when attributes are not the same' do
|
85
|
+
expect(Ably::Models::TokenDetails.new(token: 1)).to_not eq(Ably::Models::TokenDetails.new(token: 2))
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'is false when class type differs' do
|
89
|
+
expect(Ably::Models::TokenDetails.new(token: 1)).to_not eq(nil)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
context 'TokenDetails conversion methods', :api_private do
|
94
|
+
context 'with a TokenDetails object' do
|
95
|
+
let(:token_details) { Ably::Models::TokenDetails.new(client_id: random_str) }
|
96
|
+
|
97
|
+
it 'returns the TokenDetails object' do
|
98
|
+
expect(Ably::Models::TokenDetails(token_details)).to eql(token_details)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
context 'with a JSON object' do
|
103
|
+
let(:client_id) { random_str }
|
104
|
+
let(:token_details_json) { { client_id: client_id } }
|
105
|
+
|
106
|
+
it 'returns a new TokenDetails object from the JSON' do
|
107
|
+
expect(Ably::Models::TokenDetails(token_details_json).client_id).to eql(client_id)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|