Back to Repositories

Validating Attachment File Name Patterns in Paperclip

This test suite validates the file name validation functionality in Paperclip’s attachment system. It ensures proper handling of allowed and disallowed file extensions through both whitelist and blacklist approaches, with comprehensive error message validation.

Test Coverage Overview

The test suite thoroughly examines Paperclip’s AttachmentFileNameValidator functionality, covering both positive and negative validation scenarios.

  • Whitelist validation using single and multiple file extensions
  • Blacklist validation patterns for file name restrictions
  • Custom and default error message handling
  • Nil value handling and validation configuration

Implementation Analysis

The testing approach utilizes RSpec’s behavior-driven development patterns with extensive use of context blocks for organized test scenarios.

Key implementation features include:
  • Stub-based file name testing
  • Dynamic validator configuration
  • Regular expression pattern matching
  • Error message verification

Technical Details

Testing infrastructure includes:
  • RSpec test framework
  • Model rebuilding helpers
  • Stub/Mock implementations
  • Custom validator builder methods
  • Regular expression pattern matching for file extensions

Best Practices Demonstrated

The test suite exemplifies strong testing practices through organized and comprehensive validation coverage.

  • Isolated test contexts for different validation scenarios
  • Clear test case organization and naming
  • Comprehensive edge case coverage
  • DRY testing patterns with shared setup methods
  • Explicit error message validation

thoughtbot/paperclip

spec/paperclip/validators/attachment_file_name_validator_spec.rb

            
require 'spec_helper'

describe Paperclip::Validators::AttachmentFileNameValidator do
  before do
    rebuild_model
    @dummy = Dummy.new
  end

  def build_validator(options)
    @validator = Paperclip::Validators::AttachmentFileNameValidator.new(options.merge(
      attributes: :avatar
    ))
  end

  context "with a failing validation" do
    before do
      build_validator matches: /.*\.png$/, allow_nil: false
      @dummy.stubs(avatar_file_name: "data.txt")
      @validator.validate(@dummy)
    end

    it "adds error to the base object" do
      assert @dummy.errors[:avatar].present?,
        "Error not added to base attribute"
    end

    it "adds error to base object as a string" do
      expect(@dummy.errors[:avatar].first).to be_a String
    end
  end

  it "does not add error to the base object with a successful validation" do
    build_validator matches: /.*\.png$/, allow_nil: false
    @dummy.stubs(avatar_file_name: "image.png")
    @validator.validate(@dummy)

    assert @dummy.errors[:avatar].blank?, "Error was added to base attribute"
  end

  context "whitelist format" do
    context "with an allowed type" do
      context "as a single regexp" do
        before do
          build_validator matches: /.*\.jpg$/
          @dummy.stubs(avatar_file_name: "image.jpg")
          @validator.validate(@dummy)
        end

        it "does not set an error message" do
          assert @dummy.errors[:avatar_file_name].blank?
        end
      end

      context "as a list" do
        before do
          build_validator matches: [/.*\.png$/, /.*\.jpe?g$/]
          @dummy.stubs(avatar_file_name: "image.jpg")
          @validator.validate(@dummy)
        end

        it "does not set an error message" do
          assert @dummy.errors[:avatar_file_name].blank?
        end
      end
    end

    context "with a disallowed type" do
      it "sets a correct default error message" do
        build_validator matches: /^text\/.*/
        @dummy.stubs(avatar_file_name: "image.jpg")
        @validator.validate(@dummy)

        assert @dummy.errors[:avatar_file_name].present?
        expect(@dummy.errors[:avatar_file_name]).to include "is invalid"
      end

      it "sets a correct custom error message" do
        build_validator matches: /.*\.png$/, message: "should be a PNG image"
        @dummy.stubs(avatar_file_name: "image.jpg")
        @validator.validate(@dummy)

        expect(@dummy.errors[:avatar_file_name]).to include "should be a PNG image"
      end
    end
  end

  context "blacklist format" do
    context "with an allowed type" do
      context "as a single regexp" do
        before do
          build_validator not: /^text\/.*/
          @dummy.stubs(avatar_file_name: "image.jpg")
          @validator.validate(@dummy)
        end

        it "does not set an error message" do
          assert @dummy.errors[:avatar_file_name].blank?
        end
      end

      context "as a list" do
        before do
          build_validator not: [/.*\.png$/, /.*\.jpe?g$/]
          @dummy.stubs(avatar_file_name: "image.gif")
          @validator.validate(@dummy)
        end

        it "does not set an error message" do
          assert @dummy.errors[:avatar_file_name].blank?
        end
      end
    end

    context "with a disallowed type" do
      it "sets a correct default error message" do
        build_validator not: /data.*/
        @dummy.stubs(avatar_file_name: "data.txt")
        @validator.validate(@dummy)

        assert @dummy.errors[:avatar_file_name].present?
        expect(@dummy.errors[:avatar_file_name]).to include "is invalid"
      end

      it "sets a correct custom error message" do
        build_validator not: /.*\.png$/, message: "should not be a PNG image"
        @dummy.stubs(avatar_file_name: "image.png")
        @validator.validate(@dummy)

        expect(@dummy.errors[:avatar_file_name]).to include "should not be a PNG image"
      end
    end
  end

  context "using the helper" do
    before do
      Dummy.validates_attachment_file_name :avatar, matches: /.*\.jpg$/
    end

    it "adds the validator to the class" do
      assert Dummy.validators_on(:avatar).any?{ |validator| validator.kind == :attachment_file_name }
    end
  end

  context "given options" do
    it "raises argument error if no required argument was given" do
      assert_raises(ArgumentError) do
        build_validator message: "Some message"
      end
    end

    it "does not raise argument error if :matches was given" do
      build_validator matches: /.*\.jpg$/
    end

    it "does not raise argument error if :not was given" do
      build_validator not: /.*\.jpg$/
    end
  end
end