ably 1.0.7 → 1.1.4.rc

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/.editorconfig +14 -0
  3. data/.travis.yml +10 -8
  4. data/CHANGELOG.md +58 -4
  5. data/LICENSE +1 -3
  6. data/README.md +9 -5
  7. data/Rakefile +32 -0
  8. data/SPEC.md +920 -565
  9. data/ably.gemspec +16 -11
  10. data/lib/ably/auth.rb +28 -2
  11. data/lib/ably/exceptions.rb +10 -4
  12. data/lib/ably/logger.rb +7 -1
  13. data/lib/ably/models/channel_state_change.rb +1 -1
  14. data/lib/ably/models/connection_state_change.rb +1 -1
  15. data/lib/ably/models/device_details.rb +87 -0
  16. data/lib/ably/models/device_push_details.rb +86 -0
  17. data/lib/ably/models/error_info.rb +23 -2
  18. data/lib/ably/models/idiomatic_ruby_wrapper.rb +4 -4
  19. data/lib/ably/models/protocol_message.rb +32 -2
  20. data/lib/ably/models/push_channel_subscription.rb +89 -0
  21. data/lib/ably/modules/conversions.rb +1 -1
  22. data/lib/ably/modules/encodeable.rb +1 -1
  23. data/lib/ably/modules/exception_codes.rb +128 -0
  24. data/lib/ably/modules/model_common.rb +15 -2
  25. data/lib/ably/modules/state_machine.rb +2 -2
  26. data/lib/ably/realtime.rb +1 -0
  27. data/lib/ably/realtime/auth.rb +1 -1
  28. data/lib/ably/realtime/channel.rb +24 -102
  29. data/lib/ably/realtime/channel/channel_manager.rb +2 -6
  30. data/lib/ably/realtime/channel/channel_state_machine.rb +2 -2
  31. data/lib/ably/realtime/channel/publisher.rb +74 -0
  32. data/lib/ably/realtime/channel/push_channel.rb +62 -0
  33. data/lib/ably/realtime/client.rb +91 -3
  34. data/lib/ably/realtime/client/incoming_message_dispatcher.rb +6 -2
  35. data/lib/ably/realtime/client/outgoing_message_dispatcher.rb +1 -1
  36. data/lib/ably/realtime/connection.rb +34 -20
  37. data/lib/ably/realtime/connection/connection_manager.rb +25 -9
  38. data/lib/ably/realtime/connection/websocket_transport.rb +1 -1
  39. data/lib/ably/realtime/presence.rb +4 -4
  40. data/lib/ably/realtime/presence/members_map.rb +3 -3
  41. data/lib/ably/realtime/push.rb +40 -0
  42. data/lib/ably/realtime/push/admin.rb +61 -0
  43. data/lib/ably/realtime/push/channel_subscriptions.rb +108 -0
  44. data/lib/ably/realtime/push/device_registrations.rb +105 -0
  45. data/lib/ably/rest.rb +1 -0
  46. data/lib/ably/rest/channel.rb +53 -17
  47. data/lib/ably/rest/channel/push_channel.rb +62 -0
  48. data/lib/ably/rest/client.rb +161 -35
  49. data/lib/ably/rest/middleware/fail_if_unsupported_mime_type.rb +4 -1
  50. data/lib/ably/rest/middleware/parse_message_pack.rb +17 -1
  51. data/lib/ably/rest/presence.rb +1 -0
  52. data/lib/ably/rest/push.rb +42 -0
  53. data/lib/ably/rest/push/admin.rb +54 -0
  54. data/lib/ably/rest/push/channel_subscriptions.rb +121 -0
  55. data/lib/ably/rest/push/device_registrations.rb +103 -0
  56. data/lib/ably/version.rb +7 -2
  57. data/spec/acceptance/realtime/auth_spec.rb +22 -21
  58. data/spec/acceptance/realtime/channel_history_spec.rb +26 -20
  59. data/spec/acceptance/realtime/channel_spec.rb +177 -59
  60. data/spec/acceptance/realtime/client_spec.rb +153 -0
  61. data/spec/acceptance/realtime/connection_failures_spec.rb +72 -6
  62. data/spec/acceptance/realtime/connection_spec.rb +129 -18
  63. data/spec/acceptance/realtime/message_spec.rb +36 -34
  64. data/spec/acceptance/realtime/presence_spec.rb +201 -167
  65. data/spec/acceptance/realtime/push_admin_spec.rb +736 -0
  66. data/spec/acceptance/realtime/push_spec.rb +27 -0
  67. data/spec/acceptance/rest/auth_spec.rb +4 -3
  68. data/spec/acceptance/rest/base_spec.rb +2 -2
  69. data/spec/acceptance/rest/channel_spec.rb +79 -4
  70. data/spec/acceptance/rest/channels_spec.rb +6 -0
  71. data/spec/acceptance/rest/client_spec.rb +129 -10
  72. data/spec/acceptance/rest/message_spec.rb +158 -6
  73. data/spec/acceptance/rest/push_admin_spec.rb +952 -0
  74. data/spec/acceptance/rest/push_spec.rb +25 -0
  75. data/spec/acceptance/rest/time_spec.rb +1 -1
  76. data/spec/run_parallel_tests +33 -0
  77. data/spec/spec_helper.rb +1 -1
  78. data/spec/support/debug_failure_helper.rb +9 -5
  79. data/spec/support/test_app.rb +2 -2
  80. data/spec/unit/logger_spec.rb +10 -3
  81. data/spec/unit/models/device_details_spec.rb +102 -0
  82. data/spec/unit/models/device_push_details_spec.rb +101 -0
  83. data/spec/unit/models/error_info_spec.rb +51 -3
  84. data/spec/unit/models/message_spec.rb +17 -2
  85. data/spec/unit/models/presence_message_spec.rb +1 -1
  86. data/spec/unit/models/push_channel_subscription_spec.rb +86 -0
  87. data/spec/unit/modules/enum_spec.rb +1 -1
  88. data/spec/unit/realtime/client_spec.rb +13 -1
  89. data/spec/unit/realtime/connection_spec.rb +1 -1
  90. data/spec/unit/realtime/push_channel_spec.rb +36 -0
  91. data/spec/unit/rest/channel_spec.rb +8 -1
  92. data/spec/unit/rest/client_spec.rb +30 -0
  93. data/spec/unit/rest/push_channel_spec.rb +36 -0
  94. metadata +94 -31
@@ -7,7 +7,10 @@ module Ably
7
7
  class FailIfUnsupportedMimeType < Faraday::Response::Middleware
8
8
  def on_complete(env)
9
9
  unless env.response_headers['Ably-Middleware-Parsed'] == true
10
- unless (500..599).include?(env.status)
10
+ # Ignore empty body with success status code for no body response
11
+ return if env.body.to_s.empty? && env.status == 204
12
+
13
+ unless (500..599).include?(env.status)
11
14
  raise Ably::Exceptions::InvalidResponseBody,
12
15
  "Content Type #{env.response_headers['Content-Type']} is not supported by this client library"
13
16
  end
@@ -10,6 +10,14 @@ module Ably
10
10
  env.body = parse(env.body) unless env.response_headers['Ably-Middleware-Parsed'] == true
11
11
  env.response_headers['Ably-Middleware-Parsed'] = true
12
12
  end
13
+ rescue Ably::Exceptions::InvalidResponseBody => e
14
+ debug_info = {
15
+ method: env.method,
16
+ url: env.url,
17
+ base64_body: base64_body(env.body),
18
+ response_headers: env.response_headers
19
+ }
20
+ raise Ably::Exceptions::InvalidResponseBody, "#{e.message}\nRequest env: #{debug_info}"
13
21
  end
14
22
 
15
23
  def parse(body)
@@ -18,8 +26,16 @@ module Ably
18
26
  else
19
27
  body
20
28
  end
29
+ rescue MessagePack::UnknownExtTypeError => e
30
+ raise Ably::Exceptions::InvalidResponseBody, "MessagePack::UnknownExtTypeError body could not be decoded: #{e.message}. Got Base64:\n#{base64_body(body)}"
21
31
  rescue MessagePack::MalformedFormatError => e
22
- raise Ably::Exceptions::InvalidResponseBody, "Expected MessagePack response: #{e.message}"
32
+ raise Ably::Exceptions::InvalidResponseBody, "MessagePack::MalformedFormatError body could not be decoded: #{e.message}. Got Base64:\n#{base64_body(body)}"
33
+ end
34
+
35
+ def base64_body(body)
36
+ Base64.encode64(body)
37
+ rescue => err
38
+ "[#{err.message}! Could not base64 encode body: '#{body}']"
23
39
  end
24
40
  end
25
41
  end
@@ -5,6 +5,7 @@ module Ably
5
5
 
6
6
  # {Ably::Rest::Client} for this Presence object
7
7
  # @return {Ably::Rest::Client}
8
+ # @private
8
9
  attr_reader :client
9
10
 
10
11
  # {Ably::Rest::Channel} this Presence object is associated with
@@ -0,0 +1,42 @@
1
+ require 'ably/rest/push/admin'
2
+
3
+ module Ably
4
+ module Rest
5
+ # Class providing push notification functionality
6
+ class Push
7
+ include Ably::Modules::Conversions
8
+
9
+ # @private
10
+ attr_reader :client
11
+
12
+ def initialize(client)
13
+ @client = client
14
+ end
15
+
16
+ # Admin features for push notifications like managing devices and channel subscriptions
17
+ # @return [Ably::Rest::Push::Admin]
18
+ def admin
19
+ @admin ||= Admin.new(self)
20
+ end
21
+
22
+ # Activate this device for push notifications by registering with the push transport such as GCM/APNS
23
+ #
24
+ # @note This is unsupported in the Ruby library
25
+ def activate(*arg)
26
+ raise_unsupported
27
+ end
28
+
29
+ # Deactivate this device for push notifications by removing the registration with the push transport such as GCM/APNS
30
+ #
31
+ # @note This is unsupported in the Ruby library
32
+ def deactivate(*arg)
33
+ raise_unsupported
34
+ end
35
+
36
+ private
37
+ def raise_unsupported
38
+ raise Ably::Exceptions::PushNotificationsNotSupported, 'This device does not support receiving or subscribing to push notifications. All PushChannel methods are unavailable'
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,54 @@
1
+ require 'ably/rest/push/device_registrations'
2
+ require 'ably/rest/push/channel_subscriptions'
3
+
4
+ module Ably::Rest
5
+ class Push
6
+ # Class providing push notification administrative functionality
7
+ # for registering devices and attaching to channels etc.
8
+ class Admin
9
+ include Ably::Modules::Conversions
10
+
11
+ # @api private
12
+ attr_reader :client
13
+
14
+ # @api private
15
+ attr_reader :push
16
+
17
+ def initialize(push)
18
+ @push = push
19
+ @client = push.client
20
+ end
21
+
22
+ # Publish a push message directly to a single recipient
23
+ #
24
+ # @param recipient [Hash] A recipient device, client_id or raw APNS/GCM target. Refer to push documentation
25
+ # @param data [Hash] The notification payload data and fields. Refer to push documentation
26
+ #
27
+ # @return [void]
28
+ #
29
+ def publish(recipient, data)
30
+ raise ArgumentError, "Expecting a Hash object for recipient, got #{recipient.class}" unless recipient.kind_of?(Hash)
31
+ raise ArgumentError, "Recipient data is empty. You must provide recipient details" if recipient.empty?
32
+ raise ArgumentError, "Expecting a Hash object for data, got #{data.class}" unless data.kind_of?(Hash)
33
+ raise ArgumentError, "Push data field is empty. You must provide attributes for the push notification" if data.empty?
34
+
35
+ publish_data = data.merge(recipient: IdiomaticRubyWrapper(recipient))
36
+ # Co-erce to camelCase for notitication fields which are always camelCase
37
+ publish_data[:notification] = IdiomaticRubyWrapper(data[:notification]) if publish_data[:notification].kind_of?(Hash)
38
+ client.post('/push/publish', publish_data)
39
+ end
40
+
41
+ # Manage device registrations
42
+ # @return [Ably::Rest::Push::DeviceRegistrations]
43
+ def device_registrations
44
+ @device_registrations ||= DeviceRegistrations.new(self)
45
+ end
46
+
47
+ # Manage channel subscriptions for devices or clients
48
+ # @return [Ably::Rest::Push::ChannelSubscriptions]
49
+ def channel_subscriptions
50
+ @channel_subscriptions ||= ChannelSubscriptions.new(self)
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,121 @@
1
+ module Ably::Rest
2
+ class Push
3
+ # Manage push notification channel subscriptions for devices or client identifiers
4
+ class ChannelSubscriptions
5
+ include Ably::Modules::Conversions
6
+
7
+ # @api private
8
+ attr_reader :client
9
+
10
+ # @api private
11
+ attr_reader :admin
12
+
13
+ def initialize(admin)
14
+ @admin = admin
15
+ @client = admin.client
16
+ end
17
+
18
+ # List channel subscriptions filtered by optional params
19
+ #
20
+ # @param [Hash] params the filter options for the list channel subscription request. At least one of channel, client_id or device_id is required
21
+ # @option params [String] :channel filter by realtime pub/sub channel name
22
+ # @option params [String] :client_id filter by devices registered to a client identifier. If provided with device_id param, a concat operation is used so that any device with this client_id or provided device_id is returned.
23
+ # @option params [String] :device_id filter by unique device ID. If provided with client_id param, a concat operation is used so that any device with this device_id or provided client_id is returned.
24
+ # @option params [Integer] :limit maximum number of subscriptions to retrieve up to 1,000, defaults to 100
25
+ #
26
+ # @return [Ably::Models::PaginatedResult<Ably::Models::PushChannelSubscription>] Paginated list of matching {Ably::Models::PushChannelSubscription}
27
+ #
28
+ def list(params)
29
+ raise ArgumentError, "params must be a Hash" unless params.kind_of?(Hash)
30
+
31
+ if (IdiomaticRubyWrapper(params).keys & [:channel, :client_id, :device_id]).length == 0
32
+ raise ArgumentError, "at least one channel, client_id or device_id filter param must be provided"
33
+ end
34
+
35
+ params = params.clone
36
+
37
+ paginated_options = {
38
+ coerce_into: 'Ably::Models::PushChannelSubscription',
39
+ async_blocking_operations: params.delete(:async_blocking_operations),
40
+ }
41
+
42
+ response = client.get('/push/channelSubscriptions', IdiomaticRubyWrapper(params).as_json)
43
+
44
+ Ably::Models::PaginatedResult.new(response, '', client, paginated_options)
45
+ end
46
+
47
+ # List channels with at least one subscribed device
48
+ #
49
+ # @param [Hash] params the options for the list channels request
50
+ # @option params [Integer] :limit maximum number of channels to retrieve up to 1,000, defaults to 100
51
+ #
52
+ # @return [Ably::Models::PaginatedResult<String>] Paginated list of matching {Ably::Models::PushChannelSubscription}
53
+ #
54
+ def list_channels(params = {})
55
+ params = {} if params.nil?
56
+ raise ArgumentError, "params must be a Hash" unless params.kind_of?(Hash)
57
+
58
+ params = params.clone
59
+
60
+ paginated_options = {
61
+ coerce_into: 'String',
62
+ async_blocking_operations: params.delete(:async_blocking_operations),
63
+ }
64
+
65
+ response = client.get('/push/channels', IdiomaticRubyWrapper(params).as_json)
66
+
67
+ Ably::Models::PaginatedResult.new(response, '', client, paginated_options)
68
+ end
69
+
70
+ # Save push channel subscription for a device or client ID
71
+ #
72
+ # @param [Ably::Models::PushChannelSubscription,Hash] push_channel_subscription the push channel subscription details to save
73
+ #
74
+ # @return [void]
75
+ #
76
+ def save(push_channel_subscription)
77
+ push_channel_subscription_object = PushChannelSubscription(push_channel_subscription)
78
+ raise ArgumentError, "Channel is required yet is empty" if push_channel_subscription_object.channel.to_s.empty?
79
+
80
+ client.post("/push/channelSubscriptions", push_channel_subscription_object.as_json)
81
+ end
82
+
83
+ # Remove a push channel subscription
84
+ #
85
+ # @param [Ably::Models::PushChannelSubscription,Hash] push_channel_subscription the push channel subscription details to remove
86
+ #
87
+ # @return [void]
88
+ #
89
+ def remove(push_channel_subscription)
90
+ push_channel_subscription_object = PushChannelSubscription(push_channel_subscription)
91
+ raise ArgumentError, "Channel is required yet is empty" if push_channel_subscription_object.channel.to_s.empty?
92
+ if push_channel_subscription_object.client_id.to_s.empty? && push_channel_subscription_object.device_id.to_s.empty?
93
+ raise ArgumentError, "Either client_id or device_id must be present"
94
+ end
95
+
96
+ client.delete("/push/channelSubscriptions", push_channel_subscription_object.as_json)
97
+ end
98
+
99
+ # Remove all matching push channel subscriptions
100
+ #
101
+ # @param [Hash] params the filter options for the list channel subscription request. At least one of channel, client_id or device_id is required
102
+ # @option params [String] :channel filter by realtime pub/sub channel name
103
+ # @option params [String] :client_id filter by devices registered to a client identifier. If provided with device_id param, a concat operation is used so that any device with this client_id or provided device_id is returned.
104
+ # @option params [String] :device_id filter by unique device ID. If provided with client_id param, a concat operation is used so that any device with this device_id or provided client_id is returned.
105
+ #
106
+ # @return [void]
107
+ #
108
+ def remove_where(params)
109
+ raise ArgumentError, "params must be a Hash" unless params.kind_of?(Hash)
110
+
111
+ if (IdiomaticRubyWrapper(params).keys & [:channel, :client_id, :device_id]).length == 0
112
+ raise ArgumentError, "at least one channel, client_id or device_id filter param must be provided"
113
+ end
114
+
115
+ params = params.clone
116
+
117
+ client.delete("/push/channelSubscriptions", IdiomaticRubyWrapper(params).as_json)
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,103 @@
1
+ module Ably::Rest
2
+ class Push
3
+ # Manage device registrations for push notifications
4
+ class DeviceRegistrations
5
+ include Ably::Modules::Conversions
6
+
7
+ # @api private
8
+ attr_reader :client
9
+
10
+ # @api private
11
+ attr_reader :admin
12
+
13
+ def initialize(admin)
14
+ @admin = admin
15
+ @client = admin.client
16
+ end
17
+
18
+ # Get registered device by device ID
19
+ #
20
+ # @param [String, Ably::Models::DeviceDetails] device_id the device to retrieve
21
+ #
22
+ # @return [Ably::Models::DeviceDetails] Returns {Ably::Models::DeviceDetails} if a match is found else a {Ably::Exceptions::ResourceMissing} is raised
23
+ #
24
+ def get(device_id)
25
+ device_id = device_id.id if device_id.kind_of?(Ably::Models::DeviceDetails)
26
+ raise ArgumentError, "device_id must be a string or DeviceDetails object" unless device_id.kind_of?(String)
27
+
28
+ DeviceDetails(client.get("/push/deviceRegistrations/#{device_id}").body)
29
+ end
30
+
31
+ # List registered devices filtered by optional params
32
+ #
33
+ # @param [Hash] params the filter options for the list registered device request
34
+ # @option params [String] :client_id filter by devices registered to a client identifier. Cannot be used with +device_id+ param
35
+ # @option params [String] :device_id filter by unique device ID. Cannot be used with +client_id+ param
36
+ # @option params [Integer] :limit maximum number of devices to retrieve up to 1,000, defaults to 100
37
+ #
38
+ # @return [Ably::Models::PaginatedResult<Ably::Models::DeviceDetails>] Paginated list of matching {Ably::Models::DeviceDetails}
39
+ #
40
+ def list(params = {})
41
+ params = {} if params.nil?
42
+ raise ArgumentError, "params must be a Hash" unless params.kind_of?(Hash)
43
+ raise ArgumentError, "device_id filter cannot be specified alongside a client_id filter. Use one or the other" if params[:client_id] && params[:device_id]
44
+
45
+ params = params.clone
46
+
47
+ paginated_options = {
48
+ coerce_into: 'Ably::Models::DeviceDetails',
49
+ async_blocking_operations: params.delete(:async_blocking_operations),
50
+ }
51
+
52
+ response = client.get('/push/deviceRegistrations', IdiomaticRubyWrapper(params).as_json)
53
+
54
+ Ably::Models::PaginatedResult.new(response, '', client, paginated_options)
55
+ end
56
+
57
+ # Save and register device
58
+ #
59
+ # @param [Ably::Models::DeviceDetails, Hash] device the device details to save
60
+ #
61
+ # @return [void]
62
+ #
63
+ def save(device)
64
+ device_details = DeviceDetails(device)
65
+ raise ArgumentError, "Device ID is required yet is empty" if device_details.id.nil? || device_details == ''
66
+
67
+ client.put("/push/deviceRegistrations/#{device_details.id}", device_details.as_json)
68
+ end
69
+
70
+ # Remove device
71
+ #
72
+ # @param [String, Ably::Models::DeviceDetails] device_id the device to remove
73
+ #
74
+ # @return [void]
75
+ #
76
+ def remove(device_id)
77
+ device_id = device_id.id if device_id.kind_of?(Ably::Models::DeviceDetails)
78
+ raise ArgumentError, "device_id must be a string or DeviceDetails object" unless device_id.kind_of?(String)
79
+
80
+ client.delete("/push/deviceRegistrations/#{device_id}", {})
81
+ end
82
+
83
+ # Remove device matching where params
84
+ #
85
+ # @param [Hash] params the filter params for the remove request
86
+ # @option params [String] :client_id remove devices registered to a client identifier. Cannot be used with +device_id+ param
87
+ # @option params [String] :device_id remove device with this unique device ID. Cannot be used with +client_id+ param
88
+ #
89
+ # @return [void]
90
+ #
91
+ def remove_where(params = {})
92
+ filter = if params.kind_of?(Ably::Models::DeviceDetails)
93
+ { 'deviceId' => params.id }
94
+ else
95
+ raise ArgumentError, "params must be a Hash" unless params.kind_of?(Hash)
96
+ raise ArgumentError, "device_id filter cannot be specified alongside a client_id filter. Use one or the other" if params[:client_id] && params[:device_id]
97
+ IdiomaticRubyWrapper(params).as_json
98
+ end
99
+ client.delete("/push/deviceRegistrations", filter)
100
+ end
101
+ end
102
+ end
103
+ end
@@ -1,6 +1,6 @@
1
1
  module Ably
2
- VERSION = '1.0.7'
3
- PROTOCOL_VERSION = '1.0'
2
+ VERSION = '1.1.4.rc'
3
+ PROTOCOL_VERSION = '1.1'
4
4
 
5
5
  # Allow a variant to be configured for all instances of this client library
6
6
  # such as ruby-rest-[VERSION]
@@ -13,4 +13,9 @@ module Ably
13
13
  def self.lib_variant
14
14
  @lib_variant
15
15
  end
16
+
17
+ # @api private
18
+ def self.major_minor_version_numeric
19
+ VERSION.gsub(/\.\d+$/, '').to_f
20
+ end
16
21
  end
@@ -607,16 +607,17 @@ describe Ably::Realtime::Auth, :event_machine do
607
607
 
608
608
  context 'when auth fails' do
609
609
  let(:client_options) { default_options.merge(auth_callback: basic_token_cb, log_level: :none) }
610
+ let!(:token_string) { client.rest_client.auth.request_token.token }
610
611
 
611
612
  it 'transitions the connection state to the FAILED state (#RSA15c, #RTC8a2, #RTC8a3)' do
612
613
  connection_failed = false
613
614
 
614
615
  client.connection.once(:connected) do
615
- client.auth.authorize(nil, auth_callback: lambda { |token_params| 'invalid.token:will.cause.failure' }).tap do |deferrable|
616
+ client.auth.authorize(nil, auth_callback: lambda { |token_params| "#{app_id}.invalid.token.will.cause.failure" }).tap do |deferrable|
616
617
  deferrable.errback do |error|
617
618
  EventMachine.add_timer(0.2) do
618
619
  expect(connection_failed).to eql(true)
619
- expect(error.message).to match(/Invalid accessToken/i)
620
+ expect(error.message).to match(/invalid.*accessToken/i)
620
621
  expect(error.code).to eql(40005)
621
622
  stop_reactor
622
623
  end
@@ -626,7 +627,7 @@ describe Ably::Realtime::Auth, :event_machine do
626
627
  end
627
628
 
628
629
  client.connection.once(:failed) do
629
- expect(client.connection.error_reason.message).to match(/Invalid accessToken/i)
630
+ expect(client.connection.error_reason.message).to match(/invalid.*accessToken/i)
630
631
  expect(client.connection.error_reason.code).to eql(40005)
631
632
  connection_failed = true
632
633
  end
@@ -660,17 +661,19 @@ describe Ably::Realtime::Auth, :event_machine do
660
661
  client.connection.once(:disconnected) { raise 'Upgrade does not require a disconnect' }
661
662
 
662
663
  channel = client.channels.get('foo')
663
- channel.publish('not-allowed').errback do |error|
664
- expect(error.code).to eql(40160)
665
- expect(error.message).to match(/permission denied/)
664
+ channel.attach do
665
+ channel.publish('not-allowed').errback do |error|
666
+ expect(error.code).to eql(40160)
667
+ expect(error.message).to match(/permission denied/)
666
668
 
667
- client.auth.authorize(nil, auth_callback: upgraded_token_cb)
668
- client.connection.once(:update) do
669
- expect(client.connection.error_reason).to be_nil
670
- channel.subscribe('now-allowed') do |message|
671
- stop_reactor
669
+ client.auth.authorize(nil, auth_callback: upgraded_token_cb)
670
+ client.connection.once(:update) do
671
+ expect(client.connection.error_reason).to be_nil
672
+ channel.subscribe('now-allowed') do |message|
673
+ stop_reactor
674
+ end
675
+ channel.publish 'now-allowed'
672
676
  end
673
- channel.publish 'now-allowed'
674
677
  end
675
678
  end
676
679
  end
@@ -749,11 +752,8 @@ describe Ably::Realtime::Auth, :event_machine do
749
752
  end
750
753
 
751
754
  context 'when received' do
752
- # Ably in all environments other than locla will send AUTH 30 seconds before expiry
753
- # We set the TTL to 33s and wait (3s window)
754
- # In local env, that window is 5 seconds instead of 30 seconds
755
- let(:local_offset) { ENV['ABLY_ENV'] == 'local' ? 25 : 0 }
756
- let(:client_options) { default_options.merge(use_token_auth: :true, default_token_params: { ttl: 33 - local_offset }) }
755
+ # Ably will send AUTH 30 seconds before expiry
756
+ let(:client_options) { default_options.merge(use_token_auth: :true, default_token_params: { ttl: 33 }) }
757
757
 
758
758
  it 'should immediately start a new authentication process (#RTN22)' do
759
759
  client.connection.once(:connected) do
@@ -1058,7 +1058,7 @@ describe Ably::Realtime::Auth, :event_machine do
1058
1058
 
1059
1059
  it 'disconnected includes and invalid signature message' do
1060
1060
  client.connection.once(:disconnected) do |state_change|
1061
- expect(state_change.reason.message.match(/invalid signature/i)).to_not be_nil
1061
+ expect(state_change.reason.message.match(/signature verification failed/i)).to_not be_nil
1062
1062
  expect(state_change.reason.code).to eql(40144)
1063
1063
  stop_reactor
1064
1064
  end
@@ -1111,7 +1111,7 @@ describe Ably::Realtime::Auth, :event_machine do
1111
1111
 
1112
1112
  it 'authentication fails and reason for disconnection is invalid signature' do
1113
1113
  client.connection.once(:disconnected) do |state_change|
1114
- expect(state_change.reason.message.match(/invalid signature/i)).to_not be_nil
1114
+ expect(state_change.reason.message.match(/signature verification failed/i)).to_not be_nil
1115
1115
  expect(state_change.reason.code).to eql(40144)
1116
1116
  stop_reactor
1117
1117
  end
@@ -1139,10 +1139,11 @@ describe Ably::Realtime::Auth, :event_machine do
1139
1139
  context 'when credentials are invalid' do
1140
1140
  let(:key_secret) { 'invalid' }
1141
1141
  let(:token) { Faraday.get("#{auth_url}?keyName=#{key_name}&keySecret=#{key_secret}").body }
1142
+ let(:client_options) { { token: token, environment: environment, protocol: protocol, log_level: :none } }
1142
1143
 
1143
1144
  it 'fails with an invalid signature error' do
1144
1145
  client.connection.once(:disconnected) do |state_change|
1145
- expect(state_change.reason.message.match(/invalid signature/i)).to_not be_nil
1146
+ expect(state_change.reason.message.match(/signature verification failed/i)).to_not be_nil
1146
1147
  expect(state_change.reason.code).to eql(40144)
1147
1148
  stop_reactor
1148
1149
  end
@@ -1239,7 +1240,7 @@ describe Ably::Realtime::Auth, :event_machine do
1239
1240
  Faraday.get("#{auth_url}?keyName=#{key_name}&keySecret=#{key_secret}&capability=#{URI.escape(basic_capability)}").body
1240
1241
  end
1241
1242
  end
1242
- let(:client_options) { default_options.merge(auth_callback: auth_callback) }
1243
+ let(:client_options) { default_options.merge(auth_callback: auth_callback, log_level: :error) }
1243
1244
 
1244
1245
  it 'client fails to publish to a channel with subscribe-only capability and publishes successfully on a channel with permissions' do
1245
1246
  client.connection.once(:connected) do