Refactoring tag rule logic, placing in TagRuleApplicator lib class

This commit is contained in:
Rob Harrington
2016-05-26 13:46:54 +10:00
parent fa3b43a970
commit 607b674c57
25 changed files with 475 additions and 697 deletions

View File

@@ -1,3 +1,5 @@
require 'open_food_network/tag_rule_applicator'
class BaseController < ApplicationController
include Spree::Core::ControllerHelpers
include Spree::Core::ControllerHelpers::RespondWith
@@ -19,12 +21,9 @@ class BaseController < ApplicationController
@order_cycles = OrderCycle.with_distributor(@distributor).active
.order(@distributor.preferred_shopfront_order_cycle_order)
@distributor.apply_tag_rules(
type: "FilterOrderCycles",
subject: @order_cycles,
customer_tags: current_order.andand.customer.andand.tag_list || [],
shop: @distributor
)
customer_tags = current_order.andand.customer.andand.tag_list
applicator = OpenFoodNetwork::TagRuleApplicator.new(@distributor, "FilterOrderCycles", customer_tags)
applicator.filter!(@order_cycles)
# And default to the only order cycle if there's only the one
if @order_cycles.count == 1

View File

@@ -15,7 +15,7 @@ class ShopController < BaseController
# If we add any more filtering logic, we should probably
# move it all to a lib class like 'CachedProductsFilterer'
products_json = filtered_json(renderer.products_json)
products_json = filter(renderer.products_json)
render json: products_json
@@ -40,22 +40,22 @@ class ShopController < BaseController
private
def filtered_json(products_json)
tag_rules = relevant_tag_rules
return apply_tag_rules(tag_rules, products_json) if tag_rules.any?
products_json
if applicator.send(:rules).any?
filter(products_json)
else
products_json
end
end
def apply_tag_rules(tag_rules, products_json)
def filter(products_json)
products_hash = JSON.parse(products_json)
current_distributor.apply_tag_rules(
rules: tag_rules,
subject: products_hash,
customer_tags: current_order.andand.customer.andand.tag_list || []
)
applicator.filter!(products_hash)
JSON.unparse(products_hash)
end
def relevant_tag_rules
TagRule.for(current_distributor).of_type("FilterProducts")
def applicator
return @applicator unless @applicator.nil?
customer_tags = current_order.andand.customer.andand.tag_list
@applicator = OpenFoodNetwork::TagRuleApplicator.new(current_distributor, "FilterProducts", customer_tags)
end
end

View File

@@ -5,13 +5,11 @@ module EnterprisesHelper
def available_shipping_methods
shipping_methods = current_distributor.shipping_methods
if current_distributor.present?
current_distributor.apply_tag_rules(
type: "FilterShippingMethods",
subject: shipping_methods,
customer_tags: current_order.andand.customer.andand.tag_list
)
end
customer_tags = current_order.andand.customer.andand.tag_list
applicator = OpenFoodNetwork::TagRuleApplicator.new(current_distributor, "FilterShippingMethods", customer_tags)
applicator.filter!(shipping_methods)
shipping_methods.uniq
end

View File

@@ -344,13 +344,6 @@ class Enterprise < ActiveRecord::Base
abn.present?
end
def apply_tag_rules(context)
tag_rules_for(context).each do |rule|
rule.context = context
rule.apply
end
end
protected
def devise_mailer
@@ -459,10 +452,4 @@ class Enterprise < ActiveRecord::Base
def initialize_permalink
self.permalink = Enterprise.find_available_permalink(name)
end
def tag_rules_for(context)
rules = context[:rules] || tag_rules
return rules unless context[:type]
rules.merge(TagRule.of_type(context[:type]))
end
end

View File

@@ -1,6 +1,7 @@
require 'open_food_network/enterprise_fee_calculator'
require 'open_food_network/distribution_change_validator'
require 'open_food_network/feature_toggle'
require 'open_food_network/tag_rule_applicator'
ActiveSupport::Notifications.subscribe('spree.order.contents_changed') do |name, start, finish, id, payload|
payload[:order].reload.update_distribution_charge!
@@ -178,10 +179,6 @@ Spree::Order.class_eval do
if order_cycle
OpenFoodNetwork::EnterpriseFeeCalculator.new.create_order_adjustments_for self
end
if distributor.present? && customer.present?
distributor.apply_tag_rules(type: "DiscountOrder", subject: self, customer_tags: customer.andand.tag_list)
end
end
end
@@ -214,7 +211,11 @@ Spree::Order.class_eval do
def available_payment_methods
return [] unless distributor.present?
payment_methods = distributor.payment_methods.available(:front_end).all
distributor.apply_tag_rules( type: "FilterPaymentMethods", subject: payment_methods, customer_tags: customer.andand.tag_list)
customer_tags = customer.andand.tag_list
applicator = OpenFoodNetwork::TagRuleApplicator.new(distributor, "FilterPaymentMethods", customer_tags)
applicator.filter!(payment_methods)
payment_methods
end

View File

@@ -1,6 +1,4 @@
class TagRule < ActiveRecord::Base
attr_accessor :subject, :context
belongs_to :enterprise
preference :customer_tags, :string, default: ""
@@ -10,25 +8,6 @@ class TagRule < ActiveRecord::Base
attr_accessible :enterprise, :enterprise_id, :is_default, :preferred_customer_tags
scope :for, ->(enterprise) { where(enterprise_id: enterprise) }
scope :of_type, ->(type) { where(type: "TagRule::#{type}") }
def context=(context)
raise "Context for tag rule cannot be nil" if context.nil?
raise "Subject for tag rule cannot be nil" if context[:subject].nil?
@context = context
@subject = context[:subject]
end
def apply
if relevant?
if customer_tags_match?
apply!
else
apply_default! if respond_to?(:apply_default!,true)
end
end
end
def self.mapping_for(enterprises)
self.for(enterprises).inject({}) do |mapping, rule|
@@ -42,20 +21,4 @@ class TagRule < ActiveRecord::Base
mapping
end
end
private
def relevant?
return false unless subject_class_matches?
if respond_to?(:additional_requirements_met?, true)
return false unless additional_requirements_met?
end
true
end
def customer_tags_match?
context_customer_tags = context[:customer_tags] || []
preferred_tags = preferred_customer_tags.split(",")
(context_customer_tags & preferred_tags).any?
end
end

View File

@@ -4,33 +4,19 @@ class TagRule::FilterOrderCycles < TagRule
attr_accessible :preferred_matched_order_cycles_visibility, :preferred_exchange_tags
private
# Warning: this should only EVER be called via TagRule#apply
def apply!
unless preferred_matched_order_cycles_visibility == "visible"
subject.reject!{ |oc| tags_match?(oc) }
end
end
def apply_default!
if preferred_matched_order_cycles_visibility == "visible"
subject.reject!{ |oc| tags_match?(oc) }
end
end
def tags_match?(order_cycle)
exchange_tags = exchange_for(order_cycle).andand.tag_list || []
preferred_tags = preferred_exchange_tags.split(",")
( exchange_tags & preferred_tags ).any?
end
def exchange_for(order_cycle)
order_cycle.exchanges.outgoing.to_enterprise(context[:shop]).first
def reject_matched?
preferred_matched_order_cycles_visibility != "visible"
end
def subject_class_matches?
subject.class == ActiveRecord::Relation &&
subject.klass == OrderCycle
private
def exchange_for(order_cycle)
order_cycle.exchanges.outgoing.to_enterprise(enterprise).first
end
end

View File

@@ -4,29 +4,13 @@ class TagRule::FilterPaymentMethods < TagRule
attr_accessible :preferred_matched_payment_methods_visibility, :preferred_payment_method_tags
private
# Warning: this should only EVER be called via TagRule#apply
def apply!
unless preferred_matched_payment_methods_visibility == "visible"
subject.reject!{ |pm| tags_match?(pm) }
end
end
def apply_default!
if preferred_matched_payment_methods_visibility == "visible"
subject.reject!{ |pm| tags_match?(pm) }
end
end
def tags_match?(payment_method)
payment_method_tags = payment_method.andand.tag_list || []
preferred_tags = preferred_payment_method_tags.split(",")
( payment_method_tags & preferred_tags ).any?
end
def subject_class_matches?
subject.class == Array &&
subject.all? { |i| i.class < Spree::PaymentMethod }
def reject_matched?
preferred_matched_payment_methods_visibility != "visible"
end
end

View File

@@ -5,25 +5,8 @@ class TagRule
attr_accessible :preferred_matched_variants_visibility, :preferred_variant_tags
private
# Warning: this should only EVER be called via TagRule#apply
def apply!
unless preferred_matched_variants_visibility == "visible"
subject.reject! do |product|
product["variants"].reject! { |v| tags_match?(v) }
product["variants"].empty?
end
end
end
def apply_default!
if preferred_matched_variants_visibility == "visible"
subject.reject! do |product|
product["variants"].reject! { |v| tags_match?(v) }
product["variants"].empty?
end
end
def self.tagged_children_for(product)
product["variants"]
end
def tags_match?(variant)
@@ -32,8 +15,8 @@ class TagRule
(variant_tags & preferred_tags).any?
end
def subject_class_matches?
subject.class == Array
def reject_matched?
preferred_matched_variants_visibility != "visible"
end
end
end

View File

@@ -4,19 +4,8 @@ class TagRule::FilterShippingMethods < TagRule
attr_accessible :preferred_matched_shipping_methods_visibility, :preferred_shipping_method_tags
private
# Warning: this should only EVER be called via TagRule#apply
def apply!
unless preferred_matched_shipping_methods_visibility == "visible"
subject.reject!{ |sm| tags_match?(sm) }
end
end
def apply_default!
if preferred_matched_shipping_methods_visibility == "visible"
subject.reject!{ |sm| tags_match?(sm) }
end
def reject_matched?
preferred_matched_shipping_methods_visibility != "visible"
end
def tags_match?(shipping_method)
@@ -24,9 +13,4 @@ class TagRule::FilterShippingMethods < TagRule
preferred_tags = preferred_shipping_method_tags.split(",")
( shipping_method_tags & preferred_tags ).any?
end
def subject_class_matches?
subject.class == Array &&
subject.all? { |i| i.class == Spree::ShippingMethod }
end
end

View File

@@ -0,0 +1,61 @@
module OpenFoodNetwork
class TagRuleApplicator
attr_reader :enterprise, :rule_class, :customer_tags
def initialize(enterprise, rule_type, customer_tags=[])
raise "Enterprise cannot be nil" if enterprise.nil?
raise "Rule Type cannot be nil" if rule_type.nil?
@enterprise = enterprise
@rule_class = "TagRule::#{rule_type}".constantize
@customer_tags = customer_tags || []
end
def filter!(subject)
return unless subject.respond_to?(:any?) && subject.any?
subject.reject! do |element|
if rule_class.respond_to?(:tagged_children_for)
children = rule_class.tagged_children_for(element)
children.reject! { |child| reject?(child) }
children.empty?
else
reject?(element)
end
end
end
private
def reject?(element)
customer_rules.each do |rule|
return rule.reject_matched? if rule.tags_match?(element)
end
default_rules.each do |rule|
return rule.reject_matched? if rule.tags_match?(element)
end
false
end
def rules
return @rules unless @rules.nil?
@rules = rule_class.for(enterprise)
end
def customer_rules
return @customer_matched_rules unless @customer_matched_rules.nil?
@customer_matched_rules = rules.select{ |rule| customer_tags_match?(rule) }
end
def default_rules
return @default_rules unless @default_rules.nil?
@default_rules = rules.select(&:is_default?)
end
def customer_tags_match?(rule)
preferred_tags = rule.preferred_customer_tags.split(",")
(customer_tags & preferred_tags).any?
end
end
end

View File

@@ -40,12 +40,17 @@ describe EnterprisesController do
order.update_attribute(:customer_id, customer.id)
end
it "shows order cycles allowed by the rule" do
it "shows order cycles allowed by the rules" do
create(:filter_order_cycles_tag_rule,
enterprise: distributor,
preferred_customer_tags: "wholesale",
preferred_exchange_tags: "wholesale",
preferred_matched_order_cycles_visibility: 'visible')
create(:filter_order_cycles_tag_rule,
enterprise: distributor,
is_default: true,
preferred_exchange_tags: "wholesale",
preferred_matched_order_cycles_visibility: 'hidden')
spree_get :shop, {id: distributor}
expect(assigns(:order_cycles)).to include order_cycle1, order_cycle2, order_cycle3

View File

@@ -96,21 +96,20 @@ describe ShopController do
describe "determining rule relevance" do
let(:products_json) { double(:products_json) }
let(:applicator) { double(:applicator) }
before do
# allow(controller).to receive(:products_json) { products_json }
allow(controller).to receive(:relevant_tag_rules) { relevant_tag_rules }
allow(controller).to receive(:apply_tag_rules) { "some filtered json" }
allow(applicator).to receive(:rules) { tag_rules }
allow(controller).to receive(:applicator) { applicator }
allow(controller).to receive(:filter) { "some filtered json" }
end
context "when no relevant rules exist" do
let(:relevant_tag_rules) { [] }
before { allow(controller).to receive(:relevant_rules) { relevant_rules } }
let(:tag_rules) { [] }
it "does not attempt to apply any rules" do
controller.send(:filtered_json, products_json)
expect(expect(controller).to_not have_received(:apply_tag_rules))
expect(expect(controller).to_not have_received(:filter))
end
it "returns products as JSON" do
@@ -120,11 +119,11 @@ describe ShopController do
context "when relevant rules exist" do
let(:tag_rule) { create(:filter_products_tag_rule, preferred_customer_tags: "tag1", preferred_variant_tags: "tag1", preferred_matched_variants_visibility: "hidden" ) }
let(:relevant_tag_rules) { [tag_rule] }
let(:tag_rules) { [tag_rule] }
it "attempts to apply any rules" do
controller.send(:filtered_json, products_json)
expect(controller).to have_received(:apply_tag_rules).with(relevant_tag_rules, products_json)
expect(controller).to have_received(:filter).with(products_json)
end
it "returns filtered JSON" do
@@ -133,49 +132,86 @@ describe ShopController do
end
end
describe "applying tag rules" do
let(:product1) { { id: 1, name: 'product 1', "variants" => [{ id: 4, "tag_list" => ["tag1"] }] } }
let(:product2) { { id: 2, name: 'product 2', "variants" => [{ id: 5, "tag_list" => ["tag1"] }, {id: 9, "tag_list" => ["tag2"]}] } }
let(:product3) { { id: 3, name: 'product 3', "variants" => [{ id: 6, "tag_list" => ["tag3"] }] } }
context "when FilterProducts tag rules are in effect" do
let!(:tagged_customer) { create(:customer, enterprise: distributor, tag_list: "member") }
let!(:untagged_customer) { create(:customer, enterprise: distributor, tag_list: "") }
let!(:order) { create(:order, distributor: distributor) }
let!(:tag_rule) { create(:filter_products_tag_rule,
enterprise: distributor,
preferred_customer_tags: "member",
preferred_variant_tags: "members-only") }
let!(:default_tag_rule) { create(:filter_products_tag_rule,
enterprise: distributor,
is_default: true,
preferred_variant_tags: "members-only") }
let(:product1) { { "id" => 1, "name" => 'product 1', "variants" => [{ "id" => 4, "tag_list" => ["members-only"] }] } }
let(:product2) { { "id" => 2, "name" => 'product 2', "variants" => [{ "id" => 5, "tag_list" => ["members-only"] }, {"id" => 9, "tag_list" => ["something"]}] } }
let(:product3) { { "id" => 3, "name" => 'product 3', "variants" => [{ "id" => 6, "tag_list" => ["something-else"] }] } }
let(:product2_without_v5) { { "id" => 2, "name" => 'product 2', "variants" => [{"id" => 9, "tag_list" => ["something"]}] } }
let!(:products_array) { [product1, product2, product3] }
let!(:products_json) { JSON.unparse( products_array ) }
let(:tag_rule) { create(:filter_products_tag_rule, preferred_customer_tags: "tag1", preferred_variant_tags: "tag1", preferred_matched_variants_visibility: "hidden" ) }
let(:relevant_tag_rules) { [tag_rule] }
before do
allow(controller).to receive(:current_order) { order }
allow(tag_rule).to receive(:context=)
allow(tag_rule).to receive(:apply)
allow(distributor).to receive(:apply_tag_rules).and_call_original
end
context "when a current order with a customer does not exist" do
let(:order) { double(:order, customer: nil) }
context "with a preferred visiblity of 'visible', default visibility of 'hidden'" do
before { tag_rule.update_attribute(:preferred_matched_variants_visibility, 'visible') }
before { default_tag_rule.update_attribute(:preferred_matched_variants_visibility, 'hidden') }
it "sets the context customer_tags as an empty array" do
controller.send(:apply_tag_rules, relevant_tag_rules, products_json)
expect(distributor).to have_received(:apply_tag_rules).with(rules: relevant_tag_rules, subject: JSON.parse(products_json), :customer_tags=>[])
end
end
let(:filtered_products) { JSON.parse(controller.send(:filter, products_json)) }
context "when a customer does exist" do
let(:order) { double(:order, customer: double(:customer, tag_list: ["tag1", "tag2"])) }
it "sets the context customer_tags" do
controller.send(:apply_tag_rules, relevant_tag_rules, products_json)
expect(distributor).to have_received(:apply_tag_rules).with(rules: relevant_tag_rules, subject: JSON.parse(products_json), :customer_tags=>["tag1", "tag2"])
end
context "applies the rule" do
before do
allow(tag_rule).to receive(:context=).and_call_original
allow(tag_rule).to receive(:apply).and_call_original
context "when the customer is nil" do
it "applies default action (hide)" do
expect(filtered_products).to include product2_without_v5, product3
expect(filtered_products).to_not include product1, product2
end
end
it "applies the rule" do
result = controller.send(:apply_tag_rules, relevant_tag_rules, products_json)
expect(tag_rule).to have_received(:apply)
expect(result).to eq JSON.unparse([{ id: 2, name: 'product 2', variants: [{id: 9, tag_list: ["tag2"]}] }, product3])
context "when the customer's tags match" do
before { order.update_attribute(:customer_id, tagged_customer.id) }
it "applies the action (show)" do
expect(filtered_products).to include product1, product2, product3
end
end
context "when the customer's tags don't match" do
before { order.update_attribute(:customer_id, untagged_customer.id) }
it "applies the default action (hide)" do
expect(filtered_products).to include product2_without_v5, product3
expect(filtered_products).to_not include product1, product2
end
end
end
context "with a preferred visiblity of 'hidden', default visibility of 'visible'" do
before { tag_rule.update_attribute(:preferred_matched_variants_visibility, 'hidden') }
before { default_tag_rule.update_attribute(:preferred_matched_variants_visibility, 'visible') }
let(:filtered_products) { JSON.parse(controller.send(:filter, products_json)) }
context "when the customer is nil" do
it "applies default action (show)" do
expect(filtered_products).to include product1, product2, product3
end
end
context "when the customer's tags match" do
before { order.update_attribute(:customer_id, tagged_customer.id) }
it "applies the action (hide)" do
expect(filtered_products).to include product2_without_v5, product3
expect(filtered_products).to_not include product1, product2
end
end
context "when the customer's tags don't match" do
before { order.update_attribute(:customer_id, untagged_customer.id) }
it "applies the default action (show)" do
expect(filtered_products).to include product1, product2, product3
end
end
end

View File

@@ -118,10 +118,15 @@ feature "As a consumer I want to check out my cart", js: true do
preferred_customer_tags: "local",
preferred_shipping_method_tags: "local",
preferred_matched_shipping_methods_visibility: 'visible')
create(:filter_shipping_methods_tag_rule,
enterprise: distributor,
is_default: true,
preferred_shipping_method_tags: "local",
preferred_matched_shipping_methods_visibility: 'hidden')
visit checkout_path
checkout_as_guest
# Rule in effect, disallows access to 'Local'
# Default rule in effect, disallows access to 'Local'
page.should have_content "Frogs"
page.should have_content "Donkeys"
page.should_not have_content "Local"

View File

@@ -3,15 +3,19 @@ require 'spec_helper'
describe EnterprisesHelper do
describe "loading available shipping methods" do
context "when a FilterShippingMethods tag rule is in effect, with preferred visibility of 'visible'" do
context "when FilterShippingMethods tag rules are in effect" do
let!(:distributor) { create(:distributor_enterprise) }
let!(:allowed_customer) { create(:customer, enterprise: distributor, tag_list: "local") }
let!(:disallowed_customer) { create(:customer, enterprise: distributor, tag_list: "") }
let!(:tagged_customer) { create(:customer, enterprise: distributor, tag_list: "local") }
let!(:untagged_customer) { create(:customer, enterprise: distributor, tag_list: "") }
let!(:order) { create(:order, distributor: distributor) }
let!(:tag_rule) { create(:filter_shipping_methods_tag_rule,
enterprise: distributor,
preferred_customer_tags: "local",
preferred_shipping_method_tags: "local-delivery") }
let!(:default_tag_rule) { create(:filter_shipping_methods_tag_rule,
enterprise: distributor,
is_default: true,
preferred_shipping_method_tags: "local-delivery") }
let!(:tagged_sm) { create(:shipping_method, require_ship_address: false, name: "Untagged", tag_list: "local-delivery") }
let!(:untagged_sm) { create(:shipping_method, require_ship_address: false, name: "Tagged", tag_list: "") }
@@ -20,8 +24,9 @@ describe EnterprisesHelper do
allow(helper).to receive(:current_order) { order }
end
context "with a preferred visiblity of 'visible" do
context "with a preferred visiblity of 'visible', default visibility of 'hidden'" do
before { tag_rule.update_attribute(:preferred_matched_shipping_methods_visibility, 'visible') }
before { default_tag_rule.update_attribute(:preferred_matched_shipping_methods_visibility, 'hidden') }
context "when the customer is nil" do
it "applies default action (hide)" do
@@ -31,7 +36,7 @@ describe EnterprisesHelper do
end
context "when the customer's tags match" do
before { order.update_attribute(:customer_id, allowed_customer.id) }
before { order.update_attribute(:customer_id, tagged_customer.id) }
it "applies the action (show)" do
expect(helper.available_shipping_methods).to include tagged_sm, untagged_sm
@@ -39,7 +44,7 @@ describe EnterprisesHelper do
end
context "when the customer's tags don't match" do
before { order.update_attribute(:customer_id, disallowed_customer.id) }
before { order.update_attribute(:customer_id, untagged_customer.id) }
it "applies the default action (hide)" do
expect(helper.available_shipping_methods).to include untagged_sm
@@ -48,8 +53,9 @@ describe EnterprisesHelper do
end
end
context "with a preferred visiblity of 'hidden" do
context "with a preferred visiblity of 'hidden', default visibility of 'visible'" do
before { tag_rule.update_attribute(:preferred_matched_shipping_methods_visibility, 'hidden') }
before { default_tag_rule.update_attribute(:preferred_matched_shipping_methods_visibility, 'visible') }
context "when the customer is nil" do
it "applies default action (show)" do
@@ -58,7 +64,7 @@ describe EnterprisesHelper do
end
context "when the customer's tags match" do
before { order.update_attribute(:customer_id, allowed_customer.id) }
before { order.update_attribute(:customer_id, tagged_customer.id) }
it "applies the action (hide)" do
expect(helper.available_shipping_methods).to include untagged_sm
@@ -67,7 +73,7 @@ describe EnterprisesHelper do
end
context "when the customer's tags don't match" do
before { order.update_attribute(:customer_id, disallowed_customer.id) }
before { order.update_attribute(:customer_id, untagged_customer.id) }
it "applies the default action (show)" do
expect(helper.available_shipping_methods).to include tagged_sm, untagged_sm

View File

@@ -30,7 +30,6 @@ describe InjectionHelper do
shipping_methods = double(:shipping_methods, uniq: [sm])
current_distributor = double(:distributor, shipping_methods: shipping_methods)
allow(helper).to receive(:current_distributor) { current_distributor }
allow(current_distributor).to receive(:apply_tag_rules).with({type: "FilterShippingMethods", subject: shipping_methods, customer_tags: nil})
helper.inject_available_shipping_methods.should match sm.id.to_s
helper.inject_available_shipping_methods.should match sm.compute_amount(order).to_s
end

View File

@@ -0,0 +1,250 @@
require 'open_food_network/tag_rule_applicator'
module OpenFoodNetwork
describe TagRuleApplicator do
let!(:enterprise) { create(:distributor_enterprise) }
let!(:oc_tag_rule) { create(:filter_order_cycles_tag_rule, enterprise: enterprise, preferred_customer_tags: "tag1", preferred_exchange_tags: "tag1", preferred_matched_order_cycles_visibility: "visible" )}
let!(:product_tag_rule1) { create(:filter_products_tag_rule, enterprise: enterprise, preferred_customer_tags: "tag1", preferred_variant_tags: "tag1", preferred_matched_variants_visibility: "visible" ) }
let!(:product_tag_rule2) { create(:filter_products_tag_rule, enterprise: enterprise, preferred_customer_tags: "tag1", preferred_variant_tags: "tag3", preferred_matched_variants_visibility: "hidden" ) }
let!(:product_tag_rule3) { create(:filter_products_tag_rule, enterprise: enterprise, preferred_customer_tags: "tag2", preferred_variant_tags: "tag1", preferred_matched_variants_visibility: "visible" ) }
let!(:default_product_tag_rule) { create(:filter_products_tag_rule, enterprise: enterprise, is_default: true, preferred_variant_tags: "tag1", preferred_matched_variants_visibility: "hidden" ) }
let!(:sm_tag_rule) { create(:filter_shipping_methods_tag_rule, enterprise: enterprise, preferred_customer_tags: "tag1", preferred_shipping_method_tags: "tag1", preferred_matched_shipping_methods_visibility: "visible" )}
describe "initialisation" do
context "when enterprise is nil" do
let(:applicator) { OpenFoodNetwork::TagRuleApplicator.new(nil, "FilterProducts", ["tag1"]) }
it { expect{applicator}.to raise_error "Enterprise cannot be nil" }
end
context "when rule_type is nil" do
let(:applicator) { OpenFoodNetwork::TagRuleApplicator.new(enterprise, nil, ["tag1"]) }
it { expect{applicator}.to raise_error "Rule Type cannot be nil" }
end
context "when rule_type does not match an existing rule type" do
let(:applicator) { OpenFoodNetwork::TagRuleApplicator.new(enterprise, "FilterSomething", ["tag1"]) }
it { expect{applicator}.to raise_error NameError }
end
context "when enterprise and rule_type are present" do
let(:applicator) { OpenFoodNetwork::TagRuleApplicator.new(enterprise, "FilterProducts", customer_tags) }
context "when the customer tags are nil" do
let!(:customer_tags) { nil }
it "sets customer tags to an empty array" do
expect(applicator.customer_tags).to eq []
end
it "does not match rules without customer tags" do
rule = double(:rule, preferred_customer_tags: "")
expect(applicator.send(:customer_tags_match?, rule)).to be false
end
end
context "when customer tags are empty" do
let!(:customer_tags) { [] }
it "sets customer tags to an empty array" do
expect(applicator.customer_tags).to eq []
end
it "does not match rules without customer tags" do
rule = double(:rule, preferred_customer_tags: "")
expect(applicator.send(:customer_tags_match?, rule)).to be false
end
end
context "when customer_tags are present" do
let!(:customer_tags) { ["tag1"] }
let(:rules) { applicator.send(:rules)}
let(:customer_rules) { applicator.send(:customer_rules)}
let(:default_rules) { applicator.send(:default_rules)}
it "stores enterprise, rule_class and customer_tags as instance variables" do
expect(applicator.enterprise).to eq enterprise
expect(applicator.rule_class).to eq TagRule::FilterProducts
expect(applicator.customer_tags).to eq ["tag1"]
end
it "selects only rules of the specified type" do
expect(rules).to include product_tag_rule1, product_tag_rule2, product_tag_rule3, default_product_tag_rule
expect(rules).not_to include oc_tag_rule, sm_tag_rule
end
it "splits rules into those which match customer tags and those which don't" do
expect(customer_rules).to include product_tag_rule1, product_tag_rule2
expect(customer_rules).not_to include default_product_tag_rule, product_tag_rule3, oc_tag_rule, sm_tag_rule
end
it "splits out default rules" do
expect(default_rules).to include default_product_tag_rule
expect(default_rules).not_to include product_tag_rule1, product_tag_rule2, product_tag_rule3, oc_tag_rule, sm_tag_rule
end
end
end
end
describe "filter!" do
let(:applicator) { OpenFoodNetwork::TagRuleApplicator.new(enterprise, "FilterProducts", []) }
context "when the subject is nil" do
let(:subject) { double(:subject, reject!: false) }
it "returns immediately" do
applicator.filter!(subject)
expect(subject).to_not have_received(:reject!)
end
end
context "when subject is empty" do
let(:subject) { double(:subject, reject!: false) }
it "returns immediately" do
applicator.filter!(subject)
expect(subject).to_not have_received(:reject!)
end
end
context "when subject is an array" do
let(:element) { double(:element, ) }
let(:subject) { [element] }
context "when rule_class reponds to tagged_children_for" do
let(:child1) { double(:child) }
let(:child2) { double(:child) }
let(:children) { [child1, child2] }
let(:rule_class) { double(:rule_class, tagged_children_for: children) }
before{ allow(applicator).to receive(:rule_class) { rule_class } }
context "when reject? returns true only for some children" do
before do
allow(applicator).to receive(:reject?).with(child1) { true }
allow(applicator).to receive(:reject?).with(child2) { false }
applicator.filter!(subject)
end
it "rejects the specified children from the array" do
expect(children).to eq [child2]
end
it "does not remove the element from the original subject" do
expect(subject).to eq [element]
end
end
context "when reject? returns true for all children" do
before do
allow(applicator).to receive(:reject?).with(child1) { true }
allow(applicator).to receive(:reject?).with(child2) { true }
applicator.filter!(subject)
end
it "removes all children from the array" do
expect(children).to eq []
end
it "removes the element from the original subject" do
expect(subject).to eq []
end
end
end
context "when rule_class doesn't respond to tagged_children_for" do
let(:rule_class) { double(:rule_class) }
before{ allow(applicator).to receive(:rule_class) { rule_class } }
context "when reject? returns false for the element" do
before do
allow(applicator).to receive(:reject?).with(element) { false }
applicator.filter!(subject)
end
it "does not remove the element from the original subject" do
expect(subject).to eq [element]
end
end
context "when reject? returns true for the element" do
before do
allow(applicator).to receive(:reject?).with(element) { true }
applicator.filter!(subject)
end
it "removes the element from the original subject" do
expect(subject).to eq []
end
end
end
end
end
describe "reject?" do
let(:applicator) { OpenFoodNetwork::TagRuleApplicator.new(enterprise, "FilterProducts", ["tag1"]) }
let(:customer_rule) { double(:customer_rule, reject_matched?: "customer_rule.reject_matched?" )}
let(:default_rule) { double(:customer_rule, reject_matched?: "default_rule.reject_matched?" )}
let(:dummy) { double(:dummy) }
before{ allow(applicator).to receive(:customer_rules) { [customer_rule] } }
before{ allow(applicator).to receive(:default_rules) { [default_rule] } }
context "when a customer rule matches the tags of the element" do
before{ allow(customer_rule).to receive(:tags_match?).with(dummy) { true } }
it "returns the value of customer_rule.reject_matched?" do
expect(applicator.send(:reject?, dummy)).to eq "customer_rule.reject_matched?"
end
end
context "when no customer rules match the tags of the element" do
before{ allow(customer_rule).to receive(:tags_match?) { false } }
context "when a default rule matches the tags of the element" do
before{ allow(default_rule).to receive(:tags_match?) { true } }
it "returns the value of the default_rule.reject_matched?" do
expect(applicator.send(:reject?, dummy)).to eq "default_rule.reject_matched?"
end
end
context "when a default rule matches the tags of the element" do
before{ allow(default_rule).to receive(:tags_match?) { false } }
it "returns false" do
expect(applicator.send(:reject?, dummy)).to be false
end
end
end
end
describe "smoke test for products" do
let(:product1) { { id: 1, name: 'product 1', "variants" => [{ id: 4, "tag_list" => ["tag1"] }] } }
let(:product2) { { id: 2, name: 'product 2', "variants" => [{ id: 5, "tag_list" => ["tag1"] }, {id: 9, "tag_list" => ["tag2"]}] } }
let(:product3) { { id: 3, name: 'product 3', "variants" => [{ id: 6, "tag_list" => ["tag3"] }] } }
let!(:products_array) { [product1, product2, product3] }
context "when customer tags don't match any rules" do
let(:applicator) { OpenFoodNetwork::TagRuleApplicator.new(enterprise, "FilterProducts", ["lalalala"]) }
it "applies the default rule" do
applicator.filter!(products_array)
expect(products_array).to eq [{ id: 2, name: 'product 2', "variants" => [{id: 9, "tag_list" => ["tag2"]}] }, product3]
end
end
context "when customer tags match one or more rules" do
let(:applicator) { OpenFoodNetwork::TagRuleApplicator.new(enterprise, "FilterProducts", ["tag1"]) }
it "applies those rules" do
# product_tag_rule1 and product_tag_rule2 are being applied
applicator.filter!(products_array)
expect(products_array).to eq [product1, product2]
end
end
end
end
end

View File

@@ -859,54 +859,4 @@ describe Enterprise do
end
end
end
describe "finding tag rules" do
let(:enterprise) { create(:enterprise) }
let(:tag_rule1) { create(:filter_products_tag_rule, enterprise: enterprise) }
let(:tag_rule2) { create(:filter_shipping_methods_tag_rule, enterprise: enterprise) }
let(:tag_rule3) { create(:tag_rule, enterprise: enterprise) }
let(:context) { { subject: "something" } }
context "when a set of rules are passed in with the context" do
let(:tag_rules) { TagRule.where(id: [tag_rule1.id, tag_rule3.id]) }
before { context[:rules] = tag_rules }
context "when a rule type has been passed in with the context" do
before { context[:type] = "FilterProducts" }
it "returns rules of the specified type from within the list of passed-in rules" do
rules = enterprise.send(:tag_rules_for, context)
expect(rules).to include tag_rule1
expect(rules).to_not include tag_rule2, tag_rule3
end
end
context "when a rule type has not been passed in with the context" do
it "returns the list of passed-in rules" do
rules = enterprise.send(:tag_rules_for, context)
expect(rules).to include tag_rule1, tag_rule3
expect(rules).to_not include tag_rule2
end
end
end
context "when a set of rules are not passed in with the context" do
context "when a rule type has been passed in with the context" do
before { context[:type] = "FilterShippingMethods" }
it "returns rules of the specified type belonging to the enterprise" do
rules = enterprise.send(:tag_rules_for, context)
expect(rules).to include tag_rule2
expect(rules).to_not include tag_rule1, tag_rule3
end
end
context "when a rule type has not been passed in with the context" do
it "returns all rules belonging to the enterprise" do
rules = enterprise.send(:tag_rules_for, context)
expect(rules).to include tag_rule1, tag_rule2, tag_rule3
end
end
end
end
end

View File

@@ -43,13 +43,17 @@ describe Spree::Order do
end
end
context "when a FilterPaymentMethods tag rule is in effect, with preferred visibility of 'visible'" do
let!(:allowed_customer) { create(:customer, enterprise: distributor, tag_list: "trusted") }
let!(:disallowed_customer) { create(:customer, enterprise: distributor, tag_list: "") }
context "when FilterPaymentMethods tag rules are in effect" do
let!(:tagged_customer) { create(:customer, enterprise: distributor, tag_list: "trusted") }
let!(:untagged_customer) { create(:customer, enterprise: distributor, tag_list: "") }
let!(:tag_rule) { create(:filter_payment_methods_tag_rule,
enterprise: distributor,
preferred_customer_tags: "trusted",
preferred_payment_method_tags: "trusted") }
let!(:default_tag_rule) { create(:filter_payment_methods_tag_rule,
enterprise: distributor,
is_default: true,
preferred_payment_method_tags: "trusted") }
let(:tagged_pm) { pm1 }
let(:untagged_pm) { pm2 }
@@ -58,8 +62,9 @@ describe Spree::Order do
distributor.payment_methods = [tagged_pm, untagged_pm]
end
context "with a preferred visiblity of 'visible" do
context "with a preferred visiblity of 'visible', default visibility of 'hidden'" do
before { tag_rule.update_attribute(:preferred_matched_payment_methods_visibility, 'visible') }
before { default_tag_rule.update_attribute(:preferred_matched_payment_methods_visibility, 'hidden') }
context "when the customer is nil" do
it "applies default action (hide)" do
@@ -69,7 +74,7 @@ describe Spree::Order do
end
context "when the customer's tags match" do
before { order.update_attribute(:customer_id, allowed_customer.id) }
before { order.update_attribute(:customer_id, tagged_customer.id) }
it "applies the action (show)" do
expect(order.available_payment_methods).to include tagged_pm, untagged_pm
@@ -77,7 +82,7 @@ describe Spree::Order do
end
context "when the customer's tags don't match" do
before { order.update_attribute(:customer_id, disallowed_customer.id) }
before { order.update_attribute(:customer_id, untagged_customer.id) }
it "applies the default action (hide)" do
expect(order.available_payment_methods).to include untagged_pm
@@ -86,8 +91,9 @@ describe Spree::Order do
end
end
context "with a preferred visiblity of 'hidden" do
context "with a preferred visiblity of 'hidden', default visibility of 'visible'" do
before { tag_rule.update_attribute(:preferred_matched_payment_methods_visibility, 'hidden') }
before { default_tag_rule.update_attribute(:preferred_matched_payment_methods_visibility, 'visible') }
context "when the customer is nil" do
it "applies default action (show)" do
@@ -96,7 +102,7 @@ describe Spree::Order do
end
context "when the customer's tags match" do
before { order.update_attribute(:customer_id, allowed_customer.id) }
before { order.update_attribute(:customer_id, tagged_customer.id) }
it "applies the action (hide)" do
expect(order.available_payment_methods).to include untagged_pm
@@ -105,7 +111,7 @@ describe Spree::Order do
end
context "when the customer's tags don't match" do
before { order.update_attribute(:customer_id, disallowed_customer.id) }
before { order.update_attribute(:customer_id, untagged_customer.id) }
it "applies the default action (show)" do
expect(order.available_payment_methods).to include tagged_pm, untagged_pm
@@ -186,38 +192,6 @@ describe Spree::Order do
subject.update_distribution_charge!
end
context "appying tag rules" do
let(:enterprise) { create(:distributor_enterprise) }
let(:customer) { create(:customer, enterprise: enterprise, tag_list: "tagtagtag") }
let(:tag_rule) { create(:tag_rule, enterprise: enterprise, preferred_customer_tags: "tagtagtag") }
let(:order) { create(:order_with_totals_and_distribution, distributor: enterprise, customer: customer) }
before do
tag_rule.calculator.update_attribute(:preferred_flat_percent, -10)
end
context "when the rule applies" do
it "applies the rule" do
order.update_distribution_charge!
order.reload
discount = order.adjustments.find_by_label("Discount")
expect(discount).to be_a Spree::Adjustment
expect(discount.amount).to eq (order.item_total / -10).round(2)
end
end
context "when the rule does not apply" do
before { tag_rule.update_attribute(:preferred_customer_tags, "tagtag") }
it "does not apply the rule" do
order.update_distribution_charge!
order.reload
discount = order.adjustments.find_by_label("Discount")
expect(discount).to be_nil
end
end
end
end
describe "looking up whether a line item can be provided by an order cycle" do

View File

@@ -3,7 +3,7 @@ require 'spec_helper'
describe TagRule::DiscountOrder, type: :model do
let!(:tag_rule) { create(:tag_rule) }
describe "determining relevance based on additional requirements" do
pending "determining relevance based on additional requirements" do
let(:subject) { double(:subject) }
before do
@@ -29,7 +29,7 @@ describe TagRule::DiscountOrder, type: :model do
end
end
describe "determining whether a the rule has already been applied to an order" do
pending "determining whether a the rule has already been applied to an order" do
let!(:order) { create(:order) }
let!(:adjustment) { order.adjustments.create({:amount => 12.34, :source => order, :originator => tag_rule, :label => 'discount' }, :without_protection => true) }
@@ -47,7 +47,7 @@ describe TagRule::DiscountOrder, type: :model do
end
end
describe "applying the rule" do
pending "applying the rule" do
# Assume that all validation is done by the TagRule base class
let!(:line_item) { create(:line_item, price: 100.00) }

View File

@@ -37,55 +37,4 @@ describe TagRule::FilterOrderCycles, type: :model do
end
end
end
describe "applying the rule" do
# Assume that all validation is done by the TagRule base class
let(:enterprise) { create(:distributor_enterprise) }
let(:order_cycle1) { create(:simple_order_cycle, name: "order_cycle1", exchanges: [ create(:exchange, incoming: false, receiver: enterprise, tag_list: ["tag1", "something", "somethingelse"])]) }
let(:order_cycle2) { create(:simple_order_cycle, name: "order_cycle2", exchanges: [ create(:exchange, incoming: false, receiver: enterprise, tag_list: ["tag2"])]) }
let(:order_cycle3) { create(:simple_order_cycle, name: "order_cycle3", exchanges: [ create(:exchange, incoming: false, receiver: enterprise, tag_list: ["tag3"])]) }
let!(:order_cycle_hash) { [order_cycle1, order_cycle2, order_cycle3] }
before do
tag_rule.update_attribute(:preferred_exchange_tags, "tag2")
tag_rule.context = {subject: order_cycle_hash, shop: enterprise}
end
context "apply!" do
context "when showing matching exchanges" do
before { tag_rule.update_attribute(:preferred_matched_order_cycles_visibility, "visible") }
it "does nothing" do
tag_rule.send(:apply!)
expect(order_cycle_hash).to eq [order_cycle1, order_cycle2, order_cycle3]
end
end
context "when hiding matching exchanges" do
before { tag_rule.update_attribute(:preferred_matched_order_cycles_visibility, "hidden") }
it "removes matching exchanges from the list" do
tag_rule.send(:apply!)
expect(order_cycle_hash).to eq [order_cycle1, order_cycle3]
end
end
end
context "apply_default!" do
context "when showing matching exchanges" do
before { tag_rule.update_attribute(:preferred_matched_order_cycles_visibility, "visible") }
it "remove matching exchanges from the list" do
tag_rule.send(:apply_default!)
expect(order_cycle_hash).to eq [order_cycle1, order_cycle3]
end
end
context "when hiding matching exchanges" do
before { tag_rule.update_attribute(:preferred_matched_order_cycles_visibility, "hidden") }
it "does nothing" do
tag_rule.send(:apply_default!)
expect(order_cycle_hash).to eq [order_cycle1, order_cycle2, order_cycle3]
end
end
end
end
end

View File

@@ -30,54 +30,4 @@ describe TagRule::FilterPaymentMethods, type: :model do
end
end
end
describe "applying the rule" do
# Assume that all validation is done by the TagRule base class
let(:sm1) { create(:payment_method, tag_list: ["tag1", "something", "somethingelse"]) }
let(:sm2) { create(:payment_method, tag_list: ["tag2"]) }
let(:sm3) { create(:payment_method, tag_list: ["tag3"]) }
let!(:payment_methods) { [sm1, sm2, sm3] }
before do
tag_rule.update_attribute(:preferred_payment_method_tags, "tag2")
tag_rule.context = {subject: payment_methods}
end
context "apply!" do
context "when showing matching payment methods" do
before { tag_rule.update_attribute(:preferred_matched_payment_methods_visibility, "visible") }
it "does nothing" do
tag_rule.send(:apply!)
expect(payment_methods).to eq [sm1, sm2, sm3]
end
end
context "when hiding matching payment methods" do
before { tag_rule.update_attribute(:preferred_matched_payment_methods_visibility, "hidden") }
it "removes matching payment methods from the list" do
tag_rule.send(:apply!)
expect(payment_methods).to eq [sm1, sm3]
end
end
end
context "apply_default!" do
context "when showing matching payment methods" do
before { tag_rule.update_attribute(:preferred_matched_payment_methods_visibility, "visible") }
it "remove matching payment methods from the list" do
tag_rule.send(:apply_default!)
expect(payment_methods).to eq [sm1, sm3]
end
end
context "when hiding matching payment methods" do
before { tag_rule.update_attribute(:preferred_matched_payment_methods_visibility, "hidden") }
it "does nothing" do
tag_rule.send(:apply_default!)
expect(payment_methods).to eq [sm1, sm2, sm3]
end
end
end
end
end

View File

@@ -30,54 +30,4 @@ describe TagRule::FilterProducts, type: :model do
end
end
end
describe "applying the rule" do
# Assume that all validation is done by the TagRule base class
let(:product1) { { name: "product1", "variants" => [{ name: "v1", "tag_list" => ["tag1", "something", "somethingelse"]}] } }
let(:product2) { { name: "product2", "variants" => [{ name: "v2", "tag_list" => ["tag2"]}] } }
let(:product3) { { name: "product3", "variants" => [{ name: "v3", "tag_list" => ["tag3"]}] } }
let!(:product_hash) { [product1, product2, product3] }
before do
tag_rule.update_attribute(:preferred_variant_tags, "tag2")
tag_rule.context = {subject: product_hash}
end
context "apply!" do
context "when showing matching variants" do
before { tag_rule.update_attribute(:preferred_matched_variants_visibility, "visible") }
it "does nothing" do
tag_rule.send(:apply!)
expect(product_hash).to eq [product1, product2, product3]
end
end
context "when hiding matching variants" do
before { tag_rule.update_attribute(:preferred_matched_variants_visibility, "hidden") }
it "removes matching variants from the list" do
tag_rule.send(:apply!)
expect(product_hash).to eq [product1, product3]
end
end
end
context "apply_default!" do
context "when showing matching variants" do
before { tag_rule.update_attribute(:preferred_matched_variants_visibility, "visible") }
it "remove matching variants from the list" do
tag_rule.send(:apply_default!)
expect(product_hash).to eq [product1, product3]
end
end
context "when hiding matching variants" do
before { tag_rule.update_attribute(:preferred_matched_variants_visibility, "hidden") }
it "does nothing" do
tag_rule.send(:apply_default!)
expect(product_hash).to eq [product1, product2, product3]
end
end
end
end
end

View File

@@ -30,54 +30,4 @@ describe TagRule::FilterShippingMethods, type: :model do
end
end
end
describe "applying the rule" do
# Assume that all validation is done by the TagRule base class
let(:sm1) { create(:shipping_method, tag_list: ["tag1", "something", "somethingelse"]) }
let(:sm2) { create(:shipping_method, tag_list: ["tag2"]) }
let(:sm3) { create(:shipping_method, tag_list: ["tag3"]) }
let!(:shipping_methods) { [sm1, sm2, sm3] }
before do
tag_rule.update_attribute(:preferred_shipping_method_tags, "tag2")
tag_rule.context = {subject: shipping_methods}
end
context "apply!" do
context "when showing matching shipping methods" do
before { tag_rule.update_attribute(:preferred_matched_shipping_methods_visibility, "visible") }
it "does nothing" do
tag_rule.send(:apply!)
expect(shipping_methods).to eq [sm1, sm2, sm3]
end
end
context "when hiding matching shipping methods" do
before { tag_rule.update_attribute(:preferred_matched_shipping_methods_visibility, "hidden") }
it "removes matching shipping methods from the list" do
tag_rule.send(:apply!)
expect(shipping_methods).to eq [sm1, sm3]
end
end
end
context "apply_default!" do
context "when showing matching shipping methods" do
before { tag_rule.update_attribute(:preferred_matched_shipping_methods_visibility, "visible") }
it "remove matching shipping methods from the list" do
tag_rule.send(:apply_default!)
expect(shipping_methods).to eq [sm1, sm3]
end
end
context "when hiding matching shipping methods" do
before { tag_rule.update_attribute(:preferred_matched_shipping_methods_visibility, "hidden") }
it "does nothing" do
tag_rule.send(:apply_default!)
expect(shipping_methods).to eq [sm1, sm2, sm3]
end
end
end
end
end

View File

@@ -8,196 +8,4 @@ describe TagRule, type: :model do
expect(tag_rule).to validate_presence_of :enterprise
end
end
describe 'setting the context' do
let(:subject) { double(:subject) }
let(:context) { { subject: subject, some_other_property: "yay"} }
it "raises an error when context is nil" do
expect{ tag_rule.context = nil }.to raise_error "Context for tag rule cannot be nil"
end
it "raises an error when subject is nil" do
expect{ tag_rule.context = {} }.to raise_error "Subject for tag rule cannot be nil"
end
it "stores the subject and context provided as instance variables on the model" do
tag_rule.context = context
expect(tag_rule.subject).to eq subject
expect(tag_rule.context).to eq context
expect(tag_rule.instance_variable_get(:@subject)).to eq subject
expect(tag_rule.instance_variable_get(:@context)).to eq context
end
end
describe "determining relevance based on subject and context" do
context "when the subject is nil" do
it "returns false" do
expect(tag_rule.send(:relevant?)).to be false
end
end
context "when the subject is not nil" do
let(:subject) { double(:subject) }
before do
tag_rule.context = {subject: subject}
allow(tag_rule).to receive(:customer_tags_match?) { :customer_tags_match_result }
allow(tag_rule).to receive(:subject_class) { Spree::Order}
end
context "when the subject class matches tag_rule#subject_class" do
before do
allow(subject).to receive(:class) { Spree::Order }
end
context "when the rule does not repond to #additional_requirements_met?" do
before { allow(tag_rule).to receive(:respond_to?).with(:additional_requirements_met?, true) { false } }
it "returns true" do
expect(tag_rule.send(:relevant?)).to be true
end
end
context "when the rule reponds to #additional_requirements_met?" do
before { allow(tag_rule).to receive(:respond_to?).with(:additional_requirements_met?, true) { true } }
context "and #additional_requirements_met? returns a truthy value" do
before { allow(tag_rule).to receive(:additional_requirements_met?) { "smeg" } }
it "returns true immediately" do
expect(tag_rule.send(:relevant?)).to be true
end
end
context "and #additional_requirements_met? returns true" do
before { allow(tag_rule).to receive(:additional_requirements_met?) { true } }
it "returns true immediately" do
expect(tag_rule.send(:relevant?)).to be true
end
end
context "and #additional_requirements_met? returns false" do
before { allow(tag_rule).to receive(:additional_requirements_met?) { false } }
it "returns false immediately" do
expect(tag_rule.send(:relevant?)).to be false
end
end
end
end
context "when the subject class does not match tag_rule#subject_class" do
before do
allow(subject).to receive(:class) { Spree::LineItem }
end
it "returns false immediately" do
expect(tag_rule.send(:relevant?)).to be false
expect(tag_rule).to_not have_received :customer_tags_match?
end
end
end
describe "determining whether specified customer tags match the given context" do
context "when the context has no customer tags specified" do
let(:context) { { subject: double(:something), not_tags: double(:not_tags) } }
before { tag_rule.context = context }
it "returns false" do
expect(tag_rule.send(:customer_tags_match?)).to be false
end
end
context "when the context has customer tags specified" do
let(:context) { { subject: double(:something), customer_tags: ["member","local","volunteer"] } }
before { tag_rule.context = context }
context "when the rule has no preferred customer tags specified" do
before do
allow(tag_rule).to receive(:preferred_customer_tags) { "" }
end
it "returns false" do
expect(tag_rule.send(:customer_tags_match?)).to be false
end
end
context "when the rule has preferred customer tags specified that match ANY of the customer tags" do
before do
allow(tag_rule).to receive(:preferred_customer_tags) { "wholesale,some_tag,member" }
end
it "returns false" do
expect(tag_rule.send(:customer_tags_match?)).to be true
end
end
context "when the rule has preferred customer tags specified that match NONE of the customer tags" do
before do
allow(tag_rule).to receive(:preferred_customer_tags) { "wholesale,some_tag,some_other_tag" }
end
it "returns false" do
expect(tag_rule.send(:customer_tags_match?)).to be false
end
end
end
end
describe "applying a tag rule to a subject" do
before { allow(tag_rule).to receive(:apply!) }
context "when the rule is deemed to be relevant" do
before { allow(tag_rule).to receive(:relevant?) { true } }
context "and customer_tags_match? returns true" do
before { expect(tag_rule).to receive(:customer_tags_match?) { true } }
it "applies the rule" do
tag_rule.apply
expect(tag_rule).to have_received(:apply!)
end
end
context "when customer_tags_match? returns false" do
before { expect(tag_rule).to receive(:customer_tags_match?) { false } }
before { allow(tag_rule).to receive(:apply_default!) }
context "and the rule responds to #apply_default!" do
before { allow(tag_rule).to receive(:respond_to?).with(:apply_default!, true) { true } }
it "applies the default action" do
tag_rule.apply
expect(tag_rule).to_not have_received(:apply!)
expect(tag_rule).to have_received(:apply_default!)
end
end
context "and the rule does not respond to #apply_default!" do
before { allow(tag_rule).to receive(:respond_to?).with(:apply_default!, true) { false } }
it "does not apply the rule or the default action" do
tag_rule.apply
expect(tag_rule).to_not have_received(:apply!)
expect(tag_rule).to_not have_received(:apply_default!)
end
end
end
end
context "when the rule is deemed not to be relevant" do
before { allow(tag_rule).to receive(:relevant?) { false } }
it "does not apply the rule" do
tag_rule.apply
expect(tag_rule).to_not have_received(:apply!)
end
end
end
end
end