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
@@ -6,7 +6,7 @@ describe Ably::Realtime::Connection, 'failures', :event_machine do
6
6
 
7
7
  vary_by_protocol do
8
8
  let(:default_options) do
9
- { api_key: api_key, environment: environment, protocol: protocol }
9
+ { key: api_key, environment: environment, protocol: protocol }
10
10
  end
11
11
 
12
12
  let(:client_options) { default_options }
@@ -16,12 +16,12 @@ describe Ably::Realtime::Connection, 'failures', :event_machine do
16
16
 
17
17
  context 'authentication failure' do
18
18
  let(:client_options) do
19
- default_options.merge(api_key: invalid_key, log_level: :none)
19
+ default_options.merge(key: invalid_key, log_level: :none)
20
20
  end
21
21
 
22
22
  context 'when API key is invalid' do
23
23
  context 'with invalid app part of the key' do
24
- let(:invalid_key) { 'not_an_app.invalid_key_id:invalid_key_value' }
24
+ let(:invalid_key) { 'not_an_app.invalid_key_name:invalid_key_value' }
25
25
 
26
26
  it 'enters the failed state and returns a not found error' do
27
27
  connection.on(:failed) do |error|
@@ -34,8 +34,8 @@ describe Ably::Realtime::Connection, 'failures', :event_machine do
34
34
  end
35
35
  end
36
36
 
37
- context 'with invalid key ID part of the key' do
38
- let(:invalid_key) { "#{app_id}.invalid_key_id:invalid_key_value" }
37
+ context 'with invalid key name part of the key' do
38
+ let(:invalid_key) { "#{app_id}.invalid_key_name:invalid_key_value" }
39
39
 
40
40
  it 'enters the failed state and returns an authorization error' do
41
41
  connection.on(:failed) do |error|
@@ -7,7 +7,7 @@ describe Ably::Realtime::Connection, :event_machine do
7
7
 
8
8
  vary_by_protocol do
9
9
  let(:default_options) do
10
- { api_key: api_key, environment: environment, protocol: protocol }
10
+ { key: api_key, environment: environment, protocol: protocol }
11
11
  end
12
12
 
13
13
  let(:client_options) { default_options }
@@ -52,7 +52,7 @@ describe Ably::Realtime::Connection, :event_machine do
52
52
  before do
53
53
  # Reduce token expiry buffer to zero so that a token expired? predicate is exact
54
54
  # Normally there is a buffer so that a token expiring soon is considered expired
55
- stub_const 'Ably::Models::Token::TOKEN_EXPIRY_BUFFER', 0
55
+ stub_const 'Ably::Models::TokenDetails::TOKEN_EXPIRY_BUFFER', 0
56
56
  end
57
57
 
58
58
  context 'for renewable tokens' do
@@ -93,17 +93,17 @@ describe Ably::Realtime::Connection, :event_machine do
93
93
 
94
94
  it 'renews the token on connect' do
95
95
  sleep ttl + 0.1
96
- expect(client.auth.current_token).to be_expired
96
+ expect(client.auth.current_token_details).to be_expired
97
97
  expect(client.auth).to receive(:authorise).at_least(:once).and_call_original
98
98
  connection.once(:connected) do
99
- expect(client.auth.current_token).to_not be_expired
99
+ expect(client.auth.current_token_details).to_not be_expired
100
100
  stop_reactor
101
101
  end
102
102
  end
103
103
  end
104
104
 
105
105
  context 'with immediately expiring token' do
106
- let(:ttl) { 0.01 }
106
+ let(:ttl) { 0.001 }
107
107
 
108
108
  it 'renews the token on connect, and only makes one subsequent attempt to obtain a new token' do
109
109
  expect(client.auth).to receive(:authorise).at_least(:twice).and_call_original
@@ -138,19 +138,19 @@ describe Ably::Realtime::Connection, :event_machine do
138
138
 
139
139
  context 'when connected with a valid non-expired token' do
140
140
  context 'that then expires following the connection being opened' do
141
- let(:ttl) { 2 }
141
+ let(:ttl) { 5 }
142
142
  let(:channel) { client.channel('test') }
143
143
 
144
144
  context 'the server' do
145
- it 'disconnects the client, and the client automatically renews the token and then reconnects', em_timeout: 10 do
146
- original_token = client.auth.current_token
145
+ it 'disconnects the client, and the client automatically renews the token and then reconnects', em_timeout: 15 do
146
+ original_token = client.auth.current_token_details
147
147
  expect(original_token).to_not be_expired
148
148
 
149
149
  connection.once(:connected) do
150
150
  started_at = Time.now
151
151
  connection.once(:disconnected) do |error|
152
152
  connection.once(:connected) do
153
- expect(client.auth.current_token).to_not be_expired
153
+ expect(client.auth.current_token_details).to_not be_expired
154
154
  expect(Time.now - started_at >= ttl)
155
155
  expect(original_token).to be_expired
156
156
  expect(error.code).to eql(40140) # token expired
@@ -172,16 +172,16 @@ describe Ably::Realtime::Connection, :event_machine do
172
172
 
173
173
  context 'for non-renewable tokens' do
174
174
  context 'that are expired' do
175
- let!(:expired_token) do
175
+ let!(:expired_token_details) do
176
176
  Ably::Realtime::Client.new(default_options).auth.request_token(ttl: 0.01)
177
177
  end
178
178
 
179
179
  context 'opening a new connection' do
180
- let(:client_options) { default_options.merge(api_key: nil, token_id: expired_token.id, log_level: :none) }
180
+ let(:client_options) { default_options.merge(key: nil, token: expired_token_details.token, log_level: :none) }
181
181
 
182
182
  it 'transitions state to failed', em_timeout: 10 do
183
183
  EventMachine.add_timer(1) do # wait for token to expire
184
- expect(expired_token).to be_expired
184
+ expect(expired_token_details).to be_expired
185
185
  connection.once(:connected) { raise 'Connection should never connect as token has expired' }
186
186
  connection.once(:failed) do
187
187
  expect(client.connection.error_reason.code).to eql(40140)
@@ -6,7 +6,7 @@ require 'securerandom'
6
6
 
7
7
  describe 'Ably::Realtime::Channel Message', :event_machine do
8
8
  vary_by_protocol do
9
- let(:default_options) { options.merge(api_key: api_key, environment: environment, protocol: protocol) }
9
+ let(:default_options) { options.merge(key: api_key, environment: environment, protocol: protocol) }
10
10
  let(:client_options) { default_options }
11
11
  let(:client) do
12
12
  Ably::Realtime::Client.new(client_options)
@@ -77,8 +77,8 @@ describe 'Ably::Realtime::Channel Message', :event_machine do
77
77
  context 'when retrieved over REST' do
78
78
  it 'matches the sender connection#id' do
79
79
  channel.publish('event', payload) do
80
- channel.history do |messages|
81
- expect(messages.first.connection_id).to eql(client.connection.id)
80
+ channel.history do |page|
81
+ expect(page.items.first.connection_id).to eql(client.connection.id)
82
82
  stop_reactor
83
83
  end
84
84
  end
@@ -175,7 +175,7 @@ describe 'Ably::Realtime::Channel Message', :event_machine do
175
175
 
176
176
  context 'without suitable publishing permissions' do
177
177
  let(:restricted_client) do
178
- Ably::Realtime::Client.new(options.merge(api_key: restricted_api_key, environment: environment, protocol: protocol))
178
+ Ably::Realtime::Client.new(options.merge(key: restricted_api_key, environment: environment, protocol: protocol))
179
179
  end
180
180
  let(:restricted_channel) { restricted_client.channel("cansubscribe:example") }
181
181
  let(:payload) { 'Test message without permission to publish' }
@@ -293,7 +293,7 @@ describe 'Ably::Realtime::Channel Message', :event_machine do
293
293
  end
294
294
  end
295
295
 
296
- resources_root = File.expand_path('../../../resources', __FILE__)
296
+ resources_root = File.expand_path('../../../../lib/submodules/ably-common/test-resources', __FILE__)
297
297
 
298
298
  def self.add_tests_for_data(data)
299
299
  data['items'].each_with_index do |item, index|
@@ -3,7 +3,7 @@ require 'spec_helper'
3
3
 
4
4
  describe Ably::Realtime::Presence, 'history', :event_machine do
5
5
  vary_by_protocol do
6
- let(:default_options) { { api_key: api_key, environment: environment, protocol: protocol } }
6
+ let(:default_options) { { key: api_key, environment: environment, protocol: protocol } }
7
7
 
8
8
  let(:channel_name) { "persisted:#{random_str(2)}" }
9
9
 
@@ -21,16 +21,16 @@ describe Ably::Realtime::Presence, 'history', :event_machine do
21
21
  it 'provides up to the moment presence history' do
22
22
  presence_client_one.enter(data: data) do
23
23
  presence_client_one.leave(data: leave_data) do
24
- presence_client_one.history do |history|
25
- expect(history.count).to eql(2)
24
+ presence_client_one.history do |history_page|
25
+ expect(history_page.items.count).to eql(2)
26
26
 
27
- expect(history[1].action).to eq(:enter)
28
- expect(history[1].client_id).to eq(client_one.client_id)
29
- expect(history[1].data).to eql(data)
27
+ expect(history_page.items[1].action).to eq(:enter)
28
+ expect(history_page.items[1].client_id).to eq(client_one.client_id)
29
+ expect(history_page.items[1].data).to eql(data)
30
30
 
31
- expect(history[0].action).to eq(:leave)
32
- expect(history[0].client_id).to eq(client_one.client_id)
33
- expect(history[0].data).to eql(leave_data)
31
+ expect(history_page.items[0].action).to eq(:leave)
32
+ expect(history_page.items[0].client_id).to eq(client_one.client_id)
33
+ expect(history_page.items[0].data).to eql(leave_data)
34
34
 
35
35
  stop_reactor
36
36
  end
@@ -40,16 +40,59 @@ describe Ably::Realtime::Presence, 'history', :event_machine do
40
40
 
41
41
  it 'ensures REST presence history message IDs match ProtocolMessage wrapped message and connection IDs via Realtime' do
42
42
  presence_client_one.subscribe(:enter) do |message|
43
- presence_client_one.history do |history|
44
- expect(history.count).to eql(1)
43
+ presence_client_one.history do |history_page|
44
+ expect(history_page.items.count).to eql(1)
45
45
 
46
- expect(history[0].id).to eql(message.id)
47
- expect(history[0].connection_id).to eql(message.connection_id)
46
+ expect(history_page.items[0].id).to eql(message.id)
47
+ expect(history_page.items[0].connection_id).to eql(message.connection_id)
48
48
  stop_reactor
49
49
  end
50
50
  end
51
51
 
52
52
  presence_client_one.enter(data: data)
53
53
  end
54
+
55
+ context 'with option until_attach: true' do
56
+ let(:event) { random_str }
57
+ let(:presence_data_before_attach) { random_str }
58
+ let(:presence_data_after_attach) { random_str }
59
+
60
+ it 'retrieves all presence messages before channel was attached' do
61
+ presence_client_two.enter(data: presence_data_before_attach) do
62
+ presence_client_one.enter(data: presence_data_after_attach) do
63
+ presence_client_one.history(until_attach: true) do |presence_page|
64
+ expect(presence_page.items.count).to eql(1)
65
+ expect(presence_page.items.first.data).to eql(presence_data_before_attach)
66
+ stop_reactor
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ context 'and two pages of messages' do
73
+ it 'retrieves two pages of messages before channel was attached' do
74
+ when_all(*10.times.map { |i| presence_client_two.enter_client("client:#{i}", data: presence_data_before_attach) }) do
75
+ when_all(*10.times.map { |i| presence_client_one.enter_client("client:#{i}", data: presence_data_after_attach) }) do
76
+ presence_client_one.history(until_attach: true, limit: 5) do |presence_page|
77
+ expect(presence_page.items.count).to eql(5)
78
+ expect(presence_page.items.map(&:data).uniq.first).to eql(presence_data_before_attach)
79
+
80
+ presence_page.next do |presence_next_page|
81
+ expect(presence_next_page.items.count).to eql(5)
82
+ expect(presence_next_page.items.map(&:data).uniq.first).to eql(presence_data_before_attach)
83
+ expect(presence_next_page).to be_last
84
+ stop_reactor
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
91
+
92
+ it 'raises an exception unless state is attached' do
93
+ expect { presence_client_one.history(until_attach: true) }.to raise_error(ArgumentError, /not attached/)
94
+ stop_reactor
95
+ end
96
+ end
54
97
  end
55
98
  end
@@ -5,7 +5,7 @@ describe Ably::Realtime::Presence, :event_machine 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
 
11
11
  let(:anonymous_client) { Ably::Realtime::Client.new(client_options) }
@@ -487,7 +487,7 @@ describe Ably::Realtime::Presence, :event_machine do
487
487
 
488
488
  context 'without necessary capabilities to join presence' do
489
489
  let(:restricted_client) do
490
- Ably::Realtime::Client.new(default_options.merge(api_key: restricted_api_key, log_level: :fatal))
490
+ Ably::Realtime::Client.new(default_options.merge(key: restricted_api_key, log_level: :fatal))
491
491
  end
492
492
  let(:restricted_channel) { restricted_client.channel("cansubscribe:channel") }
493
493
  let(:restricted_presence) { restricted_channel.presence }
@@ -688,7 +688,7 @@ describe Ably::Realtime::Presence, :event_machine do
688
688
 
689
689
  context 'without necessary capabilities to enter on behalf of another client' do
690
690
  let(:restricted_client) do
691
- Ably::Realtime::Client.new(default_options.merge(api_key: restricted_api_key, log_level: :fatal))
691
+ Ably::Realtime::Client.new(default_options.merge(key: restricted_api_key, log_level: :fatal))
692
692
  end
693
693
  let(:restricted_channel) { restricted_client.channel("cansubscribe:channel") }
694
694
  let(:restricted_presence) { restricted_channel.presence }
@@ -1123,8 +1123,8 @@ describe Ably::Realtime::Presence, :event_machine do
1123
1123
  context 'REST #get' do
1124
1124
  it 'returns current members' do
1125
1125
  presence_client_one.enter(data: data_payload) do
1126
- members = channel_rest_client_one.presence.get
1127
- this_member = members.first
1126
+ members_page = channel_rest_client_one.presence.get
1127
+ this_member = members_page.items.first
1128
1128
 
1129
1129
  expect(this_member).to be_a(Ably::Models::PresenceMessage)
1130
1130
  expect(this_member.client_id).to eql(client_one.client_id)
@@ -1137,8 +1137,8 @@ describe Ably::Realtime::Presence, :event_machine do
1137
1137
  it 'returns no members once left' do
1138
1138
  presence_client_one.enter(data: data_payload) do
1139
1139
  presence_client_one.leave do
1140
- members = channel_rest_client_one.presence.get
1141
- expect(members.count).to eql(0)
1140
+ members_page = channel_rest_client_one.presence.get
1141
+ expect(members_page.items.count).to eql(0)
1142
1142
  stop_reactor
1143
1143
  end
1144
1144
  end
@@ -1264,7 +1264,7 @@ describe Ably::Realtime::Presence, :event_machine do
1264
1264
  context 'REST #get' do
1265
1265
  it 'returns a list of members with decrypted data' do
1266
1266
  encrypted_channel.presence.enter(data: data) do
1267
- member = channel_rest_client_one.presence.get.first
1267
+ member = channel_rest_client_one.presence.get.items.first
1268
1268
  expect(member.encoding).to be_nil
1269
1269
  expect(member.data).to eql(data)
1270
1270
  stop_reactor
@@ -3,7 +3,7 @@ require 'spec_helper'
3
3
  describe Ably::Realtime::Client, '#stats', :event_machine do
4
4
  vary_by_protocol do
5
5
  let(:client) do
6
- Ably::Realtime::Client.new(api_key: api_key, environment: environment, protocol: protocol)
6
+ Ably::Realtime::Client.new(key: api_key, environment: environment, protocol: protocol)
7
7
  end
8
8
 
9
9
  describe 'fetching stats' do
@@ -3,7 +3,7 @@ require 'spec_helper'
3
3
  describe Ably::Realtime::Client, '#time', :event_machine do
4
4
  vary_by_protocol do
5
5
  let(:client) do
6
- Ably::Realtime::Client.new(api_key: api_key, environment: environment, protocol: protocol)
6
+ Ably::Realtime::Client.new(key: api_key, environment: environment, protocol: protocol)
7
7
  end
8
8
 
9
9
  describe 'fetching the service time' do
@@ -4,17 +4,17 @@ require 'spec_helper'
4
4
  describe Ably::Auth do
5
5
  include Ably::Modules::Conversions
6
6
 
7
- def hmac_for(token_request, secret)
8
- ruby_named_token_request = Ably::Models::IdiomaticRubyWrapper.new(token_request)
7
+ def hmac_for(token_request_attributes, secret)
8
+ token_request= Ably::Models::IdiomaticRubyWrapper.new(token_request_attributes)
9
9
 
10
10
  text = [
11
- :id,
11
+ :key_name,
12
12
  :ttl,
13
13
  :capability,
14
14
  :client_id,
15
15
  :timestamp,
16
16
  :nonce
17
- ].map { |key| "#{ruby_named_token_request[key]}\n" }.join("")
17
+ ].map { |key| "#{token_request.hash[key]}\n" }.join("")
18
18
 
19
19
  encode64(
20
20
  OpenSSL::HMAC.digest(OpenSSL::Digest::SHA256.new, secret, text)
@@ -23,7 +23,7 @@ describe Ably::Auth do
23
23
 
24
24
  vary_by_protocol do
25
25
  let(:client) do
26
- Ably::Rest::Client.new(api_key: api_key, environment: environment, protocol: protocol)
26
+ Ably::Rest::Client.new(key: api_key, environment: environment, protocol: protocol)
27
27
  end
28
28
  let(:auth) { client.auth }
29
29
  let(:content_type) do
@@ -52,37 +52,43 @@ describe Ably::Auth do
52
52
  end
53
53
 
54
54
  it 'has immutable options' do
55
- expect { auth.options['key_id'] = 'new_id' }.to raise_error RuntimeError, /can't modify frozen.*Hash/
55
+ expect { auth.options['key_name'] = 'new_name' }.to raise_error RuntimeError, /can't modify frozen.*Hash/
56
56
  end
57
57
 
58
58
  describe '#request_token' do
59
59
  let(:ttl) { 30 * 60 }
60
60
  let(:capability) { { :foo => ['publish'] } }
61
61
 
62
- let(:token) do
62
+ let(:token_details) do
63
63
  auth.request_token(
64
64
  ttl: ttl,
65
65
  capability: capability
66
66
  )
67
67
  end
68
68
 
69
- it 'returns a valid requested token in the expected format with valid issued_at and expires_at attributes' do
70
- expect(token.id).to match(/^#{app_id}\.[\w-]+$/)
71
- expect(token.key_id).to match(/^#{key_id}$/)
72
- expect(token.issued_at).to be_within(2).of(Time.now)
73
- expect(token.expires_at).to be_within(2).of(Time.now + ttl)
69
+ it 'returns a valid requested token in the expected format with valid issued and expires attributes' do
70
+ expect(token_details.token).to match(/^#{app_id}\.[\w-]+$/)
71
+ expect(token_details.key_name).to match(/^#{key_name}$/)
72
+ expect(token_details.issued).to be_within(2).of(Time.now)
73
+ expect(token_details.expires).to be_within(2).of(Time.now + ttl)
74
74
  end
75
75
 
76
76
  %w(client_id capability nonce timestamp ttl).each do |option|
77
77
  context "with option :#{option}", :webmock do
78
- let(:random) { random_int_str }
78
+ def coerce_if_time_value(field_name, value, options = {})
79
+ multiply = options[:multiply]
80
+ return value unless %w(timestamp ttl).include?(field_name)
81
+ value.to_i * (multiply ? multiply : 1)
82
+ end
83
+
84
+ let(:random) { coerce_if_time_value(option, random_int_str) }
79
85
  let(:options) { { option.to_sym => random } }
80
86
 
81
- let(:token_response) { { access_token: {} } }
87
+ let(:token_response) { {} }
82
88
  let!(:request_token_stub) do
83
- stub_request(:post, "#{client.endpoint}/keys/#{key_id}/requestToken").
89
+ stub_request(:post, "#{client.endpoint}/keys/#{key_name}/requestToken").
84
90
  with do |request|
85
- request_body_includes(request, protocol, option, random)
91
+ request_body_includes(request, protocol, option, coerce_if_time_value(option, random, multiply: 1000))
86
92
  end.to_return(
87
93
  :status => 201,
88
94
  :body => serialize(token_response, protocol),
@@ -92,25 +98,25 @@ describe Ably::Auth do
92
98
 
93
99
  before { auth.request_token options }
94
100
 
95
- it 'overrides default and uses camelCase notation for all attributes' do
101
+ it "overrides default and uses camelCase notation for attributes" do
96
102
  expect(request_token_stub).to have_been_requested
97
103
  end
98
104
  end
99
105
  end
100
106
 
101
- context 'with :key_id & :key_secret options', :webmock do
102
- let(:key_id) { random_str }
107
+ context 'with :key option', :webmock do
108
+ let(:key_name) { "app.#{random_str}" }
103
109
  let(:key_secret) { random_str }
104
110
  let(:nonce) { random_str }
105
- let(:token_options) { { key_id: key_id, key_secret: key_secret, nonce: nonce, timestamp: Time.now } }
111
+ let(:token_options) { { key: "#{key_name}:#{key_secret}", nonce: nonce, timestamp: Time.now } }
106
112
  let(:token_request) { auth.create_token_request(token_options) }
107
113
  let(:mac) do
108
114
  hmac_for(token_request, key_secret)
109
115
  end
110
116
 
111
- let(:token_response) { { access_token: {} } }
117
+ let(:token_response) { {} }
112
118
  let!(:request_token_stub) do
113
- stub_request(:post, "#{client.endpoint}/keys/#{key_id}/requestToken").
119
+ stub_request(:post, "#{client.endpoint}/keys/#{key_name}/requestToken").
114
120
  with do |request|
115
121
  request_body_includes(request, protocol, 'mac', mac)
116
122
  end.to_return(
@@ -119,9 +125,38 @@ describe Ably::Auth do
119
125
  :headers => { 'Content-Type' => content_type })
120
126
  end
121
127
 
122
- let!(:token) { auth.request_token(token_options) }
128
+ let!(:token) { puts token_options; auth.request_token(token_options) }
123
129
 
124
- specify 'key_id is used in request and signing uses key_secret' do
130
+ specify 'key_name is used in request and signing uses key_secret' do
131
+ expect(request_token_stub).to have_been_requested
132
+ end
133
+ end
134
+
135
+ context 'with :key_name & :key_secret options', :webmock do
136
+ let(:key_name) { "app.#{random_str}" }
137
+ let(:key_secret) { random_str }
138
+ let(:nonce) { random_str }
139
+
140
+ let(:name_secret_token_options) { { key_name: key_name, key_secret: key_secret, nonce: nonce, timestamp: Time.now } }
141
+ let(:token_request) { auth.create_token_request(name_secret_token_options) }
142
+ let(:mac) do
143
+ hmac_for(token_request, key_secret)
144
+ end
145
+
146
+ let(:token_response) { {} }
147
+ let!(:request_token_stub) do
148
+ stub_request(:post, "#{client.endpoint}/keys/#{key_name}/requestToken").
149
+ with do |request|
150
+ request_body_includes(request, protocol, 'mac', mac)
151
+ end.to_return(
152
+ :status => 201,
153
+ :body => serialize(token_response, protocol),
154
+ :headers => { 'Content-Type' => content_type })
155
+ end
156
+
157
+ let!(:token) { auth.request_token(name_secret_token_options); }
158
+
159
+ specify 'key_name is used in request and signing uses key_secret' do
125
160
  expect(request_token_stub).to have_been_requested
126
161
  end
127
162
  end
@@ -146,8 +181,8 @@ describe Ably::Auth do
146
181
 
147
182
  context 'with :auth_url option', :webmock do
148
183
  let(:auth_url) { 'https://www.fictitious.com/get_token' }
149
- let(:auth_url_response) { { id: key_id } }
150
- let(:token_response) { { access_token: { } } }
184
+ let(:auth_url_response) { { keyName: key_name }.to_json }
185
+ let(:token_response) { {} }
151
186
  let(:query_params) { nil }
152
187
  let(:headers) { nil }
153
188
  let(:auth_method) { :get }
@@ -166,15 +201,16 @@ describe Ably::Auth do
166
201
  stub.with(:headers => headers) unless headers.nil?
167
202
  stub.to_return(
168
203
  :status => 201,
169
- :body => auth_url_response.to_json,
170
- :headers => { 'Content-Type' => 'application/json' }
204
+ :body => auth_url_response,
205
+ :headers => { 'Content-Type' => auth_url_content_type }
171
206
  )
172
207
  end
208
+ let(:auth_url_content_type) { 'application/json' }
173
209
 
174
210
  let!(:request_token_stub) do
175
- stub_request(:post, "#{client.endpoint}/keys/#{key_id}/requestToken").
211
+ stub_request(:post, "#{client.endpoint}/keys/#{key_name}/requestToken").
176
212
  with do |request|
177
- request_body_includes(request, protocol, 'id', key_id)
213
+ request_body_includes(request, protocol, 'key_name', key_name)
178
214
  end.to_return(
179
215
  :status => 201,
180
216
  :body => serialize(token_response, protocol),
@@ -191,7 +227,7 @@ describe Ably::Auth do
191
227
  end
192
228
 
193
229
  it 'returns a valid token generated from the token request' do
194
- expect(token).to be_a(Ably::Models::Token)
230
+ expect(token).to be_a(Ably::Models::TokenDetails)
195
231
  end
196
232
 
197
233
  context 'with :query_params' do
@@ -220,29 +256,45 @@ describe Ably::Auth do
220
256
  end
221
257
  end
222
258
 
223
- context 'when response from :auth_url is a token' do
224
- let(:token_id) { 'J_0Tlg.D7AVZkdOZW-PqNNGvCSp38' }
225
- let(:issued_at) { Time.now }
259
+ context 'when response from :auth_url is a token details object' do
260
+ let(:token) { 'J_0Tlg.D7AVZkdOZW-PqNNGvCSp38' }
261
+ let(:issued) { Time.now }
226
262
  let(:expires) { Time.now + 60}
227
263
  let(:capability) { {'foo'=>['publish']} }
264
+ let(:capability_str) { JSON.dump(capability) }
228
265
  let(:auth_url_response) do
229
266
  {
230
- 'id' => token_id,
231
- 'key' => 'J_0Tlg.NxCRig',
232
- 'issued_at' => issued_at.to_i,
233
- 'expires' => expires.to_i,
234
- 'capability'=> capability
235
- }
267
+ 'token' => token,
268
+ 'key_name' => 'J_0Tlg.NxCRig',
269
+ 'issued' => issued.to_i * 1000,
270
+ 'expires' => expires.to_i * 1000,
271
+ 'capability'=> capability_str
272
+ }.to_json
236
273
  end
237
274
 
238
- let!(:token) { auth.request_token(options) }
275
+ let!(:token_details) { auth.request_token(options) }
239
276
 
240
- it 'returns a Token created from the token JSON' do
277
+ it 'returns TokenDetails created from the token JSON' do
241
278
  expect(request_token_stub).to_not have_been_requested
242
- expect(token.id).to eql(token_id)
243
- expect(token.expires_at).to be_within(1).of(expires)
244
- expect(token.issued_at).to be_within(1).of(issued_at)
245
- expect(token.capability.to_json).to eql(capability.to_json)
279
+ expect(token_details).to be_a(Ably::Models::TokenDetails)
280
+ expect(token_details.token).to eql(token)
281
+ expect(token_details.expires).to be_within(1).of(expires)
282
+ expect(token_details.issued).to be_within(1).of(issued)
283
+ expect(token_details.capability).to eql(capability)
284
+ end
285
+ end
286
+
287
+ context 'when response from :auth_url is text/plain content type and a token string' do
288
+ let(:token) { 'J_0Tlg.D7AVZkdOZW-PqNNGvCSp38' }
289
+ let(:auth_url_content_type) { 'text/plain' }
290
+ let(:auth_url_response) { token }
291
+
292
+ let!(:token_details) { auth.request_token(options) }
293
+
294
+ it 'returns TokenDetails created from the token JSON' do
295
+ expect(request_token_stub).to_not have_been_requested
296
+ expect(token_details).to be_a(Ably::Models::TokenDetails)
297
+ expect(token_details.token).to eql(token)
246
298
  end
247
299
  end
248
300
 
@@ -270,90 +322,135 @@ describe Ably::Auth do
270
322
  end
271
323
  end
272
324
 
273
- context 'with token_request_block that returns a token request' do
274
- let(:client_id) { random_str }
275
- let(:options) { { client_id: client_id } }
276
- let!(:request_token) do
277
- auth.request_token(options) do |block_options|
278
- @block_called = true
279
- @block_options = block_options
280
- auth.create_token_request(client_id: client_id)
325
+ context 'with a Proc for the :auth_callback option' do
326
+ context 'that returns a TokenRequest' do
327
+ let(:client_id) { random_str }
328
+ let(:options) { { client_id: client_id } }
329
+ let!(:request_token) do
330
+ auth.request_token(options.merge(auth_callback: Proc.new do |block_options|
331
+ @block_called = true
332
+ @block_options = block_options
333
+ auth.create_token_request(client_id: client_id)
334
+ end))
281
335
  end
282
- end
283
336
 
284
- it 'calls the block when authenticating to obtain the request token' do
285
- expect(@block_called).to eql(true)
286
- expect(@block_options).to include(options)
287
- end
337
+ it 'calls the Proc when authenticating to obtain the request token' do
338
+ expect(@block_called).to eql(true)
339
+ expect(@block_options).to include(options)
340
+ end
288
341
 
289
- it 'uses the token request from the block when requesting a new token' do
290
- expect(request_token.client_id).to eql(client_id)
342
+ it 'uses the token request returned from the callback when requesting a new token' do
343
+ expect(request_token.client_id).to eql(client_id)
344
+ end
291
345
  end
292
- end
293
346
 
294
- context 'with token_request_block that returns a token' do
295
- let(:client_id) { random_str }
296
- let(:options) { { client_id: client_id } }
297
- let(:token_id) { 'J_0Tlg.D7AVZkdOZW-PqNNGvCSp38' }
298
- let(:issued_at) { Time.now }
299
- let(:expires) { Time.now + 60}
300
- let(:capability) { {'foo'=>['publish']} }
347
+ context 'that returns a TokenDetails JSON object' do
348
+ let(:client_id) { random_str }
349
+ let(:options) { { client_id: client_id } }
350
+ let(:token) { 'J_0Tlg.D7AVZkdOZW-PqNNGvCSp38' }
351
+ let(:issued) { Time.now }
352
+ let(:expires) { Time.now + 60}
353
+ let(:capability) { {'foo'=>['publish']} }
354
+ let(:capability_str) { JSON.dump(capability) }
355
+
356
+ let!(:token_details) do
357
+ auth.request_token(options.merge(auth_callback: Proc.new do |block_options|
358
+ @block_called = true
359
+ @block_options = block_options
360
+ {
361
+ 'token' => token,
362
+ 'keyName' => 'J_0Tlg.NxCRig',
363
+ 'clientId' => client_id,
364
+ 'issued' => issued.to_i * 1000,
365
+ 'expires' => expires.to_i * 1000,
366
+ 'capability'=> capability_str
367
+ }
368
+ end))
369
+ end
301
370
 
302
- let!(:request_token) do
303
- auth.request_token(options) do |block_options|
304
- @block_called = true
305
- @block_options = block_options
306
- {
307
- 'id' => token_id,
308
- 'key' => 'J_0Tlg.NxCRig',
309
- 'client_id' => client_id,
310
- 'issued_at' => issued_at.to_i,
311
- 'expires' => expires.to_i,
312
- 'capability'=> capability
313
- }
371
+ it 'calls the Proc when authenticating to obtain the request token' do
372
+ expect(@block_called).to eql(true)
373
+ expect(@block_options).to include(options)
374
+ end
375
+
376
+ it 'uses the token request returned from the callback when requesting a new token' do
377
+ expect(token_details).to be_a(Ably::Models::TokenDetails)
378
+ expect(token_details.token).to eql(token)
379
+ expect(token_details.client_id).to eql(client_id)
380
+ expect(token_details.expires).to be_within(1).of(expires)
381
+ expect(token_details.issued).to be_within(1).of(issued)
382
+ expect(token_details.capability).to eql(capability)
314
383
  end
315
384
  end
316
385
 
317
- it 'calls the block when authenticating to obtain the request token' do
318
- expect(@block_called).to eql(true)
319
- expect(@block_options).to include(options)
386
+ context 'that returns a TokenDetails object' do
387
+ let(:client_id) { random_str }
388
+
389
+ let!(:token_details) do
390
+ auth.request_token(auth_callback: Proc.new do |block_options|
391
+ auth.create_token_request({
392
+ client_id: client_id
393
+ })
394
+ end)
395
+ end
396
+
397
+ it 'uses the token request returned from the callback when requesting a new token' do
398
+ expect(token_details).to be_a(Ably::Models::TokenDetails)
399
+ expect(token_details.client_id).to eql(client_id)
400
+ end
320
401
  end
321
402
 
322
- it 'uses the token request from the block when requesting a new token' do
323
- expect(request_token).to be_a(Ably::Models::Token)
324
- expect(request_token.id).to eql(token_id)
325
- expect(request_token.client_id).to eql(client_id)
326
- expect(request_token.expires_at).to be_within(1).of(expires)
327
- expect(request_token.issued_at).to be_within(1).of(issued_at)
328
- expect(request_token.capability.to_json).to eql(capability.to_json)
403
+ context 'that returns a Token string' do
404
+ let(:second_client) { Ably::Rest::Client.new(key: api_key, environment: environment, protocol: protocol) }
405
+ let(:token) { second_client.auth.request_token.token }
406
+
407
+ let!(:token_details) do
408
+ auth.request_token(auth_callback: Proc.new do |block_options|
409
+ token
410
+ end)
411
+ end
412
+
413
+ it 'uses the token request returned from the callback when requesting a new token' do
414
+ expect(token_details).to be_a(Ably::Models::TokenDetails)
415
+ expect(token_details.token).to eql(token)
416
+ end
329
417
  end
330
418
  end
331
419
 
332
420
  context 'persisted option', api_private: true do
333
421
  context 'when set to true', api_private: true do
334
422
  let(:options) { { persisted: true } }
335
- let(:token) { auth.request_token(options) }
423
+ let(:token_details) { auth.request_token(options) }
336
424
 
337
425
  it 'returns a token with a short token ID that is used to look up the token details' do
338
- expect(token.id.length).to be < 64
339
- expect(token.id).to match(/^#{app_id}\.A/)
426
+ expect(token_details.token.length).to be < 64
427
+ expect(token_details.token).to match(/^#{app_id}\.A/)
340
428
  end
341
429
  end
342
430
 
343
431
  context 'when omitted', api_private: true do
344
432
  let(:options) { { } }
345
- let(:token) { auth.request_token(options) }
433
+ let(:token_details) { auth.request_token(options) }
346
434
 
347
435
  it 'returns a literal token' do
348
- expect(token.id.length).to be > 64
436
+ expect(token_details.token.length).to be > 64
349
437
  end
350
438
  end
351
439
  end
440
+
441
+ context 'with client_id' do
442
+ let(:client_id) { random_str }
443
+ let(:token_details) { auth.request_token(client_id: client_id) }
444
+
445
+ it 'returns a token with the client_id' do
446
+ expect(token_details.client_id).to eql(client_id)
447
+ end
448
+ end
352
449
  end
353
450
 
354
451
  context 'before #authorise has been called' do
355
- it 'has no current_token' do
356
- expect(auth.current_token).to be_nil
452
+ it 'has no current_token_details' do
453
+ expect(auth.current_token_details).to be_nil
357
454
  end
358
455
  end
359
456
 
@@ -369,62 +466,62 @@ describe Ably::Auth do
369
466
  end
370
467
 
371
468
  it 'returns a valid token' do
372
- expect(auth.authorise).to be_a(Ably::Models::Token)
469
+ expect(auth.authorise).to be_a(Ably::Models::TokenDetails)
373
470
  end
374
471
 
375
472
  it 'issues a new token if option :force => true' do
376
- expect { auth.authorise(force: true) }.to change { auth.current_token }
473
+ expect { auth.authorise(force: true) }.to change { auth.current_token_details }
377
474
  end
378
475
  end
379
476
 
380
477
  context 'with previous authorisation' do
381
478
  before do
382
479
  auth.authorise
383
- expect(auth.current_token).to_not be_expired
480
+ expect(auth.current_token_details).to_not be_expired
384
481
  end
385
482
 
386
- it 'does not request a token if current_token has not expired' do
483
+ it 'does not request a token if current_token_details has not expired' do
387
484
  expect(auth).to_not receive(:request_token)
388
485
  auth.authorise
389
486
  end
390
487
 
391
488
  it 'requests a new token if token is expired' do
392
- allow(auth.current_token).to receive(:expired?).and_return(true)
489
+ allow(auth.current_token_details).to receive(:expired?).and_return(true)
393
490
  expect(auth).to receive(:request_token)
394
- expect { auth.authorise }.to change { auth.current_token }
491
+ expect { auth.authorise }.to change { auth.current_token_details }
395
492
  end
396
493
 
397
494
  it 'issues a new token if option :force => true' do
398
- expect { auth.authorise(force: true) }.to change { auth.current_token }
495
+ expect { auth.authorise(force: true) }.to change { auth.current_token_details }
399
496
  end
400
497
  end
401
498
 
402
- it 'updates the persisted auth options thare are then used for subsequent authorise requests' do
499
+ it 'updates the persisted auth options that are then used for subsequent authorise requests' do
403
500
  expect(auth.options[:ttl]).to_not eql(26)
404
501
  auth.authorise(ttl: 26)
405
502
  expect(auth.options[:ttl]).to eql(26)
406
503
  end
407
504
 
408
- context 'with token_request_block' do
505
+ context 'with a Proc for the :auth_callback option' do
409
506
  let(:client_id) { random_str }
410
507
  let!(:token) do
411
- auth.authorise do
508
+ auth.authorise(auth_callback: Proc.new do
412
509
  @block_called ||= 0
413
510
  @block_called += 1
414
511
  auth.create_token_request(client_id: client_id)
415
- end
512
+ end)
416
513
  end
417
514
 
418
- it 'calls the block' do
515
+ it 'calls the Proc' do
419
516
  expect(@block_called).to eql(1)
420
517
  end
421
518
 
422
- it 'uses the token request returned from the block when requesting a new token' do
519
+ it 'uses the token request returned from the callback when requesting a new token' do
423
520
  expect(token.client_id).to eql(client_id)
424
521
  end
425
522
 
426
523
  context 'for every subsequent #request_token' do
427
- context 'without a provided block' do
524
+ context 'without a :auth_callback Proc' do
428
525
  it 'calls the originally provided block' do
429
526
  auth.request_token
430
527
  expect(@block_called).to eql(2)
@@ -432,8 +529,8 @@ describe Ably::Auth do
432
529
  end
433
530
 
434
531
  context 'with a provided block' do
435
- it 'does not call the originally provided block and calls the new #request_token block' do
436
- auth.request_token { @request_block_called = true; auth.create_token_request }
532
+ it 'does not call the originally provided Proc and calls the new #request_token :auth_callback Proc' do
533
+ auth.request_token(auth_callback: Proc.new { @request_block_called = true; auth.create_token_request })
437
534
  expect(@block_called).to eql(1)
438
535
  expect(@request_block_called).to eql(true)
439
536
  end
@@ -445,19 +542,19 @@ describe Ably::Auth do
445
542
  describe '#create_token_request' do
446
543
  let(:ttl) { 60 * 60 }
447
544
  let(:capability) { { :foo => ["publish"] } }
448
- let(:options) { Hash.new }
449
- subject { auth.create_token_request(options) }
545
+ let(:token_request_options) { Hash.new }
546
+ subject { auth.create_token_request(token_request_options) }
450
547
 
451
- it 'uses the key ID from the client' do
452
- expect(subject['id']).to eql(key_id)
548
+ it 'uses the key name from the client' do
549
+ expect(subject['keyName']).to eql(key_name)
453
550
  end
454
551
 
455
552
  it 'uses the default TTL' do
456
- expect(subject['ttl']).to eql(Ably::Models::Token::DEFAULTS[:ttl])
553
+ expect(subject['ttl']).to eql(Ably::Auth::TOKEN_DEFAULTS.fetch(:ttl) * 1000)
457
554
  end
458
555
 
459
556
  it 'uses the default capability' do
460
- expect(subject['capability']).to eql(Ably::Models::Token::DEFAULTS[:capability].to_json)
557
+ expect(subject['capability']).to eql(Ably::Auth::TOKEN_DEFAULTS.fetch(:capability).to_json)
461
558
  end
462
559
 
463
560
  context 'the nonce' do
@@ -471,25 +568,25 @@ describe Ably::Auth do
471
568
  end
472
569
  end
473
570
 
474
- %w(ttl capability nonce timestamp client_id).each do |attribute|
571
+ %w(ttl nonce client_id).each do |attribute|
475
572
  context "with option :#{attribute}" do
476
- let(:option_value) { random_int_str(1_000_000_000) }
573
+ let(:option_value) { random_int_str(1_000_000_000).to_i }
477
574
  before do
478
- options[attribute.to_sym] = option_value
575
+ token_request_options[attribute.to_sym] = option_value
479
576
  end
480
577
  it "overrides default" do
481
- expect(subject[convert_to_mixed_case(attribute)].to_s).to eql(option_value.to_s)
578
+ expect(subject.public_send(attribute).to_s).to eql(option_value.to_s)
482
579
  end
483
580
  end
484
581
  end
485
582
 
486
583
  context 'with additional invalid attributes' do
487
- let(:options) { { nonce: 'valid', is_not_used_by_token_request: 'invalid' } }
584
+ let(:token_request_options) { { nonce: 'valid', is_not_used_by_token_request: 'invalid' } }
488
585
  specify 'are ignored' do
489
- expect(subject.keys).to_not include(:is_not_used_by_token_request)
490
- expect(subject.keys).to_not include(convert_to_mixed_case(:is_not_used_by_token_request))
491
- expect(subject.keys).to include('nonce')
492
- expect(subject['nonce']).to eql('valid')
586
+ expect(subject.hash.keys).to_not include(:is_not_used_by_token_request)
587
+ expect(subject.hash.keys).to_not include(convert_to_mixed_case(:is_not_used_by_token_request))
588
+ expect(subject.hash.keys).to include(:nonce)
589
+ expect(subject.nonce).to eql('valid')
493
590
  end
494
591
  end
495
592
 
@@ -497,47 +594,52 @@ describe Ably::Auth do
497
594
  let(:client) { Ably::Rest::Client.new(auth_url: 'http://example.com', protocol: protocol) }
498
595
 
499
596
  it 'should raise an exception if key secret is missing' do
500
- expect { auth.create_token_request(key_id: 'id') }.to raise_error Ably::Exceptions::TokenRequestError
597
+ expect { auth.create_token_request(key_name: 'name') }.to raise_error Ably::Exceptions::TokenRequestError
501
598
  end
502
599
 
503
- it 'should raise an exception if key id is missing' do
600
+ it 'should raise an exception if key name is missing' do
504
601
  expect { auth.create_token_request(key_secret: 'secret') }.to raise_error Ably::Exceptions::TokenRequestError
505
602
  end
506
603
  end
507
604
 
508
605
  context 'with :query_time option' do
509
606
  let(:time) { Time.now - 30 }
510
- let(:options) { { query_time: true } }
607
+ let(:token_request_options) { { query_time: true } }
511
608
 
512
609
  it 'queries the server for the timestamp' do
513
610
  expect(client).to receive(:time).and_return(time)
514
- expect(subject['timestamp']).to eql(time.to_i)
611
+ expect(subject['timestamp']).to be_within(1).of(time.to_f * 1000)
515
612
  end
516
613
  end
517
614
 
518
615
  context 'with :timestamp option' do
519
616
  let(:token_request_time) { Time.now + 5 }
520
- let(:options) { { timestamp: token_request_time } }
617
+ let(:token_request_options) { { timestamp: token_request_time } }
521
618
 
522
619
  it 'uses the provided timestamp in the token request' do
523
- expect(subject['timestamp']).to eql(token_request_time.to_i)
620
+ expect(subject['timestamp']).to be_within(1).of(token_request_time.to_f * 1000)
524
621
  end
525
622
  end
526
623
 
527
624
  context 'signing' do
528
- let(:options) do
625
+ let(:token_request_options) do
529
626
  {
530
- id: random_str,
531
- ttl: random_str,
627
+ key_name: random_str,
628
+ ttl: random_int_str.to_i,
532
629
  capability: random_str,
533
630
  client_id: random_str,
534
- timestamp: random_int_str,
631
+ timestamp: random_int_str.to_i,
535
632
  nonce: random_str
536
633
  }
537
634
  end
538
635
 
636
+ # TokenRequest expects times in milliseconds, whereas create_token_request assumes Ruby default of seconds
637
+ let(:token_request_attributes) do
638
+ token_request_options.merge(timestamp: token_request_options[:timestamp] * 1000, ttl: token_request_options[:ttl] * 1000)
639
+ end
640
+
539
641
  it 'generates a valid HMAC' do
540
- hmac = hmac_for(options, key_secret)
642
+ hmac = hmac_for(Ably::Models::TokenRequest(token_request_attributes).hash, key_secret)
541
643
  expect(subject['mac']).to eql(hmac)
542
644
  end
543
645
  end
@@ -546,20 +648,20 @@ describe Ably::Auth do
546
648
  context 'using token authentication' do
547
649
  let(:capability) { { :foo => ["publish"] } }
548
650
 
549
- describe 'with :token_id option' do
651
+ describe 'with :token option' do
550
652
  let(:ttl) { 60 * 60 }
551
- let(:token) do
653
+ let(:token_details) do
552
654
  auth.request_token(
553
655
  ttl: ttl,
554
656
  capability: capability
555
657
  )
556
658
  end
557
- let(:token_id) { token.id }
659
+ let(:token) { token_details.token }
558
660
  let(:token_auth_client) do
559
- Ably::Rest::Client.new(token_id: token_id, environment: environment, protocol: protocol)
661
+ Ably::Rest::Client.new(token: token, environment: environment, protocol: protocol)
560
662
  end
561
663
 
562
- it 'authenticates successfully using the provided :token_id' do
664
+ it 'authenticates successfully using the provided :token' do
563
665
  expect(token_auth_client.channel('foo').publish('event', 'data')).to be_truthy
564
666
  end
565
667
 
@@ -587,25 +689,23 @@ describe Ably::Auth do
587
689
  context 'when implicit as a result of using :client id' do
588
690
  let(:client_id) { '999' }
589
691
  let(:client) do
590
- Ably::Rest::Client.new(api_key: api_key, client_id: client_id, environment: environment, protocol: protocol)
692
+ Ably::Rest::Client.new(key: api_key, client_id: client_id, environment: environment, protocol: protocol)
591
693
  end
592
- let(:token_id) { 'unique-token-id' }
694
+ let(:token) { 'unique-token' }
593
695
  let(:token_response) do
594
696
  {
595
- access_token: {
596
- id: token_id
597
- }
697
+ token: token
598
698
  }.to_json
599
699
  end
600
700
 
601
701
  context 'and requests to the Ably server are mocked', :webmock do
602
702
  let!(:request_token_stub) do
603
- stub_request(:post, "#{client.endpoint}/keys/#{key_id}/requestToken").
703
+ stub_request(:post, "#{client.endpoint}/keys/#{key_name}/requestToken").
604
704
  to_return(:status => 201, :body => token_response, :headers => { 'Content-Type' => 'application/json' })
605
705
  end
606
706
  let!(:publish_message_stub) do
607
707
  stub_request(:post, "#{client.endpoint}/channels/foo/publish").
608
- with(headers: { 'Authorization' => "Bearer #{encode64(token_id)}" }).
708
+ with(headers: { 'Authorization' => "Bearer #{encode64(token)}" }).
609
709
  to_return(status: 201, body: '{}', headers: { 'Content-Type' => 'application/json' })
610
710
  end
611
711
 
@@ -616,7 +716,7 @@ describe Ably::Auth do
616
716
  end
617
717
 
618
718
  describe 'a token is created' do
619
- let(:token) { client.auth.current_token }
719
+ let(:token) { client.auth.current_token_details }
620
720
 
621
721
  it 'before a request is made' do
622
722
  expect(token).to be_nil
@@ -629,22 +729,26 @@ describe Ably::Auth do
629
729
  it 'with capability and TTL defaults' do
630
730
  client.channel('foo').publish('event', 'data')
631
731
 
632
- expect(token).to be_a(Ably::Models::Token)
633
- capability_with_str_key = Ably::Models::Token::DEFAULTS[:capability]
634
- capability = Hash[capability_with_str_key.keys.map(&:to_sym).zip(capability_with_str_key.values)]
732
+ expect(token).to be_a(Ably::Models::TokenDetails)
733
+ capability_with_str_key = Ably::Auth::TOKEN_DEFAULTS.fetch(:capability)
734
+ capability = Hash[capability_with_str_key.keys.map(&:to_s).zip(capability_with_str_key.values)]
635
735
  expect(token.capability).to eq(capability)
636
- expect(token.expires_at.to_i).to be_within(2).of(Time.now.to_i + Ably::Models::Token::DEFAULTS[:ttl])
736
+ expect(token.expires.to_i).to be_within(2).of(Time.now.to_i + Ably::Auth::TOKEN_DEFAULTS.fetch(:ttl))
637
737
  expect(token.client_id).to eq(client_id)
638
738
  end
639
739
  end
640
740
  end
641
741
  end
642
742
 
643
- context 'when using an :api_key and basic auth' do
743
+ context 'when using an :key and basic auth' do
644
744
  specify '#using_token_auth? is false' do
645
745
  expect(auth).to_not be_using_token_auth
646
746
  end
647
747
 
748
+ specify '#key attribute contains the key string' do
749
+ expect(auth.key).to eql(api_key)
750
+ end
751
+
648
752
  specify '#using_basic_auth? is true' do
649
753
  expect(auth).to be_using_basic_auth
650
754
  end