copy_tuner_client 2.1.2 → 2.2.1
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 +4 -4
- data/.gitignore +5 -0
- data/.rubocop.yml +46 -1
- data/CHANGELOG.md +2 -235
- data/CLAUDE.md +1 -0
- data/Gemfile +1 -0
- data/README.md +42 -5
- data/copy_tuner_client.gemspec +3 -3
- data/gemfiles/8.0.gemfile +3 -3
- data/gemfiles/8.1.gemfile +3 -3
- data/gemfiles/main.gemfile +3 -3
- data/lib/copy_tuner_client/cache.rb +34 -27
- data/lib/copy_tuner_client/client.rb +35 -23
- data/lib/copy_tuner_client/configuration.rb +87 -40
- data/lib/copy_tuner_client/copyray/rewriter.rb +1 -1
- data/lib/copy_tuner_client/copyray.rb +1 -0
- data/lib/copy_tuner_client/copyray_middleware.rb +31 -24
- data/lib/copy_tuner_client/dotted_hash.rb +7 -7
- data/lib/copy_tuner_client/engine.rb +4 -6
- data/lib/copy_tuner_client/helper_extension.rb +58 -54
- data/lib/copy_tuner_client/i18n_backend.rb +12 -14
- data/lib/copy_tuner_client/i18n_compat.rb +2 -1
- data/lib/copy_tuner_client/poller.rb +3 -4
- data/lib/copy_tuner_client/prefixed_logger.rb +13 -12
- data/lib/copy_tuner_client/process_guard.rb +29 -26
- data/lib/copy_tuner_client/queue_with_timeout.rb +29 -19
- data/lib/copy_tuner_client/rails.rb +9 -8
- data/lib/copy_tuner_client/request_sync.rb +26 -24
- data/lib/copy_tuner_client/simple_form_extention.rb +10 -6
- data/lib/copy_tuner_client/translation_log.rb +10 -5
- data/lib/copy_tuner_client/version.rb +1 -1
- data/lib/copy_tuner_client.rb +4 -8
- data/lib/tasks/copy_tuner_client_tasks.rake +14 -16
- data/skills/copy-tuner-to-locales-cleanup/SKILL.md +5 -5
- data/skills/copy-tuner-to-locales-migrate-prefix/SKILL.md +69 -37
- data/skills/copy-tuner-to-locales-migrate-prefix/references/example-touchpoints.md +31 -10
- data/skills/copy-tuner-to-locales-migrate-prefix/scripts/migrate_prefix.rb +158 -13
- data/skills/copy-tuner-to-t-migrate/scripts/migrate_tt.rb +19 -17
- data/spec/copy_tuner_client/cache_spec.rb +63 -57
- data/spec/copy_tuner_client/client_spec.rb +82 -81
- data/spec/copy_tuner_client/configuration_spec.rb +380 -206
- data/spec/copy_tuner_client/copyray/marker_spec.rb +2 -1
- data/spec/copy_tuner_client/copyray/rewriter_spec.rb +15 -10
- data/spec/copy_tuner_client/copyray_middleware_spec.rb +9 -6
- data/spec/copy_tuner_client/copyray_spec.rb +8 -8
- data/spec/copy_tuner_client/dotted_hash_spec.rb +33 -33
- data/spec/copy_tuner_client/helper_extension_spec.rb +47 -44
- data/spec/copy_tuner_client/i18n_backend_spec.rb +144 -143
- data/spec/copy_tuner_client/poller_spec.rb +25 -24
- data/spec/copy_tuner_client/prefixed_logger_spec.rb +15 -12
- data/spec/copy_tuner_client/process_guard_spec.rb +27 -27
- data/spec/copy_tuner_client/request_sync_spec.rb +51 -58
- data/spec/copy_tuner_client/translation_log_spec.rb +38 -24
- data/spec/copy_tuner_client/warden_integration_spec.rb +95 -0
- data/spec/copy_tuner_client_spec.rb +5 -6
- data/spec/support/client_spec_helpers.rb +1 -1
- data/spec/support/defines_constants.rb +3 -34
- data/spec/support/fake_client.rb +1 -3
- data/spec/support/fake_copy_tuner_app.rb +54 -62
- data/spec/support/fake_html_safe_string.rb +2 -3
- data/spec/support/fake_logger.rb +22 -21
- data/spec/support/fake_passenger.rb +4 -6
- data/spec/support/fake_unicorn.rb +2 -4
- data/spec/support/middleware_stack.rb +38 -4
- data/spec/support/writing_cache.rb +4 -4
- metadata +2 -1
|
@@ -1,18 +1,71 @@
|
|
|
1
1
|
require 'spec_helper'
|
|
2
2
|
|
|
3
|
+
shared_context 'stubbed configuration' do
|
|
4
|
+
subject(:configuration) { CopyTunerClient::Configuration.new }
|
|
5
|
+
|
|
6
|
+
let(:backend) { instance_double(CopyTunerClient::I18nBackend) }
|
|
7
|
+
let(:cache) { instance_double(CopyTunerClient::Cache, download: 'download') }
|
|
8
|
+
let(:logger) { FakeLogger.new }
|
|
9
|
+
let(:poller) { instance_double(CopyTunerClient::Poller) }
|
|
10
|
+
let(:process_guard) { instance_double(CopyTunerClient::ProcessGuard, start: nil) }
|
|
11
|
+
|
|
12
|
+
before do
|
|
13
|
+
allow(CopyTunerClient::I18nBackend).to receive(:new).and_return(backend)
|
|
14
|
+
allow(CopyTunerClient::Client).to receive(:new).and_return(instance_double(CopyTunerClient::Client))
|
|
15
|
+
allow(CopyTunerClient::Cache).to receive(:new).and_return(cache)
|
|
16
|
+
allow(CopyTunerClient::Poller).to receive(:new).and_return(poller)
|
|
17
|
+
allow(CopyTunerClient::ProcessGuard).to receive(:new).and_return(process_guard)
|
|
18
|
+
configuration.logger = logger
|
|
19
|
+
# NOTE: apply は project_id 必須になったため、未設定だと raise する。applied 系テストは
|
|
20
|
+
# project_id 自体を検証しないので適当な値を補っておく
|
|
21
|
+
configuration.project_id ||= 1
|
|
22
|
+
apply
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
shared_examples_for 'applied configuration' do
|
|
27
|
+
include_context 'stubbed configuration'
|
|
28
|
+
|
|
29
|
+
it { is_expected.to be_applied }
|
|
30
|
+
|
|
31
|
+
it 'builds and assigns an I18n backend' do
|
|
32
|
+
expect(CopyTunerClient::I18nBackend).to have_received(:new).with(cache)
|
|
33
|
+
expect(I18n.backend).to eq(backend)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it 'builds and assigns a poller' do
|
|
37
|
+
expect(CopyTunerClient::Poller).to have_received(:new).with(cache, configuration.to_hash)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it 'builds a process guard' do
|
|
41
|
+
expect(CopyTunerClient::ProcessGuard).to have_received(:new)
|
|
42
|
+
.with(cache, poller, configuration.to_hash)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it 'logs that it is ready' do
|
|
46
|
+
expect(logger).to have_entry(:info, "Client #{CopyTunerClient::VERSION} ready")
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it 'logs environment info' do
|
|
50
|
+
expect(logger).to have_entry(:info, "Environment Info: #{configuration.environment_info}")
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
3
54
|
describe CopyTunerClient::Configuration do
|
|
55
|
+
subject(:configuration) { described_class.new }
|
|
56
|
+
|
|
4
57
|
RSpec::Matchers.define :have_config_option do |option|
|
|
5
58
|
match do |config|
|
|
6
59
|
expect(config).to respond_to(option)
|
|
7
60
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
end
|
|
61
|
+
# .default チェーン指定時のみ既定値を検証する DSL のため条件分岐が必須
|
|
62
|
+
expect(config.public_send(option)).to eq(@default) if instance_variables.include?(:@default) # rubocop:disable Sgcop/Rspec/ConditionalExample
|
|
11
63
|
|
|
12
|
-
|
|
64
|
+
# .overridable チェーン指定時のみ代入可否を検証する DSL のため条件分岐が必須
|
|
65
|
+
if @overridable # rubocop:disable Sgcop/Rspec/ConditionalExample
|
|
13
66
|
value = 'a value'
|
|
14
|
-
config.
|
|
15
|
-
expect(config.
|
|
67
|
+
config.public_send(:"#{option}=", value)
|
|
68
|
+
expect(config.public_send(option)).to eq(value)
|
|
16
69
|
end
|
|
17
70
|
end
|
|
18
71
|
|
|
@@ -47,142 +100,145 @@ describe CopyTunerClient::Configuration do
|
|
|
47
100
|
it { is_expected.to have_config_option(:cache).overridable }
|
|
48
101
|
it { is_expected.to have_config_option(:local_first_key_regexp).overridable.default(nil) }
|
|
49
102
|
|
|
50
|
-
it '
|
|
51
|
-
config =
|
|
103
|
+
it 'provides default values for secure connections' do
|
|
104
|
+
config = described_class.new
|
|
52
105
|
config.secure = true
|
|
53
106
|
expect(config.port).to eq(443)
|
|
54
107
|
expect(config.protocol).to eq('https')
|
|
55
108
|
end
|
|
56
109
|
|
|
57
|
-
it '
|
|
58
|
-
config =
|
|
110
|
+
it 'provides default values for insecure connections' do
|
|
111
|
+
config = described_class.new
|
|
59
112
|
config.secure = false
|
|
60
113
|
expect(config.port).to eq(80)
|
|
61
114
|
expect(config.protocol).to eq('http')
|
|
62
115
|
end
|
|
63
116
|
|
|
64
|
-
it '
|
|
65
|
-
config =
|
|
117
|
+
it 'does not cache inferred ports' do
|
|
118
|
+
config = described_class.new
|
|
66
119
|
config.secure = false
|
|
67
120
|
config.port
|
|
68
121
|
config.secure = true
|
|
69
122
|
expect(config.port).to eq(443)
|
|
70
123
|
end
|
|
71
124
|
|
|
72
|
-
it '
|
|
73
|
-
config =
|
|
125
|
+
it 'acts like a hash' do
|
|
126
|
+
config = described_class.new
|
|
74
127
|
hash = config.to_hash
|
|
75
128
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
129
|
+
# hash が元 config の各 option と同期していることを検証するため、比較先をリテラルに書き出せない
|
|
130
|
+
%i[
|
|
131
|
+
api_key environment_name host http_open_timeout
|
|
132
|
+
http_read_timeout client_name client_url client_version port
|
|
133
|
+
protocol proxy_host proxy_pass proxy_port proxy_user secure
|
|
134
|
+
development_environments logger framework ca_file
|
|
135
|
+
].each do |option|
|
|
136
|
+
expect(hash[option]).to eq(config[option]) # rubocop:disable Sgcop/Rspec/NoMethodCallInExpectation
|
|
81
137
|
end
|
|
82
138
|
|
|
83
|
-
expect(hash[:public]).to eq(config.public?)
|
|
139
|
+
expect(hash[:public]).to eq(config.public?) # rubocop:disable Sgcop/Rspec/NoMethodCallInExpectation
|
|
84
140
|
end
|
|
85
141
|
|
|
86
|
-
it '
|
|
87
|
-
config =
|
|
142
|
+
it 'is mergable' do
|
|
143
|
+
config = described_class.new
|
|
88
144
|
hash = config.to_hash
|
|
89
|
-
expect(config.merge(:
|
|
145
|
+
expect(config.merge(key: 'value')).to eq(hash.merge(key: 'value')) # rubocop:disable Sgcop/Rspec/NoMethodCallInExpectation
|
|
90
146
|
end
|
|
91
147
|
|
|
92
|
-
it '
|
|
93
|
-
config =
|
|
94
|
-
expect(config.development_environments).to match_array(%w
|
|
148
|
+
it 'uses development and staging as development environments by default' do
|
|
149
|
+
config = described_class.new
|
|
150
|
+
expect(config.development_environments).to match_array(%w[development staging])
|
|
95
151
|
end
|
|
96
152
|
|
|
97
|
-
it '
|
|
98
|
-
config =
|
|
99
|
-
expect(config.test_environments).to match_array(%w
|
|
153
|
+
it 'uses test and cucumber as test environments by default' do
|
|
154
|
+
config = described_class.new
|
|
155
|
+
expect(config.test_environments).to match_array(%w[test cucumber])
|
|
100
156
|
end
|
|
101
157
|
|
|
102
|
-
it '
|
|
103
|
-
config =
|
|
104
|
-
config.test_environments = %w
|
|
158
|
+
it 'is test in a test environment' do
|
|
159
|
+
config = described_class.new
|
|
160
|
+
config.test_environments = %w[test]
|
|
105
161
|
config.environment_name = 'test'
|
|
106
162
|
expect(config).to be_test
|
|
107
163
|
end
|
|
108
164
|
|
|
109
|
-
it '
|
|
110
|
-
config =
|
|
111
|
-
config.development_environments = %w
|
|
165
|
+
it 'is public in a public environment' do
|
|
166
|
+
config = described_class.new
|
|
167
|
+
config.development_environments = %w[development]
|
|
112
168
|
config.environment_name = 'production'
|
|
113
169
|
expect(config).to be_public
|
|
114
170
|
expect(config).not_to be_development
|
|
115
171
|
end
|
|
116
172
|
|
|
117
|
-
it '
|
|
118
|
-
config =
|
|
119
|
-
config.development_environments = %w
|
|
173
|
+
it 'is development in a development environment' do
|
|
174
|
+
config = described_class.new
|
|
175
|
+
config.development_environments = %w[staging]
|
|
120
176
|
config.environment_name = 'staging'
|
|
121
177
|
expect(config).to be_development
|
|
122
178
|
expect(config).not_to be_public
|
|
123
179
|
end
|
|
124
180
|
|
|
125
|
-
it '
|
|
126
|
-
config =
|
|
181
|
+
it 'is public without an environment name' do
|
|
182
|
+
config = described_class.new
|
|
127
183
|
expect(config).to be_public
|
|
128
184
|
end
|
|
129
185
|
|
|
130
|
-
it '
|
|
186
|
+
it 'yields and save a configuration when configuring' do
|
|
131
187
|
yielded_configuration = nil
|
|
132
188
|
|
|
133
|
-
CopyTunerClient.configure(false) do |config|
|
|
189
|
+
CopyTunerClient.configure(apply: false) do |config|
|
|
134
190
|
yielded_configuration = config
|
|
135
191
|
end
|
|
136
192
|
|
|
137
|
-
expect(yielded_configuration).to
|
|
193
|
+
expect(yielded_configuration).to be_a(described_class)
|
|
138
194
|
expect(CopyTunerClient.configuration).to eq(yielded_configuration)
|
|
139
195
|
end
|
|
140
196
|
|
|
141
197
|
it 'does not apply the configuration when asked not to' do
|
|
142
198
|
logger = FakeLogger.new
|
|
143
|
-
CopyTunerClient.configure(false) { |config| config.logger = logger }
|
|
199
|
+
CopyTunerClient.configure(apply: false) { |config| config.logger = logger }
|
|
144
200
|
expect(CopyTunerClient.configuration).not_to be_applied
|
|
145
201
|
expect(logger.entries[:info]).to be_empty
|
|
146
202
|
end
|
|
147
203
|
|
|
148
|
-
it '
|
|
204
|
+
it 'does not remove existing config options when configuring twice' do
|
|
149
205
|
first_config = nil
|
|
150
206
|
|
|
151
|
-
CopyTunerClient.configure(false) do |config|
|
|
207
|
+
CopyTunerClient.configure(apply: false) do |config|
|
|
152
208
|
first_config = config
|
|
153
209
|
end
|
|
154
210
|
|
|
155
|
-
CopyTunerClient.configure(false) do |config|
|
|
211
|
+
CopyTunerClient.configure(apply: false) do |config|
|
|
156
212
|
expect(config).to eq(first_config)
|
|
157
213
|
end
|
|
158
214
|
end
|
|
159
215
|
|
|
160
216
|
it 'starts out unapplied' do
|
|
161
|
-
expect(
|
|
217
|
+
expect(described_class.new).not_to be_applied
|
|
162
218
|
end
|
|
163
219
|
|
|
164
220
|
it 'logs to $stdout by default' do
|
|
165
221
|
logger = FakeLogger.new
|
|
166
|
-
|
|
167
|
-
config =
|
|
222
|
+
allow(Logger).to receive(:new).with($stdout).and_return(logger)
|
|
223
|
+
config = described_class.new
|
|
168
224
|
expect(config.logger.original_logger).to eq(logger)
|
|
169
225
|
end
|
|
170
226
|
|
|
171
227
|
it 'generates environment info without a framework' do
|
|
172
|
-
|
|
173
|
-
expect(
|
|
228
|
+
configuration.environment_name = 'production'
|
|
229
|
+
expect(configuration.environment_info).to eq("[Ruby: #{RUBY_VERSION}] [Env: production]")
|
|
174
230
|
end
|
|
175
231
|
|
|
176
232
|
it 'generates environment info with a framework' do
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
expect(
|
|
180
|
-
to eq("[Ruby: #{RUBY_VERSION}] [Sinatra: 1.0.0] [Env: production]")
|
|
233
|
+
configuration.environment_name = 'production'
|
|
234
|
+
configuration.framework = 'Sinatra: 1.0.0'
|
|
235
|
+
expect(configuration.environment_info)
|
|
236
|
+
.to eq("[Ruby: #{RUBY_VERSION}] [Sinatra: 1.0.0] [Env: production]")
|
|
181
237
|
end
|
|
182
238
|
|
|
183
239
|
it 'prefixes log entries' do
|
|
184
240
|
logger = FakeLogger.new
|
|
185
|
-
config =
|
|
241
|
+
config = described_class.new
|
|
186
242
|
|
|
187
243
|
config.logger = logger
|
|
188
244
|
|
|
@@ -192,29 +248,29 @@ describe CopyTunerClient::Configuration do
|
|
|
192
248
|
end
|
|
193
249
|
|
|
194
250
|
describe '#local_first_key?' do
|
|
195
|
-
let(:config) {
|
|
251
|
+
let(:config) { described_class.new }
|
|
196
252
|
|
|
197
253
|
it 'returns false when local_first_key_regexp is nil (default)' do
|
|
198
|
-
expect(config.local_first_key?('views.foo.bar')).to
|
|
254
|
+
expect(config.local_first_key?('views.foo.bar')).to be false
|
|
199
255
|
end
|
|
200
256
|
|
|
201
257
|
context 'when local_first_key_regexp is set' do
|
|
202
258
|
before { config.local_first_key_regexp = /\Aviews\./ }
|
|
203
259
|
|
|
204
260
|
it 'returns true for a matching key' do
|
|
205
|
-
expect(config.local_first_key?('views.foo.bar')).to
|
|
261
|
+
expect(config.local_first_key?('views.foo.bar')).to be true
|
|
206
262
|
end
|
|
207
263
|
|
|
208
264
|
it 'returns false for a non-matching key' do
|
|
209
|
-
expect(config.local_first_key?('models.foo.bar')).to
|
|
265
|
+
expect(config.local_first_key?('models.foo.bar')).to be false
|
|
210
266
|
end
|
|
211
267
|
|
|
212
268
|
it 'returns false for a nil key' do
|
|
213
|
-
expect(config.local_first_key?(nil)).to
|
|
269
|
+
expect(config.local_first_key?(nil)).to be false
|
|
214
270
|
end
|
|
215
271
|
|
|
216
272
|
it 'coerces a Symbol key before matching' do
|
|
217
|
-
expect(config.local_first_key?(:'views.foo')).to
|
|
273
|
+
expect(config.local_first_key?(:'views.foo')).to be true
|
|
218
274
|
end
|
|
219
275
|
end
|
|
220
276
|
|
|
@@ -222,40 +278,40 @@ describe CopyTunerClient::Configuration do
|
|
|
222
278
|
# ユーザー設定の有無によらず常にローカル優先(組み込み判定)になる
|
|
223
279
|
context 'with built-in Rails number format keys' do
|
|
224
280
|
it 'returns true for built-in number format keys even when local_first_key_regexp is nil' do
|
|
225
|
-
expect(config.local_first_key?('number.format')).to
|
|
226
|
-
expect(config.local_first_key?('number.currency.format')).to
|
|
227
|
-
expect(config.local_first_key?('number.currency.format.precision')).to
|
|
228
|
-
expect(config.local_first_key?('number.percentage.format')).to
|
|
229
|
-
expect(config.local_first_key?('number.human.format.significant')).to
|
|
281
|
+
expect(config.local_first_key?('number.format')).to be true
|
|
282
|
+
expect(config.local_first_key?('number.currency.format')).to be true
|
|
283
|
+
expect(config.local_first_key?('number.currency.format.precision')).to be true
|
|
284
|
+
expect(config.local_first_key?('number.percentage.format')).to be true
|
|
285
|
+
expect(config.local_first_key?('number.human.format.significant')).to be true
|
|
230
286
|
end
|
|
231
287
|
|
|
232
288
|
it 'returns false for app-defined number keys (not Rails format subtrees)' do
|
|
233
|
-
expect(config.local_first_key?('number.gift_amount')).to
|
|
234
|
-
expect(config.local_first_key?('number.my_currency.unit')).to
|
|
289
|
+
expect(config.local_first_key?('number.gift_amount')).to be false
|
|
290
|
+
expect(config.local_first_key?('number.my_currency.unit')).to be false
|
|
235
291
|
end
|
|
236
292
|
|
|
237
293
|
it 'returns false for string-only number subtrees and non-number keys' do
|
|
238
|
-
expect(config.local_first_key?('number.human.storage_units.units.byte.one')).to
|
|
239
|
-
expect(config.local_first_key?('date.formats.default')).to
|
|
240
|
-
expect(config.local_first_key?('time.formats.short')).to
|
|
241
|
-
expect(config.local_first_key?('datetime.distance_in_words.x')).to
|
|
242
|
-
expect(config.local_first_key?('views.foo')).to
|
|
243
|
-
expect(config.local_first_key?('numbers.foo')).to
|
|
294
|
+
expect(config.local_first_key?('number.human.storage_units.units.byte.one')).to be false
|
|
295
|
+
expect(config.local_first_key?('date.formats.default')).to be false
|
|
296
|
+
expect(config.local_first_key?('time.formats.short')).to be false
|
|
297
|
+
expect(config.local_first_key?('datetime.distance_in_words.x')).to be false
|
|
298
|
+
expect(config.local_first_key?('views.foo')).to be false
|
|
299
|
+
expect(config.local_first_key?('numbers.foo')).to be false
|
|
244
300
|
end
|
|
245
301
|
|
|
246
302
|
it 'keeps protecting built-in keys without breaking a user-set regexp' do
|
|
247
303
|
config.local_first_key_regexp = /\Aviews\./
|
|
248
304
|
|
|
249
|
-
expect(config.local_first_key?('number.currency.format')).to
|
|
250
|
-
expect(config.local_first_key?('views.foo')).to
|
|
251
|
-
expect(config.local_first_key?('models.foo')).to
|
|
305
|
+
expect(config.local_first_key?('number.currency.format')).to be true
|
|
306
|
+
expect(config.local_first_key?('views.foo')).to be true
|
|
307
|
+
expect(config.local_first_key?('models.foo')).to be false
|
|
252
308
|
end
|
|
253
309
|
end
|
|
254
310
|
end
|
|
255
311
|
|
|
256
312
|
describe 'project_id の必須化' do
|
|
257
313
|
let(:config) do
|
|
258
|
-
config =
|
|
314
|
+
config = described_class.new
|
|
259
315
|
config.api_key = 'abc123'
|
|
260
316
|
config
|
|
261
317
|
end
|
|
@@ -273,186 +329,304 @@ describe CopyTunerClient::Configuration do
|
|
|
273
329
|
expect(config.project_url).to include('/projects/77')
|
|
274
330
|
end
|
|
275
331
|
end
|
|
276
|
-
end
|
|
277
332
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
let(:poller) { double('poller') }
|
|
285
|
-
let(:process_guard) { double('process_guard', start: nil) }
|
|
333
|
+
context 'applied when testing' do
|
|
334
|
+
it_behaves_like 'applied configuration' do
|
|
335
|
+
it 'does not start the process guard' do
|
|
336
|
+
expect(process_guard).not_to have_received(:start)
|
|
337
|
+
end
|
|
338
|
+
end
|
|
286
339
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
allow(CopyTunerClient::Poller).to receive(:new).and_return(poller)
|
|
292
|
-
allow(CopyTunerClient::ProcessGuard).to receive(:new).and_return(process_guard)
|
|
293
|
-
subject.logger = logger
|
|
294
|
-
# NOTE: apply は project_id 必須になったため、未設定だと raise する。applied 系テストは
|
|
295
|
-
# project_id 自体を検証しないので適当な値を補っておく
|
|
296
|
-
subject.project_id ||= 1
|
|
297
|
-
apply
|
|
340
|
+
def apply
|
|
341
|
+
configuration.environment_name = 'test'
|
|
342
|
+
configuration.apply
|
|
343
|
+
end
|
|
298
344
|
end
|
|
299
|
-
end
|
|
300
|
-
|
|
301
|
-
shared_examples_for 'applied configuration' do
|
|
302
|
-
include_context 'stubbed configuration'
|
|
303
345
|
|
|
304
|
-
|
|
346
|
+
context 'applied when not testing' do
|
|
347
|
+
it_behaves_like 'applied configuration' do
|
|
348
|
+
it 'starts the process guard' do
|
|
349
|
+
expect(process_guard).to have_received(:start)
|
|
350
|
+
end
|
|
351
|
+
end
|
|
305
352
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
353
|
+
def apply
|
|
354
|
+
configuration.environment_name = 'development'
|
|
355
|
+
configuration.apply
|
|
356
|
+
end
|
|
309
357
|
end
|
|
310
358
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
359
|
+
context 'applied when developing with middleware' do
|
|
360
|
+
it_behaves_like 'applied configuration' do
|
|
361
|
+
it 'adds the sync middleware' do
|
|
362
|
+
expect(middleware).to include(CopyTunerClient::RequestSync)
|
|
363
|
+
end
|
|
364
|
+
end
|
|
314
365
|
|
|
315
|
-
|
|
316
|
-
expect(CopyTunerClient::ProcessGuard).to have_received(:new).
|
|
317
|
-
with(cache, poller, subject.to_hash)
|
|
318
|
-
end
|
|
366
|
+
let(:middleware) { MiddlewareStack.new }
|
|
319
367
|
|
|
320
|
-
|
|
321
|
-
|
|
368
|
+
def apply
|
|
369
|
+
configuration.middleware = middleware
|
|
370
|
+
configuration.environment_name = 'development'
|
|
371
|
+
configuration.apply
|
|
372
|
+
end
|
|
322
373
|
end
|
|
323
374
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
end
|
|
327
|
-
end
|
|
375
|
+
context 'applied when developing without middleware' do
|
|
376
|
+
it_behaves_like 'applied configuration'
|
|
328
377
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
378
|
+
def apply
|
|
379
|
+
configuration.middleware = nil
|
|
380
|
+
configuration.environment_name = 'development'
|
|
381
|
+
configuration.apply
|
|
333
382
|
end
|
|
334
383
|
end
|
|
335
384
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
subject.apply
|
|
339
|
-
end
|
|
340
|
-
end
|
|
385
|
+
context 'applied with middleware when not developing' do
|
|
386
|
+
let(:middleware) { MiddlewareStack.new }
|
|
341
387
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
388
|
+
it_behaves_like 'applied configuration'
|
|
389
|
+
|
|
390
|
+
def apply
|
|
391
|
+
configuration.middleware = middleware
|
|
392
|
+
configuration.environment_name = 'test'
|
|
393
|
+
configuration.apply
|
|
346
394
|
end
|
|
347
|
-
end
|
|
348
395
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
396
|
+
it 'does not add the sync middleware' do
|
|
397
|
+
expect(middleware).not_to include(CopyTunerClient::RequestSync)
|
|
398
|
+
end
|
|
352
399
|
end
|
|
353
|
-
end
|
|
354
400
|
|
|
355
|
-
describe
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
401
|
+
describe 'middleware_position の初期値' do
|
|
402
|
+
let(:config) { described_class.new }
|
|
403
|
+
|
|
404
|
+
context 'Warden::Manager と Devise がどちらも定義済みのとき' do
|
|
405
|
+
before do
|
|
406
|
+
stub_const('Warden::Manager', Class.new)
|
|
407
|
+
stub_const('Devise', Module.new)
|
|
408
|
+
end
|
|
409
|
+
|
|
410
|
+
it '{ before: Warden::Manager } になる' do
|
|
411
|
+
expect(config.middleware_position).to eq({ before: Warden::Manager })
|
|
412
|
+
end
|
|
359
413
|
end
|
|
360
|
-
end
|
|
361
414
|
|
|
362
|
-
|
|
415
|
+
context 'Warden::Manager が未定義のとき' do
|
|
416
|
+
before { hide_const('Warden::Manager') }
|
|
363
417
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
418
|
+
it 'nil のままになる' do
|
|
419
|
+
expect(config.middleware_position).to be_nil
|
|
420
|
+
end
|
|
421
|
+
end
|
|
422
|
+
|
|
423
|
+
# NOTE: authtrail のように warden を require するだけで Warden::Manager をスタックへ積まない
|
|
424
|
+
# gem があるため、Warden 単独では既定位置にしない(insert_before が起動時例外になる)。
|
|
425
|
+
context 'Warden::Manager は定義済みだが Devise が未定義のとき' do
|
|
426
|
+
before do
|
|
427
|
+
stub_const('Warden::Manager', Class.new)
|
|
428
|
+
hide_const('Devise')
|
|
429
|
+
end
|
|
370
430
|
|
|
371
|
-
|
|
372
|
-
|
|
431
|
+
it 'nil のままになる' do
|
|
432
|
+
expect(config.middleware_position).to be_nil
|
|
433
|
+
end
|
|
434
|
+
end
|
|
373
435
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
436
|
+
context 'configure ブロック内で明示指定したとき' do
|
|
437
|
+
before do
|
|
438
|
+
stub_const('Warden::Manager', Class.new)
|
|
439
|
+
stub_const('Devise', Module.new)
|
|
440
|
+
end
|
|
441
|
+
|
|
442
|
+
it '明示指定した値が優先される' do
|
|
443
|
+
other = Class.new
|
|
444
|
+
CopyTunerClient.configure(apply: false) do |c|
|
|
445
|
+
c.middleware_position = { before: other }
|
|
446
|
+
end
|
|
447
|
+
expect(CopyTunerClient.configuration.middleware_position).to eq({ before: other })
|
|
448
|
+
end
|
|
449
|
+
end
|
|
378
450
|
end
|
|
379
|
-
end
|
|
380
451
|
|
|
381
|
-
|
|
382
|
-
|
|
452
|
+
context 'Warden::Manager がスタックにある状態で apply したとき' do
|
|
453
|
+
it_behaves_like 'applied configuration' do
|
|
454
|
+
it 'デフォルト値経由で Warden::Manager の直前に RequestSync → CopyrayMiddleware の順で入る' do
|
|
455
|
+
expect(middleware.classes).to eq(
|
|
456
|
+
[
|
|
457
|
+
Rack::ETag, Rack::TempfileReaper, CopyTunerClient::RequestSync, CopyTunerClient::CopyrayMiddleware,
|
|
458
|
+
Warden::Manager, Rack::Static
|
|
459
|
+
]
|
|
460
|
+
)
|
|
461
|
+
end
|
|
383
462
|
|
|
384
|
-
|
|
463
|
+
it 'Warden::Manager より外側に配置される' do
|
|
464
|
+
expect(middleware.index(CopyTunerClient::RequestSync)).to be < middleware.index(Warden::Manager)
|
|
465
|
+
expect(middleware.index(CopyTunerClient::CopyrayMiddleware)).to be < middleware.index(Warden::Manager)
|
|
466
|
+
end
|
|
385
467
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
468
|
+
it 'RequestSync に poller / cache / interval / ignore_regex が渡る' do
|
|
469
|
+
args = middleware.args_for(CopyTunerClient::RequestSync)
|
|
470
|
+
expect(args.first).to include(
|
|
471
|
+
poller:, # rubocop:disable Sgcop/Rspec/NoMethodCallInExpectation
|
|
472
|
+
cache:,
|
|
473
|
+
interval: configuration.sync_interval,
|
|
474
|
+
ignore_regex: configuration.sync_ignore_path_regex
|
|
475
|
+
)
|
|
476
|
+
end
|
|
477
|
+
end
|
|
391
478
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
479
|
+
# NOTE: 実アプリの bin/rails middleware 出力を模した標準スタック。Warden::Manager / Devise は
|
|
480
|
+
# gem の依存にないため stub_const で fake の定数として定義する。
|
|
481
|
+
before do
|
|
482
|
+
stub_const('Warden::Manager', Class.new)
|
|
483
|
+
stub_const('Devise', Module.new)
|
|
484
|
+
end
|
|
396
485
|
|
|
397
|
-
|
|
398
|
-
|
|
486
|
+
let(:middleware) do
|
|
487
|
+
MiddlewareStack.new([Rack::ETag, Rack::TempfileReaper, Warden::Manager, Rack::Static])
|
|
488
|
+
end
|
|
399
489
|
|
|
400
|
-
|
|
401
|
-
|
|
490
|
+
def apply
|
|
491
|
+
configuration.middleware = middleware
|
|
492
|
+
configuration.environment_name = 'development'
|
|
493
|
+
configuration.apply
|
|
494
|
+
end
|
|
402
495
|
end
|
|
403
496
|
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
497
|
+
context 'Warden::Manager が未定義の状態で apply したとき' do
|
|
498
|
+
it_behaves_like 'applied configuration' do
|
|
499
|
+
it 'Warden::Manager 未定義時は従来どおりスタック末尾へ use される' do
|
|
500
|
+
expect(middleware.classes).to eq([CopyTunerClient::RequestSync, CopyTunerClient::CopyrayMiddleware])
|
|
501
|
+
end
|
|
502
|
+
end
|
|
408
503
|
|
|
409
|
-
|
|
410
|
-
include_context 'stubbed configuration'
|
|
504
|
+
before { hide_const('Warden::Manager') }
|
|
411
505
|
|
|
412
|
-
|
|
413
|
-
subject.locales = %i(en ja)
|
|
414
|
-
subject.apply
|
|
415
|
-
end
|
|
506
|
+
let(:middleware) { MiddlewareStack.new }
|
|
416
507
|
|
|
417
|
-
|
|
418
|
-
|
|
508
|
+
def apply
|
|
509
|
+
configuration.middleware = middleware
|
|
510
|
+
configuration.environment_name = 'development'
|
|
511
|
+
configuration.apply
|
|
512
|
+
end
|
|
419
513
|
end
|
|
420
|
-
end
|
|
421
514
|
|
|
422
|
-
|
|
423
|
-
|
|
515
|
+
context 'middleware_position に after を明示指定して apply したとき' do
|
|
516
|
+
it_behaves_like 'applied configuration' do
|
|
517
|
+
it '{after: X} 指定時は X の直後に RequestSync → CopyrayMiddleware の順で入る' do
|
|
518
|
+
expect(middleware.classes).to eq(
|
|
519
|
+
[:a, :x, CopyTunerClient::RequestSync, CopyTunerClient::CopyrayMiddleware, :b]
|
|
520
|
+
)
|
|
521
|
+
end
|
|
522
|
+
end
|
|
424
523
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
524
|
+
let(:middleware) { MiddlewareStack.new(%i[a x b]) }
|
|
525
|
+
|
|
526
|
+
def apply
|
|
527
|
+
configuration.middleware = middleware
|
|
528
|
+
configuration.middleware_position = { after: :x }
|
|
529
|
+
configuration.environment_name = 'development'
|
|
530
|
+
configuration.apply
|
|
430
531
|
end
|
|
532
|
+
end
|
|
533
|
+
|
|
534
|
+
context 'middleware_position に before を明示指定して apply したとき' do
|
|
535
|
+
it_behaves_like 'applied configuration' do
|
|
536
|
+
it '{before: X} 指定時は X の直前に RequestSync → CopyrayMiddleware の順で入る' do
|
|
537
|
+
expect(middleware.classes).to eq(
|
|
538
|
+
[:a, CopyTunerClient::RequestSync, CopyTunerClient::CopyrayMiddleware, :x, :b]
|
|
539
|
+
)
|
|
540
|
+
end
|
|
541
|
+
end
|
|
542
|
+
|
|
543
|
+
let(:middleware) { MiddlewareStack.new(%i[a x b]) }
|
|
431
544
|
|
|
432
|
-
|
|
433
|
-
|
|
545
|
+
def apply
|
|
546
|
+
configuration.middleware = middleware
|
|
547
|
+
configuration.middleware_position = { before: :x }
|
|
548
|
+
configuration.environment_name = 'development'
|
|
549
|
+
configuration.apply
|
|
434
550
|
end
|
|
435
551
|
end
|
|
436
552
|
|
|
437
|
-
|
|
438
|
-
|
|
553
|
+
# NOTE: { before: SomeClass if cond } のように条件次第で値が nil になる書き方を想定する。
|
|
554
|
+
# キーの有無だけで分岐すると insert_before(nil) が対象を見つけられず例外になる。
|
|
555
|
+
context 'middleware_position の値が nil のとき' do
|
|
556
|
+
it_behaves_like 'applied configuration' do
|
|
557
|
+
it '例外を投げずスタック末尾へ use する' do
|
|
558
|
+
expect(middleware.classes).to eq(
|
|
559
|
+
[:a, :x, :b, CopyTunerClient::RequestSync, CopyTunerClient::CopyrayMiddleware]
|
|
560
|
+
)
|
|
561
|
+
end
|
|
562
|
+
end
|
|
563
|
+
|
|
564
|
+
let(:middleware) { MiddlewareStack.new(%i[a x b]) }
|
|
565
|
+
|
|
566
|
+
def apply
|
|
567
|
+
configuration.middleware = middleware
|
|
568
|
+
configuration.middleware_position = { before: nil }
|
|
569
|
+
configuration.environment_name = 'development'
|
|
570
|
+
configuration.apply
|
|
571
|
+
end
|
|
439
572
|
end
|
|
440
573
|
|
|
441
|
-
context '
|
|
442
|
-
with_config(available_locales: %i(en ja))
|
|
574
|
+
context 'applied without locale filter' do
|
|
443
575
|
include_context 'stubbed configuration'
|
|
444
576
|
|
|
445
|
-
|
|
446
|
-
|
|
577
|
+
def apply
|
|
578
|
+
configuration.apply
|
|
579
|
+
end
|
|
580
|
+
|
|
581
|
+
it 'has locales [:en]' do
|
|
582
|
+
expect(configuration.locales).to eq [:en]
|
|
447
583
|
end
|
|
448
584
|
end
|
|
449
585
|
|
|
450
|
-
context 'with
|
|
451
|
-
with_config(available_locales: %i(ja))
|
|
586
|
+
context 'applied with locale filter' do
|
|
452
587
|
include_context 'stubbed configuration'
|
|
453
588
|
|
|
454
|
-
|
|
455
|
-
|
|
589
|
+
def apply
|
|
590
|
+
configuration.locales = %i[en ja]
|
|
591
|
+
configuration.apply
|
|
592
|
+
end
|
|
593
|
+
|
|
594
|
+
it 'has locales %i(en ja)' do
|
|
595
|
+
expect(configuration.locales).to eq %i[en ja]
|
|
596
|
+
end
|
|
597
|
+
end
|
|
598
|
+
|
|
599
|
+
context 'applied with Rails i18n config' do
|
|
600
|
+
def self.with_config(i18n_options)
|
|
601
|
+
before do
|
|
602
|
+
stub_const('Rails', Module.new)
|
|
603
|
+
i18n = double('i18n', i18n_options)
|
|
604
|
+
config = double('config', i18n:)
|
|
605
|
+
application = double('application', config:)
|
|
606
|
+
allow(Rails).to receive(:application).and_return(application)
|
|
607
|
+
end
|
|
608
|
+
end
|
|
609
|
+
|
|
610
|
+
def apply
|
|
611
|
+
configuration.apply
|
|
612
|
+
end
|
|
613
|
+
|
|
614
|
+
context 'with available_locales' do
|
|
615
|
+
with_config(available_locales: %i[en ja])
|
|
616
|
+
include_context 'stubbed configuration'
|
|
617
|
+
|
|
618
|
+
it 'has locales %i(en ja)' do
|
|
619
|
+
expect(configuration.locales).to eq %i[en ja]
|
|
620
|
+
end
|
|
621
|
+
end
|
|
622
|
+
|
|
623
|
+
context 'with default_locale' do
|
|
624
|
+
with_config(available_locales: %i[ja])
|
|
625
|
+
include_context 'stubbed configuration'
|
|
626
|
+
|
|
627
|
+
it 'has locales %i(ja)' do
|
|
628
|
+
expect(configuration.locales).to eq %i[ja]
|
|
629
|
+
end
|
|
456
630
|
end
|
|
457
631
|
end
|
|
458
632
|
end
|