From 134123869a1dd801678dfbe9abe3a8d0c1cd3c0e Mon Sep 17 00:00:00 2001 From: Andy Waite <13400+andyw8@users.noreply.github.com> Date: Mon, 16 Dec 2024 11:41:55 -0500 Subject: [PATCH] Add testing for DSL RBI generation --- .gitignore | 2 + .rubocop.yml | 1 + Rakefile | 4 ++ bin/rails | 14 +++++++ spec/addon_spec_helper.rb | 43 +++++++++++++++++++ spec/tapioca/addon_spec.rb | 53 ++++++++++++++++++++++++ test/dummy/Rakefile | 5 +++ test/dummy/app/models/notify_user_job.rb | 6 +++ test/dummy/config.ru | 7 ++++ test/dummy/config/application.rb | 16 +++++++ test/dummy/config/environment.rb | 5 +++ 11 files changed, 156 insertions(+) create mode 100755 bin/rails create mode 100644 spec/addon_spec_helper.rb create mode 100644 spec/tapioca/addon_spec.rb create mode 100644 test/dummy/Rakefile create mode 100644 test/dummy/app/models/notify_user_job.rb create mode 100644 test/dummy/config.ru create mode 100644 test/dummy/config/application.rb create mode 100644 test/dummy/config/environment.rb diff --git a/.gitignore b/.gitignore index a24ce3bb3..8df9bf6d7 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,5 @@ /spec/reports/ /tmp/ /vendor/ +/test/dummy/log/ +/test/dummy/tmp/ diff --git a/.rubocop.yml b/.rubocop.yml index 116e90678..1f578e740 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -47,6 +47,7 @@ Sorbet/TrueSigil: - "**/*.rake" Exclude: - "lib/ruby_lsp/tapioca/server_addon.rb" + - "test/dummy/**/*.rb" Style/CaseEquality: Enabled: false diff --git a/Rakefile b/Rakefile index 3b2ba0347..22dbb611d 100644 --- a/Rakefile +++ b/Rakefile @@ -3,6 +3,10 @@ require "bundler/gem_tasks" Dir["tasks/**/*.rake"].each { |t| load t } +APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__) + +load "rails/tasks/engine.rake" + require "rubocop/rake_task" RuboCop::RakeTask.new diff --git a/bin/rails b/bin/rails new file mode 100755 index 000000000..2d247600e --- /dev/null +++ b/bin/rails @@ -0,0 +1,14 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# This command will automatically be run when you run "rails" with Rails gems +# installed from the root of your application. + +ENGINE_ROOT = File.expand_path("..", __dir__) +APP_PATH = File.expand_path("../test/dummy/config/application", __dir__) + +# Set up gems listed in the Gemfile. +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) +require "bundler/setup" if File.exist?(ENV["BUNDLE_GEMFILE"]) + +require "rails/engine/commands" diff --git a/spec/addon_spec_helper.rb b/spec/addon_spec_helper.rb new file mode 100644 index 000000000..4a610ad84 --- /dev/null +++ b/spec/addon_spec_helper.rb @@ -0,0 +1,43 @@ +# typed: true +# frozen_string_literal: true + +# Configure Rails Environment +ENV["RAILS_ENV"] = "test" + +require "minitest/spec" +require "ruby_lsp/internal" +require "ruby_lsp/test_helper" + +module ActiveSupport + class TestCase + include RubyLsp::TestHelper + + def pop_result(server) + result = server.pop_response + result = server.pop_response until result.is_a?(RubyLsp::Result) || result.is_a?(RubyLsp::Error) + + refute_instance_of( + RubyLsp::Error, + result, + -> { "Failed to execute request #{T.cast(result, RubyLsp::Error).message}" }, + ) + T.cast(result, RubyLsp::Result) + end + + def pop_log_notification(message_queue, type) + log = message_queue.pop + return log if log.params.type == type + + log = message_queue.pop until log.params.type == type + log + end + + def pop_message(outgoing_queue, &block) + message = outgoing_queue.pop + return message if block.call(message) + + message = outgoing_queue.pop until block.call(message) + message + end + end +end diff --git a/spec/tapioca/addon_spec.rb b/spec/tapioca/addon_spec.rb new file mode 100644 index 000000000..f95936ae0 --- /dev/null +++ b/spec/tapioca/addon_spec.rb @@ -0,0 +1,53 @@ +# typed: true +# frozen_string_literal: true + +require "addon_spec_helper" +require "ruby_lsp/ruby_lsp_rails/runner_client" +require "spoom/context" +require "minitest/hooks" + +module RubyLsp + module Tapioca + class RunnerClientTest < Minitest::HooksSpec + # The approach here is based on tests within the Ruby LSP Rails gem + + before do + @outgoing_queue = Thread::Queue.new + @client = T.let(RubyLsp::Rails::RunnerClient.new(@outgoing_queue), RubyLsp::Rails::RunnerClient) + end + + after do + @client.shutdown + + # On Windows, the server process sometimes takes a lot longer to shutdown and may end up getting force killed, + # which makes this assertion flaky + assert_predicate(@client, :stopped?) unless Gem.win_platform? + @outgoing_queue.close + end + + EXPECTED_RBI_PATH = "sorbet/rbi/dsl/notify_user_job.rbi" + it "generates DSL RBIs for a gem" do + raise "RBI already exists" if File.exist?(EXPECTED_RBI_PATH) + + addon_path = File.expand_path("lib/ruby_lsp/tapioca/server_addon.rb") + @client.register_server_addon(File.expand_path(addon_path)) + @client.delegate_notification( + server_addon_name: "Tapioca", + request_name: "dsl", + constants: ["NotifyUserJob"], + ) + + begin + Timeout.timeout(10) do + until File.exist?(EXPECTED_RBI_PATH) + end + end + rescue Timeout::Error + flunk("RBI file was not generated") + end + ensure + FileUtils.rm_f(EXPECTED_RBI_PATH) + end + end + end +end diff --git a/test/dummy/Rakefile b/test/dummy/Rakefile new file mode 100644 index 000000000..c4f952387 --- /dev/null +++ b/test/dummy/Rakefile @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +require_relative "config/application" + +Rails.application.load_tasks diff --git a/test/dummy/app/models/notify_user_job.rb b/test/dummy/app/models/notify_user_job.rb new file mode 100644 index 000000000..dc9683c3f --- /dev/null +++ b/test/dummy/app/models/notify_user_job.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +class NotifyUserJob < ActiveJob::Base + def perform(user) + end +end diff --git a/test/dummy/config.ru b/test/dummy/config.ru new file mode 100644 index 000000000..b5a779e16 --- /dev/null +++ b/test/dummy/config.ru @@ -0,0 +1,7 @@ +# typed: strict +# frozen_string_literal: true + +require_relative "config/environment" + +run Rails.application +Rails.application.load_server diff --git a/test/dummy/config/application.rb b/test/dummy/config/application.rb new file mode 100644 index 000000000..a37de563d --- /dev/null +++ b/test/dummy/config/application.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../../Gemfile", __dir__) + +require "bundler/setup" if File.exist?(ENV["BUNDLE_GEMFILE"]) +$LOAD_PATH.unshift(File.expand_path("../../../lib", __dir__)) + +require "rails" # minimal, instead of "rails/all" +require "active_job/railtie" + +Bundler.require(*Rails.groups) + +module Dummy + class Application < Rails::Application + end +end diff --git a/test/dummy/config/environment.rb b/test/dummy/config/environment.rb new file mode 100644 index 000000000..e8173e052 --- /dev/null +++ b/test/dummy/config/environment.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +require_relative "application" + +Rails.application.initialize!