From 0fbadf98191ed299d3fce2cfb2a3bb319b5c08d6 Mon Sep 17 00:00:00 2001 From: Laura Ghiorghisor Date: Thu, 5 Dec 2024 13:41:46 +0000 Subject: [PATCH] exceptions wip --- lib/tasks/assets.rake | 15 +- spec/lib/tasks/assets_spec.rb | 340 ++++++++++++++++++++-------------- 2 files changed, 208 insertions(+), 147 deletions(-) diff --git a/lib/tasks/assets.rake b/lib/tasks/assets.rake index c363a155..2a022e13 100644 --- a/lib/tasks/assets.rake +++ b/lib/tasks/assets.rake @@ -80,8 +80,9 @@ namespace :assets do delete_and_update_draft(replacement_asset) processed_asset_ids[replacement_asset.id.to_s] = true puts "Asset ID: #{original_asset_id} - OK. Draft replacement #{replacement_asset.id} deleted and updated to false." - rescue StandardError - puts "Asset ID: #{original_asset_id} - ERROR. Asset replacement failed to save. Error: #{replacement_asset.errors.full_messages}." + rescue StandardError => e + message = replacement_asset.errors.full_messages.empty? ? e.message : replacement_asset.errors.full_messages + puts "Asset ID: #{original_asset_id} - ERROR. Asset replacement #{replacement_asset.id} failed to save. Error: #{message}." end next end @@ -91,8 +92,9 @@ namespace :assets do delete_and_update_draft(original_asset) processed_asset_ids[original_asset_id] = true puts "Asset ID: #{original_asset_id} - OK. Asset is a replacement. Asset deleted and updated to false." - rescue StandardError - puts "Asset ID: #{original_asset_id} - ERROR. Asset failed to save. Error: #{original_asset.errors.full_messages}." + rescue StandardError => e + message = original_asset.errors.full_messages.empty? ? e.message : original_asset.errors.full_messages + puts "Asset ID: #{original_asset_id} - ERROR. Asset failed to save. Error: #{message}." end next end @@ -111,8 +113,9 @@ namespace :assets do delete_and_update_draft(original_asset, should_update_draft: false) processed_asset_ids[original_asset_id] = true puts "Asset ID: #{original_asset_id} - OK. Asset has been deleted." - rescue StandardError - puts "Asset ID: #{original_asset_id} - ERROR. Asset failed to save. Error: #{original_asset.errors.full_messages}." + rescue StandardError => e + message = original_asset.errors.full_messages.empty? ? e : original_asset.errors.full_messages + puts "Asset ID: #{original_asset_id} - ERROR. Asset failed to save. Error: #{message}." end end end diff --git a/spec/lib/tasks/assets_spec.rb b/spec/lib/tasks/assets_spec.rb index a49b403e..a0cb86b6 100644 --- a/spec/lib/tasks/assets_spec.rb +++ b/spec/lib/tasks/assets_spec.rb @@ -48,14 +48,35 @@ describe "assets:bulk_fix:fix_assets_and_draft_replacements" do let(:task) { Rake::Task["assets:bulk_fix:fix_assets_and_draft_replacements"] } - it "skips the asset if asset is not found" do - expected_output = <<~OUTPUT - Asset ID: #{asset_id} - SKIPPED. No asset found. - OUTPUT - expect { task.invoke(filepath) }.to output(expected_output).to_stdout - end - - it "skips already processed replacement assets" do + describe "Skip processing" do + it "skips the asset if asset is not found" do + expected_output = <<~OUTPUT + Asset ID: #{asset_id} - SKIPPED. No asset found. + OUTPUT + expect { task.invoke(filepath) }.to output(expected_output).to_stdout + end + + it "skips already processed replacement assets" do + file.open + csv_file = <<~CSV + 6592008029c8c3e4dc76256c + 6592008029c8c3e4dc76256d + CSV + file.write(csv_file) + file.close + + replacement = FactoryBot.create(:asset, draft: true, deleted_at: nil, replacement_id: nil) + FactoryBot.create(:asset, id: "6592008029c8c3e4dc76256c", replacement_id: replacement.id) + FactoryBot.create(:asset, id: "6592008029c8c3e4dc76256d", replacement_id: replacement.id) + + expected_output = <<~OUTPUT + Asset ID: 6592008029c8c3e4dc76256c - OK. Draft replacement #{replacement.id} deleted and updated to false. + Asset ID: 6592008029c8c3e4dc76256d - PROCESSED. Replacement #{replacement.id} already processed. + OUTPUT + expect { task.invoke(filepath) }.to output(expected_output).to_stdout + end + + it "skips already processed assets" do file.open csv_file = <<~CSV 6592008029c8c3e4dc76256c @@ -64,34 +85,185 @@ file.write(csv_file) file.close - replacement = FactoryBot.create(:asset, draft: true, deleted_at: nil, replacement_id: nil) + replacement = FactoryBot.create(:asset, id: "6592008029c8c3e4dc76256d", draft: true, deleted_at: nil, replacement_id: nil) FactoryBot.create(:asset, id: "6592008029c8c3e4dc76256c", replacement_id: replacement.id) - FactoryBot.create(:asset, id: "6592008029c8c3e4dc76256d", replacement_id: replacement.id) expected_output = <<~OUTPUT - Asset ID: 6592008029c8c3e4dc76256c - OK. Draft replacement #{replacement.id} deleted and updated to false. - Asset ID: 6592008029c8c3e4dc76256d - PROCESSED. Replacement #{replacement.id} already processed. - OUTPUT + Asset ID: 6592008029c8c3e4dc76256c - OK. Draft replacement #{replacement.id} deleted and updated to false. + Asset ID: 6592008029c8c3e4dc76256d - PROCESSED. Asset already processed. + OUTPUT expect { task.invoke(filepath) }.to output(expected_output).to_stdout + end + + it "skips processing a line if it is marked as DONE" do + file.open + csv_file = <<~CSV + 6592008029c8c3e4dc76256c,DONE + 6592008029c8c3e4dc76256d + CSV + file.write(csv_file) + file.close + + skipped_asset = FactoryBot.create(:asset, id: "6592008029c8c3e4dc76256c", draft: true, deleted_at: nil) + asset = FactoryBot.create(:asset, id: "6592008029c8c3e4dc76256d", draft: false, deleted_at: nil) + + expected_output = <<~OUTPUT + Asset ID: 6592008029c8c3e4dc76256d - OK. Asset has been deleted. + OUTPUT + expect { task.invoke(filepath) }.to output(expected_output).to_stdout + expect(skipped_asset.reload.deleted_at).to be_nil + expect(skipped_asset.reload.draft).to be true + expect(asset.reload.deleted_at).not_to be_nil + expect(asset.reload.draft).to be false + expect(File.read(filepath)).to eq "6592008029c8c3e4dc76256c,DONE\n6592008029c8c3e4dc76256d,DONE\n" + end + + it "skips the asset if asset is itself redirected (and not replaced by draft or itself a replacement)" do + FactoryBot.create(:asset, id: asset_id, draft: false, redirect_url: "https://example.com") + + expected_output = <<~OUTPUT + Asset ID: #{asset_id} - SKIPPED. Asset is draft (false), deleted (false), replaced (false), or redirected (true). + OUTPUT + expect { task.invoke(filepath) }.to output(expected_output).to_stdout + end + + it "skips the asset, if the asset is further replaced (and not replaced by draft or itself a replacement)" do + replacement = FactoryBot.create(:asset, draft: false) + FactoryBot.create(:asset, id: asset_id, replacement_id: replacement.id) + + expected_output = <<~OUTPUT + Asset ID: #{asset_id} - SKIPPED. Asset is draft (false), deleted (false), replaced (true), or redirected (false). + OUTPUT + expect { task.invoke(filepath) }.to output(expected_output).to_stdout + end + + it "skips the asset if asset is itself in draft (and not replaced by draft or itself a replacement)" do + FactoryBot.create(:asset, id: asset_id, draft: true) + + expected_output = <<~OUTPUT + Asset ID: #{asset_id} - SKIPPED. Asset is draft (true), deleted (false), replaced (false), or redirected (false). + OUTPUT + expect { task.invoke(filepath) }.to output(expected_output).to_stdout + end + + it "skips the asset, if the asset is already deleted (and is not itself a replacement)" do + FactoryBot.create(:asset, id: asset_id, deleted_at: Time.zone.now) + + expected_output = <<~OUTPUT + Asset ID: #{asset_id} - SKIPPED. Asset is draft (false), deleted (true), replaced (false), or redirected (false). + OUTPUT + expect { task.invoke(filepath) }.to output(expected_output).to_stdout + end end - it "skips already processed assets" do - file.open - csv_file = <<~CSV - 6592008029c8c3e4dc76256c - 6592008029c8c3e4dc76256d - CSV - file.write(csv_file) - file.close + describe "error scenarios" do + it "rescues and logs if asset errors when saving" do + asset = FactoryBot.create(:asset, id: asset_id, draft: true, deleted_at: nil) + FactoryBot.create(:asset, replacement_id: asset.id) - replacement = FactoryBot.create(:asset, id: "6592008029c8c3e4dc76256d", draft: true, deleted_at: nil, replacement_id: nil) - FactoryBot.create(:asset, id: "6592008029c8c3e4dc76256c", replacement_id: replacement.id) + allow_any_instance_of(Asset).to receive(:save!).and_raise("failure!!") - expected_output = <<~OUTPUT - Asset ID: 6592008029c8c3e4dc76256c - OK. Draft replacement #{replacement.id} deleted and updated to false. - Asset ID: 6592008029c8c3e4dc76256d - PROCESSED. Asset already processed. + expected_output = <<~OUTPUT + Asset ID: #{asset_id} - ERROR. Asset failed to save. Error: failure!!. OUTPUT - expect { task.invoke(filepath) }.to output(expected_output).to_stdout + expect { task.invoke(filepath) }.to output(expected_output).to_stdout + expect(asset.reload.draft).to be true + expect(asset.reload.deleted_at).not_to be_nil + end + + it "rescues and logs if asset errors when destroy" do + asset = FactoryBot.create(:asset, id: asset_id, draft: false, deleted_at: nil, replacement_id: nil, redirect_url: nil) + + allow_any_instance_of(Asset).to receive(:destroy!).and_raise("failure!!") + + expected_output = <<~OUTPUT + Asset ID: #{asset_id} - ERROR. Asset failed to save. Error: failure!!. + OUTPUT + expect { task.invoke(filepath) }.to output(expected_output).to_stdout + expect(asset.reload.deleted_at).to be_nil + end + + it "rescues and logs if asset replacement errors when saving" do + replacement = FactoryBot.create(:asset, draft: true, deleted_at: nil) + FactoryBot.create(:asset, id: asset_id, replacement_id: replacement.id) + + allow_any_instance_of(Asset).to receive(:save!).and_raise("failure!!") + + expected_output = <<~OUTPUT + Asset ID: #{asset_id} - ERROR. Asset replacement #{replacement.id} failed to save. Error: failure!!. + OUTPUT + expect { task.invoke(filepath) }.to output(expected_output).to_stdout + expect(replacement.reload.draft).to be true + expect(replacement.reload.deleted_at).not_to be_nil + end + + it "rescues and logs if asset replacement errors when destroying" do + replacement = FactoryBot.create(:asset, draft: true, deleted_at: nil) + FactoryBot.create(:asset, id: asset_id, replacement_id: replacement.id) + + allow_any_instance_of(Asset).to receive(:destroy!).and_raise("failure!!") + + expected_output = <<~OUTPUT + Asset ID: #{asset_id} - ERROR. Asset replacement #{replacement.id} failed to save. Error: failure!!. + OUTPUT + expect { task.invoke(filepath) }.to output(expected_output).to_stdout + expect(replacement.reload.draft).to be true + expect(replacement.reload.deleted_at).to be_nil + end + + it "rescues and logs if asset fails validation when saving" do + asset = FactoryBot.create(:asset, id: asset_id, draft: true, deleted_at: nil) + FactoryBot.create(:asset, replacement_id: asset.id) + asset.state = "invalid" + asset.save(validate: false) + + expected_output = <<~OUTPUT + Asset ID: #{asset_id} - ERROR. Asset failed to save. Error: ["State is invalid"]. + OUTPUT + expect { task.invoke(filepath) }.to output(expected_output).to_stdout + expect(asset.reload.draft).to be true + expect(asset.reload.deleted_at).to be_nil + end + + it "rescues and logs if asset fails validation when destroy" do + asset = FactoryBot.create(:asset, id: asset_id, draft: false, deleted_at: nil, replacement_id: nil, redirect_url: nil) + asset.state = "invalid" + asset.save(validate: false) + + expected_output = <<~OUTPUT + Asset ID: #{asset_id} - ERROR. Asset failed to save. Error: ["State is invalid"]. + OUTPUT + expect { task.invoke(filepath) }.to output(expected_output).to_stdout + expect(asset.reload.deleted_at).to be_nil + end + + it "rescues and logs if asset replacement fails validation when saving" do + replacement = FactoryBot.create(:asset, draft: true, deleted_at: nil) + FactoryBot.create(:asset, id: asset_id, replacement_id: replacement.id) + replacement.state = "invalid" + replacement.save(validate: false) + + expected_output = <<~OUTPUT + Asset ID: #{asset_id} - ERROR. Asset replacement #{replacement.id} failed to save. Error: ["State is invalid"]. + OUTPUT + expect { task.invoke(filepath) }.to output(expected_output).to_stdout + expect(replacement.reload.draft).to be true + expect(replacement.reload.deleted_at).to be_nil + end + + it "rescues and logs if asset replacement fails validation when destroying" do + replacement = FactoryBot.create(:asset, draft: true, deleted_at: nil) + FactoryBot.create(:asset, id: asset_id, replacement_id: replacement.id) + replacement.state = "invalid" + replacement.save(validate: false) + + expected_output = <<~OUTPUT + Asset ID: #{asset_id} - ERROR. Asset replacement #{replacement.id} failed to save. Error: ["State is invalid"]. + OUTPUT + expect { task.invoke(filepath) }.to output(expected_output).to_stdout + expect(replacement.reload.draft).to be true + expect(replacement.reload.deleted_at).to be_nil + end end it "only updates the draft state of the replacement if the asset replacement is already deleted" do @@ -203,43 +375,6 @@ expect(asset.reload.state).to eq "uploaded" end - it "skips the asset if asset is itself redirected (and not replaced by draft or itself a replacement)" do - FactoryBot.create(:asset, id: asset_id, draft: false, redirect_url: "https://example.com") - - expected_output = <<~OUTPUT - Asset ID: #{asset_id} - SKIPPED. Asset is draft (false), deleted (false), replaced (false), or redirected (true). - OUTPUT - expect { task.invoke(filepath) }.to output(expected_output).to_stdout - end - - it "skips the asset, if the asset is further replaced (and not replaced by draft or itself a replacement)" do - replacement = FactoryBot.create(:asset, draft: false) - FactoryBot.create(:asset, id: asset_id, replacement_id: replacement.id) - - expected_output = <<~OUTPUT - Asset ID: #{asset_id} - SKIPPED. Asset is draft (false), deleted (false), replaced (true), or redirected (false). - OUTPUT - expect { task.invoke(filepath) }.to output(expected_output).to_stdout - end - - it "skips the asset if asset is itself in draft (and not replaced by draft or itself a replacement)" do - FactoryBot.create(:asset, id: asset_id, draft: true) - - expected_output = <<~OUTPUT - Asset ID: #{asset_id} - SKIPPED. Asset is draft (true), deleted (false), replaced (false), or redirected (false). - OUTPUT - expect { task.invoke(filepath) }.to output(expected_output).to_stdout - end - - it "skips the asset, if the asset is already deleted (and is not itself a replacement)" do - FactoryBot.create(:asset, id: asset_id, deleted_at: Time.zone.now) - - expected_output = <<~OUTPUT - Asset ID: #{asset_id} - SKIPPED. Asset is draft (false), deleted (true), replaced (false), or redirected (false). - OUTPUT - expect { task.invoke(filepath) }.to output(expected_output).to_stdout - end - it "catch-all case: it deletes the asset" do FactoryBot.create(:asset, id: asset_id, draft: false, deleted_at: nil, replacement_id: nil, redirect_url: nil) @@ -249,88 +384,11 @@ expect { task.invoke(filepath) }.to output(expected_output).to_stdout end - it "rescues and logs if asset fails to save" do - asset = FactoryBot.create(:asset, id: asset_id, draft: true, deleted_at: nil) - FactoryBot.create(:asset, replacement_id: asset.id) - - allow_any_instance_of(Asset).to receive(:save!).and_raise(StandardError) - - expected_output = <<~OUTPUT - Asset ID: #{asset_id} - ERROR. Asset failed to save. Error: #{asset.errors.full_messages}. - OUTPUT - expect { task.invoke(filepath) }.to output(expected_output).to_stdout - expect(asset.reload.draft).to be true - expect(asset.reload.deleted_at).not_to be_nil - end - - it "rescues and logs if asset fails to destroy" do - asset = FactoryBot.create(:asset, id: asset_id, draft: false, deleted_at: nil, replacement_id: nil, redirect_url: nil) - - allow_any_instance_of(Asset).to receive(:destroy!).and_raise(StandardError) - - expected_output = <<~OUTPUT - Asset ID: #{asset_id} - ERROR. Asset failed to save. Error: #{asset.errors.full_messages}. - OUTPUT - expect { task.invoke(filepath) }.to output(expected_output).to_stdout - expect(asset.reload.deleted_at).to be_nil - end - - it "rescues and logs if asset replacement fails to save" do - replacement = FactoryBot.create(:asset, draft: true, deleted_at: nil) - FactoryBot.create(:asset, id: asset_id, replacement_id: replacement.id) - - allow_any_instance_of(Asset).to receive(:save!).and_raise(StandardError) - - expected_output = <<~OUTPUT - Asset ID: #{asset_id} - ERROR. Asset replacement failed to save. Error: #{replacement.errors.full_messages}. - OUTPUT - expect { task.invoke(filepath) }.to output(expected_output).to_stdout - expect(replacement.reload.draft).to be true - expect(replacement.reload.deleted_at).not_to be_nil - end - - it "rescues and logs if asset replacement fails to destroy" do - replacement = FactoryBot.create(:asset, draft: true, deleted_at: nil) - FactoryBot.create(:asset, id: asset_id, replacement_id: replacement.id) - - allow_any_instance_of(Asset).to receive(:destroy!).and_raise(StandardError) - - expected_output = <<~OUTPUT - Asset ID: #{asset_id} - ERROR. Asset replacement failed to save. Error: #{replacement.errors.full_messages}. - OUTPUT - expect { task.invoke(filepath) }.to output(expected_output).to_stdout - expect(replacement.reload.draft).to be true - expect(replacement.reload.deleted_at).to be_nil - end - it "marks a line as 'done' in the CSV if line is processed" do FactoryBot.create(:asset, id: asset_id, draft: true, deleted_at: nil) task.invoke(filepath) expect(File.read(filepath)).to eq "6592008029c8c3e4dc76256c,DONE\n" end - - it "skips processing a line if it is marked as DONE" do - file.open - csv_file = <<~CSV - 6592008029c8c3e4dc76256c,DONE - 6592008029c8c3e4dc76256d - CSV - file.write(csv_file) - file.close - - skipped_asset = FactoryBot.create(:asset, id: "6592008029c8c3e4dc76256c", draft: true, deleted_at: nil) - asset = FactoryBot.create(:asset, id: "6592008029c8c3e4dc76256d", draft: false, deleted_at: nil) - - expected_output = <<~OUTPUT - Asset ID: 6592008029c8c3e4dc76256d - OK. Asset has been deleted. - OUTPUT - expect { task.invoke(filepath) }.to output(expected_output).to_stdout - expect(skipped_asset.reload.deleted_at).to be_nil - expect(skipped_asset.reload.draft).to be true - expect(asset.reload.deleted_at).not_to be_nil - expect(asset.reload.draft).to be false - expect(File.read(filepath)).to eq "6592008029c8c3e4dc76256c,DONE\n6592008029c8c3e4dc76256d,DONE\n" - end end end # rubocop:enable RSpec/AnyInstance