<commit_before># frozen_string_literal: true

require_relative '../../helpers/arvm_test_utilities.rb'
require_relative '../../helpers/arvm_test_models.rb'
require_relative '../../helpers/viewmodel_spec_helpers.rb'

require 'minitest/autorun'

require 'view_model/active_record'

class ViewModel::CallbacksTest < ActiveSupport::TestCase
  include ARVMTestUtilities
  extend Minitest::Spec::DSL

  def vm_serialize_context(viewmodel_class, **args)
    viewmodel_class.new_serialize_context(callbacks: callbacks, **args)
  end

  def vm_deserialize_context(viewmodel_class, **args)
    viewmodel_class.new_deserialize_context(callbacks: callbacks, **args)
  end

  # Override TestHelpers to use the callback contexts
  def serialize(view, serialize_context: vm_serialize_context(view.class))
    super(view, serialize_context: serialize_context)
  end

  def serialize_with_references(view, serialize_context: vm_serialize_context(view.class))
    super(view, serialize_context: serialize_context)
  end

  # use `alter_by_view` to test deserialization: only override the deserialize_context
  def alter_by_view!(vm_class, model,
                     deserialize_context: vm_deserialize_context(vm_class),
                     **args,
                     &block)
    super(vm_class, model, deserialize_context: deserialize_context, **args, &block)
  end

  let(:callbacks) { [callback] }

  let(:vm) { create_viewmodel! }

  describe 'tracing each callback' do
    Visit = Struct.new(:hook, :view)

    class CallbackTracer
      include ViewModel::Callbacks

      attr_reader :hook_trace

      def initialize
        @hook_trace = []
      end

      ViewModel::Callbacks::Hook.each do |hook|
        send(hook.dsl_add_hook_name) do
          hook_trace << Visit.new(hook, view)
        end
      end

      def log!
        puts hook_trace.map { |t| [t.hook.name, t.view.class, t.view.model].inspect }
      end
    end

    let(:callback) { CallbackTracer.new }

    describe 'with parent and child test models' do
      include ViewModelSpecHelpers::ParentAndBelongsToChild

      def new_model
        model_class.new(name: 'a', child: child_model_class.new(name: 'b'))
      end

      it 'visits in correct order when serializing' do
        serialize(vm)
        value(callback.hook_trace).must_equal(
          [Visit.new(ViewModel::Callbacks::Hook::BeforeVisit, vm),
           Visit.new(ViewModel::Callbacks::Hook::BeforeVisit, vm.child),
           Visit.new(ViewModel::Callbacks::Hook::AfterVisit,  vm.child),
           Visit.new(ViewModel::Callbacks::Hook::AfterVisit,  vm)])
      end

      it 'visits in correct order when deserializing' do
        alter_by_view!(viewmodel_class, vm.model) {}
        value(callback.hook_trace).must_equal(
          [Visit.new(ViewModel::Callbacks::Hook::BeforeVisit,       vm),
           Visit.new(ViewModel::Callbacks::Hook::BeforeDeserialize, vm),
           Visit.new(ViewModel::Callbacks::Hook::BeforeVisit,       vm.child),
           Visit.new(ViewModel::Callbacks::Hook::BeforeDeserialize, vm.child),
           Visit.new(ViewModel::Callbacks::Hook::AfterDeserialize,  vm.child),
           Visit.new(ViewModel::Callbacks::Hook::AfterVisit,        vm.child),
           Visit.new(ViewModel::Callbacks::Hook::AfterDeserialize,  vm),
           Visit.new(ViewModel::Callbacks::Hook::AfterVisit,        vm)])
      end

      it 'calls edit hook when updating' do
        alter_by_view!(viewmodel_class, vm.model) do |view, _refs|
          view['name'] = 'q'
        end
        value(callback.hook_trace).must_equal(
          [Visit.new(ViewModel::Callbacks::Hook::BeforeVisit,       vm),
           Visit.new(ViewModel::Callbacks::Hook::BeforeDeserialize, vm),
           Visit.new(ViewModel::Callbacks::Hook::BeforeVisit,       vm.child),
           Visit.new(ViewModel::Callbacks::Hook::BeforeDeserialize, vm.child),
           Visit.new(ViewModel::Callbacks::Hook::AfterDeserialize,  vm.child),
           Visit.new(ViewModel::Callbacks::Hook::AfterVisit,        vm.child),
           Visit.new(ViewModel::Callbacks::Hook::OnChange,          vm),
           Visit.new(ViewModel::Callbacks::Hook::AfterDeserialize,  vm),
           Visit.new(ViewModel::Callbacks::Hook::AfterVisit,        vm)])
      end

      it 'calls edit hook when deleting' do
        vm_child = vm.child
        alter_by_view!(viewmodel_class, vm.model) do |view, _refs|
          view['child'] = nil
        end

        value(callback.hook_trace).must_equal(
          [Visit.new(ViewModel::Callbacks::Hook::BeforeVisit,       vm),
           Visit.new(ViewModel::Callbacks::Hook::BeforeDeserialize, vm),
           Visit.new(ViewModel::Callbacks::Hook::OnChange,          vm),
           Visit.new(ViewModel::Callbacks::Hook::AfterDeserialize,  vm),
           Visit.new(ViewModel::Callbacks::Hook::AfterVisit,        vm),
           Visit.new(ViewModel::Callbacks::Hook::BeforeVisit,       vm_child),
           Visit.new(ViewModel::Callbacks::Hook::BeforeDeserialize, vm_child),
           Visit.new(ViewModel::Callbacks::Hook::OnChange,          vm_child),
           Visit.new(ViewModel::Callbacks::Hook::AfterDeserialize,  vm_child),
           Visit.new(ViewModel::Callbacks::Hook::AfterVisit,        vm_child)])
      end

      it 'calls hooks on old and new when replacing' do
        old_child = vm.child
        alter_by_view!(viewmodel_class, vm.model) do |view, _refs|
          view['child'] = { '_type' => 'Child', 'name' => 'q' }
        end

        value(callback.hook_trace).must_equal(
          [Visit.new(ViewModel::Callbacks::Hook::BeforeVisit,       vm),
           Visit.new(ViewModel::Callbacks::Hook::BeforeDeserialize, vm),
           Visit.new(ViewModel::Callbacks::Hook::BeforeVisit,       vm.child),
           Visit.new(ViewModel::Callbacks::Hook::BeforeDeserialize, vm.child),
           Visit.new(ViewModel::Callbacks::Hook::OnChange,          vm.child),
           Visit.new(ViewModel::Callbacks::Hook::AfterDeserialize,  vm.child),
           Visit.new(ViewModel::Callbacks::Hook::AfterVisit,        vm.child),
           Visit.new(ViewModel::Callbacks::Hook::OnChange,          vm),
           Visit.new(ViewModel::Callbacks::Hook::AfterDeserialize,  vm),
           Visit.new(ViewModel::Callbacks::Hook::AfterVisit,        vm),
           Visit.new(ViewModel::Callbacks::Hook::BeforeVisit,       old_child),
           Visit.new(ViewModel::Callbacks::Hook::BeforeDeserialize, old_child),
           Visit.new(ViewModel::Callbacks::Hook::OnChange,          old_child),
           Visit.new(ViewModel::Callbacks::Hook::AfterDeserialize,  old_child),
           Visit.new(ViewModel::Callbacks::Hook::AfterVisit,        old_child)])
      end

      it 'calls hooks on delete' do
        ctx = vm_deserialize_context(viewmodel_class)
        vm.destroy!(deserialize_context: ctx)
        value(callback.hook_trace).must_equal(
          [Visit.new(ViewModel::Callbacks::Hook::BeforeVisit,       vm),
           Visit.new(ViewModel::Callbacks::Hook::BeforeDeserialize, vm),
           Visit.new(ViewModel::Callbacks::Hook::OnChange,          vm),
           Visit.new(ViewModel::Callbacks::Hook::AfterDeserialize,  vm),
           Visit.new(ViewModel::Callbacks::Hook::AfterVisit,        vm)])<commit_msg>add tests for callbacks on subtree move<commit_after># frozen_string_literal: true

require_relative '../../helpers/arvm_test_utilities.rb'
require_relative '../../helpers/arvm_test_models.rb'
require_relative '../../helpers/viewmodel_spec_helpers.rb'

require 'minitest/autorun'

require 'view_model/active_record'

class ViewModel::CallbacksTest < ActiveSupport::TestCase
  include ARVMTestUtilities
  extend Minitest::Spec::DSL

  def vm_serialize_context(viewmodel_class, **args)
    viewmodel_class.new_serialize_context(callbacks: callbacks, **args)
  end

  def vm_deserialize_context(viewmodel_class, **args)
    viewmodel_class.new_deserialize_context(callbacks: callbacks, **args)
  end

  # Override TestHelpers to use the callback contexts
  def serialize(view, serialize_context: vm_serialize_context(view.class))
    super(view, serialize_context: serialize_context)
  end

  def serialize_with_references(view, serialize_context: vm_serialize_context(view.class))
    super(view, serialize_context: serialize_context)
  end

  # use `alter_by_view` to test deserialization: only override the deserialize_context
  def alter_by_view!(vm_class, model,
                     deserialize_context: vm_deserialize_context(vm_class),
                     **args,
                     &block)
    super(vm_class, model, deserialize_context: deserialize_context, **args, &block)
  end

  let(:callbacks) { [callback] }

  let(:vm) { create_viewmodel! }

  describe 'tracing each callback' do
    Visit = Struct.new(:hook, :view)

    class CallbackTracer
      include ViewModel::Callbacks

      attr_reader :hook_trace

      def initialize
        @hook_trace = []
      end

      ViewModel::Callbacks::Hook.each do |hook|
        send(hook.dsl_add_hook_name) do
          hook_trace << Visit.new(hook, view)
        end
      end

      def log!
        puts hook_trace.map { |t| [t.hook.name, t.view.class, t.view.model].inspect }
      end
    end

    let(:callback) { CallbackTracer.new }

    describe 'with parent and child test models' do
      include ViewModelSpecHelpers::ParentAndBelongsToChild

      def new_model
        model_class.new(name: 'a', child: child_model_class.new(name: 'b'))
      end

      it 'visits in correct order when serializing' do
        serialize(vm)
        value(callback.hook_trace).must_equal(
          [Visit.new(ViewModel::Callbacks::Hook::BeforeVisit, vm),
           Visit.new(ViewModel::Callbacks::Hook::BeforeVisit, vm.child),
           Visit.new(ViewModel::Callbacks::Hook::AfterVisit,  vm.child),
           Visit.new(ViewModel::Callbacks::Hook::AfterVisit,  vm)])
      end

      it 'visits in correct order when deserializing' do
        alter_by_view!(viewmodel_class, vm.model) {}
        value(callback.hook_trace).must_equal(
          [Visit.new(ViewModel::Callbacks::Hook::BeforeVisit,       vm),
           Visit.new(ViewModel::Callbacks::Hook::BeforeDeserialize, vm),
           Visit.new(ViewModel::Callbacks::Hook::BeforeVisit,       vm.child),
           Visit.new(ViewModel::Callbacks::Hook::BeforeDeserialize, vm.child),
           Visit.new(ViewModel::Callbacks::Hook::AfterDeserialize,  vm.child),
           Visit.new(ViewModel::Callbacks::Hook::AfterVisit,        vm.child),
           Visit.new(ViewModel::Callbacks::Hook::AfterDeserialize,  vm),
           Visit.new(ViewModel::Callbacks::Hook::AfterVisit,        vm)])
      end

      it 'calls edit hook when updating' do
        alter_by_view!(viewmodel_class, vm.model) do |view, _refs|
          view['name'] = 'q'
        end
        value(callback.hook_trace).must_equal(
          [Visit.new(ViewModel::Callbacks::Hook::BeforeVisit,       vm),
           Visit.new(ViewModel::Callbacks::Hook::BeforeDeserialize, vm),
           Visit.new(ViewModel::Callbacks::Hook::BeforeVisit,       vm.child),
           Visit.new(ViewModel::Callbacks::Hook::BeforeDeserialize, vm.child),
           Visit.new(ViewModel::Callbacks::Hook::AfterDeserialize,  vm.child),
           Visit.new(ViewModel::Callbacks::Hook::AfterVisit,        vm.child),
           Visit.new(ViewModel::Callbacks::Hook::OnChange,          vm),
           Visit.new(ViewModel::Callbacks::Hook::AfterDeserialize,  vm),
           Visit.new(ViewModel::Callbacks::Hook::AfterVisit,        vm)])
      end

      it 'calls edit hook when deleting' do
        vm_child = vm.child
        alter_by_view!(viewmodel_class, vm.model) do |view, _refs|
          view['child'] = nil
        end

        value(callback.hook_trace).must_equal(
          [Visit.new(ViewModel::Callbacks::Hook::BeforeVisit,       vm),
           Visit.new(ViewModel::Callbacks::Hook::BeforeDeserialize, vm),
           Visit.new(ViewModel::Callbacks::Hook::OnChange,          vm),
           Visit.new(ViewModel::Callbacks::Hook::AfterDeserialize,  vm),
           Visit.new(ViewModel::Callbacks::Hook::AfterVisit,        vm),
           Visit.new(ViewModel::Callbacks::Hook::BeforeVisit,       vm_child),
           Visit.new(ViewModel::Callbacks::Hook::BeforeDeserialize, vm_child),
           Visit.new(ViewModel::Callbacks::Hook::OnChange,          vm_child),
           Visit.new(ViewModel::Callbacks::Hook::AfterDeserialize,  vm_child),
           Visit.new(ViewModel::Callbacks::Hook::AfterVisit,        vm_child)])
      end

      it 'calls hooks on old and new when replacing' do
        old_child = vm.child
        alter_by_view!(viewmodel_class, vm.model) do |view, _refs|
          view['child'] = { '_type' => 'Child', 'name' => 'q' }
        end

        value(callback.hook_trace).must_equal(
          [Visit.new(ViewModel::Callbacks::Hook::BeforeVisit,       vm),
           Visit.new(ViewModel::Callbacks::Hook::BeforeDeserialize, vm),
           Visit.new(ViewModel::Callbacks::Hook::BeforeVisit,       vm.child),
           Visit.new(ViewModel::Callbacks::Hook::BeforeDeserialize, vm.child),
           Visit.new(ViewModel::Callbacks::Hook::OnChange,          vm.child),
           Visit.new(ViewModel::Callbacks::Hook::AfterDeserialize,  vm.child),
           Visit.new(ViewModel::Callbacks::Hook::AfterVisit,        vm.child),
           Visit.new(ViewModel::Callbacks::Hook::OnChange,          vm),
           Visit.new(ViewModel::Callbacks::Hook::AfterDeserialize,  vm),
           Visit.new(ViewModel::Callbacks::Hook::AfterVisit,        vm),
           Visit.new(ViewModel::Callbacks::Hook::BeforeVisit,       old_child),
           Visit.new(ViewModel::Callbacks::Hook::BeforeDeserialize, old_child),
           Visit.new(ViewModel::Callbacks::Hook::OnChange,          old_child),
           Visit.new(ViewModel::Callbacks::Hook::AfterDeserialize,  old_child),
           Visit.new(ViewModel::Callbacks::Hook::AfterVisit,        old_child)])
      end

      it 'calls hooks on old and new when moving' do
        child = vm.child
        vm2 = viewmodel_class.new(model_class.create!(name: 'z'))
        alter_by_view!(viewmodel_class, [vm.model, vm2.model]) do |views, _refs|
          views[1]['child'] = views[0]['child']
          views[0]['child'] = nil
        end

        value(callback.hook_trace).must_equal(
          [Visit.new(ViewModel::Callbacks::Hook::BeforeVisit,       vm),
           Visit.new(ViewModel::Callbacks::Hook::BeforeDeserialize, vm),
           Visit.new(ViewModel::Callbacks::Hook::OnChange,          vm),
           Visit.new(ViewModel::Callbacks::Hook::AfterDeserialize,  vm),
           Visit.new(ViewModel::Callbacks::Hook::AfterVisit,        vm),
           Visit.new(ViewModel::Callbacks::Hook::BeforeVisit,       vm2),
           Visit.new(ViewModel::Callbacks::Hook::BeforeDeserialize, vm2),
           Visit.new(ViewModel::Callbacks::Hook::BeforeVisit,       child),
           Visit.new(ViewModel::Callbacks::Hook::BeforeDeserialize, child),
           Visit.new(ViewModel::Callbacks::Hook::AfterDeserialize,  child),
           Visit.new(ViewModel::Callbacks::Hook::AfterVisit,        child),
           Visit.new(ViewModel::Callbacks::Hook::OnChange,          vm2),
           Visit.new(ViewModel::Callbacks::Hook::AfterDeserialize,  vm2),
           Visit.new(ViewModel::Callbacks::Hook::AfterVisit,        vm2)])
      end

      it 'calls hooks on delete' do
        ctx = vm_deserialize_context(viewmodel_class)
        vm.destroy!(deserialize_context: ctx)
        value(callback.hook_trace).must_equal(
          [Visit.new(ViewModel::Callbacks::Hook::BeforeVisit,       vm),
           Visit.new(ViewModel::Callbacks::Hook::BeforeDeserialize, vm),
           Visit.new(ViewModel::Callbacks::Hook::OnChange,          vm),
           Visit.new(ViewModel::Callbacks::Hook::AfterDeserialize,  vm),
           Visit.new(ViewModel::Callbacks::Hook::AfterVisit,        vm)])
{'fraction_non_alphanumeric': 0.12759310172456886, 'fraction_numerical': 0.0006248437890527368, 'mean_word_length': 3.077707006369427, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5310467', 'n_tokens_mistral': 4786, 'n_tokens_neox': 4574, 'n_words': 739}