diff --git a/.idea/jsLibraryMappings.xml b/.idea/jsLibraryMappings.xml index d23208f..3d2e5dd 100644 --- a/.idea/jsLibraryMappings.xml +++ b/.idea/jsLibraryMappings.xml @@ -1,6 +1,942 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.rubocop.yml b/.rubocop.yml index e816d99..e2b6db3 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,4 +1,7 @@ -plugins: rubocop-rails +plugins: + - rubocop-rails + - rubocop-rspec + - rubocop-rspec_rails AllCops: UseCache: false @@ -294,6 +297,8 @@ Rails/NegateInclude: # new in 2.7 Enabled: true Rails/Pluck: # new in 2.7 Enabled: true + Exclude: + - 'spec/**/*' Rails/PluckInWhere: # new in 2.7 Enabled: true Rails/RenderInline: # new in 2.7 @@ -594,3 +599,152 @@ Rails/WhereNotWithMultipleConditions: # new in 2.17 Enabled: true Rails/WhereRange: # new in 2.25 Enabled: true +RSpec/IncludeExamples: # new in 3.6 + Enabled: true +RSpec/LeakyLocalVariable: # new in 3.8 + Enabled: true +RSpecRails/HaveHttpStatus: # new in 2.12 + Enabled: true +RSpecRails/MinitestAssertions: # new in 2.17 + Enabled: true +RSpecRails/NegationBeValid: # new in 2.23 + Enabled: true +RSpecRails/TravelAround: # new in 2.19 + Enabled: true + +############################################################ +# rubocop-rspec + +# believe me, it wasn't by choice +RSpec/AnyInstance: + Enabled: false + +RSpec/VerifiedDoubleReference: + Enabled: false + +# we meant to do that +RSpec/BeforeAfterAll: + Enabled: false + +RSpec/ContainExactly: + Enabled: false + +# more words != more readable +RSpec/ContextWording: + Enabled: false + +# not everything we test is a class +RSpec/DescribeClass: + Enabled: false + +# explicit >>> implicit +RSpec/DescribedClass: + Enabled: false + +# more punctuation != more readable +RSpec/DescribeSymbol: + Enabled: false + +# setup cost / time >>> failure granularity +RSpec/ExampleLength: + Max: 25 + CountAsOne: + - array + - hash + - heredoc + +# we meant to do that +RSpec/ExpectInHook: + Enabled: false + +RSpec/SpecFilePathFormat: + Enabled: false + +RSpec/SpecFilePathSuffix: + Enabled: false + +# Requests are not inferred properly without explicit typing. +# Similarly, system tests don't pick up Selenium without explicit typing. +RSpecRails/InferredSpecType: + Enabled: true + Exclude: + - 'spec/requests/*.rb' + - 'spec/system/*.rb' + +# explicit >>> implicit +RSpec/InstanceVariable: + Enabled: false + +# maybe when 'all' has a corresponding 'none' matcher +RSpec/IteratedExpectation: + Enabled: false + +# we meant to do that +RSpec/MessageSpies: + Enabled: false + +# too late now +RSpec/MultipleMemoizedHelpers: + Enabled: false + +# setup cost / time >>> failure granularity +RSpec/MultipleExpectations: + Enabled: false + +# cure is worse than the disease +RSpec/NestedGroups: + Enabled: false + +# more quotation marks != more readable +RSpec/SharedExamples: + Enabled: false + +# we meant to do that +RSpec/StubbedMock: + Enabled: false + +# we meant to do that +RSpec/VerifiedDoubles: + Enabled: false + +############################################################ +# rubocop-rspec + +# enable newer rubocop-rspec cops + +RSpec/IdenticalEqualityAssertion: # new in 2.4 + Enabled: true + +RSpecRails/AvoidSetupHook: # new in 2.4 + Enabled: true + +# Sometimes we're testing the operator +Lint/BinaryOperatorWithIdenticalOperands: + Exclude: + - 'spec/**/*' + +Metrics/AbcSize: + Exclude: + - 'spec/**/*' + +Metrics/BlockLength: + Exclude: + - 'spec/**/*' + +Metrics/ModuleLength: + Exclude: + - 'spec/**/*' + +Metrics/MethodLength: + Exclude: + - 'spec/**/*' + +# we meant to do that +Rails/SkipsModelValidations: + Exclude: + - 'spec/**/*' + +# we meant to do that +Rails/TimeZone: + Exclude: + - 'spec/**/*' \ No newline at end of file diff --git a/.yarnrc b/.yarnrc index cd6a666..c81d7dc 100644 --- a/.yarnrc +++ b/.yarnrc @@ -2,4 +2,4 @@ # yarn lockfile v1 -lastUpdateCheck 1780439312657 +lastUpdateCheck 1782867282598 diff --git a/Dockerfile b/Dockerfile index 1749531..b231ec5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,7 +4,7 @@ # The base stage scaffolds elements which are common to building and running # the application, such as installing ca-certificates, creating the app user, # and installing runtime system dependencies. -FROM ruby:3.3-slim AS base +FROM ruby:3.4-slim AS base # ------------------------------------------------------------ # Declarative metadata diff --git a/Gemfile b/Gemfile index cffeeda..026f5c3 100644 --- a/Gemfile +++ b/Gemfile @@ -1,7 +1,7 @@ source 'https://rubygems.org' git_source(:github) { |repo| "https://github.com/#{repo}.git" } -ruby '~> 3.3' +ruby '~> 3.4' gem 'addressable', '~> 2.8' gem 'awesome_print', '~> 1.9' @@ -28,14 +28,14 @@ gem 'pg', '~> 1.2' gem 'pg_search', '~> 2.3' gem 'puma', '~> 7.2' gem 'puma-plugin-delayed_stop', '~> 0.1.2' -gem 'rails', '~> 8.0.5' +gem 'rails', '~> 8.1.3' gem 'ruby-vips', '~> 2.3' gem 'sprockets-rails', '~> 3.5.0' group :development, :test do gem 'brakeman' gem 'bundle-audit' - gem 'byebug', platforms: %i[mri mingw x64_mingw] + gem 'byebug', platforms: %i[mri windows] gem 'colorize' gem 'factory_bot_rails' gem 'rspec-rails', '~> 8.0' diff --git a/Gemfile.lock b/Gemfile.lock index b25def1..e943661 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,29 +1,31 @@ GEM remote: https://rubygems.org/ specs: - actioncable (8.0.5) - actionpack (= 8.0.5) - activesupport (= 8.0.5) + action_text-trix (2.1.19) + railties + actioncable (8.1.3) + actionpack (= 8.1.3) + activesupport (= 8.1.3) nio4r (~> 2.0) websocket-driver (>= 0.6.1) zeitwerk (~> 2.6) - actionmailbox (8.0.5) - actionpack (= 8.0.5) - activejob (= 8.0.5) - activerecord (= 8.0.5) - activestorage (= 8.0.5) - activesupport (= 8.0.5) + actionmailbox (8.1.3) + actionpack (= 8.1.3) + activejob (= 8.1.3) + activerecord (= 8.1.3) + activestorage (= 8.1.3) + activesupport (= 8.1.3) mail (>= 2.8.0) - actionmailer (8.0.5) - actionpack (= 8.0.5) - actionview (= 8.0.5) - activejob (= 8.0.5) - activesupport (= 8.0.5) + actionmailer (8.1.3) + actionpack (= 8.1.3) + actionview (= 8.1.3) + activejob (= 8.1.3) + activesupport (= 8.1.3) mail (>= 2.8.0) rails-dom-testing (~> 2.2) - actionpack (8.0.5) - actionview (= 8.0.5) - activesupport (= 8.0.5) + actionpack (8.1.3) + actionview (= 8.1.3) + activesupport (= 8.1.3) nokogiri (>= 1.8.5) rack (>= 2.2.4) rack-session (>= 1.0.1) @@ -31,42 +33,43 @@ GEM rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) useragent (~> 0.16) - actiontext (8.0.5) - actionpack (= 8.0.5) - activerecord (= 8.0.5) - activestorage (= 8.0.5) - activesupport (= 8.0.5) + actiontext (8.1.3) + action_text-trix (~> 2.1.15) + actionpack (= 8.1.3) + activerecord (= 8.1.3) + activestorage (= 8.1.3) + activesupport (= 8.1.3) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (8.0.5) - activesupport (= 8.0.5) + actionview (8.1.3) + activesupport (= 8.1.3) builder (~> 3.1) erubi (~> 1.11) rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) - activejob (8.0.5) - activesupport (= 8.0.5) + activejob (8.1.3) + activesupport (= 8.1.3) globalid (>= 0.3.6) - activemodel (8.0.5) - activesupport (= 8.0.5) - activerecord (8.0.5) - activemodel (= 8.0.5) - activesupport (= 8.0.5) + activemodel (8.1.3) + activesupport (= 8.1.3) + activerecord (8.1.3) + activemodel (= 8.1.3) + activesupport (= 8.1.3) timeout (>= 0.4.0) - activestorage (8.0.5) - actionpack (= 8.0.5) - activejob (= 8.0.5) - activerecord (= 8.0.5) - activesupport (= 8.0.5) + activestorage (8.1.3) + actionpack (= 8.1.3) + activejob (= 8.1.3) + activerecord (= 8.1.3) + activesupport (= 8.1.3) marcel (~> 1.0) - activesupport (8.0.5) + activesupport (8.1.3) base64 - benchmark (>= 0.3) bigdecimal concurrent-ruby (~> 1.0, >= 1.3.1) connection_pool (>= 2.2.5) drb i18n (>= 1.6, < 2) + json logger (>= 1.4.2) minitest (>= 5.1) securerandom (>= 0.3) @@ -99,7 +102,7 @@ GEM berkeley_library-logging (~> 0.3) rest-client (~> 2.1) typesafe_enum (~> 0.3) - bigdecimal (4.0.1) + bigdecimal (4.1.2) bindex (0.8.1) brakeman (8.0.4) racc @@ -121,12 +124,12 @@ GEM regexp_parser (>= 1.5, < 3.0) xpath (~> 3.2) colorize (1.1.0) - concurrent-ruby (1.3.6) + concurrent-ruby (1.3.7) connection_pool (3.0.2) crack (1.0.1) bigdecimal rexml - crass (1.0.6) + crass (1.0.7) cssbundling-rails (1.4.3) railties (>= 6.0.0) csv (3.3.5) @@ -150,7 +153,7 @@ GEM factory_bot_rails (6.5.1) factory_bot (~> 6.5) railties (>= 6.1.0) - faraday (2.14.2) + faraday (2.14.3) faraday-net_http (>= 2.0, < 3.5) json logger @@ -162,7 +165,7 @@ GEM foreman (0.90.0) thor (~> 1.4) geo_coord (0.2.0) - globalid (1.3.0) + globalid (1.4.0) activesupport (>= 6.1) hashdiff (1.2.1) hashie (5.1.0) @@ -170,7 +173,7 @@ GEM http-accept (1.7.0) http-cookie (1.1.0) domain_name (~> 0.5) - i18n (1.14.8) + i18n (1.15.2) concurrent-ruby (~> 1.0) iiif-presentation (1.4.2) activesupport (>= 3.2.18) @@ -178,7 +181,7 @@ GEM geo_coord json io-console (0.8.2) - irb (1.17.0) + irb (1.18.0) pp (>= 0.6.0) prism (>= 1.3.0) rdoc (>= 4.0.0) @@ -188,7 +191,7 @@ GEM activesupport (>= 7.0.0) jsbundling-rails (1.3.1) railties (>= 6.0.0) - json (2.19.7) + json (2.20.0) jwt (2.10.3) base64 language_server-protocol (3.17.0.5) @@ -222,13 +225,13 @@ GEM mime-types-data (~> 3.2025, >= 3.2025.0507) mime-types-data (3.2026.0224) mini_mime (1.1.5) - minitest (6.0.2) + minitest (6.0.6) drb (~> 2.0) prism (~> 1.5) mutex_m (0.3.0) net-http (0.9.1) uri (>= 0.11.1) - net-imap (0.6.4) + net-imap (0.6.4.1) date net-protocol net-pop (0.1.2) @@ -239,14 +242,14 @@ GEM net-protocol netrc (0.11.0) nio4r (2.7.5) - nokogiri (1.19.3-aarch64-linux-gnu) + nokogiri (1.19.4-aarch64-linux-gnu) racc (~> 1.4) - nokogiri (1.19.3-arm64-darwin) + nokogiri (1.19.4-arm64-darwin) racc (~> 1.4) - nokogiri (1.19.3-x86_64-linux-gnu) + nokogiri (1.19.4-x86_64-linux-gnu) racc (~> 1.4) observer (0.1.2) - oj (3.16.15) + oj (3.17.3) bigdecimal (>= 3.0) ostruct (>= 0.2) okcomputer (1.19.1) @@ -281,15 +284,12 @@ GEM pg_search (2.3.7) activerecord (>= 6.1) activesupport (>= 6.1) - pp (0.6.3) + pp (0.6.4) prettyprint prettyprint (0.2.0) prism (1.9.0) - psych (5.3.1) - date - stringio public_suffix (7.0.5) - puma (7.2.0) + puma (7.2.1) nio4r (~> 2.0) puma-plugin-delayed_stop (0.1.2) puma (>= 5.0, < 8) @@ -306,20 +306,20 @@ GEM rack (>= 1.3) rackup (2.3.1) rack (>= 3) - rails (8.0.5) - actioncable (= 8.0.5) - actionmailbox (= 8.0.5) - actionmailer (= 8.0.5) - actionpack (= 8.0.5) - actiontext (= 8.0.5) - actionview (= 8.0.5) - activejob (= 8.0.5) - activemodel (= 8.0.5) - activerecord (= 8.0.5) - activestorage (= 8.0.5) - activesupport (= 8.0.5) + rails (8.1.3) + actioncable (= 8.1.3) + actionmailbox (= 8.1.3) + actionmailer (= 8.1.3) + actionpack (= 8.1.3) + actiontext (= 8.1.3) + actionview (= 8.1.3) + activejob (= 8.1.3) + activemodel (= 8.1.3) + activerecord (= 8.1.3) + activestorage (= 8.1.3) + activesupport (= 8.1.3) bundler (>= 1.15.0) - railties (= 8.0.5) + railties (= 8.1.3) rails-dom-testing (2.3.0) activesupport (>= 5.0.0) minitest @@ -327,9 +327,9 @@ GEM rails-html-sanitizer (1.7.0) loofah (~> 2.25) nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0) - railties (8.0.5) - actionpack (= 8.0.5) - activesupport (= 8.0.5) + railties (8.1.3) + actionpack (= 8.1.3) + activesupport (= 8.1.3) irb (~> 1.13) rackup (>= 1.0.0) rake (>= 12.2) @@ -337,13 +337,18 @@ GEM tsort (>= 0.2) zeitwerk (~> 2.6) rainbow (3.1.1) - rake (13.3.1) + rake (13.4.2) rb-fsevent (0.11.2) rb-inotify (0.11.1) ffi (~> 1.0) - rdoc (7.2.0) + rbs (4.0.3) + logger + prism (>= 1.6.0) + tsort + rdoc (8.0.0) erb - psych (>= 4.0.0) + prism (>= 1.6.0) + rbs (>= 4.0.0) tsort regexp_parser (2.11.3) reline (0.6.3) @@ -441,9 +446,8 @@ GEM actionpack (>= 6.1) activesupport (>= 6.1) sprockets (>= 3.0.0) - stringio (3.2.0) thor (1.5.0) - timeout (0.6.0) + timeout (0.6.1) tsort (0.2.0) typesafe_enum (0.3.1) tzinfo (2.0.6) @@ -462,14 +466,14 @@ GEM crack (>= 0.3.2) hashdiff (>= 0.4.0, < 2.0.0) websocket (1.2.11) - websocket-driver (0.8.0) + websocket-driver (0.8.2) base64 websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) xpath (3.2.0) nokogiri (~> 1.8) yaml (0.4.0) - zeitwerk (2.7.5) + zeitwerk (2.8.2) PLATFORMS aarch64-linux-gnu @@ -513,7 +517,7 @@ DEPENDENCIES pg_search (~> 2.3) puma (~> 7.2) puma-plugin-delayed_stop (~> 0.1.2) - rails (~> 8.0.5) + rails (~> 8.1.3) rspec (~> 3.10) rspec-rails (~> 8.0) rspec_junit_formatter (~> 0.5) @@ -531,7 +535,7 @@ DEPENDENCIES webmock RUBY VERSION - ruby 3.3.10p183 + ruby 3.4.9 BUNDLED WITH - 2.5.22 + 4.0.15 diff --git a/bin/brakeman b/bin/brakeman new file mode 100755 index 0000000..ace1c9b --- /dev/null +++ b/bin/brakeman @@ -0,0 +1,7 @@ +#!/usr/bin/env ruby +require "rubygems" +require "bundler/setup" + +ARGV.unshift("--ensure-latest") + +load Gem.bin_path("brakeman", "brakeman") diff --git a/bin/bundler-audit b/bin/bundler-audit new file mode 100755 index 0000000..e2ef226 --- /dev/null +++ b/bin/bundler-audit @@ -0,0 +1,6 @@ +#!/usr/bin/env ruby +require_relative "../config/boot" +require "bundler/audit/cli" + +ARGV.concat %w[ --config config/bundler-audit.yml ] if ARGV.empty? || ARGV.include?("check") +Bundler::Audit::CLI.start diff --git a/bin/ci b/bin/ci new file mode 100755 index 0000000..4137ad5 --- /dev/null +++ b/bin/ci @@ -0,0 +1,6 @@ +#!/usr/bin/env ruby +require_relative "../config/boot" +require "active_support/continuous_integration" + +CI = ActiveSupport::ContinuousIntegration +require_relative "../config/ci.rb" diff --git a/bin/dev b/bin/dev index 1ca73ac..52088a3 100755 --- a/bin/dev +++ b/bin/dev @@ -1,4 +1,2 @@ -#!/usr/bin/env bash -# bin/dev: run Rails and JS bundler together in development - -exec foreman start -f Procfile.dev "$@" +#!/usr/bin/env ruby +exec './bin/rails', 'server', *ARGV diff --git a/bin/lending/_zeitwerk.rb b/bin/lending/_zeitwerk.rb old mode 100644 new mode 100755 diff --git a/bin/rubocop b/bin/rubocop new file mode 100755 index 0000000..5a20504 --- /dev/null +++ b/bin/rubocop @@ -0,0 +1,8 @@ +#!/usr/bin/env ruby +require "rubygems" +require "bundler/setup" + +# Explicit RuboCop config increases performance slightly while avoiding config confusion. +ARGV.unshift("--config", File.expand_path("../.rubocop.yml", __dir__)) + +load Gem.bin_path("rubocop", "rubocop") diff --git a/bin/setup b/bin/setup index be3db3c..81be011 100755 --- a/bin/setup +++ b/bin/setup @@ -22,6 +22,7 @@ FileUtils.chdir APP_ROOT do puts "\n== Preparing database ==" system! "bin/rails db:prepare" + system! "bin/rails db:reset" if ARGV.include?("--reset") puts "\n== Removing old logs and tempfiles ==" system! "bin/rails log:clear tmp:clear" diff --git a/config/application.rb b/config/application.rb index 0cf1595..59d64e7 100644 --- a/config/application.rb +++ b/config/application.rb @@ -20,7 +20,7 @@ module UCBEARS class Application < Rails::Application # Initialize configuration defaults for originally generated Rails version. - config.load_defaults 8.0 + config.load_defaults 8.1 # Please, add to the `ignore` list any other `lib` subdirectories that do # not contain `.rb` files, or that should not be reloaded or eager loaded. diff --git a/config/bundler-audit.yml b/config/bundler-audit.yml new file mode 100644 index 0000000..e74b3af --- /dev/null +++ b/config/bundler-audit.yml @@ -0,0 +1,5 @@ +# Audit all gems listed in the Gemfile for known security problems by running bin/bundler-audit. +# CVEs that are not relevant to the application can be enumerated on the ignore list below. + +ignore: + - CVE-THAT-DOES-NOT-APPLY diff --git a/config/ci.rb b/config/ci.rb new file mode 100644 index 0000000..f0bb2b8 --- /dev/null +++ b/config/ci.rb @@ -0,0 +1,19 @@ +# Run using bin/ci + +CI.run do + step 'Setup', 'bin/setup --skip-server' + + step 'Style: Ruby', 'bin/rubocop' + + step 'Security: Gem audit', 'bin/bundler-audit' + step 'Security: Importmap vulnerability audit', 'bin/importmap audit' + step 'Security: Brakeman code analysis', 'bin/brakeman --quiet --no-pager --exit-on-warn --exit-on-error' + + # Optional: set a green GitHub commit status to unblock PR merge. + # Requires the `gh` CLI and `gh extension install basecamp/gh-signoff`. + # if success? + # step 'Signoff: All systems go. Ready for merge and deploy.', 'gh signoff' + # else + # failure 'Signoff: CI failed. Do not merge or deploy.', 'Fix the issues and try again.' + # end +end diff --git a/config/environments/development.rb b/config/environments/development.rb index 883b0ba..7a930a7 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -1,5 +1,6 @@ require 'active_support/core_ext/integer/time' +# rubocop:disable Metrics/BlockLength Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb. @@ -45,6 +46,9 @@ # Highlight code that enqueued background job in logs. config.active_job.verbose_enqueue_logs = true + # Highlight code that triggered redirect in logs. + config.action_dispatch.verbose_redirect_logs = true + # Allow cross-origin requests in development. config.action_dispatch.default_headers['Access-Control-Allow-Origin'] = '*' @@ -69,3 +73,4 @@ # Apply autocorrection by RuboCop to files generated by `bin/rails generate`. # config.generators.apply_rubocop_autocorrect_after_generate! end +# rubocop:enable Metrics/BlockLength diff --git a/config/environments/production.rb b/config/environments/production.rb index bb42154..01b143c 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -15,9 +15,6 @@ # Turn on fragment caching in view templates. config.action_controller.perform_caching = true - # Serve static files from the app server. - config.public_file_server.enabled = true - # Do not fallback to assets pipeline if a precompiled asset is missed. config.assets.compile = false @@ -41,7 +38,7 @@ config.log_tags = [:request_id] - # Change to "debug" to log everything (including potentially personally-identifiable information!) + # Change to "debug" to log everything (including potentially personally-identifiable information!). config.log_level = ENV.fetch('RAILS_LOG_LEVEL', 'info') # Prevent health checks from clogging up the logs. diff --git a/config/initializers/content_security_policy.rb b/config/initializers/content_security_policy.rb new file mode 100644 index 0000000..d51d713 --- /dev/null +++ b/config/initializers/content_security_policy.rb @@ -0,0 +1,29 @@ +# Be sure to restart your server when you modify this file. + +# Define an application-wide content security policy. +# See the Securing Rails Applications Guide for more information: +# https://guides.rubyonrails.org/security.html#content-security-policy-header + +# Rails.application.configure do +# config.content_security_policy do |policy| +# policy.default_src :self, :https +# policy.font_src :self, :https, :data +# policy.img_src :self, :https, :data +# policy.object_src :none +# policy.script_src :self, :https +# policy.style_src :self, :https +# # Specify URI for violation reports +# # policy.report_uri "/csp-violation-report-endpoint" +# end +# +# # Generate session nonces for permitted importmap, inline scripts, and inline styles. +# config.content_security_policy_nonce_generator = ->(request) { request.session.id.to_s } +# config.content_security_policy_nonce_directives = %w(script-src style-src) +# +# # Automatically add `nonce` to `javascript_tag`, `javascript_include_tag`, and `stylesheet_link_tag` +# # if the corresponding directives are specified in `content_security_policy_nonce_directives`. +# # config.content_security_policy_nonce_auto = true +# +# # Report violations without enforcing the policy. +# # config.content_security_policy_report_only = true +# end diff --git a/config/puma.rb b/config/puma.rb index 2c9cfb3..7fee6bb 100644 --- a/config/puma.rb +++ b/config/puma.rb @@ -7,7 +7,8 @@ # # You can control the number of workers using ENV["WEB_CONCURRENCY"]. You # should only set this value when you want to run 2 or more workers. The -# default is already 1. +# default is already 1. You can set it to `auto` to automatically start a worker +# for each available processor. # # The ideal number of threads per worker depends both on how much time the # application spends waiting for IO operations and on how much you wish to diff --git a/config/routes.rb b/config/routes.rb index b5d99c7..cbb322b 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,3 +1,4 @@ +# rubocop:disable Metrics/BlockLength Rails.application.routes.draw do root 'sessions#index' @@ -48,3 +49,4 @@ # Map OkComputer's /health/all.json to /health get '/health', to: 'ok_computer/ok_computer#index', defaults: { format: :json } end +# rubocop:enable Metrics/BlockLength diff --git a/public/400.html b/public/400.html index 282dbc8..640de03 100644 --- a/public/400.html +++ b/public/400.html @@ -35,12 +35,35 @@ font-weight: 400; letter-spacing: -0.0025em; line-height: 1.4; - min-height: 100vh; + min-height: 100dvh; place-items: center; text-rendering: optimizeLegibility; -webkit-text-size-adjust: 100%; } + #error-description { + fill: #d30001; + } + + #error-id { + fill: #f0eff0; + } + + @media (prefers-color-scheme: dark) { + body { + background: #101010; + color: #e0e0e0; + } + + #error-description { + fill: #FF6161; + } + + #error-id { + fill: #2c2c2c; + } + } + a { color: inherit; font-weight: 700; @@ -83,13 +106,11 @@ } main article br { - display: none; @media(min-width: 48em) { display: inline; } - } @@ -102,10 +123,10 @@
- +
-

The server cannot process the request due to a client error. Please check the request and try again. If you’re the application owner check the logs for more information.

+

The server cannot process the request due to a client error. Please check the request and try again. If you're the application owner check the logs for more information.

diff --git a/public/404.html b/public/404.html index c0670bc..d7f0f14 100644 --- a/public/404.html +++ b/public/404.html @@ -4,7 +4,7 @@ - The page you were looking for doesn’t exist (404 Not found) + The page you were looking for doesn't exist (404 Not found) @@ -35,12 +35,35 @@ font-weight: 400; letter-spacing: -0.0025em; line-height: 1.4; - min-height: 100vh; + min-height: 100dvh; place-items: center; text-rendering: optimizeLegibility; -webkit-text-size-adjust: 100%; } + #error-description { + fill: #d30001; + } + + #error-id { + fill: #f0eff0; + } + + @media (prefers-color-scheme: dark) { + body { + background: #101010; + color: #e0e0e0; + } + + #error-description { + fill: #FF6161; + } + + #error-id { + fill: #2c2c2c; + } + } + a { color: inherit; font-weight: 700; @@ -83,13 +106,11 @@ } main article br { - display: none; @media(min-width: 48em) { display: inline; } - } @@ -102,10 +123,10 @@
- +
-

The page you were looking for doesn’t exist. You may have mistyped the address or the page may have moved. If you’re the application owner check the logs for more information.

+

The page you were looking for doesn't exist. You may have mistyped the address or the page may have moved. If you're the application owner check the logs for more information.

diff --git a/public/406-unsupported-browser.html b/public/406-unsupported-browser.html new file mode 100644 index 0000000..43d2811 --- /dev/null +++ b/public/406-unsupported-browser.html @@ -0,0 +1,135 @@ + + + + + + + Your browser is not supported (406 Not Acceptable) + + + + + + + + + + + + + +
+
+ +
+
+

Your browser is not supported.
Please upgrade your browser to continue.

+
+
+ + + + diff --git a/public/422.html b/public/422.html index 8bcf060..f12fb4a 100644 --- a/public/422.html +++ b/public/422.html @@ -35,12 +35,35 @@ font-weight: 400; letter-spacing: -0.0025em; line-height: 1.4; - min-height: 100vh; + min-height: 100dvh; place-items: center; text-rendering: optimizeLegibility; -webkit-text-size-adjust: 100%; } + #error-description { + fill: #d30001; + } + + #error-id { + fill: #f0eff0; + } + + @media (prefers-color-scheme: dark) { + body { + background: #101010; + color: #e0e0e0; + } + + #error-description { + fill: #FF6161; + } + + #error-id { + fill: #2c2c2c; + } + } + a { color: inherit; font-weight: 700; @@ -83,13 +106,11 @@ } main article br { - display: none; @media(min-width: 48em) { display: inline; } - } @@ -102,10 +123,10 @@
- +
-

The change you wanted was rejected. Maybe you tried to change something you didn’t have access to. If you’re the application owner check the logs for more information.

+

The change you wanted was rejected. Maybe you tried to change something you didn't have access to. If you're the application owner check the logs for more information.

diff --git a/public/500.html b/public/500.html index d77718c..e4eb18a 100644 --- a/public/500.html +++ b/public/500.html @@ -4,7 +4,7 @@ - We’re sorry, but something went wrong (500 Internal Server Error) + We're sorry, but something went wrong (500 Internal Server Error) @@ -35,12 +35,35 @@ font-weight: 400; letter-spacing: -0.0025em; line-height: 1.4; - min-height: 100vh; + min-height: 100dvh; place-items: center; text-rendering: optimizeLegibility; -webkit-text-size-adjust: 100%; } + #error-description { + fill: #d30001; + } + + #error-id { + fill: #f0eff0; + } + + @media (prefers-color-scheme: dark) { + body { + background: #101010; + color: #e0e0e0; + } + + #error-description { + fill: #FF6161; + } + + #error-id { + fill: #2c2c2c; + } + } + a { color: inherit; font-weight: 700; @@ -83,13 +106,11 @@ } main article br { - display: none; @media(min-width: 48em) { display: inline; } - } @@ -102,10 +123,10 @@
- +
-

We’re sorry, but something went wrong.
If you’re the application owner check the logs for more information.

+

We're sorry, but something went wrong.
If you're the application owner check the logs for more information.

diff --git a/public/icon.png b/public/icon.png new file mode 100644 index 0000000..c4c9dbf Binary files /dev/null and b/public/icon.png differ diff --git a/public/icon.svg b/public/icon.svg new file mode 100644 index 0000000..04b34bf --- /dev/null +++ b/public/icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/spec/.rubocop.yml b/spec/.rubocop.yml index b7de565..a3d5081 100644 --- a/spec/.rubocop.yml +++ b/spec/.rubocop.yml @@ -1,139 +1,12 @@ inherit_from: ../.rubocop.yml -plugins: - - rubocop-rspec - - rubocop-rspec_rails -# Sometimes we're testing the operator -Lint/BinaryOperatorWithIdenticalOperands: - Enabled: false -Metrics/AbcSize: - Enabled: false - -Metrics/BlockLength: - Enabled: false - -Metrics/ModuleLength: - Enabled: false - -Metrics/MethodLength: - Enabled: false ############################################################ # rubocop-rails -# we meant to do that -Rails/SkipsModelValidations: - Enabled: false - -# we meant to do that -Rails/TimeZone: - Enabled: false - -############################################################ -# rubocop-rspec - -# believe me, it wasn't by choice -RSpec/AnyInstance: - Enabled: false - # This is used as plain Ruby not ActiveRecord. Pluck not available -# in the context used +# in the context used Rails/Pluck: - Enabled: false - -RSpec/VerifiedDoubleReference: - Enabled: false - -# we meant to do that -RSpec/BeforeAfterAll: - Enabled: false - -RSpec/ContainExactly: - Enabled: false - -# more words != more readable -RSpec/ContextWording: - Enabled: false - -# not everything we test is a class -RSpec/DescribeClass: - Enabled: false - -# explicit >>> implicit -RSpec/DescribedClass: - Enabled: false - -# more punctuation != more readable -RSpec/DescribeSymbol: - Enabled: false - -# setup cost / time >>> failure granularity -RSpec/ExampleLength: - Max: 25 - CountAsOne: - - array - - hash - - heredoc - -# we meant to do that -RSpec/ExpectInHook: - Enabled: false - -# your naming scheme is not in possession of all the facts -# RSpec/FilePath: -# Enabled: false - -RSpec/SpecFilePathFormat: - Enabled: false - -RSpec/SpecFilePathSuffix: - Enabled: false - -# explicit >>> implicit -RSpec/InstanceVariable: - Enabled: false - -# maybe when 'all' has a corresponding 'none' matcher -RSpec/IteratedExpectation: - Enabled: false - -# we meant to do that -RSpec/MessageSpies: - Enabled: false - -# too late now -RSpec/MultipleMemoizedHelpers: - Enabled: false - -# setup cost / time >>> failure granularity -RSpec/MultipleExpectations: - Enabled: false - -# cure is worse than the disease -RSpec/NestedGroups: - Enabled: false - -# more quotation marks != more readable -RSpec/SharedExamples: - Enabled: false - -# we meant to do that -RSpec/StubbedMock: - Enabled: false - -# we meant to do that -RSpec/VerifiedDoubles: - Enabled: false - -############################################################ -# rubocop-rspec - -# enable newer rubocop-rspec cops - -RSpec/IdenticalEqualityAssertion: # new in 2.4 - Enabled: true - -RSpecRails/AvoidSetupHook: # new in 2.4 - Enabled: true + Enabled: false \ No newline at end of file diff --git a/spec/lib/lending/tileizer_spec.rb b/spec/lib/lending/tileizer_spec.rb index 656d18b..b67a9e4 100644 --- a/spec/lib/lending/tileizer_spec.rb +++ b/spec/lib/lending/tileizer_spec.rb @@ -71,14 +71,16 @@ module Lending end describe :tileize_all do + # rubocop:disable RSpec/LeakyLocalVariable sample_root = 'spec/data/lending/samples' + # rubocop:enable RSpec/LeakyLocalVariable context 'with TIFF files' do - include_examples :tileize_all_examples, File.join(sample_root, 'b135297126_C068087930_TIFF') + it_behaves_like :tileize_all_examples, File.join(sample_root, 'b135297126_C068087930_TIFF') end context 'with JPEG files' do - include_examples :tileize_all_examples, File.join(sample_root, 'b135297126_C068087930_JPEG') + it_behaves_like :tileize_all_examples, File.join(sample_root, 'b135297126_C068087930_JPEG') end end end diff --git a/spec/models/item_spec.rb b/spec/models/item_spec.rb index ea56472..9be4f7d 100644 --- a/spec/models/item_spec.rb +++ b/spec/models/item_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -describe Item, type: :model do +describe Item do let(:factory_names) do %i[ inactive_item diff --git a/spec/models/loan_spec.rb b/spec/models/loan_spec.rb index b2d6afd..0a999cc 100644 --- a/spec/models/loan_spec.rb +++ b/spec/models/loan_spec.rb @@ -81,27 +81,19 @@ attr_reader :loans_by_date before do - @env_tz_actual = ENV.fetch('TZ', nil) - @rails_tz_actual = Time.zone - - Time.zone = 'America/Los_Angeles' - - @loans_by_date = {} - - local_date = (Date.current - 7.days) - year, month, day = %i[year month day].map { |attr| local_date.send(attr) } - [2, 8, 14, 20].each do |hour| - loan_date = Time.zone.local(year, month, day, hour) - loan = create(:expired_loan, loan_date:, item_id: item.id, patron_identifier: user.borrower_id) - (loans_by_date[local_date] ||= []) << loan + Time.use_zone 'America/Los_Angeles' do + @loans_by_date = {} + + local_date = (Date.current - 7.days) + year, month, day = %i[year month day].map { |attr| local_date.send(attr) } + [2, 8, 14, 20].each do |hour| + loan_date = Time.zone.local(year, month, day, hour) + loan = create(:expired_loan, loan_date:, item_id: item.id, patron_identifier: user.borrower_id) + (loans_by_date[local_date] ||= []) << loan + end end end - after do - ENV['TZ'] = env_tz_actual - Time.zone = rails_tz_actual - end - it 'returns loans for the correct date regardless of the Ruby time zone' do %w[UTC America/Los_Angeles].each do |tz| ENV['TZ'] = tz diff --git a/spec/models/settings_spec.rb b/spec/models/settings_spec.rb index ed322b3..86ebde3 100644 --- a/spec/models/settings_spec.rb +++ b/spec/models/settings_spec.rb @@ -1,5 +1,5 @@ require 'rails_helper' -RSpec.describe Settings, type: :model do +describe Settings do pending "add some examples to (or delete) #{__FILE__}" end diff --git a/spec/models/term_spec.rb b/spec/models/term_spec.rb index 6104bbe..a459fe2 100644 --- a/spec/models/term_spec.rb +++ b/spec/models/term_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -RSpec.describe Term, type: :model do +describe Term do describe :create do it 'requires a name' do term = Term.create(start_date: Date.current, end_date: Date.current + 1.days) diff --git a/spec/request/lending_request_spec.rb b/spec/request/lending_request_spec.rb index eef753e..c3fbdf4 100644 --- a/spec/request/lending_request_spec.rb +++ b/spec/request/lending_request_spec.rb @@ -80,13 +80,13 @@ def available it 'returns 404 not found for nonexistent items' do get lending_show_path(directory: 'not_a_directory') - expect(response.status).to eq(404) + expect(response).to have_http_status(:not_found) end # This is now treated as an http error in Rails 8 it 'returns 404 not found for /lending' do get '/lending' - expect(response.status).to eq(404) + expect(response).to have_http_status(:not_found) end end @@ -102,7 +102,7 @@ def available it 'returns 404 not found for nonexistent items' do get lending_edit_path(directory: 'not_a_directory') - expect(response.status).to eq(404) + expect(response).to have_http_status(:not_found) end end @@ -118,7 +118,7 @@ def available it 'returns 404 not found for nonexistent items' do get lending_manifest_path(directory: 'not_a_directory') - expect(response.status).to eq(404) + expect(response).to have_http_status(:not_found) end end @@ -146,7 +146,7 @@ def available expect(item).not_to be_nil # just to be sure patch lending_update_path(directory: item.directory), params: { item: { active: true, copies: 17 } } - expect(response.status).to eq(422) + expect(response).to have_http_status(:unprocessable_content) item.reload expect(item).not_to be_active @@ -158,7 +158,7 @@ def available expect(item).not_to be_nil # just to be sure patch lending_update_path(directory: item.directory), params: { item: { active: true, copies: -1 } } - expect(response.status).to eq(422) + expect(response).to have_http_status(:unprocessable_content) item.reload expect(item).not_to be_active @@ -167,7 +167,7 @@ def available it 'returns 404 not found for nonexistent items' do patch lending_update_path(directory: 'not_a_directory'), params: { item: { active: true, copies: 17 } } - expect(response.status).to eq(404) + expect(response).to have_http_status(:not_found) end end end @@ -213,7 +213,7 @@ def available it 'returns 404 not found for nonexistent items' do get lending_activate_path(directory: 'not_a_directory') - expect(response.status).to eq(404) + expect(response).to have_http_status(:not_found) end end @@ -260,7 +260,7 @@ def available it 'returns 404 not found for nonexistent items' do get lending_deactivate_path(directory: 'not_a_directory') - expect(response.status).to eq(404) + expect(response).to have_http_status(:not_found) end end @@ -296,7 +296,7 @@ def available it 'returns 404 not found for nonexistent items' do get lending_destroy_path(directory: 'not_a_directory') - expect(response.status).to eq(404) + expect(response).to have_http_status(:not_found) end it 'works for incomplete items that differ from complete items only by "file extension"' do @@ -340,7 +340,7 @@ def available it 'returns 404 not found for nonexistent items' do get lending_reload_path(directory: 'not_a_directory') - expect(response.status).to eq(404) + expect(response).to have_http_status(:not_found) end it 'succeeds for unchanged items' do @@ -382,12 +382,12 @@ def available describe :show do it 'returns 403 Forbidden' do get lending_show_path(directory: item.directory) - expect(response.status).to eq(403) + expect(response).to have_http_status(:forbidden) end it 'returns 403 forbidden even for nonexistent items' do get lending_deactivate_path(directory: 'not_a_directory') - expect(response.status).to eq(403) + expect(response).to have_http_status(:forbidden) end end @@ -405,7 +405,7 @@ def available it 'returns 404 not found for nonexistent items' do get lending_view_path(directory: 'not_a_directory') - expect(response.status).to eq(404) + expect(response).to have_http_status(:not_found) end it 'shows a loan if one exists' do @@ -503,7 +503,7 @@ def available it 'returns 404 not found for nonexistent items' do get lending_check_out_path(directory: 'not_a_directory') - expect(response.status).to eq(404) + expect(response).to have_http_status(:not_found) end it 'fails if this user has already checked out the item' do @@ -514,7 +514,7 @@ def available get lending_check_out_path(directory: item.directory) end.not_to change(Loan, :count) - expect(response.status).to eq(422) # unprocessable entity + expect(response).to have_http_status(:unprocessable_content) expect(response.body).to include(Item::MSG_CHECKED_OUT) end @@ -534,7 +534,7 @@ def available get lending_check_out_path(directory: item.directory) end.not_to change(Loan, :count) - expect(response.status).to eq(422) # unprocessable entity + expect(response).to have_http_status(:unprocessable_content) expect(response.body).to include(Item::MSG_CHECKOUT_LIMIT_REACHED) end @@ -610,7 +610,7 @@ def available get lending_check_out_path(directory: item.directory) end.not_to change(Loan, :count) - expect(response.status).to eq(422) # unprocessable entity + expect(response).to have_http_status(:unprocessable_content) expect(response.body).to include(Item::MSG_UNAVAILABLE) end @@ -621,7 +621,7 @@ def available get lending_check_out_path(directory: item.directory) end.not_to change(Loan, :count) - expect(response.status).to eq(422) # unprocessable entity + expect(response).to have_http_status(:unprocessable_content) expect(response.body).to include(Item::MSG_INACTIVE) end end @@ -657,7 +657,7 @@ def available it 'returns 404 not found for nonexistent items' do get lending_return_path(directory: 'not_a_directory') - expect(response.status).to eq(404) + expect(response).to have_http_status(:not_found) end end @@ -671,31 +671,31 @@ def available it 'returns 403 Forbidden if the item has not been checked out' do get lending_manifest_path(directory: item.directory) - expect(response.status).to eq(403) + expect(response).to have_http_status(:forbidden) end it 'returns 404 not found for nonexistent items' do get lending_manifest_path(directory: 'not_a_directory') - expect(response.status).to eq(404) + expect(response).to have_http_status(:not_found) end end describe :edit do it 'returns 403 forbidden' do get lending_edit_path(directory: item.directory) - expect(response.status).to eq(403) + expect(response).to have_http_status(:forbidden) end it 'returns 403 forbidden even for nonexistent items' do get lending_edit_path(directory: 'not_a_directory') - expect(response.status).to eq(403) + expect(response).to have_http_status(:forbidden) end end describe :activate do it 'returns 403 forbidden' do get lending_activate_path(directory: item.directory) - expect(response.status).to eq(403) + expect(response).to have_http_status(:forbidden) end it "doesn't activate the item" do @@ -709,14 +709,14 @@ def available it 'returns 403 forbidden even for nonexistent items' do get lending_activate_path(directory: 'not_a_directory') - expect(response.status).to eq(403) + expect(response).to have_http_status(:forbidden) end end describe :inactivate do it 'returns 403 forbidden' do get lending_deactivate_path(directory: item.directory) - expect(response.status).to eq(403) + expect(response).to have_http_status(:forbidden) item.reload expect(item.active).to be(true) @@ -724,14 +724,14 @@ def available it 'returns 403 forbidden even for nonexistent items' do get lending_deactivate_path(directory: 'not_a_directory') - expect(response.status).to eq(403) + expect(response).to have_http_status(:forbidden) end end describe :reload do it 'returns 403 forbidden' do get lending_reload_path(directory: item.directory) - expect(response.status).to eq(403) + expect(response).to have_http_status(:forbidden) end it "doesn't reload the MARC metadata" do @@ -747,7 +747,7 @@ def available it 'returns 403 forbidden even for nonexistent items' do get lending_reload_path(directory: 'not_a_directory') - expect(response.status).to eq(403) + expect(response).to have_http_status(:forbidden) end end end @@ -815,34 +815,34 @@ def available it 'GET lending_manifest_path returns 403 Forbidden' do get lending_manifest_path(directory: item.directory) - expect(response.status).to eq(403) + expect(response).to have_http_status(:forbidden) end it 'GET lending_edit_path returns 403 Forbidden' do get lending_edit_path(directory: item.directory) - expect(response.status).to eq(403) + expect(response).to have_http_status(:forbidden) end it 'GET lending_view_path returns 403 Forbidden' do get lending_view_path(directory: item.directory) - expect(response.status).to eq(403) + expect(response).to have_http_status(:forbidden) end it 'DELETE lending_destroy_path returns 403 Forbidden' do delete lending_destroy_path(directory: item.directory) - expect(response.status).to eq(403) + expect(response).to have_http_status(:forbidden) end # TODO: use PATCH it 'GET lending_activate_path returns 403 Forbidden' do get lending_activate_path(directory: item.directory) - expect(response.status).to eq(403) + expect(response).to have_http_status(:forbidden) end # TODO: use PATCH it 'GET lending_deactivate_path returns 403 Forbidden' do get lending_deactivate_path(directory: item.directory) - expect(response.status).to eq(403) + expect(response).to have_http_status(:forbidden) end end end diff --git a/spec/request/stats_request_spec.rb b/spec/request/stats_request_spec.rb index afab647..61f34e7 100644 --- a/spec/request/stats_request_spec.rb +++ b/spec/request/stats_request_spec.rb @@ -112,13 +112,13 @@ it 'rejects a non-date argument' do get stats_download_path(date: 'not a date') - expect(response.status).to eq(400) + expect(response).to have_http_status(:bad_request) expect(response.body).to be_empty end it 'rejects a bad date' do get stats_download_path(date: '9999-99-99') - expect(response.status).to eq(400) + expect(response).to have_http_status(:bad_request) expect(response.body).to be_empty end end diff --git a/spec/requests/items_spec.rb b/spec/requests/items_spec.rb index 78c88e3..b4ce6bf 100644 --- a/spec/requests/items_spec.rb +++ b/spec/requests/items_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -RSpec.describe '/items', type: :request do +describe '/items', type: :request do def expected_json(item) renderer = ApplicationController.renderer.new(http_host: request.host) @@ -54,7 +54,7 @@ def expected_json(item) describe 'GET /index' do it 'returns 403 Forbidden for HTML requests' do get items_url, as: :html - expect(response.status).to eq(403) + expect(response).to have_http_status(:forbidden) expect(response.content_type).to start_with('text/html') expect(response.body).to include('restricted to UC BEARS administrators') end @@ -65,7 +65,7 @@ def expected_json(item) expect(response).to be_successful expect(response.content_type).to start_with('application/json') - parsed_response = JSON.parse(response.body) + parsed_response = response.parsed_body expect(parsed_response).to be_an(Array) expected_items = Item.order(:title) @@ -88,7 +88,7 @@ def expected_json(item) links = link_header.split(', ') expect(links).to include('; rel="profile"') - parsed_response = JSON.parse(response.body) + parsed_response = response.parsed_body expect(parsed_response).to be_an(Array) expected_items = Item.order(:title) @@ -147,7 +147,7 @@ def expected_json(item) expect(response).to be_successful expect(response.content_type).to start_with('application/json') - parsed_response = JSON.parse(response.body) + parsed_response = response.parsed_body expect(parsed_response).to be_an(Array) expected_items = Item.order(:title).first(Pagy.options[:limit]) @@ -163,7 +163,7 @@ def expected_json(item) expect(response).to be_successful expect(response.content_type).to start_with('application/json') - parsed_response = JSON.parse(response.body) + parsed_response = response.parsed_body expect(parsed_response).to be_an(Array) expected_items = Item.complete @@ -181,7 +181,7 @@ def expected_json(item) expect(response).to be_successful expect(response.content_type).to start_with('application/json') - parsed_response = JSON.parse(response.body) + parsed_response = response.parsed_body expect(parsed_response).to be_an(Array) expected_items = Item.incomplete @@ -199,7 +199,7 @@ def expected_json(item) expect(response).to be_successful expect(response.content_type).to start_with('application/json') - parsed_response = JSON.parse(response.body) + parsed_response = response.parsed_body expect(parsed_response).to be_an(Array) expected_items = Item.where(active: true) @@ -217,7 +217,7 @@ def expected_json(item) expect(response).to be_successful expect(response.content_type).to start_with('application/json') - parsed_response = JSON.parse(response.body) + parsed_response = response.parsed_body expect(parsed_response).to be_an(Array) expected_items = Item.where(active: false) @@ -235,7 +235,7 @@ def expected_json(item) expect(response).to be_successful expect(response.content_type).to start_with('application/json') - parsed_response = JSON.parse(response.body) + parsed_response = response.parsed_body expect(parsed_response).to be_an(Array) expected_items = Item.inactive.complete @@ -253,7 +253,7 @@ def expected_json(item) expect(response).to be_successful expect(response.content_type).to start_with('application/json') - parsed_response = JSON.parse(response.body) + parsed_response = response.parsed_body expect(parsed_response).to be_an(Array) expected_items = term_fall_2021.items.incomplete.where(active: true) @@ -321,7 +321,7 @@ def expected_json(item) item.reload - actual_json = JSON.parse(response.body) + actual_json = response.parsed_body expect(actual_json).to eq(expected_json(item)) expect(response.content_type).to start_with('application/json') @@ -337,7 +337,7 @@ def expected_json(item) expect(response).to have_http_status(:unprocessable_entity) expect(response.content_type).to start_with('application/json') - parsed_response = JSON.parse(response.body) + parsed_response = response.parsed_body expect(parsed_response).to be_a(Hash) expect(parsed_response['success']).to be(false) @@ -377,7 +377,7 @@ def expected_json(item) expect(response).to have_http_status(:forbidden) expect(response.content_type).to start_with('application/json') - parsed_response = JSON.parse(response.body) + parsed_response = response.parsed_body expect(parsed_response).to be_a(Hash) expect(parsed_response['success']).to be(false) @@ -437,7 +437,7 @@ def expected_json(item) get processing_url, as: :json expect(response).to be_successful - parsed_response = JSON.parse(response.body) + parsed_response = response.parsed_body expect(parsed_response).to be_a(Array) expect(parsed_response.size).to eq(symlinks.size) diff --git a/spec/requests/terms_spec.rb b/spec/requests/terms_spec.rb index 11f3508..e17a4e5 100644 --- a/spec/requests/terms_spec.rb +++ b/spec/requests/terms_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -RSpec.describe '/terms', type: :request do +describe '/terms', type: :request do def expected_json(term) renderer = ApplicationController.renderer.new(http_host: request.host) expected_json = renderer.render(template: 'terms/show', assigns: { term: }) @@ -65,7 +65,7 @@ def expected_json(term) describe 'GET /index' do it 'returns 403 Forbidden for HTML requests' do get terms_url, as: :html - expect(response.status).to eq(403) + expect(response).to have_http_status(:forbidden) expect(response.content_type).to start_with('text/html') expect(response.body).to include('restricted to UC BEARS administrators') end @@ -76,7 +76,7 @@ def expected_json(term) expect(response).to be_successful expect(response.content_type).to start_with('application/json') - parsed_response = JSON.parse(response.body) + parsed_response = response.parsed_body expect(parsed_response).to be_an(Array) expected_terms = Term.all @@ -101,7 +101,7 @@ def expected_json(term) expect(response).to be_successful expect(response.content_type).to start_with('application/json') - parsed_response = JSON.parse(response.body) + parsed_response = response.parsed_body expect(parsed_response).to be_an(Array) expected_terms = Term.all @@ -121,7 +121,7 @@ def expected_json(term) expect(response).to be_successful expect(response.content_type).to start_with('application/json') - parsed_response = JSON.parse(response.body) + parsed_response = response.parsed_body expect(parsed_response).to eq(expected_json(term)) end end @@ -170,7 +170,7 @@ def expected_json(term) expect(response).to be_successful expect(response.content_type).to match(%r{^application/json}) - parsed_response = JSON.parse(response.body) + parsed_response = response.parsed_body expect(parsed_response).to be_a(Hash) expected = { @@ -206,7 +206,7 @@ def expected_json(term) expect(response).to have_http_status(:unprocessable_entity) expect(response.content_type).to start_with('application/json') - parsed_response = JSON.parse(response.body) + parsed_response = response.parsed_body expect(parsed_response).to be_a(Hash) expect(parsed_response['success']).to be(false) @@ -250,7 +250,7 @@ def expected_json(term) expected_msg = I18n.t('activerecord.errors.messages.taken') expected_msg_re = /#{Regexp.escape(expected_msg)}/ - parsed_response = JSON.parse(response.body) + parsed_response = response.parsed_body expect(parsed_response).to be_a(Hash) expect(parsed_response['success']).to be(false) @@ -286,7 +286,7 @@ def expected_json(term) expect(response).to be_successful expect(response.content_type).to match(%r{^application/json}) - actual_json = JSON.parse(response.body) + actual_json = response.parsed_body expect(actual_json).to eq(expected_json(term)) end @@ -341,7 +341,7 @@ def expected_json(term) expect(response).to be_successful expect(response.content_type).to match(%r{^application/json}) - actual_json = JSON.parse(response.body) + actual_json = response.parsed_body expect(actual_json).to eq(expected_json(term)) end @@ -358,7 +358,7 @@ def expected_json(term) expect(response).to have_http_status(:unprocessable_entity) expect(response.content_type).to start_with('application/json') - parsed_response = JSON.parse(response.body) + parsed_response = response.parsed_body expect(parsed_response).to be_a(Hash) expect(parsed_response['success']).to be(false) @@ -421,7 +421,7 @@ def expected_json(term) expected_msg = I18n.t('activerecord.errors.messages.taken') expected_msg_re = /#{Regexp.escape(expected_msg)}/ - parsed_response = JSON.parse(response.body) + parsed_response = response.parsed_body expect(parsed_response).to be_a(Hash) expect(parsed_response['success']).to be(false)