diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7dc2a5a..371af99 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,6 +6,7 @@ on: branches: [ "main" ] jobs: test: + permissions: write-all runs-on: ubuntu-latest services: postgres: @@ -21,7 +22,7 @@ jobs: DATABASE_URL: "postgres://rails:password@localhost:5432/rails_test" steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v2 # Add or replace dependency steps here - name: Install Ruby and gems uses: ruby/setup-ruby@v1 @@ -31,20 +32,9 @@ jobs: - name: Set up database schema run: bin/rails db:schema:load # Add or replace test runners here - - name: Run specs + - name: Run Tests run: bin/rspec - - lint: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v3 - - name: Install Ruby and gems - uses: ruby/setup-ruby@v1 - with: - bundler-cache: true - # Add or replace any other lints here - - name: Security audit application code - run: bin/brakeman - - name: Lint Ruby files + - name: Ruby Linter run: bin/rubocop --parallel + - name: Brakeman Scan + run: bin/brakeman diff --git a/.gitignore b/.gitignore index f84af9b..1590ec9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,26 +1,10 @@ -# See https://help.github.com/articles/ignoring-files for more about ignoring files. -# -# If you find yourself ignoring temporary files generated by your text editor -# or operating system, you probably want to add a global ignore instead: -# git config --global core.excludesfile '~/.gitignore_global' - -# Ignore bundler config. /.bundle - -# Ignore all environment files (except templates). /.env* !/.env*.erb - -# Ignore all logfiles and tempfiles. +/coverage/* /log/* /tmp/* - -# Ignore storage (uploaded files in development and any SQLite databases). /storage/* - /public/assets - -# Ignore master key for decrypting credentials and more. /config/master.key - .byebug_history diff --git a/.simplecov b/.simplecov new file mode 100644 index 0000000..386ceef --- /dev/null +++ b/.simplecov @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +require 'simplecov' + +SimpleCov.start 'rails' do + minimum_coverage 95 +end diff --git a/Gemfile b/Gemfile index e566c39..3fac26b 100644 --- a/Gemfile +++ b/Gemfile @@ -21,6 +21,7 @@ group :development, :test do end group :development do + gem 'solargraph' gem 'web-console' end @@ -35,5 +36,6 @@ group :test do gem 'rubocop-rails', require: false gem 'rubocop-rspec', require: false gem 'shoulda-matchers' + gem 'simplecov', require: false gem 'webdrivers' end diff --git a/Gemfile.lock b/Gemfile.lock index ac4f2ca..fdaf78f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -105,7 +105,9 @@ GEM addressable (2.8.6) public_suffix (>= 2.0.2, < 6.0) ast (2.4.2) + backport (1.2.0) base64 (0.2.0) + benchmark (0.3.0) bigdecimal (3.1.5) bindex (0.8.1) bootsnap (1.17.0) @@ -128,8 +130,10 @@ GEM crass (1.0.6) date (3.3.4) diff-lcs (1.5.0) + docile (1.4.0) drb (2.2.0) ruby2_keywords + e2mmap (0.1.0) erubi (1.12.0) factory_bot (6.4.4) activesupport (>= 5.0.0) @@ -148,10 +152,15 @@ GEM irb (1.11.0) rdoc reline (>= 0.3.8) + jaro_winkler (1.5.6) jbuilder (2.11.5) actionview (>= 5.0.0) activesupport (>= 5.0.0) json (2.7.1) + kramdown (2.4.0) + rexml + kramdown-parser-gfm (1.1.0) + kramdown (~> 2.0) language_server-protocol (3.17.0.3) loofah (2.22.0) crass (~> 1.0.2) @@ -212,11 +221,14 @@ GEM nokogiri (~> 1.14) rainbow (3.1.1) rake (13.1.0) + rbs (2.8.4) rdoc (6.6.2) psych (>= 4.0.0) regexp_parser (2.8.3) reline (0.4.1) io-console (~> 0.5) + reverse_markdown (2.1.1) + nokogiri rexml (3.2.6) rspec-core (3.12.2) rspec-support (~> 3.12.0) @@ -273,6 +285,28 @@ GEM websocket (~> 1.0) shoulda-matchers (6.0.0) activesupport (>= 5.2.0) + simplecov (0.22.0) + docile (~> 1.1) + simplecov-html (~> 0.11) + simplecov_json_formatter (~> 0.1) + simplecov-html (0.12.3) + simplecov_json_formatter (0.1.4) + solargraph (0.50.0) + backport (~> 1.2) + benchmark + bundler (~> 2.0) + diff-lcs (~> 1.4) + e2mmap + jaro_winkler (~> 1.5) + kramdown (~> 2.3) + kramdown-parser-gfm (~> 1.1) + parser (~> 3.0) + rbs (~> 2.0) + reverse_markdown (~> 2.0) + rubocop (~> 1.38) + thor (~> 1.0) + tilt (~> 2.0) + yard (~> 0.9, >= 0.9.24) sprockets (4.2.1) concurrent-ruby (~> 1.0) rack (>= 2.2.4, < 4) @@ -284,6 +318,7 @@ GEM railties (>= 6.0.0) stringio (3.1.0) thor (1.3.0) + tilt (2.3.0) timeout (0.4.1) turbo-rails (1.5.0) actionpack (>= 6.0.0) @@ -308,6 +343,7 @@ GEM websocket-extensions (0.1.5) xpath (3.2.0) nokogiri (~> 1.8) + yard (0.9.34) zeitwerk (2.6.12) PLATFORMS @@ -334,6 +370,8 @@ DEPENDENCIES rubocop-rails rubocop-rspec shoulda-matchers + simplecov + solargraph sprockets-rails stimulus-rails turbo-rails diff --git a/app/channels/application_cable/channel.rb b/app/channels/application_cable/channel.rb index 9aec230..9ca9b45 100644 --- a/app/channels/application_cable/channel.rb +++ b/app/channels/application_cable/channel.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true +# :nocov: Remove once code is added. module ApplicationCable class Channel < ActionCable::Channel::Base end end +# :nocov: diff --git a/app/channels/application_cable/connection.rb b/app/channels/application_cable/connection.rb index 8d6c2a1..305e207 100644 --- a/app/channels/application_cable/connection.rb +++ b/app/channels/application_cable/connection.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true +# :nocov: Remove once code is added. module ApplicationCable class Connection < ActionCable::Connection::Base end end +# :nocov: diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 15b06f0..a9728ba 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -1,4 +1,6 @@ # frozen_string_literal: true +# :nocov: Remove once code is added. module ApplicationHelper end +# :nocov: diff --git a/app/helpers/categories_helper.rb b/app/helpers/categories_helper.rb index 46512be..509dbdd 100644 --- a/app/helpers/categories_helper.rb +++ b/app/helpers/categories_helper.rb @@ -1,4 +1,6 @@ # frozen_string_literal: true +# :nocov: Remove once code is added. module CategoriesHelper end +# :nocov: diff --git a/app/helpers/statements_helper.rb b/app/helpers/statements_helper.rb new file mode 100644 index 0000000..d41b844 --- /dev/null +++ b/app/helpers/statements_helper.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +# :nocov: Remove once code is added. +module StatementsHelper +end +# :nocov: diff --git a/app/helpers/transactions_helper.rb b/app/helpers/transactions_helper.rb index 42b1f30..e219241 100644 --- a/app/helpers/transactions_helper.rb +++ b/app/helpers/transactions_helper.rb @@ -1,4 +1,6 @@ # frozen_string_literal: true +# :nocov: Remove once code is added. module TransactionsHelper end +# :nocov: diff --git a/app/jobs/application_job.rb b/app/jobs/application_job.rb index bef3959..73b6411 100644 --- a/app/jobs/application_job.rb +++ b/app/jobs/application_job.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true +# :nocov: Remove once code is added. class ApplicationJob < ActiveJob::Base # Automatically retry jobs that encountered a deadlock # retry_on ActiveRecord::Deadlocked @@ -7,3 +8,4 @@ class ApplicationJob < ActiveJob::Base # Most jobs are safe to ignore if the underlying records are no longer available # discard_on ActiveJob::DeserializationError end +# :nocov: diff --git a/app/mailers/application_mailer.rb b/app/mailers/application_mailer.rb index d84cb6e..5a1f583 100644 --- a/app/mailers/application_mailer.rb +++ b/app/mailers/application_mailer.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true +# :nocov: Remove once code is added. class ApplicationMailer < ActionMailer::Base default from: 'from@example.com' layout 'mailer' end +# :nocov: diff --git a/bin/ci b/bin/ci index 6dd454c..22e83b5 100755 --- a/bin/ci +++ b/bin/ci @@ -22,11 +22,16 @@ Dir.chdir(APP_ROOT) do brakeman_passed = system("bin/brakeman") terminal_message("Done.\n\n") + terminal_message("Checking Code Coverage...") + coverage_passed = system("bin/coverage") + terminal_message("Done.\n\n") + pass_fail(ruby_tests_passed, "Ruby Tests") pass_fail(ruby_linting_passed, "Ruby Linter") pass_fail(brakeman_passed, "Brakeman Scan") + pass_fail(coverage_passed, "Code Coverage") - return 1 unless ruby_tests_passed && ruby_linting_passed && brakeman_passed + return 1 unless ruby_tests_passed && ruby_linting_passed && brakeman_passed && coverage_passed return 0 end diff --git a/bin/coverage b/bin/coverage new file mode 100755 index 0000000..e6cd8bc --- /dev/null +++ b/bin/coverage @@ -0,0 +1,21 @@ +#!/usr/bin/env ruby +require 'json' + +MINIMUM_COVERAGE = 95 + +coverage_report_path = "#{Dir.pwd}/coverage/.last_run.json" +if File.exist?(coverage_report_path) + coverage_data = JSON.parse(File.read(coverage_report_path)) + coverage_percentage = coverage_data['result']['line'] + + if coverage_percentage >= MINIMUM_COVERAGE + puts "🥳 Awesome coverage (#{coverage_percentage}%)!\n" + exit 0 + else + puts "😥 Coverage insufficient (#{coverage_percentage}%).\n" + exit 1 + end +else + puts "Coverage report not found.\n" + exit 1 +end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 99ebdaf..5533447 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -7,6 +7,8 @@ # Prevent database truncation if the environment is production abort('The Rails environment is running in production mode!') if Rails.env.production? require 'rspec/rails' +require 'simplecov' + # Add additional requires below this line. Rails is not loaded until this point! # Requires supporting ruby files with custom matchers and macros, etc, in