Module: Funes::ProjectionTestHelper

Defined in:
app/helpers/funes/projection_test_helper.rb

Overview

Test helper for testing projections in isolation.

Include this module in your test classes to access helper methods that allow you to test individual projection interpretations without needing to process entire event streams.

Examples:

Include in your test

class InventorySnapshotProjectionTest < ActiveSupport::TestCase
  include Funes::ProjectionTestHelper

  test "receiving items increases quantity on hand" do
    initial_state = InventorySnapshot.new(quantity_on_hand: 10)
    event = Inventory::ItemReceived.new(quantity: 5, unit_cost: 9.99)

    result = interpret_event_based_on(InventorySnapshotProjection, event, initial_state)

    assert_equal 15, result.quantity_on_hand
  end
end

Instance Method Summary collapse

Instance Method Details

#apply_final_state_based_on(projection_class, previous_state, at = Time.current) ⇒ ActiveModel::Model, ActiveRecord::Base

Test a final_state block in isolation.

This method extracts and executes the final_state block from a projection, allowing you to test how the projection transforms state after all event interpretations have been applied.

Examples:

Test final state transformation

state = OrderSnapshot.new(total: 100, item_count: 3)

result = apply_final_state_based_on(OrderSnapshotProjection, state)

assert_equal 33.33, result.average_item_price

Test with a specific timestamp

state = InventorySnapshot.new(quantity_on_hand: 10)
at = Time.new(2023, 5, 10)

result = apply_final_state_based_on(InventorySnapshotProjection, state, at)

assert_equal at, result.finalized_at

Parameters:

  • projection_class (Class<Funes::Projection>)

    The projection class being tested.

  • previous_state (ActiveModel::Model, ActiveRecord::Base)

    The state before applying the final transformation.

  • at (Time) (defaults to: Time.current)

    The temporal reference point. Defaults to Time.current.

Returns:

  • (ActiveModel::Model, ActiveRecord::Base)

    The final state after applying the transformation.



107
108
109
110
# File 'app/helpers/funes/projection_test_helper.rb', line 107

def apply_final_state_based_on(projection_class, previous_state, at = Time.current)
  projection_class.instance_variable_get(:@interpretations)[:final]
                  .call(previous_state, at)
end

#build_initial_state_based_on(projection_class, at = Time.current) ⇒ ActiveModel::Model, ActiveRecord::Base

Test an initial_state block in isolation.

This method extracts and executes the initial_state block from a projection, allowing you to test how the projection builds its starting state without processing entire event streams.

Examples:

Test initial state construction

result = build_initial_state_based_on(InventorySnapshotProjection)

assert_equal 0, result.quantity_on_hand

Test with a specific timestamp

at = Time.new(2023, 5, 10)

result = build_initial_state_based_on(InventorySnapshotProjection, at)

assert_equal at, result.created_at

Parameters:

  • projection_class (Class<Funes::Projection>)

    The projection class being tested.

  • at (Time) (defaults to: Time.current)

    The temporal reference point. Defaults to Time.current.

Returns:

  • (ActiveModel::Model, ActiveRecord::Base)

    The initial state produced by the projection.



77
78
79
80
# File 'app/helpers/funes/projection_test_helper.rb', line 77

def build_initial_state_based_on(projection_class, at = Time.current)
  projection_class.instance_variable_get(:@interpretations)[:init]
                  .call(projection_class.instance_variable_get(:@materialization_model), at)
end

#interpret_event_based_on(projection_class, event_instance, previous_state, at = Time.current) ⇒ ActiveModel::Model, ActiveRecord::Base

Test a single event interpretation in isolation.

This method extracts and executes a single interpretation block from a projection, allowing you to test how specific events transform state without processing entire event streams.

Examples:

Test a single interpretation

initial_state = OrderSnapshot.new(total: 100)
event = Order::ItemAdded.new(amount: 50)

result = interpret_event_based_on(OrderSnapshotProjection, event, initial_state)

assert_equal 150, result.total

Test with validations

state = InventorySnapshot.new(quantity_on_hand: 5)
event = Inventory::ItemShipped.new(quantity: 10)

result = interpret_event_based_on(InventorySnapshotProjection, event, state)

assert_equal -5, result.quantity_on_hand
refute result.valid?

Parameters:

  • projection_class (Class<Funes::Projection>)

    The projection class being tested.

  • event_instance (Funes::Event)

    The event to interpret.

  • previous_state (ActiveModel::Model, ActiveRecord::Base)

    The state before applying the event.

  • at (Time) (defaults to: Time.current)

    The temporal reference point. Defaults to Time.current.

Returns:

  • (ActiveModel::Model, ActiveRecord::Base)

    The new state after applying the interpretation.



49
50
51
52
53
54
# File 'app/helpers/funes/projection_test_helper.rb', line 49

def interpret_event_based_on(projection_class, event_instance, previous_state, at = Time.current)
  at = at.beginning_of_day if at.is_a?(Date) && !at.is_a?(Time)
  event_at = event_instance.occurred_at || at
  projection_class.instance_variable_get(:@interpretations)[event_instance.class]
                  .call(previous_state, event_instance, event_at)
end