diff --git a/app/controllers/base_controller.rb b/app/controllers/base_controller.rb index 124dd7f38a..7bcb8bf3c9 100644 --- a/app/controllers/base_controller.rb +++ b/app/controllers/base_controller.rb @@ -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 diff --git a/app/controllers/shop_controller.rb b/app/controllers/shop_controller.rb index b0324f14e0..e244a2a189 100644 --- a/app/controllers/shop_controller.rb +++ b/app/controllers/shop_controller.rb @@ -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 diff --git a/app/helpers/enterprises_helper.rb b/app/helpers/enterprises_helper.rb index b4375b2577..886546585c 100644 --- a/app/helpers/enterprises_helper.rb +++ b/app/helpers/enterprises_helper.rb @@ -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 diff --git a/app/models/enterprise.rb b/app/models/enterprise.rb index f7e5a4b6f3..554529d7f2 100644 --- a/app/models/enterprise.rb +++ b/app/models/enterprise.rb @@ -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 diff --git a/app/models/spree/order_decorator.rb b/app/models/spree/order_decorator.rb index 411e7e71a2..9125d33991 100644 --- a/app/models/spree/order_decorator.rb +++ b/app/models/spree/order_decorator.rb @@ -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 diff --git a/app/models/tag_rule.rb b/app/models/tag_rule.rb index 1c578d2848..ee1ef105a3 100644 --- a/app/models/tag_rule.rb +++ b/app/models/tag_rule.rb @@ -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 diff --git a/app/models/tag_rule/filter_order_cycles.rb b/app/models/tag_rule/filter_order_cycles.rb index 2ed99320a5..de626ac70a 100644 --- a/app/models/tag_rule/filter_order_cycles.rb +++ b/app/models/tag_rule/filter_order_cycles.rb @@ -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 diff --git a/app/models/tag_rule/filter_payment_methods.rb b/app/models/tag_rule/filter_payment_methods.rb index da29426298..04a10889e0 100644 --- a/app/models/tag_rule/filter_payment_methods.rb +++ b/app/models/tag_rule/filter_payment_methods.rb @@ -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 diff --git a/app/models/tag_rule/filter_products.rb b/app/models/tag_rule/filter_products.rb index 9df35e00a0..e1d3865036 100644 --- a/app/models/tag_rule/filter_products.rb +++ b/app/models/tag_rule/filter_products.rb @@ -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 diff --git a/app/models/tag_rule/filter_shipping_methods.rb b/app/models/tag_rule/filter_shipping_methods.rb index 74438e560e..2091a14a63 100644 --- a/app/models/tag_rule/filter_shipping_methods.rb +++ b/app/models/tag_rule/filter_shipping_methods.rb @@ -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 diff --git a/lib/open_food_network/tag_rule_applicator.rb b/lib/open_food_network/tag_rule_applicator.rb new file mode 100644 index 0000000000..181624a210 --- /dev/null +++ b/lib/open_food_network/tag_rule_applicator.rb @@ -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 diff --git a/spec/controllers/enterprises_controller_spec.rb b/spec/controllers/enterprises_controller_spec.rb index 1ed68958f9..24837ceed9 100644 --- a/spec/controllers/enterprises_controller_spec.rb +++ b/spec/controllers/enterprises_controller_spec.rb @@ -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 diff --git a/spec/controllers/shop_controller_spec.rb b/spec/controllers/shop_controller_spec.rb index 669e17db5f..777050847d 100644 --- a/spec/controllers/shop_controller_spec.rb +++ b/spec/controllers/shop_controller_spec.rb @@ -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 diff --git a/spec/features/consumer/shopping/checkout_spec.rb b/spec/features/consumer/shopping/checkout_spec.rb index 9e9d830a50..e5e4871df0 100644 --- a/spec/features/consumer/shopping/checkout_spec.rb +++ b/spec/features/consumer/shopping/checkout_spec.rb @@ -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" diff --git a/spec/helpers/enterprises_helper_spec.rb b/spec/helpers/enterprises_helper_spec.rb index 1dc32d63b3..7a9028e5d7 100644 --- a/spec/helpers/enterprises_helper_spec.rb +++ b/spec/helpers/enterprises_helper_spec.rb @@ -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 diff --git a/spec/helpers/injection_helper_spec.rb b/spec/helpers/injection_helper_spec.rb index d5ec204286..e7d775c657 100644 --- a/spec/helpers/injection_helper_spec.rb +++ b/spec/helpers/injection_helper_spec.rb @@ -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 diff --git a/spec/lib/open_food_network/tag_rule_applicator_spec.rb b/spec/lib/open_food_network/tag_rule_applicator_spec.rb new file mode 100644 index 0000000000..0b560a3e0a --- /dev/null +++ b/spec/lib/open_food_network/tag_rule_applicator_spec.rb @@ -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 diff --git a/spec/models/enterprise_spec.rb b/spec/models/enterprise_spec.rb index f0fd6bca4c..b8360094ec 100644 --- a/spec/models/enterprise_spec.rb +++ b/spec/models/enterprise_spec.rb @@ -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 diff --git a/spec/models/spree/order_spec.rb b/spec/models/spree/order_spec.rb index 89ffd66e00..051c78896d 100644 --- a/spec/models/spree/order_spec.rb +++ b/spec/models/spree/order_spec.rb @@ -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 diff --git a/spec/models/tag_rule/discount_order_spec.rb b/spec/models/tag_rule/discount_order_spec.rb index fd1a7e9251..93723ad84a 100644 --- a/spec/models/tag_rule/discount_order_spec.rb +++ b/spec/models/tag_rule/discount_order_spec.rb @@ -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) } diff --git a/spec/models/tag_rule/filter_order_cycles_spec.rb b/spec/models/tag_rule/filter_order_cycles_spec.rb index 53619a776d..4c08af7c7f 100644 --- a/spec/models/tag_rule/filter_order_cycles_spec.rb +++ b/spec/models/tag_rule/filter_order_cycles_spec.rb @@ -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 diff --git a/spec/models/tag_rule/filter_payment_methods_spec.rb b/spec/models/tag_rule/filter_payment_methods_spec.rb index 8eec06f773..d8d30cefaa 100644 --- a/spec/models/tag_rule/filter_payment_methods_spec.rb +++ b/spec/models/tag_rule/filter_payment_methods_spec.rb @@ -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 diff --git a/spec/models/tag_rule/filter_products_spec.rb b/spec/models/tag_rule/filter_products_spec.rb index 185eb8fa47..0008db456d 100644 --- a/spec/models/tag_rule/filter_products_spec.rb +++ b/spec/models/tag_rule/filter_products_spec.rb @@ -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 diff --git a/spec/models/tag_rule/filter_shipping_methods_spec.rb b/spec/models/tag_rule/filter_shipping_methods_spec.rb index 598d89c798..ff90ed8428 100644 --- a/spec/models/tag_rule/filter_shipping_methods_spec.rb +++ b/spec/models/tag_rule/filter_shipping_methods_spec.rb @@ -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 diff --git a/spec/models/tag_rule_spec.rb b/spec/models/tag_rule_spec.rb index 32d2d532a4..06dac64194 100644 --- a/spec/models/tag_rule_spec.rb +++ b/spec/models/tag_rule_spec.rb @@ -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