ably 0.1.5 → 0.1.6

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 (86) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +11 -1
  3. data/ably.gemspec +4 -3
  4. data/lib/ably.rb +6 -2
  5. data/lib/ably/auth.rb +24 -16
  6. data/lib/ably/exceptions.rb +16 -5
  7. data/lib/ably/{realtime/models → models}/error_info.rb +9 -11
  8. data/lib/ably/models/idiomatic_ruby_wrapper.rb +57 -26
  9. data/lib/ably/{realtime/models → models}/message.rb +45 -38
  10. data/lib/ably/{realtime/models → models}/nil_channel.rb +4 -4
  11. data/lib/ably/{rest/models/paged_resource.rb → models/paginated_resource.rb} +21 -10
  12. data/lib/ably/models/presence_message.rb +126 -0
  13. data/lib/ably/{realtime/models → models}/protocol_message.rb +76 -38
  14. data/lib/ably/models/token.rb +74 -0
  15. data/lib/ably/modules/channels_collection.rb +49 -0
  16. data/lib/ably/modules/conversions.rb +2 -0
  17. data/lib/ably/modules/event_emitter.rb +43 -8
  18. data/lib/ably/modules/event_machine_helpers.rb +1 -0
  19. data/lib/ably/modules/http_helpers.rb +9 -2
  20. data/lib/ably/modules/message_pack.rb +14 -0
  21. data/lib/ably/modules/model_common.rb +29 -0
  22. data/lib/ably/modules/{state.rb → state_emitter.rb} +8 -7
  23. data/lib/ably/realtime.rb +37 -7
  24. data/lib/ably/realtime/channel.rb +154 -31
  25. data/lib/ably/realtime/channels.rb +47 -0
  26. data/lib/ably/realtime/client.rb +39 -33
  27. data/lib/ably/realtime/client/incoming_message_dispatcher.rb +50 -21
  28. data/lib/ably/realtime/client/outgoing_message_dispatcher.rb +9 -11
  29. data/lib/ably/realtime/connection.rb +148 -79
  30. data/lib/ably/realtime/connection/connection_state_machine.rb +111 -0
  31. data/lib/ably/realtime/connection/websocket_transport.rb +161 -0
  32. data/lib/ably/realtime/presence.rb +270 -0
  33. data/lib/ably/rest.rb +14 -3
  34. data/lib/ably/rest/channel.rb +3 -3
  35. data/lib/ably/rest/channels.rb +26 -12
  36. data/lib/ably/rest/client.rb +42 -25
  37. data/lib/ably/rest/middleware/exceptions.rb +21 -23
  38. data/lib/ably/rest/middleware/external_exceptions.rb +8 -10
  39. data/lib/ably/rest/middleware/fail_if_unsupported_mime_type.rb +17 -0
  40. data/lib/ably/rest/middleware/parse_json.rb +9 -2
  41. data/lib/ably/rest/middleware/parse_message_pack.rb +6 -2
  42. data/lib/ably/rest/presence.rb +4 -4
  43. data/lib/ably/version.rb +1 -1
  44. data/spec/acceptance/realtime/channel_history_spec.rb +125 -0
  45. data/spec/acceptance/realtime/channel_spec.rb +135 -63
  46. data/spec/acceptance/realtime/connection_spec.rb +86 -0
  47. data/spec/acceptance/realtime/message_spec.rb +116 -94
  48. data/spec/acceptance/realtime/presence_history_spec.rb +0 -0
  49. data/spec/acceptance/realtime/presence_spec.rb +277 -0
  50. data/spec/acceptance/rest/auth_spec.rb +351 -347
  51. data/spec/acceptance/rest/base_spec.rb +43 -26
  52. data/spec/acceptance/rest/channel_spec.rb +88 -83
  53. data/spec/acceptance/rest/channels_spec.rb +32 -28
  54. data/spec/acceptance/rest/presence_spec.rb +83 -63
  55. data/spec/acceptance/rest/stats_spec.rb +38 -37
  56. data/spec/acceptance/rest/time_spec.rb +10 -6
  57. data/spec/integration/modules/{state_spec.rb → state_emitter_spec.rb} +16 -2
  58. data/spec/spec_helper.rb +14 -0
  59. data/spec/support/api_helper.rb +4 -0
  60. data/spec/support/model_helper.rb +28 -9
  61. data/spec/support/protocol_msgbus_helper.rb +8 -1
  62. data/spec/support/test_app.rb +24 -14
  63. data/spec/unit/{realtime → models}/error_info_spec.rb +4 -4
  64. data/spec/unit/models/idiomatic_ruby_wrapper_spec.rb +46 -9
  65. data/spec/unit/models/message_spec.rb +229 -0
  66. data/spec/unit/{rest/paged_resource_spec.rb → models/paginated_resource_spec.rb} +19 -11
  67. data/spec/unit/models/presence_message_spec.rb +230 -0
  68. data/spec/unit/models/protocol_message_spec.rb +280 -0
  69. data/spec/unit/{token_spec.rb → models/token_spec.rb} +18 -22
  70. data/spec/unit/modules/conversions_spec.rb +1 -1
  71. data/spec/unit/modules/event_emitter_spec.rb +36 -4
  72. data/spec/unit/realtime/channel_spec.rb +76 -2
  73. data/spec/unit/realtime/channels_spec.rb +50 -0
  74. data/spec/unit/realtime/client_spec.rb +31 -1
  75. data/spec/unit/realtime/connection_spec.rb +8 -15
  76. data/spec/unit/realtime/incoming_message_dispatcher_spec.rb +6 -6
  77. data/spec/unit/realtime/presence_spec.rb +100 -0
  78. data/spec/unit/rest/channels_spec.rb +48 -0
  79. metadata +72 -38
  80. data/lib/ably/realtime/models/shared.rb +0 -17
  81. data/lib/ably/rest/models/message.rb +0 -64
  82. data/lib/ably/rest/models/presence_message.rb +0 -21
  83. data/lib/ably/token.rb +0 -80
  84. data/spec/unit/realtime/message_spec.rb +0 -117
  85. data/spec/unit/realtime/protocol_message_spec.rb +0 -172
  86. data/spec/unit/rest/message_spec.rb +0 -75
@@ -14,8 +14,6 @@ describe "REST" do
14
14
  Ably::Rest::Client.new(client_options.merge(api_key: 'appid.keyuid:keysecret'))
15
15
  end
16
16
 
17
- skip '#protocol should default to :msgpack'
18
-
19
17
  context 'transport' do
20
18
  let(:now) { Time.now - 1000 }
21
19
  let(:body_value) { [as_since_epoch(now)] }
@@ -26,22 +24,8 @@ describe "REST" do
26
24
  to_return(:status => 200, :body => request_body, :headers => { 'Content-Type' => mime })
27
25
  end
28
26
 
29
- context 'when protocol is set as :json' do
30
- let(:client_options) { { protocol: :json } }
31
- let(:mime) { 'application/json' }
32
- let(:request_body) { body_value.to_json }
33
-
34
- it 'uses JSON', webmock: true do
35
- expect(client.protocol).to eql(:json)
36
- expect(client.time).to be_within(1).of(now)
37
- end
38
-
39
- skip 'uses JSON against Ably service for Auth'
40
- skip 'uses JSON against Ably service for Messages'
41
- end
42
-
43
- context 'when protocol is set as :msgpack' do
44
- let(:client_options) { { protocol: :msgpack } }
27
+ context 'when protocol is not defined it defaults to :msgpack' do
28
+ let(:client_options) { { } }
45
29
  let(:mime) { 'application/x-msgpack' }
46
30
  let(:request_body) { body_value.to_msgpack }
47
31
 
@@ -49,9 +33,40 @@ describe "REST" do
49
33
  expect(client.protocol).to eql(:msgpack)
50
34
  expect(client.time).to be_within(1).of(now)
51
35
  end
36
+ end
37
+
38
+ options = [
39
+ { protocol: :json },
40
+ { use_binary_protocol: false }
41
+ ].each do |client_option|
42
+
43
+ context "when option #{client_option} is used" do
44
+ let(:client_options) { client_option }
45
+ let(:mime) { 'application/json' }
46
+ let(:request_body) { body_value.to_json }
47
+
48
+ it 'uses JSON', webmock: true do
49
+ expect(client.protocol).to eql(:json)
50
+ expect(client.time).to be_within(1).of(now)
51
+ end
52
+ end
53
+ end
54
+
55
+ options = [
56
+ { protocol: :json },
57
+ { use_binary_protocol: false }
58
+ ].each do |client_option|
52
59
 
53
- skip 'uses MsgPack against Ably service for Auth'
54
- skip 'uses MsgPack against Ably service for Messages'
60
+ context "when option #{client_option} is used" do
61
+ let(:client_options) { { protocol: :msgpack } }
62
+ let(:mime) { 'application/x-msgpack' }
63
+ let(:request_body) { body_value.to_msgpack }
64
+
65
+ it 'uses MsgPack', webmock: true do
66
+ expect(client.protocol).to eql(:msgpack)
67
+ expect(client.time).to be_within(1).of(now)
68
+ end
69
+ end
55
70
  end
56
71
  end
57
72
  end
@@ -60,7 +75,7 @@ describe "REST" do
60
75
  it "should raise an InvalidRequest exception with a valid message" do
61
76
  invalid_client = Ably::Rest::Client.new(api_key: 'appid.keyuid:keysecret', environment: environment)
62
77
  expect { invalid_client.channel('test').publish('foo', 'choo') }.to raise_error do |error|
63
- expect(error).to be_a(Ably::Exceptions::InvalidRequest)
78
+ expect(error).to be_a(Ably::Exceptions::InvalidToken)
64
79
  expect(error.message).to match(/invalid credentials/)
65
80
  expect(error.code).to eql(40100)
66
81
  expect(error.status).to eql(401)
@@ -71,7 +86,8 @@ describe "REST" do
71
86
  let(:error_response) { '{ "error": { "statusCode": 500, "code": 50000, "message": "Internal error" } }' }
72
87
 
73
88
  before do
74
- stub_request(:get, "#{client.endpoint}/time").to_return(:status => 500, :body => error_response, :headers => { 'Content-Type' => 'application/json' })
89
+ stub_request(:get, "#{client.endpoint}/time").
90
+ to_return(:status => 500, :body => error_response, :headers => { 'Content-Type' => 'application/json' })
75
91
  end
76
92
 
77
93
  it "should raise a ServerError exception" do
@@ -81,7 +97,8 @@ describe "REST" do
81
97
 
82
98
  describe "server error", webmock: true do
83
99
  before do
84
- stub_request(:get, "#{client.endpoint}/time").to_return(:status => 500)
100
+ stub_request(:get, "#{client.endpoint}/time").
101
+ to_return(:status => 500, :headers => { 'Content-Type' => 'application/json' })
85
102
  end
86
103
 
87
104
  it "should raise a ServerError exception" do
@@ -110,9 +127,9 @@ describe "REST" do
110
127
  stub_request(:post, "#{client.endpoint}/channels/#{channel}/publish").to_return do
111
128
  @publish_attempts += 1
112
129
  if [1, 3].include?(@publish_attempts)
113
- { status: 201, :body => '[]' }
130
+ { status: 201, :body => '[]', :headers => { 'Content-Type' => 'application/json' } }
114
131
  else
115
- raise Ably::Exceptions::InvalidRequest.new('Authentication failure', status: 401, code: 40140)
132
+ raise Ably::Exceptions::InvalidRequest.new('Authentication failure', 401, 40140)
116
133
  end
117
134
  end
118
135
  end
@@ -185,7 +202,7 @@ describe "REST" do
185
202
  let(:token_request_2) { client.auth.create_token_request(token_request_options.merge(client_id: SecureRandom.hex)) }
186
203
 
187
204
  context 'when expired' do
188
- let(:token_request_options) { { key_id: key_id, key_secret: key_secret, ttl: Ably::Token::TOKEN_EXPIRY_BUFFER } }
205
+ let(:token_request_options) { { key_id: key_id, key_secret: key_secret, ttl: Ably::Models::Token::TOKEN_EXPIRY_BUFFER } }
189
206
 
190
207
  it 'creates a new token automatically when the old token expires' do
191
208
  expect { client.channel('channel_name').publish('event', 'message') }.to change { client.auth.current_token }
@@ -4,107 +4,112 @@ require "securerandom"
4
4
  describe "REST" do
5
5
  include Ably::Modules::Conversions
6
6
 
7
- let(:client) do
8
- Ably::Rest::Client.new(api_key: api_key, environment: environment)
9
- end
10
-
11
- describe "publishing messages" do
12
- let(:channel) { client.channel("test") }
13
- let(:event) { "foo" }
14
- let(:message) { "woop!" }
15
-
16
- it "should publish the message ok" do
17
- expect(channel.publish(event, message)).to eql(true)
18
- end
19
- end
7
+ [:msgpack, :json].each do |protocol|
8
+ context "over #{protocol}" do
9
+ let(:client) do
10
+ Ably::Rest::Client.new(api_key: api_key, environment: environment, protocol: protocol)
11
+ end
20
12
 
21
- describe "fetching channel history" do
22
- let(:channel) { client.channel("persisted:#{SecureRandom.hex(4)}") }
23
- let(:expected_history) do
24
- [
25
- { :name => "test1", :data => "foo" },
26
- { :name => "test2", :data => "bar" },
27
- { :name => "test3", :data => "baz" }
28
- ]
29
- end
13
+ describe "publishing messages" do
14
+ let(:channel) { client.channel("test") }
15
+ let(:event) { "foo" }
16
+ let(:message) { "woop!" }
30
17
 
31
- before(:each) do
32
- expected_history.each do |message|
33
- channel.publish(message[:name], message[:data]) || raise("Unable to publish message")
18
+ it "should publish the message ok" do
19
+ expect(channel.publish(event, message)).to eql(true)
20
+ end
34
21
  end
35
- end
36
22
 
37
- it "should return all the history for the channel" do
38
- actual_history = channel.history
39
-
40
- expect(actual_history.size).to eql(3)
23
+ describe "fetching channel history" do
24
+ let(:channel) { client.channel("persisted:#{SecureRandom.hex(4)}") }
25
+ let(:expected_history) do
26
+ [
27
+ { :name => "test1", :data => "foo" },
28
+ { :name => "test2", :data => "bar" },
29
+ { :name => "test3", :data => "baz" }
30
+ ]
31
+ end
41
32
 
42
- expected_history.each do |message|
43
- expect(actual_history).to include(Ably::Rest::Models::Message.new(message))
44
- expect(actual_history.map(&:json)).to include(message)
45
- end
46
- end
33
+ before(:each) do
34
+ expected_history.each do |message|
35
+ channel.publish(message[:name], message[:data]) || raise("Unable to publish message")
36
+ end
37
+ end
47
38
 
48
- it "should return paged history" do
49
- page_1 = channel.history(limit: 1)
50
- page_2 = page_1.next_page
51
- page_3 = page_2.next_page
39
+ it "should return all the history for the channel" do
40
+ actual_history = channel.history
52
41
 
53
- all_items = [page_1[0], page_2[0], page_3[0]]
54
- expect(all_items.uniq).to eql(all_items)
42
+ expect(actual_history.size).to eql(3)
55
43
 
56
- expect(page_1.size).to eql(1)
57
- expect(page_1).to_not be_last_page
58
- expect(page_1).to be_first_page
44
+ expected_history.each do |message|
45
+ expect(actual_history).to include(Ably::Models::Message.new(message))
46
+ expect(actual_history.map(&:hash)).to include(message)
47
+ end
48
+ end
59
49
 
60
- # Page 2
61
- expect(page_2.size).to eql(1)
62
- expect(page_2).to_not be_last_page
63
- expect(page_2).to_not be_first_page
50
+ it "should return paged history" do
51
+ page_1 = channel.history(limit: 1)
52
+ page_2 = page_1.next_page
53
+ page_3 = page_2.next_page
64
54
 
65
- # Page 3
66
- expect(page_3.size).to eql(1)
67
- expect(page_3).to be_last_page
68
- expect(page_3).to_not be_first_page
69
- end
70
- end
55
+ all_items = [page_1[0].id, page_2[0].id, page_3[0].id]
56
+ expect(all_items.uniq).to eql(all_items)
71
57
 
72
- describe "options" do
73
- let(:channel_name) { "persisted:#{SecureRandom.hex(4)}" }
74
- let(:channel) { client.channel(channel_name) }
75
- let(:endpoint) do
76
- client.endpoint.tap do |client_end_point|
77
- client_end_point.user = key_id
78
- client_end_point.password = key_secret
79
- end
80
- end
58
+ expect(page_1.size).to eql(1)
59
+ expect(page_1).to_not be_last_page
60
+ expect(page_1).to be_first_page
81
61
 
82
- [:start, :end].each do |option|
83
- describe ":{option}", webmock: true do
84
- let!(:history_stub) {
85
- stub_request(:get, "#{endpoint}/channels/#{CGI.escape(channel_name)}/messages?live=true&#{option}=#{milliseconds}").to_return(:body => '{}')
86
- }
62
+ # Page 2
63
+ expect(page_2.size).to eql(1)
64
+ expect(page_2).to_not be_last_page
65
+ expect(page_2).to_not be_first_page
87
66
 
88
- before do
89
- channel.history(options)
67
+ # Page 3
68
+ expect(page_3.size).to eql(1)
69
+ expect(page_3).to be_last_page
70
+ expect(page_3).to_not be_first_page
90
71
  end
72
+ end
91
73
 
92
- context 'with milliseconds since epoch' do
93
- let(:milliseconds) { as_since_epoch(Time.now) }
94
- let(:options) { { option => milliseconds } }
95
-
96
- specify 'are left unchanged' do
97
- expect(history_stub).to have_been_requested
74
+ describe "options" do
75
+ let(:channel_name) { "persisted:#{SecureRandom.hex(4)}" }
76
+ let(:channel) { client.channel(channel_name) }
77
+ let(:endpoint) do
78
+ client.endpoint.tap do |client_end_point|
79
+ client_end_point.user = key_id
80
+ client_end_point.password = key_secret
98
81
  end
99
82
  end
100
83
 
101
- context 'with Time' do
102
- let(:time) { Time.now }
103
- let(:milliseconds) { as_since_epoch(time) }
104
- let(:options) { { option => time } }
105
-
106
- specify 'are left unchanged' do
107
- expect(history_stub).to have_been_requested
84
+ [:start, :end].each do |option|
85
+ describe ":{option}", webmock: true do
86
+ let!(:history_stub) {
87
+ stub_request(:get, "#{endpoint}/channels/#{CGI.escape(channel_name)}/messages?live=true&#{option}=#{milliseconds}").
88
+ to_return(:body => '{}', :headers => { 'Content-Type' => 'application/json' })
89
+ }
90
+
91
+ before do
92
+ channel.history(options)
93
+ end
94
+
95
+ context 'with milliseconds since epoch' do
96
+ let(:milliseconds) { as_since_epoch(Time.now) }
97
+ let(:options) { { option => milliseconds } }
98
+
99
+ specify 'are left unchanged' do
100
+ expect(history_stub).to have_been_requested
101
+ end
102
+ end
103
+
104
+ context 'with Time' do
105
+ let(:time) { Time.now }
106
+ let(:milliseconds) { as_since_epoch(time) }
107
+ let(:options) { { option => time } }
108
+
109
+ specify 'are left unchanged' do
110
+ expect(history_stub).to have_been_requested
111
+ end
112
+ end
108
113
  end
109
114
  end
110
115
  end
@@ -2,38 +2,42 @@ require "spec_helper"
2
2
  require "securerandom"
3
3
 
4
4
  describe Ably::Rest::Channels do
5
- let(:client) do
6
- Ably::Rest::Client.new(api_key: api_key, environment: environment)
7
- end
8
- let(:channel_name) { SecureRandom.hex }
9
- let(:options) { { key: 'value' } }
5
+ [:msgpack, :json].each do |protocol|
6
+ context "over #{protocol}" do
7
+ let(:client) do
8
+ Ably::Rest::Client.new(api_key: api_key, environment: environment, protocol: protocol)
9
+ end
10
+ let(:channel_name) { SecureRandom.hex }
11
+ let(:options) { { key: 'value' } }
10
12
 
11
- shared_examples "a channel" do
12
- it "should access a channel" do
13
- expect(channel).to be_a Ably::Rest::Channel
14
- expect(channel.name).to eql(channel_name)
15
- end
13
+ shared_examples "a channel" do
14
+ it "should access a channel" do
15
+ expect(channel).to be_a Ably::Rest::Channel
16
+ expect(channel.name).to eql(channel_name)
17
+ end
16
18
 
17
- it "should allow options to be set on a channel" do
18
- expect(channel_with_options.options).to eql(options)
19
- end
20
- end
19
+ it "should allow options to be set on a channel" do
20
+ expect(channel_with_options.options).to eql(options)
21
+ end
22
+ end
21
23
 
22
- describe "using shortcut method on client" do
23
- let(:channel) { client.channel(channel_name) }
24
- let(:channel_with_options) { client.channel(channel_name, options) }
25
- it_behaves_like 'a channel'
26
- end
24
+ describe "using shortcut method on client" do
25
+ let(:channel) { client.channel(channel_name) }
26
+ let(:channel_with_options) { client.channel(channel_name, options) }
27
+ it_behaves_like 'a channel'
28
+ end
27
29
 
28
- describe "using documented .get method on client.channels" do
29
- let(:channel) { client.channels.get(channel_name) }
30
- let(:channel_with_options) { client.channels.get(channel_name, options) }
31
- it_behaves_like 'a channel'
32
- end
30
+ describe "using documented .get method on client.channels" do
31
+ let(:channel) { client.channels.get(channel_name) }
32
+ let(:channel_with_options) { client.channels.get(channel_name, options) }
33
+ it_behaves_like 'a channel'
34
+ end
33
35
 
34
- describe "using undocumented [] method on client.channels" do
35
- let(:channel) { client.channels[channel_name] }
36
- let(:channel_with_options) { client.channels[channel_name, options] }
37
- it_behaves_like 'a channel'
36
+ describe "using undocumented [] method on client.channels" do
37
+ let(:channel) { client.channels[channel_name] }
38
+ let(:channel_with_options) { client.channels[channel_name, options] }
39
+ it_behaves_like 'a channel'
40
+ end
41
+ end
38
42
  end
39
43
  end
@@ -4,85 +4,105 @@ require "securerandom"
4
4
  describe "REST" do
5
5
  include Ably::Modules::Conversions
6
6
 
7
- let(:client) do
8
- Ably::Rest::Client.new(api_key: api_key, environment: environment)
9
- end
10
-
11
- let(:fixtures) do
12
- TestApp::APP_SPEC['channels'].first['presence'].map do |fixture|
13
- IdiomaticRubyWrapper(fixture, stop_at: [:client_data])
14
- end
15
- end
16
-
17
- describe "fetching presence" do
18
- let(:channel) { client.channel("persisted:presence_fixtures") }
19
- let(:presence) { channel.presence.get }
20
-
21
- it "should return current members on the channel" do
22
- expect(presence.size).to eql(4)
7
+ [:msgpack, :json].each do |protocol|
8
+ context "over #{protocol}" do
9
+ let(:client) do
10
+ Ably::Rest::Client.new(api_key: api_key, environment: environment, protocol: protocol)
11
+ end
23
12
 
24
- fixtures.each do |fixture|
25
- presence_message = presence.find { |client| client[:client_id] == fixture[:client_id] }
26
- expect(presence_message[:client_data]).to eq(fixture[:client_data])
13
+ let(:fixtures) do
14
+ TestApp::APP_SPEC['channels'].first['presence'].map do |fixture|
15
+ IdiomaticRubyWrapper(fixture, stop_at: [:client_data])
16
+ end
27
17
  end
28
- end
29
- end
30
18
 
31
- describe "presence history" do
32
- let(:channel) { client.channel("persisted:presence_fixtures") }
33
- let(:history) { channel.presence.history }
19
+ describe "fetching presence" do
20
+ let(:channel) { client.channel("persisted:presence_fixtures") }
21
+ let(:presence) { channel.presence.get }
34
22
 
35
- it "should return recent presence activity" do
36
- expect(history.size).to eql(4)
23
+ it "returns current members on the channel" do
24
+ expect(presence.size).to eql(4)
37
25
 
38
- fixtures.each do |fixture|
39
- presence_message = history.find { |client| client[:client_id] == fixture['clientId'] }
40
- expect(presence_message[:client_data]).to eq(fixture[:client_data])
26
+ fixtures.each do |fixture|
27
+ presence_message = presence.find { |client| client.client_id == fixture[:client_id] }
28
+ expect(presence_message.client_data).to eq(fixture[:client_data])
29
+ end
30
+ end
41
31
  end
42
- end
43
- end
44
32
 
45
- describe "options" do
46
- let(:channel_name) { "persisted:#{SecureRandom.hex(4)}" }
47
- let(:presence) { client.channel(channel_name).presence }
48
- let(:user) { 'appid.keyuid' }
49
- let(:secret) { SecureRandom.hex(8) }
50
- let(:endpoint) do
51
- client.endpoint.tap do |client_end_point|
52
- client_end_point.user = user
53
- client_end_point.password = secret
54
- end
55
- end
56
- let(:client) do
57
- Ably::Rest::Client.new(api_key: "#{user}:#{secret}")
58
- end
33
+ describe "presence history" do
34
+ let(:channel) { client.channel("persisted:presence_fixtures") }
35
+ let(:history) { channel.presence.history }
59
36
 
60
- [:start, :end].each do |option|
61
- describe ":{option}", webmock: true do
62
- let!(:history_stub) {
63
- stub_request(:get, "#{endpoint}/channels/#{CGI.escape(channel_name)}/presence/history?live=true&#{option}=#{milliseconds}").to_return(:body => '{}')
64
- }
37
+ it "returns recent presence activity" do
38
+ expect(history.size).to eql(4)
65
39
 
66
- before do
67
- presence.history(options)
40
+ fixtures.each do |fixture|
41
+ presence_message = history.find { |client| client.client_id == fixture['clientId'] }
42
+ expect(presence_message.client_data).to eq(fixture[:client_data])
43
+ end
68
44
  end
69
45
 
70
- context 'with milliseconds since epoch' do
71
- let(:milliseconds) { as_since_epoch(Time.now) }
72
- let(:options) { { option => milliseconds } }
46
+ context 'with options' do
47
+ let(:page_size) { 2 }
48
+ let(:paged_history_forward) { channel.presence.history(limit: page_size, direction: :forwards) }
73
49
 
74
- specify 'are left unchanged' do
75
- expect(history_stub).to have_been_requested
50
+ it "returns recent presence activity with options passsed to Ably" do
51
+ expect(paged_history_forward).to be_a(Ably::Models::PaginatedResource)
52
+ expect(paged_history_forward.size).to eql(2)
53
+
54
+ next_page = paged_history_forward.next_page
55
+
56
+ expect(paged_history_forward.first.id).to eql(history.last.id)
57
+ expect(next_page.first.id).to eql(history[page_size].id)
76
58
  end
77
59
  end
60
+ end
78
61
 
79
- context 'with Time' do
80
- let(:time) { Time.now }
81
- let(:milliseconds) { as_since_epoch(time) }
82
- let(:options) { { option => time } }
62
+ describe "options" do
63
+ let(:channel_name) { "persisted:#{SecureRandom.hex(4)}" }
64
+ let(:presence) { client.channel(channel_name).presence }
65
+ let(:user) { 'appid.keyuid' }
66
+ let(:secret) { SecureRandom.hex(8) }
67
+ let(:endpoint) do
68
+ client.endpoint.tap do |client_end_point|
69
+ client_end_point.user = user
70
+ client_end_point.password = secret
71
+ end
72
+ end
73
+ let(:client) do
74
+ Ably::Rest::Client.new(api_key: "#{user}:#{secret}")
75
+ end
83
76
 
84
- specify 'are left unchanged' do
85
- expect(history_stub).to have_been_requested
77
+ [:start, :end].each do |option|
78
+ describe ":{option}", webmock: true do
79
+ let!(:history_stub) {
80
+ stub_request(:get, "#{endpoint}/channels/#{CGI.escape(channel_name)}/presence/history?live=true&#{option}=#{milliseconds}").
81
+ to_return(:body => '{}', :headers => { 'Content-Type' => 'application/json' })
82
+ }
83
+
84
+ before do
85
+ presence.history(options)
86
+ end
87
+
88
+ context 'with milliseconds since epoch' do
89
+ let(:milliseconds) { as_since_epoch(Time.now) }
90
+ let(:options) { { option => milliseconds } }
91
+
92
+ specify 'are left unchanged' do
93
+ expect(history_stub).to have_been_requested
94
+ end
95
+ end
96
+
97
+ context 'with Time' do
98
+ let(:time) { Time.now }
99
+ let(:milliseconds) { as_since_epoch(time) }
100
+ let(:options) { { option => time } }
101
+
102
+ specify 'are left unchanged' do
103
+ expect(history_stub).to have_been_requested
104
+ end
105
+ end
86
106
  end
87
107
  end
88
108
  end