実現したいこと
- エラー原因の特定。
- テストのパス
発生している問題・分からないこと
11.3.1authenticated?メソッドの抽象化
https://railstutorial.jp/chapters/account_activation?version=7.0#sec-generalizing_the_authenticated_method
の進行中でテストをしたところエラーが2件発生しました。
エラーメッセージ
error
1ERROR SessionsHelperTest#test_current_user_returns_nil_when_remember_digest_is_wrong (0.13s) 2Minitest::UnexpectedError: ArgumentError: wrong number of arguments (given 1, expected 2) 3 app/models/user.rb:39:in `authenticated?' 4 test/helpers/sessions_helper_test.rb:26:in `current_user' 5 test/helpers/sessions_helper_test.rb:17:in `block in <class:SessionsHelperTest>' 6 7ERROR SessionsHelperTest#test_current_user_returns_right_user_when_session_is_nil (0.14s) 8Minitest::UnexpectedError: ArgumentError: wrong number of arguments (given 1, expected 2) 9 app/models/user.rb:39:in `authenticated?' 10 test/helpers/sessions_helper_test.rb:26:in `current_user' 11 test/helpers/sessions_helper_test.rb:11:in `block in <class:SessionsHelperTest>'
該当のソースコード
Ruby
1authenticated?メソッドを含めたapp/models/user.rb 2 3class User < ApplicationRecord 4 attr_accessor :remember_token, :activation_token 5 before_save :downcase_email 6 before_create :create_activation_digest 7 validates :name, presence: true, length: { maximum: 50 } 8 VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i 9 validates :email, presence: true, length: { maximum: 255 }, 10 format: { with: VALID_EMAIL_REGEX }, 11 uniqueness: true 12 has_secure_password 13 validates :password, presence: true, length: { minimum: 6 }, allow_nil: true 14 15 # 渡された文字列のハッシュ値を返す 16 def User.digest(string) 17 cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST : 18 BCrypt::Engine.cost 19 BCrypt::Password.create(string, cost: cost) 20 end 21 22 # ランダムなトークンを返す 23 def User.new_token 24 SecureRandom.urlsafe_base64 25 end 26 27 # 永続的セッションのためにユーザーをデータベースに記憶する 28 def remember 29 self.remember_token = User.new_token 30 update_attribute(:remember_digest, User.digest(remember_token)) 31 remember_digest 32 end 33 34 # セッションハイジャック防止のためにセッショントークンを返す 35 # この記憶ダイジェストを再利用しているのは単に利便性のため 36 def session_token 37 remember_digest || remember 38 end 39 40 # 渡されたトークンがダイジェストと一致したらtrueを返す(追加した部分です) 41 def authenticated?(attribute, token) 42 digest = send("#{attribute}_digest") 43 return false if digest.nil? 44 BCrypt::Password.new(digest).is_password?(token) 45 end 46 47 # ユーザーのログイン情報を破棄する 48 def forget 49 update_attribute(:remember_digest, nil) 50 end 51 52 private 53 54 # メールアドレスをすべて小文字にする 55 def downcase_email 56 self.email = email.downcase 57 end 58 59 # 有効化トークンとダイジェストを作成および代入する 60 def create_activation_digest 61 self.activation_token = User.new_token 62 self.activation_digest = User.digest(activation_token) 63 end 64 65end 66 67
Ruby
1current_user内の抽象化したauthenticated?メソッド 2 3app/helpers/sessions_helper.rb 4 5module SessionsHelper 6 7 # 渡されたユーザーでログインする 8 def log_in(user) 9 session[:user_id] = user.id 10 # セッションリプレイ攻撃から保護する 11 # 詳しくは https://techracho.bpsinc.jp/hachi8833/2023_06_02/130443 を参照 12 session[:session_token] = user.session_token 13 end 14 15 # 永続的セッションのためにユーザーをデータベースに記憶する 16 def remember(user) 17 user.remember 18 cookies.permanent.encrypted[:user_id] = user.id 19 cookies.permanent[:remember_token] = user.remember_token 20 end 21 22 # 記憶トークンcookieに対応するユーザーを返す 23 def current_user 24 if (user_id = session[:user_id]) 25 user = User.find_by(id: user_id) 26 @current_user ||= user if session[:session_token] == user.session_token 27 elsif (user_id = cookies.encrypted[:user_id]) 28 user = User.find_by(id: user_id) 29 if user && user.authenticated?(:remember, cookies[:remember_token]) ⇦追加したコードです 30 log_in user 31 @current_user = user 32 end 33 end 34 end 35 36 37 # 渡されたユーザーがカレントユーザーであればtrueを返す 38 def current_user?(user) 39 user && user == current_user 40 end 41 42 # ユーザーがログインしていればtrue、その他ならfalseを返す 43 def logged_in? 44 !current_user.nil? 45 end 46 47 # 永続的セッションを破棄する 48 def forget(user) 49 user.forget 50 cookies.delete(:user_id) 51 cookies.delete(:remember_token) 52 end 53 54 # 現在のユーザーをログアウトする 55 def log_out 56 forget(current_user) 57 reset_session 58 @current_user = nil 59 end 60 61 # アクセスしようとしたURLを保存する 62 def store_location 63 session[:forwarding_url] = request.original_url if request.get? 64 end 65 66end 67
Ruby
1Userテスト内の抽象化したauthenticated?メソッド 2 3test/models/user_test.rb 4 5require "test_helper" 6 7class UserTest < ActiveSupport::TestCase 8 9 def setup 10 @user = User.new(name: "Example User", email: "user@example.com", 11 password: "foobar", password_confirmation: "foobar") 12 end 13 14 test "should be valid" do 15 assert @user.valid? 16 end 17 18 test "name should be present" do 19 @user.name = "" 20 assert_not @user.valid? 21 end 22 23 test "email should be present" do 24 @user.email = " " 25 assert_not @user.valid? 26 end 27 28 test "name should not be too long" do 29 @user.name = "a" * 51 30 assert_not @user.valid? 31 end 32 33 test "email should not be too long" do 34 @user.email = "a" * 244 + "@example.com" 35 assert_not @user.valid? 36 end 37 38 test "email validation should accept valid addresses" do 39 valid_addresses = %w[user@example.com USER@foo.COM A_US-ER@foo.bar.org 40 first.last@foo.jp alice+bob@baz.cn] 41 valid_addresses.each do |valid_address| 42 @user.email = valid_address 43 assert @user.valid?, "#{valid_address.inspect} should be valid" 44 end 45 end 46 47 test "email validation should reject invalid addresses" do 48 invalid_addresses = %w[user@example,com user_at_foo.org user.name@example. 49 foo@bar_baz.com foo@bar+baz.com] 50 invalid_addresses.each do |invalid_address| 51 @user.email = invalid_address 52 assert_not @user.valid?, "#{invalid_address.inspect} should be invalid" 53 end 54 end 55 56 57 test "email addresses should be unique" do 58 duplicate_user = @user.dup 59 @user.save 60 assert_not duplicate_user.valid? 61 end 62 63 test "password should be present (nonblank)" do 64 @user.password = @user.password_confirmation = " " * 6 65 assert_not @user.valid? 66 end 67 68 test "password should have a minimum length" do 69 @user.password = @user.password_confirmation = "a" * 5 70 assert_not @user.valid? 71 end 72 73 test "authenticated? should return false for a user with nil digest" do ⇦追加した部分です。 74 assert_not @user.authenticated?(:remember, '') 75 end 76end 77
※追加したsessions_helper_test.rb
Ruby
1 2require "test_helper" 3 4class SessionsHelperTest < ActionView::TestCase 5 6 def setup 7 @user = users(:michael) 8 remember(@user) 9 end 10 11 test "current_user returns right user when session is nil" do 12 assert_equal @user, current_user 13 assert is_logged_in? 14 end 15 16 test "current_user returns nil when remember digest is wrong" do 17 @user.update_attribute(:remember_digest, User.digest(User.new_token)) 18 assert_nil current_user 19 end 20 21# 記憶トークンcookieに対応するユーザーを返す 22 def current_user 23 if (user_id = session[:user_id]) 24 @current_user ||= User.find_by(id: user_id) 25 elsif (user_id = cookies.encrypted[:user_id]) 26 user = User.find_by(id: user_id) 27 if user && user.authenticated?(cookies[:remember_token]) 28 log_in user 29 @current_user = user 30 end 31 end 32 end 33 34end 35
試したこと・調べたこと
- teratailやGoogle等で検索した
- ソースコードを自分なりに変更した
- 知人に聞いた
- その他
上記の詳細・結果
- チャットgptにて質問した結果を引用
エラーメッセージから判断すると、authenticated? メソッドに渡す引数が間違っている可能性があります。authenticated? メソッドは2つの引数を期待していますが、渡された引数が1つだけであることが原因でエラーが発生しているようです。
authenticated? メソッドに渡す引数を確認し、適切なトークン(cookies[:remember_token] など)を渡してみてください。
修正方法
authenticated? メソッドに渡す引数を確認し、適切なトークン(cookies[:remember_token] など)を渡してみてください。
以下のように修正してみてください:
修正例:
ruby
コードをコピーする
user.rb の authenticated? メソッド
def authenticated?(attribute, token)
digest = send("#{attribute}_digest")
return false if digest.nil?
BCrypt::Password.new(digest).is_password?(token)
end
sessions_helper.rb 内での呼び出し
if user && user.authenticated?(:remember, cookies[:remember_token]) # ここでトークンを渡す
log_in user
@current_user = user
end
修正箇所がないように見えます。
sessions_helper_test.rbを追記しました。リスト 11.30のrails testでgreenになるはずがエラーによってRedの状態です。
補足
Rails 7.0.8.4
回答2件
あなたの回答
tips
プレビュー