Skip to content
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

Merged
merged 18 commits into from
Nov 15, 2023

Conversation

ogawa-tomo
Copy link
Contributor

@ogawa-tomo ogawa-tomo commented Oct 11, 2023

Issue

概要

下記PRのマージ後に動作確認したところ、本番環境でベストアンサーなし通知が動作していませんでした。
#6578

ベストアンサーなしの通知をキックするコントローラがSchedulerControllerを継承していないことが原因と判明したため、SchedulerControllerを継承させるよう修正しました。
https://bootcamp.fjord.jp/questions/1766#answer_6530

また、こちらのイシューで指摘されている通り、以下の修正を行いました。

  • 環境変数TOKENが未設定だとSchedulerControllerの認証がパスしないようにする
  • スケジューラを呼び出すテストコードではユーザー認証せずにトークン認証するようにする
  • /schdulerで始まるパスをリクエストされた && self が SchedulerController を継承していないなら 500エラーとする

上記の修正に合わせ、既存の定期処理の実装も修正しました。

変更確認方法

以下の挙動を確認する。

  • サーバに環境変数TOKENが設定されていないとき、アクセスすると401エラーになること
  • サーバに環境変数TOKENが設定されているとき、正しいトークンを指定しないでアクセスすると401エラーになること
  • サーバに環境変数TOKENが設定されているとき、正しいトークンを指定してアクセスすると問題なく動くこと

なお、非ログイン状態でアクセスできることを確認ため、テストはブラウザのシークレットモードで行う。本番環境で実行するときは、スケジューラが非ログイン状態でURLをキックするため。

  1. bug/notification-on-no-correct-answer-on-productionをローカルに取り込む
  2. bin/rails sでサーバを立ち上げる
  3. 適当なユーザー(ここではhatsuno)でログインし、質問を作成する
  4. 別の適当なユーザー(ここではkimura)でログインし、4. で作成した質問に対する回答を作成する
  5. Railsコンソールで下記のコメントを実行し、回答の作成日を1週間前にする
> Answer.last.update!(created_at: 1.week.ago)
  1. シークレットモードでブラウザを開き(非ログイン状態で動作を確認するため)、以下のようにtokenパラメータを指定せずにURLにアクセスする
    http://localhost:3000/scheduler/daily/notify_certain_period_passed_after_last_answer
  2. 401エラーとなることを確認する
    image
  3. いったんRailsサーバを終了し、ターミナルでコマンドを実行し、環境変数TOKENを適当に設定する
> export TOKEN=hoge
  1. 再度bin/rails sでサーバを立ち上げる
  2. シークレットモードでブラウザを開き、以下のように正しくないtokenパラメータを指定したURLにアクセスする
    http://localhost:3000/scheduler/daily/notify_certain_period_passed_after_last_answer?token=hog
  3. 401エラーとなることを確認する
    image
  4. 続いて、以下のように正しいtokenパラメータを指定したURLにアクセスする
    http://localhost:3000/scheduler/daily/notify_certain_period_passed_after_last_answer?token=hoge
  5. http://localhost:3000/letter_opener/ にアクセスし、質問を作成したユーザー(ここではhatsuno)にメールが飛んでいることを確認する
    image
  6. 質問を作成したユーザー(ここではhatsuno)でログインし、web通知が来ていることを確認する
    image

参考PR

この機能が実装されたPR

@ogawa-tomo ogawa-tomo force-pushed the bug/notification-on-no-correct-answer-on-production branch from 9936c06 to 048b62f Compare October 14, 2023 03:15
@ogawa-tomo ogawa-tomo marked this pull request as draft October 14, 2023 04:00
@ogawa-tomo ogawa-tomo force-pushed the bug/notification-on-no-correct-answer-on-production branch from 175a0d4 to 048b62f Compare October 14, 2023 10:52
@ogawa-tomo ogawa-tomo changed the base branch from main to bug/debug-flaky-regular-events-test October 14, 2023 12:43
@ogawa-tomo ogawa-tomo force-pushed the bug/notification-on-no-correct-answer-on-production branch from 048b62f to 89655c4 Compare October 14, 2023 12:56
@ogawa-tomo ogawa-tomo marked this pull request as ready for review October 14, 2023 13:26
@ogawa-tomo
Copy link
Contributor Author

@omochiumaiumai こちら、よろしければレビューお願いできますでしょうか?

@omochiumaiumai
Copy link
Contributor

@ogawa-tomo
お疲れ様です!
今週は時間が取れず対応できないため、申し訳ありませんが他の方にお願いしていただければ幸いです🙇‍♂💦

@ogawa-tomo ogawa-tomo force-pushed the bug/notification-on-no-correct-answer-on-production branch from 50969e4 to 248b2e9 Compare October 15, 2023 11:59
@ogawa-tomo
Copy link
Contributor Author

@omochiumaiumai 承知しました、別の方にお願いしようと思います:bow:

@ogawa-tomo
Copy link
Contributor Author

テストが落ちているのと、テストでもトークン認証をチェックできたほうがよいと思い、いったんドラフトに戻す

@ogawa-tomo ogawa-tomo marked this pull request as draft October 15, 2023 12:21
@ogawa-tomo ogawa-tomo removed the request for review from omochiumaiumai October 15, 2023 12:26
@ogawa-tomo ogawa-tomo force-pushed the bug/notification-on-no-correct-answer-on-production branch from 9f08365 to 248b2e9 Compare October 16, 2023 12:33
Base automatically changed from bug/debug-flaky-regular-events-test to main October 16, 2023 18:12
@ogawa-tomo ogawa-tomo force-pushed the bug/notification-on-no-correct-answer-on-production branch from 956f4fa to 2e6e84b Compare October 23, 2023 12:35
@siso25
Copy link
Contributor

siso25 commented Oct 24, 2023

@ogawa-tomo
お疲れ様です。
こちら修正中かと思いますが、修正範囲について相談させてください。

こちらのpull requestで「定期処理がSchedulerControllerを継承していないときエラーにする」対応を行うと、fetch_external_entryのシステムテストが失敗するようになると思います。(ExternalEntryがSchedulerControllerを継承していないため)

上記を回避するためには「ExternalEntryにScheduleControllerを継承する」対応を行う必要がありますが、以下のpull requestで先に対応してしまうと、今度はtokenを使った定期実行テスト用のヘルパーメソッドの実装が被ってしまいます。
#6941

そのため、以下の対応についてはこちらのpull requestで一緒に対応いただいた方が良いかもしれないと思いましたが、いかがでしょうか。

  • ExternalEntryにSchedulerControllerを継承させる
  • test/system/external_entries_test.rbで定期実行用のヘルパーメソッド(ogawa-tomoさんに定義していただいたもの)を使うようにする

@ogawa-tomo
Copy link
Contributor Author

@siso25 ご連絡ありがとうございます!たしかにおっしゃる通りですね。その2点についてもこのPRで対応してしまおうと思います。関連する修正なので、小分けにするより一緒にやってしまったほうがよさそうな気もしますし。

@siso25
Copy link
Contributor

siso25 commented Oct 25, 2023

@ogawa-tomo
すみません。よろしくお願いします!🙏

@ogawa-tomo
Copy link
Contributor Author

@siso25 上記対応しました!こちらのPR、レビュー依頼もお願いしてしまってもよろしいでしょうか?sisoさんがレビュー依頼可能なステータスなのか分かっていないのですが、sisoさんのPRにも関連しますし、確認はお願いしたく思っています:pray:

@ogawa-tomo ogawa-tomo requested a review from siso25 October 25, 2023 12:47
Comment on lines 316 to 321
mock_env({ 'TOKEN' => 'token' }) do
visit_with_query scheduler_daily_notify_certain_period_passed_after_last_answer_path, { token: 'token' }
end
Copy link
Contributor Author

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つを独立にテストすることも可能になる(今回はやっていませんが)というメリットもあると思います。

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

修正ありがとうございます!
たしかにこちらの方が環境変数をモックしていることが分かりやすそうです。

@siso25
Copy link
Contributor

siso25 commented Oct 26, 2023

@ogawa-tomo
レビューの件承知しました。
今週末までには確認したいと思います!

Copy link
Contributor

@JunichiIto JunichiIto left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

コメントしました〜。

Comment on lines 55 to 58
return unless request.path_info.start_with?('/scheduler')
return if is_a?(SchedulerController)

head :internal_server_error
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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

とした方がコードの意図が明確になりそう。

そして、できたらこの仕組みもテストコードがほしいけど、ちょっと難しいかなあ。。

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ガード節が好きでなんとなく使ってしまったのですが、たしかにこちらのほうが今回の意図を肯定形で表現できて分かりやすいですね。
ご提案いただいたコードだとrubocopにガード節か後置ifのどちらかにしろと言われたので、後置ifを用いた書き方に修正しました。
782f084

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

この仕組みのテストコードは実現方法が思いつかずでして、そのままにしています:pray:

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

了解です。最低限、一時的にcontrollerの親クラスを書き替えるなどして、手元で期待した動作をしているかどうかだけ確認しておいてほしいです〜。(スクショとかのエビデンスがあればなお良し)

Copy link
Contributor Author

@ogawa-tomo ogawa-tomo Nov 4, 2023

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に一時的に書き換え、ディスクリプションに記載した方法で動作確認を行いました。

class Scheduler::Daily::NotifyCertainPeriodPassedAfterLastAnswerController < SchedulerController

その結果、500エラーとなることを確認しました。
image
親クラスを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]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TOKENが未設定だとエラー、paramsのtokenが不一致だったらエラー、というパターンもテストしたい。
(門番が門番として機能していることをテストする)

Copy link
Contributor Author

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

@@ -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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
mock_env({ 'TOKEN' => 'token' }) do
mock_env('TOKEN' => 'token') do

でよさそう

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

修正しました!引数の最後がハッシュだった場合は{}が省略できるのですね。
03e3b7e

@@ -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' }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
visit_with_query scheduler_daily_auto_retire_path, { token: 'token' }
visit_with_query scheduler_daily_auto_retire_path, token: 'token'

でよさそう

そもそも、

Suggested change
visit_with_query scheduler_daily_auto_retire_path, { token: 'token' }
visit scheduler_daily_auto_retire_path(token: 'token')

でいいのでは?

Copy link
Contributor Author

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「テストの質問」のベストアンサーがまだ選ばれていません。'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

なるほどです。通知一覧のページにアクセスできていることの検証を追加しました。
ba0c596

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

↑だけだと通知のメッセージが変わったときも通っちゃうな、と思い直したので、通知の件数が変わっていないことの検証も追加しました:bow:
a6e1f58

@ogawa-tomo ogawa-tomo force-pushed the bug/notification-on-no-correct-answer-on-production branch from 432e576 to 03d531f Compare November 6, 2023 11:51
@ogawa-tomo
Copy link
Contributor Author

@JunichiIto コメントいただいた点を修正しましたのでご確認お願いします:pray:

@ogawa-tomo ogawa-tomo requested a review from JunichiIto November 6, 2023 12:12
Copy link
Contributor

@JunichiIto JunichiIto left a 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)
Copy link
Contributor

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') }

とした上で

Suggested change
head :internal_server_error if request.path_info.start_with?('/scheduler') && !is_a?(SchedulerController)
head :internal_server_error unless is_a?(SchedulerController)

とした方がコードの意図が明確になりそうな気がしました

Copy link
Contributor Author

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が未設定
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

コメントいらないんじゃないかなー。
(僕のサンプルコードのコメントはあくまで説明用だったので)

Copy link
Contributor Author

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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

質問)nilって付けないと問題が起きるんでしょうか?

Copy link
Contributor Author

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

Comment on lines 4 to 12
def mock_env(partial_env_hash)
old = ENV.to_hash
ENV.update partial_env_hash
begin
yield
ensure
ENV.replace old
end
end
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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メソッドのした方が安心かも

Copy link
Contributor Author

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

@ogawa-tomo
Copy link
Contributor Author

@JunichiIto 再度ご確認お願いします:pray:

@ogawa-tomo ogawa-tomo requested a review from JunichiIto November 7, 2023 11:58
Copy link
Contributor

@JunichiIto JunichiIto left a 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') }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

テストを書いてないのでちゃんと動くかどうか不安だけど、ローカルでは動作確認済みですよね?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

こちらと同じ方法で確認しました!
#6970 (comment)

@ogawa-tomo
Copy link
Contributor Author

@JunichiIto @siso25 レビューありがとうございました:bow:

@komagata メンバーのレビューが完了しましたので、レビューをお願いします:bow:

@ogawa-tomo ogawa-tomo requested a review from komagata November 11, 2023 02:54
Copy link
Member

@komagata komagata left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

確認させて頂きました。OKです〜🙆‍♂️

@komagata komagata merged commit e64f379 into main Nov 15, 2023
8 checks passed
@komagata komagata deleted the bug/notification-on-no-correct-answer-on-production branch November 15, 2023 07:03
@github-actions github-actions bot mentioned this pull request Nov 15, 2023
8 tasks
@ogawa-tomo
Copy link
Contributor Author

📝 ステージング環境での動作確認
こちらと同様の手順で確認(ただし、ブラウザで定期処理URLにアクセスする箇所はシークレットモードを用いる)
#6578 (comment)
定期処理URLにアクセスした際にBASIC認証のID/PWを要求されたが、それを入力するとweb通知・メール通知ともに正常に届いた

image
image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants