Back to Repositories

Validating Authentication Core Components in Devise Framework

This test suite verifies core functionality of the Devise authentication framework, focusing on configuration, encryption, and module management. It ensures the reliability of authentication mechanisms and proper integration with Warden.

Test Coverage Overview

The test suite provides comprehensive coverage of Devise’s fundamental components including:
  • BCrypt password encryption and validation
  • Configuration management and customization
  • Warden integration and setup
  • Module system extensibility
  • Email validation patterns
  • Secure string comparison

Implementation Analysis

The testing approach utilizes ActiveSupport::TestCase with focused unit tests for isolated component verification. The implementation leverages Ruby’s module system and struct-based test doubles for encryption testing, while employing swap patterns for configuration testing.

Key patterns include dependency injection for Warden configuration and modular test organization for distinct feature sets.

Technical Details

Testing tools and configuration:
  • ActiveSupport::TestCase as the base testing framework
  • BCrypt for password encryption verification
  • Warden integration for authentication workflow
  • Custom helper methods for configuration management
  • Struct-based test doubles for isolation

Best Practices Demonstrated

The test suite exemplifies several testing best practices:
  • Isolated component testing with proper setup and teardown
  • Comprehensive edge case coverage for security-critical functions
  • Clear test naming conventions and organization
  • Effective use of test helpers and shared contexts
  • Proper validation of security-sensitive features

heartcombo/devise

test/devise_test.rb

            
# frozen_string_literal: true

require 'test_helper'

module Devise
  def self.yield_and_restore
    @@warden_configured = nil
    c, b = @@warden_config, @@warden_config_blocks
    yield
  ensure
    @@warden_config, @@warden_config_blocks = c, b
  end
end

class DeviseTest < ActiveSupport::TestCase
  test 'bcrypt on the class' do
    password = "super secret"
    klass    = Struct.new(:pepper, :stretches).new("blahblah", 2)
    hash     = Devise::Encryptor.digest(klass, password)
    assert_equal ::BCrypt::Password.create(hash), hash

    klass    = Struct.new(:pepper, :stretches).new("bla", 2)
    hash     = Devise::Encryptor.digest(klass, password)
    assert_not_equal ::BCrypt::Password.new(hash), hash
  end

  test 'model options can be configured through Devise' do
    swap Devise, allow_unconfirmed_access_for: 113, pepper: "foo" do
      assert_equal 113, Devise.allow_unconfirmed_access_for
      assert_equal "foo", Devise.pepper
    end
  end

  test 'setup block yields self' do
    Devise.setup do |config|
      assert_equal Devise, config
    end
  end

  test 'stores warden configuration' do
    assert_kind_of Devise::Delegator, Devise.warden_config.failure_app
    assert_equal :user, Devise.warden_config.default_scope
  end

  test 'warden manager user configuration through a block' do
    Devise.yield_and_restore do
      executed = false
      Devise.warden do |config|
        executed = true
        assert_kind_of Warden::Config, config
      end

      Devise.configure_warden!
      assert executed
    end
  end

  test 'warden manager user configuration through multiple blocks' do
    Devise.yield_and_restore do
      executed = 0

      3.times do
        Devise.warden { |config| executed += 1 }
      end

      Devise.configure_warden!
      assert_equal 3, executed
    end
  end

  test 'add new module using the helper method' do
    Devise.add_module(:coconut)
    assert_equal 1, Devise::ALL.select { |v| v == :coconut }.size
    assert_not Devise::STRATEGIES.include?(:coconut)
    assert_not defined?(Devise::Models::Coconut)
    Devise::ALL.delete(:coconut)

    Devise.add_module(:banana, strategy: :fruits)
    assert_equal :fruits, Devise::STRATEGIES[:banana]
    Devise::ALL.delete(:banana)
    Devise::STRATEGIES.delete(:banana)

    Devise.add_module(:kivi, controller: :fruits)
    assert_equal :fruits, Devise::CONTROLLERS[:kivi]
    Devise::ALL.delete(:kivi)
    Devise::CONTROLLERS.delete(:kivi)
  end

  test 'should complain when comparing empty or different sized passes' do
    [nil, ""].each do |empty|
      assert_not Devise.secure_compare(empty, "something")
      assert_not Devise.secure_compare("something", empty)
      assert_not Devise.secure_compare(empty, empty)
    end
    assert_not Devise.secure_compare("size_1", "size_four")
  end

  test 'Devise.email_regexp should match valid email addresses' do
    valid_emails = ["[email protected]", "[email protected]", "[email protected]", "[email protected]", "test@tt", "[email protected]"]
    non_valid_emails = ["rex", "test [email protected]", "test_user@example server.com"]

    valid_emails.each do |email|
      assert_match Devise.email_regexp, email
    end
    non_valid_emails.each do |email|
      assert_no_match Devise.email_regexp, email
    end
  end

  test 'Devise.activerecord51? deprecation' do
    assert_deprecated("`Devise.activerecord51?` is deprecated", Devise.deprecator) do
      Devise.activerecord51?
    end
  end
end