Back to Repositories

Testing JSON Version History Management in paper_trail

This test suite validates PaperTrail’s JSON versioning functionality for the Fruit model, focusing on version change tracking and querying capabilities in a PostgreSQL environment. The tests ensure proper version history management and attribute change detection through JsonVersion implementation.

Test Coverage Overview

The test suite provides comprehensive coverage of PaperTrail’s JsonVersion functionality, specifically for the Fruit model. Key areas tested include:

  • Version change detection using have_a_version_with_changes matcher
  • Attribute change tracking across versions
  • Complex version querying capabilities
  • Object state transitions and history

Implementation Analysis

The testing approach utilizes RSpec’s behavior-driven development patterns with focused contexts for version management. The implementation leverages PaperTrail’s JsonVersion features for PostgreSQL, demonstrating both simple and complex querying patterns for version history tracking.

  • Custom matcher validation for version changes
  • Structured version query testing
  • State transition verification

Technical Details

Testing infrastructure includes:

  • RSpec as the testing framework
  • PostgreSQL database configuration
  • PaperTrail’s JsonVersion implementation
  • Custom version change matchers
  • Environment-specific test execution (DB=postgres)

Best Practices Demonstrated

The test suite exemplifies several testing best practices:

  • Isolated test scenarios with clear setup and expectations
  • Comprehensive version history validation
  • Effective use of RSpec’s let and describe blocks
  • Clear separation of concerns in test organization
  • Thorough edge case coverage

paper-trail-gem/paper_trail

spec/models/fruit_spec.rb

            
# frozen_string_literal: true

require "spec_helper"

if ENV["DB"] == "postgres" && JsonVersion.table_exists?
  RSpec.describe Fruit, type: :model, versioning: true do
    describe "have_a_version_with_changes matcher" do
      it "works with Fruit because Fruit uses JsonVersion" do
        # As of PT 9.0.0, with_version_changes only supports json(b) columns,
        # so that's why were testing the have_a_version_with_changes matcher
        # here.
        banana = described_class.create!(color: "Red", name: "Banana")
        banana.update!(color: "Yellow")
        expect(banana).to have_a_version_with_changes(color: "Yellow")
        expect(banana).not_to have_a_version_with_changes(color: "Pink")
        expect(banana).not_to have_a_version_with_changes(color: "Yellow", name: "Kiwi")
      end
    end

    describe "queries of versions", versioning: true do
      let!(:fruit) { described_class.create(name: "Apple", mass: 1, color: "green") }

      before do
        described_class.create(name: "Pear")
        fruit.update(name: "Fidget")
        fruit.update(name: "Digit")
      end

      it "return the fruit whose name has changed" do
        result = JsonVersion.where_attribute_changes(:name).map(&:item)
        expect(result).to include(fruit)
      end

      it "returns the fruit whose name was Fidget" do
        result = JsonVersion.where_object_changes_from({ name: "Fidget" }).map(&:item)
        expect(result).to include(fruit)
      end

      it "returns the fruit whose name became Digit" do
        result = JsonVersion.where_object_changes_to({ name: "Digit" }).map(&:item)
        expect(result).to include(fruit)
      end

      it "returns the fruit where the object was named Fidget before it changed" do
        result = JsonVersion.where_object({ name: "Fidget" }).map(&:item)
        expect(result).to include(fruit)
      end

      it "returns the fruit that changed to Fidget" do
        result = JsonVersion.where_object_changes({ name: "Fidget" }).map(&:item)
        expect(result).to include(fruit)
      end
    end
  end
end