Skip to content

Commit 52117bf

Browse files
committed
fix code coverage
1 parent 3596c55 commit 52117bf

File tree

3 files changed

+206
-87
lines changed

3 files changed

+206
-87
lines changed

app/services/bulk_zombie_url_uploader.rb

Lines changed: 49 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -8,65 +8,85 @@ class Error < StandardError; end
88
def initialize(filename, filepath)
99
@file_name = filename
1010
@file_path = filepath
11+
@results = nil
1112
end
1213

1314
def upload
14-
@results = BulkZombieUrls::Results.new(@file_name)
15-
raise 'Results object not initialized' if @results.nil?
16-
17-
begin
18-
upload_urls
19-
rescue => e
20-
error_message = 'Your document could not be processed. Please check the format and try again.'
21-
Rails.logger.error "Problem processing bulk zombie URL document: #{error_message} | #{e.message}"
22-
end
23-
@results
15+
initialize_results
16+
process_upload
17+
rescue StandardError => e
18+
log_upload_error(e)
19+
ensure
20+
@results ||= BulkZombieUrls::Results.new(@file_name)
2421
end
2522

2623
private
2724

28-
def upload_urls
29-
parse_csv.each do |row|
30-
process_row(row)
31-
end
25+
def initialize_results
26+
@results = BulkZombieUrls::Results.new(@file_name)
27+
raise Error, 'Results object not initialized' unless @results
28+
end
29+
30+
def process_upload
31+
parse_csv.each { |row| process_row(row) }
3232
rescue CSV::MalformedCSVError => e
3333
handle_csv_error(e)
3434
end
3535

3636
def parse_csv
37-
CSV.parse(File.read(@file_path), headers: true)
37+
csv = CSV.parse(File.read(@file_path), headers: true)
38+
raise CSV::MalformedCSVError, "Missing required headers" unless %w[URL DOC_ID].all? { |col| csv.headers.include?(col) }
39+
csv
40+
rescue CSV::MalformedCSVError, ArgumentError => e
41+
raise CSV::MalformedCSVError.new('CSV', "Malformed or invalid CSV: #{e.message}")
3842
end
3943

4044
def process_row(row)
45+
raise Error, 'Results object not initialized' unless @results
46+
4147
url = row['URL']&.strip
4248
document_id = row['DOC_ID']&.strip
4349

44-
if document_id.blank?
45-
@results.add_error('Document ID is missing', url || 'Unknown')
46-
Rails.logger.error("Skipping row: #{row.inspect}. Document ID is mandatory.")
47-
return
48-
end
50+
return log_missing_document_id(row, url) if document_id.blank?
4951

50-
process_url_with_rescue(url, document_id, row)
52+
handle_url_processing(url, document_id, row)
5153
end
5254

53-
def process_url_with_rescue(url, document_id, row)
54-
process_url(url, document_id)
55-
@results.delete_ok
56-
@results.increment_updated
55+
def handle_url_processing(url, document_id, row)
56+
process_url_with_rescue(url, document_id)
57+
update_results
5758
rescue StandardError => e
5859
handle_processing_error(e, url, document_id, row)
5960
end
6061

62+
def update_results
63+
@results.delete_ok
64+
@results.increment_updated
65+
end
66+
67+
def log_missing_document_id(row, url)
68+
@results.add_error('Document ID is missing', url || 'Unknown')
69+
Rails.logger.error("Skipping row: #{row.inspect}. Document ID is mandatory.")
70+
end
71+
6172
def handle_csv_error(error)
6273
@results.add_error('Invalid CSV format', 'Entire file')
63-
Rails.logger.error "Error parsing CSV: #{error.message}"
74+
Rails.logger.error("Error parsing CSV: #{error.message}")
75+
end
76+
77+
def log_upload_error(error)
78+
error_message = "Failed to process bulk zombie URL document (file: #{@file_name})."
79+
Rails.logger.error("#{error_message} Error: #{error.message}\n#{error.backtrace.join("\n")}")
6480
end
6581

6682
def handle_processing_error(error, url, document_id, row)
6783
key = url.presence || document_id
68-
@results.add_error(error.message, key)
69-
Rails.logger.error "Failure to process bulk upload zombie URL row: #{row.inspect}\n#{error.message}\n#{error.backtrace.join("\n")}"
84+
@results&.add_error(error.message, key)
85+
Rails.logger.error("Failure to process bulk upload zombie URL row: #{row.inspect}\n#{error.message}\n#{error.backtrace.join("\n")}")
86+
end
87+
88+
def process_url_with_rescue(url, document_id)
89+
process_url(url, document_id)
7090
end
7191

7292
def process_url(url, document_id)
@@ -79,11 +99,7 @@ def process_url(url, document_id)
7999

80100
def process_url_with_searchgov(url, document_id)
81101
searchgov_url = SearchgovUrl.find_by(url:)
82-
if searchgov_url
83-
searchgov_url.destroy
84-
else
85-
delete_document(document_id)
86-
end
102+
searchgov_url ? searchgov_url.destroy : delete_document(document_id)
87103
end
88104

89105
def delete_document(document_id)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
URL,DOC_ID
2+
https://open.defense.gov/1,abc124
3+
https://open.defense.gov2/,abc125

spec/services/bulk_zombie_url_uploader_spec.rb

Lines changed: 154 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -2,99 +2,199 @@
22

33
describe BulkZombieUrlUploader do
44
let(:filename) { 'test_file.csv' }
5-
let(:filepath) { '/path/to/test_file.csv' }
5+
let(:filepath) { Rails.root.join('spec', 'fixtures', 'files', filename) }
66
let(:uploader) { described_class.new(filename, filepath) }
7-
let(:results) { instance_double(BulkZombieUrls::Results) }
8-
let(:csv_content) do
9-
<<~CSV
10-
URL,DOC_ID
11-
http://example.com,doc1
12-
,doc2
13-
http://missingdoc.com,
14-
CSV
15-
end
7+
let(:results_double) { instance_double('BulkZombieUrls::Results') }
168

179
before do
18-
allow(File).to receive(:read).with(filepath).and_return(csv_content)
19-
allow(BulkZombieUrls::Results).to receive(:new).and_return(results)
20-
allow(results).to receive(:add_error)
21-
allow(results).to receive(:delete_ok)
22-
allow(results).to receive(:increment_updated)
23-
uploader.instance_variable_set(:@results, results) # Ensure `@results` is initialized
10+
allow(BulkZombieUrls::Results).to receive(:new).and_return(results_double)
11+
allow(results_double).to receive(:add_error)
12+
allow(results_double).to receive(:delete_ok)
13+
allow(results_double).to receive(:increment_updated)
2414
end
2515

2616
describe '#initialize' do
2717
it 'assigns filename and filepath' do
2818
expect(uploader.instance_variable_get(:@file_name)).to eq(filename)
2919
expect(uploader.instance_variable_get(:@file_path)).to eq(filepath)
20+
expect(uploader.results).to be_nil
3021
end
3122
end
3223

33-
describe '#upload_urls' do
34-
context 'with valid CSV content' do
35-
it 'processes each row in the CSV' do
36-
allow(uploader).to receive(:process_row)
37-
uploader.send(:upload_urls)
38-
expect(uploader).to have_received(:process_row).exactly(3).times
24+
describe '#upload' do
25+
subject { uploader.upload }
26+
27+
before do
28+
allow(uploader).to receive(:initialize_results).and_call_original
29+
allow(uploader).to receive(:process_upload)
30+
allow(uploader).to receive(:log_upload_error)
31+
end
32+
33+
it 'initializes results correctly' do
34+
expect { subject }.not_to raise_error
35+
end
36+
37+
context 'when no error occurs' do
38+
it 'initializes results correctly' do
39+
expect { subject }.not_to raise_error
3940
end
4041
end
4142

42-
context 'with invalid CSV content' do
43-
let(:csv_error) { CSV::MalformedCSVError.new('Invalid CSV format', 'Line causing error') }
43+
context 'when an error occurs' do
44+
let(:error) { StandardError.new('Test Error') }
4445

4546
before do
46-
allow(CSV).to receive(:parse).and_raise(csv_error)
47-
allow(Rails.logger).to receive(:error)
47+
allow(uploader).to receive(:process_upload).and_raise(error)
48+
end
49+
50+
it 'logs the upload error' do
51+
expect(uploader).to receive(:log_upload_error).with(error)
52+
subject
4853
end
54+
end
55+
end
56+
57+
describe '#initialize_results' do
58+
subject { uploader.send(:initialize_results) }
59+
60+
it 'initializes the @results object' do
61+
expect { subject }.not_to raise_error
62+
expect(uploader.instance_variable_get(:@results)).to eq(results_double)
63+
end
64+
end
65+
66+
describe '#process_upload' do
67+
subject { uploader.send(:process_upload) }
68+
69+
let(:csv_content) { "URL,DOC_ID\nhttp://example.com,123\n" }
70+
let(:parsed_csv) { CSV.parse(csv_content, headers: true) }
71+
72+
before do
73+
allow(File).to receive(:read).with(filepath).and_return(csv_content)
74+
allow(CSV).to receive(:parse).and_return(parsed_csv)
75+
allow(uploader).to receive(:process_row)
76+
end
77+
78+
it 'parses the CSV and processes each row' do
79+
expect(CSV).to receive(:parse).and_return(parsed_csv)
80+
expect(uploader).to receive(:process_row).with(parsed_csv.first)
81+
subject
82+
end
83+
end
84+
85+
describe '#parse_csv' do
86+
subject { uploader.send(:parse_csv) }
87+
88+
let(:csv_content) { "URL,DOC_ID\nhttp://example.com,123\n" }
89+
90+
before do
91+
allow(File).to receive(:read).with(filepath).and_return(csv_content)
92+
end
93+
94+
context 'with valid CSV headers' do
95+
it 'returns parsed CSV' do
96+
expect(subject).to be_a(CSV::Table)
97+
end
98+
end
4999

50-
it 'handles the CSV error and logs it' do
51-
expect(results).to receive(:add_error).with('Invalid CSV format', 'Entire file')
52-
uploader.send(:upload_urls)
53-
expect(Rails.logger).to have_received(:error).with(/Error parsing CSV/)
100+
context 'with missing headers' do
101+
let(:csv_content) { "INVALID_HEADER\nhttp://example.com\n" }
102+
103+
it 'raises a CSV::MalformedCSVError' do
104+
expect { subject }.to raise_error(CSV::MalformedCSVError)
54105
end
55106
end
56107
end
57108

58109
describe '#process_row' do
59-
let(:row) { { 'URL' => 'http://example.com', 'DOC_ID' => 'doc1' } }
110+
subject { uploader.send(:process_row, row) }
111+
112+
let(:row) { { 'URL' => 'http://example.com', 'DOC_ID' => '123' } }
113+
114+
context 'when @results is not initialized' do
115+
before do
116+
uploader.instance_variable_set(:@results, nil)
117+
end
118+
119+
it 'raises an error' do
120+
expect { subject }.to raise_error(BulkZombieUrlUploader::Error, 'Results object not initialized')
121+
end
122+
end
60123

61-
context 'when DOC_ID is blank' do
62-
let(:row) { { 'URL' => 'http://example.com', 'DOC_ID' => nil } }
124+
context 'when @results is initialized' do
125+
before do
126+
uploader.send(:initialize_results)
127+
end
63128

64-
it 'adds an error and logs it' do
65-
allow(Rails.logger).to receive(:error)
66-
uploader.send(:process_row, row)
67-
expect(results).to have_received(:add_error).with('Document ID is missing', 'http://example.com')
68-
expect(Rails.logger).to have_received(:error).with(/Document ID is mandatory/)
129+
it 'does not raise an error' do
130+
expect { subject }.not_to raise_error
69131
end
70132
end
71133
end
72134

73-
describe '#process_url_with_rescue' do
74-
let(:row) { { 'URL' => 'http://example.com', 'DOC_ID' => 'doc1' } }
135+
describe '#handle_url_processing' do
136+
subject { uploader.send(:handle_url_processing, url, document_id, row) }
75137

76-
before do
77-
allow(uploader).to receive(:process_url)
138+
let(:url) { 'http://example.com' }
139+
let(:document_id) { '123' }
140+
let(:row) { { 'URL' => url, 'DOC_ID' => document_id } }
141+
142+
context 'when no error occurs' do
143+
before do
144+
allow(uploader).to receive(:process_url_with_rescue)
145+
allow(uploader).to receive(:update_results)
146+
end
147+
148+
it 'processes the URL and updates results' do
149+
expect(uploader).to receive(:process_url_with_rescue).with(url, document_id)
150+
expect(uploader).to receive(:update_results)
151+
subject
152+
end
78153
end
79154

80-
it 'processes the URL and updates results' do
81-
uploader.send(:process_url_with_rescue, 'http://example.com', 'doc1', row)
82-
expect(results).to have_received(:delete_ok)
83-
expect(results).to have_received(:increment_updated)
155+
context 'when an error occurs' do
156+
let(:error) { StandardError.new('Test Error') }
157+
158+
before do
159+
allow(uploader).to receive(:process_url_with_rescue).and_raise(error)
160+
allow(uploader).to receive(:handle_processing_error)
161+
end
162+
163+
it 'handles processing error' do
164+
expect(uploader).to receive(:handle_processing_error).with(error, url, document_id, row)
165+
subject
166+
end
167+
end
168+
end
169+
170+
describe '#process_url' do
171+
subject { uploader.send(:process_url, url, document_id) }
172+
173+
let(:document_id) { '123' }
174+
175+
context 'when URL is present' do
176+
let(:url) { 'http://example.com' }
177+
178+
before do
179+
allow(uploader).to receive(:process_url_with_searchgov)
180+
end
181+
182+
it 'processes URL with Searchgov' do
183+
expect(uploader).to receive(:process_url_with_searchgov).with(url, document_id)
184+
subject
185+
end
84186
end
85187

86-
context 'when an error occurs during processing' do
87-
let(:error) { StandardError.new('Processing error') }
188+
context 'when URL is blank' do
189+
let(:url) { nil }
88190

89191
before do
90-
allow(uploader).to receive(:process_url).and_raise(error)
91-
allow(Rails.logger).to receive(:error)
192+
allow(uploader).to receive(:delete_document)
92193
end
93194

94-
it 'handles the error and logs it' do
95-
uploader.send(:process_url_with_rescue, 'http://example.com', 'doc1', row)
96-
expect(results).to have_received(:add_error).with('Processing error', 'http://example.com')
97-
expect(Rails.logger).to have_received(:error).with(/Failure to process bulk upload zombie URL row/)
195+
it 'deletes the document' do
196+
expect(uploader).to receive(:delete_document).with(document_id)
197+
subject
98198
end
99199
end
100200
end

0 commit comments

Comments
 (0)