diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index fd8c8708..c8bc1998 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -77,12 +77,38 @@ jobs: run: bundle exec rspec env: COVERAGE: 1 + - name: Upload coverage results + uses: actions/upload-artifact@v4 + with: + include-hidden-files: true + name: coverage-results + path: coverage + retention-days: 1 - name: Upload code coverage to Code Climate run: | ./cc-test-reporter after-build \ --coverage-input-type simplecov \ ./coverage/.resultset.json + coverage-check: + permissions: + checks: write + needs: test + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Download coverage results + uses: actions/download-artifact@v4 + with: + name: coverage-results + path: coverage + - uses: joshmfrankel/simplecov-check-action@be89e11889202cc59efb14aab2a7091622fa9aad + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + minimum_suite_coverage: 100 + minimum_file_coverage: 100 + coverage_json_path: coverage/simplecov-check-action.json + rubocop: runs-on: ubuntu-latest steps: diff --git a/.rubocop.yml b/.rubocop.yml index 007b2a28..4bc466f9 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -36,6 +36,9 @@ Layout/CaseIndentation: - end IndentOneStep: true +Layout/FirstArrayElementIndentation: + EnforcedStyle: consistent + Layout/EndAlignment: EnforcedStyleAlignWith: variable diff --git a/lib/pundit/context.rb b/lib/pundit/context.rb index 3a60c9b9..d906f74b 100644 --- a/lib/pundit/context.rb +++ b/lib/pundit/context.rb @@ -118,7 +118,6 @@ def policy_scope(scope) # @return [Scope{#resolve}] instance of scope class which can resolve to a scope def policy_scope!(scope) policy_scope_class = policy_finder(scope).scope! - return unless policy_scope_class begin policy_scope = policy_scope_class.new(user, pundit_model(scope)) diff --git a/lib/pundit/rspec.rb b/lib/pundit/rspec.rb index a6c44030..fb44ba60 100644 --- a/lib/pundit/rspec.rb +++ b/lib/pundit/rspec.rb @@ -43,15 +43,21 @@ def description(user, record) end failure_message_proc = lambda do |policy| - was_were = @violating_permissions.count > 1 ? "were" : "was" "Expected #{policy} to grant #{permissions.to_sentence} on " \ - "#{record} but #{@violating_permissions.to_sentence} #{was_were} not granted" + "#{record} but #{@violating_permissions.to_sentence} #{was_or_were} not granted" end failure_message_when_negated_proc = lambda do |policy| - was_were = @violating_permissions.count > 1 ? "were" : "was" "Expected #{policy} not to grant #{permissions.to_sentence} on " \ - "#{record} but #{@violating_permissions.to_sentence} #{was_were} granted" + "#{record} but #{@violating_permissions.to_sentence} #{was_or_were} granted" + end + + def was_or_were + if @violating_permissions.count > 1 + "were" + else + "was" + end end description do @@ -64,14 +70,29 @@ def description(user, record) failure_message(&failure_message_proc) failure_message_when_negated(&failure_message_when_negated_proc) else + # :nocov: + # Compatibility with RSpec < 3.0, released 2014-06-01. match_for_should(&match_proc) match_for_should_not(&match_when_negated_proc) failure_message_for_should(&failure_message_proc) failure_message_for_should_not(&failure_message_when_negated_proc) + # :nocov: + end + + if ::RSpec.respond_to?(:current_example) + def current_example + ::RSpec.current_example + end + else + # :nocov: + # Compatibility with RSpec < 3.0, released 2014-06-01. + def current_example + example + end + # :nocov: end def permissions - current_example = ::RSpec.respond_to?(:current_example) ? ::RSpec.current_example : example current_example.metadata[:permissions] end end diff --git a/spec/dsl_spec.rb b/spec/rspec_dsl_spec.rb similarity index 89% rename from spec/dsl_spec.rb rename to spec/rspec_dsl_spec.rb index 2119f1ae..c1f65dae 100644 --- a/spec/dsl_spec.rb +++ b/spec/rspec_dsl_spec.rb @@ -36,7 +36,7 @@ end describe "#permit" do - permissions :update? do + permissions :edit?, :update? do it "succeeds when action is permitted" do expect(policy).to permit(user, post) end @@ -46,7 +46,7 @@ expect do expect(policy).to permit(other_user, post) end.to raise_error(RSpec::Expectations::ExpectationNotMetError, <<~MSG.strip) - Expected PostPolicy to grant update? on Post but update? was not granted + Expected PostPolicy to grant edit? and update? on Post but edit? and update? were not granted MSG end end @@ -61,7 +61,7 @@ expect do expect(policy).not_to permit(user, post) end.to raise_error(RSpec::Expectations::ExpectationNotMetError, <<~MSG.strip) - Expected PostPolicy not to grant update? on Post but update? was granted + Expected PostPolicy not to grant edit? and update? on Post but edit? and update? were granted MSG end end diff --git a/spec/simple_cov_check_action_formatter.rb b/spec/simple_cov_check_action_formatter.rb new file mode 100644 index 00000000..d7dff62c --- /dev/null +++ b/spec/simple_cov_check_action_formatter.rb @@ -0,0 +1,79 @@ +# frozen_string_literal: true + +require "simplecov" +require "json" + +class SimpleCovCheckActionFormatter + SourceFile = Data.define(:source_file) do + def covered_strength = source_file.covered_strength + def covered_percent = source_file.covered_percent + + def to_json(*) + { + filename: source_file.filename, + covered_percent: covered_percent.nan? ? 0.0 : covered_percent, + coverage: source_file.coverage_data, + covered_strength: covered_strength.nan? ? 0.0 : covered_strength, + covered_lines: source_file.covered_lines.count, + lines_of_code: source_file.lines_of_code + }.to_json + end + end + + Result = Data.define(:result) do + def included?(source_file) = result.filenames.include?(source_file.filename) + + def files + result.files.filter_map do |source_file| + next unless result.filenames.include? source_file.filename + + SourceFile.new(source_file) + end + end + + def to_json(*) # rubocop:disable Metrics/AbcSize + { + timestamp: result.created_at.to_i, + command_name: result.command_name, + files: files, + metrics: { + covered_percent: result.covered_percent, + covered_strength: result.covered_strength.nan? ? 0.0 : result.covered_strength, + covered_lines: result.covered_lines, + total_lines: result.total_lines + } + }.to_json + end + end + + FormatterWithOptions = Data.define(:formatter) do + def new = formatter + end + + class << self + def with_options(...) + FormatterWithOptions.new(new(...)) + end + end + + def initialize(output_filename: "coverage.json", output_directory: SimpleCov.coverage_path) + @output_filename = output_filename + @output_directory = output_directory + end + + attr_reader :output_filename, :output_directory + + def output_filepath = File.join(output_directory, output_filename) + + def format(result_data) + result = Result.new(result_data) + json = result.to_json + File.write(output_filepath, json) + puts output_message(result_data) + json + end + + def output_message(result) + "Coverage report generated for #{result.command_name} to #{output_filepath}. #{result.covered_lines} / #{result.total_lines} LOC (#{result.covered_percent.round(2)}%) covered." # rubocop:disable Layout/LineLength + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index d1c4be00..6d63c514 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -2,8 +2,19 @@ if ENV["COVERAGE"] require "simplecov" + require "simplecov_json_formatter" + require_relative "simple_cov_check_action_formatter" + SimpleCov.formatters = SimpleCov::Formatter::MultiFormatter.new([ + SimpleCov::Formatter::HTMLFormatter, + SimpleCov::Formatter::JSONFormatter, + SimpleCovCheckActionFormatter.with_options( + output_filename: "simplecov-check-action.json" + ) + ]) SimpleCov.start do add_filter "/spec/" + enable_coverage :branch + primary_coverage :branch end end diff --git a/spec/support/policies/post_policy.rb b/spec/support/policies/post_policy.rb index baf8df7a..b68fce3c 100644 --- a/spec/support/policies/post_policy.rb +++ b/spec/support/policies/post_policy.rb @@ -12,6 +12,7 @@ def resolve def update? post.user == user end + alias edit? update? def destroy? false