-
Notifications
You must be signed in to change notification settings - Fork 71
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ベストアンサーなし通知が本番で動作しないバグを修正 #6970
ベストアンサーなし通知が本番で動作しないバグを修正 #6970
Conversation
9936c06
to
048b62f
Compare
175a0d4
to
048b62f
Compare
048b62f
to
89655c4
Compare
@omochiumaiumai こちら、よろしければレビューお願いできますでしょうか? |
@ogawa-tomo |
50969e4
to
248b2e9
Compare
@omochiumaiumai 承知しました、別の方にお願いしようと思います:bow: |
テストが落ちているのと、テストでもトークン認証をチェックできたほうがよいと思い、いったんドラフトに戻す |
9f08365
to
248b2e9
Compare
956f4fa
to
2e6e84b
Compare
@ogawa-tomo こちらのpull requestで「定期処理がSchedulerControllerを継承していないときエラーにする」対応を行うと、 上記を回避するためには「ExternalEntryにScheduleControllerを継承する」対応を行う必要がありますが、以下のpull requestで先に対応してしまうと、今度はtokenを使った定期実行テスト用のヘルパーメソッドの実装が被ってしまいます。 そのため、以下の対応についてはこちらのpull requestで一緒に対応いただいた方が良いかもしれないと思いましたが、いかがでしょうか。
|
@siso25 ご連絡ありがとうございます!たしかにおっしゃる通りですね。その2点についてもこのPRで対応してしまおうと思います。関連する修正なので、小分けにするより一緒にやってしまったほうがよさそうな気もしますし。 |
@ogawa-tomo |
@siso25 上記対応しました!こちらのPR、レビュー依頼もお願いしてしまってもよろしいでしょうか?sisoさんがレビュー依頼可能なステータスなのか分かっていないのですが、sisoさんのPRにも関連しますし、確認はお願いしたく思っています:pray: |
mock_env({ 'TOKEN' => 'token' }) do | ||
visit_with_query scheduler_daily_notify_certain_period_passed_after_last_answer_path, { token: 'token' } | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@siso25 sisoさんにご提案いただいたコードでは、環境変数TOKENの設定とクエリパラメータtokenを設定してのアクセスという2つのことを1つのメソッドで行っていましたが、両者は分離すべきと考え、別のメソッドとして実装しました。このほうが仕様として上記の2つが必要であることがテストコードから読み取りやすいですし、上記の2つを独立にテストすることも可能になる(今回はやっていませんが)というメリットもあると思います。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
修正ありがとうございます!
たしかにこちらの方が環境変数をモックしていることが分かりやすそうです。
@ogawa-tomo |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
コメントしました〜。
return unless request.path_info.start_with?('/scheduler') | ||
return if is_a?(SchedulerController) | ||
|
||
head :internal_server_error |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return unless request.path_info.start_with?('/scheduler') | |
return if is_a?(SchedulerController) | |
head :internal_server_error | |
if request.path_info.start_with?('/scheduler') && !is_a?(SchedulerController) | |
head :internal_server_error | |
end |
とした方がコードの意図が明確になりそう。
そして、できたらこの仕組みもテストコードがほしいけど、ちょっと難しいかなあ。。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ガード節が好きでなんとなく使ってしまったのですが、たしかにこちらのほうが今回の意図を肯定形で表現できて分かりやすいですね。
ご提案いただいたコードだとrubocopにガード節か後置ifのどちらかにしろと言われたので、後置ifを用いた書き方に修正しました。
782f084
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
この仕組みのテストコードは実現方法が思いつかずでして、そのままにしています:pray:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
了解です。最低限、一時的にcontrollerの親クラスを書き替えるなどして、手元で期待した動作をしているかどうかだけ確認しておいてほしいです〜。(スクショとかのエビデンスがあればなお良し)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
期待した動作が得られなかったので下記の修正を加えています。先にrequire_active_user_login
が評価されて200が帰ってきていたため、require_scheduler_inheritation
が先に評価されるように変更しました。
d45e5e5
上記の変更を加えたうえで、下記の箇所でNotifyCertainPeriodPassedAfterLastAnswerController
の親クラスをApplicationController
に一時的に書き換え、ディスクリプションに記載した方法で動作確認を行いました。
Line 3 in b049748
class Scheduler::Daily::NotifyCertainPeriodPassedAfterLastAnswerController < SchedulerController |
その結果、500エラーとなることを確認しました。
親クラスを
SchedulerController
に戻すと正常に動作することも確認しました。
@@ -8,7 +8,7 @@ class SchedulerController < ApplicationController | |||
protected | |||
|
|||
def require_token | |||
return if ENV['TOKEN'] == params[:token] | |||
return if ENV['TOKEN'].present? && ENV['TOKEN'] == params[:token] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TOKENが未設定だとエラー、paramsのtokenが不一致だったらエラー、というパターンもテストしたい。
(門番が門番として機能していることをテストする)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
それぞれのテストについて、TOKENが未設定のケースと不一致のケースのテストを加えました。
580816e
ちょっと冗長かなとも思ったのですが、テストは必ずしもDRYでなくてよいという伊藤さんの記事も参考にしつつ、全てのテストについて愚直に繰り返し書いています。
https://qiita.com/jnchito/items/eb3cfa9f7db752dcb796
test/system/auto_retire_test.rb
Outdated
@@ -16,7 +16,9 @@ class AutoRetireTest < ApplicationSystemTestCase | |||
user = users(:kyuukai) | |||
travel_to Time.zone.local(2020, 7, 2, 0, 0, 0) do | |||
VCR.use_cassette 'subscription/update' do | |||
visit_with_auth scheduler_daily_auto_retire_path, 'komagata' | |||
mock_env({ 'TOKEN' => 'token' }) do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mock_env({ 'TOKEN' => 'token' }) do | |
mock_env('TOKEN' => 'token') do |
でよさそう
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
修正しました!引数の最後がハッシュだった場合は{}
が省略できるのですね。
03e3b7e
test/system/auto_retire_test.rb
Outdated
@@ -16,7 +16,9 @@ class AutoRetireTest < ApplicationSystemTestCase | |||
user = users(:kyuukai) | |||
travel_to Time.zone.local(2020, 7, 2, 0, 0, 0) do | |||
VCR.use_cassette 'subscription/update' do | |||
visit_with_auth scheduler_daily_auto_retire_path, 'komagata' | |||
mock_env({ 'TOKEN' => 'token' }) do | |||
visit_with_query scheduler_daily_auto_retire_path, { token: 'token' } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
visit_with_query scheduler_daily_auto_retire_path, { token: 'token' } | |
visit_with_query scheduler_daily_auto_retire_path, token: 'token' |
でよさそう
そもそも、
visit_with_query scheduler_daily_auto_retire_path, { token: 'token' } | |
visit scheduler_daily_auto_retire_path(token: 'token') |
でいいのでは?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
なるほど、それで書けるのですね。修正し、QueryHelperを削除しました。
1d66013
mock_env({ 'TOKEN' => 'token' }) do | ||
visit_with_query scheduler_daily_notify_certain_period_passed_after_last_answer_path, { token: 'token' } | ||
end | ||
visit_with_auth '/notifications', 'kimura' | ||
|
||
assert_no_text 'Q&A「テストの質問」のベストアンサーがまだ選ばれていません。' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no textの検証だけはなるべく避けたい
システムスペックやフィーチャスペックで「〜が表示されないこと」だけを検証するのはちょっと危険、という話 #Ruby - Qiita
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
なるほどです。通知一覧のページにアクセスできていることの検証を追加しました。
ba0c596
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
↑だけだと通知のメッセージが変わったときも通っちゃうな、と思い直したので、通知の件数が変わっていないことの検証も追加しました:bow:
a6e1f58
432e576
to
03d531f
Compare
@JunichiIto コメントいただいた点を修正しましたのでご確認お願いします:pray: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
修正ありがとうございます!コメントしました〜。
@@ -50,6 +51,10 @@ def require_subscription | |||
redirect_to root_path, notice: 'サブスクリプション登録が必要です。' unless current_user&.subscription? | |||
end | |||
|
|||
def require_scheduler_inheritation | |||
head :internal_server_error if request.path_info.start_with?('/scheduler') && !is_a?(SchedulerController) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
before_action :require_scheduler_inheritation, if: -> { request.path_info.start_with?('/scheduler') }
とした上で
head :internal_server_error if request.path_info.start_with?('/scheduler') && !is_a?(SchedulerController) | |
head :internal_server_error unless is_a?(SchedulerController) |
とした方がコードの意図が明確になりそうな気がしました
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
こちらで修正しました。
5ba8640
include MockEnvHelper | ||
|
||
test 'token evaluation' do | ||
# サーバー側のTOKENが未設定 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
コメントいらないんじゃないかなー。
(僕のサンプルコードのコメントはあくまで説明用だったので)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
削除しました!
da12474
assert_response 401 | ||
|
||
# tokenが正しい | ||
ExternalEntry.stub(:fetch_and_save_rss_feeds, nil) do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
質問)nilって付けないと問題が起きるんでしょうか?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
stubメソッドでは第2引数で戻り値を指定する必要があるようです。省略するとエラーになりました。ここでは戻り値はとくに問題ではないので、nilを指定することにしました。
https://www.rubydoc.info/gems/minitest/Object:stub
test/supports/mock_env_helper.rb
Outdated
def mock_env(partial_env_hash) | ||
old = ENV.to_hash | ||
ENV.update partial_env_hash | ||
begin | ||
yield | ||
ensure | ||
ENV.replace old | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
def mock_env(partial_env_hash) | |
old = ENV.to_hash | |
ENV.update partial_env_hash | |
begin | |
yield | |
ensure | |
ENV.replace old | |
end | |
end | |
def mock_env(hash_for_mock) | |
env_hash = ENV.to_hash.merge(hash_for_mock) | |
ENV.stub(:[], ->(key) { env_hash[key] }) do | |
yield | |
end | |
end |
ENVを書き替えるのは事故が怖いのでstubメソッドのした方が安心かも
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
こちらで修正しました。
f546eb4
rubocopでこの警告が出たため、yieldを使わない形に書き換えています。
https://www.rubydoc.info/gems/rubocop/RuboCop/Cop/Style/ExplicitBlockArgument
@JunichiIto 再度ご確認お願いします:pray: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1点コメントしましたが、その点が問題なければこれでOKです〜 🙆♂️
@@ -6,6 +6,7 @@ class ApplicationController < ActionController::Base | |||
include PolicyHelper | |||
helper_method :staging? | |||
protect_from_forgery with: :exception | |||
before_action :require_scheduler_inheritation, if: -> { request.path_info.start_with?('/scheduler') } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
テストを書いてないのでちゃんと動くかどうか不安だけど、ローカルでは動作確認済みですよね?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
こちらと同じ方法で確認しました!
#6970 (comment)
@JunichiIto @siso25 レビューありがとうございました:bow: @komagata メンバーのレビューが完了しましたので、レビューをお願いします:bow: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
確認させて頂きました。OKです〜🙆♂️
📝 ステージング環境での動作確認 |
Issue
概要
下記PRのマージ後に動作確認したところ、本番環境でベストアンサーなし通知が動作していませんでした。
#6578
ベストアンサーなしの通知をキックするコントローラがSchedulerControllerを継承していないことが原因と判明したため、SchedulerControllerを継承させるよう修正しました。
https://bootcamp.fjord.jp/questions/1766#answer_6530
また、こちらのイシューで指摘されている通り、以下の修正を行いました。
上記の修正に合わせ、既存の定期処理の実装も修正しました。
変更確認方法
以下の挙動を確認する。
TOKEN
が設定されていないとき、アクセスすると401エラーになることTOKEN
が設定されているとき、正しいトークンを指定しないでアクセスすると401エラーになることTOKEN
が設定されているとき、正しいトークンを指定してアクセスすると問題なく動くことなお、非ログイン状態でアクセスできることを確認ため、テストはブラウザのシークレットモードで行う。本番環境で実行するときは、スケジューラが非ログイン状態でURLをキックするため。
bug/notification-on-no-correct-answer-on-production
をローカルに取り込むbin/rails s
でサーバを立ち上げるhttp://localhost:3000/scheduler/daily/notify_certain_period_passed_after_last_answer
bin/rails s
でサーバを立ち上げるhttp://localhost:3000/scheduler/daily/notify_certain_period_passed_after_last_answer?token=hog
http://localhost:3000/scheduler/daily/notify_certain_period_passed_after_last_answer?token=hoge
参考PR
この機能が実装されたPR