File: secure_password_test.rb

package info (click to toggle)
rails 2%3A7.2.2.1%2Bdfsg-7
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 43,352 kB
  • sloc: ruby: 349,799; javascript: 30,703; yacc: 46; sql: 43; sh: 29; makefile: 27
file content (108 lines) | stat: -rw-r--r-- 3,651 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# frozen_string_literal: true

require "cases/helper"
require "models/user"

class SecurePasswordTest < ActiveRecord::TestCase
  setup do
    # Speed up tests
    @original_min_cost = ActiveModel::SecurePassword.min_cost
    ActiveModel::SecurePassword.min_cost = true

    @user = User.create(password: "abc123", recovery_password: "123abc")
  end

  teardown do
    ActiveModel::SecurePassword.min_cost = @original_min_cost
  end

  test "authenticate_by authenticates when password is correct" do
    assert_equal @user, User.authenticate_by(token: @user.token, password: @user.password)
  end

  test "authenticate_by does not authenticate when password is incorrect" do
    assert_nil User.authenticate_by(token: @user.token, password: "wrong")
  end

  test "authenticate_by takes the same amount of time regardless of whether record is found" do
    # Warm-up (mostly to ensure the DB connection is established)
    User.authenticate_by(token: @user.token, password: @user.password)

    retry_flaky_test do
      # Benchmark.realtime returns fractional seconds.  Thus, summing over 1000
      # iterations is equivalent to averaging over 1000 iterations and then
      # multiplying by 1000 to convert to milliseconds.
      found_average_time_in_ms = 1000.times.sum do
        Benchmark.realtime do
          User.authenticate_by(token: @user.token, password: @user.password)
        end
      end

      not_found_average_time_in_ms = 1000.times.sum do
        Benchmark.realtime do
          User.authenticate_by(token: "wrong", password: @user.password)
        end
      end

      assert_in_delta found_average_time_in_ms, not_found_average_time_in_ms, 0.5
    end
  end

  test "authenticate_by short circuits when password is nil" do
    assert_no_queries do
      assert_nil User.authenticate_by(token: @user.token, password: nil)
    end
  end

  test "authenticate_by short circuits when password is an empty string" do
    assert_no_queries do
      assert_nil User.authenticate_by(token: @user.token, password: "")
    end
  end

  test "authenticate_by finds record using multiple attributes" do
    assert_equal @user, User.authenticate_by(token: @user.token, auth_token: @user.auth_token, password: @user.password)
    assert_nil User.authenticate_by(token: @user.token, auth_token: "wrong", password: @user.password)
  end

  test "authenticate_by authenticates using multiple passwords" do
    assert_equal @user, User.authenticate_by(token: @user.token, password: @user.password, recovery_password: @user.recovery_password)
    assert_nil User.authenticate_by(token: @user.token, password: @user.password, recovery_password: "wrong")
  end

  test "authenticate_by requires at least one password" do
    assert_raises ArgumentError do
      User.authenticate_by(token: @user.token)
    end
  end

  test "authenticate_by requires at least one attribute" do
    assert_raises ArgumentError do
      User.authenticate_by(password: @user.password)
    end
  end

  test "authenticate_by accepts any object that implements to_h" do
    params = Enumerator.new { raise "must access via to_h" }

    assert_called_with(params, :to_h, [], returns: { token: @user.token, password: @user.password }) do
      assert_equal @user, User.authenticate_by(params)
    end

    assert_called_with(params, :to_h, [], returns: { token: "wrong", password: @user.password }) do
      assert_nil User.authenticate_by(params)
    end
  end

  private
    def retry_flaky_test(retry_count: 3)
      yield
    rescue Minitest::Assertion
      if retry_count > 0
        retry_count -= 1
        retry
      else
        raise
      end
    end
end