<NME> product_set_spec.rb
<BEF> # frozen_string_literal: true

require 'spec_helper'

describe Sets::ProductSet do
  describe '#save' do
    context 'when passing :collection_attributes' do
      let(:product_set) do
        described_class.new(collection_attributes: collection_hash)
      end

      context 'when the product does not exist yet' do
        let!(:stock_location) { create(:stock_location, backorderable_default: false) }
        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
        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(:collection_hash) do
            {
              0 => {
                id: product.id,
                variant_unit: 'weight',
                variant_unit_scale: 1
              }
            }
          end

          it 'updates the product' do
            product_set.save

            expect(product.reload.attributes).to include(
              'variant_unit' => 'weight'
            )
          end

          it 'does not add an error' do
            product_set.save
            expect(product_set.errors)
              .to be_empty
          end

          it 'returns true' do
            expect(product_set.save).to eq(true)
          end
        end

        context 'when a different supplier is passed' do
          let!(:producer) { create(:enterprise) }
          let!(:product) { create(:simple_product) }
          let(:collection_hash) do
            {
              0 => {
                id: product.id,
                supplier_id: producer.id
              }
            }
          end

          let(:distributor) { create(:distributor_enterprise) }
          let!(:order_cycle) {
            create(:simple_order_cycle, variants: [product.variants.first],
                                        coordinator: distributor,
                                        distributors: [distributor])
          }

          it 'updates the product and removes the product from order cycles' do
            product_set.save

            expect(product.reload.attributes).to include(
              'supplier_id' => producer.id
            )
            expect(order_cycle.distributed_variants).to_not include product.variants.first
          end
        end

        context 'when attributes of the variants are passed' do
          let!(:product) { create(:simple_product) }
          let(:collection_hash) { { 0 => { id: product.id } } }

          context 'when :variants_attributes are passed' do
            let(:variants_attributes) { [{ sku: '123', id: product.variants.first.id.to_s }] }

            before { collection_hash[0][:variants_attributes] = variants_attributes }

            it 'updates the attributes of the variant' do
              product_set.save

              expect(product.reload.variants.first[:sku]).to eq variants_attributes.first[:sku]
            end

            context 'and when product attributes are also passed' do
              it 'updates product and variant attributes' do
                collection_hash[0][:permalink] = "test_permalink"

                product_set.save

                expect(product.reload.variants.first[:sku]).to eq variants_attributes.first[:sku]
                expect(product.reload.attributes).to include(
                  'permalink' => "test_permalink"
                )
              end
            end
          end

          context 'when :master_attributes is passed' do
            let(:master_attributes) { { sku: '123' } }

            before do
              collection_hash[0][:master_attributes] = master_attributes
            end

            context 'and the variant does exist' do
              let!(:variant) { create(:variant, product: product) }

              before { master_attributes[:id] = variant.id }

              it 'updates the attributes of the master variant' do
                product_set.save
                expect(variant.reload.sku).to eq('123')
              end
            end

            context 'and the variant does not exist' do
              context 'and attributes provided are valid' do
                let(:master_attributes) do
                  attributes_for(:variant).merge(sku: '123')
                end

                it 'creates it with the specified attributes' do
                  number_of_variants = Spree::Variant.all.size
                  product_set.save
                  expect(Spree::Variant.last.sku).to eq('123')
                  expect(Spree::Variant.all.size).to eq number_of_variants + 1
                end
              end

              context 'and attributes provided are not valid' do
                let(:master_attributes) do
                  # unit_value nil makes the variant invalid
                  # on_hand with a value would make on_hand be updated and fail with exception
                  attributes_for(:variant).merge(unit_value: nil, on_hand: 1, sku: '321')
                end

                it 'does not create variant and notifies bugsnag still raising the exception' do
                  number_of_variants = Spree::Variant.all.size
                  expect(product_set.save).to eq(false)
                  expect(Spree::Variant.all.size).to eq number_of_variants
                  expect(Spree::Variant.last.sku).not_to eq('321')
                end
              end
            end
          end
        end
      end
    end
  end
end

<MSG> Spec both cases of removing variants from order cycles

The spec tested only the removal but not he keeping of variants.
I want to change this in the next commit to simplify the class.

<DFF> @@ -75,17 +75,9 @@ describe Sets::ProductSet do
           end
         end
 
-        context 'when a different supplier is passed' do
-          let!(:producer) { create(:enterprise) }
-          let!(:product) { create(:simple_product) }
-          let(:collection_hash) do
-            {
-              0 => {
-                id: product.id,
-                supplier_id: producer.id
-              }
-            }
-          end
+        context "when the product is in an order cycle" do
+          let(:producer) { create(:enterprise) }
+          let(:product) { create(:simple_product) }
 
           let(:distributor) { create(:distributor_enterprise) }
           let!(:order_cycle) {
@@ -94,13 +86,36 @@ describe Sets::ProductSet do
                                         distributors: [distributor])
           }
 
-          it 'updates the product and removes the product from order cycles' do
-            product_set.save
+          context 'and only the name changes' do
+            let(:collection_hash) do
+              { 0 => { id: product.id, name: "New season product" } }
+            end
 
-            expect(product.reload.attributes).to include(
-              'supplier_id' => producer.id
-            )
-            expect(order_cycle.distributed_variants).to_not include product.variants.first
+            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
+
+          context 'and a different supplier is passed' do
+            let(:collection_hash) do
+              { 0 => { id: product.id, supplier_id: producer.id } }
+            end
+
+            it 'updates the product and removes the product from order cycles' do
+              expect {
+                product_set.save
+                product.reload
+              }.to change { product.supplier }.to(producer).
+                and change { order_cycle.distributed_variants.count }.by(-1)
+
+              expect(order_cycle.distributed_variants).to_not include product.variants.first
+            end
           end
         end
 

{'commit': 'diff --git a/spec/services/sets/product_set_spec.rb b/spec/services/sets/product_set_spec.rb\nindex 026d3b3ff..38d54a7f1 100644\n--- a/spec/services/sets/product_set_spec.rb\n+++ b/spec/services/sets/product_set_spec.rb\n@@ -75,17 +75,9 @@ describe Sets::ProductSet do\n           end\n         end\n \n-        context \'when a different supplier is passed\' do\n-          let!(:producer) { create(:enterprise) }\n-          let!(:product) { create(:simple_product) }\n-          let(:collection_hash) do\n-            {\n-              0 => {\n-                id: product.id,\n-                supplier_id: producer.id\n-              }\n-            }\n-          end\n+        context "when the product is in an order cycle" do\n+          let(:producer) { create(:enterprise) }\n+          let(:product) { create(:simple_product) }\n \n           let(:distributor) { create(:distributor_enterprise) }\n           let!(:order_cycle) {\n@@ -94,13 +86,36 @@ describe Sets::ProductSet do\n                                         distributors: [distributor])\n           }\n \n-          it \'updates the product and removes the product from order cycles\' do\n-            product_set.save\n+          context \'and only the name changes\' do\n+            let(:collection_hash) do\n+              { 0 => { id: product.id, name: "New season product" } }\n+            end\n \n-            expect(product.reload.attributes).to include(\n-              \'supplier_id\' => producer.id\n-            )\n-            expect(order_cycle.distributed_variants).to_not include product.variants.first\n+            it \'updates the product and keeps it in order cycles\' do\n+              expect {\n+                product_set.save\n+                product.reload\n+              }.to change { product.name }.to("New season product").\n+                and change { order_cycle.distributed_variants.count }.by(0)\n+\n+              expect(order_cycle.distributed_variants).to include product.variants.first\n+            end\n+          end\n+\n+          context \'and a different supplier is passed\' do\n+            let(:collection_hash) do\n+              { 0 => { id: product.id, supplier_id: producer.id } }\n+            end\n+\n+            it \'updates the product and removes the product from order cycles\' do\n+              expect {\n+                product_set.save\n+                product.reload\n+              }.to change { product.supplier }.to(producer).\n+                and change { order_cycle.distributed_variants.count }.by(-1)\n+\n+              expect(order_cycle.distributed_variants).to_not include product.variants.first\n+            end\n           end\n         end\n \n', 'commit_subject': 'Spec both cases of removing variants from order cycles', 'repo_name': 'openfoodfoundation/openfoodnetwork', 'committer': {'name': 'Konrad', 'email': '1d6e1cf70ec6f9ab28d3ea4b27a49a77654d370e@konradsachse.de', 'time_sec': '1665212618', 'tz_offset': '120', 'date': {'seconds': '1665212618'}}, 'commit_tree': 'fc7a2eae3fc546e15b17197f3e3991f2f7e4178c', 'commit_message': 'Spec both cases of removing variants from order cycles\n\nThe spec tested only the removal but not he keeping of variants.\nI want to change this in the next commit to simplify the class.\n', 'language_name': 'Ruby', 'license': 'agpl-3.0', 'hunks': '@@ -75,17 +75,9 @@ describe Sets::ProductSet do\n           end\n         end\n \n-        context \'when a different supplier is passed\' do\n-          let!(:producer) { create(:enterprise) }\n-          let!(:product) { create(:simple_product) }\n-          let(:collection_hash) do\n-            {\n-              0 => {\n-                id: product.id,\n-                supplier_id: producer.id\n-              }\n-            }\n-          end\n+        context "when the product is in an order cycle" do\n+          let(:producer) { create(:enterprise) }\n+          let(:product) { create(:simple_product) }\n \n           let(:distributor) { create(:distributor_enterprise) }\n           let!(:order_cycle) {\n@@ -94,13 +86,36 @@ describe Sets::ProductSet do\n                                         distributors: [distributor])\n           }\n \n-          it \'updates the product and removes the product from order cycles\' do\n-            product_set.save\n+          context \'and only the name changes\' do\n+            let(:collection_hash) do\n+              { 0 => { id: product.id, name: "New season product" } }\n+            end\n \n-            expect(product.reload.attributes).to include(\n-              \'supplier_id\' => producer.id\n-            )\n-            expect(order_cycle.distributed_variants).to_not include product.variants.first\n+            it \'updates the product and keeps it in order cycles\' do\n+              expect {\n+                product_set.save\n+                product.reload\n+              }.to change { product.name }.to("New season product").\n+                and change { order_cycle.distributed_variants.count }.by(0)\n+\n+              expect(order_cycle.distributed_variants).to include product.variants.first\n+            end\n+          end\n+\n+          context \'and a different supplier is passed\' do\n+            let(:collection_hash) do\n+              { 0 => { id: product.id, supplier_id: producer.id } }\n+            end\n+\n+            it \'updates the product and removes the product from order cycles\' do\n+              expect {\n+                product_set.save\n+                product.reload\n+              }.to change { product.supplier }.to(producer).\n+                and change { order_cycle.distributed_variants.count }.by(-1)\n+\n+              expect(order_cycle.distributed_variants).to_not include product.variants.first\n+            end\n           end\n         end\n \n', 'addition_count': 32, 'deletion_count': 17, 'src_file': 'a/spec/services/sets/product_set_spec.rb', 'tgt_file': 'b/spec/services/sets/product_set_spec.rb', 'file_extension': '.rb', 'before_file': '# frozen_string_literal: true\n\nrequire \'spec_helper\'\n\ndescribe Sets::ProductSet do\n  describe \'#save\' do\n    context \'when passing :collection_attributes\' do\n      let(:product_set) do\n        described_class.new(collection_attributes: collection_hash)\n      end\n\n      context \'when the product does not exist yet\' do\n        let!(:stock_location) { create(:stock_location, backorderable_default: false) }\n        let(:collection_hash) do\n          {\n            0 => {\n              name: \'a product\',\n              price: 2.0,\n              supplier_id: create(:enterprise).id,\n              primary_taxon_id: create(:taxon).id,\n              unit_description: \'description\',\n              variant_unit: \'items\',\n              variant_unit_name: \'bunches\',\n              shipping_category_id: create(:shipping_category).id\n            }\n          }\n        end\n\n        it \'does not create a new product\' do\n          product_set.save\n\n          expect(Spree::Product.last).to be nil\n        end\n      end\n\n      context \'when the product does exist\' do\n        context \'when a different variant_unit is passed\' do\n          let!(:product) do\n            create(\n              :simple_product,\n              variant_unit: \'items\',\n              variant_unit_scale: nil,\n              variant_unit_name: \'bunches\',\n              unit_value: nil,\n              unit_description: \'some description\'\n            )\n          end\n\n          let(:collection_hash) do\n            {\n              0 => {\n                id: product.id,\n                variant_unit: \'weight\',\n                variant_unit_scale: 1\n              }\n            }\n          end\n\n          it \'updates the product\' do\n            product_set.save\n\n            expect(product.reload.attributes).to include(\n              \'variant_unit\' => \'weight\'\n            )\n          end\n\n          it \'does not add an error\' do\n            product_set.save\n            expect(product_set.errors)\n              .to be_empty\n          end\n\n          it \'returns true\' do\n            expect(product_set.save).to eq(true)\n          end\n        end\n\n        context \'when a different supplier is passed\' do\n          let!(:producer) { create(:enterprise) }\n          let!(:product) { create(:simple_product) }\n          let(:collection_hash) do\n            {\n              0 => {\n                id: product.id,\n                supplier_id: producer.id\n              }\n            }\n          end\n\n          let(:distributor) { create(:distributor_enterprise) }\n          let!(:order_cycle) {\n            create(:simple_order_cycle, variants: [product.variants.first],\n                                        coordinator: distributor,\n                                        distributors: [distributor])\n          }\n\n          it \'updates the product and removes the product from order cycles\' do\n            product_set.save\n\n            expect(product.reload.attributes).to include(\n              \'supplier_id\' => producer.id\n            )\n            expect(order_cycle.distributed_variants).to_not include product.variants.first\n          end\n        end\n\n        context \'when attributes of the variants are passed\' do\n          let!(:product) { create(:simple_product) }\n          let(:collection_hash) { { 0 => { id: product.id } } }\n\n          context \'when :variants_attributes are passed\' do\n            let(:variants_attributes) { [{ sku: \'123\', id: product.variants.first.id.to_s }] }\n\n            before { collection_hash[0][:variants_attributes] = variants_attributes }\n\n            it \'updates the attributes of the variant\' do\n              product_set.save\n\n              expect(product.reload.variants.first[:sku]).to eq variants_attributes.first[:sku]\n            end\n\n            context \'and when product attributes are also passed\' do\n              it \'updates product and variant attributes\' do\n                collection_hash[0][:permalink] = "test_permalink"\n\n                product_set.save\n\n                expect(product.reload.variants.first[:sku]).to eq variants_attributes.first[:sku]\n                expect(product.reload.attributes).to include(\n                  \'permalink\' => "test_permalink"\n                )\n              end\n            end\n          end\n\n          context \'when :master_attributes is passed\' do\n            let(:master_attributes) { { sku: \'123\' } }\n\n            before do\n              collection_hash[0][:master_attributes] = master_attributes\n            end\n\n            context \'and the variant does exist\' do\n              let!(:variant) { create(:variant, product: product) }\n\n              before { master_attributes[:id] = variant.id }\n\n              it \'updates the attributes of the master variant\' do\n                product_set.save\n                expect(variant.reload.sku).to eq(\'123\')\n              end\n            end\n\n            context \'and the variant does not exist\' do\n              context \'and attributes provided are valid\' do\n                let(:master_attributes) do\n                  attributes_for(:variant).merge(sku: \'123\')\n                end\n\n                it \'creates it with the specified attributes\' do\n                  number_of_variants = Spree::Variant.all.size\n                  product_set.save\n                  expect(Spree::Variant.last.sku).to eq(\'123\')\n                  expect(Spree::Variant.all.size).to eq number_of_variants + 1\n                end\n              end\n\n              context \'and attributes provided are not valid\' do\n                let(:master_attributes) do\n                  # unit_value nil makes the variant invalid\n                  # on_hand with a value would make on_hand be updated and fail with exception\n                  attributes_for(:variant).merge(unit_value: nil, on_hand: 1, sku: \'321\')\n                end\n\n                it \'does not create variant and notifies bugsnag still raising the exception\' do\n                  number_of_variants = Spree::Variant.all.size\n                  expect(product_set.save).to eq(false)\n                  expect(Spree::Variant.all.size).to eq number_of_variants\n                  expect(Spree::Variant.last.sku).not_to eq(\'321\')\n                end\n              end\n            end\n          end\n        end\n      end\n    end\n  end\nend\n', 'message': 'Spec both cases of removing variants from order cycles\n\nThe spec tested only the removal but not he keeping of variants.\nI want to change this in the next commit to simplify the class.\n', 'author_name': 'Konrad', 'lang': 'rb'}