ably 0.1.5 → 0.1.6

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