diff --git a/spec/coverage_reporter/cli/cmd_spec.cr b/spec/coverage_reporter/cli/cmd_spec.cr index 6c03632d..9159d3ee 100644 --- a/spec/coverage_reporter/cli/cmd_spec.cr +++ b/spec/coverage_reporter/cli/cmd_spec.cr @@ -2,189 +2,307 @@ require "../../spec_helper" require "../../../src/coverage_reporter/cli/cmd" Spectator.describe CoverageReporter::Cli do - subject { described_class } + subject { described_class.run(options, reporter) } + + let(reporter) { ReporterMock.new } describe ".run" do - it "applies defaults" do - reporter = subject.run %w(--dry-run) + context "defaults" do + let(options) { %w(--dry-run) } - expect(reporter.fail_empty).to eq true - expect(reporter.dry_run).to eq true - expect(reporter.parallel).to eq false + it "applies defaults" do + expect(subject).to eq 0 + expect(reporter.fail_empty).to eq true + expect(reporter.dry_run).to eq true + expect(reporter.parallel).to eq false + end end - it "parses overrides" do - reporter = subject.run %w(--service-name overriden --dry-run --no-logo) + context "with overrides" do + let(options) { %w(--service-name overriden --dry-run --no-logo) } - expect(reporter.dry_run).to eq true - expect(reporter.overrides.try(&.to_h)).to eq({ - :service_name => "overriden", - }) + it "parses overrides" do + expect(subject).to eq 0 + expect(reporter.dry_run).to eq true + expect(reporter.overrides.try(&.to_h)).to eq({ + :service_name => "overriden", + }) + end end - it "parses overrides" do - reporter = subject.run %w( - report - --parallel - -j super-flag - --base-path src/* - --service-name=service-name - --service-job-id=job-id - --service-build-url=build-url - --service-job-url=job-url - --service-branch=branch - --service-pull-request=pr - --build-number=build-number - --compare-ref=develop - --dry-run - ) - - expect(reporter.job_flag_name).to eq "super-flag" - expect(reporter.parallel).to eq true - expect(reporter.compare_ref).to eq "develop" - expect(reporter.dry_run).to eq true - expect(reporter.base_path).to eq "src/*" - expect(reporter.overrides.try(&.to_h)).to eq({ - :service_name => "service-name", - :service_number => "build-number", - :service_job_id => "job-id", - :service_build_url => "build-url", - :service_job_url => "job-url", - :service_branch => "branch", - :service_pull_request => "pr", - }) + context "with more overrides" do + let(options) do + %w( + report + --parallel + -j super-flag + --base-path src/* + --service-name=service-name + --service-job-id=job-id + --service-build-url=build-url + --service-job-url=job-url + --service-branch=branch + --service-pull-request=pr + --build-number=build-number + --compare-ref=develop + --dry-run + ) + end + + it "parses overrides" do + expect(subject).to eq 0 + expect(reporter.job_flag_name).to eq "super-flag" + expect(reporter.parallel).to eq true + expect(reporter.compare_ref).to eq "develop" + expect(reporter.dry_run).to eq true + expect(reporter.base_path).to eq "src/*" + expect(reporter.overrides.try(&.to_h)).to eq({ + :service_name => "service-name", + :service_number => "build-number", + :service_job_id => "job-id", + :service_build_url => "build-url", + :service_job_url => "job-url", + :service_branch => "branch", + :service_pull_request => "pr", + }) + end + end + + context "with new args" do + let(options) do + %w( + report + --parallel + -j super-flag + --base-path src/* + --service-name=service-name + --job-id=job-id + --build-url=build-url + --job-url=job-url + --branch=branch + --pull-request=pr + --build-number=build-number + --compare-ref=develop + --attempt=4 + --dry-run + ) + end + + it "parses overrides (new args)" do + expect(subject).to eq 0 + expect(reporter.job_flag_name).to eq "super-flag" + expect(reporter.parallel).to eq true + expect(reporter.compare_ref).to eq "develop" + expect(reporter.dry_run).to eq true + expect(reporter.base_path).to eq "src/*" + expect(reporter.overrides.try(&.to_h)).to eq({ + :service_name => "service-name", + :service_number => "build-number", + :service_job_id => "job-id", + :service_build_url => "build-url", + :service_job_url => "job-url", + :service_branch => "branch", + :service_pull_request => "pr", + :service_attempt => "4", + }) + end end - it "parses overrides (new args)" do - reporter = subject.run %w( - report - --parallel - -j super-flag - --base-path src/* - --service-name=service-name - --job-id=job-id - --build-url=build-url - --job-url=job-url - --branch=branch - --pull-request=pr - --build-number=build-number - --compare-ref=develop - --attempt=4 - --dry-run - ) - - expect(reporter.job_flag_name).to eq "super-flag" - expect(reporter.parallel).to eq true - expect(reporter.compare_ref).to eq "develop" - expect(reporter.dry_run).to eq true - expect(reporter.base_path).to eq "src/*" - expect(reporter.overrides.try(&.to_h)).to eq({ - :service_name => "service-name", - :service_number => "build-number", - :service_job_id => "job-id", - :service_build_url => "build-url", - :service_job_url => "job-url", - :service_branch => "branch", - :service_pull_request => "pr", - :service_attempt => "4", - }) + context "with empty values" do + let(options) do + %w( + --service-name= + --service-job-id= + --service-build-url= + --service-job-url= + --service-branch= + --service-pull-request= + --done + --dry-run + ) + end + it "doesn't apply empty values as overrides" do + expect(subject).to eq 0 + expect(reporter.dry_run).to eq true + # expect(reporter.overrides.try(&.to_h)).to eq({} of Symbol => String) + end end - it "doesn't apply empty values as overrides" do - reporter = subject.run %w( - --service-name= - --service-job-id= - --service-build-url= - --service-job-url= - --service-branch= - --service-pull-request= - --done - --dry-run - ) - - expect(reporter.dry_run).to eq true - expect(reporter.overrides.try(&.to_h)).to eq({} of Symbol => String) + context "with allow empty option" do + let(options) do + %w( + --allow-empty + --dry-run + ) + end + it "accepts --allow-empty option" do + expect(subject).to eq 0 + expect(reporter.fail_empty).to eq false + end end - it "accepts --allow-empty option" do - reporter = subject.run %w( - --allow-empty - --dry-run - ) + context "with carryforward option" do + let(options) do + %w( + --carryforward 1,2,3 + --dry-run + ) + end - expect(reporter.fail_empty).to eq false + it "accepts --carryforward option" do + expect(subject).to eq 0 + expect(reporter.carryforward).to eq "1,2,3" + end end - it "accepts --carryforward option" do - reporter = subject.run %w( - --carryforward 1,2,3 - --dry-run - ) + context "with carryforward (new args)" do + let(options) do + %w( + done + --build-number 3 + --carryforward 1,2,3 + --dry-run + ) + end - expect(reporter.carryforward).to eq "1,2,3" + it "accepts --carryforward option" do + expect(subject).to eq 0 + expect(reporter.carryforward).to eq "1,2,3" + expect(reporter.overrides.try(&.to_h)).to eq({ + :service_number => "3", + }) + end end - it "accepts --carryforward option" do - reporter = subject.run %w( - done - --build-number 3 - --carryforward 1,2,3 - --dry-run - ) - - expect(reporter.carryforward).to eq "1,2,3" - expect(reporter.overrides.try(&.to_h)).to eq({ - :service_number => "3", - }) + context "with format option" do + let(options) do + %w( + --format lcov + --dry-run + ) + end + + it "accepts --format option" do + expect(subject).to eq 0 + expect(reporter.coverage_format).to eq "lcov" + end end - it "accepts --format option" do - reporter = subject.run %w( - --format lcov - --dry-run - ) + context "with filename option" do + let(options) do + %w( + --file spec/fixtures/lcov/test.lcov + --dry-run + ) + end - expect(reporter.coverage_format).to eq "lcov" + it "accepts --filename option" do + expect(subject).to eq 0 + expect(reporter.coverage_files).to eq ["spec/fixtures/lcov/test.lcov"] + end end - it "accepts --filename option" do - reporter = subject.run %w( - --file spec/fixtures/lcov/test.lcov - --dry-run - ) + context "with multiple files report" do + let(options) do + %w( + report + spec/fixtures/lcov/test.lcov + spec/fixtures/lcov/test.lcov + spec/fixtures/lcov/empty.lcov + --dry-run + ) + end - expect(reporter.coverage_files).to eq ["spec/fixtures/lcov/test.lcov"] + it "reports multiple files" do + expect(subject).to eq 0 + expect(reporter.coverage_files).to eq [ + "spec/fixtures/lcov/test.lcov", + "spec/fixtures/lcov/empty.lcov", + ] + end end - it "reports multiple files" do - reporter = subject.run %w( - report - spec/fixtures/lcov/test.lcov - spec/fixtures/lcov/test.lcov - spec/fixtures/lcov/empty.lcov - --dry-run - ) - - expect(reporter.coverage_files).to eq [ - "spec/fixtures/lcov/test.lcov", - "spec/fixtures/lcov/empty.lcov", - ] + context "with -- separator" do + let(options) do + %w( + report + --dry-run + -- + spec/fixtures/lcov/test.lcov + spec/fixtures/lcov/test.lcov + spec/fixtures/lcov/empty.lcov + ) + end + + it "reports multiple files after --" do + expect(subject).to eq 0 + expect(reporter.coverage_files).to eq [ + "spec/fixtures/lcov/test.lcov", + "spec/fixtures/lcov/empty.lcov", + ] + end end - it "reports multiple files after --" do - reporter = subject.run %w( - report - --dry-run - -- - spec/fixtures/lcov/test.lcov - spec/fixtures/lcov/test.lcov - spec/fixtures/lcov/empty.lcov - ) - - expect(reporter.coverage_files).to eq [ - "spec/fixtures/lcov/test.lcov", - "spec/fixtures/lcov/empty.lcov", - ] + context "when raises an error" do + mock ReporterMock + + let(reporter) { mock(ReporterMock) } + let(options) { %w(report) } + + context "internal server error" do + let(error) do + CoverageReporter::Api::InternalServerError.new(HTTP::Client::Response.new(500, "")) + end + + it "returns 1" do + allow(reporter).to receive(:report).and_raise(error) + expect(subject).to eq 1 + end + end + + context "unprocessable entity" do + let(error) do + CoverageReporter::Api::UnprocessableEntity.new(HTTP::Client::Response.new(422, "")) + end + + it "returns 1" do + allow(reporter).to receive(:report).and_raise(error) + expect(subject).to eq 1 + end + end + + context "other HTTP error" do + let(error) do + CoverageReporter::Api::HTTPError.new(HTTP::Client::Response.new(403, "")) + end + + it "returns 1" do + allow(reporter).to receive(:report).and_raise(error) + expect(subject).to eq 1 + end + end + + context "internal logic error" do + let(error) do + CoverageReporter::BaseException.new + end + + it "returns 1" do + allow(reporter).to receive(:report).and_raise(error) + expect(subject).to eq 1 + end + end + + context "internal logic error (with don't fail flag)" do + let(error) do + CoverageReporter::BaseException.new(fail: false) + end + + it "returns 1" do + allow(reporter).to receive(:report).and_raise(error) + expect(subject).to eq 0 + end + end end end end diff --git a/spec/spec_helper.cr b/spec/spec_helper.cr index f5146093..3c33c0ca 100644 --- a/spec/spec_helper.cr +++ b/spec/spec_helper.cr @@ -1,4 +1,5 @@ require "../src/coverage_reporter" +require "./support/*" require "webmock" require "spectator" @@ -9,6 +10,6 @@ Spectator.configure do |config| config.before_suite do ENV.clear - CoverageReporter::Log.set(CoverageReporter::Log::Level::Error) + CoverageReporter::Log.set(CoverageReporter::Log::Level::Suppress) end end diff --git a/spec/support/reporter_mock.cr b/spec/support/reporter_mock.cr new file mode 100644 index 00000000..be66f52f --- /dev/null +++ b/spec/support/reporter_mock.cr @@ -0,0 +1,28 @@ +class ReporterMock + alias Settings = String | Bool | Nil | Array(String) | CoverageReporter::CI::Options + getter settings : Hash(Symbol, Settings) + + def initialize(*args, **kwargs) + @settings = {} of Symbol => Settings + end + + def configure(*args, **kwargs) + @settings = kwargs.to_h + end + + def report + end + + def parallel_done + end + + def overrides : CoverageReporter::CI::Options + settings[:overrides].as(CoverageReporter::CI::Options) + end + + macro method_missing(key) + def {{ key.id }} + settings[{{ key.symbolize }}] + end + end +end diff --git a/src/cli.cr b/src/cli.cr index efd8d5d5..9ed65213 100644 --- a/src/cli.cr +++ b/src/cli.cr @@ -1,3 +1,3 @@ require "./coverage_reporter/cli/cmd" -CoverageReporter::Cli.run +exit CoverageReporter::Cli.run diff --git a/src/coverage_reporter/cli/cmd.cr b/src/coverage_reporter/cli/cmd.cr index 02d99d3e..9652a9be 100644 --- a/src/coverage_reporter/cli/cmd.cr +++ b/src/coverage_reporter/cli/cmd.cr @@ -5,11 +5,11 @@ require "colorize" module CoverageReporter::Cli extend self - def run(args = ARGV) + def run(args = ARGV, reporter = CoverageReporter::Reporter.new) opts = parse_args(args) greet(opts.no_logo?) - reporter = CoverageReporter::Reporter.new( + reporter.configure( base_path: opts.base_path, carryforward: opts.carryforward, compare_ref: opts.compare_ref, @@ -31,22 +31,22 @@ module CoverageReporter::Cli reporter.report end - reporter + 0 rescue ex : BaseException Log.error ex.message - exit(ex.fail? ? 1 : 0) + ex.fail? ? 1 : 0 rescue ex : Socket::Error Log.error ex.message - exit 1 + 1 rescue ex : ArgumentError Log.error <<-ERROR Oops! #{ex.message} Coveralls Coverage Reporter v#{CoverageReporter::VERSION} ERROR - exit 1 + 1 rescue ex : Api::InternalServerError Log.error "⚠️ Internal server error. Please contact Coveralls team." - exit 1 + 1 rescue ex : Api::UnprocessableEntity Log.error <<-ERROR --- @@ -58,7 +58,7 @@ module CoverageReporter::Cli More info/troubleshooting here: https://docs.coveralls.io - 💛, Coveralls ERROR - exit 1 + 1 rescue ex : Api::HTTPError Log.error <<-ERROR HTTP error: @@ -67,12 +67,12 @@ module CoverageReporter::Cli Message: #{ex.response} --- ERROR - exit 1 + 1 rescue ex raise(ex) if opts.try(&.debug?) Log.error ex.inspect - exit 1 + 1 end private class Opts diff --git a/src/coverage_reporter/log.cr b/src/coverage_reporter/log.cr index fcf55323..db005bfa 100644 --- a/src/coverage_reporter/log.cr +++ b/src/coverage_reporter/log.cr @@ -9,6 +9,7 @@ module CoverageReporter YELLOW = Colorize::Color256.new(220) # ffaf00 enum Level + Suppress Error Warning Info diff --git a/src/coverage_reporter/reporter.cr b/src/coverage_reporter/reporter.cr index dd5a226b..00fa97c1 100644 --- a/src/coverage_reporter/reporter.cr +++ b/src/coverage_reporter/reporter.cr @@ -2,19 +2,38 @@ require "./*" module CoverageReporter class Reporter - getter base_path, - carryforward, - compare_ref, - compare_sha, - config_path, - coverage_files, - coverage_format, - dry_run, - fail_empty, - job_flag_name, - overrides, - parallel, - repo_token + struct Settings + getter base_path, + carryforward, + compare_ref, + compare_sha, + config_path, + coverage_files, + coverage_format, + dry_run, + fail_empty, + job_flag_name, + overrides, + parallel, + repo_token + + def initialize( + @base_path : String? = nil, + @carryforward : String? = nil, + @compare_ref : String? = nil, + @compare_sha : String? = nil, + @config_path : String? = nil, + @coverage_files : Array(String) | Nil = nil, + @coverage_format : String? = nil, + @dry_run : Bool = false, + @fail_empty : Bool = false, + @job_flag_name : String? = nil, + @overrides : CI::Options? = nil, + @parallel : Bool = false, + @repo_token : String? = nil + ) + end + end class NoSourceFiles < BaseException def message @@ -22,21 +41,14 @@ module CoverageReporter end end - def initialize( - @base_path : String?, - @carryforward : String?, - @compare_ref : String?, - @compare_sha : String?, - @config_path : String?, - @coverage_files : Array(String) | Nil, - @coverage_format : String?, - @dry_run : Bool, - @fail_empty : Bool, - @job_flag_name : String?, - @overrides : CI::Options?, - @parallel : Bool, - @repo_token : String? - ) + getter settings : Settings + + def initialize(*args, **kwargs) + @settings = Settings.new(*args, **kwargs) + end + + def configure(*args, **kwargs) + initialize(*args, **kwargs) end # Parses the coverage reports in the current directory or the given *coverage_file* @@ -46,15 +58,15 @@ module CoverageReporter # current directory will be searched for all supported report formats. def report source_files = Parser.new( - coverage_files: coverage_files, - coverage_format: coverage_format, - base_path: base_path, + coverage_files: settings.coverage_files, + coverage_format: settings.coverage_format, + base_path: settings.base_path, ).parse - raise NoSourceFiles.new(fail_empty) unless source_files.size > 0 + raise NoSourceFiles.new(settings.fail_empty) unless source_files.size > 0 - api = Api::Jobs.new(config, parallel, source_files, Git.info(config)) + api = Api::Jobs.new(config, settings.parallel, source_files, Git.info(config)) - api.send_request(dry_run) + api.send_request(settings.dry_run) end # Reports that all parallel jobs were reported and Coveralls can aggregate @@ -64,19 +76,19 @@ module CoverageReporter # from a CI-specific ENV, or can be set explicitly via `COVERALLS_SERVICE_NUMBER` # environment variable. def parallel_done - api = Api::Webhook.new(config, carryforward || config.carryforward) + api = Api::Webhook.new(config, settings.carryforward || config.carryforward) - api.send_request(dry_run) + api.send_request(settings.dry_run) end private def config @config ||= Config.new( - repo_token: dry_run ? "dry-run" : repo_token, - flag_name: job_flag_name, - compare_ref: compare_ref, - compare_sha: compare_sha, - path: config_path, - overrides: overrides, + repo_token: settings.dry_run ? "dry-run" : settings.repo_token, + flag_name: settings.job_flag_name, + compare_ref: settings.compare_ref, + compare_sha: settings.compare_sha, + path: settings.config_path, + overrides: settings.overrides, ) end end