lib/audited/rspec_matchers.rb in audited-4.6.0 vs lib/audited/rspec_matchers.rb in audited-4.7.0

- old
+ new

@@ -39,35 +39,32 @@ @options[:associated_with] = model self end def only(*fields) - @options[:only] = fields.flatten + @options[:only] = fields.flatten.map(&:to_s) self end def except(*fields) - @options[:except] = fields.flatten + @options[:except] = fields.flatten.map(&:to_s) self end def requires_comment @options[:comment_required] = true self end def on(*actions) - @options[:on] = actions.flatten + @options[:on] = actions.flatten.map(&:to_sym) self end def matches?(subject) @subject = subject - auditing_enabled? && - associated_with_model? && - records_changes_to_specified_fields? && - comment_required_valid? + auditing_enabled? && required_checks_for_options_satisfied? end def failure_message "Expected #{@expectation}" end @@ -107,33 +104,85 @@ expects "#{model_class} to record audits to associated model #{@options[:associated_with]}" model_class.audit_associated_with == @options[:associated_with] end def records_changes_to_specified_fields? - if @options[:only] || @options[:except] - if @options[:only] - except = model_class.column_names - @options[:only].map(&:to_s) - else - except = model_class.default_ignored_attributes + Audited.ignored_attributes - except |= @options[:except].collect(&:to_s) if @options[:except] - end + ignored_fields = build_ignored_fields_from_options - expects "non audited columns (#{model_class.non_audited_columns.inspect}) to match (#{except})" - model_class.non_audited_columns =~ except + expects "non audited columns (#{model_class.non_audited_columns.inspect}) to match (#{ignored_fields})" + model_class.non_audited_columns.to_set == ignored_fields.to_set + end + + def comment_required_valid? + expects "to require audit_comment before #{model_class.audited_options[:on]} when comment required" + validate_callbacks_include_presence_of_comment? && destroy_callbacks_include_comment_required? + end + + def only_audit_on_designated_callbacks? + { + create: [:after, :audit_create], + update: [:before, :audit_update], + destroy: [:before, :audit_destroy] + }.map do |(action, kind_callback)| + kind, callback = kind_callback + callbacks_for(action, kind: kind).include?(callback) if @options[:on].include?(action) + end.compact.all? + end + + def validate_callbacks_include_presence_of_comment? + if @options[:comment_required] && audited_on_create_or_update? + callbacks_for(:validate).include?(:presence_of_audit_comment) else true end end - def comment_required_valid? - if @options[:comment_required] - @subject.audit_comment = nil + def audited_on_create_or_update? + model_class.audited_options[:on].include?(:create) || model_class.audited_options[:on].include?(:update) + end - expects "to be invalid when audit_comment is not specified" - @subject.valid? == false && @subject.errors.key?(:audit_comment) + def destroy_callbacks_include_comment_required? + if @options[:comment_required] && model_class.audited_options[:on].include?(:destroy) + callbacks_for(:destroy).include?(:require_comment) else true end + end + + def requires_comment_before_callbacks? + [:create, :update, :destroy].map do |action| + if @options[:comment_required] && model_class.audited_options[:on].include?(action) + callbacks_for(action).include?(:require_comment) + end + end.compact.all? + end + + def callbacks_for(action, kind: :before) + model_class.send("_#{action}_callbacks").select { |cb| cb.kind == kind }.map(&:filter) + end + + def build_ignored_fields_from_options + default_ignored_attributes = model_class.default_ignored_attributes + + if @options[:only].present? + (default_ignored_attributes | model_class.column_names) - @options[:only] + elsif @options[:except].present? + default_ignored_attributes | @options[:except] + else + default_ignored_attributes + end + end + + def required_checks_for_options_satisfied? + { + only: :records_changes_to_specified_fields?, + except: :records_changes_to_specified_fields?, + comment_required: :comment_required_valid?, + associated_with: :associated_with_model?, + on: :only_audit_on_designated_callbacks? + }.map do |(option, check)| + send(check) if @options[option].present? + end.compact.all? end end class AssociatedAuditMatcher # :nodoc: def matches?(subject)