copy_tuner_client 2.2.0 → 2.3.0

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.
@@ -20,6 +20,14 @@ describe CopyTunerClient::ProcessGuard do
20
20
  process_guard
21
21
  end
22
22
 
23
+ it 'fork の前後で poller を張り直すフックを登録する' do
24
+ allow(CopyTunerClient::ForkHook).to receive(:install)
25
+
26
+ build_process_guard.start
27
+
28
+ expect(CopyTunerClient::ForkHook).to have_received(:install)
29
+ end
30
+
23
31
  it 'starts polling from a worker process' do
24
32
  process_guard = build_process_guard
25
33
  process_guard.start
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+ require 'timeout'
3
+
4
+ describe CopyTunerClient::QueueWithTimeout do
5
+ subject(:queue) { described_class.new }
6
+
7
+ describe '#pop_with_timeout' do
8
+ it 'キューに要素があれば取り出す' do
9
+ queue << :sync
10
+
11
+ expect(queue.pop_with_timeout(0)).to eq(:sync)
12
+ end
13
+
14
+ it '要素が無いままタイムアウトしたら ThreadError を投げる' do
15
+ expect { queue.pop_with_timeout(0.1) }.to raise_error(ThreadError)
16
+ end
17
+
18
+ it 'ウォールクロックが巻き戻ってもタイムアウトが伸びない' do
19
+ # 待っている最中に NTP の step 補正や手動の時刻変更で時計が後ろへ飛ぶ状況を模す。
20
+ # 経過時間をウォールクロックで測っていると、飛んだ幅がそのまま待ち時間に乗る
21
+ base = Time.now
22
+ allow(Time).to receive(:now).and_return(base, base - 3600)
23
+
24
+ expect { Timeout.timeout(2) { queue.pop_with_timeout(0.1) } }.to raise_error(ThreadError)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,95 @@
1
+ require 'spec_helper'
2
+ require 'copy_tuner_client/copyray_middleware'
3
+ require 'copy_tuner_client/copyray'
4
+ require 'copy_tuner_client/copyray/marker'
5
+ require 'copy_tuner_client/translation_log'
6
+
7
+ # Warden::Manager の catch(:warden) と failure app 呼び出しだけを模した fake(warden gem に依存しないため)
8
+ class FakeWarden
9
+ def initialize(app, failure_app)
10
+ @app = app
11
+ @failure_app = failure_app
12
+ end
13
+
14
+ def call(env)
15
+ result = catch(:warden) { @app.call(env) }
16
+ result.is_a?(Array) ? result : @failure_app.call(env)
17
+ end
18
+ end
19
+
20
+ # NOTE: CopyrayMiddleware 単体ではなく、Warden 相当の fake との組み合わせ位置を検証するため
21
+ # describe の第一引数はクラスではなく説明文にしている
22
+ describe 'throw :warden と CopyrayMiddleware の位置関係' do # rubocop:disable RSpec/DescribeClass
23
+ def marker(key)
24
+ CopyTunerClient::Copyray::Marker.encode(key)
25
+ end
26
+
27
+ # NOTE: authenticate_user! 失敗を模す。throw :warden するとこの @app.call(env) は正常リターンせず、
28
+ # CopyrayMiddleware が内側にあると Rewriter.rewrite が実行されない。
29
+ let(:throwing_app) { ->(_env) { throw :warden } }
30
+ let(:failure_status) { 200 }
31
+ let(:failure_app) do
32
+ ->(_env) do
33
+ [failure_status, { 'Content-Type' => 'text/html' }, [failure_body]]
34
+ end
35
+ end
36
+ let(:failure_body) { "<html><body><p>#{marker('devise.failure.unauthenticated')}Please sign in</p></body></html>" }
37
+
38
+ before do
39
+ CopyTunerClient.configure do |configuration|
40
+ configuration.project_id = 1
41
+ configuration.client = FakeClient.new
42
+ end
43
+ end
44
+
45
+ # NOTE: append_js は Rails の ActionController::Base.helpers に依存するため、
46
+ # copyray_middleware_spec.rb と同じ手法で no-op スタブに差し替え、Rewriter の効果だけを見る。
47
+ def stub_append_js(middleware)
48
+ allow(middleware).to receive(:append_js) { |html, *| html }
49
+ end
50
+
51
+ context '内側構成(CopyrayMiddleware が Warden より内側)のとき' do
52
+ it 'マーカー ⟦CT: がレスポンスに残る(不具合の記録)' do
53
+ copyray = CopyTunerClient::CopyrayMiddleware.new(throwing_app)
54
+ stub_append_js(copyray)
55
+ warden = FakeWarden.new(copyray, failure_app)
56
+
57
+ _status, _headers, response = warden.call({})
58
+ result = response.join
59
+
60
+ expect(result).to match(CopyTunerClient::Copyray::Marker::SCAN_REGEXP)
61
+ end
62
+ end
63
+
64
+ context '外側構成(CopyrayMiddleware が Warden より外側)のとき' do
65
+ it 'マーカーが消え data-copyray-key に変換される' do
66
+ warden = FakeWarden.new(throwing_app, failure_app)
67
+ copyray = CopyTunerClient::CopyrayMiddleware.new(warden)
68
+ stub_append_js(copyray)
69
+
70
+ _status, _headers, response = copyray.call({})
71
+ result = response.join
72
+
73
+ expect(result).to include('data-copyray-key="devise.failure.unauthenticated"')
74
+ expect(result).not_to match(CopyTunerClient::Copyray::Marker::SCAN_REGEXP)
75
+ end
76
+
77
+ context 'failure app が 422 を返すとき(Devise.responder.error_status = :unprocessable_entity 相当)' do
78
+ let(:unprocessable_failure_app) do
79
+ ->(_env) { [422, { 'Content-Type' => 'text/html' }, [failure_body]] }
80
+ end
81
+
82
+ it 'マーカーが消え data-copyray-key に変換される' do
83
+ warden = FakeWarden.new(throwing_app, unprocessable_failure_app)
84
+ copyray = CopyTunerClient::CopyrayMiddleware.new(warden)
85
+ stub_append_js(copyray)
86
+
87
+ _status, _headers, response = copyray.call({})
88
+ result = response.join
89
+
90
+ expect(result).to include('data-copyray-key="devise.failure.unauthenticated"')
91
+ expect(result).not_to match(CopyTunerClient::Copyray::Marker::SCAN_REGEXP)
92
+ end
93
+ end
94
+ end
95
+ end
data/spec/spec_helper.rb CHANGED
@@ -26,4 +26,10 @@ RSpec.configure do |config|
26
26
  FakeCopyTunerApp.reset
27
27
  reset_config
28
28
  end
29
+
30
+ # apply を通す spec は本物の poller スレッドを起動するので、example ごとに止める。
31
+ # 放置するとスレッドがスイート終了まで積み上がり、実際に HTTP を叩き続ける
32
+ config.after do
33
+ CopyTunerClient.configuration&.poller&.stop
34
+ end
29
35
  end
@@ -1,13 +1,47 @@
1
+ # ActionDispatch::MiddlewareStack の挿入 API を模した fake(挿入順序と引数を検証するため)
1
2
  class MiddlewareStack
2
- def initialize
3
- @middlewares = []
3
+ Entry = Struct.new(:klass, :args)
4
+
5
+ def initialize(existing = [])
6
+ @middlewares = existing.map { |klass| Entry.new(klass, []) }
7
+ end
8
+
9
+ def use(klass, *args)
10
+ @middlewares << Entry.new(klass, args)
11
+ end
12
+
13
+ def insert_before(target, klass, *args)
14
+ index = index_of!(target, 'before')
15
+ @middlewares.insert(index, Entry.new(klass, args))
4
16
  end
5
17
 
6
- def use(klass, *)
7
- @middlewares << klass.new('fake_app', *)
18
+ def insert_after(target, klass, *args)
19
+ index = index_of!(target, 'after')
20
+ @middlewares.insert(index + 1, Entry.new(klass, args))
8
21
  end
9
22
 
10
23
  def include?(klass)
11
- @middlewares.any?(klass)
24
+ classes.include?(klass)
25
+ end
26
+
27
+ def classes
28
+ @middlewares.map(&:klass)
29
+ end
30
+
31
+ def index(klass)
32
+ classes.index(klass)
33
+ end
34
+
35
+ def args_for(klass)
36
+ @middlewares.find { |entry| entry.klass == klass }&.args
37
+ end
38
+
39
+ private
40
+
41
+ def index_of!(target, where)
42
+ index = classes.index(target)
43
+ raise "No such middleware to insert #{where}: #{target.inspect}" if index.nil?
44
+
45
+ index
12
46
  end
13
47
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: copy_tuner_client
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.0
4
+ version: 2.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - SonicGarden
@@ -200,6 +200,7 @@ files:
200
200
  - app/assets/javascripts/copytuner.js
201
201
  - biome.json
202
202
  - copy_tuner_client.gemspec
203
+ - docs/poller-startup.md
203
204
  - gemfiles/8.0.gemfile
204
205
  - gemfiles/8.1.gemfile
205
206
  - gemfiles/main.gemfile
@@ -215,6 +216,7 @@ files:
215
216
  - lib/copy_tuner_client/dotted_hash.rb
216
217
  - lib/copy_tuner_client/engine.rb
217
218
  - lib/copy_tuner_client/errors.rb
219
+ - lib/copy_tuner_client/fork_hook.rb
218
220
  - lib/copy_tuner_client/helper_extension.rb
219
221
  - lib/copy_tuner_client/i18n_backend.rb
220
222
  - lib/copy_tuner_client/i18n_compat.rb
@@ -251,13 +253,16 @@ files:
251
253
  - spec/copy_tuner_client/copyray_middleware_spec.rb
252
254
  - spec/copy_tuner_client/copyray_spec.rb
253
255
  - spec/copy_tuner_client/dotted_hash_spec.rb
256
+ - spec/copy_tuner_client/fork_hook_spec.rb
254
257
  - spec/copy_tuner_client/helper_extension_spec.rb
255
258
  - spec/copy_tuner_client/i18n_backend_spec.rb
256
259
  - spec/copy_tuner_client/poller_spec.rb
257
260
  - spec/copy_tuner_client/prefixed_logger_spec.rb
258
261
  - spec/copy_tuner_client/process_guard_spec.rb
262
+ - spec/copy_tuner_client/queue_with_timeout_spec.rb
259
263
  - spec/copy_tuner_client/request_sync_spec.rb
260
264
  - spec/copy_tuner_client/translation_log_spec.rb
265
+ - spec/copy_tuner_client/warden_integration_spec.rb
261
266
  - spec/copy_tuner_client_spec.rb
262
267
  - spec/spec_helper.rb
263
268
  - spec/support/client_spec_helpers.rb