# frozen_string_literal: true require 'spec_helper' RSpec.describe Sets::ProductSet do describe '#save' do let(:product_set) do described_class.new(collection_attributes: collection_hash) end subject{ product_set.save } context 'when the product does not exist yet' do let(:collection_hash) do { 0 => { name: 'a product', price: 2.0, supplier_id: create(:enterprise).id, primary_taxon_id: create(:taxon).id, unit_description: 'description', variant_unit: 'items', variant_unit_name: 'bunches', shipping_category_id: create(:shipping_category).id } } end it 'does not create a new product' do product_set.save expect(Spree::Product.last).to be nil end end context 'when the product does exist' do let(:product) { create(:simple_product, name: "product name") } context "with valid name" do let(:collection_hash) { { 0 => { id: product.id, name: "New season product" } } } it "returns true and counts results" do is_expected.to eq true expect(product_set.saved_count).to eq 1 end end context "with invalid name" do let(:collection_hash) { { 0 => { id: product.id, name: "" } } # Product Name can't be blank } it "returns false and counts results" do is_expected.to eq false expect(product_set.saved_count).to eq 0 end end context 'when a different variant_unit is passed' do let!(:product) do create( :simple_product, variant_unit: 'items', variant_unit_scale: nil, variant_unit_name: 'bunches', unit_value: nil, unit_description: 'some description' ) end let(:variant) { product.variants.first } let(:collection_hash) do { 0 => { id: product.id, variants_attributes: [{ id: variant.id.to_s, variant_unit: 'weight', variant_unit_scale: 1 }] } } end it 'updates the product without error' do expect(product_set.save).to eq true # updating variant doesn't increment saved_count # expect(product_set.saved_count).to eq 1 expect(variant.reload.attributes).to include( 'variant_unit' => 'weight' ) expect(product_set.errors).to be_empty end end context "when the product is in an order cycle" do let(:distributor) { create(:distributor_enterprise) } let!(:order_cycle) { create(:simple_order_cycle, variants: [product.variants.first], coordinator: distributor, distributors: [distributor]) } context 'and only the name changes' do let(:collection_hash) do { 0 => { id: product.id, name: "New season product" } } end it 'updates the product and keeps it in order cycles' do expect { product_set.save product.reload }.to change { product.name }.to("New season product"). and change { order_cycle.distributed_variants.count }.by(0) expect(order_cycle.distributed_variants).to include product.variants.first end end end context "when product attributes are not changed" do let(:collection_hash) { { 0 => { id: product.id, name: product.name } } } it 'returns true' do is_expected.to eq true end it 'does not increase saved_count' do subject expect(product_set.saved_count).to eq 0 end it 'does not update any product by calling save' do expect_any_instance_of(Spree::Product).not_to receive(:save) subject end end end describe "updating a product's variants" do let(:product) { create(:simple_product, supplier_id: create(:supplier_enterprise).id) } let(:variant) { product.variants.first } let(:product_attributes) { {} } let(:variant_attributes) { { sku: "var_sku" } } let(:variants_attributes) { [{ **variant_attributes, id: product.variants.first.id.to_s }] } let(:collection_hash) { { 0 => { id: product.id, **product_attributes, variants_attributes: } } } it "updates the variant" do expect { product_set.save variant.reload }.to change { variant.sku }.to("var_sku") pending expect(product_set.saved_count).to eq 1 end shared_examples "nothing saved" do it "doesn't update product" do expect { product_set.save product.reload }.not_to change { product.sku } expect(product_set.saved_count).to be_zero expect(product_set.invalid.count).to be_positive end it "doesn't update variant" do expect { product_set.save variant.reload }.not_to change { variant.sku } end it 'assigns the in-memory attributes of the variant' do pending expect { product_set.save }.to change { variant.sku }.to("123") end end context "variant has error" do let(:variant_attributes) { { sku: "var_sku", display_name: "A" * 256 } } # maximum length include_examples "nothing saved" end context "when attributes are not changed" do let(:variant_attributes) { { sku: variant.sku } } before { variant } it 'updates product by calling save' do expect_any_instance_of(Spree::Variant).not_to receive(:save) subject end it 'does not increase saved_count' do subject expect(product_set.saved_count).to eq 0 end end context "when the variant is in an order cycle" do let(:distributor) { create(:distributor_enterprise) } let!(:order_cycle) { create(:simple_order_cycle, variants: [variant], coordinator: distributor, distributors: [distributor]) } let(:variant_attributes) { { display_name: "New season variant" } } it 'updates the variant and keeps it in order cycles' do expect { product_set.save variant.reload }.to change { variant.display_name }.to("New season variant"). and change { order_cycle.distributed_variants.count }.by(0) expect(order_cycle.distributed_variants).to include variant end context 'when supplier is updated' do let(:producer) { create(:supplier_enterprise) } let(:variant_attributes) { { supplier_id: producer.id } } it 'updates the variant and removes the variant from order cycles' do expect { product_set.save variant.reload }.to change { variant.supplier }.to(producer). and change { order_cycle.distributed_variants.count }.by(-1) expect(order_cycle.distributed_variants).not_to include variant end end end context "when products attributes are also updated" do let(:product_attributes) { { sku: "prod_sku" } } it "updates product and variant" do expect { product_set.save product.reload }.to change { product.sku }.to("prod_sku") .and change { product.variants.first.sku }.to("var_sku") expect(product_set.saved_count).to eq 1 end xcontext "variant has error" do let(:variant_attributes) { { sku: "var_sku", display_name: "A" * 256 } } # maximum length include_examples "nothing saved" end context "product has error" do before { collection_hash[0][:name] = "" } # product.name can't be blank include_examples "nothing saved" end end context "when multiple variants are updated" do let(:variant2) { create(:variant, product:) } let(:variants_attributes) { [ { **variant_attributes, id: product.variants.first.id.to_s }, { sku: "var_sku2", id: variant2.id.to_s }, ] } it "updates each variant" do expect { product_set.save variant2.reload }.to change { product.variants.first.sku }.to("var_sku") .and change { variant2.sku }.to("var_sku2") end xcontext "variant has error" do let(:variant_attributes) { { sku: "var_sku", display_name: "A" * 256 } } # maximum length include_examples "nothing saved" do after { expect(variant2.reload.sku).not_to eq "var_sku2" } end end end context "new variant" do let(:variants_attributes) { [ { id: product.variants.first.id.to_s }, # default variant unchanged # omit ID for new variant { sku: "new sku", price: "5.00", unit_value: "5", variant_unit: "weight", variant_unit_scale: 1, supplier_id: supplier.id, primary_taxon_id: create(:taxon).id }, ] } let(:supplier) { create(:supplier_enterprise) } it "creates new variant" do expect { product_set.save expect(product_set.errors).to be_empty }.to change { product.variants.count }.by(1) variant = product.variants.last expect(variant.sku).to eq "new sku" expect(variant.price).to eq 5.00 expect(variant.unit_value).to eq 5 expect(variant.variant_unit).to eq "weight" expect(variant.variant_unit_scale).to eq 1 end context "variant has error" do let(:variants_attributes) { [ { id: product.variants.first.id.to_s }, # default variant unchanged # price missing, unit_value should be number { sku: "new sku", unit_value: "blah", supplier_id: supplier.id }, ] } include_examples "nothing saved" it "logs variant errors" do product_set.save expect(product_set.errors.full_messages).to include( "Variant price is not a number", "Variant price can't be blank", "Variant unit value is not a number" ) end end end end context 'when there are multiple products' do let(:product_c) { create(:simple_product, name: "Carrots") } let!(:product_b) { create(:simple_product, name: "Bananas") } let!(:product_a) { create(:simple_product, name: "Apples") } let(:collection_hash) do { 0 => { id: product_a.id, name: "Pommes", }, 1 => { id: product_b.id, name: "Bananes", }, 2 => { id: product_c.id, name: "Carrots", }, } end it 'updates the products' do expect(product_set.save).to eq true expect(product_set.saved_count).to eq 2 # only two were changed expect(product_a.reload.name).to eq "Pommes" expect(product_b.reload.name).to eq "Bananes" expect(product_c.reload.name).to eq "Carrots" # no change end it 'retains the order of products' do product_set.save # even though the first product is now alphabetically last expect(product_set.collection[0]).to eq product_a.reload expect(product_set.collection[1]).to eq product_b.reload expect(product_set.collection[2]).to eq product_c.reload end context 'first product has an error' do before { collection_hash[0][:name] = "" } # product.name can't be blank it 'continues to update subsequent products' do product_set.save expect(product_set.saved_count).to eq 1 # Errors are logged on the model first_item = product_set.collection[0] expect(first_item.errors.full_messages.to_sentence).to eq "Product Name can't be blank" expect(first_item.name).to eq "" # Subsequent product was updated expect(product_b.reload.name).to eq "Bananes" end end end end end