Back to Repositories

Testing Book Model Version Management in PaperTrail

This test suite validates the versioning functionality of the Book model in PaperTrail, focusing on has_many through relationships and record lifecycle tracking. It ensures proper version creation and management across associated models and complex relationships.

Test Coverage Overview

The test suite comprehensively covers version tracking for Book model associations and lifecycle events.

  • Has-many through relationship versioning
  • Source record manipulation tracking
  • Join table operations versioning
  • Record update and destruction change tracking

Implementation Analysis

The testing approach utilizes RSpec’s context-based organization to separate distinct versioning scenarios.

Implementation focuses on:
  • Association manipulation testing (<<, create, destroy)
  • Version count verification
  • Change attribute validation
  • YAML-serialized change tracking

Technical Details

Key technical components include:
  • RSpec testing framework
  • PaperTrail versioning system
  • YAML for change serialization
  • ActiveRecord associations
  • Custom version tracking configurations

Best Practices Demonstrated

The test suite exemplifies robust testing practices through organized, focused test cases.

  • Isolated context blocks for different scenarios
  • Explicit expectations for version creation
  • Comprehensive relationship testing
  • Proper setup and teardown management

paper-trail-gem/paper_trail

spec/models/book_spec.rb

            
# frozen_string_literal: true

require "spec_helper"

RSpec.describe Book, versioning: true do
  context "with :has_many :through" do
    it "store version on source <<" do
      book = described_class.create(title: "War and Peace")
      dostoyevsky = Person.create(name: "Dostoyevsky")
      Person.create(name: "Solzhenitsyn")
      count = PaperTrail::Version.count
      (book.authors << dostoyevsky)
      expect((PaperTrail::Version.count - count)).to(eq(1))
      expect(book.authorships.first.versions.first).to(eq(PaperTrail::Version.last))
    end

    it "store version on source create" do
      book = described_class.create(title: "War and Peace")
      Person.create(name: "Dostoyevsky")
      Person.create(name: "Solzhenitsyn")
      count = PaperTrail::Version.count
      book.authors.create(name: "Tolstoy")
      expect((PaperTrail::Version.count - count)).to(eq(2))
      expect(
        [PaperTrail::Version.order(:id).to_a[-2].item, PaperTrail::Version.last.item]
      ).to match_array([Person.last, Authorship.last])
    end

    it "store version on join destroy" do
      book = described_class.create(title: "War and Peace")
      dostoyevsky = Person.create(name: "Dostoyevsky")
      Person.create(name: "Solzhenitsyn")
      (book.authors << dostoyevsky)
      count = PaperTrail::Version.count
      book.authorships.reload.last.destroy
      expect((PaperTrail::Version.count - count)).to(eq(1))
      expect(PaperTrail::Version.last.reify.book).to(eq(book))
      expect(PaperTrail::Version.last.reify.author).to(eq(dostoyevsky))
    end

    it "store version on join clear" do
      book = described_class.create(title: "War and Peace")
      dostoyevsky = Person.create(name: "Dostoyevsky")
      Person.create(name: "Solzhenitsyn")
      book.authors << dostoyevsky
      count = PaperTrail::Version.count
      book.authorships.reload.destroy_all
      expect((PaperTrail::Version.count - count)).to(eq(1))
      expect(PaperTrail::Version.last.reify.book).to(eq(book))
      expect(PaperTrail::Version.last.reify.author).to(eq(dostoyevsky))
    end
  end

  context "when a persisted record is updated then destroyed" do
    it "has changes" do
      book = described_class.create! title: "A"
      changes = YAML.load book.versions.last.attributes["object_changes"]
      expect(changes).to eq("id" => [nil, book.id], "title" => [nil, "A"])

      book.update! title: "B"
      changes = YAML.load book.versions.last.attributes["object_changes"]
      expect(changes).to eq("title" => %w[A B])

      book.destroy
      changes = YAML.load book.versions.last.attributes["object_changes"]
      expect(changes).to eq("id" => [book.id, nil], "title" => ["B", nil])
    end
  end
end