Back to Repositories

Testing Index Cache Implementation in Searchkick

This test suite validates the index caching functionality of the Searchkick gem, focusing on object ID persistence and thread safety. It ensures proper cache behavior across multiple index operations and concurrent access scenarios.

Test Coverage Overview

The test suite comprehensively covers index caching mechanisms in Searchkick:
  • Default caching behavior verification
  • Cache size limitations testing
  • Thread safety validation
  • Concurrent access stress testing

Implementation Analysis

The testing approach employs Minitest framework with systematic validation of cache behavior:
  • Object ID tracking for cache consistency
  • Multiple thread execution patterns
  • Cache size boundary testing
  • Exception handling for thread operations

Technical Details

Testing infrastructure includes:
  • Minitest as the testing framework
  • Thread manipulation utilities
  • Custom helper methods for object ID tracking
  • Class variable cache management

Best Practices Demonstrated

The test suite exemplifies robust testing practices:
  • Proper test isolation through setup methods
  • Comprehensive edge case coverage
  • Thread safety consideration
  • Clean helper method organization
  • Explicit exception handling

ankane/searchkick

test/index_cache_test.rb

            
require_relative "test_helper"

class IndexCacheTest < Minitest::Test
  def setup
    Product.class_variable_get(:@@searchkick_index_cache).clear
  end

  def test_default
    object_id = Product.searchkick_index.object_id
    3.times do
      assert_equal object_id, Product.searchkick_index.object_id
    end
  end

  def test_max_size
    starting_ids = object_ids(20)
    assert_equal starting_ids, object_ids(20)
    Product.searchkick_index(name: "other")
    refute_equal starting_ids, object_ids(20)
  end

  def test_thread_safe
    object_ids = with_threads { object_ids(20) }
    assert_equal object_ids[0], object_ids[1]
    assert_equal object_ids[0], object_ids[2]
  end

  # object ids can differ since threads progress at different speeds
  # test to make sure doesn't crash
  def test_thread_safe_max_size
    with_threads { object_ids(1000) }
  end

  private

  def object_ids(count)
    count.times.map { |i| Product.searchkick_index(name: "index#{i}").object_id }
  end

  def with_threads
    previous = Thread.report_on_exception
    begin
      Thread.report_on_exception = true
      3.times.map { Thread.new { yield } }.map(&:join).map(&:value)
    ensure
      Thread.report_on_exception = previous
    end
  end
end