Skip to content

Commit

Permalink
Make code/test coverage a thing we care about
Browse files Browse the repository at this point in the history
It's not perfect, but it is something.

Full line coverage, full branch coverage, checked for every PR.
  • Loading branch information
Burgestrand committed Oct 11, 2024
1 parent fb00037 commit e296e83
Show file tree
Hide file tree
Showing 8 changed files with 149 additions and 9 deletions.
26 changes: 26 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
3 changes: 3 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ Layout/CaseIndentation:
- end
IndentOneStep: true

Layout/FirstArrayElementIndentation:
EnforcedStyle: consistent

Layout/EndAlignment:
EnforcedStyleAlignWith: variable

Expand Down
1 change: 0 additions & 1 deletion lib/pundit/context.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
31 changes: 26 additions & 5 deletions lib/pundit/rspec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
6 changes: 3 additions & 3 deletions spec/dsl_spec.rb → spec/rspec_dsl_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down
79 changes: 79 additions & 0 deletions spec/simple_cov_check_action_formatter.rb
Original file line number Diff line number Diff line change
@@ -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(*args)
{
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(*args)
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(*args) # 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(*args)
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 = JSON.generate(result)
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
11 changes: 11 additions & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
1 change: 1 addition & 0 deletions spec/support/policies/post_policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ def resolve
def update?
post.user == user
end
alias edit? update?

def destroy?
false
Expand Down

0 comments on commit e296e83

Please sign in to comment.