diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index f11d6d803d..4bde8eb617 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -160,7 +160,6 @@ Metrics/ModuleLength: - 'app/helpers/spree/admin/orders_helper.rb' - 'app/models/spree/order/checkout.rb' - 'app/models/spree/payment/processing.rb' - - 'engines/order_management/spec/services/order_management/subscriptions/validator_spec.rb' - 'engines/order_management/spec/services/order_management/subscriptions/variants_list_spec.rb' - 'lib/open_food_network/column_preference_defaults.rb' - 'spec/lib/open_food_network/address_finder_spec.rb' diff --git a/engines/order_management/spec/services/order_management/subscriptions/validator_spec.rb b/engines/order_management/spec/services/order_management/subscriptions/validator_spec.rb index 2d76ac4ace..945b938118 100644 --- a/engines/order_management/spec/services/order_management/subscriptions/validator_spec.rb +++ b/engines/order_management/spec/services/order_management/subscriptions/validator_spec.rb @@ -2,116 +2,382 @@ require "spec_helper" -module OrderManagement - module Subscriptions - RSpec.describe Validator do - let(:owner) { create(:user) } - let(:shop) { create(:enterprise, name: "Shop", owner:) } +RSpec.describe OrderManagement::Subscriptions::Validator do + let(:owner) { create(:user) } + let(:shop) { create(:enterprise, name: "Shop", owner:) } - describe "delegation" do - let(:subscription) { create(:subscription, shop:) } - let(:validator) { Validator.new(subscription) } + describe "delegation" do + let(:subscription) { create(:subscription, shop:) } + let(:validator) { described_class.new(subscription) } - it "delegates to subscription" do - expect(validator.shop).to eq subscription.shop - expect(validator.customer).to eq subscription.customer - expect(validator.schedule).to eq subscription.schedule - expect(validator.shipping_method).to eq subscription.shipping_method - expect(validator.payment_method).to eq subscription.payment_method - expect(validator.bill_address).to eq subscription.bill_address - expect(validator.ship_address).to eq subscription.ship_address - expect(validator.begins_at).to eq subscription.begins_at - expect(validator.ends_at).to eq subscription.ends_at + it "delegates to subscription" do + expect(validator.shop).to eq subscription.shop + expect(validator.customer).to eq subscription.customer + expect(validator.schedule).to eq subscription.schedule + expect(validator.shipping_method).to eq subscription.shipping_method + expect(validator.payment_method).to eq subscription.payment_method + expect(validator.bill_address).to eq subscription.bill_address + expect(validator.ship_address).to eq subscription.ship_address + expect(validator.begins_at).to eq subscription.begins_at + expect(validator.ends_at).to eq subscription.ends_at + end + end + + describe "validations" do + let(:subscription_stubs) do + { + shop:, + customer: true, + schedule: true, + shipping_method: true, + payment_method: true, + bill_address: true, + ship_address: true, + begins_at: true, + ends_at: true, + } + end + + let(:validation_stubs) do + { + shipping_method_allowed?: true, + payment_method_allowed?: true, + payment_method_type_allowed?: true, + ends_at_after_begins_at?: true, + customer_allowed?: true, + schedule_allowed?: true, + credit_card_ok?: true, + subscription_line_items_present?: true, + requested_variants_available?: true + } + end + + let(:subscription) { instance_double(Subscription, subscription_stubs) } + let(:validator) { OrderManagement::Subscriptions::Validator.new(subscription) } + + def stub_validations(validator, methods) + methods.each do |name, value| + allow(validator).to receive(name) { value } + end + end + + describe "shipping method validation" do + let(:subscription) { + instance_double(Subscription, subscription_stubs.except(:shipping_method)) + } + before { stub_validations(validator, validation_stubs.except(:shipping_method_allowed?)) } + + context "when no shipping method is present" do + before { expect(subscription).to receive(:shipping_method).at_least(:once) { nil } } + + it "adds an error and returns false" do + expect(validator.valid?).to be false + expect(validator.errors[:shipping_method]).not_to be_empty end end - describe "validations" do - let(:subscription_stubs) do - { - shop:, - customer: true, - schedule: true, - shipping_method: true, - payment_method: true, - bill_address: true, - ship_address: true, - begins_at: true, - ends_at: true, - } - end + context "when a shipping method is present" do + let(:shipping_method) { instance_double(Spree::ShippingMethod, distributors: [shop]) } + before { + expect(subscription).to receive(:shipping_method).at_least(:once) { shipping_method } + } - let(:validation_stubs) do - { - shipping_method_allowed?: true, - payment_method_allowed?: true, - payment_method_type_allowed?: true, - ends_at_after_begins_at?: true, - customer_allowed?: true, - schedule_allowed?: true, - credit_card_ok?: true, - subscription_line_items_present?: true, - requested_variants_available?: true - } - end + context "and the shipping method is not associated with the shop" do + before { allow(shipping_method).to receive(:distributors) { [double(:enterprise)] } } - let(:subscription) { instance_double(Subscription, subscription_stubs) } - let(:validator) { OrderManagement::Subscriptions::Validator.new(subscription) } - - def stub_validations(validator, methods) - methods.each do |name, value| - allow(validator).to receive(name) { value } + it "adds an error and returns false" do + expect(validator.valid?).to be false + expect(validator.errors[:shipping_method]).not_to be_empty end end - describe "shipping method validation" do - let(:subscription) { - instance_double(Subscription, subscription_stubs.except(:shipping_method)) - } - before { stub_validations(validator, validation_stubs.except(:shipping_method_allowed?)) } + context "and the shipping method is associated with the shop" do + before { allow(shipping_method).to receive(:distributors) { [shop] } } - context "when no shipping method is present" do - before { expect(subscription).to receive(:shipping_method).at_least(:once) { nil } } - - it "adds an error and returns false" do - expect(validator.valid?).to be false - expect(validator.errors[:shipping_method]).not_to be_empty - end + it "returns true" do + expect(validator.valid?).to be true + expect(validator.errors[:shipping_method]).to be_empty end + end + end + end - context "when a shipping method is present" do - let(:shipping_method) { instance_double(Spree::ShippingMethod, distributors: [shop]) } - before { - expect(subscription).to receive(:shipping_method).at_least(:once) { shipping_method } - } + describe "payment method validation" do + let(:subscription) { + instance_double(Subscription, subscription_stubs.except(:payment_method)) + } + before { stub_validations(validator, validation_stubs.except(:payment_method_allowed?)) } - context "and the shipping method is not associated with the shop" do - before { allow(shipping_method).to receive(:distributors) { [double(:enterprise)] } } + context "when no payment method is present" do + before { expect(subscription).to receive(:payment_method).at_least(:once) { nil } } - it "adds an error and returns false" do - expect(validator.valid?).to be false - expect(validator.errors[:shipping_method]).not_to be_empty - end - end + it "adds an error and returns false" do + expect(validator.valid?).to be false + expect(validator.errors[:payment_method]).not_to be_empty + end + end - context "and the shipping method is associated with the shop" do - before { allow(shipping_method).to receive(:distributors) { [shop] } } + context "when a payment method is present" do + let(:payment_method) { instance_double(Spree::PaymentMethod, distributors: [shop]) } + before { + expect(subscription).to receive(:payment_method).at_least(:once) { payment_method } + } - it "returns true" do - expect(validator.valid?).to be true - expect(validator.errors[:shipping_method]).to be_empty - end - end + context "and the payment method is not associated with the shop" do + before { allow(payment_method).to receive(:distributors) { [double(:enterprise)] } } + + it "adds an error and returns false" do + expect(validator.valid?).to be false + expect(validator.errors[:payment_method]).not_to be_empty end end - describe "payment method validation" do - let(:subscription) { - instance_double(Subscription, subscription_stubs.except(:payment_method)) - } - before { stub_validations(validator, validation_stubs.except(:payment_method_allowed?)) } + context "and the payment method is associated with the shop" do + before { allow(payment_method).to receive(:distributors) { [shop] } } - context "when no payment method is present" do - before { expect(subscription).to receive(:payment_method).at_least(:once) { nil } } + it "returns true" do + expect(validator.valid?).to be true + expect(validator.errors[:payment_method]).to be_empty + end + end + end + end + + describe "payment method type validation" do + let(:subscription) { + instance_double(Subscription, subscription_stubs.except(:payment_method)) + } + before { + stub_validations(validator, validation_stubs.except(:payment_method_type_allowed?)) + } + + context "when a payment method is present" do + let(:payment_method) { instance_double(Spree::PaymentMethod, distributors: [shop]) } + before { + expect(subscription).to receive(:payment_method).at_least(:once) { payment_method } + } + + context "and the payment method type is not in the approved list" do + before { allow(payment_method).to receive(:type) { "Blah" } } + + it "adds an error and returns false" do + expect(validator.valid?).to be false + expect(validator.errors[:payment_method]).not_to be_empty + end + end + + context "and the payment method is in the approved list" do + let(:approved_type) { Subscription::ALLOWED_PAYMENT_METHOD_TYPES.first } + before { allow(payment_method).to receive(:type) { approved_type } } + + it "returns true" do + expect(validator.valid?).to be true + expect(validator.errors[:payment_method]).to be_empty + end + end + end + end + + describe "dates" do + let(:subscription) { + instance_double(Subscription, subscription_stubs.except(:begins_at, :ends_at)) + } + before { stub_validations(validator, validation_stubs.except(:ends_at_after_begins_at?)) } + before { expect(subscription).to receive(:begins_at).at_least(:once) { begins_at } } + + context "when no begins_at is present" do + let(:begins_at) { nil } + + it "adds an error and returns false" do + expect(validator.valid?).to be false + expect(validator.errors[:begins_at]).not_to be_empty + end + end + + context "when a start date is present" do + let(:begins_at) { Time.zone.today } + before { expect(subscription).to receive(:ends_at).at_least(:once) { ends_at } } + + context "when no ends_at is present" do + let(:ends_at) { nil } + + it "returns true" do + expect(validator.valid?).to be true + expect(validator.errors[:ends_at]).to be_empty + end + end + + context "when ends_at is equal to begins_at" do + let(:ends_at) { Time.zone.today } + it "adds an error and returns false" do + expect(validator.valid?).to be false + expect(validator.errors[:ends_at]).not_to be_empty + end + end + + context "when ends_at is before begins_at" do + let(:ends_at) { Time.zone.today - 1.day } + it "adds an error and returns false" do + expect(validator.valid?).to be false + expect(validator.errors[:ends_at]).not_to be_empty + end + end + + context "when ends_at is after begins_at" do + let(:ends_at) { Time.zone.today + 1.day } + it "adds an error and returns false" do + expect(validator.valid?).to be true + expect(validator.errors[:ends_at]).to be_empty + end + end + end + end + + describe "addresses" do + before { stub_validations(validator, validation_stubs) } + let(:subscription) { + instance_double(Subscription, subscription_stubs.except(:bill_address, :ship_address)) + } + before { expect(subscription).to receive(:bill_address).at_least(:once) { bill_address } } + before { expect(subscription).to receive(:ship_address).at_least(:once) { ship_address } } + + context "when bill_address and ship_address are not present" do + let(:bill_address) { nil } + let(:ship_address) { nil } + + it "adds an error and returns false" do + expect(validator.valid?).to be false + expect(validator.errors[:bill_address]).not_to be_empty + expect(validator.errors[:ship_address]).not_to be_empty + end + end + + context "when bill_address and ship_address are present" do + let(:bill_address) { instance_double(Spree::Address) } + let(:ship_address) { instance_double(Spree::Address) } + + it "returns true" do + expect(validator.valid?).to be true + expect(validator.errors[:bill_address]).to be_empty + expect(validator.errors[:ship_address]).to be_empty + end + end + end + + describe "customer" do + let(:subscription) { instance_double(Subscription, subscription_stubs.except(:customer)) } + before { stub_validations(validator, validation_stubs.except(:customer_allowed?)) } + before { expect(subscription).to receive(:customer).at_least(:once) { customer } } + + context "when no customer is present" do + let(:customer) { nil } + + it "adds an error and returns false" do + expect(validator.valid?).to be false + expect(validator.errors[:customer]).not_to be_empty + end + end + + context "when a customer is present" do + let(:customer) { instance_double(Customer) } + + context "and the customer is not associated with the shop" do + before { allow(customer).to receive(:enterprise) { double(:enterprise) } } + + it "adds an error and returns false" do + expect(validator.valid?).to be false + expect(validator.errors[:customer]).not_to be_empty + end + end + + context "and the customer is associated with the shop" do + before { allow(customer).to receive(:enterprise) { shop } } + + it "returns true" do + expect(validator.valid?).to be true + expect(validator.errors[:customer]).to be_empty + end + end + end + end + + describe "schedule" do + let(:subscription) { instance_double(Subscription, subscription_stubs.except(:schedule)) } + before { stub_validations(validator, validation_stubs.except(:schedule_allowed?)) } + before { expect(subscription).to receive(:schedule).at_least(:once) { schedule } } + + context "when no schedule is present" do + let(:schedule) { nil } + + it "adds an error and returns false" do + expect(validator.valid?).to be false + expect(validator.errors[:schedule]).not_to be_empty + end + end + + context "when a schedule is present" do + let(:schedule) { instance_double(Schedule) } + + context "and the schedule is not associated with the shop" do + before { allow(schedule).to receive(:coordinators) { [double(:enterprise)] } } + + it "adds an error and returns false" do + expect(validator.valid?).to be false + expect(validator.errors[:schedule]).not_to be_empty + end + end + + context "and the schedule is associated with the shop" do + before { allow(schedule).to receive(:coordinators) { [shop] } } + + it "returns true" do + expect(validator.valid?).to be true + expect(validator.errors[:schedule]).to be_empty + end + end + end + end + + describe "credit card" do + let(:subscription) { + instance_double(Subscription, subscription_stubs.except(:payment_method)) + } + before { stub_validations(validator, validation_stubs.except(:credit_card_ok?)) } + before { + expect(subscription).to receive(:payment_method).at_least(:once) { payment_method } + } + + context "when using a Check payment method" do + let(:payment_method) { + instance_double(Spree::PaymentMethod, type: "Spree::PaymentMethod::Check") + } + + it "returns true" do + expect(validator.valid?).to be true + expect(validator.errors[:subscription_line_items]).to be_empty + end + end + + context "when using the StripeSCA payment gateway" do + let(:payment_method) { + instance_double(Spree::PaymentMethod, type: "Spree::Gateway::StripeSCA") + } + before { expect(subscription).to receive(:customer).at_least(:once) { customer } } + + context "when the customer does not allow charges" do + let(:customer) { instance_double(Customer, allow_charges: false) } + + it "adds an error and returns false" do + expect(validator.valid?).to be false + expect(validator.errors[:payment_method]).not_to be_empty + end + end + + context "when the customer allows charges" do + let(:customer) { instance_double(Customer, allow_charges: true) } + + context "and the customer is not associated with a user" do + before { allow(customer).to receive(:user) { nil } } it "adds an error and returns false" do expect(validator.valid?).to be false @@ -119,14 +385,11 @@ module OrderManagement end end - context "when a payment method is present" do - let(:payment_method) { instance_double(Spree::PaymentMethod, distributors: [shop]) } - before { - expect(subscription).to receive(:payment_method).at_least(:once) { payment_method } - } + context "and the customer is associated with a user" do + before { expect(customer).to receive(:user).once { user } } - context "and the payment method is not associated with the shop" do - before { allow(payment_method).to receive(:distributors) { [double(:enterprise)] } } + context "and the user has no default card set" do + let(:user) { instance_double(Spree::User, default_card: nil) } it "adds an error and returns false" do expect(validator.valid?).to be false @@ -134,8 +397,8 @@ module OrderManagement end end - context "and the payment method is associated with the shop" do - before { allow(payment_method).to receive(:distributors) { [shop] } } + context "and the user has a default card set" do + let(:user) { instance_double(Spree::User, default_card: 'some card') } it "returns true" do expect(validator.valid?).to be true @@ -144,416 +407,149 @@ module OrderManagement end end end - - describe "payment method type validation" do - let(:subscription) { - instance_double(Subscription, subscription_stubs.except(:payment_method)) - } - before { - stub_validations(validator, validation_stubs.except(:payment_method_type_allowed?)) - } - - context "when a payment method is present" do - let(:payment_method) { instance_double(Spree::PaymentMethod, distributors: [shop]) } - before { - expect(subscription).to receive(:payment_method).at_least(:once) { payment_method } - } - - context "and the payment method type is not in the approved list" do - before { allow(payment_method).to receive(:type) { "Blah" } } - - it "adds an error and returns false" do - expect(validator.valid?).to be false - expect(validator.errors[:payment_method]).not_to be_empty - end - end - - context "and the payment method is in the approved list" do - let(:approved_type) { Subscription::ALLOWED_PAYMENT_METHOD_TYPES.first } - before { allow(payment_method).to receive(:type) { approved_type } } - - it "returns true" do - expect(validator.valid?).to be true - expect(validator.errors[:payment_method]).to be_empty - end - end - end - end - - describe "dates" do - let(:subscription) { - instance_double(Subscription, subscription_stubs.except(:begins_at, :ends_at)) - } - before { stub_validations(validator, validation_stubs.except(:ends_at_after_begins_at?)) } - before { expect(subscription).to receive(:begins_at).at_least(:once) { begins_at } } - - context "when no begins_at is present" do - let(:begins_at) { nil } - - it "adds an error and returns false" do - expect(validator.valid?).to be false - expect(validator.errors[:begins_at]).not_to be_empty - end - end - - context "when a start date is present" do - let(:begins_at) { Time.zone.today } - before { expect(subscription).to receive(:ends_at).at_least(:once) { ends_at } } - - context "when no ends_at is present" do - let(:ends_at) { nil } - - it "returns true" do - expect(validator.valid?).to be true - expect(validator.errors[:ends_at]).to be_empty - end - end - - context "when ends_at is equal to begins_at" do - let(:ends_at) { Time.zone.today } - it "adds an error and returns false" do - expect(validator.valid?).to be false - expect(validator.errors[:ends_at]).not_to be_empty - end - end - - context "when ends_at is before begins_at" do - let(:ends_at) { Time.zone.today - 1.day } - it "adds an error and returns false" do - expect(validator.valid?).to be false - expect(validator.errors[:ends_at]).not_to be_empty - end - end - - context "when ends_at is after begins_at" do - let(:ends_at) { Time.zone.today + 1.day } - it "adds an error and returns false" do - expect(validator.valid?).to be true - expect(validator.errors[:ends_at]).to be_empty - end - end - end - end - - describe "addresses" do - before { stub_validations(validator, validation_stubs) } - let(:subscription) { - instance_double(Subscription, subscription_stubs.except(:bill_address, :ship_address)) - } - before { expect(subscription).to receive(:bill_address).at_least(:once) { bill_address } } - before { expect(subscription).to receive(:ship_address).at_least(:once) { ship_address } } - - context "when bill_address and ship_address are not present" do - let(:bill_address) { nil } - let(:ship_address) { nil } - - it "adds an error and returns false" do - expect(validator.valid?).to be false - expect(validator.errors[:bill_address]).not_to be_empty - expect(validator.errors[:ship_address]).not_to be_empty - end - end - - context "when bill_address and ship_address are present" do - let(:bill_address) { instance_double(Spree::Address) } - let(:ship_address) { instance_double(Spree::Address) } - - it "returns true" do - expect(validator.valid?).to be true - expect(validator.errors[:bill_address]).to be_empty - expect(validator.errors[:ship_address]).to be_empty - end - end - end - - describe "customer" do - let(:subscription) { instance_double(Subscription, subscription_stubs.except(:customer)) } - before { stub_validations(validator, validation_stubs.except(:customer_allowed?)) } - before { expect(subscription).to receive(:customer).at_least(:once) { customer } } - - context "when no customer is present" do - let(:customer) { nil } - - it "adds an error and returns false" do - expect(validator.valid?).to be false - expect(validator.errors[:customer]).not_to be_empty - end - end - - context "when a customer is present" do - let(:customer) { instance_double(Customer) } - - context "and the customer is not associated with the shop" do - before { allow(customer).to receive(:enterprise) { double(:enterprise) } } - - it "adds an error and returns false" do - expect(validator.valid?).to be false - expect(validator.errors[:customer]).not_to be_empty - end - end - - context "and the customer is associated with the shop" do - before { allow(customer).to receive(:enterprise) { shop } } - - it "returns true" do - expect(validator.valid?).to be true - expect(validator.errors[:customer]).to be_empty - end - end - end - end - - describe "schedule" do - let(:subscription) { instance_double(Subscription, subscription_stubs.except(:schedule)) } - before { stub_validations(validator, validation_stubs.except(:schedule_allowed?)) } - before { expect(subscription).to receive(:schedule).at_least(:once) { schedule } } - - context "when no schedule is present" do - let(:schedule) { nil } - - it "adds an error and returns false" do - expect(validator.valid?).to be false - expect(validator.errors[:schedule]).not_to be_empty - end - end - - context "when a schedule is present" do - let(:schedule) { instance_double(Schedule) } - - context "and the schedule is not associated with the shop" do - before { allow(schedule).to receive(:coordinators) { [double(:enterprise)] } } - - it "adds an error and returns false" do - expect(validator.valid?).to be false - expect(validator.errors[:schedule]).not_to be_empty - end - end - - context "and the schedule is associated with the shop" do - before { allow(schedule).to receive(:coordinators) { [shop] } } - - it "returns true" do - expect(validator.valid?).to be true - expect(validator.errors[:schedule]).to be_empty - end - end - end - end - - describe "credit card" do - let(:subscription) { - instance_double(Subscription, subscription_stubs.except(:payment_method)) - } - before { stub_validations(validator, validation_stubs.except(:credit_card_ok?)) } - before { - expect(subscription).to receive(:payment_method).at_least(:once) { payment_method } - } - - context "when using a Check payment method" do - let(:payment_method) { - instance_double(Spree::PaymentMethod, type: "Spree::PaymentMethod::Check") - } - - it "returns true" do - expect(validator.valid?).to be true - expect(validator.errors[:subscription_line_items]).to be_empty - end - end - - context "when using the StripeSCA payment gateway" do - let(:payment_method) { - instance_double(Spree::PaymentMethod, type: "Spree::Gateway::StripeSCA") - } - before { expect(subscription).to receive(:customer).at_least(:once) { customer } } - - context "when the customer does not allow charges" do - let(:customer) { instance_double(Customer, allow_charges: false) } - - it "adds an error and returns false" do - expect(validator.valid?).to be false - expect(validator.errors[:payment_method]).not_to be_empty - end - end - - context "when the customer allows charges" do - let(:customer) { instance_double(Customer, allow_charges: true) } - - context "and the customer is not associated with a user" do - before { allow(customer).to receive(:user) { nil } } - - it "adds an error and returns false" do - expect(validator.valid?).to be false - expect(validator.errors[:payment_method]).not_to be_empty - end - end - - context "and the customer is associated with a user" do - before { expect(customer).to receive(:user).once { user } } - - context "and the user has no default card set" do - let(:user) { instance_double(Spree::User, default_card: nil) } - - it "adds an error and returns false" do - expect(validator.valid?).to be false - expect(validator.errors[:payment_method]).not_to be_empty - end - end - - context "and the user has a default card set" do - let(:user) { instance_double(Spree::User, default_card: 'some card') } - - it "returns true" do - expect(validator.valid?).to be true - expect(validator.errors[:payment_method]).to be_empty - end - end - end - end - end - end - - describe "subscription line items" do - let(:subscription) { instance_double(Subscription, subscription_stubs) } - before { - stub_validations(validator, validation_stubs.except(:subscription_line_items_present?)) - } - before { - expect(subscription).to receive(:subscription_line_items).at_least(:once) { - subscription_line_items - } - } - - context "when no subscription line items exist" do - let(:subscription_line_items) { [] } - - it "adds an error and returns false" do - expect(validator.valid?).to be false - expect(validator.errors[:subscription_line_items]).not_to be_empty - end - end - - context "when some subscription line items exist" do - context "and they have some quantity but all are marked for destruction" do - let(:subscription_line_item1) { - instance_double(SubscriptionLineItem, marked_for_destruction?: true, quantity: 1) - } - let(:subscription_line_items) { [subscription_line_item1] } - - it "adds an error and returns false" do - expect(validator.valid?).to be false - expect(validator.errors[:subscription_line_items]).not_to be_empty - end - end - - context "and at least one has some quantity and is not marked for destruction" do - let(:subscription_line_item1) { - instance_double(SubscriptionLineItem, marked_for_destruction?: true, quantity: 1) - } - let(:subscription_line_item2) { - instance_double(SubscriptionLineItem, marked_for_destruction?: false, quantity: 1) - } - let(:subscription_line_items) { [subscription_line_item1, subscription_line_item2] } - - it "returns true" do - expect(validator.valid?).to be true - expect(validator.errors[:subscription_line_items]).to be_empty - end - end - - context "and they are not marked for destruction but all have no quantity" do - let(:subscription_line_item1) { - instance_double(SubscriptionLineItem, marked_for_destruction?: false, quantity: 0) - } - let(:subscription_line_items) { [subscription_line_item1] } - - it "adds an error and returns false" do - expect(validator.valid?).to be false - expect(validator.errors[:subscription_line_items]).not_to be_empty - end - end - - context "but all have no quantity and are marked for destruction" do - let(:subscription_line_item1) { - instance_double(SubscriptionLineItem, marked_for_destruction?: true, quantity: 0) - } - let(:subscription_line_items) { [subscription_line_item1] } - - it "adds an error and returns false" do - expect(validator.valid?).to be false - expect(validator.errors[:subscription_line_items]).not_to be_empty - end - end - end - end - - describe "variant availability" do - let(:subscription) { instance_double(Subscription, subscription_stubs) } - before { - stub_validations(validator, validation_stubs.except(:requested_variants_available?)) - } - before { - expect(subscription).to receive(:subscription_line_items).at_least(:once) { - subscription_line_items - } - } - - context "when no subscription line items exist" do - let(:subscription_line_items) { [] } - - it "returns true" do - expect(validator.valid?).to be true - expect(validator.errors[:subscription_line_items]).to be_empty - end - end - - context "when subscription line items exist" do - let(:variant1) { instance_double(Spree::Variant, id: 1, deleted_at: nil) } - let(:variant2) { instance_double(Spree::Variant, id: 2) } - let(:variant3) { instance_double(Spree::Variant, id: 1, deleted_at: Time.zone.now ) } - let(:subscription_line_item1) { - instance_double(SubscriptionLineItem, - variant: variant1, - marked_for_destruction?: false) - } - let(:subscription_line_item2) { - instance_double(SubscriptionLineItem, - variant: variant2, - marked_for_destruction?: false) - } - let(:subscription_line_item3) { - instance_double(SubscriptionLineItem, - variant: variant3, - marked_for_destruction?: true) - } - let(:subscription_line_items) { [subscription_line_item1, subscription_line_item3] } - - context "but some variants are unavailable" do - let(:product) { instance_double(Spree::Product, name: "some_name") } - - before do - allow(validator).to receive(:available_variant_ids) { [variant2.id] } - allow(variant1).to receive(:product) { product } - allow(variant1).to receive(:full_name) { "some name" } - end - - it "adds an error and returns false" do - expect(validator.valid?).to be false - expect(validator.errors[:subscription_line_items]).not_to be_empty - end - end - - context "and all requested variants are available" do - before do - allow(validator).to receive(:available_variant_ids) { [variant1.id, variant2.id] } - end - - it "returns true" do - expect(validator.valid?).to be true - expect(validator.errors[:subscription_line_items]).to be_empty - end - end - end - end + end + end + + describe "subscription line items" do + let(:subscription) { instance_double(Subscription, subscription_stubs) } + before { + stub_validations(validator, validation_stubs.except(:subscription_line_items_present?)) + } + before { + expect(subscription).to receive(:subscription_line_items).at_least(:once) { + subscription_line_items + } + } + + context "when no subscription line items exist" do + let(:subscription_line_items) { [] } + + it "adds an error and returns false" do + expect(validator.valid?).to be false + expect(validator.errors[:subscription_line_items]).not_to be_empty + end + end + + context "when some subscription line items exist" do + context "and they have some quantity but all are marked for destruction" do + let(:subscription_line_item1) { + instance_double(SubscriptionLineItem, marked_for_destruction?: true, quantity: 1) + } + let(:subscription_line_items) { [subscription_line_item1] } + + it "adds an error and returns false" do + expect(validator.valid?).to be false + expect(validator.errors[:subscription_line_items]).not_to be_empty + end + end + + context "and at least one has some quantity and is not marked for destruction" do + let(:subscription_line_item1) { + instance_double(SubscriptionLineItem, marked_for_destruction?: true, quantity: 1) + } + let(:subscription_line_item2) { + instance_double(SubscriptionLineItem, marked_for_destruction?: false, quantity: 1) + } + let(:subscription_line_items) { [subscription_line_item1, subscription_line_item2] } + + it "returns true" do + expect(validator.valid?).to be true + expect(validator.errors[:subscription_line_items]).to be_empty + end + end + + context "and they are not marked for destruction but all have no quantity" do + let(:subscription_line_item1) { + instance_double(SubscriptionLineItem, marked_for_destruction?: false, quantity: 0) + } + let(:subscription_line_items) { [subscription_line_item1] } + + it "adds an error and returns false" do + expect(validator.valid?).to be false + expect(validator.errors[:subscription_line_items]).not_to be_empty + end + end + + context "but all have no quantity and are marked for destruction" do + let(:subscription_line_item1) { + instance_double(SubscriptionLineItem, marked_for_destruction?: true, quantity: 0) + } + let(:subscription_line_items) { [subscription_line_item1] } + + it "adds an error and returns false" do + expect(validator.valid?).to be false + expect(validator.errors[:subscription_line_items]).not_to be_empty + end + end + end + end + + describe "variant availability" do + let(:subscription) { instance_double(Subscription, subscription_stubs) } + before { + stub_validations(validator, validation_stubs.except(:requested_variants_available?)) + } + before { + expect(subscription).to receive(:subscription_line_items).at_least(:once) { + subscription_line_items + } + } + + context "when no subscription line items exist" do + let(:subscription_line_items) { [] } + + it "returns true" do + expect(validator.valid?).to be true + expect(validator.errors[:subscription_line_items]).to be_empty + end + end + + context "when subscription line items exist" do + let(:variant1) { instance_double(Spree::Variant, id: 1, deleted_at: nil) } + let(:variant2) { instance_double(Spree::Variant, id: 2) } + let(:variant3) { instance_double(Spree::Variant, id: 1, deleted_at: Time.zone.now ) } + let(:subscription_line_item1) { + instance_double(SubscriptionLineItem, + variant: variant1, + marked_for_destruction?: false) + } + let(:subscription_line_item2) { + instance_double(SubscriptionLineItem, + variant: variant2, + marked_for_destruction?: false) + } + let(:subscription_line_item3) { + instance_double(SubscriptionLineItem, + variant: variant3, + marked_for_destruction?: true) + } + let(:subscription_line_items) { [subscription_line_item1, subscription_line_item3] } + + context "but some variants are unavailable" do + let(:product) { instance_double(Spree::Product, name: "some_name") } + + before do + allow(validator).to receive(:available_variant_ids) { [variant2.id] } + allow(variant1).to receive(:product) { product } + allow(variant1).to receive(:full_name) { "some name" } + end + + it "adds an error and returns false" do + expect(validator.valid?).to be false + expect(validator.errors[:subscription_line_items]).not_to be_empty + end + end + + context "and all requested variants are available" do + before do + allow(validator).to receive(:available_variant_ids) { [variant1.id, variant2.id] } + end + + it "returns true" do + expect(validator.valid?).to be true + expect(validator.errors[:subscription_line_items]).to be_empty + end + end end end end