dawarich/spec/jobs/imports/destroy_job_spec.rb
2025-12-27 13:32:33 +01:00

192 lines
5.9 KiB
Ruby

# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Imports::DestroyJob, type: :job do
describe '#perform' do
let(:user) { create(:user) }
let(:import) { create(:import, user: user, status: :completed) }
describe 'queue configuration' do
it 'uses the default queue' do
expect(described_class.queue_name).to eq('default')
end
end
context 'when import exists' do
before do
create_list(:point, 3, user: user, import: import)
end
it 'changes import status to deleting and deletes it' do
expect(import).not_to be_deleting
import_id = import.id
described_class.perform_now(import_id)
expect(Import.find_by(id: import_id)).to be_nil
end
it 'calls the Imports::Destroy service' do
destroy_service = instance_double(Imports::Destroy)
allow(Imports::Destroy).to receive(:new).with(user, import).and_return(destroy_service)
allow(destroy_service).to receive(:call)
described_class.perform_now(import.id)
expect(Imports::Destroy).to have_received(:new).with(user, import)
expect(destroy_service).to have_received(:call)
end
it 'broadcasts status update to the user' do
allow(ImportsChannel).to receive(:broadcast_to)
described_class.perform_now(import.id)
expect(ImportsChannel).to have_received(:broadcast_to).with(
user,
hash_including(
action: 'status_update',
import: hash_including(
id: import.id,
status: 'deleting'
)
)
).at_least(:once)
end
it 'broadcasts deletion complete to the user' do
allow(ImportsChannel).to receive(:broadcast_to)
described_class.perform_now(import.id)
expect(ImportsChannel).to have_received(:broadcast_to).with(
user,
hash_including(
action: 'delete',
import: hash_including(id: import.id)
)
).at_least(:once)
end
it 'broadcasts both status update and deletion messages' do
allow(ImportsChannel).to receive(:broadcast_to)
described_class.perform_now(import.id)
expect(ImportsChannel).to have_received(:broadcast_to).twice
end
it 'deletes the import and its points' do
import_id = import.id
point_ids = import.points.pluck(:id)
described_class.perform_now(import_id)
expect(Import.find_by(id: import_id)).to be_nil
expect(Point.where(id: point_ids)).to be_empty
end
end
context 'when import does not exist' do
let(:non_existent_id) { 999_999 }
it 'does not raise an error' do
expect { described_class.perform_now(non_existent_id) }.not_to raise_error
end
it 'does not call the Imports::Destroy service' do
expect(Imports::Destroy).not_to receive(:new)
described_class.perform_now(non_existent_id)
end
it 'does not broadcast any messages' do
expect(ImportsChannel).not_to receive(:broadcast_to)
described_class.perform_now(non_existent_id)
end
it 'returns early without logging' do
allow(Rails.logger).to receive(:warn)
described_class.perform_now(non_existent_id)
expect(Rails.logger).not_to have_received(:warn)
end
end
context 'when import is deleted during job execution' do
it 'handles RecordNotFound gracefully' do
allow(Import).to receive(:find_by).with(id: import.id).and_return(import)
allow(import).to receive(:deleting!).and_raise(ActiveRecord::RecordNotFound)
expect { described_class.perform_now(import.id) }.not_to raise_error
end
it 'logs a warning when RecordNotFound is raised' do
allow(Import).to receive(:find_by).with(id: import.id).and_return(import)
allow(import).to receive(:deleting!).and_raise(ActiveRecord::RecordNotFound)
allow(Rails.logger).to receive(:warn)
described_class.perform_now(import.id)
expect(Rails.logger).to have_received(:warn).with(/Import #{import.id} not found/)
end
end
context 'when broadcast fails' do
before do
allow(ImportsChannel).to receive(:broadcast_to).and_raise(StandardError, 'Broadcast error')
end
it 'allows the error to propagate' do
expect { described_class.perform_now(import.id) }.to raise_error(StandardError, 'Broadcast error')
end
end
context 'when Imports::Destroy service fails' do
before do
allow_any_instance_of(Imports::Destroy).to receive(:call).and_raise(StandardError, 'Destroy failed')
end
it 'allows the error to propagate' do
expect { described_class.perform_now(import.id) }.to raise_error(StandardError, 'Destroy failed')
end
it 'has already set status to deleting before service is called' do
expect do
described_class.perform_now(import.id)
rescue StandardError
StandardError
end.to change { import.reload.status }.to('deleting')
end
end
context 'with multiple imports for different users' do
let(:user2) { create(:user) }
let(:import2) { create(:import, user: user2, status: :completed) }
it 'only broadcasts to the correct user' do
expect(ImportsChannel).to receive(:broadcast_to).with(user, anything).twice
expect(ImportsChannel).not_to receive(:broadcast_to).with(user2, anything)
described_class.perform_now(import.id)
end
end
context 'job enqueuing' do
it 'can be enqueued' do
expect do
described_class.perform_later(import.id)
end.to have_enqueued_job(described_class).with(import.id)
end
it 'can be performed later with correct arguments' do
expect do
described_class.perform_later(import.id)
end.to have_enqueued_job(described_class).on_queue('default').with(import.id)
end
end
end
end