Back to Repositories

Testing Condition Node Predicate Handling in Ransack

This test suite validates the Condition node functionality in Ransack, focusing on predicate handling, attribute extraction, and configuration behavior. The tests ensure proper handling of search conditions, aliases, and predicates within the Ransack query builder framework.

Test Coverage Overview

The test suite provides comprehensive coverage of Ransack’s Condition node functionality:

  • Tuple behavior preservation for array inputs
  • Alias handling and attribute conversion
  • Multiple value handling with predicates
  • Negative condition validation
  • Invalid predicate handling
  • Empty predicate configuration

Implementation Analysis

The testing approach employs RSpec’s context-based structure to organize related test cases. It utilizes subject blocks for condition extraction and leverages configuration blocks to test different Ransack settings. The implementation focuses on isolated unit tests for the Condition class functionality.

Technical Details

  • Testing Framework: RSpec
  • Test Environment: Spec helper configuration
  • Test Objects: Person model context
  • Configuration Testing: Ransack.configure blocks
  • Assertion Methods: expect syntax with matchers

Best Practices Demonstrated

The test suite exemplifies several testing best practices:

  • Isolated context blocks for related scenarios
  • Clear subject definitions
  • Configuration state management
  • Explicit expectation matching
  • Comprehensive edge case coverage
  • Clean test organization

activerecord-hackery/ransack

spec/ransack/nodes/condition_spec.rb

            
require 'spec_helper'

module Ransack
  module Nodes
    describe Condition do
      context 'bug report #1245' do
        it 'preserves tuple behavior' do
          ransack_hash = {
              m: 'and',
              g: [
                { title_type_in: ['["title 1", ""]'] }
              ]
            }

          sql = Article.ransack(ransack_hash).result.to_sql
          expect(sql).to include("IN (('title 1', ''))")
        end
      end

      context 'with an alias' do
        subject {
          Condition.extract(
            Context.for(Person), 'term_start', Person.first(2).map(&:name)
          )
        }

        specify { expect(subject.combinator).to eq 'or' }
        specify { expect(subject.predicate.name).to eq 'start' }

        it 'converts the alias to the correct attributes' do
          expect(subject.attributes.map(&:name)).to eq(['name', 'email'])
        end
      end

      context 'with multiple values and an _any predicate' do
        subject {
          Condition.extract(
            Context.for(Person), 'name_eq_any', Person.first(2).map(&:name)
          )
        }

        specify { expect(subject.values.size).to eq(2) }
      end

      describe '#negative?' do
        let(:context) { Context.for(Person) }
        let(:eq)      { Condition.extract(context, 'name_eq', 'A') }
        let(:not_eq)  { Condition.extract(context, 'name_not_eq', 'A') }

        specify { expect(not_eq.negative?).to be true }
        specify { expect(eq.negative?).to be false }
      end

      context 'with an invalid predicate' do
        subject {
          Condition.extract(
            Context.for(Person), 'name_invalid', Person.first.name
          )
        }

        context "when ignore_unknown_conditions is false" do
          before do
            Ransack.configure { |c| c.ignore_unknown_conditions = false }
          end

          specify { expect { subject }.to raise_error ArgumentError }
        end

        context "when ignore_unknown_conditions is true" do
          before do
            Ransack.configure { |c| c.ignore_unknown_conditions = true }
          end

          specify { expect(subject).to be_nil }
        end
      end

      context 'with an empty predicate' do
        subject {
          Condition.extract(
            Context.for(Person), 'full_name', Person.first.name
          )
        }

        context "when default_predicate = nil" do
          before do
            Ransack.configure { |c| c.default_predicate = nil }
          end

          specify { expect(subject).to be_nil }
        end

        context "when default_predicate = 'eq'" do
          before do
            Ransack.configure { |c| c.default_predicate = 'eq' }
          end

          specify { expect(subject).to eq Condition.extract(Context.for(Person), 'full_name_eq', Person.first.name) }
        end
      end
    end
  end
end