yutokyokutyo / rebuild_sample_app

https://railstutorial.jp/chapters/static_pages?version=5.0#cha-static_pages
https://rebuildsampleapp.herokuapp.com/
5 stars 0 forks source link

Exercises-12.3.3 #29

Closed yutokyokutyo closed 7 years ago

yutokyokutyo commented 7 years ago
  1. リスト 12.6にあるcreate_reset_digestメソッドはupdate_attributeを2回呼び出していますが、これは各行で1回ずつデータベースへ問い合わせしていることになります。リスト 12.20に記したテンプレートを使って、update_attributeの呼び出しを1回のupdate_columns呼び出しにまとめてみましょう (これでデータベースへの問い合わせが1回で済むようになります)。また、変更後にテストを実行し、 greenになることも確認してください。ちなみにリスト 12.20にあるコードには、前章の演習 (リスト 11.39) の解答も含まれています。
  2. リスト 12.16のテンプレートを埋めて、期限切れのパスワード再設定で発生する分岐 (リスト 12.21) を統合テストで網羅してみましょう (12.21 のコードにあるresponse.bodyは、そのページのHTML本文をすべて返すメソッドです)。 期限切れをテストする方法はいくつかありますが、リスト 12.21でオススメした手法を使えば、レスポンスの本文に「expired」という語があるかどうかでチェックできます (なお、大文字と小文字は区別されません)。
  3. 2時間経ったらパスワードを再設定できなくする方針は、セキュリティ的に好ましいやり方でしょう。しかし、もっと良くする方法はまだあります。例えば、公共の (または共有された) コンピューターでパスワード再設定が行われた場合を考えてみてください。仮にログアウトして離席したとしても、2時間以内であれば、そのコンピューターの履歴からパスワード再設定フォームを表示させ、パスワードを更新してしまうことができてしまいます (しかもそのままログイン機構まで突破されてしまいます!)。この問題を解決するために、リスト 12.22のコードを追加し、パスワードの再設定に成功したらダイジェストをnilになるように変更してみましょう3。
  4. リスト 12.18に1行追加し、1つ前の演習課題に対するテストを書いてみましょう。ヒント: リスト 9.25のassert_nilメソッドとリスト 11.33のuser.reloadメソッドを組み合わせて、reset_digest属性を直接テストしてみましょう。
yutokyokutyo commented 7 years ago

リスト 12.18に1行追加し、1つ前の演習課題に対するテストを書いてみましょう。ヒント: リスト 9.25のassert_nilメソッドとリスト 11.33のuser.reloadメソッドを組み合わせて、reset_digest属性を直接テストしてみましょう。

% git diff                                                                                                                                                  (git)-[Exercises-12.3.3]
diff --git a/test/integration/password_resets_test.rb b/test/integration/password_resets_test.rb
index 5c12946..39212e3 100644
--- a/test/integration/password_resets_test.rb
+++ b/test/integration/password_resets_test.rb
@@ -58,6 +58,7 @@ class PasswordResetsTest < ActionDispatch::IntegrationTest
     assert is_logged_in?
     assert_not flash.empty?
     assert_redirected_to user
+    assert_nil @user.reset_digest
   end

   test "expired token" do
% rails t                                                                                                                                                   (git)-[Exercises-12.3.3]
Running via Spring preloader in process 36867
Started with run options --seed 10366

 FAIL["test_password_resets", PasswordResetsTest, 2.9190750130001106]
 test_password_resets#PasswordResetsTest (2.92s)
        Expected "$2a$04$G.jarVpDd/Bcn7q5RfiKgemNR9Qe3jXx2yIXGvsDLfKkED0tw1PXK" to be nil.
        test/integration/password_resets_test.rb:61:in `block in <class:PasswordResetsTest>'

  47/47: [======================================================================================================================================] 100% Time: 00:00:03, Time: 00:00:03

Finished in 3.92936s
47 tests, 222 assertions, 1 failures, 0 errors, 0 skips

テストに失敗してしまった。

yutokyokutyo commented 7 years ago

password resets のintegrationテストの中で有効なパスワードを入力した後にreset_digestがnilになることを確かめるテストを書いた。でも実際にはnilにならずに値が入っていた。なんでだろう?

yutokyokutyo commented 7 years ago

binding.pryしてみると、やっぱり@user.reset_digest には 値が入っていた。

% rails t                                                                                                                                                   (git)-[Exercises-12.3.3]
Running via Spring preloader in process 38763
Started with run options --seed 16909

  47/13: [====================================                                                                                                   ] 27% Time: 00:00:01,  ETA: 00:00:05
Frame number: 0/29

From: /Users/y.kondo/project/workspace/rebuild_sample_app/test/integration/password_resets_test.rb @ line 62 PasswordResetsTest#test_password_resets:

    57:                             password_confirmation: "foobaz" } }
    58:     assert is_logged_in?
    59:     assert_not flash.empty?
    60:     assert_redirected_to user
    61:     binding.pry
 => 62:     assert_nil @user.reset_digest
    63:   end
    64:
    65:   test "expired token" do
    66:     get new_password_reset_path
    67:     post password_resets_path,

[1] pry(#<PasswordResetsTest>)> @user.reset_digest
=> "$2a$04$YEQ1vsu8EXX858juB6v2JOYLaA8j8AZwjZuWZdMKqXoZ7iotIxENS"
yutokyokutyo commented 7 years ago

app/controllers/password_resets_controller.rb の update アクションに書いた @user.update_attribute(:reset_digest, nil) が実行されていればnilになるはず。その直前にpryを置いて本当に実行されているのかどうかを確かめてみよう。

yutokyokutyo commented 7 years ago
% rails t test/integration/password_resets_test.rb                                                                                                          (git)-[Exercises-12.3.3]
Running via Spring preloader in process 40515
Started with run options --seed 2586

  2/1: [====================================================================                                                                     ] 50% Time: 00:00:01,  ETA: 00:00:02
Frame number: 0/94

From: /Users/y.kondo/project/workspace/rebuild_sample_app/app/controllers/password_resets_controller.rb @ line 33 PasswordResetsController#update:

    25: def update
    26:   if params[:user][:password].empty?                  # 新しいパスワードが空文字列になっていないか
    27:     @user.errors.add(:password, "can't be empty")
    28:     render 'edit'
    29:   elsif @user.update_attributes(user_params)          # 新しいパスワードが正しければ、更新する
    30:     log_in @user
    31:     @user.update_attribute(:reset_digest, nil)
    32:     binding.pry
 => 33:     flash[:success] = "Password has been reset."
    34:     redirect_to @user
    35:   else
    36:     render 'edit'                                     # 無効なパスワードであれば失敗させる
    37:   end
    38: end

[1] pry(#<PasswordResetsController>)> @user
=> #<User:0x007fd5438fa3e8
 id: 762146111,
 name: "Michael Example",
 email: "michael@example.com",
 created_at: Tue, 02 May 2017 00:46:15 UTC +00:00,
 updated_at: Tue, 02 May 2017 00:46:16 UTC +00:00,
 password_digest: "$2a$04$O8RgYo0yUkFZn9qXhBiRsO6fnObwsEM825PFc0uXBzShYZdpZ37vW",
 remember_digest: nil,
 admin: true,
 activation_digest: nil,
 activated: true,
 activated_at: Tue, 02 May 2017 00:46:15 UTC +00:00,
 reset_digest: nil,
 reset_sent_at: Tue, 02 May 2017 00:46:16 UTC +00:00>

nil になってるじゃまいか!

yutokyokutyo commented 7 years ago

大分絞れてきたので後はどのタイミングで呼ばれているか。テストの方で少しづつコメントアウトしてみて範囲を狭めていこう。

yutokyokutyo commented 7 years ago
      password_reset PATCH  /password_resets/:id(.:format)          password_resets#update
                        PUT    /password_resets/:id(.:format)          password_resets#update

このときだよなぁ

yutokyokutyo commented 7 years ago

実装は合っているけどテストの書き方がミスっていた。 reload が必要なのであった。以前やった同じよう書き方を探すことにより解決。

yutokyokutyo commented 7 years ago

テストが通ったのでマージ 💪