diff --git a/.github/workflows/rubocop.yml b/.github/workflows/rubocop.yml index b9e891b..2f27637 100644 --- a/.github/workflows/rubocop.yml +++ b/.github/workflows/rubocop.yml @@ -25,4 +25,5 @@ jobs: - name: RuboCop run: | gem install rubocop + gem install rubocop-rspec rubocop diff --git a/.rspec b/.rspec index fc4807b..c99d2e7 100644 --- a/.rspec +++ b/.rspec @@ -1 +1 @@ ---format doc +--require spec_helper diff --git a/.rubocop.yml b/.rubocop.yml index 751c3d9..d199271 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,5 +1,8 @@ inherit_from: .rubocop_todo.yml +require: + - rubocop-rspec + AllCops: NewCops: enable TargetRubyVersion: 2.6 @@ -30,6 +33,15 @@ Metrics/ModuleLength: Exclude: - 'spec/**/*' +RSpec/ExampleLength: + Enabled: false + +RSpec/MultipleExpectations: + Enabled: false + +RSpec/MultipleMemoizedHelpers: + Max: 10 + Style/TrailingCommaInArrayLiteral: EnforcedStyleForMultiline: consistent_comma diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 316f9e9..3d14cc2 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -51,13 +51,11 @@ Lint/AmbiguousOperatorPrecedence: - 'bin/docpath' - 'config.ru' - 'lib/sidekiq_app.rb' - - 'spec/spec_helper.rb' # Configuration parameters: AllowComments, AllowEmptyLambdas. Lint/EmptyBlock: Exclude: - 'lib/heathen/processor.rb' - - 'spec/lib/sidekiq_workers_spec.rb' # This cop supports unsafe autocorrection (--autocorrect-all). Lint/NonAtomicFileOperation: @@ -68,7 +66,6 @@ Lint/NonAtomicFileOperation: Lint/NonDeterministicRequireOrder: Exclude: - 'lib/heathen.rb' - - 'spec/spec_helper.rb' # This cop supports safe autocorrection (--autocorrect). Lint/ParenthesesAsGroupedExpression: @@ -116,7 +113,7 @@ Lint/UselessAssignment: Metrics/AbcSize: Max: 52 -# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns. +# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns, inherit_mode. # AllowedMethods: refine Metrics/BlockLength: Max: 37 @@ -168,6 +165,76 @@ Naming/VariableNumber: Exclude: - 'spec/heathen/convert_spec.rb' +RSpec/BeforeAfterAll: + Exclude: + - 'spec/spec_helper.rb' + - 'spec/rails_helper.rb' + - 'spec/support/**/*.rb' + - 'spec/lib/autoheathen/config_spec.rb' + +# Configuration parameters: Prefixes, AllowedPatterns. +# Prefixes: when, with, without +RSpec/ContextWording: + Exclude: + - 'spec/heathen/processor_methods/libreoffice_spec.rb' + - 'spec/integration/standard_tasks_spec.rb' + - 'spec/lib/app_spec.rb' + +# Configuration parameters: IgnoredMetadata. +RSpec/DescribeClass: + Exclude: + - '**/spec/features/**/*' + - '**/spec/requests/**/*' + - '**/spec/routing/**/*' + - '**/spec/system/**/*' + - '**/spec/views/**/*' + - 'spec/integration/standard_tasks_spec.rb' + +# Configuration parameters: Include, CustomTransform, IgnoreMethods, SpecSuffixOnly. +# Include: **/*_spec*rb*, **/spec/**/* +RSpec/FilePath: + Enabled: false + +# Configuration parameters: AssignmentOnly. +RSpec/InstanceVariable: + Exclude: + - 'spec/heathen/processor_methods/libreoffice_spec.rb' + - 'spec/lib/autoheathen/config_spec.rb' + - 'spec/lib/sidekiq_workers_spec.rb' + +# Configuration parameters: EnforcedStyle. +# SupportedStyles: have_received, receive +RSpec/MessageSpies: + Exclude: + - 'spec/lib/app_spec.rb' + - 'spec/lib/autoheathen/email_processor_spec.rb' + - 'spec/lib/config_spec.rb' + - 'spec/lib/sidekiq_workers_spec.rb' + +RSpec/MultipleDescribes: + Exclude: + - 'spec/lib/sidekiq_workers_spec.rb' + +# Configuration parameters: AllowedPatterns. +# AllowedPatterns: ^expect_, ^assert_ +RSpec/NoExpectationExample: + Exclude: + - 'spec/lib/document_spec.rb' + +RSpec/StubbedMock: + Exclude: + - 'spec/lib/app_spec.rb' + - 'spec/lib/config_spec.rb' + - 'spec/lib/sidekiq_workers_spec.rb' + +# Configuration parameters: IgnoreNameless, IgnoreSymbolicNames. +RSpec/VerifiedDoubles: + Exclude: + - 'spec/lib/app_spec.rb' + - 'spec/lib/converter_spec.rb' + - 'spec/lib/legacy_converter_spec.rb' + - 'spec/lib/sidekiq_workers_spec.rb' + # This cop supports unsafe autocorrection (--autocorrect-all). Security/YAMLLoad: Exclude: @@ -221,7 +288,6 @@ Style/ClassEqualityComparison: Style/ColonMethodCall: Exclude: - 'lib/autoheathen/config.rb' - - 'spec/spec_helper.rb' # Configuration parameters: AllowedConstants. Style/Documentation: @@ -426,7 +492,6 @@ Style/Semicolon: Style/SingleLineMethods: Exclude: - 'lib/errors.rb' - - 'spec/spec_helper.rb' # This cop supports unsafe autocorrection (--autocorrect-all). Style/SlicingWithRange: @@ -461,7 +526,6 @@ Style/StringConcatenation: - 'lib/sidekiq_app.rb' - 'spec/lib/document_spec.rb' - 'spec/lib/sidekiq_workers_spec.rb' - - 'spec/spec_helper.rb' - 'unicorn.rb' # This cop supports safe autocorrection (--autocorrect). @@ -498,7 +562,6 @@ Style/WordArray: - 'spec/lib/app_spec.rb' - 'spec/lib/autoheathen/config_spec.rb' - 'spec/lib/autoheathen/email_processor_spec.rb' - - 'spec/lib/document_spec.rb' # This cop supports unsafe autocorrection (--autocorrect-all). Style/ZeroLengthPredicate: diff --git a/spec/heathen/convert_spec.rb b/spec/heathen/convert_spec.rb index c0f2eb3..b8823e3 100644 --- a/spec/heathen/convert_spec.rb +++ b/spec/heathen/convert_spec.rb @@ -15,10 +15,11 @@ def failing_step_1 end end -describe Heathen::Converter do +RSpec.describe Heathen::Converter do before do Heathen::Task.clear 'test' end + after do Heathen::Task.clear 'test' end diff --git a/spec/heathen/filename_spec.rb b/spec/heathen/filename_spec.rb index e0fd06b..e1fee44 100644 --- a/spec/heathen/filename_spec.rb +++ b/spec/heathen/filename_spec.rb @@ -1,10 +1,11 @@ require 'spec_helper' require 'filemagic/ext' -describe Heathen::Filename do +RSpec.describe Heathen::Filename do let(:content) { 'The quick brown fox jumps over the lazy dog' } let(:mime_type) { content.mime_type } - context '.suggest' do + + describe '.suggest' do it 'suggests foo.pdf' do expect(described_class.suggest('foo.pdf', mime_type)).to eq 'foo.txt' end @@ -22,7 +23,7 @@ end end - context '.suggest_in_new_dir' do + describe '.suggest_in_new_dir' do it 'suggests (short dir) -> (longer dir)' do expect( described_class.suggest_in_new_dir('/home/joe/src/foo.pdf', mime_type, '/home', '/opt/users') diff --git a/spec/heathen/processor_methods/convert_image_spec.rb b/spec/heathen/processor_methods/convert_image_spec.rb index eb4d640..0889838 100644 --- a/spec/heathen/processor_methods/convert_image_spec.rb +++ b/spec/heathen/processor_methods/convert_image_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Heathen::Processor do +RSpec.describe Heathen::Processor do let(:content) { File.read(fixture('heathen/quickfox.jpg')) } let(:job) { Heathen::Job.new 'foo', content, 'en' } let(:processor) { described_class.new job: job, logger: spec_logger } @@ -9,7 +9,7 @@ processor.clean_up end - context '#convert_image' do + describe '#convert_image' do it 'converts to tiff' do processor.convert_image to: :tiff, params: '-density 72' expect(job.content.mime_type).to eq 'image/tiff; charset=binary' diff --git a/spec/heathen/processor_methods/htmltotext_spec.rb b/spec/heathen/processor_methods/htmltotext_spec.rb index 7db92db..6a5e462 100644 --- a/spec/heathen/processor_methods/htmltotext_spec.rb +++ b/spec/heathen/processor_methods/htmltotext_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Heathen::Processor do +RSpec.describe Heathen::Processor do let(:content) { File.read(fixture('heathen/quickfox.html')) } let(:job) { Heathen::Job.new 'foo', content, 'en' } let(:processor) { described_class.new job: job, logger: Logger.new($stderr) } @@ -9,7 +9,7 @@ processor.clean_up end - context '#htmltotext' do + describe '#htmltotext' do it 'converts HTML to TXT' do processor.htmltotext expect(job.content.mime_type).to eq 'text/plain; charset=us-ascii' diff --git a/spec/heathen/processor_methods/libreoffice_spec.rb b/spec/heathen/processor_methods/libreoffice_spec.rb index af00f4f..a9f0ecc 100644 --- a/spec/heathen/processor_methods/libreoffice_spec.rb +++ b/spec/heathen/processor_methods/libreoffice_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Heathen::Processor do +RSpec.describe Heathen::Processor do let(:ms_word_content) { File.read(fixture('heathen/msword.docx')) } let(:ms_spreadsheet_content) { File.read(fixture('heathen/msexcel.xlsx')) } let(:ms_ppt_content) { File.read(fixture('heathen/mspowerpoint.pptx')) } @@ -17,33 +17,38 @@ def new_job(content) @processor.clean_up end - context '#libreoffice' do + describe '#libreoffice' do context 'convert to PDF' do it 'from MS word' do new_job ms_word_content @processor.libreoffice format: 'pdf' expect(@job.content.mime_type).to eq 'application/pdf; charset=binary' end + it 'from MS spreadsheet' do new_job ms_spreadsheet_content @processor.libreoffice format: 'pdf' expect(@job.content.mime_type).to eq 'application/pdf; charset=binary' end + it 'from MS powerpoint' do new_job ms_ppt_content @processor.libreoffice format: 'pdf' expect(@job.content.mime_type).to eq 'application/pdf; charset=binary' end + it 'from OO word' do new_job oo_word_content @processor.libreoffice format: 'pdf' expect(@job.content.mime_type).to eq 'application/pdf; charset=binary' end + it 'from OO spreadsheet' do new_job oo_spreadsheet_content @processor.libreoffice format: 'pdf' expect(@job.content.mime_type).to eq 'application/pdf; charset=binary' end + it 'from OO presentation' do new_job oo_presentation_content @processor.libreoffice format: 'pdf' @@ -57,11 +62,13 @@ def new_job(content) @processor.libreoffice format: 'msoffice' expect(ms_word_mime_types).to include(@job.content.mime_type) end + it 'from OO spreadsheet' do new_job oo_spreadsheet_content @processor.libreoffice format: 'msoffice' expect(ms_excel_mime_types).to include(@job.content.mime_type) end + it 'from OO presentation' do new_job oo_presentation_content @processor.libreoffice format: 'msoffice' @@ -75,11 +82,13 @@ def new_job(content) @processor.libreoffice format: 'ooffice' expect(oo_odt_mime_types).to include(@job.content.mime_type) end + it 'from MS spreadsheet' do new_job ms_spreadsheet_content @processor.libreoffice format: 'ooffice' expect(oo_ods_mime_types).to include(@job.content.mime_type) end + it 'from MS powerpoint' do new_job ms_ppt_content @processor.libreoffice format: 'ooffice' @@ -93,6 +102,7 @@ def new_job(content) @processor.libreoffice format: 'txt' expect(@job.content.mime_type).to eq 'text/plain; charset=us-ascii' end + it 'from OO word' do new_job oo_word_content @processor.libreoffice format: 'txt' diff --git a/spec/heathen/processor_methods/pdftotext_spec.rb b/spec/heathen/processor_methods/pdftotext_spec.rb index 0d0f719..72ff47c 100644 --- a/spec/heathen/processor_methods/pdftotext_spec.rb +++ b/spec/heathen/processor_methods/pdftotext_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Heathen::Processor do +RSpec.describe Heathen::Processor do let(:content) { File.read(fixture('heathen/quickfox.pdf')) } let(:job) { Heathen::Job.new 'foo', content, 'en' } let(:processor) { described_class.new job: job, logger: Logger.new($stderr) } @@ -9,7 +9,7 @@ processor.clean_up end - context '#pdftotext' do + describe '#pdftotext' do it 'converts PDF to TXT' do processor.pdftotext expect(job.content.mime_type).to eq 'text/plain; charset=us-ascii' diff --git a/spec/heathen/processor_methods/tesseract_spec.rb b/spec/heathen/processor_methods/tesseract_spec.rb index deea3b0..85f5974 100644 --- a/spec/heathen/processor_methods/tesseract_spec.rb +++ b/spec/heathen/processor_methods/tesseract_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Heathen::Processor do +RSpec.describe Heathen::Processor do let(:content) { File.read(fixture('heathen/quickfox.tiff')) } let(:job) { Heathen::Job.new 'foo', content, 'en' } let(:processor) { described_class.new job: job, logger: spec_logger } @@ -9,15 +9,17 @@ processor.clean_up end - context '#tesseract' do + describe '#tesseract' do it 'converts a tiff to text' do processor.tesseract format: nil expect(job.content.mime_type).to eq 'text/plain; charset=us-ascii' end + it 'converts a tiff to PDF' do processor.tesseract format: 'pdf' expect(job.content.mime_type).to eq 'application/pdf; charset=binary' end + it 'converts a tiff to HOCR' do processor.tesseract format: 'hocr' expect(tesseract_hocr_mime_types).to include(job.content.mime_type) diff --git a/spec/heathen/processor_methods/wkhtmltopdf_spec.rb b/spec/heathen/processor_methods/wkhtmltopdf_spec.rb index 2af3360..e368948 100644 --- a/spec/heathen/processor_methods/wkhtmltopdf_spec.rb +++ b/spec/heathen/processor_methods/wkhtmltopdf_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Heathen::Processor do +RSpec.describe Heathen::Processor do let(:content) { File.read(fixture('heathen/quickfox.html')) } let(:job) { Heathen::Job.new 'foo', content, 'en' } let(:processor) { described_class.new job: job, logger: spec_logger } @@ -9,7 +9,7 @@ processor.clean_up end - context '#wkhtmltopdf' do + describe '#wkhtmltopdf' do it 'converts HTML to PDF' do processor.wkhtmltopdf expect(job.content.mime_type).to eq 'application/pdf; charset=binary' diff --git a/spec/integration/standard_tasks_spec.rb b/spec/integration/standard_tasks_spec.rb index a3627c9..933cba1 100644 --- a/spec/integration/standard_tasks_spec.rb +++ b/spec/integration/standard_tasks_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'Standard Heathen tasks:' do +RSpec.describe 'Standard Heathen tasks:' do before do setup_storage allow(Colore::C_).to receive(:storage_directory) { tmp_storage_dir } diff --git a/spec/lib/app_spec.rb b/spec/lib/app_spec.rb index 8a7359a..0a7847c 100644 --- a/spec/lib/app_spec.rb +++ b/spec/lib/app_spec.rb @@ -1,8 +1,11 @@ require 'spec_helper' require 'app' -require 'config' -describe Colore::App do +RSpec.describe Colore::App do + include Rack::Test::Methods + + subject(:app) { described_class } + let(:appname) { 'app' } let(:doc_id) { '12345' } let(:filename) { 'arglebargle.docx' } @@ -49,6 +52,7 @@ def show_backtrace(response) ) expect(Colore::Sidekiq::ConversionWorker).to have_received(:perform_async).twice end + it 'fails to create an existing document' do put "/document/#{appname}/#{doc_id}/#{filename}", { title: 'A title', @@ -60,7 +64,7 @@ def show_backtrace(response) expect(last_response.status).to eq 409 expect(last_response.content_type).to eq 'application/json' expect(JSON.parse(last_response.body)).to be_a Hash - expect(Colore::Sidekiq::ConversionWorker).to_not have_received(:perform_async) + expect(Colore::Sidekiq::ConversionWorker).not_to have_received(:perform_async) end end @@ -90,7 +94,7 @@ def show_backtrace(response) expect(last_response.status).to eq 404 expect(last_response.content_type).to eq 'application/json' expect(JSON.parse(last_response.body)).to be_a Hash - expect(Colore::Sidekiq::ConversionWorker).to_not have_received(:perform_async) + expect(Colore::Sidekiq::ConversionWorker).not_to have_received(:perform_async) end end @@ -125,6 +129,7 @@ def show_backtrace(response) ) expect(Colore::Sidekiq::ConversionWorker).to have_received(:perform_async).once end + it 'fails if invalid document' do post "/document/#{appname}/#{invalid_doc_id}/current/#{filename}/ocr", { backtrace: true, @@ -133,8 +138,9 @@ def show_backtrace(response) expect(last_response.status).to eq 404 expect(last_response.content_type).to eq 'application/json' expect(JSON.parse(last_response.body)).to be_a Hash - expect(Colore::Sidekiq::ConversionWorker).to_not have_received(:perform_async) + expect(Colore::Sidekiq::ConversionWorker).not_to have_received(:perform_async) end + it 'fails if invalid version' do post "/document/#{appname}/#{doc_id}/fred/#{filename}/ocr", { backtrace: true, @@ -143,7 +149,7 @@ def show_backtrace(response) expect(last_response.status).to eq 400 expect(last_response.content_type).to eq 'application/json' expect(JSON.parse(last_response.body)).to be_a Hash - expect(Colore::Sidekiq::ConversionWorker).to_not have_received(:perform_async) + expect(Colore::Sidekiq::ConversionWorker).not_to have_received(:perform_async) end end @@ -173,6 +179,7 @@ def show_backtrace(response) { "status" => 200, "description" => "Document version deleted" } ) end + it 'fails if you try to delete current' do delete "/document/#{appname}/#{doc_id}/current", { deleted_by: 'a.person', @@ -182,6 +189,7 @@ def show_backtrace(response) expect(last_response.content_type).to eq 'application/json' expect(JSON.parse(last_response.body)).to be_a Hash end + it 'fails if you try to delete the current version' do delete "/document/#{appname}/#{doc_id}/v002", { deleted_by: 'a.person', @@ -199,14 +207,16 @@ def show_backtrace(response) show_backtrace last_response expect(last_response.status).to eq 200 expect(last_response.content_type).to eq 'application/vnd.openxmlformats-officedocument.wordprocessingml.document; charset=binary' - expect(last_response.body).to_not be_nil + expect(last_response.body).not_to be_nil end + it 'fails for an invalid document' do get "/document/#{appname}/#{invalid_doc_id}/current/#{filename}" expect(last_response.status).to eq 404 expect(last_response.content_type).to eq 'application/json' expect(JSON.parse(last_response.body)).to be_a Hash end + it 'fails for an invalid filename' do get "/document/#{appname}/#{doc_id}/current/foo.txt" expect(last_response.status).to eq 400 @@ -223,6 +233,7 @@ def show_backtrace(response) expect(last_response.content_type).to eq 'application/json' expect(JSON.parse(last_response.body)).to be_a Hash end + it 'fails for an invalid document' do get "/document/#{appname}/#{invalid_doc_id}" expect(last_response.status).to eq 404 @@ -244,6 +255,7 @@ def show_backtrace(response) expect(body['description'].to_s).to eq "missing file parameter" end end + context 'when file is not correct' do it 'returns an error' do params = { @@ -256,6 +268,7 @@ def show_backtrace(response) expect(body['description'].to_s).to eq "invalid file parameter" end end + it 'converts and saves file' do foo = double(Colore::Converter) allow(Colore::Converter).to receive(:new) { foo } @@ -263,12 +276,13 @@ def show_backtrace(response) action: 'pdf', file: Rack::Test::UploadedFile.new(__FILE__, 'application/ruby'), } - expect(foo).to receive(:convert_file).with(params[:action], String, nil) { "%PDF-1.4" } + expect(foo).to receive(:convert_file).with(params[:action], String, nil).and_return("%PDF-1.4") post "/convert", params expect(last_response.status).to eq 200 expect(last_response.content_type).to eq 'application/pdf; charset=us-ascii' expect(last_response.body).to eq '%PDF-1.4' end + it 'returns correct JSON structure on fail' do foo = double(Colore::Converter) allow(Colore::Converter).to receive(:new) { foo } @@ -294,14 +308,15 @@ def show_backtrace(response) action: 'pdf', file: Rack::Test::UploadedFile.new(__FILE__, 'application/ruby'), } - expect(foo).to receive(:convert_file).with(params[:action], String, nil) { 'foobar' } + expect(foo).to receive(:convert_file).with(params[:action], String, nil).and_return('foobar') post "/#{Colore::LegacyConverter::LEGACY}/convert", params expect(last_response.status).to eq 200 expect(last_response.content_type).to eq 'application/json' body = JSON.parse(last_response.body) expect(body).to be_a Hash - expect(body['converted'].to_s).to_not eq '' + expect(body['converted'].to_s).not_to eq '' end + it 'converts and saves URL' do foo = double(Colore::LegacyConverter) allow(Colore::LegacyConverter).to receive(:new) { foo } @@ -309,15 +324,16 @@ def show_backtrace(response) action: 'pdf', url: 'http://localhost/foo/bar', } - expect(Net::HTTP).to receive(:get).with(URI(params[:url])) { 'The quick brown flox' } - expect(foo).to receive(:convert_file).with(params[:action], String, nil) { 'foobar' } + expect(Net::HTTP).to receive(:get).with(URI(params[:url])).and_return('The quick brown flox') + expect(foo).to receive(:convert_file).with(params[:action], String, nil).and_return('foobar') post "/#{Colore::LegacyConverter::LEGACY}/convert", params expect(last_response.status).to eq 200 expect(last_response.content_type).to eq 'application/json' body = JSON.parse(last_response.body) expect(body).to be_a Hash - expect(body['converted'].to_s).to_not eq '' + expect(body['converted'].to_s).not_to eq '' end + it 'returns correct JSON structure on fail' do foo = double(Colore::LegacyConverter) allow(Colore::LegacyConverter).to receive(:new) { foo } @@ -343,13 +359,14 @@ def show_backtrace(response) expect(last_response.content_type).to eq 'text/plain; charset=us-ascii' expect(last_response.body).to eq 'The quick brown fox' end + it 'returns correct JSON structure on fail' do get "/#{Colore::LegacyConverter::LEGACY}/foo.txt" expect(last_response.status).to eq 400 expect(last_response.content_type).to eq 'application/json' body = JSON.parse(last_response.body) expect(body).to be_a Hash - expect(body['error'].to_s).to_not eq '' + expect(body['error'].to_s).not_to eq '' end end end diff --git a/spec/lib/autoheathen/config_spec.rb b/spec/lib/autoheathen/config_spec.rb index 8b7468f..6979ce2 100644 --- a/spec/lib/autoheathen/config_spec.rb +++ b/spec/lib/autoheathen/config_spec.rb @@ -2,17 +2,18 @@ require 'autoheathen' require 'tempfile' -describe AutoHeathen::Config do +RSpec.describe AutoHeathen::Config do before :all do @clazz = Class.new - @clazz.include AutoHeathen::Config + @clazz.include described_class end - before :each do + + before do @obj = @clazz.new @tempfile = Tempfile.new 'spectest' end - after :each do + after do @tempfile.unlink end diff --git a/spec/lib/autoheathen/email_processor_spec.rb b/spec/lib/autoheathen/email_processor_spec.rb index 52b63e0..4bb4eab 100644 --- a/spec/lib/autoheathen/email_processor_spec.rb +++ b/spec/lib/autoheathen/email_processor_spec.rb @@ -1,9 +1,9 @@ require 'spec_helper' require 'autoheathen' -describe AutoHeathen::EmailProcessor do +RSpec.describe AutoHeathen::EmailProcessor do let(:processor) { - AutoHeathen::EmailProcessor.new({ + described_class.new({ cc_blacklist: ['wikilex@ifad.org'], }, fixture('autoheathen/autoheathen.yml')) } @@ -22,10 +22,10 @@ expect(processor.cfg).to be_a Hash expect(processor.logger).to be_a Logger expect(processor.cfg[:from]).to eq 'noreply@ifad.org' # from config file - expect(processor.cfg[:mail_host]).to_not be_nil - expect(processor.cfg[:mail_port]).to_not be_nil - expect(processor.cfg[:text_template]).to_not be_nil - expect(processor.cfg[:html_template]).to_not be_nil + expect(processor.cfg[:mail_host]).not_to be_nil + expect(processor.cfg[:mail_port]).not_to be_nil + expect(processor.cfg[:text_template]).not_to be_nil + expect(processor.cfg[:html_template]).not_to be_nil end it 'sends email onwards' do @@ -75,7 +75,7 @@ end it 'reads a file' do - expect(processor.read_file('spec/fixtures/autoheathen/autoheathen.yml').to_s).to_not eq '' + expect(processor.read_file('spec/fixtures/autoheathen/autoheathen.yml').to_s).not_to eq '' end it 'validates content types' do diff --git a/spec/lib/config_spec.rb b/spec/lib/config_spec.rb index b84a99a..f452e90 100644 --- a/spec/lib/config_spec.rb +++ b/spec/lib/config_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' require 'config' -describe Colore::C_ do +RSpec.describe Colore::C_ do before do described_class.reset allow(described_class).to receive(:config_file_path) { fixture('app.yml') } @@ -11,7 +11,7 @@ described_class.reset end - context '.config' do + describe '.config' do it 'runs' do expect(described_class.config).to be_a(described_class) expect(described_class.config.storage_directory).to eq 'foo' @@ -23,10 +23,11 @@ end end - context '.method_missing' do + describe '.method_missing' do it 'finds #storage_directory' do expect(described_class.storage_directory).to eq 'foo' end + it 'fails on invalid value' do expect { described_class.foo diff --git a/spec/lib/converter_spec.rb b/spec/lib/converter_spec.rb index d0fcdf9..421e412 100644 --- a/spec/lib/converter_spec.rb +++ b/spec/lib/converter_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Colore::Converter do +RSpec.describe Colore::Converter do let(:storage_dir) { tmp_storage_dir } let(:doc_key) { Colore::DocKey.new('app', '12345') } let(:version) { 'v001' } @@ -13,18 +13,18 @@ before do setup_storage allow(Colore::C_).to receive(:storage_directory) { tmp_storage_dir } - allow(Colore::C_).to receive(:wkhtmltopdf_path) { '/usr/local/bin/wkhtmltopdf' } + allow(Colore::C_).to receive(:wkhtmltopdf_path).and_return('/usr/local/bin/wkhtmltopdf') end after do delete_storage end - context '#convert' do + describe '#convert' do it 'runs' do foo = double(Heathen::Converter) allow(Heathen::Converter).to receive(:new) { foo } - allow(foo).to receive(:convert) { "The quick brown fox" } + allow(foo).to receive(:convert).and_return("The quick brown fox") expect(converter.convert(doc_key, version, filename, action)).to eq new_filename content_type, content = document.get_file version, new_filename expect(content_type).to eq 'text/plain; charset=us-ascii' diff --git a/spec/lib/doc_key_spec.rb b/spec/lib/doc_key_spec.rb index 3a03d96..d56e34b 100644 --- a/spec/lib/doc_key_spec.rb +++ b/spec/lib/doc_key_spec.rb @@ -1,30 +1,31 @@ require 'spec_helper' -describe Colore::DocKey do +RSpec.describe Colore::DocKey do let(:doc_key) { described_class.new('myapp', 'mydoc') } - context '.initialize' do + describe '.initialize' do it 'throws error if app is invalid' do expect { described_class.new 'my app', 'mydoc' }.to raise_error(Colore::InvalidParameter) end + it 'throws error if doc_id is invalid' do expect { described_class.new 'myapp', 'my doc' }.to raise_error(Colore::InvalidParameter) end end - context '#path' do + describe '#path' do it 'runs' do expect(doc_key.path).to be_a Pathname end end - context '#to_s' do + describe '#to_s' do it 'runs' do expect(doc_key.to_s).to eq 'myapp/mydoc' end end - context '#subdirectory' do + describe '#subdirectory' do it 'runs' do expect(doc_key.subdirectory).to eq 'd8' end diff --git a/spec/lib/document_spec.rb b/spec/lib/document_spec.rb index c2f94df..2ca389b 100644 --- a/spec/lib/document_spec.rb +++ b/spec/lib/document_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' require 'colore' -describe Colore::Document do +RSpec.describe Colore::Document do let(:app) { 'app' } let(:doc_id) { '12345' } let(:doc_key) { Colore::DocKey.new(app, doc_id) } @@ -18,28 +18,28 @@ delete_storage end - context '.directory' do + describe '.directory' do it 'runs' do - expect(described_class.directory(storage_dir, doc_key).to_s).to_not be_nil + expect(described_class.directory(storage_dir, doc_key).to_s).not_to be_nil end end - context '.exists?' do + describe '.exists?' do it 'runs' do - expect(described_class.exists?(storage_dir, doc_key)).to eq true + expect(described_class.exists?(storage_dir, doc_key)).to be true end it 'returns false if directory does not exist' do - expect(described_class.exists?(storage_dir, invalid_doc_key)).to eq false + expect(described_class.exists?(storage_dir, invalid_doc_key)).to be false end end - context '.create' do + describe '.create' do it 'runs' do create_key = Colore::DocKey.new('app2', 'foo') doc = described_class.create storage_dir, create_key - expect(doc).to_not be_nil - expect(described_class.exists?(storage_dir, create_key)).to eq true + expect(doc).not_to be_nil + expect(described_class.exists?(storage_dir, create_key)).to be true end it 'raises error if doc already exists' do @@ -49,10 +49,10 @@ end end - context '.load' do + describe '.load' do it 'runs' do doc = described_class.load storage_dir, doc_key - expect(doc).to_not be_nil + expect(doc).not_to be_nil end it 'raises exception if directory does not exist' do @@ -62,28 +62,28 @@ end end - context '.delete' do + describe '.delete' do it 'runs' do - Colore::Document.delete storage_dir, doc_key - expect(Colore::Document.exists?(storage_dir, doc_key)).to eq false + described_class.delete storage_dir, doc_key + expect(described_class.exists?(storage_dir, doc_key)).to be false end end - context '#directory' do + describe '#directory' do it 'runs' do dir = document.directory - expect(dir).to_not be_nil - expect(File.exist?(dir)).to eq true + expect(dir).not_to be_nil + expect(File.exist?(dir)).to be true end end - context '#title' do + describe '#title' do it 'runs' do expect(document.title).to eq 'Sample document' end end - context '#title=' do + describe '#title=' do it 'runs' do document.title = 'New title' new_doc = described_class.load storage_dir, doc_key @@ -97,70 +97,74 @@ end end - context '#versions' do + describe '#versions' do it 'runs' do - expect(document.versions).to match_array ['v001', 'v002'] + expect(document.versions).to contain_exactly('v001', 'v002') end end - context '#has_version?' do + describe '#has_version?' do it 'runs' do - expect(document.has_version?('v001')).to eq true + expect(document.has_version?('v001')).to be true end + it 'accepts current' do - expect(document.has_version?('current')).to eq true + expect(document.has_version?('current')).to be true end + it 'rejects invalid' do - expect(document.has_version?('foo')).to eq false + expect(document.has_version?('foo')).to be false end end - context '#current_version' do + describe '#current_version' do it 'runs' do expect(document.current_version).to eq 'v002' end end - context '#next_version_number' do + describe '#next_version_number' do it 'runs' do expect(document.next_version_number).to eq 'v003' end end - context '#new_version' do + describe '#new_version' do it 'runs' do version = document.new_version - expect(version).to_not be_nil - expect(File.exist?(document.directory + version)).to eq true + expect(version).not_to be_nil + expect(File.exist?(document.directory + version)).to be true new_doc = described_class.load storage_dir, doc_key - expect(new_doc.versions.include?(version)).to eq true + expect(new_doc.versions.include?(version)).to be true end end - context '#add_file' do + describe '#add_file' do it 'runs without author' do file = __FILE__ body = File.read(file) document.add_file 'v002', File.basename(file), body - expect(File.exist?(document.directory + 'v002' + File.basename(file))).to eq true + expect(File.exist?(document.directory + 'v002' + File.basename(file))).to be true end + it 'runs with author' do file = __FILE__ body = File.read(file) document.add_file 'v002', File.basename(file), body, author - expect(File.exist?(document.directory + 'v002' + File.basename(file))).to eq true - expect(File.exist?(document.directory + 'v002' + described_class::AUTHOR_FILE)).to eq true + expect(File.exist?(document.directory + 'v002' + File.basename(file))).to be true + expect(File.exist?(document.directory + 'v002' + described_class::AUTHOR_FILE)).to be true expect(File.read(document.directory + 'v002' + described_class::AUTHOR_FILE).chomp).to eq author end + it 'runs with IO for body' do file = __FILE__ body = File.open(file) document.add_file 'v002', File.basename(file), body - expect(File.exist?(document.directory + 'v002' + File.basename(file))).to eq true + expect(File.exist?(document.directory + 'v002' + File.basename(file))).to be true end end - context '#set_current' do + describe '#set_current' do it 'runs' do document.set_current 'v001' st1 = File.stat(document.directory + 'current') @@ -181,48 +185,54 @@ end end - context '#delete_version' do + describe '#delete_version' do it 'runs' do document.delete_version 'v001' - expect(File.exist?(document.directory + 'v001')).to eq false + expect(File.exist?(document.directory + 'v001')).to be false end + it 'refuses to delete "current"' do expect { document.delete_version Colore::Document::CURRENT }.to raise_error Colore::VersionIsCurrent end + it 'refuses to delete current version' do expect { document.delete_version 'v002' }.to raise_error Colore::VersionIsCurrent end + it 'silently does nothing for an invalid version' do document.delete_version 'foo' end end - context '#file_path' do + describe '#file_path' do it 'runs' do expect(document.file_path('v001', 'arglebargle.docx')).to eq "/document/#{app}/#{doc_id}/v001/arglebargle.docx" end end - context '#get_file' do + describe '#get_file' do it 'runs' do content_type, body = document.get_file 'v001', 'arglebargle.docx' expect(content_type).to eq 'application/vnd.openxmlformats-officedocument.wordprocessingml.document; charset=binary' - expect(body).to_not be_nil + expect(body).not_to be_nil end + it 'runs for current' do content_type, body = document.get_file Colore::Document::CURRENT, 'arglebargle.txt' expect(content_type).to eq 'text/plain; charset=us-ascii' - expect(body).to_not be_nil + expect(body).not_to be_nil end + it 'raises FileNotFound for an invalid version' do expect { document.get_file 'foo', 'arglebargle.txt' }.to raise_error Colore::FileNotFound end + it 'raises FileNotFound for an invalid filename' do expect { document.get_file 'v001', 'text/plain; charset=us-ascii' @@ -230,7 +240,7 @@ end end - context '#to_hash' do + describe '#to_hash' do it 'runs' do testhash = JSON.parse(File.read(fixture('document.json'))) testhash = Colore::Utils.symbolize_keys testhash @@ -253,10 +263,10 @@ end end - context '#save_metadata' do + describe '#save_metadata' do it 'runs' do document.save_metadata - expect(File.exist?(document.directory + 'metadata.json')).to eq true + expect(File.exist?(document.directory + 'metadata.json')).to be true # expect this to pass JSON.parse File.read(document.directory + 'metadata.json') end diff --git a/spec/lib/legacy_converter_spec.rb b/spec/lib/legacy_converter_spec.rb index 14ba0b0..bb217ae 100644 --- a/spec/lib/legacy_converter_spec.rb +++ b/spec/lib/legacy_converter_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Colore::LegacyConverter do +RSpec.describe Colore::LegacyConverter do let(:storage_dir) { tmp_storage_dir } let(:content) { 'The brown fox quits' } let(:new_format) { 'pdf' } @@ -18,27 +18,27 @@ delete_storage end - context '#convert_file' do + describe '#convert_file' do it 'runs' do new_filename = converter.convert_file new_format, content - expect(new_filename).to_not be_nil - expect((storage_dir + new_filename).file?).to eq true + expect(new_filename).not_to be_nil + expect((storage_dir + new_filename).file?).to be true stored_content = File.read(storage_dir + new_filename) expect(stored_content).to eq 'The quick brown fox' end end - context '#store_file' do + describe '#store_file' do it 'runs' do filename = 'foo.txt' content = 'The quick brown fox' converter.store_file filename, content - expect((converter.legacy_dir + filename).file?).to eq true + expect((converter.legacy_dir + filename).file?).to be true expect(File.read(converter.legacy_dir + filename)).to eq content end end - context '#get_file' do + describe '#get_file' do it 'runs' do filename = converter.convert_file new_format, content expect(converter.get_file(File.basename(filename))).to eq 'The quick brown fox' diff --git a/spec/lib/sidekiq_workers_spec.rb b/spec/lib/sidekiq_workers_spec.rb index 1085e22..dec8157 100644 --- a/spec/lib/sidekiq_workers_spec.rb +++ b/spec/lib/sidekiq_workers_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' require 'rest_client' -describe Colore::Sidekiq::ConversionWorker do +RSpec.describe Colore::Sidekiq::ConversionWorker do let(:doc_key) { Colore::DocKey.new('app', '12345') } let(:callback_url) { 'http://foo/bar' } @@ -11,7 +11,7 @@ allow(@mock_converter).to receive(:convert) end - context '#perform' do + describe '#perform' do it 'runs' do expect(Colore::Sidekiq::CallbackWorker).to receive(:perform_async) described_class.new.perform doc_key.to_s, 'current', 'arglebargle.docx', 'pdf', callback_url @@ -19,29 +19,32 @@ it 'gives up on Heathen::TaskNotFound' do allow(@mock_converter).to receive(:convert) { raise Heathen::TaskNotFound.new('foo', 'bar') } - expect(Colore::Sidekiq::CallbackWorker).to receive(:perform_async) {} + expect(Colore::Sidekiq::CallbackWorker).to receive(:perform_async).and_return(nil) described_class.new.perform doc_key.to_s, 'current', 'arglebargle.docx', 'pdf', callback_url end it 'gives up on other errors' do allow(@mock_converter).to receive(:convert) { raise 'arglebargle' } - expect(Colore::Sidekiq::CallbackWorker).to receive(:perform_async) {} + expect(Colore::Sidekiq::CallbackWorker).to receive(:perform_async).and_return(nil) described_class.new.perform doc_key.to_s, 'current', 'arglebargle.docx', 'pdf', callback_url end end end -describe Colore::Sidekiq::CallbackWorker do +RSpec.describe Colore::Sidekiq::CallbackWorker do let(:doc_key) { Colore::DocKey.new('app', '12345') } let(:callback_url) { 'http://foo/bar' } + before do setup_storage allow(Colore::C_).to receive(:storage_directory) { tmp_storage_dir } end + after do delete_storage end - context '#perform' do + + describe '#perform' do it 'runs' do expect(RestClient).to receive(:post).with(callback_url, Hash) described_class.new.perform doc_key.to_s, 'current', 'arglebargle.docx', 'pdf', callback_url, 250, 'foobar' @@ -49,16 +52,18 @@ end end -describe Colore::Sidekiq::LegacyPurgeWorker do +RSpec.describe Colore::Sidekiq::LegacyPurgeWorker do before do setup_storage allow(Colore::C_).to receive(:storage_directory) { tmp_storage_dir } - allow(Colore::C_).to receive(:legacy_purge_days) { 2 } + allow(Colore::C_).to receive(:legacy_purge_days).and_return(2) end + after do delete_storage end - context '#perform' do + + describe '#perform' do it 'runs' do dir = Colore::LegacyConverter.new.legacy_dir file1 = dir + 'file1.tiff' @@ -66,16 +71,16 @@ file1.open('w') { |f| f.write 'foobar' } file2.open('w') { |f| f.write 'foobar' } described_class.new.perform - expect(file1.file?).to eq true - expect(file2.file?).to eq true + expect(file1.file?).to be true + expect(file2.file?).to be true Timecop.freeze(Date.today + 1) described_class.new.perform - expect(file1.file?).to eq true - expect(file2.file?).to eq true + expect(file1.file?).to be true + expect(file2.file?).to be true Timecop.freeze(Date.today + 3) described_class.new.perform - expect(file1.file?).to eq false - expect(file2.file?).to eq false + expect(file1.file?).to be false + expect(file2.file?).to be false end end end diff --git a/spec/lib/utils_spec.rb b/spec/lib/utils_spec.rb index 2b7e8dd..7766910 100644 --- a/spec/lib/utils_spec.rb +++ b/spec/lib/utils_spec.rb @@ -1,10 +1,10 @@ require 'spec_helper' require 'utils' -describe Colore::Utils do +RSpec.describe Colore::Utils do include described_class - context '#symbolize_keys' do + describe '#symbolize_keys' do it 'symbolizes hash' do h = { name: 'Fred', @@ -37,7 +37,7 @@ end it 'symbolizes array' do - expect(symbolize_keys([1234, 'fred'])).to match_array [1234, 'fred'] + expect(symbolize_keys([1234, 'fred'])).to contain_exactly(1234, 'fred') end it 'symbolizes fixnum' do diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 0c0b9e9..1ddba2a 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -12,41 +12,131 @@ ENV['RACK_ENV'] = 'test' -require 'pathname' -require 'fileutils' -require 'logger' +require 'rspec' + require 'byebug' +require 'logger' require 'rack/test' require 'sidekiq/testing' require 'simplecov' require 'timecop' +require 'colore' + Sidekiq.logger = nil -SPEC_BASE = Pathname.new(__FILE__).realpath.parent +def fixture(fixture_name) + file_fixture_path = "#{File.expand_path(__dir__)}/fixtures" + path = Pathname.new(File.join(file_fixture_path, fixture_name)) -$: << SPEC_BASE.parent + 'lib' -require 'colore' - -def fixture(name) - SPEC_BASE + 'fixtures' + name + if path.exist? + path + else + msg = "the directory '%s' does not contain a file named '%s'" + raise ArgumentError.new(format(msg, file_fixture_path, fixture_name)) + end end def spec_logger Logger.new('spec/output.log') end -Dir.glob((SPEC_BASE + "helpers" + "**.rb").to_s).each do |helper| - require helper -end +Dir.glob("#{File.expand_path(__dir__)}/helpers/**/*.rb").sort.each { |f| require f } -module RSpecMixin - include Rack::Test::Methods - def app() described_class end -end +# This file was generated by the `rspec --init` command. Conventionally, all +# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. +# The generated `.rspec` file contains `--require spec_helper` which will cause +# this file to always be loaded, without a need to explicitly require it in any +# files. +# +# Given that it is always loaded, you are encouraged to keep this file as +# light-weight as possible. Requiring heavyweight dependencies from this file +# will add to the boot time of your test suite on EVERY test run, even for an +# individual file that may not need all of that loaded. Instead, consider making +# a separate helper file that requires the additional dependencies and performs +# the additional setup, and require it from the spec files that actually need +# it. +# +# See https://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration +RSpec.configure do |config| + # rspec-expectations config goes here. You can use an alternate + # assertion/expectation library such as wrong or the stdlib/minitest + # assertions if you prefer. + config.expect_with :rspec do |expectations| + # This option will default to `true` in RSpec 4. It makes the `description` + # and `failure_message` of custom matchers include text for helper methods + # defined using `chain`, e.g.: + # be_bigger_than(2).and_smaller_than(4).description + # # => "be bigger than 2 and smaller than 4" + # ...rather than: + # # => "be bigger than 2" + expectations.include_chain_clauses_in_custom_matcher_descriptions = true + end + + # rspec-mocks config goes here. You can use an alternate test double + # library (such as bogus or mocha) by changing the `mock_with` option here. + config.mock_with :rspec do |mocks| + # Prevents you from mocking or stubbing a method that does not exist on + # a real object. This is generally recommended, and will default to + # `true` in RSpec 4. + # TODO: enable this + # mocks.verify_partial_doubles = true + end + + # This option will default to `:apply_to_host_groups` in RSpec 4 (and will + # have no way to turn it off -- the option exists only for backwards + # compatibility in RSpec 3). It causes shared context metadata to be + # inherited by the metadata hash of host groups and examples, rather than + # triggering implicit auto-inclusion in groups with matching metadata. + config.shared_context_metadata_behavior = :apply_to_host_groups + + # This allows you to limit a spec run to individual examples or groups + # you care about by tagging them with `:focus` metadata. When nothing + # is tagged with `:focus`, all examples get run. RSpec also provides + # aliases for `it`, `describe`, and `context` that include `:focus` + # metadata: `fit`, `fdescribe` and `fcontext`, respectively. + config.filter_run_when_matching :focus + + # Allows RSpec to persist some state between runs in order to support + # the `--only-failures` and `--next-failure` CLI options. We recommend + # you configure your source control system to ignore this file. + config.example_status_persistence_file_path = 'spec/examples.txt' + + # Limits the available syntax to the non-monkey patched syntax that is + # recommended. For more details, see: + # https://rspec.info/features/3-12/rspec-core/configuration/zero-monkey-patching-mode/ + config.disable_monkey_patching! + + # This setting enables warnings. It's recommended, but in some cases may + # be too noisy due to issues in dependencies. + # TODO: enable this + config.warnings = false + + # Many RSpec users commonly either run the entire suite or an individual + # file, and it's useful to allow more verbose output when running an + # individual spec file. + if config.files_to_run.one? + # Use the documentation formatter for detailed output, + # unless a formatter has already been configured + # (e.g. via a command-line flag). + config.default_formatter = 'doc' + end + + # Print the 10 slowest examples and example groups at the + # end of the spec run, to help surface which specs are running + # particularly slow. + config.profile_examples = 10 + + # Run specs in random order to surface order dependencies. If you find an + # order dependency and want to debug it, you can fix the order by providing + # the seed, which is printed after each run. + # --seed 1234 + # TODO: enable this + # config.order = :random -RSpec::configure do |rspec| - rspec.tty = true - rspec.color = true - rspec.include RSpecMixin + # Seed global randomization in this process using the `--seed` CLI option. + # Setting this allows you to use `--seed` to deterministically reproduce + # test failures related to randomization by passing the same `--seed` value + # as the one that triggered the failure. + Kernel.srand config.seed end