ably-rest 0.7.1 → 0.7.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +13 -5
- data/.gitmodules +1 -1
- data/.rspec +1 -0
- data/.travis.yml +7 -3
- data/SPEC.md +495 -419
- data/ably-rest.gemspec +19 -5
- data/lib/ably-rest.rb +9 -1
- data/lib/submodules/ably-ruby/.gitignore +6 -0
- data/lib/submodules/ably-ruby/.rspec +1 -0
- data/lib/submodules/ably-ruby/.ruby-version.old +1 -0
- data/lib/submodules/ably-ruby/.travis.yml +10 -0
- data/lib/submodules/ably-ruby/Gemfile +4 -0
- data/lib/submodules/ably-ruby/LICENSE.txt +22 -0
- data/lib/submodules/ably-ruby/README.md +122 -0
- data/lib/submodules/ably-ruby/Rakefile +34 -0
- data/lib/submodules/ably-ruby/SPEC.md +1794 -0
- data/lib/submodules/ably-ruby/ably.gemspec +36 -0
- data/lib/submodules/ably-ruby/lib/ably.rb +12 -0
- data/lib/submodules/ably-ruby/lib/ably/auth.rb +438 -0
- data/lib/submodules/ably-ruby/lib/ably/exceptions.rb +69 -0
- data/lib/submodules/ably-ruby/lib/ably/logger.rb +102 -0
- data/lib/submodules/ably-ruby/lib/ably/models/error_info.rb +37 -0
- data/lib/submodules/ably-ruby/lib/ably/models/idiomatic_ruby_wrapper.rb +223 -0
- data/lib/submodules/ably-ruby/lib/ably/models/message.rb +132 -0
- data/lib/submodules/ably-ruby/lib/ably/models/message_encoders/base.rb +108 -0
- data/lib/submodules/ably-ruby/lib/ably/models/message_encoders/base64.rb +40 -0
- data/lib/submodules/ably-ruby/lib/ably/models/message_encoders/cipher.rb +83 -0
- data/lib/submodules/ably-ruby/lib/ably/models/message_encoders/json.rb +34 -0
- data/lib/submodules/ably-ruby/lib/ably/models/message_encoders/utf8.rb +26 -0
- data/lib/submodules/ably-ruby/lib/ably/models/nil_logger.rb +20 -0
- data/lib/submodules/ably-ruby/lib/ably/models/paginated_resource.rb +173 -0
- data/lib/submodules/ably-ruby/lib/ably/models/presence_message.rb +147 -0
- data/lib/submodules/ably-ruby/lib/ably/models/protocol_message.rb +210 -0
- data/lib/submodules/ably-ruby/lib/ably/models/stat.rb +161 -0
- data/lib/submodules/ably-ruby/lib/ably/models/token.rb +74 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/ably.rb +15 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/async_wrapper.rb +62 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/channels_collection.rb +69 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/conversions.rb +100 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/encodeable.rb +69 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/enum.rb +202 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/event_emitter.rb +128 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/event_machine_helpers.rb +26 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/http_helpers.rb +41 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/message_pack.rb +14 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/model_common.rb +41 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/state_emitter.rb +153 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/state_machine.rb +57 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/statesman_monkey_patch.rb +33 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/uses_state_machine.rb +74 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime.rb +64 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/channel.rb +298 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_manager.rb +92 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_state_machine.rb +69 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/channels.rb +50 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/client.rb +184 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/client/incoming_message_dispatcher.rb +184 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/client/outgoing_message_dispatcher.rb +70 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/connection.rb +445 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/connection/connection_manager.rb +368 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/connection/connection_state_machine.rb +91 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/connection/websocket_transport.rb +188 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/models/nil_channel.rb +30 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/presence.rb +564 -0
- data/lib/submodules/ably-ruby/lib/ably/rest.rb +43 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/channel.rb +104 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/channels.rb +44 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/client.rb +396 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/middleware/encoder.rb +49 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/middleware/exceptions.rb +41 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/middleware/external_exceptions.rb +24 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/middleware/fail_if_unsupported_mime_type.rb +17 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/middleware/logger.rb +58 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/middleware/parse_json.rb +27 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/middleware/parse_message_pack.rb +27 -0
- data/lib/submodules/ably-ruby/lib/ably/rest/presence.rb +92 -0
- data/lib/submodules/ably-ruby/lib/ably/util/crypto.rb +105 -0
- data/lib/submodules/ably-ruby/lib/ably/util/pub_sub.rb +43 -0
- data/lib/submodules/ably-ruby/lib/ably/version.rb +3 -0
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_history_spec.rb +154 -0
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_spec.rb +558 -0
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/client_spec.rb +119 -0
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_failures_spec.rb +575 -0
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_spec.rb +785 -0
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/message_spec.rb +457 -0
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_history_spec.rb +55 -0
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_spec.rb +1001 -0
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/stats_spec.rb +23 -0
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/time_spec.rb +27 -0
- data/lib/submodules/ably-ruby/spec/acceptance/rest/auth_spec.rb +564 -0
- data/lib/submodules/ably-ruby/spec/acceptance/rest/base_spec.rb +165 -0
- data/lib/submodules/ably-ruby/spec/acceptance/rest/channel_spec.rb +134 -0
- data/lib/submodules/ably-ruby/spec/acceptance/rest/channels_spec.rb +41 -0
- data/lib/submodules/ably-ruby/spec/acceptance/rest/client_spec.rb +273 -0
- data/lib/submodules/ably-ruby/spec/acceptance/rest/encoders_spec.rb +185 -0
- data/lib/submodules/ably-ruby/spec/acceptance/rest/message_spec.rb +247 -0
- data/lib/submodules/ably-ruby/spec/acceptance/rest/presence_spec.rb +292 -0
- data/lib/submodules/ably-ruby/spec/acceptance/rest/stats_spec.rb +172 -0
- data/lib/submodules/ably-ruby/spec/acceptance/rest/time_spec.rb +15 -0
- data/lib/submodules/ably-ruby/spec/resources/crypto-data-128.json +56 -0
- data/lib/submodules/ably-ruby/spec/resources/crypto-data-256.json +56 -0
- data/lib/submodules/ably-ruby/spec/rspec_config.rb +57 -0
- data/lib/submodules/ably-ruby/spec/shared/client_initializer_behaviour.rb +212 -0
- data/lib/submodules/ably-ruby/spec/shared/model_behaviour.rb +86 -0
- data/lib/submodules/ably-ruby/spec/shared/protocol_msgbus_behaviour.rb +36 -0
- data/lib/submodules/ably-ruby/spec/spec_helper.rb +20 -0
- data/lib/submodules/ably-ruby/spec/support/api_helper.rb +60 -0
- data/lib/submodules/ably-ruby/spec/support/event_machine_helper.rb +104 -0
- data/lib/submodules/ably-ruby/spec/support/markdown_spec_formatter.rb +118 -0
- data/lib/submodules/ably-ruby/spec/support/private_api_formatter.rb +36 -0
- data/lib/submodules/ably-ruby/spec/support/protocol_helper.rb +32 -0
- data/lib/submodules/ably-ruby/spec/support/random_helper.rb +15 -0
- data/lib/submodules/ably-ruby/spec/support/rest_testapp_before_retry.rb +15 -0
- data/lib/submodules/ably-ruby/spec/support/test_app.rb +113 -0
- data/lib/submodules/ably-ruby/spec/unit/auth_spec.rb +68 -0
- data/lib/submodules/ably-ruby/spec/unit/logger_spec.rb +146 -0
- data/lib/submodules/ably-ruby/spec/unit/models/error_info_spec.rb +18 -0
- data/lib/submodules/ably-ruby/spec/unit/models/idiomatic_ruby_wrapper_spec.rb +349 -0
- data/lib/submodules/ably-ruby/spec/unit/models/message_encoders/base64_spec.rb +181 -0
- data/lib/submodules/ably-ruby/spec/unit/models/message_encoders/cipher_spec.rb +260 -0
- data/lib/submodules/ably-ruby/spec/unit/models/message_encoders/json_spec.rb +135 -0
- data/lib/submodules/ably-ruby/spec/unit/models/message_encoders/utf8_spec.rb +56 -0
- data/lib/submodules/ably-ruby/spec/unit/models/message_spec.rb +389 -0
- data/lib/submodules/ably-ruby/spec/unit/models/paginated_resource_spec.rb +288 -0
- data/lib/submodules/ably-ruby/spec/unit/models/presence_message_spec.rb +386 -0
- data/lib/submodules/ably-ruby/spec/unit/models/protocol_message_spec.rb +315 -0
- data/lib/submodules/ably-ruby/spec/unit/models/stat_spec.rb +113 -0
- data/lib/submodules/ably-ruby/spec/unit/models/token_spec.rb +86 -0
- data/lib/submodules/ably-ruby/spec/unit/modules/async_wrapper_spec.rb +124 -0
- data/lib/submodules/ably-ruby/spec/unit/modules/conversions_spec.rb +72 -0
- data/lib/submodules/ably-ruby/spec/unit/modules/enum_spec.rb +272 -0
- data/lib/submodules/ably-ruby/spec/unit/modules/event_emitter_spec.rb +184 -0
- data/lib/submodules/ably-ruby/spec/unit/modules/state_emitter_spec.rb +283 -0
- data/lib/submodules/ably-ruby/spec/unit/realtime/channel_spec.rb +206 -0
- data/lib/submodules/ably-ruby/spec/unit/realtime/channels_spec.rb +81 -0
- data/lib/submodules/ably-ruby/spec/unit/realtime/client_spec.rb +30 -0
- data/lib/submodules/ably-ruby/spec/unit/realtime/connection_spec.rb +33 -0
- data/lib/submodules/ably-ruby/spec/unit/realtime/incoming_message_dispatcher_spec.rb +36 -0
- data/lib/submodules/ably-ruby/spec/unit/realtime/presence_spec.rb +111 -0
- data/lib/submodules/ably-ruby/spec/unit/realtime/realtime_spec.rb +9 -0
- data/lib/submodules/ably-ruby/spec/unit/realtime/websocket_transport_spec.rb +25 -0
- data/lib/submodules/ably-ruby/spec/unit/rest/channel_spec.rb +109 -0
- data/lib/submodules/ably-ruby/spec/unit/rest/channels_spec.rb +79 -0
- data/lib/submodules/ably-ruby/spec/unit/rest/client_spec.rb +53 -0
- data/lib/submodules/ably-ruby/spec/unit/rest/rest_spec.rb +10 -0
- data/lib/submodules/ably-ruby/spec/unit/util/crypto_spec.rb +87 -0
- data/lib/submodules/ably-ruby/spec/unit/util/pub_sub_spec.rb +86 -0
- metadata +182 -27
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
require 'spec_helper'
|
|
3
|
+
|
|
4
|
+
describe Ably::Rest::Client, '#stats' do
|
|
5
|
+
include Ably::Modules::Conversions
|
|
6
|
+
|
|
7
|
+
LAST_YEAR = Time.now.year - 1
|
|
8
|
+
LAST_INTERVAL = Time.new(LAST_YEAR, 2, 3, 15, 5, 0) # 3rd Feb 20(x) 16:05:00
|
|
9
|
+
|
|
10
|
+
STATS_FIXTURES = [
|
|
11
|
+
{
|
|
12
|
+
intervalId: Ably::Models::Stat.to_interval_id(LAST_INTERVAL - 120, :minute),
|
|
13
|
+
inbound: { realtime: { messages: { count: 50, data: 5000 } } },
|
|
14
|
+
outbound: { realtime: { messages: { count: 20, data: 2000 } } }
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
intervalId: Ably::Models::Stat.to_interval_id(LAST_INTERVAL - 60, :minute),
|
|
18
|
+
inbound: { realtime: { messages: { count: 60, data: 6000 } } },
|
|
19
|
+
outbound: { realtime: { messages: { count: 10, data: 1000 } } }
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
intervalId: Ably::Models::Stat.to_interval_id(LAST_INTERVAL, :minute),
|
|
23
|
+
inbound: { realtime: { messages: { count: 70, data: 7000 } } },
|
|
24
|
+
outbound: { realtime: { messages: { count: 40, data: 4000 } } },
|
|
25
|
+
persisted: { presence: { count: 20, data: 2000 } },
|
|
26
|
+
connections: { tls: { peak: 20, opened: 10 } },
|
|
27
|
+
channels: { peak: 50, opened: 30 },
|
|
28
|
+
apiRequests: { succeeded: 50, failed: 10 },
|
|
29
|
+
tokenRequests: { succeeded: 60, failed: 20 },
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
before(:context) do
|
|
34
|
+
reload_test_app # ensure no previous stats interfere
|
|
35
|
+
TestApp.instance.create_test_stats(STATS_FIXTURES)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
vary_by_protocol do
|
|
39
|
+
let(:client) { Ably::Rest::Client.new(api_key: api_key, environment: environment, protocol: protocol) }
|
|
40
|
+
|
|
41
|
+
describe 'fetching application stats' do
|
|
42
|
+
context 'by minute' do
|
|
43
|
+
let(:first_inbound_realtime_count) { STATS_FIXTURES.first[:inbound][:realtime][:messages][:count] }
|
|
44
|
+
let(:last_inbound_realtime_count) { STATS_FIXTURES.last[:inbound][:realtime][:messages][:count] }
|
|
45
|
+
|
|
46
|
+
context 'with :from set to last interval and :limit set to 1' do
|
|
47
|
+
let(:subject) { client.stats(start: as_since_epoch(LAST_INTERVAL), by: :minute, limit: 1) }
|
|
48
|
+
let(:stat) { subject.first}
|
|
49
|
+
|
|
50
|
+
it 'retrieves only one stat' do
|
|
51
|
+
expect(subject.count).to eql(1)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it 'returns all aggregated message data' do
|
|
55
|
+
expect(stat.all[:messages][:count]).to eql(70 + 40) # inbound + outbound
|
|
56
|
+
expect(stat.all[:messages][:data]).to eql(7000 + 4000) # inbound + outbound
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it 'returns inbound realtime all data' do
|
|
60
|
+
expect(stat.inbound[:realtime][:all][:count]).to eql(70)
|
|
61
|
+
expect(stat.inbound[:realtime][:all][:data]).to eql(7000)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
it 'returns inbound realtime message data' do
|
|
65
|
+
expect(stat.inbound[:realtime][:messages][:count]).to eql(70)
|
|
66
|
+
expect(stat.inbound[:realtime][:messages][:data]).to eql(7000)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
it 'returns outbound realtime all data' do
|
|
70
|
+
expect(stat.outbound[:realtime][:all][:count]).to eql(40)
|
|
71
|
+
expect(stat.outbound[:realtime][:all][:data]).to eql(4000)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
it 'returns persisted presence all data' do
|
|
75
|
+
expect(stat.persisted[:all][:count]).to eql(20)
|
|
76
|
+
expect(stat.persisted[:all][:data]).to eql(2000)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
it 'returns connections all data' do
|
|
80
|
+
expect(stat.connections[:tls][:peak]).to eql(20)
|
|
81
|
+
expect(stat.connections[:tls][:opened]).to eql(10)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
it 'returns channels all data' do
|
|
85
|
+
expect(stat.channels[:peak]).to eql(50)
|
|
86
|
+
expect(stat.channels[:opened]).to eql(30)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
it 'returns api_requests data' do
|
|
90
|
+
expect(stat.api_requests[:succeeded]).to eql(50)
|
|
91
|
+
expect(stat.api_requests[:failed]).to eql(10)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
it 'returns token_requests data' do
|
|
95
|
+
expect(stat.token_requests[:succeeded]).to eql(60)
|
|
96
|
+
expect(stat.token_requests[:failed]).to eql(20)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
it 'returns stat objects with #interval_granularity equal to :minute' do
|
|
100
|
+
expect(stat.interval_granularity).to eq(:minute)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
it 'returns stat objects with #interval_id matching :start' do
|
|
104
|
+
expect(stat.interval_id).to eql(LAST_INTERVAL.strftime('%Y-%m-%d:%H:%M'))
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
it 'returns stat objects with #interval_time matching :start Time' do
|
|
108
|
+
expect(stat.interval_time.to_i).to eql(LAST_INTERVAL.to_i)
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
context 'with :start set to first interval, :limit set to 1 and direction :forwards' do
|
|
113
|
+
let(:first_interval) { LAST_INTERVAL - 120 }
|
|
114
|
+
let(:subject) { client.stats(start: as_since_epoch(first_interval), by: :minute, direction: :forwards, limit: 1) }
|
|
115
|
+
let(:stat) { subject.first}
|
|
116
|
+
|
|
117
|
+
it 'returns the first interval stats as stats are provided forwards from :start' do
|
|
118
|
+
expect(stat.inbound[:realtime][:all][:count]).to eql(first_inbound_realtime_count)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
it 'returns 3 pages of stats' do
|
|
122
|
+
expect(subject).to be_first_page
|
|
123
|
+
expect(subject).to_not be_last_page
|
|
124
|
+
page3 = subject.next_page.next_page
|
|
125
|
+
expect(page3).to be_last_page
|
|
126
|
+
expect(page3.first.inbound[:realtime][:all][:count]).to eql(last_inbound_realtime_count)
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
context 'with :end set to last interval, :limit set to 1 and direction :backwards' do
|
|
131
|
+
let(:subject) { client.stats(:end => as_since_epoch(LAST_INTERVAL), by: :minute, direction: :backwards, limit: 1) }
|
|
132
|
+
let(:stat) { subject.first}
|
|
133
|
+
|
|
134
|
+
it 'returns the 3rd interval stats first as stats are provided backwards from :end' do
|
|
135
|
+
expect(stat.inbound[:realtime][:all][:count]).to eql(last_inbound_realtime_count)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
it 'returns 3 pages of stats' do
|
|
139
|
+
expect(subject).to be_first_page
|
|
140
|
+
expect(subject).to_not be_last_page
|
|
141
|
+
page3 = subject.next_page.next_page
|
|
142
|
+
expect(page3.first.inbound[:realtime][:all][:count]).to eql(first_inbound_realtime_count)
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
[:hour, :day, :month].each do |interval|
|
|
148
|
+
context "by #{interval}" do
|
|
149
|
+
let(:subject) { client.stats(start: as_since_epoch(LAST_INTERVAL), by: interval, direction: 'forwards', limit: 1) }
|
|
150
|
+
let(:stat) { subject.first }
|
|
151
|
+
let(:aggregate_messages_count) do
|
|
152
|
+
STATS_FIXTURES.inject(0) do |sum, fixture|
|
|
153
|
+
sum + fixture[:inbound][:realtime][:messages][:count] + fixture[:outbound][:realtime][:messages][:count]
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
let(:aggregate_messages_data) do
|
|
157
|
+
STATS_FIXTURES.inject(0) do |sum, fixture|
|
|
158
|
+
sum + fixture[:inbound][:realtime][:messages][:data] + fixture[:outbound][:realtime][:messages][:data]
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
it 'should aggregate the stats for that period' do
|
|
163
|
+
expect(subject.count).to eql(1)
|
|
164
|
+
|
|
165
|
+
expect(stat.all[:messages][:count]).to eql(aggregate_messages_count)
|
|
166
|
+
expect(stat.all[:messages][:data]).to eql(aggregate_messages_data)
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Ably::Rest::Client, '#time' do
|
|
4
|
+
vary_by_protocol do
|
|
5
|
+
let(:client) do
|
|
6
|
+
Ably::Rest::Client.new(api_key: api_key, environment: environment, protocol: protocol)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
describe 'fetching the service time' do
|
|
10
|
+
it 'should return the service time as a Time object' do
|
|
11
|
+
expect(client.time).to be_within(2).of(Time.now)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"algorithm": "aes",
|
|
3
|
+
"mode": "cbc",
|
|
4
|
+
"keylength": 128,
|
|
5
|
+
"key": "WUP6u0K7MXI5Zeo0VppPwg==",
|
|
6
|
+
"iv": "HO4cYSP8LybPYBPZPHQOtg==",
|
|
7
|
+
"items": [
|
|
8
|
+
{
|
|
9
|
+
"encoded": {
|
|
10
|
+
"name": "example",
|
|
11
|
+
"data": "The quick brown fox jumped over the lazy dog"
|
|
12
|
+
},
|
|
13
|
+
"encrypted": {
|
|
14
|
+
"name": "example",
|
|
15
|
+
"data": "HO4cYSP8LybPYBPZPHQOtmHItcxYdSvcNUC6kXVpMn0VFL+9z2/5tJ6WFbR0SBT1xhFRuJ+MeBGTU3yOY9P5ow==",
|
|
16
|
+
"encoding": "utf-8/cipher+aes-128-cbc/base64"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"encoded": {
|
|
21
|
+
"name": "example",
|
|
22
|
+
"data": "AAECAwQFBgcICQoLDA0ODw==",
|
|
23
|
+
"encoding": "base64"
|
|
24
|
+
},
|
|
25
|
+
"encrypted": {
|
|
26
|
+
"name": "example",
|
|
27
|
+
"data": "HO4cYSP8LybPYBPZPHQOtuB3dfKG08yw7J4qx3kkjxdW0eoZv+nGAp76OKqYQ327",
|
|
28
|
+
"encoding": "cipher+aes-128-cbc/base64"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"encoded": {
|
|
33
|
+
"name": "example",
|
|
34
|
+
"data": "{\"example\":{\"json\":\"Object\"}}",
|
|
35
|
+
"encoding": "json"
|
|
36
|
+
},
|
|
37
|
+
"encrypted": {
|
|
38
|
+
"name": "example",
|
|
39
|
+
"data": "HO4cYSP8LybPYBPZPHQOtuD53yrD3YV3NBoTEYBh4U0N1QXHbtkfsDfTspKeLQFt",
|
|
40
|
+
"encoding": "json/utf-8/cipher+aes-128-cbc/base64"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"encoded": {
|
|
45
|
+
"name": "example",
|
|
46
|
+
"data": "[\"example\",\"json\",\"array\"]",
|
|
47
|
+
"encoding": "json"
|
|
48
|
+
},
|
|
49
|
+
"encrypted": {
|
|
50
|
+
"name": "example",
|
|
51
|
+
"data": "HO4cYSP8LybPYBPZPHQOtvmStzmExkdjvrn51J6cmaTZrGl+EsJ61sgxmZ6j6jcA",
|
|
52
|
+
"encoding": "json/utf-8/cipher+aes-128-cbc/base64"
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
]
|
|
56
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"algorithm": "aes",
|
|
3
|
+
"mode": "cbc",
|
|
4
|
+
"keylength": 256,
|
|
5
|
+
"key": "o9qXZoPGDNla50VnRwH7cGqIrpyagTxGsRgimKJbY40=",
|
|
6
|
+
"iv": "NMDl1Acnel8HVdu1cEWdrw==",
|
|
7
|
+
"items": [
|
|
8
|
+
{
|
|
9
|
+
"encoded": {
|
|
10
|
+
"name": "example",
|
|
11
|
+
"data": "The quick brown fox jumped over the lazy dog"
|
|
12
|
+
},
|
|
13
|
+
"encrypted": {
|
|
14
|
+
"name": "example",
|
|
15
|
+
"data": "NMDl1Acnel8HVdu1cEWdr9CGPYFoBoLgJCzoybbQbnyfwx3UQ8CGuKyP/g56Za/JB3xW6XGkNzrHYvZwad4fvA==",
|
|
16
|
+
"encoding": "utf-8/cipher+aes-256-cbc/base64"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"encoded": {
|
|
21
|
+
"name": "example",
|
|
22
|
+
"data": "AAECAwQFBgcICQoLDA0ODw==",
|
|
23
|
+
"encoding": "base64"
|
|
24
|
+
},
|
|
25
|
+
"encrypted": {
|
|
26
|
+
"name": "example",
|
|
27
|
+
"data": "NMDl1Acnel8HVdu1cEWdr8UFEi56Ms0zPHszbppM61BC8Yf6ndq+kiCj9xXW97/O",
|
|
28
|
+
"encoding": "cipher+aes-256-cbc/base64"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"encoded": {
|
|
33
|
+
"name": "example",
|
|
34
|
+
"data": "{\"example\":{\"json\":\"Object\"}}",
|
|
35
|
+
"encoding": "json"
|
|
36
|
+
},
|
|
37
|
+
"encrypted": {
|
|
38
|
+
"name": "example",
|
|
39
|
+
"data": "NMDl1Acnel8HVdu1cEWdr21pS5//hdtQf3QqQzZM/jWAtn09Vh52E6jMdC3mWS98",
|
|
40
|
+
"encoding": "json/utf-8/cipher+aes-256-cbc/base64"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"encoded": {
|
|
45
|
+
"name": "example",
|
|
46
|
+
"data": "[\"example\",\"json\",\"array\"]",
|
|
47
|
+
"encoding": "json"
|
|
48
|
+
},
|
|
49
|
+
"encrypted": {
|
|
50
|
+
"name": "example",
|
|
51
|
+
"data": "NMDl1Acnel8HVdu1cEWdr4J5sVAFpnXsz0fTtsuwOaTRU+6P5GaWlNjePWwiOZCQ",
|
|
52
|
+
"encoding": "json/utf-8/cipher+aes-256-cbc/base64"
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
]
|
|
56
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# This file was generated by the `rspec --init` command. Conventionally, all
|
|
2
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
|
3
|
+
# Require this file using `require "spec_helper"` to ensure that it is only
|
|
4
|
+
# loaded once.
|
|
5
|
+
#
|
|
6
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
|
7
|
+
|
|
8
|
+
require 'rspec/retry'
|
|
9
|
+
|
|
10
|
+
RSpec.configure do |config|
|
|
11
|
+
config.run_all_when_everything_filtered = true
|
|
12
|
+
config.filter_run :focus
|
|
13
|
+
|
|
14
|
+
config.mock_with :rspec do |mocks|
|
|
15
|
+
# This option should be set when all dependencies are being loaded
|
|
16
|
+
# before a spec run, as is the case in a typical spec helper. It will
|
|
17
|
+
# cause any verifying double instantiation for a class that does not
|
|
18
|
+
# exist to raise, protecting against incorrectly spelt names.
|
|
19
|
+
mocks.verify_doubled_constant_names = true
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Run specs in random order to surface order dependencies. If you find an
|
|
23
|
+
# order dependency and want to debug it, you can fix the order by providing
|
|
24
|
+
# the seed, which is printed after each run.
|
|
25
|
+
# --seed 1234
|
|
26
|
+
config.order = 'random'
|
|
27
|
+
|
|
28
|
+
config.before(:example) do
|
|
29
|
+
WebMock.disable!
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
config.before(:example, :webmock) do
|
|
33
|
+
allow(TestApp).to receive(:instance).and_return(instance_double('TestApp',
|
|
34
|
+
app_id: 'app_id',
|
|
35
|
+
key_id: 'app_id.key_id',
|
|
36
|
+
api_key: 'app_id.key_id:secret',
|
|
37
|
+
environment: 'sandbox'
|
|
38
|
+
))
|
|
39
|
+
WebMock.enable!
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
if defined?(EventMachine)
|
|
43
|
+
config.before(:example) do
|
|
44
|
+
# Ensure EventMachine shutdown hooks are deregistered for every test
|
|
45
|
+
EventMachine.instance_variable_set '@tails', []
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
config.add_formatter Ably::RSpec::PrivateApiFormatter
|
|
50
|
+
|
|
51
|
+
if ENV['RSPEC_RETRY']
|
|
52
|
+
puts 'Running tests using RSpec retry'
|
|
53
|
+
config.verbose_retry = true # show retry status in spec process
|
|
54
|
+
config.default_retry_count = 3
|
|
55
|
+
config.default_sleep_interval = 2
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
shared_examples 'a client initializer' do
|
|
4
|
+
def subdomain
|
|
5
|
+
if rest?
|
|
6
|
+
'rest'
|
|
7
|
+
else
|
|
8
|
+
'realtime'
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def protocol
|
|
13
|
+
if rest?
|
|
14
|
+
'http'
|
|
15
|
+
else
|
|
16
|
+
'ws'
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def rest?
|
|
21
|
+
subject.kind_of?(Ably::Rest::Client)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
context 'with invalid arguments' do
|
|
25
|
+
context 'empty hash' do
|
|
26
|
+
let(:client_options) { Hash.new }
|
|
27
|
+
|
|
28
|
+
it 'raises an exception' do
|
|
29
|
+
expect { subject }.to raise_error(ArgumentError, /api_key is missing/)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
context 'nil' do
|
|
34
|
+
let(:client_options) { nil }
|
|
35
|
+
|
|
36
|
+
it 'raises an exception' do
|
|
37
|
+
expect { subject }.to raise_error(ArgumentError, /Options Hash is expected/)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
context 'api_key: "invalid"' do
|
|
42
|
+
let(:client_options) { { api_key: 'invalid' } }
|
|
43
|
+
|
|
44
|
+
it 'raises an exception' do
|
|
45
|
+
expect { subject }.to raise_error(ArgumentError, /api_key is invalid/)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
context 'api_key: "invalid:asdad"' do
|
|
50
|
+
let(:client_options) { { api_key: 'invalid:asdad' } }
|
|
51
|
+
|
|
52
|
+
it 'raises an exception' do
|
|
53
|
+
expect { subject }.to raise_error(ArgumentError, /api_key is invalid/)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
context 'api_key and key_id' do
|
|
58
|
+
let(:client_options) { { api_key: 'appid.keyuid:keysecret', key_id: 'invalid' } }
|
|
59
|
+
|
|
60
|
+
it 'raises an exception' do
|
|
61
|
+
expect { subject }.to raise_error(ArgumentError, /api_key and key_id or key_secret are mutually exclusive/)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
context 'api_key and key_secret' do
|
|
66
|
+
let(:client_options) { { api_key: 'appid.keyuid:keysecret', key_secret: 'invalid' } }
|
|
67
|
+
|
|
68
|
+
it 'raises an exception' do
|
|
69
|
+
expect { subject }.to raise_error(ArgumentError, /api_key and key_id or key_secret are mutually exclusive/)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
context 'client_id as only option' do
|
|
74
|
+
let(:client_options) { { client_id: 'valid' } }
|
|
75
|
+
|
|
76
|
+
it 'requires a valid key' do
|
|
77
|
+
expect { subject }.to raise_error(ArgumentError, /client_id cannot be provided without a complete API key/)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
context 'with valid arguments' do
|
|
83
|
+
let(:default_options) { { api_key: 'appid.keyuid:keysecret' } }
|
|
84
|
+
let(:client_options) { default_options }
|
|
85
|
+
|
|
86
|
+
context 'api_key only' do
|
|
87
|
+
it 'connects to the Ably service' do
|
|
88
|
+
expect { subject }.to_not raise_error
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
context 'key_id and key_secret' do
|
|
93
|
+
let(:client_options) { { key_id: 'id', key_secret: 'secret' } }
|
|
94
|
+
|
|
95
|
+
it 'constructs an api_key' do
|
|
96
|
+
expect(subject.auth.api_key).to eql('id:secret')
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
context 'with a string key instead of options hash' do
|
|
101
|
+
let(:client_options) { 'app.key:secret' }
|
|
102
|
+
|
|
103
|
+
it 'sets the api_key' do
|
|
104
|
+
expect(subject.auth.api_key).to eql(client_options)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
it 'sets the key_id' do
|
|
108
|
+
expect(subject.auth.key_id).to eql('app.key')
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
it 'sets the key_secret' do
|
|
112
|
+
expect(subject.auth.key_secret).to eql('secret')
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
context 'with token' do
|
|
117
|
+
let(:client_options) { { token_id: 'token' } }
|
|
118
|
+
|
|
119
|
+
it 'sets the token_id' do
|
|
120
|
+
expect(subject.auth.token_id).to eql('token')
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
context 'endpoint' do
|
|
125
|
+
it 'defaults to production' do
|
|
126
|
+
expect(subject.endpoint.to_s).to eql("#{protocol}s://#{subdomain}.ably.io")
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
context 'with environment option' do
|
|
130
|
+
let(:client_options) { default_options.merge(environment: 'sandbox') }
|
|
131
|
+
|
|
132
|
+
it 'uses an alternate endpoint' do
|
|
133
|
+
expect(subject.endpoint.to_s).to eql("#{protocol}s://sandbox-#{subdomain}.ably.io")
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
context 'tls' do
|
|
139
|
+
context 'set to false' do
|
|
140
|
+
let(:client_options) { default_options.merge(tls: false) }
|
|
141
|
+
|
|
142
|
+
it 'uses plain text' do
|
|
143
|
+
expect(subject.use_tls?).to eql(false)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
it 'uses HTTP' do
|
|
147
|
+
expect(subject.endpoint.to_s).to eql("#{protocol}://#{subdomain}.ably.io")
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
it 'defaults to TLS' do
|
|
152
|
+
expect(subject.use_tls?).to eql(true)
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
context 'logger' do
|
|
157
|
+
context 'default' do
|
|
158
|
+
it 'uses Ruby Logger' do
|
|
159
|
+
expect(subject.logger.logger).to be_a(::Logger)
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
it 'specifies Logger::ERROR log level' do
|
|
163
|
+
expect(subject.logger.log_level).to eql(::Logger::ERROR)
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
context 'with log_level :none' do
|
|
168
|
+
let(:client_options) { default_options.merge(log_level: :none) }
|
|
169
|
+
|
|
170
|
+
it 'silences all logging with a NilLogger' do
|
|
171
|
+
expect(subject.logger.logger.class).to eql(Ably::Models::NilLogger)
|
|
172
|
+
expect(subject.logger.log_level).to eql(:none)
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
context 'with custom logger and log_level' do
|
|
177
|
+
let(:custom_logger) do
|
|
178
|
+
Class.new do
|
|
179
|
+
extend Forwardable
|
|
180
|
+
def initialize
|
|
181
|
+
@logger = Logger.new(STDOUT)
|
|
182
|
+
end
|
|
183
|
+
def_delegators :@logger, :fatal, :error, :warn, :info, :debug, :level, :level=
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
let(:client_options) { default_options.merge(logger: custom_logger.new, log_level: Logger::DEBUG) }
|
|
187
|
+
|
|
188
|
+
it 'uses the custom logger' do
|
|
189
|
+
expect(subject.logger.logger.class).to eql(custom_logger)
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
it 'sets the custom log level' do
|
|
193
|
+
expect(subject.logger.log_level).to eql(Logger::DEBUG)
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
context 'delegators' do
|
|
200
|
+
let(:client_options) { 'app.key:secret' }
|
|
201
|
+
|
|
202
|
+
it 'delegates :client_id to .auth' do
|
|
203
|
+
expect(subject.auth).to receive(:client_id).and_return('john')
|
|
204
|
+
expect(subject.client_id).to eql('john')
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
it 'delegates :auth_options to .auth' do
|
|
208
|
+
expect(subject.auth).to receive(:auth_options).and_return({ option: 1 })
|
|
209
|
+
expect(subject.auth_options).to eql({ option: 1 })
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
end
|