diff --git a/.annotaterb.yml b/.annotaterb.yml new file mode 100644 index 0000000..81b3664 --- /dev/null +++ b/.annotaterb.yml @@ -0,0 +1,71 @@ +--- +:position: before +:position_in_additional_file_patterns: before +:position_in_class: before +:position_in_factory: before +:position_in_fixture: before +:position_in_routes: before +:position_in_serializer: before +:position_in_test: before +:classified_sort: true +:exclude_controllers: true +:exclude_factories: false +:exclude_fixtures: false +:exclude_helpers: true +:exclude_scaffolds: true +:exclude_serializers: false +:exclude_sti_subclasses: false +:exclude_tests: false +:force: false +:format_markdown: false +:format_rdoc: false +:format_yard: false +:frozen: false +:grouped_polymorphic: false +:ignore_database_name: false +:ignore_model_sub_dir: false +:ignore_unknown_models: false +:include_version: false +:show_check_constraints: false +:show_unique_constraints: false +:show_exclusion_constraints: false +:show_enums: false +:show_complete_foreign_keys: false +:show_foreign_keys: true +:show_indexes: true +:show_indexes_comments: false +:show_indexes_include: false +:simple_indexes: false +:sort: false +:timestamp: false +:trace: false +:with_comment: true +:with_column_comments: true +:with_table_comments: true +:position_of_column_comment: :with_name +:enum_default_format: :label +:active_admin: false +:command: +:debug: false +:hide_default_column_types: json,jsonb,hstore +:hide_limit_column_types: integer,bigint,boolean +:timestamp_columns: +- created_at +- updated_at +:ignore_columns: +:ignore_routes: +:ignore_multi_database_name: false +:models: true +:skip_on_db_migrate: false +:auto_annotate_routes_after_migrate: false +:target_action: :do_annotations +:wrapper: +:wrapper_close: +:wrapper_open: +:classes_default_to_s: [] +:additional_file_patterns: [] +:model_dir: +- app/models +:require: [] +:root_dir: +- '' diff --git a/.rubocop.yml b/.rubocop.yml index 59d78f8..5f27d0b 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -6,7 +6,6 @@ AllCops: - "db/schema.rb" # Fixes missing gem exception when running Rubocop on GitHub Actions. - "vendor/bundle/**/*" - - lib/tasks/auto_annotate_models.rake # Always use double quotes Style/StringLiterals: diff --git a/Gemfile b/Gemfile index 4d3c0de..8f39784 100644 --- a/Gemfile +++ b/Gemfile @@ -5,6 +5,8 @@ gem "abbrev" gem "authie" gem "autoprefixer-rails" gem "bcrypt" +gem "benchmark" +gem "cgi" gem "chronic" gem "domain_name" gem "dotenv" @@ -29,7 +31,7 @@ gem "ostruct" gem "prometheus-client" gem "puma" gem "rackup" -gem "rails", "= 7.1.6" +gem "rails", "= 8.1.4" gem "resolv" gem "secure_headers" gem "sentry-rails" @@ -51,7 +53,7 @@ group :development, :assets do end group :development do - gem "annotate" + gem "annotaterb" gem "rubocop" gem "rubocop-rails" end diff --git a/Gemfile.lock b/Gemfile.lock index 16adf02..e7d88b8 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -2,91 +2,86 @@ GEM remote: https://rubygems.org/ specs: abbrev (0.1.2) - actioncable (7.1.6) - actionpack (= 7.1.6) - activesupport (= 7.1.6) + action_text-trix (2.1.19) + railties + actioncable (8.1.4) + actionpack (= 8.1.4) + activesupport (= 8.1.4) nio4r (~> 2.0) websocket-driver (>= 0.6.1) zeitwerk (~> 2.6) - actionmailbox (7.1.6) - actionpack (= 7.1.6) - activejob (= 7.1.6) - activerecord (= 7.1.6) - activestorage (= 7.1.6) - activesupport (= 7.1.6) - mail (>= 2.7.1) - net-imap - net-pop - net-smtp - actionmailer (7.1.6) - actionpack (= 7.1.6) - actionview (= 7.1.6) - activejob (= 7.1.6) - activesupport (= 7.1.6) - mail (~> 2.5, >= 2.5.4) - net-imap - net-pop - net-smtp + actionmailbox (8.1.4) + actionpack (= 8.1.4) + activejob (= 8.1.4) + activerecord (= 8.1.4) + activestorage (= 8.1.4) + activesupport (= 8.1.4) + mail (>= 2.8.0) + actionmailer (8.1.4) + actionpack (= 8.1.4) + actionview (= 8.1.4) + activejob (= 8.1.4) + activesupport (= 8.1.4) + mail (>= 2.8.0) rails-dom-testing (~> 2.2) - actionpack (7.1.6) - actionview (= 7.1.6) - activesupport (= 7.1.6) - cgi + actionpack (8.1.4) + actionview (= 8.1.4) + activesupport (= 8.1.4) nokogiri (>= 1.8.5) - racc rack (>= 2.2.4) rack-session (>= 1.0.1) rack-test (>= 0.6.3) rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) - actiontext (7.1.6) - actionpack (= 7.1.6) - activerecord (= 7.1.6) - activestorage (= 7.1.6) - activesupport (= 7.1.6) + useragent (~> 0.16) + actiontext (8.1.4) + action_text-trix (~> 2.1.15) + actionpack (= 8.1.4) + activerecord (= 8.1.4) + activestorage (= 8.1.4) + activesupport (= 8.1.4) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (7.1.6) - activesupport (= 7.1.6) + actionview (8.1.4) + activesupport (= 8.1.4) builder (~> 3.1) - cgi erubi (~> 1.11) rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) - activejob (7.1.6) - activesupport (= 7.1.6) + activejob (8.1.4) + activesupport (= 8.1.4) globalid (>= 0.3.6) - activemodel (7.1.6) - activesupport (= 7.1.6) - activerecord (7.1.6) - activemodel (= 7.1.6) - activesupport (= 7.1.6) + activemodel (8.1.4) + activesupport (= 8.1.4) + activerecord (8.1.4) + activemodel (= 8.1.4) + activesupport (= 8.1.4) timeout (>= 0.4.0) - activestorage (7.1.6) - actionpack (= 7.1.6) - activejob (= 7.1.6) - activerecord (= 7.1.6) - activesupport (= 7.1.6) + activestorage (8.1.4) + actionpack (= 8.1.4) + activejob (= 8.1.4) + activerecord (= 8.1.4) + activesupport (= 8.1.4) marcel (~> 1.0) - activesupport (7.1.6) + activesupport (8.1.4) base64 - benchmark (>= 0.3) bigdecimal - concurrent-ruby (~> 1.0, >= 1.0.2) + 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) - mutex_m securerandom (>= 0.3) - tzinfo (~> 2.0) + tzinfo (~> 2.0, >= 2.0.5) + uri (>= 0.13.1) addressable (2.8.6) public_suffix (>= 2.0.2, < 6.0) aes_key_wrap (1.1.0) - annotate (3.2.0) - activerecord (>= 3.2, < 8.0) - rake (>= 10.4, < 14.0) + annotaterb (4.25.0) + activerecord (>= 6.0.0) + activesupport (>= 6.0.0) ast (2.4.2) attr_required (1.0.2) authie (5.0.0) @@ -237,7 +232,7 @@ GEM hashie (>= 3.4.6) rack (>= 2.2.3) rack-protection - omniauth-rails_csrf_protection (1.0.1) + omniauth-rails_csrf_protection (2.0.1) actionpack (>= 4.2) omniauth (~> 2.0) omniauth_openid_connect (0.7.1) @@ -291,20 +286,20 @@ GEM rack (>= 1.3) rackup (2.3.1) rack (>= 3) - rails (7.1.6) - actioncable (= 7.1.6) - actionmailbox (= 7.1.6) - actionmailer (= 7.1.6) - actionpack (= 7.1.6) - actiontext (= 7.1.6) - actionview (= 7.1.6) - activejob (= 7.1.6) - activemodel (= 7.1.6) - activerecord (= 7.1.6) - activestorage (= 7.1.6) - activesupport (= 7.1.6) + rails (8.1.4) + actioncable (= 8.1.4) + actionmailbox (= 8.1.4) + actionmailer (= 8.1.4) + actionpack (= 8.1.4) + actiontext (= 8.1.4) + actionview (= 8.1.4) + activejob (= 8.1.4) + activemodel (= 8.1.4) + activerecord (= 8.1.4) + activestorage (= 8.1.4) + activesupport (= 8.1.4) bundler (>= 1.15.0) - railties (= 7.1.6) + railties (= 8.1.4) rails-dom-testing (2.3.0) activesupport (>= 5.0.0) minitest @@ -312,11 +307,10 @@ 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 (7.1.6) - actionpack (= 7.1.6) - activesupport (= 7.1.6) - cgi - irb + railties (8.1.4) + actionpack (= 8.1.4) + activesupport (= 8.1.4) + irb (~> 1.13) rackup (>= 1.0.0) rake (>= 12.2) thor (~> 1.0, >= 1.2.2) @@ -346,14 +340,14 @@ GEM rspec-mocks (3.13.5) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) - rspec-rails (6.1.1) - actionpack (>= 6.1) - activesupport (>= 6.1) - railties (>= 6.1) - rspec-core (~> 3.12) - rspec-expectations (~> 3.12) - rspec-mocks (~> 3.12) - rspec-support (~> 3.12) + rspec-rails (8.0.4) + actionpack (>= 7.2) + activesupport (>= 7.2) + railties (>= 7.2) + rspec-core (>= 3.13.0, < 5.0.0) + rspec-expectations (>= 3.13.0, < 5.0.0) + rspec-mocks (>= 3.13.0, < 5.0.0) + rspec-support (>= 3.13.0, < 5.0.0) rspec-support (3.13.6) rubocop (1.48.1) json (~> 2.3) @@ -419,6 +413,7 @@ GEM execjs (>= 0.3.0, < 3) unicode-display_width (2.4.2) uri (1.0.3) + useragent (0.16.11) validate_url (1.0.15) activemodel (>= 3.0.0) public_suffix @@ -445,10 +440,12 @@ PLATFORMS DEPENDENCIES abbrev - annotate + annotaterb authie autoprefixer-rails bcrypt + benchmark + cgi chronic coffee-rails (~> 5.0) database_cleaner-active_record @@ -479,7 +476,7 @@ DEPENDENCIES prometheus-client puma rackup - rails (= 7.1.6) + rails (= 8.1.4) resolv rspec rspec-rails diff --git a/config/environments/test.rb b/config/environments/test.rb index da3785d..281a97a 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -25,7 +25,7 @@ Rails.application.configure do config.action_controller.perform_caching = false # Raise exceptions instead of rendering exception templates. - config.action_dispatch.show_exceptions = false + config.action_dispatch.show_exceptions = :none # Disable request forgery protection in test environment. config.action_controller.allow_forgery_protection = false diff --git a/db/schema.rb b/db/schema.rb index acf8c4d..c68c9ab 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.1].define(version: 2026_09_03_100000) do +ActiveRecord::Schema[8.1].define(version: 2026_09_03_100000) do create_table "additional_route_endpoints", id: :integer, charset: "utf8mb4", collation: "utf8mb4_general_ci", force: :cascade do |t| t.integer "route_id" t.string "endpoint_type" @@ -381,5 +381,4 @@ ActiveRecord::Schema[7.1].define(version: 2026_09_03_100000) do t.datetime "acquired_at", precision: nil t.index ["role"], name: "index_worker_roles_on_role", unique: true end - end diff --git a/lib/migration_waiter.rb b/lib/migration_waiter.rb index 36b36e0..ac3eaa4 100644 --- a/lib/migration_waiter.rb +++ b/lib/migration_waiter.rb @@ -15,7 +15,7 @@ class MigrationWaiter def wait attempts_remaining = ATTEMPTS loop do - pending_migrations = ActiveRecord::Base.connection.migration_context.open.pending_migrations.size + pending_migrations = ActiveRecord::Base.connection_pool.migration_context.open.pending_migrations.size if pending_migrations.zero? Postal.logger.info "no pending migrations, continuing" return diff --git a/lib/tasks/annotate_rb.rake b/lib/tasks/annotate_rb.rake new file mode 100644 index 0000000..88a20bf --- /dev/null +++ b/lib/tasks/annotate_rb.rake @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +# This rake task was added by annotate_rb gem. + +# Can set `ANNOTATERB_SKIP_ON_DB_TASKS` to be anything to skip this +if Rails.env.development? && ENV["ANNOTATERB_SKIP_ON_DB_TASKS"].nil? + require "annotate_rb" + + # Can modify the config path here if needed - by default, it's .annotaterb.yml in the root of the project + # AnnotateRb::ConfigFinder.config_path = "" + AnnotateRb::Core.load_rake_tasks +end diff --git a/spec/lib/migration_waiter_spec.rb b/spec/lib/migration_waiter_spec.rb new file mode 100644 index 0000000..ee8748b --- /dev/null +++ b/spec/lib/migration_waiter_spec.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +require "rails_helper" + +RSpec.describe MigrationWaiter do + describe ".wait" do + it "returns without waiting when there are no pending migrations" do + expect(described_class).not_to receive(:sleep) + expect(Process).not_to receive(:exit) + described_class.wait + end + + it "exits after all attempts have been used while migrations are still pending" do + stub_const("MigrationWaiter::ATTEMPTS", 2) + allow_any_instance_of(ActiveRecord::Migrator).to receive(:pending_migrations).and_return([double]) + expect(described_class).to receive(:sleep).once + expect(Process).to receive(:exit).with(1).and_raise(SystemExit) + expect { described_class.wait }.to raise_error(SystemExit) + end + end +end diff --git a/spec/lib/worker/process_spec.rb b/spec/lib/worker/process_spec.rb new file mode 100644 index 0000000..ac7ce77 --- /dev/null +++ b/spec/lib/worker/process_spec.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +require "rails_helper" + +module Worker + + RSpec.describe Process do + subject(:process) { described_class.new(thread_count: 1) } + + before do + # Each process registers its metrics so each example needs its own registry + allow(Prometheus::Client).to receive(:registry).and_return(Prometheus::Client::Registry.new) + + # Errors are normally logged and swallowed, raise them so they fail the example instead + allow(process).to receive(:capture_errors).and_yield + end + + describe "#work" do + it "runs each job and returns false when there was no work to do" do + expect(process.send(:work, 0)).to be false + end + end + + describe "#run_task" do + it "runs a task which is due and schedules its next run" do + scheduled_task = ScheduledTask.create!(name: "PruneWebhookRequestsScheduledTask", next_run_after: 1.minute.ago) + task = instance_double(PruneWebhookRequestsScheduledTask, call: nil) + allow(PruneWebhookRequestsScheduledTask).to receive(:new).and_return(task) + + process.send(:run_task, PruneWebhookRequestsScheduledTask) + + expect(task).to have_received(:call) + expect(scheduled_task.reload.next_run_after).to be > Time.current + end + end + end + +end diff --git a/spec/models/http_endpoint_spec.rb b/spec/models/http_endpoint_spec.rb index 37a6881..8f0862d 100644 --- a/spec/models/http_endpoint_spec.rb +++ b/spec/models/http_endpoint_spec.rb @@ -1,5 +1,25 @@ # frozen_string_literal: true +# == Schema Information +# +# Table name: http_endpoints +# +# id :integer not null, primary key +# disabled_until :datetime +# encoding :string(255) +# error :text(65535) +# format :string(255) +# include_attachments :boolean default(TRUE) +# last_used_at :datetime +# name :string(255) +# strip_replies :boolean default(FALSE) +# timeout :integer +# url :string(255) +# uuid :string(255) +# created_at :datetime +# updated_at :datetime +# server_id :integer +# require "rails_helper" RSpec.describe HTTPEndpoint do