copycopter_client 1.1.2 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/.travis.yml +9 -0
- data/Appraisals +10 -10
- data/Gemfile +1 -1
- data/Gemfile.lock +1 -1
- data/MIT-LICENSE +1 -1
- data/README.md +44 -37
- data/Rakefile +5 -3
- data/copycopter_client.gemspec +27 -30
- data/features/step_definitions/rails_steps.rb +1 -0
- data/gemfiles/2.3.gemfile +1 -1
- data/gemfiles/2.3.gemfile.lock +2 -2
- data/gemfiles/3.0.gemfile +1 -1
- data/gemfiles/3.0.gemfile.lock +29 -29
- data/gemfiles/3.1.gemfile +1 -1
- data/gemfiles/3.1.gemfile.lock +69 -67
- data/lib/copycopter_client.rb +14 -4
- data/lib/copycopter_client/cache.rb +53 -15
- data/lib/copycopter_client/client.rb +20 -17
- data/lib/copycopter_client/configuration.rb +30 -26
- data/lib/copycopter_client/version.rb +2 -3
- data/lib/tasks/copycopter_client_tasks.rake +13 -0
- data/spec/copycopter_client/cache_spec.rb +65 -0
- data/spec/copycopter_client/client_spec.rb +22 -23
- data/spec/copycopter_client/configuration_spec.rb +75 -77
- data/spec/spec_helper.rb +5 -5
- data/spec/support/fake_client.rb +6 -2
- data/spec/support/fake_copycopter_app.rb +41 -30
- metadata +152 -228
- data/.bundle/config +0 -2
- data/AddTrustExternalCARoot.crt +0 -25
- data/CONTRIBUTING.md +0 -38
@@ -18,7 +18,7 @@ describe CopycopterClient do
|
|
18
18
|
build_client(config)
|
19
19
|
end
|
20
20
|
|
21
|
-
describe
|
21
|
+
describe 'opening a connection' do
|
22
22
|
let(:config) { CopycopterClient::Configuration.new }
|
23
23
|
let(:http) { Net::HTTP.new(config.host, config.port) }
|
24
24
|
|
@@ -26,37 +26,36 @@ describe CopycopterClient do
|
|
26
26
|
Net::HTTP.stubs(:new => http)
|
27
27
|
end
|
28
28
|
|
29
|
-
it
|
29
|
+
it 'should timeout when connecting' do
|
30
30
|
project = add_project
|
31
31
|
client = build_client(:api_key => project.api_key, :http_open_timeout => 4)
|
32
32
|
client.download { |ignore| }
|
33
33
|
http.open_timeout.should == 4
|
34
34
|
end
|
35
35
|
|
36
|
-
it
|
36
|
+
it 'should timeout when reading' do
|
37
37
|
project = add_project
|
38
38
|
client = build_client(:api_key => project.api_key, :http_read_timeout => 4)
|
39
39
|
client.download { |ignore| }
|
40
40
|
http.read_timeout.should == 4
|
41
41
|
end
|
42
42
|
|
43
|
-
it
|
43
|
+
it 'uses verified ssl when secure' do
|
44
44
|
project = add_project
|
45
45
|
client = build_client(:api_key => project.api_key, :secure => true)
|
46
46
|
client.download { |ignore| }
|
47
47
|
http.use_ssl?.should == true
|
48
48
|
http.verify_mode.should == OpenSSL::SSL::VERIFY_PEER
|
49
|
-
http.ca_file.should == CopycopterClient::Configuration::CA_FILE
|
50
49
|
end
|
51
50
|
|
52
|
-
it
|
51
|
+
it 'does not use ssl when insecure' do
|
53
52
|
project = add_project
|
54
53
|
client = build_client(:api_key => project.api_key, :secure => false)
|
55
54
|
client.download { |ignore| }
|
56
55
|
http.use_ssl?.should == false
|
57
56
|
end
|
58
57
|
|
59
|
-
it
|
58
|
+
it 'wraps HTTP errors with ConnectionError' do
|
60
59
|
errors = [
|
61
60
|
Timeout::Error.new,
|
62
61
|
Errno::EINVAL.new,
|
@@ -81,39 +80,39 @@ describe CopycopterClient do
|
|
81
80
|
end
|
82
81
|
end
|
83
82
|
|
84
|
-
it
|
83
|
+
it 'handles 500 errors from downloads with ConnectionError' do
|
85
84
|
client = build_client(:api_key => 'raise_error')
|
86
85
|
expect { client.download { |ignore| } }.
|
87
86
|
to raise_error(CopycopterClient::ConnectionError)
|
88
87
|
end
|
89
88
|
|
90
|
-
it
|
89
|
+
it 'handles 500 errors from uploads with ConnectionError' do
|
91
90
|
client = build_client(:api_key => 'raise_error')
|
92
91
|
expect { client.upload({}) }.to raise_error(CopycopterClient::ConnectionError)
|
93
92
|
end
|
94
93
|
|
95
|
-
it
|
94
|
+
it 'handles 404 errors from downloads with ConnectionError' do
|
96
95
|
client = build_client(:api_key => 'bogus')
|
97
96
|
expect { client.download { |ignore| } }.
|
98
97
|
to raise_error(CopycopterClient::InvalidApiKey)
|
99
98
|
end
|
100
99
|
|
101
|
-
it
|
100
|
+
it 'handles 404 errors from uploads with ConnectionError' do
|
102
101
|
client = build_client(:api_key => 'bogus')
|
103
102
|
expect { client.upload({}) }.to raise_error(CopycopterClient::InvalidApiKey)
|
104
103
|
end
|
105
104
|
end
|
106
105
|
|
107
|
-
it
|
106
|
+
it 'downloads published blurbs for an existing project' do
|
108
107
|
project = add_project
|
109
108
|
project.update({
|
110
109
|
'draft' => {
|
111
|
-
'key.one' =>
|
112
|
-
'key.three' =>
|
110
|
+
'key.one' => 'unexpected one',
|
111
|
+
'key.three' => 'unexpected three'
|
113
112
|
},
|
114
113
|
'published' => {
|
115
|
-
'key.one' =>
|
116
|
-
'key.two' =>
|
114
|
+
'key.one' => 'expected one',
|
115
|
+
'key.two' => 'expected two'
|
117
116
|
}
|
118
117
|
})
|
119
118
|
client = build_client(:api_key => project.api_key, :public => true)
|
@@ -127,23 +126,23 @@ describe CopycopterClient do
|
|
127
126
|
}
|
128
127
|
end
|
129
128
|
|
130
|
-
it
|
129
|
+
it 'logs that it performed a download' do
|
131
130
|
logger = FakeLogger.new
|
132
131
|
client = build_client_with_project(:logger => logger)
|
133
132
|
client.download { |ignore| }
|
134
|
-
logger.should have_entry(:info,
|
133
|
+
logger.should have_entry(:info, 'Downloaded translations')
|
135
134
|
end
|
136
135
|
|
137
|
-
it
|
136
|
+
it 'downloads draft blurbs for an existing project' do
|
138
137
|
project = add_project
|
139
138
|
project.update({
|
140
139
|
'draft' => {
|
141
|
-
'key.one' =>
|
142
|
-
'key.two' =>
|
140
|
+
'key.one' => 'expected one',
|
141
|
+
'key.two' => 'expected two'
|
143
142
|
},
|
144
143
|
'published' => {
|
145
|
-
'key.one' =>
|
146
|
-
'key.three' =>
|
144
|
+
'key.one' => 'unexpected one',
|
145
|
+
'key.three' => 'unexpected three'
|
147
146
|
}
|
148
147
|
})
|
149
148
|
client = build_client(:api_key => project.api_key, :public => false)
|
@@ -25,48 +25,42 @@ describe CopycopterClient::Configuration do
|
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
|
-
it { should have_config_option(:proxy_host).
|
29
|
-
it { should have_config_option(:proxy_port).
|
30
|
-
it { should have_config_option(:proxy_user).
|
31
|
-
it { should have_config_option(:proxy_pass).
|
32
|
-
it { should have_config_option(:environment_name).
|
33
|
-
it { should have_config_option(:client_version).
|
34
|
-
it { should have_config_option(:client_name).
|
35
|
-
it { should have_config_option(:client_url).
|
36
|
-
it { should have_config_option(:secure).
|
37
|
-
it { should have_config_option(:host).
|
38
|
-
it { should have_config_option(:http_open_timeout).
|
39
|
-
it { should have_config_option(:http_read_timeout).
|
40
|
-
it { should have_config_option(:port).
|
28
|
+
it { should have_config_option(:proxy_host).overridable.default(nil) }
|
29
|
+
it { should have_config_option(:proxy_port).overridable.default(nil) }
|
30
|
+
it { should have_config_option(:proxy_user).overridable.default(nil) }
|
31
|
+
it { should have_config_option(:proxy_pass).overridable.default(nil) }
|
32
|
+
it { should have_config_option(:environment_name).overridable.default(nil) }
|
33
|
+
it { should have_config_option(:client_version).overridable.default(CopycopterClient::VERSION) }
|
34
|
+
it { should have_config_option(:client_name).overridable.default('Copycopter Client') }
|
35
|
+
it { should have_config_option(:client_url).overridable.default('http://copycopter.com') }
|
36
|
+
it { should have_config_option(:secure).overridable.default(true) }
|
37
|
+
it { should have_config_option(:host).overridable.default('copycopter.com') }
|
38
|
+
it { should have_config_option(:http_open_timeout).overridable.default(2) }
|
39
|
+
it { should have_config_option(:http_read_timeout).overridable.default(5) }
|
40
|
+
it { should have_config_option(:port).overridable }
|
41
41
|
it { should have_config_option(:development_environments).overridable }
|
42
|
-
it { should have_config_option(:api_key).
|
43
|
-
it { should have_config_option(:polling_delay).
|
44
|
-
it { should have_config_option(:framework).
|
45
|
-
it { should have_config_option(:middleware).
|
46
|
-
it { should have_config_option(:client).
|
47
|
-
it { should have_config_option(:cache).
|
48
|
-
|
49
|
-
it
|
50
|
-
should have_config_option(:ca_file).
|
51
|
-
overridable.
|
52
|
-
default(File.join(PROJECT_ROOT, "AddTrustExternalCARoot.crt"))
|
53
|
-
end
|
54
|
-
|
55
|
-
it "should provide default values for secure connections" do
|
42
|
+
it { should have_config_option(:api_key).overridable }
|
43
|
+
it { should have_config_option(:polling_delay).overridable.default(300) }
|
44
|
+
it { should have_config_option(:framework).overridable }
|
45
|
+
it { should have_config_option(:middleware).overridable }
|
46
|
+
it { should have_config_option(:client).overridable }
|
47
|
+
it { should have_config_option(:cache).overridable }
|
48
|
+
|
49
|
+
it 'should provide default values for secure connections' do
|
56
50
|
config = CopycopterClient::Configuration.new
|
57
51
|
config.secure = true
|
58
52
|
config.port.should == 443
|
59
53
|
config.protocol.should == 'https'
|
60
54
|
end
|
61
55
|
|
62
|
-
it
|
56
|
+
it 'should provide default values for insecure connections' do
|
63
57
|
config = CopycopterClient::Configuration.new
|
64
58
|
config.secure = false
|
65
59
|
config.port.should == 80
|
66
60
|
config.protocol.should == 'http'
|
67
61
|
end
|
68
62
|
|
69
|
-
it
|
63
|
+
it 'should not cache inferred ports' do
|
70
64
|
config = CopycopterClient::Configuration.new
|
71
65
|
config.secure = false
|
72
66
|
config.port
|
@@ -74,42 +68,44 @@ describe CopycopterClient::Configuration do
|
|
74
68
|
config.port.should == 443
|
75
69
|
end
|
76
70
|
|
77
|
-
it
|
71
|
+
it 'should act like a hash' do
|
78
72
|
config = CopycopterClient::Configuration.new
|
79
73
|
hash = config.to_hash
|
74
|
+
|
80
75
|
[:api_key, :environment_name, :host, :http_open_timeout,
|
81
76
|
:http_read_timeout, :client_name, :client_url, :client_version, :port,
|
82
77
|
:protocol, :proxy_host, :proxy_pass, :proxy_port, :proxy_user, :secure,
|
83
78
|
:development_environments, :logger, :framework, :ca_file].each do |option|
|
84
79
|
hash[option].should == config[option]
|
85
80
|
end
|
81
|
+
|
86
82
|
hash[:public].should == config.public?
|
87
83
|
end
|
88
84
|
|
89
|
-
it
|
85
|
+
it 'should be mergable' do
|
90
86
|
config = CopycopterClient::Configuration.new
|
91
87
|
hash = config.to_hash
|
92
88
|
config.merge(:key => 'value').should == hash.merge(:key => 'value')
|
93
89
|
end
|
94
90
|
|
95
|
-
it
|
91
|
+
it 'should use development and staging as development environments by default' do
|
96
92
|
config = CopycopterClient::Configuration.new
|
97
93
|
config.development_environments.should =~ %w(development staging)
|
98
94
|
end
|
99
95
|
|
100
|
-
it
|
96
|
+
it 'should use test and cucumber as test environments by default' do
|
101
97
|
config = CopycopterClient::Configuration.new
|
102
98
|
config.test_environments.should =~ %w(test cucumber)
|
103
99
|
end
|
104
100
|
|
105
|
-
it
|
101
|
+
it 'should be test in a test environment' do
|
106
102
|
config = CopycopterClient::Configuration.new
|
107
103
|
config.test_environments = %w(test)
|
108
104
|
config.environment_name = 'test'
|
109
105
|
config.should be_test
|
110
106
|
end
|
111
107
|
|
112
|
-
it
|
108
|
+
it 'should be public in a public environment' do
|
113
109
|
config = CopycopterClient::Configuration.new
|
114
110
|
config.development_environments = %w(development)
|
115
111
|
config.environment_name = 'production'
|
@@ -117,7 +113,7 @@ describe CopycopterClient::Configuration do
|
|
117
113
|
config.should_not be_development
|
118
114
|
end
|
119
115
|
|
120
|
-
it
|
116
|
+
it 'should be development in a development environment' do
|
121
117
|
config = CopycopterClient::Configuration.new
|
122
118
|
config.development_environments = %w(staging)
|
123
119
|
config.environment_name = 'staging'
|
@@ -125,13 +121,14 @@ describe CopycopterClient::Configuration do
|
|
125
121
|
config.should_not be_public
|
126
122
|
end
|
127
123
|
|
128
|
-
it
|
124
|
+
it 'should be public without an environment name' do
|
129
125
|
config = CopycopterClient::Configuration.new
|
130
126
|
config.should be_public
|
131
127
|
end
|
132
128
|
|
133
|
-
it
|
129
|
+
it 'should yield and save a configuration when configuring' do
|
134
130
|
yielded_configuration = nil
|
131
|
+
|
135
132
|
CopycopterClient.configure(false) do |config|
|
136
133
|
yielded_configuration = config
|
137
134
|
end
|
@@ -140,49 +137,50 @@ describe CopycopterClient::Configuration do
|
|
140
137
|
CopycopterClient.configuration.should == yielded_configuration
|
141
138
|
end
|
142
139
|
|
143
|
-
it
|
140
|
+
it 'does not apply the configuration when asked not to' do
|
144
141
|
logger = FakeLogger.new
|
145
142
|
CopycopterClient.configure(false) { |config| config.logger = logger }
|
146
143
|
CopycopterClient.configuration.should_not be_applied
|
147
144
|
logger.entries[:info].should be_empty
|
148
145
|
end
|
149
146
|
|
150
|
-
it
|
147
|
+
it 'should not remove existing config options when configuring twice' do
|
151
148
|
first_config = nil
|
149
|
+
|
152
150
|
CopycopterClient.configure(false) do |config|
|
153
151
|
first_config = config
|
154
152
|
end
|
153
|
+
|
155
154
|
CopycopterClient.configure(false) do |config|
|
156
155
|
config.should == first_config
|
157
156
|
end
|
158
157
|
end
|
159
158
|
|
160
|
-
it
|
159
|
+
it 'starts out unapplied' do
|
161
160
|
CopycopterClient::Configuration.new.should_not be_applied
|
162
161
|
end
|
163
162
|
|
164
|
-
it
|
163
|
+
it 'logs to $stdout by default' do
|
165
164
|
logger = FakeLogger.new
|
166
|
-
Logger.stubs
|
167
|
-
|
165
|
+
Logger.stubs :new => logger
|
168
166
|
config = CopycopterClient::Configuration.new
|
169
167
|
Logger.should have_received(:new).with($stdout)
|
170
168
|
config.logger.original_logger.should == logger
|
171
169
|
end
|
172
170
|
|
173
|
-
it
|
171
|
+
it 'generates environment info without a framework' do
|
174
172
|
subject.environment_name = 'production'
|
175
173
|
subject.environment_info.should == "[Ruby: #{RUBY_VERSION}] [Env: production]"
|
176
174
|
end
|
177
175
|
|
178
|
-
it
|
176
|
+
it 'generates environment info with a framework' do
|
179
177
|
subject.environment_name = 'production'
|
180
178
|
subject.framework = 'Sinatra: 1.0.0'
|
181
179
|
subject.environment_info.
|
182
180
|
should == "[Ruby: #{RUBY_VERSION}] [Sinatra: 1.0.0] [Env: production]"
|
183
181
|
end
|
184
182
|
|
185
|
-
it
|
183
|
+
it 'prefixes log entries' do
|
186
184
|
logger = FakeLogger.new
|
187
185
|
config = CopycopterClient::Configuration.new
|
188
186
|
|
@@ -194,53 +192,53 @@ describe CopycopterClient::Configuration do
|
|
194
192
|
end
|
195
193
|
end
|
196
194
|
|
197
|
-
share_examples_for
|
195
|
+
share_examples_for 'applied configuration' do
|
196
|
+
subject { CopycopterClient::Configuration.new }
|
198
197
|
let(:backend) { stub('i18n-backend') }
|
199
|
-
let(:cache)
|
200
|
-
let(:client)
|
201
|
-
let(:
|
198
|
+
let(:cache) { stub('cache') }
|
199
|
+
let(:client) { stub('client') }
|
200
|
+
let(:logger) { FakeLogger.new }
|
201
|
+
let(:poller) { stub('poller') }
|
202
202
|
let(:process_guard) { stub('process_guard', :start => nil) }
|
203
|
-
let(:logger) { FakeLogger.new }
|
204
|
-
subject { CopycopterClient::Configuration.new }
|
205
203
|
|
206
204
|
before do
|
207
|
-
CopycopterClient::I18nBackend.stubs
|
208
|
-
CopycopterClient::Client.stubs
|
209
|
-
CopycopterClient::Cache.stubs
|
210
|
-
CopycopterClient::Poller.stubs
|
211
|
-
CopycopterClient::ProcessGuard.stubs
|
205
|
+
CopycopterClient::I18nBackend.stubs :new => backend
|
206
|
+
CopycopterClient::Client.stubs :new => client
|
207
|
+
CopycopterClient::Cache.stubs :new => cache
|
208
|
+
CopycopterClient::Poller.stubs :new => poller
|
209
|
+
CopycopterClient::ProcessGuard.stubs :new => process_guard
|
212
210
|
subject.logger = logger
|
213
211
|
apply
|
214
212
|
end
|
215
213
|
|
216
214
|
it { should be_applied }
|
217
215
|
|
218
|
-
it
|
216
|
+
it 'builds and assigns an I18n backend' do
|
219
217
|
CopycopterClient::I18nBackend.should have_received(:new).with(cache)
|
220
218
|
I18n.backend.should == backend
|
221
219
|
end
|
222
220
|
|
223
|
-
it
|
221
|
+
it 'builds and assigns a poller' do
|
224
222
|
CopycopterClient::Poller.should have_received(:new).with(cache, subject.to_hash)
|
225
223
|
end
|
226
224
|
|
227
|
-
it
|
225
|
+
it 'builds a process guard' do
|
228
226
|
CopycopterClient::ProcessGuard.should have_received(:new).
|
229
227
|
with(cache, poller, subject.to_hash)
|
230
228
|
end
|
231
229
|
|
232
|
-
it
|
230
|
+
it 'logs that it is ready' do
|
233
231
|
logger.should have_entry(:info, "Client #{CopycopterClient::VERSION} ready")
|
234
232
|
end
|
235
233
|
|
236
|
-
it
|
234
|
+
it 'logs environment info' do
|
237
235
|
logger.should have_entry(:info, "Environment Info: #{subject.environment_info}")
|
238
236
|
end
|
239
237
|
end
|
240
238
|
|
241
|
-
describe CopycopterClient::Configuration,
|
242
|
-
it_should_behave_like
|
243
|
-
it
|
239
|
+
describe CopycopterClient::Configuration, 'applied when testing' do
|
240
|
+
it_should_behave_like 'applied configuration' do
|
241
|
+
it 'does not start the process guard' do
|
244
242
|
process_guard.should have_received(:start).never
|
245
243
|
end
|
246
244
|
end
|
@@ -251,9 +249,9 @@ describe CopycopterClient::Configuration, "applied when testing" do
|
|
251
249
|
end
|
252
250
|
end
|
253
251
|
|
254
|
-
describe CopycopterClient::Configuration,
|
255
|
-
it_should_behave_like
|
256
|
-
it
|
252
|
+
describe CopycopterClient::Configuration, 'applied when not testing' do
|
253
|
+
it_should_behave_like 'applied configuration' do
|
254
|
+
it 'starts the process guard' do
|
257
255
|
process_guard.should have_received(:start)
|
258
256
|
end
|
259
257
|
end
|
@@ -264,9 +262,9 @@ describe CopycopterClient::Configuration, "applied when not testing" do
|
|
264
262
|
end
|
265
263
|
end
|
266
264
|
|
267
|
-
describe CopycopterClient::Configuration,
|
268
|
-
it_should_behave_like
|
269
|
-
it
|
265
|
+
describe CopycopterClient::Configuration, 'applied when developing with middleware' do
|
266
|
+
it_should_behave_like 'applied configuration' do
|
267
|
+
it 'adds the sync middleware' do
|
270
268
|
middleware.should include(CopycopterClient::RequestSync)
|
271
269
|
end
|
272
270
|
end
|
@@ -280,8 +278,8 @@ describe CopycopterClient::Configuration, "applied when developing with middlewa
|
|
280
278
|
end
|
281
279
|
end
|
282
280
|
|
283
|
-
describe CopycopterClient::Configuration,
|
284
|
-
it_should_behave_like
|
281
|
+
describe CopycopterClient::Configuration, 'applied when developing without middleware' do
|
282
|
+
it_should_behave_like 'applied configuration'
|
285
283
|
|
286
284
|
def apply
|
287
285
|
subject.middleware = nil
|
@@ -290,8 +288,8 @@ describe CopycopterClient::Configuration, "applied when developing without middl
|
|
290
288
|
end
|
291
289
|
end
|
292
290
|
|
293
|
-
describe CopycopterClient::Configuration,
|
294
|
-
it_should_behave_like
|
291
|
+
describe CopycopterClient::Configuration, 'applied with middleware when not developing' do
|
292
|
+
it_should_behave_like 'applied configuration'
|
295
293
|
|
296
294
|
let(:middleware) { MiddlewareStack.new }
|
297
295
|
|
@@ -301,7 +299,7 @@ describe CopycopterClient::Configuration, "applied with middleware when not deve
|
|
301
299
|
subject.apply
|
302
300
|
end
|
303
301
|
|
304
|
-
it
|
302
|
+
it 'does not add the sync middleware' do
|
305
303
|
middleware.should_not include(CopycopterClient::RequestSync)
|
306
304
|
end
|
307
305
|
end
|