diff --git a/spec/components/bulk_upload_summary_component_spec.rb b/spec/components/bulk_upload_summary_component_spec.rb new file mode 100644 index 000000000..c10780bc1 --- /dev/null +++ b/spec/components/bulk_upload_summary_component_spec.rb @@ -0,0 +1,137 @@ +require "rails_helper" + +RSpec.describe BulkUploadSummaryComponent, type: :component do + let(:user) { create(:user) } + let(:support_user) { create(:user, :support) } + let(:bulk_upload) { create(:bulk_upload, :lettings, user:, year: 2024, total_logs_count: 10) } + + it "shows the file name" do + result = render_inline(described_class.new(bulk_upload:)) + expect(result).to have_content(bulk_upload.filename) + end + + it "shows the collection year" do + result = render_inline(described_class.new(bulk_upload:)) + expect(result).to have_content("2024/2025") + end + + it "includes a download file link" do + result = render_inline(described_class.new(bulk_upload:)) + expect(result).to have_link("Download file", href: "/lettings-logs/bulk-uploads/#{bulk_upload.id}/download") + end + + it "shows the total log count" do + result = render_inline(described_class.new(bulk_upload:)) + expect(result).to have_content("10 total logs") + end + + it "shows the uploaded by user" do + result = render_inline(described_class.new(bulk_upload:)) + expect(result).to have_content("Uploaded by: #{bulk_upload.user.name}") + end + + it "shows the uploading organisation" do + result = render_inline(described_class.new(bulk_upload:)) + expect(result).to have_content("Uploading organisation: #{bulk_upload.user.organisation.name}") + end + + it "shows the time of upload" do + result = render_inline(described_class.new(bulk_upload:)) + expect(result).to have_content("Time of upload: #{bulk_upload.created_at.to_formatted_s(:govuk_date_and_time)}") + end + + context "shows a bulk upload with only critical errors" do + let(:bulk_upload_errors) { create_list(:bulk_upload_error, 2, category: nil) } + let(:bulk_upload) { create(:bulk_upload, :lettings, user:, bulk_upload_errors:, total_logs_count: 10) } + + it "shows the critical errors status and error count" do + result = render_inline(described_class.new(bulk_upload:)) + expect(result).to have_content("Critical errors in CSV") + expect(result).to have_content("2 critical errors") + expect(result).to have_no_content("errors on important") + expect(result).to have_no_content("potential") + end + + it "includes a view error report link" do + result = render_inline(described_class.new(bulk_upload:)) + expect(result).to have_link("View error report", href: "/lettings-logs/bulk-upload-results/#{bulk_upload.id}") + end + end + + context "shows a bulk upload with only potential errors" do + let(:bulk_upload_errors) { create_list(:bulk_upload_error, 2, category: "soft_validation") } + let(:bulk_upload) { create(:bulk_upload, :lettings, user:, bulk_upload_errors:, total_logs_count: 16) } + + it "shows the potential errors status and error count" do + result = render_inline(described_class.new(bulk_upload:)) + expect(result).to have_content("Potential errors in CSV") + expect(result).to have_content("2 potential errors") + expect(result).to have_content("16 total logs") + expect(result).to have_no_content("errors on important") + expect(result).to have_no_content("critical") + end + end + + context "shows a bulk upload with only errors on important questions" do + let(:bulk_upload_errors) { create_list(:bulk_upload_error, 2, category: "setup") } + let(:bulk_upload) { create(:bulk_upload, :lettings, user:, bulk_upload_errors:, total_logs_count: 16) } + + it "shows the errors on important questions status and error count" do + result = render_inline(described_class.new(bulk_upload:)) + expect(result).to have_content("Errors on important questions in CSV") + expect(result).to have_content("2 errors on important questions") + expect(result).to have_content("16 total logs") + expect(result).to have_no_content("potential") + expect(result).to have_no_content("critical") + end + + it "includes a view error report link to the summary page" do + result = render_inline(described_class.new(bulk_upload:)) + expect(result).to have_link("View error report", href: %r{.*/lettings-logs/bulk-upload-results/#{bulk_upload.id}/summary}) + end + end + + context "shows a bulk upload uploaded with no errors" do + let(:bulk_upload) { create(:bulk_upload, :sales, user:, total_logs_count: 1) } + + it "shows the logs uploaded with no errors status and no error counts" do + result = render_inline(described_class.new(bulk_upload:)) + expect(result).to have_content("Logs uploaded with no errors") + expect(result).to have_content("1 total log") + expect(result).to have_no_content("important questions") + expect(result).to have_no_content("potential") + expect(result).to have_no_content("critical") + end + end + + context "shows a bulk upload uploaded with errors" do + let(:bulk_upload_errors) { create_list(:bulk_upload_error, 1) } + let(:bulk_upload) { create(:bulk_upload, :sales, user:, bulk_upload_errors:, total_logs_count: 21) } + + before do + create_list(:sales_log, 21, bulk_upload: bulk_upload) + end + + it "shows the logs upload with errors status and error count" do + result = render_inline(described_class.new(bulk_upload:)) + expect(result).to have_content("Logs uploaded with errors") + expect(result).to have_content("21 total logs") + expect(result).to have_content("1 critical error") + expect(result).to have_no_content("important questions") + expect(result).to have_no_content("potential") + end + end + + context "shows a bulk upload with a wrong template" do + let(:bulk_upload) { create(:bulk_upload, :sales, user:, total_logs_count: 1) } + + it "shows the wrong template status and no error counts" do + result = render_inline(described_class.new(bulk_upload:)) + expect(result).to have_content("Wrong template") + expect(result).to have_content("1 total log") + expect(result).to have_no_content("important questions") + expect(result).to have_no_content("potential") + expect(result).to have_no_content("critical") + end + end +end diff --git a/spec/components/create_log_actions_component_spec.rb b/spec/components/create_log_actions_component_spec.rb index b1caa1443..eda25b246 100644 --- a/spec/components/create_log_actions_component_spec.rb +++ b/spec/components/create_log_actions_component_spec.rb @@ -37,13 +37,23 @@ RSpec.describe CreateLogActionsComponent, type: :component do expect(component.create_button_href).to eq("/lettings-logs") end - it "returns upload button copy" do - expect(component.upload_button_copy).to eq("Upload lettings logs in bulk") + it "does not show the upload button" do + render_inline(component) + expect(rendered_content).not_to have_link("Upload lettings logs in bulk", href: "/lettings-logs/bulk-upload-logs/start") end - it "returns upload button href" do + it "returns view uploads button copy" do + expect(component.view_uploads_button_copy).to eq("View lettings bulk uploads") + end + + it "returns view uploads button href" do render - expect(component.upload_button_href).to eq("/lettings-logs/bulk-upload-logs/start") + expect(component.view_uploads_button_href).to eq("/lettings-logs/bulk-uploads") + end + + it "shows the view uploads button" do + render_inline(component) + expect(rendered_content).to have_link("View lettings bulk uploads", href: "/lettings-logs/bulk-uploads") end context "when sales log type" do @@ -61,6 +71,16 @@ RSpec.describe CreateLogActionsComponent, type: :component do render expect(component.create_button_href).to eq("/sales-logs") end + + it "does not show the upload button" do + render_inline(component) + expect(rendered_content).not_to have_link("Upload sales logs in bulk", href: "/sales-logs/bulk-upload-logs/start") + end + + it "shows the view uploads button" do + render_inline(component) + expect(rendered_content).to have_link("View sales bulk uploads", href: "/sales-logs/bulk-uploads") + end end end diff --git a/spec/factories/bulk_upload.rb b/spec/factories/bulk_upload.rb index cefe95c2b..5942762c3 100644 --- a/spec/factories/bulk_upload.rb +++ b/spec/factories/bulk_upload.rb @@ -10,6 +10,7 @@ FactoryBot.define do needstype { 1 } rent_type_fix_status { BulkUpload.rent_type_fix_statuses.values.sample } organisation_id { user.organisation_id } + total_logs_count { Faker::Number.number(digits: 2) } trait(:sales) do log_type { BulkUpload.log_types[:sales] } diff --git a/spec/factories/bulk_upload_error.rb b/spec/factories/bulk_upload_error.rb index bd2b9038c..6e2497972 100644 --- a/spec/factories/bulk_upload_error.rb +++ b/spec/factories/bulk_upload_error.rb @@ -11,5 +11,6 @@ FactoryBot.define do purchaser_code { SecureRandom.hex(4) } field { "field_#{rand(1..134)}" } error { "some error" } + category { nil } end end diff --git a/spec/features/lettings_log_spec.rb b/spec/features/lettings_log_spec.rb index ac9a1e4a8..1cf425f9c 100644 --- a/spec/features/lettings_log_spec.rb +++ b/spec/features/lettings_log_spec.rb @@ -408,6 +408,37 @@ RSpec.describe "Lettings Log Features" do expect(deleted_log.status).to eq "deleted" expect(deleted_log.discarded_at).not_to be nil end + + context "when visiting the bulk uploads page" do + let(:bulk_upload_errors) { create_list(:bulk_upload_error, 2, category: nil) } + let(:bulk_upload) { create(:bulk_upload, :lettings, user: support_user, bulk_upload_errors:, total_logs_count: 10) } + let(:mock_storage_service) { instance_double("S3Service") } + + before do + allow(Storage::S3Service).to receive(:new).and_return(mock_storage_service) + allow(mock_storage_service).to receive(:get_presigned_url).with(bulk_upload.identifier, 60, response_content_disposition: "attachment; filename=#{bulk_upload.filename}").and_return("/lettings-logs/bulk-uploads") + bulk_upload + visit("/lettings-logs/bulk-uploads") + end + + it "displays the right title" do + expect(page).to have_content("Lettings bulk uploads") + end + + it "shows the bulk upload file name" do + expect(page).to have_content(bulk_upload.filename) + end + + it "redirects to the error report page when clicking 'View error report'" do + click_link("View error report") + expect(page).to have_current_path("/lettings-logs/bulk-upload-results/#{bulk_upload.id}") + end + + it "allows the user to download the file" do + click_link("Download file", href: "/lettings-logs/bulk-uploads/#{bulk_upload.id}/download") + expect(page).to have_current_path("/lettings-logs/bulk-uploads") + end + end end context "when the signed is user is not a Support user" do diff --git a/spec/features/sales_log_spec.rb b/spec/features/sales_log_spec.rb index 779d978bf..8e4ffba42 100644 --- a/spec/features/sales_log_spec.rb +++ b/spec/features/sales_log_spec.rb @@ -279,6 +279,37 @@ RSpec.describe "Sales Log Features" do expect(breadcrumbs[2][:href]).to eq sales_log_path(sales_log.id) end end + + context "when visiting the bulk uploads page" do + let(:bulk_upload_errors) { create_list(:bulk_upload_error, 2, category: nil) } + let(:bulk_upload) { create(:bulk_upload, :sales, user:, bulk_upload_errors:, total_logs_count: 10) } + let(:mock_storage_service) { instance_double("S3Service") } + + before do + allow(Storage::S3Service).to receive(:new).and_return(mock_storage_service) + allow(mock_storage_service).to receive(:get_presigned_url).with(bulk_upload.identifier, 60, response_content_disposition: "attachment; filename=#{bulk_upload.filename}").and_return("/sales-logs/bulk-uploads") + bulk_upload + visit("/sales-logs/bulk-uploads") + end + + it "displays the right title" do + expect(page).to have_content("Sales bulk uploads") + end + + it "shows the bulk upload file name" do + expect(page).to have_content(bulk_upload.filename) + end + + it "redirects to the error report page when clicking 'View error report'" do + click_link("View error report") + expect(page).to have_current_path("/sales-logs/bulk-upload-results/#{bulk_upload.id}") + end + + it "allows the user to download the file" do + click_link("Download file", href: "/sales-logs/bulk-uploads/#{bulk_upload.id}/download") + expect(page).to have_current_path("/sales-logs/bulk-uploads") + end + end end context "when a log becomes a duplicate" do diff --git a/spec/models/bulk_upload_spec.rb b/spec/models/bulk_upload_spec.rb index e38ea0402..d39f93391 100644 --- a/spec/models/bulk_upload_spec.rb +++ b/spec/models/bulk_upload_spec.rb @@ -51,4 +51,204 @@ RSpec.describe BulkUpload, type: :model do end end end + + describe "scopes" do + let!(:lettings_bulk_upload_1) { create(:bulk_upload, log_type: "lettings") } + let!(:lettings_bulk_upload_2) { create(:bulk_upload, log_type: "lettings") } + let!(:sales_bulk_upload_1) { create(:bulk_upload, log_type: "sales") } + let!(:sales_bulk_upload_2) { create(:bulk_upload, log_type: "sales") } + + describe ".lettings" do + it "returns only lettings bulk uploads" do + expect(BulkUpload.lettings).to match_array([lettings_bulk_upload_1, lettings_bulk_upload_2]) + end + end + + describe ".sales" do + it "returns only sales bulk uploads" do + expect(BulkUpload.sales).to match_array([sales_bulk_upload_1, sales_bulk_upload_2]) + end + end + + describe ".search_by_filename" do + it "returns the correct bulk upload" do + expect(BulkUpload.search_by_filename(lettings_bulk_upload_1.filename).first).to eql(lettings_bulk_upload_1) + end + + it "does not return the incorrect bulk upload" do + expect(BulkUpload.search_by_filename(lettings_bulk_upload_1.filename).first).not_to eql(lettings_bulk_upload_2) + end + end + + describe ".search_by_user_name" do + it "returns the correct bulk upload" do + expect(BulkUpload.search_by_user_name(lettings_bulk_upload_1.user.name).first).to eql(lettings_bulk_upload_1) + end + + it "does not return the incorrect bulk upload" do + expect(BulkUpload.search_by_user_name(lettings_bulk_upload_1.user.name).first).not_to eql(lettings_bulk_upload_2) + end + end + + describe ".search_by_user_email" do + it "returns the correct bulk upload" do + expect(BulkUpload.search_by_user_email(sales_bulk_upload_1.user.email).first).to eql(sales_bulk_upload_1) + end + + it "does not return the incorrect bulk upload" do + expect(BulkUpload.search_by_user_email(sales_bulk_upload_1.user.email).first).not_to eql(sales_bulk_upload_2) + end + end + + describe ".search_by_organisation_name" do + it "returns the correct bulk upload" do + expect(BulkUpload.search_by_organisation_name(lettings_bulk_upload_1.user.organisation.name).first).to eql(lettings_bulk_upload_1) + end + + it "does not return the incorrect bulk upload" do + expect(BulkUpload.search_by_organisation_name(lettings_bulk_upload_1.user.organisation.name).first).not_to eql(lettings_bulk_upload_2) + end + end + + describe ".filter_by_id" do + it "returns the correct bulk upload" do + expect(BulkUpload.filter_by_id(lettings_bulk_upload_1.id).first).to eql(lettings_bulk_upload_1) + end + + it "does not return the incorrect bulk upload" do + expect(BulkUpload.filter_by_id(lettings_bulk_upload_1.id).first).not_to eql(lettings_bulk_upload_2) + end + end + + describe ".filter_by_years" do + it "returns the correct bulk upload" do + expect(BulkUpload.filter_by_years([lettings_bulk_upload_1.year]).first).to eql(lettings_bulk_upload_1) + end + + it "does not return the incorrect bulk upload" do + expect(BulkUpload.filter_by_years([lettings_bulk_upload_1.year]).first).not_to eql(lettings_bulk_upload_2) + end + end + + describe ".filter_by_uploaded_by" do + it "returns the correct bulk upload" do + expect(BulkUpload.filter_by_uploaded_by(sales_bulk_upload_1.user.id).first).to eql(sales_bulk_upload_1) + end + + it "does not return the incorrect bulk upload" do + expect(BulkUpload.filter_by_uploaded_by(sales_bulk_upload_1.user.id).first).not_to eql(sales_bulk_upload_2) + end + end + + describe ".filter_by_user_text_search" do + it "returns the correct bulk upload" do + expect(BulkUpload.filter_by_user_text_search(lettings_bulk_upload_1.user.name).first).to eql(lettings_bulk_upload_1) + end + + it "does not return the incorrect bulk upload" do + expect(BulkUpload.filter_by_user_text_search(lettings_bulk_upload_1.user.name).first).not_to eql(lettings_bulk_upload_2) + end + end + + describe ".filter_by_user" do + it "returns the correct bulk upload" do + expect(BulkUpload.filter_by_user(sales_bulk_upload_1.user.id).first).to eql(sales_bulk_upload_1) + end + + it "does not return the incorrect bulk upload" do + expect(BulkUpload.filter_by_user(sales_bulk_upload_1.user.id).first).not_to eql(sales_bulk_upload_2) + end + end + + describe ".filter_by_uploading_organisation" do + it "returns the correct bulk upload" do + expect(BulkUpload.filter_by_uploading_organisation(lettings_bulk_upload_1.user.organisation.id).first).to eql(lettings_bulk_upload_1) + end + + it "does not return the incorrect bulk upload" do + expect(BulkUpload.filter_by_uploading_organisation(lettings_bulk_upload_1.user.organisation.id).first).not_to eql(lettings_bulk_upload_2) + end + end + end + + describe "#status" do + context "when the bulk upload was uploaded with a blank template" do + let(:bulk_upload) { create(:bulk_upload, failed: 1) } + + it "returns the correct status" do + expect(bulk_upload.status).to eql(:blank_template) + end + end + + context "when the bulk upload was uploaded with the wrong template" do + let(:bulk_upload) { create(:bulk_upload, failed: 2) } + + it "returns the correct status" do + expect(bulk_upload.status).to eql(:wrong_template) + end + end + + context "when the bulk upload is processing" do + let(:bulk_upload) { create(:bulk_upload, processing: true) } + + it "returns the correct status" do + expect(bulk_upload.status).to eql(:processing) + end + end + + context "when the bulk upload has potential errors" do + let(:bulk_upload_errors) { create_list(:bulk_upload_error, 2, category: "soft_validation") } + let(:bulk_upload) { create(:bulk_upload, bulk_upload_errors:) } + + it "returns the correct status" do + expect(bulk_upload.status).to eql(:potential_errors) + end + end + + context "when the bulk upload has critical errors" do + let(:bulk_upload_errors) { create_list(:bulk_upload_error, 2, category: nil) } + let(:bulk_upload) { create(:bulk_upload, bulk_upload_errors:) } + + it "returns the correct status" do + expect(bulk_upload.status).to eql(:critical_errors) + end + end + + context "when the bulk upload has important errors" do + let(:bulk_upload_errors) { create_list(:bulk_upload_error, 2, category: "setup") } + let(:bulk_upload) { create(:bulk_upload, bulk_upload_errors:) } + + it "returns the correct status" do + expect(bulk_upload.status).to eql(:important_errors) + end + end + + context "when the bulk upload has no errors" do + let(:bulk_upload) { create(:bulk_upload) } + + it "returns the correct status" do + expect(bulk_upload.status).to eql(:logs_uploaded_no_errors) + end + end + + context "when the bulk upload has visible logs, errors and is not complete" do + let(:bulk_upload_errors) { create_list(:bulk_upload_error, 2, category: "soft_validation") } + let(:bulk_upload) { create(:bulk_upload, :lettings, bulk_upload_errors:) } + let!(:log) { create(:lettings_log, :in_progress, bulk_upload:) } + + it "returns logs_uploaded_with_errors" do + expect(bulk_upload.status).to eql(:logs_uploaded_with_errors) + end + end + + context "when the bulk upload has visible logs, errors and is complete" do + let(:bulk_upload_errors) { create_list(:bulk_upload_error, 2, category: "soft_validation") } + let(:bulk_upload) { create(:bulk_upload, :lettings, bulk_upload_errors:) } + let!(:log) { create(:lettings_log, :completed, bulk_upload:) } + + it "returns errors_fixed_in_service" do + expect(bulk_upload.status).to eql(:errors_fixed_in_service) + end + end + end end diff --git a/spec/services/bulk_upload/downloader_spec.rb b/spec/services/bulk_upload/downloader_spec.rb index 48b046d8e..bae90aede 100644 --- a/spec/services/bulk_upload/downloader_spec.rb +++ b/spec/services/bulk_upload/downloader_spec.rb @@ -45,4 +45,16 @@ RSpec.describe BulkUpload::Downloader do expect(File).not_to exist(path) end end + + describe "#presigned_url" do + let(:mock_storage_service) { instance_double(Storage::S3Service, get_presigned_url: "https://example.com") } + + before do + allow(Storage::S3Service).to receive(:new).and_return(mock_storage_service) + end + + it "returns a presigned URL" do + expect(downloader.presigned_url).to eql("https://example.com") + end + end end diff --git a/spec/services/bulk_upload/processor_spec.rb b/spec/services/bulk_upload/processor_spec.rb index 08fe7d705..de0ed2dba 100644 --- a/spec/services/bulk_upload/processor_spec.rb +++ b/spec/services/bulk_upload/processor_spec.rb @@ -43,6 +43,13 @@ RSpec.describe BulkUpload::Processor do end describe "#call" do + it "changes processing from true to false" do + bulk_upload.update!(processing: true) + expect { + processor.call + }.to change { bulk_upload.reload.processing }.from(true).to(false) + end + context "when errors exist from prior job run" do let!(:existing_error) { create(:bulk_upload_error, bulk_upload:) }