# frozen_string_literal: true require 'rails_helper' RSpec.describe Imports::SecureFileDownloader do let(:file_content) { 'test content' } let(:file_size) { file_content.bytesize } let(:checksum) { Base64.strict_encode64(Digest::MD5.digest(file_content)) } let(:blob) { double('ActiveStorage::Blob', byte_size: file_size, checksum: checksum) } # Create a mock that mimics ActiveStorage::Attached::One let(:storage_attachment) { double('ActiveStorage::Attached::One', blob: blob) } subject { described_class.new(storage_attachment) } describe '#download_with_verification' do context 'when download is successful' do before do # Mock the download method to yield the file content allow(storage_attachment).to receive(:download) do |&block| block.call(file_content) end end it 'returns the file content' do expect(subject.download_with_verification).to eq(file_content) end end context 'when timeout occurs but succeeds on retry' do it 'retries the download internally and returns success after retries' do call_count = 0 # Mock storage_attachment to fail twice then succeed allow(storage_attachment).to receive(:download) do |&block| call_count += 1 raise Timeout::Error if call_count < 3 block.call(file_content) end # Expect logging for each retry attempt expect(Rails.logger).to receive(:warn).with(/Download timeout, attempt 1 of/).ordered expect(Rails.logger).to receive(:warn).with(/Download timeout, attempt 2 of/).ordered # The method should eventually return the content result = subject.download_with_verification expect(result).to eq(file_content) expect(call_count).to eq(3) # Verify retry attempts end end context 'when all download attempts timeout' do it 'raises the error after max retries' do # Make download always raise Timeout::Error allow(storage_attachment).to receive(:download).and_raise(Timeout::Error) # Expect warnings for each retry described_class::MAX_RETRIES.times do |i| expect(Rails.logger).to receive(:warn).with(/Download timeout, attempt #{i + 1} of/).ordered end # Expect error log on final failure expect(Rails.logger).to receive(:error).with(/Download failed after/).ordered # It should raise the Timeout::Error expect { subject.download_with_verification }.to raise_error(Timeout::Error) end end context 'when file size does not match' do let(:blob) { double('ActiveStorage::Blob', byte_size: 100, checksum: checksum) } before do allow(storage_attachment).to receive(:download) do |&block| block.call(file_content) end end it 'raises an error' do expect { subject.download_with_verification }.to raise_error(/Incomplete download/) end end context 'when checksum does not match' do let(:blob) { double('ActiveStorage::Blob', byte_size: file_size, checksum: 'invalid_checksum') } before do allow(storage_attachment).to receive(:download) do |&block| block.call(file_content) end end it 'raises an error' do expect { subject.download_with_verification }.to raise_error(/Checksum mismatch/) end end context 'when download fails with a different error' do before do allow(storage_attachment).to receive(:download).and_raise(StandardError, 'Download failed') end it 'logs the error and re-raises it' do expect(Rails.logger).to receive(:error).with(/Download error: Download failed/) expect { subject.download_with_verification }.to raise_error(StandardError, 'Download failed') end end end end