diff options
Diffstat (limited to 'railties/test')
126 files changed, 4341 insertions, 1207 deletions
diff --git a/railties/test/abstract_unit.rb b/railties/test/abstract_unit.rb index e4b2d0457d..b42f37d6b9 100644 --- a/railties/test/abstract_unit.rb +++ b/railties/test/abstract_unit.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + ENV["RAILS_ENV"] ||= "test" require "stringio" @@ -12,8 +14,7 @@ require "rails/all" module TestApp class Application < Rails::Application - config.root = File.dirname(__FILE__) - secrets.secret_key_base = "b3c631c314c0bbca50c1b2843150fe33" + config.root = __dir__ end end diff --git a/railties/test/app_loader_test.rb b/railties/test/app_loader_test.rb index 85f5502b4d..bb556f1968 100644 --- a/railties/test/app_loader_test.rb +++ b/railties/test/app_loader_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "tmpdir" require "abstract_unit" require "rails/app_loader" diff --git a/railties/test/application/asset_debugging_test.rb b/railties/test/application/asset_debugging_test.rb index 3e17a1efa5..e56c7b958e 100644 --- a/railties/test/application/asset_debugging_test.rb +++ b/railties/test/application/asset_debugging_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "isolation/abstract_unit" require "rack/test" @@ -7,10 +9,7 @@ module ApplicationTests include Rack::Test::Methods def setup - # FIXME: shush Sass warning spam, not relevant to testing Railties - Kernel.silence_warnings do - build_app(initializers: true) - end + build_app(initializers: true) app_file "app/assets/javascripts/application.js", "//= require_tree ." app_file "app/assets/javascripts/xmlhr.js", "function f1() { alert(); }" @@ -34,16 +33,10 @@ module ApplicationTests teardown_app end - # FIXME: shush Sass warning spam, not relevant to testing Railties - def get(*) - Kernel.silence_warnings { super } - end - test "assets are concatenated when debug is off and compile is off either if debug_assets param is provided" do # config.assets.debug and config.assets.compile are false for production environment ENV["RAILS_ENV"] = "production" - output = Dir.chdir(app_path) { `bin/rails assets:precompile --trace 2>&1` } - assert $?.success?, output + rails "assets:precompile", "--trace" # Load app env app "production" diff --git a/railties/test/application/assets_test.rb b/railties/test/application/assets_test.rb index f38cacd6da..0d3262d6f6 100644 --- a/railties/test/application/assets_test.rb +++ b/railties/test/application/assets_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "isolation/abstract_unit" require "rack/test" require "active_support/json" @@ -60,10 +62,7 @@ module ApplicationTests add_to_env_config "development", "config.assets.digest = false" - # FIXME: shush Sass warning spam, not relevant to testing Railties - Kernel.silence_warnings do - require "#{app_path}/config/environment" - end + require "#{app_path}/config/environment" get "/assets/demo.js" assert_equal 'a = "/assets/rails.png";', last_response.body.strip @@ -475,9 +474,9 @@ module ApplicationTests class ::PostsController < ActionController::Base; end - get "/posts", {}, "HTTPS" => "off" + get "/posts", {}, { "HTTPS" => "off" } assert_match('src="http://example.com/assets/application.self.js', last_response.body) - get "/posts", {}, "HTTPS" => "on" + get "/posts", {}, { "HTTPS" => "on" } assert_match('src="https://example.com/assets/application.self.js', last_response.body) end diff --git a/railties/test/application/bin_setup_test.rb b/railties/test/application/bin_setup_test.rb index 0fb995900f..54934dbe24 100644 --- a/railties/test/application/bin_setup_test.rb +++ b/railties/test/application/bin_setup_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "isolation/abstract_unit" module ApplicationTests @@ -20,7 +22,7 @@ module ApplicationTests end RUBY - list_tables = lambda { `bin/rails runner 'p ActiveRecord::Base.connection.tables'`.strip } + list_tables = lambda { rails("runner", "p ActiveRecord::Base.connection.tables").strip } File.write("log/test.log", "zomg!") assert_equal "[]", list_tables.call diff --git a/railties/test/application/configuration/custom_test.rb b/railties/test/application/configuration/custom_test.rb index 8360b7bf4b..05b17b4a7a 100644 --- a/railties/test/application/configuration/custom_test.rb +++ b/railties/test/application/configuration/custom_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "isolation/abstract_unit" module ApplicationTests diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb index 14433fbba0..5f932f38db 100644 --- a/railties/test/application/configuration_test.rb +++ b/railties/test/application/configuration_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "isolation/abstract_unit" require "rack/test" require "env_helpers" @@ -38,10 +40,7 @@ module ApplicationTests @app ||= begin ENV["RAILS_ENV"] = env - # FIXME: shush Sass warning spam, not relevant to testing Railties - Kernel.silence_warnings do - require "#{app_path}/config/environment" - end + require "#{app_path}/config/environment" Rails.application ensure @@ -176,13 +175,11 @@ module ApplicationTests test "Rails.application responds to all instance methods" do app "development" - assert_respond_to Rails.application, :routes_reloader assert_equal Rails.application.routes_reloader, AppTemplate::Application.routes_reloader end test "Rails::Application responds to paths" do app "development" - assert_respond_to AppTemplate::Application, :paths assert_equal ["#{app_path}/app/views"], AppTemplate::Application.paths["app/views"].expanded end @@ -240,6 +237,66 @@ module ApplicationTests assert_instance_of Pathname, Rails.public_path end + test "does not eager load controller actions in development" do + app_file "app/controllers/posts_controller.rb", <<-RUBY + class PostsController < ActionController::Base + def index;end + def show;end + end + RUBY + + app "development" + + assert_nil PostsController.instance_variable_get(:@action_methods) + end + + test "eager loads controller actions in production" do + app_file "app/controllers/posts_controller.rb", <<-RUBY + class PostsController < ActionController::Base + def index;end + def show;end + end + RUBY + + add_to_config <<-RUBY + config.eager_load = true + config.cache_classes = true + RUBY + + app "production" + + assert_equal %w(index show).to_set, PostsController.instance_variable_get(:@action_methods) + end + + test "does not eager load mailer actions in development" do + app_file "app/mailers/posts_mailer.rb", <<-RUBY + class PostsMailer < ActionMailer::Base + def noop_email;end + end + RUBY + + app "development" + + assert_nil PostsMailer.instance_variable_get(:@action_methods) + end + + test "eager loads mailer actions in production" do + app_file "app/mailers/posts_mailer.rb", <<-RUBY + class PostsMailer < ActionMailer::Base + def noop_email;end + end + RUBY + + add_to_config <<-RUBY + config.eager_load = true + config.cache_classes = true + RUBY + + app "production" + + assert_equal %w(noop_email).to_set, PostsMailer.instance_variable_get(:@action_methods) + end + test "initialize an eager loaded, cache classes app" do add_to_config <<-RUBY config.eager_load = true @@ -257,6 +314,7 @@ module ApplicationTests end test "the application can be eager loaded even when there are no frameworks" do + FileUtils.rm_rf("#{app_path}/app/jobs/application_job.rb") FileUtils.rm_rf("#{app_path}/app/models/application_record.rb") FileUtils.rm_rf("#{app_path}/app/mailers/application_mailer.rb") FileUtils.rm_rf("#{app_path}/config/environments") @@ -418,47 +476,61 @@ module ApplicationTests test "application message verifier can be used when the key_generator is ActiveSupport::LegacyKeyGenerator" do app_file "config/initializers/secret_token.rb", <<-RUBY + Rails.application.credentials.secret_key_base = nil Rails.application.config.secret_token = "b3c631c314c0bbca50c1b2843150fe33" RUBY - app_file "config/secrets.yml", <<-YAML - development: - secret_key_base: - YAML - app "development" + app "production" - assert_equal app.env_config["action_dispatch.key_generator"], Rails.application.key_generator - assert_equal app.env_config["action_dispatch.key_generator"].class, ActiveSupport::LegacyKeyGenerator + assert_kind_of ActiveSupport::LegacyKeyGenerator, Rails.application.key_generator message = app.message_verifier(:sensitive_value).generate("some_value") assert_equal "some_value", Rails.application.message_verifier(:sensitive_value).verify(message) end - test "warns when secrets.secret_key_base is blank and config.secret_token is set" do + test "config.secret_token is deprecated" do app_file "config/initializers/secret_token.rb", <<-RUBY Rails.application.config.secret_token = "b3c631c314c0bbca50c1b2843150fe33" RUBY - app_file "config/secrets.yml", <<-YAML - development: - secret_key_base: - YAML - app "development" + app "production" - assert_deprecated(/You didn't set `secret_key_base`./) do - app.env_config + assert_deprecated(/secret_token/) do + app.secrets end end - test "raise when secrets.secret_key_base is not a type of string" do + test "secrets.secret_token is deprecated" do app_file "config/secrets.yml", <<-YAML - development: - secret_key_base: 123 + production: + secret_token: "b3c631c314c0bbca50c1b2843150fe33" YAML - app "development" + app "production" + + assert_deprecated(/secret_token/) do + app.secrets + end + end + + + test "raises when secret_key_base is blank" do + app_file "config/initializers/secret_token.rb", <<-RUBY + Rails.application.credentials.secret_key_base = nil + RUBY + + error = assert_raise(ArgumentError) do + app "production" + end + assert_match(/Missing `secret_key_base`./, error.message) + end + + test "raise when secret_key_base is not a type of string" do + add_to_config <<-RUBY + Rails.application.credentials.secret_key_base = 123 + RUBY assert_raise(ArgumentError) do - app.key_generator + app "production" end end @@ -478,7 +550,7 @@ module ApplicationTests test "application verifier can build different verifiers" do make_basic_app do |application| - application.secrets.secret_key_base = "b3c631c314c0bbca50c1b2843150fe33" + application.credentials.secret_key_base = "b3c631c314c0bbca50c1b2843150fe33" application.config.session_store :disabled end @@ -596,37 +668,15 @@ module ApplicationTests test "uses ActiveSupport::LegacyKeyGenerator as app.key_generator when secrets.secret_key_base is blank" do app_file "config/initializers/secret_token.rb", <<-RUBY + Rails.application.credentials.secret_key_base = nil Rails.application.config.secret_token = "b3c631c314c0bbca50c1b2843150fe33" RUBY - app_file "config/secrets.yml", <<-YAML - development: - secret_key_base: - YAML - app "development" + app "production" assert_equal "b3c631c314c0bbca50c1b2843150fe33", app.config.secret_token - assert_nil app.secrets.secret_key_base - assert_equal app.key_generator.class, ActiveSupport::LegacyKeyGenerator - end - - test "uses ActiveSupport::LegacyKeyGenerator with config.secret_token as app.key_generator when secrets.secret_key_base is blank" do - app_file "config/initializers/secret_token.rb", <<-RUBY - Rails.application.config.secret_token = "" - RUBY - app_file "config/secrets.yml", <<-YAML - development: - secret_key_base: - YAML - - app "development" - - assert_equal "", app.config.secret_token - assert_nil app.secrets.secret_key_base - e = assert_raise ArgumentError do - app.key_generator - end - assert_match(/\AA secret is required/, e.message) + assert_nil app.credentials.secret_key_base + assert_kind_of ActiveSupport::LegacyKeyGenerator, app.key_generator end test "that nested keys are symbolized the same as parents for hashes more than one level deep" do @@ -643,6 +693,28 @@ module ApplicationTests assert_equal "697361616320736c6f616e2028656c6f7265737429", app.secrets.smtp_settings[:password] end + test "require_master_key aborts app boot when missing key" do + skip "can't run without fork" unless Process.respond_to?(:fork) + + remove_file "config/master.key" + add_to_config "config.require_master_key = true" + + error = capture(:stderr) do + Process.wait(Process.fork { app "development" }) + end + + assert_equal 1, $?.exitstatus + assert_match(/Missing.*RAILS_MASTER_KEY/, error) + end + + test "credentials does not raise error when require_master_key is false and master key does not exist" do + remove_file "config/master.key" + add_to_config "config.require_master_key = false" + app "development" + + assert_not app.credentials.secret_key_base + end + test "protect from forgery is the default in a new app" do make_basic_app @@ -693,6 +765,128 @@ module ApplicationTests assert_match(/label/, last_response.body) end + test "form_with can be configured with form_with_generates_ids" do + app_file "config/initializers/form_builder.rb", <<-RUBY + Rails.configuration.action_view.form_with_generates_ids = false + RUBY + + app_file "app/models/post.rb", <<-RUBY + class Post + include ActiveModel::Model + attr_accessor :name + end + RUBY + + app_file "app/controllers/posts_controller.rb", <<-RUBY + class PostsController < ApplicationController + def index + render inline: "<%= begin; form_with(model: Post.new) {|f| f.text_field(:name)}; rescue => e; e.to_s; end %>" + end + end + RUBY + + add_to_config <<-RUBY + routes.prepend do + resources :posts + end + RUBY + + app "development" + + get "/posts" + + assert_no_match(/id=('|")post_name('|")/, last_response.body) + end + + test "form_with outputs ids by default" do + app_file "app/models/post.rb", <<-RUBY + class Post + include ActiveModel::Model + attr_accessor :name + end + RUBY + + app_file "app/controllers/posts_controller.rb", <<-RUBY + class PostsController < ApplicationController + def index + render inline: "<%= begin; form_with(model: Post.new) {|f| f.text_field(:name)}; rescue => e; e.to_s; end %>" + end + end + RUBY + + add_to_config <<-RUBY + routes.prepend do + resources :posts + end + RUBY + + app "development" + + get "/posts" + + assert_match(/id=('|")post_name('|")/, last_response.body) + end + + test "form_with can be configured with form_with_generates_remote_forms" do + app_file "config/initializers/form_builder.rb", <<-RUBY + Rails.configuration.action_view.form_with_generates_remote_forms = false + RUBY + + app_file "app/models/post.rb", <<-RUBY + class Post + include ActiveModel::Model + attr_accessor :name + end + RUBY + + app_file "app/controllers/posts_controller.rb", <<-RUBY + class PostsController < ApplicationController + def index + render inline: "<%= begin; form_with(model: Post.new) {|f| f.text_field(:name)}; rescue => e; e.to_s; end %>" + end + end + RUBY + + add_to_config <<-RUBY + routes.prepend do + resources :posts + end + RUBY + + app "development" + + get "/posts" + assert_no_match(/data-remote/, last_response.body) + end + + test "form_with generates remote forms by default" do + app_file "app/models/post.rb", <<-RUBY + class Post + include ActiveModel::Model + attr_accessor :name + end + RUBY + + app_file "app/controllers/posts_controller.rb", <<-RUBY + class PostsController < ApplicationController + def index + render inline: "<%= begin; form_with(model: Post.new) {|f| f.text_field(:name)}; rescue => e; e.to_s; end %>" + end + end + RUBY + + add_to_config <<-RUBY + routes.prepend do + resources :posts + end + RUBY + + app "development" + + get "/posts" + assert_match(/data-remote/, last_response.body) + end + test "default method for update can be changed" do app_file "app/models/post.rb", <<-RUBY class Post @@ -1072,6 +1266,9 @@ module ApplicationTests app "development" + force_lazy_load_hooks { ActionController::Base } + force_lazy_load_hooks { ActionController::API } + assert_equal :raise, ActionController::Parameters.action_on_unpermitted_parameters post "/posts", post: { "title" => "zomg" } @@ -1080,6 +1277,10 @@ module ApplicationTests test "config.action_controller.always_permitted_parameters are: controller, action by default" do app "development" + + force_lazy_load_hooks { ActionController::Base } + force_lazy_load_hooks { ActionController::API } + assert_equal %w(controller action), ActionController::Parameters.always_permitted_parameters end @@ -1090,6 +1291,9 @@ module ApplicationTests app "development" + force_lazy_load_hooks { ActionController::Base } + force_lazy_load_hooks { ActionController::API } + assert_equal %w( controller action format ), ActionController::Parameters.always_permitted_parameters end @@ -1112,30 +1316,85 @@ module ApplicationTests app "development" + force_lazy_load_hooks { ActionController::Base } + force_lazy_load_hooks { ActionController::API } + assert_equal :raise, ActionController::Parameters.action_on_unpermitted_parameters post "/posts", post: { "title" => "zomg" }, format: "json" assert_equal 200, last_response.status end - test "config.action_controller.action_on_unpermitted_parameters is :log by default on development" do + test "config.action_controller.action_on_unpermitted_parameters is :log by default in development" do app "development" + force_lazy_load_hooks { ActionController::Base } + force_lazy_load_hooks { ActionController::API } + assert_equal :log, ActionController::Parameters.action_on_unpermitted_parameters end - test "config.action_controller.action_on_unpermitted_parameters is :log by default on test" do + test "config.action_controller.action_on_unpermitted_parameters is :log by default in test" do app "test" + force_lazy_load_hooks { ActionController::Base } + force_lazy_load_hooks { ActionController::API } + assert_equal :log, ActionController::Parameters.action_on_unpermitted_parameters end - test "config.action_controller.action_on_unpermitted_parameters is false by default on production" do + test "config.action_controller.action_on_unpermitted_parameters is false by default in production" do app "production" + force_lazy_load_hooks { ActionController::Base } + force_lazy_load_hooks { ActionController::API } + assert_equal false, ActionController::Parameters.action_on_unpermitted_parameters end + test "config.action_controller.default_protect_from_forgery is true by default" do + app "development" + + assert_equal true, ActionController::Base.default_protect_from_forgery + assert_includes ActionController::Base.__callbacks[:process_action].map(&:filter), :verify_authenticity_token + end + + test "config.action_controller.permit_all_parameters can be configured in an initializer" do + app_file "config/initializers/permit_all_parameters.rb", <<-RUBY + Rails.application.config.action_controller.permit_all_parameters = true + RUBY + + app "development" + + force_lazy_load_hooks { ActionController::Base } + force_lazy_load_hooks { ActionController::API } + assert_equal true, ActionController::Parameters.permit_all_parameters + end + + test "config.action_controller.always_permitted_parameters can be configured in an initializer" do + app_file "config/initializers/always_permitted_parameters.rb", <<-RUBY + Rails.application.config.action_controller.always_permitted_parameters = [] + RUBY + + app "development" + + force_lazy_load_hooks { ActionController::Base } + force_lazy_load_hooks { ActionController::API } + assert_equal [], ActionController::Parameters.always_permitted_parameters + end + + test "config.action_controller.action_on_unpermitted_parameters can be configured in an initializer" do + app_file "config/initializers/action_on_unpermitted_parameters.rb", <<-RUBY + Rails.application.config.action_controller.action_on_unpermitted_parameters = :raise + RUBY + + app "development" + + force_lazy_load_hooks { ActionController::Base } + force_lazy_load_hooks { ActionController::API } + assert_equal :raise, ActionController::Parameters.action_on_unpermitted_parameters + end + test "config.action_dispatch.ignore_accept_header" do make_basic_app do |application| application.config.action_dispatch.ignore_accept_header = true @@ -1150,17 +1409,16 @@ module ApplicationTests end end - get "/", {}, "HTTP_ACCEPT" => "application/xml" + get "/", {}, { "HTTP_ACCEPT" => "application/xml" } assert_equal "HTML", last_response.body - get "/", { format: :xml }, "HTTP_ACCEPT" => "application/xml" + get "/", { format: :xml }, { "HTTP_ACCEPT" => "application/xml" } assert_equal "XML", last_response.body end test "Rails.application#env_config exists and include some existing parameters" do make_basic_app - assert_respond_to app, :env_config assert_equal app.env_config["action_dispatch.parameter_filter"], app.config.filter_parameters assert_equal app.env_config["action_dispatch.show_exceptions"], app.config.action_dispatch.show_exceptions assert_equal app.env_config["action_dispatch.logger"], Rails.logger @@ -1232,12 +1490,18 @@ module ApplicationTests assert_not ActiveRecord::Base.dump_schema_after_migration end - test "config.active_record.dump_schema_after_migration is true by default on development" do + test "config.active_record.dump_schema_after_migration is true by default in development" do app "development" assert ActiveRecord::Base.dump_schema_after_migration end + test "config.active_record.verbose_query_logs is false by default in development" do + app "development" + + assert_not ActiveRecord::Base.verbose_query_logs + end + test "config.annotations wrapping SourceAnnotationExtractor::Annotation class" do make_basic_app do |application| application.config.annotations.register_extensions("coffee") do |tag| @@ -1340,13 +1604,47 @@ module ApplicationTests test "raises with proper error message if no database configuration found" do FileUtils.rm("#{app_path}/config/database.yml") - app "development" err = assert_raises RuntimeError do + app "development" Rails.application.config.database_configuration end assert_match "config/database", err.message end + test "loads database.yml using shared keys" do + app_file "config/database.yml", <<-YAML + shared: + username: bobby + adapter: sqlite3 + + development: + database: 'dev_db' + YAML + + app "development" + + ar_config = Rails.application.config.database_configuration + assert_equal "sqlite3", ar_config["development"]["adapter"] + assert_equal "bobby", ar_config["development"]["username"] + assert_equal "dev_db", ar_config["development"]["database"] + end + + test "loads database.yml using shared keys for undefined environments" do + app_file "config/database.yml", <<-YAML + shared: + username: bobby + adapter: sqlite3 + database: 'dev_db' + YAML + + app "development" + + ar_config = Rails.application.config.database_configuration + assert_equal "sqlite3", ar_config["development"]["adapter"] + assert_equal "bobby", ar_config["development"]["username"] + assert_equal "dev_db", ar_config["development"]["database"] + end + test "config.action_mailer.show_previews defaults to true in development" do app "development" @@ -1439,6 +1737,50 @@ module ApplicationTests assert_equal({}, Rails.application.config.my_custom_config) end + test "default SQLite3Adapter.represent_boolean_as_integer for 5.1 is false" do + remove_from_config '.*config\.load_defaults.*\n' + + app_file "app/models/post.rb", <<-RUBY + class Post < ActiveRecord::Base + end + RUBY + + app "development" + force_lazy_load_hooks { Post } + + assert_not ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer + end + + test "default SQLite3Adapter.represent_boolean_as_integer for new installs is true" do + app_file "app/models/post.rb", <<-RUBY + class Post < ActiveRecord::Base + end + RUBY + + app "development" + force_lazy_load_hooks { Post } + + assert ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer + end + + test "represent_boolean_as_integer should be able to set via config.active_record.sqlite3.represent_boolean_as_integer" do + remove_from_config '.*config\.load_defaults.*\n' + + app_file "config/initializers/new_framework_defaults_5_2.rb", <<-RUBY + Rails.application.config.active_record.sqlite3.represent_boolean_as_integer = true + RUBY + + app_file "app/models/post.rb", <<-RUBY + class Post < ActiveRecord::Base + end + RUBY + + app "development" + force_lazy_load_hooks { Post } + + assert ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer + end + test "config_for containing ERB tags should evaluate" do app_file "config/custom.yml", <<-RUBY development: @@ -1545,5 +1887,62 @@ module ApplicationTests assert_equal 301, last_response.status assert_equal "https://example.org/", last_response.location end + + test "ActiveSupport::MessageEncryptor.use_authenticated_message_encryption is true by default for new apps" do + app "development" + + assert_equal true, ActiveSupport::MessageEncryptor.use_authenticated_message_encryption + end + + test "ActiveSupport::MessageEncryptor.use_authenticated_message_encryption is false by default for upgraded apps" do + remove_from_config '.*config\.load_defaults.*\n' + + app "development" + + assert_equal false, ActiveSupport::MessageEncryptor.use_authenticated_message_encryption + end + + test "ActiveSupport::MessageEncryptor.use_authenticated_message_encryption can be configured via config.active_support.use_authenticated_message_encryption" do + remove_from_config '.*config\.load_defaults.*\n' + + app_file "config/initializers/new_framework_defaults_5_2.rb", <<-RUBY + Rails.application.config.active_support.use_authenticated_message_encryption = true + RUBY + + app "development" + + assert_equal true, ActiveSupport::MessageEncryptor.use_authenticated_message_encryption + end + + test "ActiveSupport::Digest.hash_digest_class is Digest::SHA1 by default for new apps" do + app "development" + + assert_equal Digest::SHA1, ActiveSupport::Digest.hash_digest_class + end + + test "ActiveSupport::Digest.hash_digest_class is Digest::MD5 by default for upgraded apps" do + remove_from_config '.*config\.load_defaults.*\n' + + app "development" + + assert_equal Digest::MD5, ActiveSupport::Digest.hash_digest_class + end + + test "ActiveSupport::Digest.hash_digest_class can be configured via config.active_support.use_sha1_digests" do + remove_from_config '.*config\.load_defaults.*\n' + + app_file "config/initializers/new_framework_defaults_5_2.rb", <<-RUBY + Rails.application.config.active_support.use_sha1_digests = true + RUBY + + app "development" + + assert_equal Digest::SHA1, ActiveSupport::Digest.hash_digest_class + end + + private + def force_lazy_load_hooks + yield # Tasty clarifying sugar, homie! We only need to reference a constant to load it. + end end end diff --git a/railties/test/application/console_test.rb b/railties/test/application/console_test.rb index 72f340df34..13164f49c2 100644 --- a/railties/test/application/console_test.rb +++ b/railties/test/application/console_test.rb @@ -1,4 +1,7 @@ +# frozen_string_literal: true + require "isolation/abstract_unit" +require "console_helpers" class ConsoleTest < ActiveSupport::TestCase include ActiveSupport::Testing::Isolation @@ -93,14 +96,11 @@ class ConsoleTest < ActiveSupport::TestCase end end -begin - require "pty" -rescue LoadError -end - class FullStackConsoleTest < ActiveSupport::TestCase + include ConsoleHelpers + def setup - skip "PTY unavailable" unless defined?(PTY) && PTY.respond_to?(:open) + skip "PTY unavailable" unless available_pty? build_app app_file "app/models/post.rb", <<-CODE @@ -116,48 +116,43 @@ class FullStackConsoleTest < ActiveSupport::TestCase teardown_app end - def assert_output(expected, timeout = 1) - timeout = Time.now + timeout - - output = "" - until output.include?(expected) || Time.now > timeout - if IO.select([@master], [], [], 0.1) - output << @master.read(1) - end - end - - assert_includes output, expected, "#{expected.inspect} expected, but got:\n\n#{output}" - end - def write_prompt(command, expected_output = nil) @master.puts command - assert_output command - assert_output expected_output if expected_output - assert_output "> " + assert_output command, @master + assert_output expected_output, @master if expected_output + assert_output "> ", @master end - def spawn_console + def spawn_console(options) Process.spawn( - "#{app_path}/bin/rails console --sandbox", + "#{app_path}/bin/rails console #{options}", in: @slave, out: @slave, err: @slave ) - assert_output "> ", 30 + assert_output "> ", @master, 30 end def test_sandbox - spawn_console + spawn_console("--sandbox") write_prompt "Post.count", "=> 0" write_prompt "Post.create" write_prompt "Post.count", "=> 1" @master.puts "quit" - spawn_console + spawn_console("--sandbox") write_prompt "Post.count", "=> 0" write_prompt "Post.transaction { Post.create; raise }" write_prompt "Post.count", "=> 0" @master.puts "quit" end + + def test_environment_option_and_irb_option + spawn_console("test -- --verbose") + + write_prompt "a = 1", "a = 1" + write_prompt "puts Rails.env", "puts Rails.env\r\ntest" + @master.puts "quit" + end end diff --git a/railties/test/application/content_security_policy_test.rb b/railties/test/application/content_security_policy_test.rb new file mode 100644 index 0000000000..97f2957c33 --- /dev/null +++ b/railties/test/application/content_security_policy_test.rb @@ -0,0 +1,197 @@ +# frozen_string_literal: true + +require "isolation/abstract_unit" +require "rack/test" + +module ApplicationTests + class ContentSecurityPolicyTest < ActiveSupport::TestCase + include ActiveSupport::Testing::Isolation + include Rack::Test::Methods + + def setup + build_app + end + + def teardown + teardown_app + end + + test "default content security policy is empty" do + controller :pages, <<-RUBY + class PagesController < ApplicationController + def index + render html: "<h1>Welcome to Rails!</h1>" + end + end + RUBY + + app_file "config/routes.rb", <<-RUBY + Rails.application.routes.draw do + root to: "pages#index" + end + RUBY + + app("development") + + get "/" + assert_equal ";", last_response.headers["Content-Security-Policy"] + end + + test "global content security policy in an initializer" do + controller :pages, <<-RUBY + class PagesController < ApplicationController + def index + render html: "<h1>Welcome to Rails!</h1>" + end + end + RUBY + + app_file "config/initializers/content_security_policy.rb", <<-RUBY + Rails.application.config.content_security_policy do |p| + p.default_src :self, :https + end + RUBY + + app_file "config/routes.rb", <<-RUBY + Rails.application.routes.draw do + root to: "pages#index" + end + RUBY + + app("development") + + get "/" + assert_policy "default-src 'self' https:;" + end + + test "global report only content security policy in an initializer" do + controller :pages, <<-RUBY + class PagesController < ApplicationController + def index + render html: "<h1>Welcome to Rails!</h1>" + end + end + RUBY + + app_file "config/initializers/content_security_policy.rb", <<-RUBY + Rails.application.config.content_security_policy do |p| + p.default_src :self, :https + end + + Rails.application.config.content_security_policy_report_only = true + RUBY + + app_file "config/routes.rb", <<-RUBY + Rails.application.routes.draw do + root to: "pages#index" + end + RUBY + + app("development") + + get "/" + assert_policy "default-src 'self' https:;", report_only: true + end + + test "override content security policy in a controller" do + controller :pages, <<-RUBY + class PagesController < ApplicationController + content_security_policy do |p| + p.default_src "https://example.com" + end + + def index + render html: "<h1>Welcome to Rails!</h1>" + end + end + RUBY + + app_file "config/initializers/content_security_policy.rb", <<-RUBY + Rails.application.config.content_security_policy do |p| + p.default_src :self, :https + end + RUBY + + app_file "config/routes.rb", <<-RUBY + Rails.application.routes.draw do + root to: "pages#index" + end + RUBY + + app("development") + + get "/" + assert_policy "default-src https://example.com;" + end + + test "override content security policy to report only in a controller" do + controller :pages, <<-RUBY + class PagesController < ApplicationController + content_security_policy_report_only + + def index + render html: "<h1>Welcome to Rails!</h1>" + end + end + RUBY + + app_file "config/initializers/content_security_policy.rb", <<-RUBY + Rails.application.config.content_security_policy do |p| + p.default_src :self, :https + end + RUBY + + app_file "config/routes.rb", <<-RUBY + Rails.application.routes.draw do + root to: "pages#index" + end + RUBY + + app("development") + + get "/" + assert_policy "default-src 'self' https:;", report_only: true + end + + test "global content security policy added to rack app" do + app_file "config/initializers/content_security_policy.rb", <<-RUBY + Rails.application.config.content_security_policy do |p| + p.default_src :self, :https + end + RUBY + + app_file "config/routes.rb", <<-RUBY + Rails.application.routes.draw do + + app = ->(env) { + [200, { "Content-Type" => "text/html" }, ["<p>Hello, World!</p>"]] + } + + root to: app + end + RUBY + + app("development") + + get "/" + assert_policy "default-src 'self' https:;" + end + + private + + def assert_policy(expected, report_only: false) + assert_equal 200, last_response.status + + if report_only + expected_header = "Content-Security-Policy-Report-Only" + unexpected_header = "Content-Security-Policy" + else + expected_header = "Content-Security-Policy" + unexpected_header = "Content-Security-Policy-Report-Only" + end + + assert_nil last_response.headers[unexpected_header] + assert_equal expected, last_response.headers[expected_header] + end + end +end diff --git a/railties/test/application/current_attributes_integration_test.rb b/railties/test/application/current_attributes_integration_test.rb new file mode 100644 index 0000000000..146e96facc --- /dev/null +++ b/railties/test/application/current_attributes_integration_test.rb @@ -0,0 +1,88 @@ +# frozen_string_literal: true + +require "isolation/abstract_unit" +require "rack/test" + +class CurrentAttributesIntegrationTest < ActiveSupport::TestCase + include ActiveSupport::Testing::Isolation + include Rack::Test::Methods + + setup do + build_app + + app_file "app/models/current.rb", <<-RUBY + class Current < ActiveSupport::CurrentAttributes + attribute :customer + + resets { Time.zone = "UTC" } + + def customer=(customer) + super + Time.zone = customer.try(:time_zone) + end + end + RUBY + + app_file "app/models/customer.rb", <<-RUBY + class Customer < Struct.new(:name) + def time_zone + "Copenhagen" + end + end + RUBY + + app_file "config/routes.rb", <<-RUBY + Rails.application.routes.draw do + get "/customers/:action", controller: :customers + end + RUBY + + app_file "app/controllers/customers_controller.rb", <<-RUBY + class CustomersController < ApplicationController + layout false + + def set_current_customer + Current.customer = Customer.new("david") + render :index + end + + def set_no_customer + render :index + end + end + RUBY + + app_file "app/views/customers/index.html.erb", <<-RUBY + <%= Current.customer.try(:name) || 'noone' %>,<%= Time.zone.name %> + RUBY + + require "#{app_path}/config/environment" + end + + teardown :teardown_app + + test "current customer is assigned and cleared" do + get "/customers/set_current_customer" + assert_equal 200, last_response.status + assert_match(/david,Copenhagen/, last_response.body) + + get "/customers/set_no_customer" + assert_equal 200, last_response.status + assert_match(/noone,UTC/, last_response.body) + end + + test "resets after execution" do + assert_nil Current.customer + assert_equal "UTC", Time.zone.name + + Rails.application.executor.wrap do + Current.customer = Customer.new("david") + + assert_equal "david", Current.customer.name + assert_equal "Copenhagen", Time.zone.name + end + + assert_nil Current.customer + assert_equal "UTC", Time.zone.name + end +end diff --git a/railties/test/application/dbconsole_test.rb b/railties/test/application/dbconsole_test.rb new file mode 100644 index 0000000000..8eb293c179 --- /dev/null +++ b/railties/test/application/dbconsole_test.rb @@ -0,0 +1,74 @@ +# frozen_string_literal: true + +require "isolation/abstract_unit" +require "console_helpers" + +module ApplicationTests + class DBConsoleTest < ActiveSupport::TestCase + include ActiveSupport::Testing::Isolation + include ConsoleHelpers + + def setup + skip "PTY unavailable" unless available_pty? + + build_app + end + + def teardown + teardown_app + end + + def test_use_value_defined_in_environment_file_in_database_yml + app_file "config/database.yml", <<-YAML + development: + database: <%= Rails.application.config.database %> + adapter: sqlite3 + pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> + timeout: 5000 + YAML + + app_file "config/environments/development.rb", <<-RUBY + Rails.application.configure do + config.database = "db/development.sqlite3" + end + RUBY + + master, slave = PTY.open + spawn_dbconsole(slave) + assert_output("sqlite>", master) + ensure + master.puts ".exit" + end + + def test_respect_environment_option + app_file "config/database.yml", <<-YAML + default: &default + adapter: sqlite3 + pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> + timeout: 5000 + + development: + <<: *default + database: db/development.sqlite3 + + production: + <<: *default + database: db/production.sqlite3 + YAML + + master, slave = PTY.open + spawn_dbconsole(slave, "-e production") + assert_output("sqlite>", master) + + master.puts "pragma database_list;" + assert_output("production.sqlite3", master) + ensure + master.puts ".exit" + end + + private + def spawn_dbconsole(fd, options = nil) + Process.spawn("#{app_path}/bin/rails dbconsole #{options}", in: fd, out: fd, err: fd) + end + end +end diff --git a/railties/test/application/generators_test.rb b/railties/test/application/generators_test.rb index ee0d697599..e5e557d204 100644 --- a/railties/test/application/generators_test.rb +++ b/railties/test/application/generators_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "isolation/abstract_unit" module ApplicationTests @@ -29,7 +31,7 @@ module ApplicationTests end test "allow running plugin new generator inside Rails app directory" do - FileUtils.cd(rails_root) { `ruby bin/rails plugin new vendor/plugins/bukkits` } + rails "plugin", "new", "vendor/plugins/bukkits" assert File.exist?(File.join(rails_root, "vendor/plugins/bukkits/test/dummy/config/application.rb")) end @@ -165,13 +167,14 @@ module ApplicationTests config.api_only = true RUBY - FileUtils.cd(rails_root) { `bin/rails generate mailer notifier foo` } + rails "generate", "mailer", "notifier", "foo" assert File.exist?(File.join(rails_root, "app/views/notifier_mailer/foo.text.erb")) assert File.exist?(File.join(rails_root, "app/views/notifier_mailer/foo.html.erb")) end test "ARGV is mutated as expected" do require "#{app_path}/config/environment" + require "rails/command" Rails::Command.const_set("APP_PATH", "rails/all") FileUtils.cd(rails_root) do @@ -185,9 +188,13 @@ module ApplicationTests Rails::Command.send(:remove_const, "APP_PATH") end - test "help does not show hidden namespaces" do + test "help does not show hidden namespaces and hidden commands" do FileUtils.cd(rails_root) do - output = `bin/rails generate --help` + output = rails("generate", "--help") + assert_no_match "active_record:migration", output + assert_no_match "credentials", output + + output = rails("destroy", "--help") assert_no_match "active_record:migration", output end end diff --git a/railties/test/application/help_test.rb b/railties/test/application/help_test.rb index 0c3fe8bfa3..f728fc3b85 100644 --- a/railties/test/application/help_test.rb +++ b/railties/test/application/help_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "isolation/abstract_unit" class HelpTest < ActiveSupport::TestCase @@ -12,12 +14,12 @@ class HelpTest < ActiveSupport::TestCase end test "command works" do - output = Dir.chdir(app_path) { `bin/rails help` } + output = rails("help") assert_match "The most common rails commands are", output end test "short-cut alias works" do - output = Dir.chdir(app_path) { `bin/rails -h` } + output = rails("-h") assert_match "The most common rails commands are", output end end diff --git a/railties/test/application/initializers/frameworks_test.rb b/railties/test/application/initializers/frameworks_test.rb index 90927159dd..d2b77bd015 100644 --- a/railties/test/application/initializers/frameworks_test.rb +++ b/railties/test/application/initializers/frameworks_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "isolation/abstract_unit" module ApplicationTests @@ -209,24 +211,20 @@ module ApplicationTests test "database middleware doesn't initialize when activerecord is not in frameworks" do use_frameworks [] require "#{app_path}/config/environment" - assert_nil defined?(ActiveRecord::Base) + assert !defined?(ActiveRecord::Base) || ActiveRecord.autoload?(:Base) end test "use schema cache dump" do - Dir.chdir(app_path) do - `rails generate model post title:string; - bin/rails db:migrate db:schema:cache:dump` - end + rails %w(generate model post title:string) + rails %w(db:migrate db:schema:cache:dump) require "#{app_path}/config/environment" ActiveRecord::Base.connection.drop_table("posts") # force drop posts table for test. assert ActiveRecord::Base.connection.schema_cache.data_sources("posts") end test "expire schema cache dump" do - Dir.chdir(app_path) do - `rails generate model post title:string; - bin/rails db:migrate db:schema:cache:dump db:rollback` - end + rails %w(generate model post title:string) + rails %w(db:migrate db:schema:cache:dump db:rollback) require "#{app_path}/config/environment" assert !ActiveRecord::Base.connection.schema_cache.data_sources("posts") end @@ -262,5 +260,13 @@ module ApplicationTests Rails.env = orig_rails_env if orig_rails_env end end + + test "connections checked out during initialization are returned to the pool" do + app_file "config/initializers/active_record.rb", <<-RUBY + ActiveRecord::Base.connection + RUBY + require "#{app_path}/config/environment" + assert !ActiveRecord::Base.connection_pool.active_connection? + end end end diff --git a/railties/test/application/initializers/hooks_test.rb b/railties/test/application/initializers/hooks_test.rb index 36926c50ff..1e130c2f9e 100644 --- a/railties/test/application/initializers/hooks_test.rb +++ b/railties/test/application/initializers/hooks_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "isolation/abstract_unit" module ApplicationTests diff --git a/railties/test/application/initializers/i18n_test.rb b/railties/test/application/initializers/i18n_test.rb index 206e42703b..8058052771 100644 --- a/railties/test/application/initializers/i18n_test.rb +++ b/railties/test/application/initializers/i18n_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "isolation/abstract_unit" module ApplicationTests @@ -245,7 +247,7 @@ fr: end test "[shortcut] config.i18n.fallbacks = [{ :ca => :'es-ES' }] initializes fallbacks with a mapping ca => es-ES" do - I18n::Railtie.config.i18n.fallbacks.map = { ca: :'es-ES' } + I18n::Railtie.config.i18n.fallbacks = [{ ca: :'es-ES' }] load_app assert_fallbacks ca: [:ca, :"es-ES", :es, :en] end diff --git a/railties/test/application/initializers/load_path_test.rb b/railties/test/application/initializers/load_path_test.rb index dbefb22837..78cd4776d6 100644 --- a/railties/test/application/initializers/load_path_test.rb +++ b/railties/test/application/initializers/load_path_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "isolation/abstract_unit" module ApplicationTests diff --git a/railties/test/application/initializers/notifications_test.rb b/railties/test/application/initializers/notifications_test.rb index b847ac6b36..c65c955734 100644 --- a/railties/test/application/initializers/notifications_test.rb +++ b/railties/test/application/initializers/notifications_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "isolation/abstract_unit" module ApplicationTests @@ -30,6 +32,7 @@ module ApplicationTests logger = ActiveSupport::LogSubscriber::TestHelper::MockLogger.new ActiveRecord::Base.logger = logger + ActiveRecord::Base.verbose_query_logs = false # Mimic Active Record notifications instrument "sql.active_record", name: "SQL", sql: "SHOW tables" diff --git a/railties/test/application/integration_test_case_test.rb b/railties/test/application/integration_test_case_test.rb index 1118e5037a..c08761092b 100644 --- a/railties/test/application/integration_test_case_test.rb +++ b/railties/test/application/integration_test_case_test.rb @@ -1,8 +1,11 @@ +# frozen_string_literal: true + require "isolation/abstract_unit" +require "env_helpers" module ApplicationTests class IntegrationTestCaseTest < ActiveSupport::TestCase - include ActiveSupport::Testing::Isolation + include ActiveSupport::Testing::Isolation, EnvHelpers setup do build_app @@ -13,7 +16,7 @@ module ApplicationTests end test "resets Action Mailer test deliveries" do - script("generate mailer BaseMailer welcome") + rails "generate", "mailer", "BaseMailer", "welcome" app_file "test/integration/mailer_integration_test.rb", <<-RUBY require 'test_helper' @@ -37,14 +40,14 @@ module ApplicationTests end RUBY - output = Dir.chdir(app_path) { `bin/rails test 2>&1` } - assert_equal 0, $?.to_i, output + with_rails_env("test") { rails("db:migrate") } + output = rails("test") assert_match(/0 failures, 0 errors/, output) end end class IntegrationTestDefaultApp < ActiveSupport::TestCase - include ActiveSupport::Testing::Isolation + include ActiveSupport::Testing::Isolation, EnvHelpers setup do build_app @@ -65,8 +68,8 @@ module ApplicationTests end RUBY - output = Dir.chdir(app_path) { `bin/rails test 2>&1` } - assert_equal 0, $?.to_i, output + with_rails_env("test") { rails("db:migrate") } + output = rails("test") assert_match(/0 failures, 0 errors/, output) end end diff --git a/railties/test/application/loading_test.rb b/railties/test/application/loading_test.rb index c75a25bc6f..de1e240fd3 100644 --- a/railties/test/application/loading_test.rb +++ b/railties/test/application/loading_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "isolation/abstract_unit" class LoadingTest < ActiveSupport::TestCase @@ -115,11 +117,11 @@ class LoadingTest < ActiveSupport::TestCase require "#{rails_root}/config/environment" setup_ar! - assert_equal [ActiveRecord::SchemaMigration, ActiveRecord::InternalMetadata], ActiveRecord::Base.descendants + assert_equal [ActiveStorage::Blob, ActiveStorage::Attachment, ActiveRecord::SchemaMigration, ActiveRecord::InternalMetadata].collect(&:to_s).sort, ActiveRecord::Base.descendants.collect(&:to_s).sort get "/load" - assert_equal [ActiveRecord::SchemaMigration, ActiveRecord::InternalMetadata, Post], ActiveRecord::Base.descendants + assert_equal [ActiveStorage::Blob, ActiveStorage::Attachment, ActiveRecord::SchemaMigration, ActiveRecord::InternalMetadata, Post].collect(&:to_s).sort, ActiveRecord::Base.descendants.collect(&:to_s).sort get "/unload" - assert_equal [ActiveRecord::SchemaMigration, ActiveRecord::InternalMetadata], ActiveRecord::Base.descendants + assert_equal [ActiveStorage::Blob, ActiveStorage::Attachment, ActiveRecord::SchemaMigration, ActiveRecord::InternalMetadata].collect(&:to_s).sort, ActiveRecord::Base.descendants.collect(&:to_s).sort end test "initialize cant be called twice" do @@ -298,7 +300,7 @@ class LoadingTest < ActiveSupport::TestCase end MIGRATION - Dir.chdir(app_path) { `rake db:migrate` } + rails("db:migrate") require "#{rails_root}/config/environment" get "/title" @@ -312,7 +314,7 @@ class LoadingTest < ActiveSupport::TestCase end MIGRATION - Dir.chdir(app_path) { `rake db:migrate` } + rails("db:migrate") get "/body" assert_equal "BODY", last_response.body diff --git a/railties/test/application/mailer_previews_test.rb b/railties/test/application/mailer_previews_test.rb index c3a360e5d4..4e77cece1b 100644 --- a/railties/test/application/mailer_previews_test.rb +++ b/railties/test/application/mailer_previews_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "isolation/abstract_unit" require "rack/test" require "base64" @@ -30,7 +32,7 @@ module ApplicationTests test "/rails/mailers is accessible with correct configuration" do add_to_config "config.action_mailer.show_previews = true" app("production") - get "/rails/mailers", {}, "REMOTE_ADDR" => "4.2.42.42" + get "/rails/mailers", {}, { "REMOTE_ADDR" => "4.2.42.42" } assert_equal 200, last_response.status end @@ -480,11 +482,62 @@ module ApplicationTests app("development") - get "/rails/mailers", {}, "SCRIPT_NAME" => "/my_app" + get "/rails/mailers", {}, { "SCRIPT_NAME" => "/my_app" } assert_match '<h3><a href="/my_app/rails/mailers/notifier">Notifier</a></h3>', last_response.body assert_match '<li><a href="/my_app/rails/mailers/notifier/foo">foo</a></li>', last_response.body end + test "mailer preview receives query params" do + mailer "notifier", <<-RUBY + class Notifier < ActionMailer::Base + default from: "from@example.com" + + def foo(name) + @name = name + mail to: "to@example.org" + end + end + RUBY + + html_template "notifier/foo", <<-RUBY + <p>Hello, <%= @name %>!</p> + RUBY + + text_template "notifier/foo", <<-RUBY + Hello, <%= @name %>! + RUBY + + mailer_preview "notifier", <<-RUBY + class NotifierPreview < ActionMailer::Preview + def foo + Notifier.foo(params[:name] || "World") + end + end + RUBY + + app("development") + + get "/rails/mailers/notifier/foo.txt" + assert_equal 200, last_response.status + assert_match '<iframe seamless name="messageBody" src="?part=text%2Fplain">', last_response.body + assert_match '<option selected value="?part=text%2Fplain">', last_response.body + assert_match '<option value="?part=text%2Fhtml">', last_response.body + + get "/rails/mailers/notifier/foo?part=text%2Fplain" + assert_equal 200, last_response.status + assert_match %r[Hello, World!], last_response.body + + get "/rails/mailers/notifier/foo.html?name=Ruby" + assert_equal 200, last_response.status + assert_match '<iframe seamless name="messageBody" src="?name=Ruby&part=text%2Fhtml">', last_response.body + assert_match '<option selected value="?name=Ruby&part=text%2Fhtml">', last_response.body + assert_match '<option value="?name=Ruby&part=text%2Fplain">', last_response.body + + get "/rails/mailers/notifier/foo?name=Ruby&part=text%2Fhtml" + assert_equal 200, last_response.status + assert_match %r[<p>Hello, Ruby!</p>], last_response.body + end + test "plain text mailer preview with attachment" do image_file "pixel.png", "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEWzIioca/JlAAAACklEQVQI12NgAAAAAgAB4iG8MwAAAABJRU5ErkJgggo=" diff --git a/railties/test/application/middleware/cache_test.rb b/railties/test/application/middleware/cache_test.rb index dc1d816dc5..9822ec563d 100644 --- a/railties/test/application/middleware/cache_test.rb +++ b/railties/test/application/middleware/cache_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "isolation/abstract_unit" module ApplicationTests @@ -54,7 +56,7 @@ module ApplicationTests simple_controller expected = "Wed, 30 May 1984 19:43:31 GMT" - get "/expires/keeps_if_modified_since", {}, "HTTP_IF_MODIFIED_SINCE" => expected + get "/expires/keeps_if_modified_since", {}, { "HTTP_IF_MODIFIED_SINCE" => expected } assert_equal 200, last_response.status assert_equal expected, last_response.body, "cache should have kept If-Modified-Since" @@ -117,12 +119,12 @@ module ApplicationTests assert_equal "miss, store", last_response.headers["X-Rack-Cache"] assert_equal "public", last_response.headers["Cache-Control"] - body = last_response.body etag = last_response.headers["ETag"] - get "/expires/expires_etag", {}, "If-None-Match" => etag + get "/expires/expires_etag", {}, { "HTTP_IF_NONE_MATCH" => etag } assert_equal "stale, valid, store", last_response.headers["X-Rack-Cache"] - assert_equal body, last_response.body + assert_equal 304, last_response.status + assert_equal "", last_response.body end def test_cache_works_with_etags_private @@ -137,7 +139,7 @@ module ApplicationTests body = last_response.body etag = last_response.headers["ETag"] - get "/expires/expires_etag", { private: true }, "If-None-Match" => etag + get "/expires/expires_etag", { private: true }, { "HTTP_IF_NONE_MATCH" => etag } assert_equal "miss", last_response.headers["X-Rack-Cache"] assert_not_equal body, last_response.body end @@ -151,12 +153,12 @@ module ApplicationTests assert_equal "miss, store", last_response.headers["X-Rack-Cache"] assert_equal "public", last_response.headers["Cache-Control"] - body = last_response.body last = last_response.headers["Last-Modified"] - get "/expires/expires_last_modified", {}, "If-Modified-Since" => last + get "/expires/expires_last_modified", {}, { "HTTP_IF_MODIFIED_SINCE" => last } assert_equal "stale, valid, store", last_response.headers["X-Rack-Cache"] - assert_equal body, last_response.body + assert_equal 304, last_response.status + assert_equal "", last_response.body end def test_cache_works_with_last_modified_private @@ -171,7 +173,7 @@ module ApplicationTests body = last_response.body last = last_response.headers["Last-Modified"] - get "/expires/expires_last_modified", { private: true }, "If-Modified-Since" => last + get "/expires/expires_last_modified", { private: true }, { "HTTP_IF_MODIFIED_SINCE" => last } assert_equal "miss", last_response.headers["X-Rack-Cache"] assert_not_equal body, last_response.body end diff --git a/railties/test/application/middleware/cookies_test.rb b/railties/test/application/middleware/cookies_test.rb index 1e4b5d086c..ecb4ee3446 100644 --- a/railties/test/application/middleware/cookies_test.rb +++ b/railties/test/application/middleware/cookies_test.rb @@ -1,8 +1,12 @@ +# frozen_string_literal: true + require "isolation/abstract_unit" +require "rack/test" module ApplicationTests class CookiesTest < ActiveSupport::TestCase include ActiveSupport::Testing::Isolation + include Rack::Test::Methods def new_app File.expand_path("#{app_path}/../new_app") @@ -13,6 +17,10 @@ module ApplicationTests FileUtils.rm_rf("#{app_path}/config/environments") end + def app + Rails.application + end + def teardown teardown_app FileUtils.rm_rf(new_app) if File.directory?(new_app) @@ -42,5 +50,144 @@ module ApplicationTests require "#{app_path}/config/environment" assert_equal false, ActionDispatch::Cookies::CookieJar.always_write_cookie end + + test "signed cookies with SHA512 digest and rotated out SHA256 and SHA1 digests" do + app_file "config/routes.rb", <<-RUBY + Rails.application.routes.draw do + get ':controller(/:action)' + post ':controller(/:action)' + end + RUBY + + controller :foo, <<-RUBY + class FooController < ActionController::Base + protect_from_forgery with: :null_session + + def write_raw_cookie_sha1 + cookies[:signed_cookie] = TestVerifiers.sha1.generate("signed cookie") + head :ok + end + + def write_raw_cookie_sha256 + cookies[:signed_cookie] = TestVerifiers.sha256.generate("signed cookie") + head :ok + end + + def read_signed + render plain: cookies.signed[:signed_cookie].inspect + end + + def read_raw_cookie + render plain: cookies[:signed_cookie] + end + end + RUBY + + add_to_config <<-RUBY + sha1_secret = Rails.application.key_generator.generate_key("sha1") + sha256_secret = Rails.application.key_generator.generate_key("sha256") + + ::TestVerifiers = Class.new do + class_attribute :sha1, default: ActiveSupport::MessageVerifier.new(sha1_secret, digest: "SHA1") + class_attribute :sha256, default: ActiveSupport::MessageVerifier.new(sha256_secret, digest: "SHA256") + end + + config.action_dispatch.signed_cookie_digest = "SHA512" + config.action_dispatch.signed_cookie_salt = "sha512 salt" + + config.action_dispatch.cookies_rotations.tap do |cookies| + cookies.rotate :signed, sha1_secret, digest: "SHA1" + cookies.rotate :signed, sha256_secret, digest: "SHA256" + end + RUBY + + require "#{app_path}/config/environment" + + verifier_sha512 = ActiveSupport::MessageVerifier.new(app.key_generator.generate_key("sha512 salt"), digest: :SHA512) + + get "/foo/write_raw_cookie_sha1" + get "/foo/read_signed" + assert_equal "signed cookie".inspect, last_response.body + + get "/foo/read_raw_cookie" + assert_equal "signed cookie", verifier_sha512.verify(last_response.body) + + get "/foo/write_raw_cookie_sha256" + get "/foo/read_signed" + assert_equal "signed cookie".inspect, last_response.body + + get "/foo/read_raw_cookie" + assert_equal "signed cookie", verifier_sha512.verify(last_response.body) + end + + test "encrypted cookies rotating multiple encryption keys" do + app_file "config/routes.rb", <<-RUBY + Rails.application.routes.draw do + get ':controller(/:action)' + post ':controller(/:action)' + end + RUBY + + controller :foo, <<-RUBY + class FooController < ActionController::Base + protect_from_forgery with: :null_session + + def write_raw_cookie_one + cookies[:encrypted_cookie] = TestEncryptors.first_gcm.encrypt_and_sign("encrypted cookie") + head :ok + end + + def write_raw_cookie_two + cookies[:encrypted_cookie] = TestEncryptors.second_gcm.encrypt_and_sign("encrypted cookie") + head :ok + end + + def read_encrypted + render plain: cookies.encrypted[:encrypted_cookie].inspect + end + + def read_raw_cookie + render plain: cookies[:encrypted_cookie] + end + end + RUBY + + add_to_config <<-RUBY + first_secret = Rails.application.key_generator.generate_key("first", 32) + second_secret = Rails.application.key_generator.generate_key("second", 32) + + ::TestEncryptors = Class.new do + class_attribute :first_gcm, default: ActiveSupport::MessageEncryptor.new(first_secret, cipher: "aes-256-gcm") + class_attribute :second_gcm, default: ActiveSupport::MessageEncryptor.new(second_secret, cipher: "aes-256-gcm") + end + + config.action_dispatch.use_authenticated_cookie_encryption = true + config.action_dispatch.encrypted_cookie_cipher = "aes-256-gcm" + config.action_dispatch.authenticated_encrypted_cookie_salt = "salt" + + config.action_dispatch.cookies_rotations.tap do |cookies| + cookies.rotate :encrypted, first_secret + cookies.rotate :encrypted, second_secret + end + RUBY + + require "#{app_path}/config/environment" + + encryptor = ActiveSupport::MessageEncryptor.new(app.key_generator.generate_key("salt", 32), cipher: "aes-256-gcm") + + get "/foo/write_raw_cookie_one" + get "/foo/read_encrypted" + assert_equal "encrypted cookie".inspect, last_response.body + + get "/foo/read_raw_cookie" + assert_equal "encrypted cookie", encryptor.decrypt_and_verify(last_response.body) + + get "/foo/write_raw_cookie_sha256" + get "/foo/read_encrypted" + assert_equal "encrypted cookie".inspect, last_response.body + + get "/foo/read_raw_cookie" + assert_equal "encrypted cookie", encryptor.decrypt_and_verify(last_response.body) + end end end diff --git a/railties/test/application/middleware/exceptions_test.rb b/railties/test/application/middleware/exceptions_test.rb index cbb990f13b..2d659ade8d 100644 --- a/railties/test/application/middleware/exceptions_test.rb +++ b/railties/test/application/middleware/exceptions_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "isolation/abstract_unit" require "rack/test" @@ -100,6 +102,20 @@ module ApplicationTests end end + test "routing to a nonexistent controller when action_dispatch.show_exceptions and consider_all_requests_local are set shows diagnostics" do + app_file "config/routes.rb", <<-RUBY + Rails.application.routes.draw do + resources :articles + end + RUBY + + app.config.action_dispatch.show_exceptions = true + app.config.consider_all_requests_local = true + + get "/articles" + assert_match "<title>Action Controller: Exception caught</title>", last_response.body + end + test "displays diagnostics message when exception raised in template that contains UTF-8" do controller :foo, <<-RUBY class FooController < ActionController::Base diff --git a/railties/test/application/middleware/remote_ip_test.rb b/railties/test/application/middleware/remote_ip_test.rb index c34d9d6ee7..83cf8a27f7 100644 --- a/railties/test/application/middleware/remote_ip_test.rb +++ b/railties/test/application/middleware/remote_ip_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "ipaddr" require "isolation/abstract_unit" require "active_support/key_generator" diff --git a/railties/test/application/middleware/sendfile_test.rb b/railties/test/application/middleware/sendfile_test.rb index 4938402fdc..9def3a0ce7 100644 --- a/railties/test/application/middleware/sendfile_test.rb +++ b/railties/test/application/middleware/sendfile_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "isolation/abstract_unit" module ApplicationTests @@ -13,10 +15,6 @@ module ApplicationTests teardown_app end - def app - @app ||= Rails.application - end - define_method :simple_controller do class ::OmgController < ActionController::Base def index diff --git a/railties/test/application/middleware/session_test.rb b/railties/test/application/middleware/session_test.rb index 959a629ede..a17988235a 100644 --- a/railties/test/application/middleware/session_test.rb +++ b/railties/test/application/middleware/session_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "isolation/abstract_unit" require "rack/test" @@ -162,6 +164,11 @@ module ApplicationTests end RUBY + add_to_config <<-RUBY + # Enable AEAD cookies + config.action_dispatch.use_authenticated_cookie_encryption = true + RUBY + require "#{app_path}/config/environment" get "/foo/write_session" @@ -171,9 +178,9 @@ module ApplicationTests get "/foo/read_encrypted_cookie" assert_equal "1", last_response.body - secret = app.key_generator.generate_key("encrypted cookie") - sign_secret = app.key_generator.generate_key("signed encrypted cookie") - encryptor = ActiveSupport::MessageEncryptor.new(secret[0, ActiveSupport::MessageEncryptor.key_len], sign_secret) + cipher = "aes-256-gcm" + secret = app.key_generator.generate_key("authenticated encrypted cookie") + encryptor = ActiveSupport::MessageEncryptor.new(secret[0, ActiveSupport::MessageEncryptor.key_len(cipher)], cipher: cipher) get "/foo/read_raw_cookie" assert_equal 1, encryptor.decrypt_and_verify(last_response.body)["foo"] @@ -209,6 +216,9 @@ module ApplicationTests add_to_config <<-RUBY secrets.secret_token = "3b7cd727ee24e8444053437c36cc66c4" + + # Enable AEAD cookies + config.action_dispatch.use_authenticated_cookie_encryption = true RUBY require "#{app_path}/config/environment" @@ -220,9 +230,9 @@ module ApplicationTests get "/foo/read_encrypted_cookie" assert_equal "1", last_response.body - secret = app.key_generator.generate_key("encrypted cookie") - sign_secret = app.key_generator.generate_key("signed encrypted cookie") - encryptor = ActiveSupport::MessageEncryptor.new(secret[0, ActiveSupport::MessageEncryptor.key_len], sign_secret) + cipher = "aes-256-gcm" + secret = app.key_generator.generate_key("authenticated encrypted cookie") + encryptor = ActiveSupport::MessageEncryptor.new(secret[0, ActiveSupport::MessageEncryptor.key_len(cipher)], cipher: cipher) get "/foo/read_raw_cookie" assert_equal 1, encryptor.decrypt_and_verify(last_response.body)["foo"] @@ -264,6 +274,9 @@ module ApplicationTests add_to_config <<-RUBY secrets.secret_token = "3b7cd727ee24e8444053437c36cc66c4" + + # Enable AEAD cookies + config.action_dispatch.use_authenticated_cookie_encryption = true RUBY require "#{app_path}/config/environment" @@ -279,14 +292,84 @@ module ApplicationTests get "/foo/read_encrypted_cookie" assert_equal "2", last_response.body - secret = app.key_generator.generate_key("encrypted cookie") - sign_secret = app.key_generator.generate_key("signed encrypted cookie") - encryptor = ActiveSupport::MessageEncryptor.new(secret[0, ActiveSupport::MessageEncryptor.key_len], sign_secret) + cipher = "aes-256-gcm" + secret = app.key_generator.generate_key("authenticated encrypted cookie") + encryptor = ActiveSupport::MessageEncryptor.new(secret[0, ActiveSupport::MessageEncryptor.key_len(cipher)], cipher: cipher) get "/foo/read_raw_cookie" assert_equal 2, encryptor.decrypt_and_verify(last_response.body)["foo"] end + test "session upgrading from AES-CBC-HMAC encryption to AES-GCM encryption" do + app_file "config/routes.rb", <<-RUBY + Rails.application.routes.draw do + get ':controller(/:action)' + end + RUBY + + controller :foo, <<-RUBY + class FooController < ActionController::Base + def write_raw_session + # AES-256-CBC with SHA1 HMAC + # {"session_id"=>"1965d95720fffc123941bdfb7d2e6870", "foo"=>1} + cookies[:_myapp_session] = "TlgrdS85aUpDd1R2cDlPWlR6K0FJeGExckwySjZ2Z0pkR3d2QnRObGxZT25aalJWYWVvbFVLcHF4d0VQVDdSaFF2QjFPbG9MVjJzeWp3YjcyRUlKUUU2ZlR4bXlSNG9ZUkJPRUtld0E3dVU9LS0xNDZXbGpRZ3NjdW43N2haUEZJSUNRPT0=--3639b5ce54c09495cfeaae928cd5634e0c4b2e96" + head :ok + end + + def write_session + session[:foo] = session[:foo] + 1 + head :ok + end + + def read_session + render plain: session[:foo] + end + + def read_encrypted_cookie + render plain: cookies.encrypted[:_myapp_session]['foo'] + end + + def read_raw_cookie + render plain: cookies[:_myapp_session] + end + end + RUBY + + add_to_config <<-RUBY + # Use a static key + Rails.application.credentials.secret_key_base = "known key base" + + # Enable AEAD cookies + config.action_dispatch.use_authenticated_cookie_encryption = true + RUBY + + begin + old_rails_env, ENV["RAILS_ENV"] = ENV["RAILS_ENV"], "production" + + require "#{app_path}/config/environment" + + get "/foo/write_raw_session" + get "/foo/read_session" + assert_equal "1", last_response.body + + get "/foo/write_session" + get "/foo/read_session" + assert_equal "2", last_response.body + + get "/foo/read_encrypted_cookie" + assert_equal "2", last_response.body + + cipher = "aes-256-gcm" + secret = app.key_generator.generate_key("authenticated encrypted cookie") + encryptor = ActiveSupport::MessageEncryptor.new(secret[0, ActiveSupport::MessageEncryptor.key_len(cipher)], cipher: cipher) + + get "/foo/read_raw_cookie" + assert_equal 2, encryptor.decrypt_and_verify(last_response.body)["foo"] + ensure + ENV["RAILS_ENV"] = old_rails_env + end + end + test "session upgrading legacy signed cookies to new signed cookies" do app_file "config/routes.rb", <<-RUBY Rails.application.routes.draw do @@ -323,26 +406,32 @@ module ApplicationTests add_to_config <<-RUBY secrets.secret_token = "3b7cd727ee24e8444053437c36cc66c4" - secrets.secret_key_base = nil + Rails.application.credentials.secret_key_base = nil RUBY - require "#{app_path}/config/environment" + begin + old_rails_env, ENV["RAILS_ENV"] = ENV["RAILS_ENV"], "production" - get "/foo/write_raw_session" - get "/foo/read_session" - assert_equal "1", last_response.body + require "#{app_path}/config/environment" - get "/foo/write_session" - get "/foo/read_session" - assert_equal "2", last_response.body + get "/foo/write_raw_session" + get "/foo/read_session" + assert_equal "1", last_response.body - get "/foo/read_signed_cookie" - assert_equal "2", last_response.body + get "/foo/write_session" + get "/foo/read_session" + assert_equal "2", last_response.body - verifier = ActiveSupport::MessageVerifier.new(app.secrets.secret_token) + get "/foo/read_signed_cookie" + assert_equal "2", last_response.body - get "/foo/read_raw_cookie" - assert_equal 2, verifier.verify(last_response.body)["foo"] + verifier = ActiveSupport::MessageVerifier.new(app.secrets.secret_token) + + get "/foo/read_raw_cookie" + assert_equal 2, verifier.verify(last_response.body)["foo"] + ensure + ENV["RAILS_ENV"] = old_rails_env + end end test "calling reset_session on request does not trigger an error for API apps" do diff --git a/railties/test/application/middleware/static_test.rb b/railties/test/application/middleware/static_test.rb index 5cd3e4325e..0977042cfe 100644 --- a/railties/test/application/middleware/static_test.rb +++ b/railties/test/application/middleware/static_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "isolation/abstract_unit" require "rack/test" diff --git a/railties/test/application/middleware_test.rb b/railties/test/application/middleware_test.rb index 0a6e5b52e9..d59384e982 100644 --- a/railties/test/application/middleware_test.rb +++ b/railties/test/application/middleware_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "isolation/abstract_unit" module ApplicationTests @@ -40,6 +42,7 @@ module ApplicationTests "ActionDispatch::Cookies", "ActionDispatch::Session::CookieStore", "ActionDispatch::Flash", + "ActionDispatch::ContentSecurityPolicy::Middleware", "Rack::Head", "Rack::ConditionalGet", "Rack::ETag" @@ -246,7 +249,7 @@ module ApplicationTests test "can't change middleware after it's built" do boot! - assert_raise RuntimeError do + assert_raise frozen_error_class do app.config.middleware.use Rack::Config end end @@ -274,7 +277,7 @@ module ApplicationTests assert_equal "max-age=0, private, must-revalidate", last_response.headers["Cache-Control"] assert_equal etag, last_response.headers["Etag"] - get "/", {}, "HTTP_IF_NONE_MATCH" => etag + get "/", {}, { "HTTP_IF_NONE_MATCH" => etag } assert_equal 304, last_response.status assert_equal "", last_response.body assert_nil last_response.headers["Content-Type"] diff --git a/railties/test/application/multiple_applications_test.rb b/railties/test/application/multiple_applications_test.rb index 26b810af73..d6c81c1fe2 100644 --- a/railties/test/application/multiple_applications_test.rb +++ b/railties/test/application/multiple_applications_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "isolation/abstract_unit" module ApplicationTests diff --git a/railties/test/application/paths_test.rb b/railties/test/application/paths_test.rb index 515205296c..0abc5cc9aa 100644 --- a/railties/test/application/paths_test.rb +++ b/railties/test/application/paths_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "isolation/abstract_unit" module ApplicationTests diff --git a/railties/test/application/per_request_digest_cache_test.rb b/railties/test/application/per_request_digest_cache_test.rb index 6c003e9bcc..10d3313f6e 100644 --- a/railties/test/application/per_request_digest_cache_test.rb +++ b/railties/test/application/per_request_digest_cache_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "isolation/abstract_unit" require "rack/test" require "minitest/mock" @@ -18,6 +20,10 @@ class PerRequestDigestCacheTest < ActiveSupport::TestCase class Customer < Struct.new(:name, :id) extend ActiveModel::Naming include ActiveModel::Conversion + + def cache_key + [ name, id ].join("/") + end end RUBY @@ -53,7 +59,7 @@ class PerRequestDigestCacheTest < ActiveSupport::TestCase assert_equal 200, last_response.status values = ActionView::LookupContext::DetailsKey.digest_caches.first.values - assert_equal [ "8ba099b7749542fe765ff34a6824d548" ], values + assert_equal [ "effc8928d0b33535c8a21d24ec617161" ], values assert_equal %w(david dingus), last_response.body.split.map(&:strip) end diff --git a/railties/test/application/rack/logger_test.rb b/railties/test/application/rack/logger_test.rb index e71bcbc536..d949a48366 100644 --- a/railties/test/application/rack/logger_test.rb +++ b/railties/test/application/rack/logger_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "isolation/abstract_unit" require "active_support/log_subscriber/test_helper" require "rack/test" diff --git a/railties/test/application/rackup_test.rb b/railties/test/application/rackup_test.rb index 2943e9ee5d..383f18a7da 100644 --- a/railties/test/application/rackup_test.rb +++ b/railties/test/application/rackup_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "isolation/abstract_unit" module ApplicationTests diff --git a/railties/test/application/rake/dbs_test.rb b/railties/test/application/rake/dbs_test.rb index c63f23fa0a..5b4c42c189 100644 --- a/railties/test/application/rake/dbs_test.rb +++ b/railties/test/application/rake/dbs_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "isolation/abstract_unit" module ApplicationTests @@ -24,13 +26,13 @@ module ApplicationTests FileUtils.rm_rf("#{app_path}/config/database.yml") end - def db_create_and_drop(expected_database) + def db_create_and_drop(expected_database, environment_loaded: true) Dir.chdir(app_path) do - output = `bin/rails db:create` + output = rails("db:create") assert_match(/Created database/, output) assert File.exist?(expected_database) - assert_equal expected_database, ActiveRecord::Base.connection_config[:database] - output = `bin/rails db:drop` + assert_equal expected_database, ActiveRecord::Base.connection_config[:database] if environment_loaded + output = rails("db:drop") assert_match(/Dropped database/, output) assert !File.exist?(expected_database) end @@ -47,20 +49,35 @@ module ApplicationTests db_create_and_drop database_url_db_name end + test "db:create and db:drop respect environment setting" do + app_file "config/database.yml", <<-YAML + development: + database: <%= Rails.application.config.database %> + adapter: sqlite3 + YAML + + app_file "config/environments/development.rb", <<-RUBY + Rails.application.configure do + config.database = "db/development.sqlite3" + end + RUBY + + db_create_and_drop "db/development.sqlite3", environment_loaded: false + end + def with_database_existing Dir.chdir(app_path) do set_database_url - `bin/rails db:create` + rails "db:create" yield - `bin/rails db:drop` + rails "db:drop" end end test "db:create failure because database exists" do with_database_existing do - output = `bin/rails db:create 2>&1` + output = rails("db:create") assert_match(/already exists/, output) - assert_equal 0, $?.exitstatus end end @@ -75,24 +92,35 @@ module ApplicationTests test "db:create failure because bad permissions" do with_bad_permissions do - output = `bin/rails db:create 2>&1` + output = rails("db:create", allow_failure: true) assert_match(/Couldn't create database/, output) assert_equal 1, $?.exitstatus end end - test "db:drop failure because database does not exist" do - Dir.chdir(app_path) do - output = `bin/rails db:drop:_unsafe --trace 2>&1` - assert_match(/does not exist/, output) + test "db:create works when schema cache exists and database does not exist" do + use_postgresql + + begin + rails %w(db:create db:migrate db:schema:cache:dump) + + rails "db:drop" + rails "db:create" assert_equal 0, $?.exitstatus + ensure + rails "db:drop" rescue nil end end + test "db:drop failure because database does not exist" do + output = rails("db:drop:_unsafe", "--trace") + assert_match(/does not exist/, output) + end + test "db:drop failure because bad permissions" do with_database_existing do with_bad_permissions do - output = `bin/rails db:drop 2>&1` + output = rails("db:drop", allow_failure: true) assert_match(/Couldn't drop/, output) assert_equal 1, $?.exitstatus end @@ -100,13 +128,11 @@ module ApplicationTests end def db_migrate_and_status(expected_database) - Dir.chdir(app_path) do - `bin/rails generate model book title:string; - bin/rails db:migrate` - output = `bin/rails db:migrate:status` - assert_match(%r{database:\s+\S*#{Regexp.escape(expected_database)}}, output) - assert_match(/up\s+\d{14}\s+Create books/, output) - end + rails "generate", "model", "book", "title:string" + rails "db:migrate" + output = rails("db:migrate:status") + assert_match(%r{database:\s+\S*#{Regexp.escape(expected_database)}}, output) + assert_match(/up\s+\d{14}\s+Create books/, output) end test "db:migrate and db:migrate:status without database_url" do @@ -122,8 +148,8 @@ module ApplicationTests def db_schema_dump Dir.chdir(app_path) do - `bin/rails generate model book title:string; - bin/rails db:migrate db:schema:dump` + rails "generate", "model", "book", "title:string" + rails "db:migrate", "db:schema:dump" schema_dump = File.read("db/schema.rb") assert_match(/create_table \"books\"/, schema_dump) end @@ -140,8 +166,8 @@ module ApplicationTests def db_fixtures_load(expected_database) Dir.chdir(app_path) do - `bin/rails generate model book title:string; - bin/rails db:migrate db:fixtures:load` + rails "generate", "model", "book", "title:string" + rails "db:migrate", "db:fixtures:load" assert_match expected_database, ActiveRecord::Base.connection_config[:database] require "#{app_path}/app/models/book" assert_equal 2, Book.count @@ -161,24 +187,23 @@ module ApplicationTests test "db:fixtures:load with namespaced fixture" do require "#{app_path}/config/environment" - Dir.chdir(app_path) do - `bin/rails generate model admin::book title:string; - bin/rails db:migrate db:fixtures:load` - require "#{app_path}/app/models/admin/book" - assert_equal 2, Admin::Book.count - end + + rails "generate", "model", "admin::book", "title:string" + rails "db:migrate", "db:fixtures:load" + require "#{app_path}/app/models/admin/book" + assert_equal 2, Admin::Book.count end def db_structure_dump_and_load(expected_database) Dir.chdir(app_path) do - `bin/rails generate model book title:string; - bin/rails db:migrate db:structure:dump` + rails "generate", "model", "book", "title:string" + rails "db:migrate", "db:structure:dump" structure_dump = File.read("db/structure.sql") assert_match(/CREATE TABLE (?:IF NOT EXISTS )?\"books\"/, structure_dump) - `bin/rails environment db:drop db:structure:load` + rails "environment", "db:drop", "db:structure:load" assert_match expected_database, ActiveRecord::Base.connection_config[:database] require "#{app_path}/app/models/book" - #if structure is not loaded correctly, exception would be raised + # if structure is not loaded correctly, exception would be raised assert_equal 0, Book.count end end @@ -194,79 +219,86 @@ module ApplicationTests db_structure_dump_and_load database_url_db_name end + test "db:structure:dump and db:structure:load set ar_internal_metadata" do + require "#{app_path}/config/environment" + db_structure_dump_and_load ActiveRecord::Base.configurations[Rails.env]["database"] + + assert_equal "test", rails("runner", "-e", "test", "puts ActiveRecord::InternalMetadata[:environment]").strip + assert_equal "development", rails("runner", "puts ActiveRecord::InternalMetadata[:environment]").strip + end + test "db:structure:dump does not dump schema information when no migrations are used" do - Dir.chdir(app_path) do - # create table without migrations - `bin/rails runner 'ActiveRecord::Base.connection.create_table(:posts) {|t| t.string :title }'` + # create table without migrations + rails "runner", "ActiveRecord::Base.connection.create_table(:posts) {|t| t.string :title }" - stderr_output = capture(:stderr) { `bin/rails db:structure:dump` } - assert_empty stderr_output - structure_dump = File.read("db/structure.sql") - assert_match(/CREATE TABLE (?:IF NOT EXISTS )?\"posts\"/, structure_dump) - end + stderr_output = capture(:stderr) { rails("db:structure:dump", stderr: true, allow_failure: true) } + assert_empty stderr_output + structure_dump = File.read("#{app_path}/db/structure.sql") + assert_match(/CREATE TABLE (?:IF NOT EXISTS )?\"posts\"/, structure_dump) end test "db:schema:load and db:structure:load do not purge the existing database" do - Dir.chdir(app_path) do - `bin/rails runner 'ActiveRecord::Base.connection.create_table(:posts) {|t| t.string :title }'` + rails "runner", "ActiveRecord::Base.connection.create_table(:posts) {|t| t.string :title }" - app_file "db/schema.rb", <<-RUBY - ActiveRecord::Schema.define(version: 20140423102712) do - create_table(:comments) {} - end - RUBY + app_file "db/schema.rb", <<-RUBY + ActiveRecord::Schema.define(version: 20140423102712) do + create_table(:comments) {} + end + RUBY - list_tables = lambda { `bin/rails runner 'p ActiveRecord::Base.connection.tables'`.strip } + list_tables = lambda { rails("runner", "p ActiveRecord::Base.connection.tables").strip } - assert_equal '["posts"]', list_tables[] - `bin/rails db:schema:load` - assert_equal '["posts", "comments", "schema_migrations", "ar_internal_metadata"]', list_tables[] + assert_equal '["posts"]', list_tables[] + rails "db:schema:load" + assert_equal '["posts", "comments", "schema_migrations", "ar_internal_metadata"]', list_tables[] - app_file "db/structure.sql", <<-SQL - CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar(255)); - SQL + app_file "db/structure.sql", <<-SQL + CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar(255)); + SQL - `bin/rails db:structure:load` - assert_equal '["posts", "comments", "schema_migrations", "ar_internal_metadata", "users"]', list_tables[] - end + rails "db:structure:load" + assert_equal '["posts", "comments", "schema_migrations", "ar_internal_metadata", "users"]', list_tables[] end test "db:schema:load with inflections" do - Dir.chdir(app_path) do - app_file "config/initializers/inflection.rb", <<-RUBY - ActiveSupport::Inflector.inflections do |inflect| - inflect.irregular 'goose', 'geese' - end - RUBY - app_file "config/initializers/primary_key_table_name.rb", <<-RUBY - ActiveRecord::Base.primary_key_prefix_type = :table_name - RUBY - app_file "db/schema.rb", <<-RUBY - ActiveRecord::Schema.define(version: 20140423102712) do - create_table("goose".pluralize) do |t| - t.string :name - end + app_file "config/initializers/inflection.rb", <<-RUBY + ActiveSupport::Inflector.inflections do |inflect| + inflect.irregular 'goose', 'geese' + end + RUBY + app_file "config/initializers/primary_key_table_name.rb", <<-RUBY + ActiveRecord::Base.primary_key_prefix_type = :table_name + RUBY + app_file "db/schema.rb", <<-RUBY + ActiveRecord::Schema.define(version: 20140423102712) do + create_table("goose".pluralize) do |t| + t.string :name end - RUBY + end + RUBY - `bin/rails db:schema:load` + rails "db:schema:load" - tables = `bin/rails runner 'p ActiveRecord::Base.connection.tables'`.strip - assert_match(/"geese"/, tables) + tables = rails("runner", "p ActiveRecord::Base.connection.tables").strip + assert_match(/"geese"/, tables) - columns = `bin/rails runner 'p ActiveRecord::Base.connection.columns("geese").map(&:name)'`.strip - assert_equal columns, '["gooseid", "name"]' - end + columns = rails("runner", "p ActiveRecord::Base.connection.columns('geese').map(&:name)").strip + assert_equal columns, '["gooseid", "name"]' + end + + test "db:schema:load fails if schema.rb doesn't exist yet" do + stderr_output = capture(:stderr) { rails("db:schema:load", stderr: true, allow_failure: true) } + assert_match(/Run `rails db:migrate` to create it/, stderr_output) end def db_test_load_structure Dir.chdir(app_path) do - `bin/rails generate model book title:string; - bin/rails db:migrate db:structure:dump db:test:load_structure` + rails "generate", "model", "book", "title:string" + rails "db:migrate", "db:structure:dump", "db:test:load_structure" ActiveRecord::Base.configurations = Rails.application.config.database_configuration ActiveRecord::Base.establish_connection :test require "#{app_path}/app/models/book" - #if structure is not loaded correctly, exception would be raised + # if structure is not loaded correctly, exception would be raised assert_equal 0, Book.count assert_match ActiveRecord::Base.configurations["test"]["database"], ActiveRecord::Base.connection_config[:database] @@ -297,15 +329,52 @@ module ApplicationTests puts ActiveRecord::Base.connection_config[:database] RUBY - Dir.chdir(app_path) do - database_path = `bin/rails db:setup` - assert_equal "development.sqlite3", File.basename(database_path.strip) - end + database_path = rails("db:setup") + assert_equal "development.sqlite3", File.basename(database_path.strip) ensure ENV["RAILS_ENV"] = @old_rails_env ENV["RACK_ENV"] = @old_rack_env end end + + test "db:setup sets ar_internal_metadata" do + app_file "db/schema.rb", "" + rails "db:setup" + + test_environment = lambda { rails("runner", "-e", "test", "puts ActiveRecord::InternalMetadata[:environment]").strip } + development_environment = lambda { rails("runner", "puts ActiveRecord::InternalMetadata[:environment]").strip } + + assert_equal "test", test_environment.call + assert_equal "development", development_environment.call + + app_file "db/structure.sql", "" + app_file "config/initializers/enable_sql_schema_format.rb", <<-RUBY + Rails.application.config.active_record.schema_format = :sql + RUBY + + rails "db:setup" + + assert_equal "test", test_environment.call + assert_equal "development", development_environment.call + end + + test "db:test:prepare sets test ar_internal_metadata" do + app_file "db/schema.rb", "" + rails "db:test:prepare" + + test_environment = lambda { rails("runner", "-e", "test", "puts ActiveRecord::InternalMetadata[:environment]").strip } + + assert_equal "test", test_environment.call + + app_file "db/structure.sql", "" + app_file "config/initializers/enable_sql_schema_format.rb", <<-RUBY + Rails.application.config.active_record.schema_format = :sql + RUBY + + rails "db:test:prepare" + + assert_equal "test", test_environment.call + end end end end diff --git a/railties/test/application/rake/dev_test.rb b/railties/test/application/rake/dev_test.rb index 4f992d9c8d..66e1ac9d99 100644 --- a/railties/test/application/rake/dev_test.rb +++ b/railties/test/application/rake/dev_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "isolation/abstract_unit" module ApplicationTests @@ -15,7 +17,7 @@ module ApplicationTests test "dev:cache creates file and outputs message" do Dir.chdir(app_path) do - output = `rails dev:cache` + output = rails("dev:cache") assert File.exist?("tmp/caching-dev.txt") assert_match(/Development mode is now being cached/, output) end @@ -23,19 +25,23 @@ module ApplicationTests test "dev:cache deletes file and outputs message" do Dir.chdir(app_path) do - `rails dev:cache` # Create caching file. - output = `rails dev:cache` # Delete caching file. + rails "dev:cache" # Create caching file. + output = rails("dev:cache") # Delete caching file. assert_not File.exist?("tmp/caching-dev.txt") assert_match(/Development mode is no longer being cached/, output) end end - test "dev:cache removes server.pid also" do + test "dev:cache touches tmp/restart.txt" do Dir.chdir(app_path) do - FileUtils.mkdir_p("tmp/pids") - FileUtils.touch("tmp/pids/server.pid") - `rails dev:cache` - assert_not File.exist?("tmp/pids/server.pid") + rails "dev:cache" + assert File.exist?("tmp/restart.txt") + + prev_mtime = File.mtime("tmp/restart.txt") + sleep(1) + rails "dev:cache" + curr_mtime = File.mtime("tmp/restart.txt") + assert_not_equal prev_mtime, curr_mtime end end end diff --git a/railties/test/application/rake/framework_test.rb b/railties/test/application/rake/framework_test.rb index 40488a6aab..644b1924b5 100644 --- a/railties/test/application/rake/framework_test.rb +++ b/railties/test/application/rake/framework_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "isolation/abstract_unit" module ApplicationTests diff --git a/railties/test/application/rake/log_test.rb b/railties/test/application/rake/log_test.rb index fdd3c71fe8..678f26db26 100644 --- a/railties/test/application/rake/log_test.rb +++ b/railties/test/application/rake/log_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "isolation/abstract_unit" module ApplicationTests @@ -21,7 +23,7 @@ module ApplicationTests File.write("log/test.log", "test") File.write("log/dummy.log", "dummy") - `rails log:clear` + rails "log:clear" assert_equal 0, File.size("log/test.log") assert_equal 0, File.size("log/staging.log") diff --git a/railties/test/application/rake/migrations_test.rb b/railties/test/application/rake/migrations_test.rb index 76cb302c62..788f9160d6 100644 --- a/railties/test/application/rake/migrations_test.rb +++ b/railties/test/application/rake/migrations_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "isolation/abstract_unit" module ApplicationTests @@ -13,185 +15,396 @@ module ApplicationTests end test "running migrations with given scope" do - Dir.chdir(app_path) do - `bin/rails generate model user username:string password:string` + rails "generate", "model", "user", "username:string", "password:string" - app_file "db/migrate/01_a_migration.bukkits.rb", <<-MIGRATION - class AMigration < ActiveRecord::Migration::Current - end - MIGRATION + app_file "db/migrate/01_a_migration.bukkits.rb", <<-MIGRATION + class AMigration < ActiveRecord::Migration::Current + end + MIGRATION - output = `bin/rails db:migrate SCOPE=bukkits` - assert_no_match(/create_table\(:users\)/, output) - assert_no_match(/CreateUsers/, output) - assert_no_match(/add_column\(:users, :email, :string\)/, output) + output = rails("db:migrate", "SCOPE=bukkits") + assert_no_match(/create_table\(:users\)/, output) + assert_no_match(/CreateUsers/, output) + assert_no_match(/add_column\(:users, :email, :string\)/, output) - assert_match(/AMigration: migrated/, output) + assert_match(/AMigration: migrated/, output) - output = `bin/rails db:migrate SCOPE=bukkits VERSION=0` - assert_no_match(/drop_table\(:users\)/, output) - assert_no_match(/CreateUsers/, output) - assert_no_match(/remove_column\(:users, :email\)/, output) + output = rails("db:migrate", "SCOPE=bukkits", "VERSION=0") + assert_no_match(/drop_table\(:users\)/, output) + assert_no_match(/CreateUsers/, output) + assert_no_match(/remove_column\(:users, :email\)/, output) - assert_match(/AMigration: reverted/, output) - end + assert_match(/AMigration: reverted/, output) + end + + test "migrate with specified VERSION in different formats" do + app_file "db/migrate/01_one_migration.rb", <<-MIGRATION + class OneMigration < ActiveRecord::Migration::Current + end + MIGRATION + + app_file "db/migrate/02_two_migration.rb", <<-MIGRATION + class TwoMigration < ActiveRecord::Migration::Current + end + MIGRATION + + app_file "db/migrate/03_three_migration.rb", <<-MIGRATION + class ThreeMigration < ActiveRecord::Migration::Current + end + MIGRATION + + rails "db:migrate" + + output = rails("db:migrate:status") + assert_match(/up\s+001\s+One migration/, output) + assert_match(/up\s+002\s+Two migration/, output) + assert_match(/up\s+003\s+Three migration/, output) + + rails "db:migrate", "VERSION=01_one_migration.rb" + output = rails("db:migrate:status") + assert_match(/up\s+001\s+One migration/, output) + assert_match(/down\s+002\s+Two migration/, output) + assert_match(/down\s+003\s+Three migration/, output) + + rails "db:migrate", "VERSION=3" + output = rails("db:migrate:status") + assert_match(/up\s+001\s+One migration/, output) + assert_match(/up\s+002\s+Two migration/, output) + assert_match(/up\s+003\s+Three migration/, output) + + rails "db:migrate", "VERSION=001" + output = rails("db:migrate:status") + assert_match(/up\s+001\s+One migration/, output) + assert_match(/down\s+002\s+Two migration/, output) + assert_match(/down\s+003\s+Three migration/, output) + end + + test "migration with empty version" do + app_file "db/migrate/01_one_migration.rb", <<-MIGRATION + class OneMigration < ActiveRecord::Migration::Current + end + MIGRATION + + app_file "db/migrate/02_two_migration.rb", <<-MIGRATION + class TwoMigration < ActiveRecord::Migration::Current + end + MIGRATION + + rails("db:migrate", "VERSION=") + + output = rails("db:migrate:status") + assert_match(/up\s+001\s+One migration/, output) + assert_match(/up\s+002\s+Two migration/, output) + + output = rails("db:migrate:redo", "VERSION=", allow_failure: true) + assert_match(/Empty VERSION provided/, output) + + output = rails("db:migrate:up", "VERSION=", allow_failure: true) + assert_match(/VERSION is required/, output) + + output = rails("db:migrate:up", allow_failure: true) + assert_match(/VERSION is required/, output) + + output = rails("db:migrate:down", "VERSION=", allow_failure: true) + assert_match(/VERSION is required - To go down one migration, use db:rollback/, output) + + output = rails("db:migrate:down", allow_failure: true) + assert_match(/VERSION is required - To go down one migration, use db:rollback/, output) + + output = rails("db:migrate:status") + assert_match(/up\s+001\s+One migration/, output) + assert_match(/up\s+002\s+Two migration/, output) + end + + test "migration with 0 version" do + app_file "db/migrate/01_one_migration.rb", <<-MIGRATION + class OneMigration < ActiveRecord::Migration::Current + end + MIGRATION + + app_file "db/migrate/02_two_migration.rb", <<-MIGRATION + class TwoMigration < ActiveRecord::Migration::Current + end + MIGRATION + + rails "db:migrate" + + output = rails("db:migrate:status") + assert_match(/up\s+001\s+One migration/, output) + assert_match(/up\s+002\s+Two migration/, output) + + rails "db:migrate", "VERSION=0" + + output = rails("db:migrate:status") + assert_match(/down\s+001\s+One migration/, output) + assert_match(/down\s+002\s+Two migration/, output) end test "model and migration generator with change syntax" do - Dir.chdir(app_path) do - `bin/rails generate model user username:string password:string; - bin/rails generate migration add_email_to_users email:string` - - output = `bin/rails db:migrate` - assert_match(/create_table\(:users\)/, output) - assert_match(/CreateUsers: migrated/, output) - assert_match(/add_column\(:users, :email, :string\)/, output) - assert_match(/AddEmailToUsers: migrated/, output) - - output = `bin/rails db:rollback STEP=2` - assert_match(/drop_table\(:users\)/, output) - assert_match(/CreateUsers: reverted/, output) - assert_match(/remove_column\(:users, :email, :string\)/, output) - assert_match(/AddEmailToUsers: reverted/, output) - end + rails "generate", "model", "user", "username:string", "password:string" + rails "generate", "migration", "add_email_to_users", "email:string" + + output = rails("db:migrate") + assert_match(/create_table\(:users\)/, output) + assert_match(/CreateUsers: migrated/, output) + assert_match(/add_column\(:users, :email, :string\)/, output) + assert_match(/AddEmailToUsers: migrated/, output) + + output = rails("db:rollback", "STEP=2") + assert_match(/drop_table\(:users\)/, output) + assert_match(/CreateUsers: reverted/, output) + assert_match(/remove_column\(:users, :email, :string\)/, output) + assert_match(/AddEmailToUsers: reverted/, output) end test "migration status when schema migrations table is not present" do - output = Dir.chdir(app_path) { `bin/rails db:migrate:status 2>&1` } + output = rails("db:migrate:status", allow_failure: true) assert_equal "Schema migrations table does not exist yet.\n", output end - test "test migration status" do - Dir.chdir(app_path) do - `bin/rails generate model user username:string password:string; - bin/rails generate migration add_email_to_users email:string; - bin/rails db:migrate` + test "migration status" do + rails "generate", "model", "user", "username:string", "password:string" + rails "generate", "migration", "add_email_to_users", "email:string" + rails "db:migrate" - output = `bin/rails db:migrate:status` + output = rails("db:migrate:status") - assert_match(/up\s+\d{14}\s+Create users/, output) - assert_match(/up\s+\d{14}\s+Add email to users/, output) + assert_match(/up\s+\d{14}\s+Create users/, output) + assert_match(/up\s+\d{14}\s+Add email to users/, output) - `bin/rails db:rollback STEP=1` - output = `bin/rails db:migrate:status` + rails "db:rollback", "STEP=1" + output = rails("db:migrate:status") - assert_match(/up\s+\d{14}\s+Create users/, output) - assert_match(/down\s+\d{14}\s+Add email to users/, output) - end + assert_match(/up\s+\d{14}\s+Create users/, output) + assert_match(/down\s+\d{14}\s+Add email to users/, output) end test "migration status without timestamps" do add_to_config("config.active_record.timestamped_migrations = false") - Dir.chdir(app_path) do - `bin/rails generate model user username:string password:string; - bin/rails generate migration add_email_to_users email:string; - bin/rails db:migrate` + rails "generate", "model", "user", "username:string", "password:string" + rails "generate", "migration", "add_email_to_users", "email:string" + rails "db:migrate" - output = `bin/rails db:migrate:status` + output = rails("db:migrate:status") - assert_match(/up\s+\d{3,}\s+Create users/, output) - assert_match(/up\s+\d{3,}\s+Add email to users/, output) + assert_match(/up\s+\d{3,}\s+Create users/, output) + assert_match(/up\s+\d{3,}\s+Add email to users/, output) - `bin/rails db:rollback STEP=1` - output = `bin/rails db:migrate:status` + rails "db:rollback", "STEP=1" + output = rails("db:migrate:status") - assert_match(/up\s+\d{3,}\s+Create users/, output) - assert_match(/down\s+\d{3,}\s+Add email to users/, output) - end + assert_match(/up\s+\d{3,}\s+Create users/, output) + assert_match(/down\s+\d{3,}\s+Add email to users/, output) end - test "test migration status after rollback and redo" do - Dir.chdir(app_path) do - `bin/rails generate model user username:string password:string; - bin/rails generate migration add_email_to_users email:string; - bin/rails db:migrate` + test "migration status after rollback and redo" do + rails "generate", "model", "user", "username:string", "password:string" + rails "generate", "migration", "add_email_to_users", "email:string" + rails "db:migrate" + + output = rails("db:migrate:status") + + assert_match(/up\s+\d{14}\s+Create users/, output) + assert_match(/up\s+\d{14}\s+Add email to users/, output) - output = `bin/rails db:migrate:status` + rails "db:rollback", "STEP=2" + output = rails("db:migrate:status") + + assert_match(/down\s+\d{14}\s+Create users/, output) + assert_match(/down\s+\d{14}\s+Add email to users/, output) + + rails "db:migrate:redo" + output = rails("db:migrate:status") + + assert_match(/up\s+\d{14}\s+Create users/, output) + assert_match(/up\s+\d{14}\s+Add email to users/, output) + end + test "migration status after rollback and forward" do + rails "generate", "model", "user", "username:string", "password:string" + rails "generate", "migration", "add_email_to_users", "email:string" + rails "db:migrate" + + output = rails("db:migrate:status") + + assert_match(/up\s+\d{14}\s+Create users/, output) + assert_match(/up\s+\d{14}\s+Add email to users/, output) + + rails "db:rollback", "STEP=2" + output = rails("db:migrate:status") + + assert_match(/down\s+\d{14}\s+Create users/, output) + assert_match(/down\s+\d{14}\s+Add email to users/, output) + + rails "db:forward", "STEP=2" + output = rails("db:migrate:status") + + assert_match(/up\s+\d{14}\s+Create users/, output) + assert_match(/up\s+\d{14}\s+Add email to users/, output) + end + + test "raise error on any move when current migration does not exist" do + Dir.chdir(app_path) do + rails "generate", "model", "user", "username:string", "password:string" + rails "generate", "migration", "add_email_to_users", "email:string" + rails "db:migrate" + `rm db/migrate/*email*.rb` + + output = rails("db:migrate:status") assert_match(/up\s+\d{14}\s+Create users/, output) - assert_match(/up\s+\d{14}\s+Add email to users/, output) + assert_match(/up\s+\d{14}\s+\** NO FILE \**/, output) - `bin/rails db:rollback STEP=2` - output = `bin/rails db:migrate:status` + output = rails("db:rollback", allow_failure: true) + assert_match(/rails aborted!/, output) + assert_match(/ActiveRecord::UnknownMigrationVersionError:/, output) + assert_match(/No migration with version number\s\d{14}\./, output) - assert_match(/down\s+\d{14}\s+Create users/, output) - assert_match(/down\s+\d{14}\s+Add email to users/, output) + output = rails("db:migrate:status") + assert_match(/up\s+\d{14}\s+Create users/, output) + assert_match(/up\s+\d{14}\s+\** NO FILE \**/, output) - `bin/rails db:migrate:redo` - output = `bin/rails db:migrate:status` + output = rails("db:forward", allow_failure: true) + assert_match(/rails aborted!/, output) + assert_match(/ActiveRecord::UnknownMigrationVersionError:/, output) + assert_match(/No migration with version number\s\d{14}\./, output) + output = rails("db:migrate:status") assert_match(/up\s+\d{14}\s+Create users/, output) - assert_match(/up\s+\d{14}\s+Add email to users/, output) + assert_match(/up\s+\d{14}\s+\** NO FILE \**/, output) end end + test "raise error on any move when target migration does not exist" do + app_file "db/migrate/01_one_migration.rb", <<-MIGRATION + class OneMigration < ActiveRecord::Migration::Current + end + MIGRATION + + app_file "db/migrate/02_two_migration.rb", <<-MIGRATION + class TwoMigration < ActiveRecord::Migration::Current + end + MIGRATION + + rails "db:migrate" + + output = rails("db:migrate:status") + assert_match(/up\s+001\s+One migration/, output) + assert_match(/up\s+002\s+Two migration/, output) + + output = rails("db:migrate", "VERSION=3", allow_failure: true) + assert_match(/rails aborted!/, output) + assert_match(/ActiveRecord::UnknownMigrationVersionError:/, output) + assert_match(/No migration with version number 3/, output) + + output = rails("db:migrate:status") + assert_match(/up\s+001\s+One migration/, output) + assert_match(/up\s+002\s+Two migration/, output) + end + + test "raise error on any move when VERSION has invalid format" do + output = rails("db:migrate", "VERSION=unknown", allow_failure: true) + assert_match(/rails aborted!/, output) + assert_match(/Invalid format of target version/, output) + + output = rails("db:migrate", "VERSION=0.1.11", allow_failure: true) + assert_match(/rails aborted!/, output) + assert_match(/Invalid format of target version/, output) + + output = rails("db:migrate", "VERSION=1.1.11", allow_failure: true) + assert_match(/rails aborted!/, output) + assert_match(/Invalid format of target version/, output) + + output = rails("db:migrate", "VERSION='0 '", allow_failure: true) + assert_match(/rails aborted!/, output) + assert_match(/Invalid format of target version/, output) + + output = rails("db:migrate", "VERSION=1.", allow_failure: true) + assert_match(/rails aborted!/, output) + assert_match(/Invalid format of target version/, output) + + output = rails("db:migrate", "VERSION=1_", allow_failure: true) + assert_match(/rails aborted!/, output) + assert_match(/Invalid format of target version/, output) + + output = rails("db:migrate", "VERSION=1_name", allow_failure: true) + assert_match(/rails aborted!/, output) + assert_match(/Invalid format of target version/, output) + + output = rails("db:migrate:redo", "VERSION=unknown", allow_failure: true) + assert_match(/rails aborted!/, output) + assert_match(/Invalid format of target version/, output) + + output = rails("db:migrate:up", "VERSION=unknown", allow_failure: true) + assert_match(/rails aborted!/, output) + assert_match(/Invalid format of target version/, output) + + output = rails("db:migrate:down", "VERSION=unknown", allow_failure: true) + assert_match(/rails aborted!/, output) + assert_match(/Invalid format of target version/, output) + end + test "migration status after rollback and redo without timestamps" do add_to_config("config.active_record.timestamped_migrations = false") - Dir.chdir(app_path) do - `bin/rails generate model user username:string password:string; - bin/rails generate migration add_email_to_users email:string; - bin/rails db:migrate` + rails "generate", "model", "user", "username:string", "password:string" + rails "generate", "migration", "add_email_to_users", "email:string" + rails "db:migrate" - output = `bin/rails db:migrate:status` + output = rails("db:migrate:status") - assert_match(/up\s+\d{3,}\s+Create users/, output) - assert_match(/up\s+\d{3,}\s+Add email to users/, output) + assert_match(/up\s+\d{3,}\s+Create users/, output) + assert_match(/up\s+\d{3,}\s+Add email to users/, output) - `bin/rails db:rollback STEP=2` - output = `bin/rails db:migrate:status` + rails "db:rollback", "STEP=2" + output = rails("db:migrate:status") - assert_match(/down\s+\d{3,}\s+Create users/, output) - assert_match(/down\s+\d{3,}\s+Add email to users/, output) + assert_match(/down\s+\d{3,}\s+Create users/, output) + assert_match(/down\s+\d{3,}\s+Add email to users/, output) - `bin/rails db:migrate:redo` - output = `bin/rails db:migrate:status` + rails "db:migrate:redo" + output = rails("db:migrate:status") - assert_match(/up\s+\d{3,}\s+Create users/, output) - assert_match(/up\s+\d{3,}\s+Add email to users/, output) - end + assert_match(/up\s+\d{3,}\s+Create users/, output) + assert_match(/up\s+\d{3,}\s+Add email to users/, output) end test "running migrations with not timestamp head migration files" do - Dir.chdir(app_path) do + app_file "db/migrate/1_one_migration.rb", <<-MIGRATION + class OneMigration < ActiveRecord::Migration::Current + end + MIGRATION - app_file "db/migrate/1_one_migration.rb", <<-MIGRATION - class OneMigration < ActiveRecord::Migration::Current - end - MIGRATION + app_file "db/migrate/02_two_migration.rb", <<-MIGRATION + class TwoMigration < ActiveRecord::Migration::Current + end + MIGRATION - app_file "db/migrate/02_two_migration.rb", <<-MIGRATION - class TwoMigration < ActiveRecord::Migration::Current - end - MIGRATION + rails "db:migrate" - `bin/rails db:migrate` + output = rails("db:migrate:status") - output = `bin/rails db:migrate:status` - - assert_match(/up\s+001\s+One migration/, output) - assert_match(/up\s+002\s+Two migration/, output) - end + assert_match(/up\s+001\s+One migration/, output) + assert_match(/up\s+002\s+Two migration/, output) end test "schema generation when dump_schema_after_migration is set" do add_to_config("config.active_record.dump_schema_after_migration = false") Dir.chdir(app_path) do - `bin/rails generate model book title:string` - output = `bin/rails generate model author name:string` + rails "generate", "model", "book", "title:string" + output = rails("generate", "model", "author", "name:string") version = output =~ %r{[^/]+db/migrate/(\d+)_create_authors\.rb} && $1 - `bin/rails db:migrate db:rollback db:forward db:migrate:up db:migrate:down VERSION=#{version}` + rails "db:migrate", "db:rollback", "db:forward", "db:migrate:up", "db:migrate:down", "VERSION=#{version}" assert !File.exist?("db/schema.rb"), "should not dump schema when configured not to" end add_to_config("config.active_record.dump_schema_after_migration = true") Dir.chdir(app_path) do - `bin/rails generate model reviews book_id:integer` - `bin/rails db:migrate` + rails "generate", "model", "reviews", "book_id:integer" + rails "db:migrate" structure_dump = File.read("db/schema.rb") assert_match(/create_table "reviews"/, structure_dump) @@ -200,23 +413,22 @@ module ApplicationTests test "default schema generation after migration" do Dir.chdir(app_path) do - `bin/rails generate model book title:string; - bin/rails db:migrate` + rails "generate", "model", "book", "title:string" + rails "db:migrate" structure_dump = File.read("db/schema.rb") assert_match(/create_table "books"/, structure_dump) end end - test "test migration status migrated file is deleted" do + test "migration status migrated file is deleted" do Dir.chdir(app_path) do - `bin/rails generate model user username:string password:string; - bin/rails generate migration add_email_to_users email:string; - bin/rails db:migrate - rm db/migrate/*email*.rb` + rails "generate", "model", "user", "username:string", "password:string" + rails "generate", "migration", "add_email_to_users", "email:string" + rails "db:migrate" + `rm db/migrate/*email*.rb` - output = `bin/rails db:migrate:status` - File.write("test.txt", output) + output = rails("db:migrate:status") assert_match(/up\s+\d{14}\s+Create users/, output) assert_match(/up\s+\d{14}\s+\** NO FILE \**/, output) diff --git a/railties/test/application/rake/notes_test.rb b/railties/test/application/rake/notes_test.rb index e7ffea2e71..8e9fe9b6b4 100644 --- a/railties/test/application/rake/notes_test.rb +++ b/railties/test/application/rake/notes_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "isolation/abstract_unit" require "rails/source_annotation_extractor" @@ -90,7 +92,8 @@ module ApplicationTests test "custom rake task finds specific notes in specific directories" do app_file "app/controllers/some_controller.rb", "# TODO: note in app directory" - app_file "lib/some_file.rb", "# OPTIMIZE: note in lib directory\n" << "# FIXME: note in lib directory" + app_file "lib/some_file.rb", "# OPTIMIZE: note in lib directory\n" \ + "# FIXME: note in lib directory" app_file "test/some_test.rb", 1000.times.map { "" }.join("\n") << "# TODO: note in test directory" app_file "lib/tasks/notes_custom.rake", <<-EOS diff --git a/railties/test/application/rake/restart_test.rb b/railties/test/application/rake/restart_test.rb index 6ebd2d5461..8614560bf2 100644 --- a/railties/test/application/rake/restart_test.rb +++ b/railties/test/application/rake/restart_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "isolation/abstract_unit" module ApplicationTests @@ -15,12 +17,12 @@ module ApplicationTests test "rails restart touches tmp/restart.txt" do Dir.chdir(app_path) do - `bin/rails restart` + rails "restart" assert File.exist?("tmp/restart.txt") prev_mtime = File.mtime("tmp/restart.txt") sleep(1) - `bin/rails restart` + rails "restart" curr_mtime = File.mtime("tmp/restart.txt") assert_not_equal prev_mtime, curr_mtime end @@ -29,19 +31,10 @@ module ApplicationTests test "rails restart should work even if tmp folder does not exist" do Dir.chdir(app_path) do FileUtils.remove_dir("tmp") - `bin/rails restart` + rails "restart" assert File.exist?("tmp/restart.txt") end end - - test "rails restart removes server.pid also" do - Dir.chdir(app_path) do - FileUtils.mkdir_p("tmp/pids") - FileUtils.touch("tmp/pids/server.pid") - `bin/rails restart` - assert_not File.exist?("tmp/pids/server.pid") - end - end end end end diff --git a/railties/test/application/rake/tmp_test.rb b/railties/test/application/rake/tmp_test.rb new file mode 100644 index 0000000000..048fd7adcc --- /dev/null +++ b/railties/test/application/rake/tmp_test.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +require "isolation/abstract_unit" + +module ApplicationTests + module RakeTests + class TmpTest < ActiveSupport::TestCase + include ActiveSupport::Testing::Isolation + + def setup + build_app + end + + def teardown + teardown_app + end + + test "tmp:clear clear cache, socket and screenshot files" do + Dir.chdir(app_path) do + FileUtils.mkdir_p("tmp/cache") + FileUtils.touch("tmp/cache/cache_file") + + FileUtils.mkdir_p("tmp/sockets") + FileUtils.touch("tmp/sockets/socket_file") + + FileUtils.mkdir_p("tmp/screenshots") + FileUtils.touch("tmp/screenshots/fail.png") + + rails "tmp:clear" + + assert_not File.exist?("tmp/cache/cache_file") + assert_not File.exist?("tmp/sockets/socket_file") + assert_not File.exist?("tmp/screenshots/fail.png") + end + end + + test "tmp:clear should work if folder missing" do + FileUtils.remove_dir("#{app_path}/tmp") + rails "tmp:clear" + end + end + end +end diff --git a/railties/test/application/rake_test.rb b/railties/test/application/rake_test.rb index 1b64a0a1ca..5a6404bd0a 100644 --- a/railties/test/application/rake_test.rb +++ b/railties/test/application/rake_test.rb @@ -1,9 +1,12 @@ +# frozen_string_literal: true + require "isolation/abstract_unit" +require "env_helpers" require "active_support/core_ext/string/strip" module ApplicationTests class RakeTest < ActiveSupport::TestCase - include ActiveSupport::Testing::Isolation + include ActiveSupport::Testing::Isolation, EnvHelpers def setup build_app @@ -24,20 +27,20 @@ module ApplicationTests end test "task is protected when previous migration was production" do - Dir.chdir(app_path) do - output = `bin/rails generate model product name:string; - env RAILS_ENV=production bin/rails db:create db:migrate; - env RAILS_ENV=production bin/rails db:test:prepare test 2>&1` + with_rails_env "production" do + rails "generate", "model", "product", "name:string" + rails "db:create", "db:migrate" + output = rails("db:test:prepare", allow_failure: true) assert_match(/ActiveRecord::ProtectedEnvironmentError/, output) end end def test_not_protected_when_previous_migration_was_not_production - Dir.chdir(app_path) do - output = `bin/rails generate model product name:string; - env RAILS_ENV=test bin/rails db:create db:migrate; - env RAILS_ENV=test bin/rails db:test:prepare test 2>&1` + with_rails_env "test" do + rails "generate", "model", "product", "name:string" + rails "db:create", "db:migrate" + output = rails("db:test:prepare", "test") refute_match(/ActiveRecord::ProtectedEnvironmentError/, output) end @@ -54,7 +57,7 @@ module ApplicationTests Rails.application.initialize! RUBY - assert_match("SuperMiddleware", Dir.chdir(app_path) { `bin/rails middleware` }) + assert_match("SuperMiddleware", rails("middleware")) end def test_initializers_are_executed_in_rake_tasks @@ -69,7 +72,7 @@ module ApplicationTests end RUBY - output = Dir.chdir(app_path) { `bin/rails do_nothing` } + output = rails("do_nothing") assert_match "Doing something...", output end @@ -90,7 +93,7 @@ module ApplicationTests end RUBY - output = Dir.chdir(app_path) { `bin/rails do_nothing` } + output = rails("do_nothing") assert_match "Hello world", output end @@ -98,6 +101,7 @@ module ApplicationTests add_to_config <<-RUBY rake_tasks do task do_nothing: :environment do + puts 'There is nothing' end end RUBY @@ -110,15 +114,13 @@ module ApplicationTests raise 'should not be pre-required for rake even eager_load=true' RUBY - Dir.chdir(app_path) do - assert system("bin/rails do_nothing RAILS_ENV=production"), - "should not be pre-required for rake even eager_load=true" - end + output = rails("do_nothing", "RAILS_ENV=production") + assert_match "There is nothing", output end def test_code_statistics_sanity - assert_match "Code LOC: 26 Test LOC: 0 Code to Test Ratio: 1:0.0", - Dir.chdir(app_path) { `bin/rails stats` } + assert_match "Code LOC: 25 Test LOC: 0 Code to Test Ratio: 1:0.0", + rails("stats") end def test_rails_routes_calls_the_route_inspector @@ -128,8 +130,17 @@ module ApplicationTests end RUBY - output = Dir.chdir(app_path) { `bin/rails routes` } - assert_equal "Prefix Verb URI Pattern Controller#Action\n cart GET /cart(.:format) cart#show\n", output + output = rails("routes") + assert_equal <<-MESSAGE.strip_heredoc, output + Prefix Verb URI Pattern Controller#Action + cart GET /cart(.:format) cart#show + rails_service_blob GET /rails/active_storage/blobs/:signed_id/*filename(.:format) active_storage/blobs#show + rails_blob_variation GET /rails/active_storage/variants/:signed_blob_id/:variation_key/*filename(.:format) active_storage/variants#show + rails_blob_preview GET /rails/active_storage/previews/:signed_blob_id/:variation_key/*filename(.:format) active_storage/previews#show + rails_disk_service GET /rails/active_storage/disk/:encoded_key/*filename(.:format) active_storage/disk#show + update_rails_disk_service PUT /rails/active_storage/disk/:encoded_token(.:format) active_storage/disk#update + rails_direct_uploads POST /rails/active_storage/direct_uploads(.:format) active_storage/direct_uploads#create + MESSAGE end def test_singular_resource_output_in_rake_routes @@ -148,7 +159,7 @@ module ApplicationTests " DELETE /post(.:format) posts#destroy", " POST /post(.:format) posts#create\n"].join("\n") - output = Dir.chdir(app_path) { `bin/rails routes -c PostController` } + output = rails("routes", "-c", "PostController") assert_equal expected_output, output end @@ -161,13 +172,24 @@ module ApplicationTests end RUBY - output = Dir.chdir(app_path) { `bin/rails routes -g show` } - assert_equal "Prefix Verb URI Pattern Controller#Action\n cart GET /cart(.:format) cart#show\n", output + output = rails("routes", "-g", "show") + assert_equal <<-MESSAGE.strip_heredoc, output + Prefix Verb URI Pattern Controller#Action + cart GET /cart(.:format) cart#show + rails_service_blob GET /rails/active_storage/blobs/:signed_id/*filename(.:format) active_storage/blobs#show + rails_blob_variation GET /rails/active_storage/variants/:signed_blob_id/:variation_key/*filename(.:format) active_storage/variants#show + rails_blob_preview GET /rails/active_storage/previews/:signed_blob_id/:variation_key/*filename(.:format) active_storage/previews#show + rails_disk_service GET /rails/active_storage/disk/:encoded_key/*filename(.:format) active_storage/disk#show + MESSAGE - output = Dir.chdir(app_path) { `bin/rails routes -g POST` } - assert_equal "Prefix Verb URI Pattern Controller#Action\n POST /cart(.:format) cart#create\n", output + output = rails("routes", "-g", "POST") + assert_equal <<-MESSAGE.strip_heredoc, output + Prefix Verb URI Pattern Controller#Action + POST /cart(.:format) cart#create + rails_direct_uploads POST /rails/active_storage/direct_uploads(.:format) active_storage/direct_uploads#create + MESSAGE - output = Dir.chdir(app_path) { `bin/rails routes -g basketballs` } + output = rails("routes", "-g", "basketballs") assert_equal " Prefix Verb URI Pattern Controller#Action\n" \ "basketballs GET /basketballs(.:format) basketball#index\n", output end @@ -180,13 +202,13 @@ module ApplicationTests end RUBY - output = Dir.chdir(app_path) { `bin/rails routes -c cart` } + output = rails("routes", "-c", "cart") assert_equal "Prefix Verb URI Pattern Controller#Action\n cart GET /cart(.:format) cart#show\n", output - output = Dir.chdir(app_path) { `bin/rails routes -c Cart` } + output = rails("routes", "-c", "Cart") assert_equal "Prefix Verb URI Pattern Controller#Action\n cart GET /cart(.:format) cart#show\n", output - output = Dir.chdir(app_path) { `bin/rails routes -c CartController` } + output = rails("routes", "-c", "CartController") assert_equal "Prefix Verb URI Pattern Controller#Action\n cart GET /cart(.:format) cart#show\n", output end @@ -207,10 +229,10 @@ module ApplicationTests " DELETE /admin/post(.:format) admin/posts#destroy", " POST /admin/post(.:format) admin/posts#create\n"].join("\n") - output = Dir.chdir(app_path) { `bin/rails routes -c Admin::PostController` } + output = rails("routes", "-c", "Admin::PostController") assert_equal expected_output, output - output = Dir.chdir(app_path) { `bin/rails routes -c PostController` } + output = rails("routes", "-c", "PostController") assert_equal expected_output, output end @@ -220,12 +242,14 @@ module ApplicationTests end RUBY - assert_equal <<-MESSAGE.strip_heredoc, Dir.chdir(app_path) { `bin/rails routes` } - You don't have any routes defined! - - Please add some routes in config/routes.rb. - - For more information about routes, see the Rails guide: http://guides.rubyonrails.org/routing.html. + assert_equal <<-MESSAGE.strip_heredoc, rails("routes") + Prefix Verb URI Pattern Controller#Action + rails_service_blob GET /rails/active_storage/blobs/:signed_id/*filename(.:format) active_storage/blobs#show + rails_blob_variation GET /rails/active_storage/variants/:signed_blob_id/:variation_key/*filename(.:format) active_storage/variants#show + rails_blob_preview GET /rails/active_storage/previews/:signed_blob_id/:variation_key/*filename(.:format) active_storage/previews#show + rails_disk_service GET /rails/active_storage/disk/:encoded_key/*filename(.:format) active_storage/disk#show + update_rails_disk_service PUT /rails/active_storage/disk/:encoded_token(.:format) active_storage/disk#update + rails_direct_uploads POST /rails/active_storage/direct_uploads(.:format) active_storage/direct_uploads#create MESSAGE end @@ -237,7 +261,17 @@ module ApplicationTests RUBY output = Dir.chdir(app_path) { `bin/rake --rakefile Rakefile routes` } - assert_equal "Prefix Verb URI Pattern Controller#Action\n cart GET /cart(.:format) cart#show\n", output + + assert_equal <<-MESSAGE.strip_heredoc, output + Prefix Verb URI Pattern Controller#Action + cart GET /cart(.:format) cart#show + rails_service_blob GET /rails/active_storage/blobs/:signed_id/*filename(.:format) active_storage/blobs#show + rails_blob_variation GET /rails/active_storage/variants/:signed_blob_id/:variation_key/*filename(.:format) active_storage/variants#show + rails_blob_preview GET /rails/active_storage/previews/:signed_blob_id/:variation_key/*filename(.:format) active_storage/previews#show + rails_disk_service GET /rails/active_storage/disk/:encoded_key/*filename(.:format) active_storage/disk#show + update_rails_disk_service PUT /rails/active_storage/disk/:encoded_token(.:format) active_storage/disk#update + rails_direct_uploads POST /rails/active_storage/direct_uploads(.:format) active_storage/direct_uploads#create + MESSAGE end def test_logger_is_flushed_when_exiting_production_rake_tasks @@ -249,44 +283,37 @@ module ApplicationTests end RUBY - output = Dir.chdir(app_path) { `bin/rails log_something RAILS_ENV=production && cat log/production.log` } - assert_match "Sample log message", output + rails "log_something", "RAILS_ENV=production" + assert_match "Sample log message", File.read("#{app_path}/log/production.log") end def test_loading_specific_fixtures - Dir.chdir(app_path) do - `bin/rails generate model user username:string password:string; - bin/rails generate model product name:string; - bin/rails db:migrate` - end + rails "generate", "model", "user", "username:string", "password:string" + rails "generate", "model", "product", "name:string" + rails "db:migrate" require "#{rails_root}/config/environment" # loading a specific fixture - errormsg = Dir.chdir(app_path) { `bin/rails db:fixtures:load FIXTURES=products` } - assert $?.success?, errormsg + rails "db:fixtures:load", "FIXTURES=products" assert_equal 2, ::AppTemplate::Application::Product.count assert_equal 0, ::AppTemplate::Application::User.count end def test_loading_only_yml_fixtures - Dir.chdir(app_path) do - `bin/rails db:migrate` - end + rails "db:migrate" app_file "test/fixtures/products.csv", "" require "#{rails_root}/config/environment" - errormsg = Dir.chdir(app_path) { `bin/rails db:fixtures:load` } - assert $?.success?, errormsg + rails "db:fixtures:load" end def test_scaffold_tests_pass_by_default - output = Dir.chdir(app_path) do - `bin/rails generate scaffold user username:string password:string; - RAILS_ENV=test bin/rails db:migrate test` - end + rails "generate", "scaffold", "user", "username:string", "password:string" + with_rails_env("test") { rails("db:migrate") } + output = rails("test") assert_match(/7 runs, 9 assertions, 0 failures, 0 errors/, output) assert_no_match(/Errors running/, output) @@ -302,22 +329,20 @@ module ApplicationTests end RUBY - output = Dir.chdir(app_path) do - `bin/rails generate scaffold user username:string password:string; - RAILS_ENV=test bin/rails db:migrate test` - end + rails "generate", "scaffold", "user", "username:string", "password:string" + with_rails_env("test") { rails("db:migrate") } + output = rails("test") assert_match(/5 runs, 7 assertions, 0 failures, 0 errors/, output) assert_no_match(/Errors running/, output) end def test_scaffold_with_references_columns_tests_pass_by_default - output = Dir.chdir(app_path) do - `bin/rails generate model Product; - bin/rails generate model Cart; - bin/rails generate scaffold LineItems product:references cart:belongs_to; - RAILS_ENV=test bin/rails db:migrate test` - end + rails "generate", "model", "Product" + rails "generate", "model", "Cart" + rails "generate", "scaffold", "LineItems", "product:references", "cart:belongs_to" + with_rails_env("test") { rails("db:migrate") } + output = rails("test") assert_match(/7 runs, 9 assertions, 0 failures, 0 errors/, output) assert_no_match(/Errors running/, output) @@ -325,59 +350,47 @@ module ApplicationTests def test_db_test_prepare_when_using_sql_format add_to_config "config.active_record.schema_format = :sql" - output = Dir.chdir(app_path) do - `bin/rails generate scaffold user username:string; - bin/rails db:migrate; - bin/rails db:test:prepare 2>&1 --trace` - end + rails "generate", "scaffold", "user", "username:string" + rails "db:migrate" + output = rails("db:test:prepare", "--trace") assert_match(/Execute db:test:load_structure/, output) end def test_rake_dump_structure_should_respect_db_structure_env_variable - Dir.chdir(app_path) do - # ensure we have a schema_migrations table to dump - `bin/rails db:migrate db:structure:dump SCHEMA=db/my_structure.sql` - end + # ensure we have a schema_migrations table to dump + rails "db:migrate", "db:structure:dump", "SCHEMA=db/my_structure.sql" assert File.exist?(File.join(app_path, "db", "my_structure.sql")) end def test_rake_dump_structure_should_be_called_twice_when_migrate_redo add_to_config "config.active_record.schema_format = :sql" - output = Dir.chdir(app_path) do - `bin/rails g model post title:string; - bin/rails db:migrate:redo 2>&1 --trace;` - end + rails "g", "model", "post", "title:string" + output = rails("db:migrate:redo", "--trace") # expect only Invoke db:structure:dump (first_time) assert_no_match(/^\*\* Invoke db:structure:dump\s+$/, output) end def test_rake_dump_schema_cache - Dir.chdir(app_path) do - `bin/rails generate model post title:string; - bin/rails generate model product name:string; - bin/rails db:migrate db:schema:cache:dump` - end + rails "generate", "model", "post", "title:string" + rails "generate", "model", "product", "name:string" + rails "db:migrate", "db:schema:cache:dump" assert File.exist?(File.join(app_path, "db", "schema_cache.yml")) end def test_rake_clear_schema_cache - Dir.chdir(app_path) do - `bin/rails db:schema:cache:dump db:schema:cache:clear` - end + rails "db:schema:cache:dump", "db:schema:cache:clear" assert !File.exist?(File.join(app_path, "db", "schema_cache.yml")) end def test_copy_templates - Dir.chdir(app_path) do - `bin/rails app:templates:copy` - %w(controller mailer scaffold).each do |dir| - assert File.exist?(File.join(app_path, "lib", "templates", "erb", dir)) - end - %w(controller helper scaffold_controller assets).each do |dir| - assert File.exist?(File.join(app_path, "lib", "templates", "rails", dir)) - end + rails "app:templates:copy" + %w(controller mailer scaffold).each do |dir| + assert File.exist?(File.join(app_path, "lib", "templates", "erb", dir)) + end + %w(controller helper scaffold_controller assets).each do |dir| + assert File.exist?(File.join(app_path, "lib", "templates", "rails", dir)) end end @@ -385,18 +398,8 @@ module ApplicationTests app_file "config/initializers/dummy.rb", "puts 'Hello, World!'" app_file "template.rb", "" - output = Dir.chdir(app_path) do - `bin/rails app:template LOCATION=template.rb` - end - + output = rails("app:template", "LOCATION=template.rb") assert_match(/Hello, World!/, output) end - - def test_tmp_clear_should_work_if_folder_missing - FileUtils.remove_dir("#{app_path}/tmp") - errormsg = Dir.chdir(app_path) { `bin/rails tmp:clear` } - assert_predicate $?, :success? - assert_empty errormsg - end end end diff --git a/railties/test/application/rendering_test.rb b/railties/test/application/rendering_test.rb index ccafc5b6f1..3724886c54 100644 --- a/railties/test/application/rendering_test.rb +++ b/railties/test/application/rendering_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "isolation/abstract_unit" require "rack/test" diff --git a/railties/test/application/routing_test.rb b/railties/test/application/routing_test.rb index 6742da20cc..bec038fb51 100644 --- a/railties/test/application/routing_test.rb +++ b/railties/test/application/routing_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "isolation/abstract_unit" require "rack/test" @@ -293,7 +295,7 @@ module ApplicationTests extend ActiveModel::Naming include ActiveModel::Conversion - def model_name + def self.model_name @_model_name ||= ActiveModel::Name.new(self.class, nil, "User") end @@ -430,7 +432,7 @@ module ApplicationTests extend ActiveModel::Naming include ActiveModel::Conversion - def model_name + def self.model_name @_model_name ||= ActiveModel::Name.new(self.class, nil, "User") end @@ -542,7 +544,7 @@ module ApplicationTests extend ActiveModel::Naming include ActiveModel::Conversion - def model_name + def self.model_name @_model_name ||= ActiveModel::Name.new(self.class, nil, "User") end diff --git a/railties/test/application/runner_test.rb b/railties/test/application/runner_test.rb index 7d058f6ee6..aa5d495c97 100644 --- a/railties/test/application/runner_test.rb +++ b/railties/test/application/runner_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "isolation/abstract_unit" require "env_helpers" @@ -24,15 +26,20 @@ module ApplicationTests end def test_should_include_runner_in_shebang_line_in_help_without_option - assert_match "/rails runner", Dir.chdir(app_path) { `bin/rails runner` } + assert_match "/rails runner", rails("runner", allow_failure: true) end def test_should_include_runner_in_shebang_line_in_help - assert_match "/rails runner", Dir.chdir(app_path) { `bin/rails runner --help` } + assert_match "/rails runner", rails("runner", "--help") end def test_should_run_ruby_statement - assert_match "42", Dir.chdir(app_path) { `bin/rails runner "puts User.count"` } + assert_match "42", rails("runner", "puts User.count") + end + + def test_should_set_argv_when_running_code + output = rails("runner", "puts ARGV.join(',')", "--foo", "a1", "-b", "a2", "a3", "--moo") + assert_equal "--foo,a1,-b,a2,a3,--moo", output.chomp end def test_should_run_file @@ -40,7 +47,7 @@ module ApplicationTests puts User.count SCRIPT - assert_match "42", Dir.chdir(app_path) { `bin/rails runner "bin/count_users.rb"` } + assert_match "42", rails("runner", "bin/count_users.rb") end def test_no_minitest_loaded_in_production_mode @@ -57,7 +64,7 @@ module ApplicationTests puts $0 SCRIPT - assert_match "bin/dollar0.rb", Dir.chdir(app_path) { `bin/rails runner "bin/dollar0.rb"` } + assert_match "bin/dollar0.rb", rails("runner", "bin/dollar0.rb") end def test_should_set_dollar_program_name_to_file @@ -65,7 +72,7 @@ module ApplicationTests puts $PROGRAM_NAME SCRIPT - assert_match "bin/program_name.rb", Dir.chdir(app_path) { `bin/rails runner "bin/program_name.rb"` } + assert_match "bin/program_name.rb", rails("runner", "bin/program_name.rb") end def test_passes_extra_args_to_file @@ -73,7 +80,15 @@ module ApplicationTests p ARGV SCRIPT - assert_match %w( a b ).to_s, Dir.chdir(app_path) { `bin/rails runner "bin/program_name.rb" a b` } + assert_match %w( a b ).to_s, rails("runner", "bin/program_name.rb", "a", "b") + end + + def test_should_run_stdin + app_file "bin/count_users.rb", <<-SCRIPT + puts User.count + SCRIPT + + assert_match "42", Dir.chdir(app_path) { `cat bin/count_users.rb | bin/rails runner -` } end def test_with_hook @@ -83,35 +98,47 @@ module ApplicationTests end RUBY - assert_match "true", Dir.chdir(app_path) { `bin/rails runner "puts Rails.application.config.ran"` } + assert_match "true", rails("runner", "puts Rails.application.config.ran") end def test_default_environment - assert_match "development", Dir.chdir(app_path) { `bin/rails runner "puts Rails.env"` } + assert_match "development", rails("runner", "puts Rails.env") end def test_runner_detects_syntax_errors - output = Dir.chdir(app_path) { `bin/rails runner "puts 'hello world" 2>&1` } + output = rails("runner", "puts 'hello world", allow_failure: true) assert_not $?.success? assert_match "unterminated string meets end of file", output end def test_runner_detects_bad_script_name - output = Dir.chdir(app_path) { `bin/rails runner "iuiqwiourowe" 2>&1` } + output = rails("runner", "iuiqwiourowe", allow_failure: true) assert_not $?.success? assert_match "undefined local variable or method `iuiqwiourowe' for", output end def test_environment_with_rails_env with_rails_env "production" do - assert_match "production", Dir.chdir(app_path) { `bin/rails runner "puts Rails.env"` } + assert_match "production", rails("runner", "puts Rails.env") end end def test_environment_with_rack_env with_rack_env "production" do - assert_match "production", Dir.chdir(app_path) { `bin/rails runner "puts Rails.env"` } + assert_match "production", rails("runner", "puts Rails.env") end end + + def test_can_call_same_name_class_as_defined_in_thor + app_file "app/models/task.rb", <<-MODEL + class Task + def self.count + 42 + end + end + MODEL + + assert_match "42", rails("runner", "puts Task.count") + end end end diff --git a/railties/test/application/server_test.rb b/railties/test/application/server_test.rb new file mode 100644 index 0000000000..f3a7e00a4d --- /dev/null +++ b/railties/test/application/server_test.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true + +require "isolation/abstract_unit" +require "console_helpers" +require "rails/command" +require "rails/commands/server/server_command" + +module ApplicationTests + class ServerTest < ActiveSupport::TestCase + include ActiveSupport::Testing::Isolation + include ConsoleHelpers + + def setup + build_app + end + + def teardown + teardown_app + end + + test "deprecate support of older `config.ru`" do + remove_file "config.ru" + app_file "config.ru", <<-RUBY + require_relative 'config/environment' + run AppTemplate::Application + RUBY + + server = Rails::Server.new(config: "#{app_path}/config.ru") + server.app + + log = File.read(Rails.application.config.paths["log"].first) + assert_match(/DEPRECATION WARNING: Using `Rails::Application` subclass to start the server is deprecated/, log) + end + + test "restart rails server with custom pid file path" do + skip "PTY unavailable" unless available_pty? + + File.open("#{app_path}/config/boot.rb", "w") do |f| + f.puts "ENV['BUNDLE_GEMFILE'] = '#{Bundler.default_gemfile.to_s}'" + f.puts "require 'bundler/setup'" + end + + master, slave = PTY.open + pid = nil + + begin + pid = Process.spawn("#{app_path}/bin/rails server -P tmp/dummy.pid", in: slave, out: slave, err: slave) + assert_output("Listening", master) + + rails("restart") + + assert_output("Restarting", master) + assert_output("Inherited", master) + ensure + kill(pid) if pid + end + end + + private + def kill(pid) + Process.kill("TERM", pid) + Process.wait(pid) + rescue Errno::ESRCH + end + end +end diff --git a/railties/test/application/test_runner_test.rb b/railties/test/application/test_runner_test.rb index a8e3a7ec5b..a01325fdb8 100644 --- a/railties/test/application/test_runner_test.rb +++ b/railties/test/application/test_runner_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "isolation/abstract_unit" require "active_support/core_ext/string/strip" require "env_helpers" @@ -16,13 +18,13 @@ module ApplicationTests end def test_run_via_backwardscompatibility - require "rails/test_unit/minitest_plugin" + require "minitest/rails_plugin" assert_nothing_raised do Minitest.run_via[:ruby] = true end - assert_predicate Minitest.run_via, :ruby? + assert Minitest.run_via[:ruby] end def test_run_single_file @@ -31,19 +33,33 @@ module ApplicationTests assert_match "1 runs, 1 assertions, 0 failures", run_test_command("test/models/foo_test.rb") end + def test_run_single_file_with_absolute_path + create_test_file :models, "foo" + create_test_file :models, "bar" + assert_match "1 runs, 1 assertions, 0 failures", run_test_command("#{app_path}/test/models/foo_test.rb") + end + def test_run_multiple_files create_test_file :models, "foo" create_test_file :models, "bar" assert_match "2 runs, 2 assertions, 0 failures", run_test_command("test/models/foo_test.rb test/models/bar_test.rb") end + def test_run_multiple_files_with_absolute_paths + create_test_file :models, "foo" + create_test_file :controllers, "foobar_controller" + create_test_file :models, "bar" + + assert_match "2 runs, 2 assertions, 0 failures", run_test_command("#{app_path}/test/models/foo_test.rb #{app_path}/test/controllers/foobar_controller_test.rb") + end + def test_run_file_with_syntax_error app_file "test/models/error_test.rb", <<-RUBY require 'test_helper' def; end RUBY - error = capture(:stderr) { run_test_command("test/models/error_test.rb") } + error = capture(:stderr) { run_test_command("test/models/error_test.rb", stderr: true) } assert_match "syntax error", error end @@ -75,13 +91,11 @@ module ApplicationTests create_test_file :unit, "baz_unit" create_test_file :controllers, "foobar_controller" - Dir.chdir(app_path) do - `bin/rails test:units`.tap do |output| - assert_match "FooTest", output - assert_match "BarHelperTest", output - assert_match "BazUnitTest", output - assert_match "3 runs, 3 assertions, 0 failures", output - end + rails("test:units").tap do |output| + assert_match "FooTest", output + assert_match "BarHelperTest", output + assert_match "BazUnitTest", output + assert_match "3 runs, 3 assertions, 0 failures", output end end @@ -124,13 +138,11 @@ module ApplicationTests create_test_file :functional, "baz_functional" create_test_file :models, "foo" - Dir.chdir(app_path) do - `bin/rails test:functionals`.tap do |output| - assert_match "FooMailerTest", output - assert_match "BarControllerTest", output - assert_match "BazFunctionalTest", output - assert_match "3 runs, 3 assertions, 0 failures", output - end + rails("test:functionals").tap do |output| + assert_match "FooMailerTest", output + assert_match "BarControllerTest", output + assert_match "BazFunctionalTest", output + assert_match "3 runs, 3 assertions, 0 failures", output end end @@ -264,6 +276,18 @@ module ApplicationTests end end + def test_run_multiple_folders_with_absolute_paths + create_test_file :models, "account" + create_test_file :controllers, "accounts_controller" + create_test_file :helpers, "foo_helper" + + run_test_command("#{app_path}/test/models #{app_path}/test/controllers").tap do |output| + assert_match "AccountTest", output + assert_match "AccountsControllerTest", output + assert_match "2 runs, 2 assertions, 0 failures, 0 errors, 0 skips", output + end + end + def test_run_with_ruby_command app_file "test/models/post_test.rb", <<-RUBY require 'test_helper' @@ -323,7 +347,7 @@ module ApplicationTests assert true end - test "test line filter does not run this" do + test "line filter does not run this" do assert true end end @@ -469,7 +493,7 @@ module ApplicationTests def test_run_app_without_rails_loaded # Simulate a real Rails app boot. app_file "config/boot.rb", <<-RUBY - ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) + ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) require 'bundler/setup' # Set up gems listed in the Gemfile. RUBY @@ -489,18 +513,18 @@ module ApplicationTests create_test_file :models, "post", pass: false output = run_test_command("test/models/post_test.rb") - assert_match %r{Finished in.*\n\n1 runs, 1 assertions}, output + assert_match %r{Finished in.*\n1 runs, 1 assertions}, output end def test_fail_fast create_test_file :models, "post", pass: false assert_match(/Interrupt/, - capture(:stderr) { run_test_command("test/models/post_test.rb --fail-fast") }) + capture(:stderr) { run_test_command("test/models/post_test.rb --fail-fast", stderr: true) }) end def test_raise_error_when_specified_file_does_not_exist - error = capture(:stderr) { run_test_command("test/not_exists.rb") } + error = capture(:stderr) { run_test_command("test/not_exists.rb", stderr: true) } assert_match(%r{cannot load such file.+test/not_exists\.rb}, error) end @@ -526,14 +550,16 @@ module ApplicationTests def test_rails_db_create_all_restores_db_connection create_test_file :models, "account" - output = Dir.chdir(app_path) { `bin/rails db:create:all db:migrate && echo ".tables" | rails dbconsole` } + rails "db:create:all", "db:migrate" + output = Dir.chdir(app_path) { `echo ".tables" | rails dbconsole` } assert_match "ar_internal_metadata", output, "tables should be dumped" end def test_rails_db_create_all_restores_db_connection_after_drop create_test_file :models, "account" - Dir.chdir(app_path) { `bin/rails db:create:all` } # create all to avoid warnings - output = Dir.chdir(app_path) { `bin/rails db:drop:all db:create:all db:migrate && echo ".tables" | rails dbconsole` } + rails "db:create:all" # create all to avoid warnings + rails "db:drop:all", "db:create:all", "db:migrate" + output = Dir.chdir(app_path) { `echo ".tables" | rails dbconsole` } assert_match "ar_internal_metadata", output, "tables should be dumped" end @@ -543,6 +569,40 @@ module ApplicationTests assert_match "AccountTest#test_truth", output, "passing TEST= should run selected test" end + def test_running_with_ruby_gets_test_env_by_default + # Subshells inherit `ENV`, so we need to ensure `RAILS_ENV` is set to + # nil before we run the tests in the test app. + re, ENV["RAILS_ENV"] = ENV["RAILS_ENV"], nil + + file = create_test_for_env("test") + results = Dir.chdir(app_path) { + `ruby -Ilib:test #{file}`.each_line.map { |line| JSON.parse line } + } + assert_equal 1, results.length + failures = results.first["failures"] + flunk(failures.first) unless failures.empty? + + ensure + ENV["RAILS_ENV"] = re + end + + def test_running_with_ruby_can_set_env_via_cmdline + # Subshells inherit `ENV`, so we need to ensure `RAILS_ENV` is set to + # nil before we run the tests in the test app. + re, ENV["RAILS_ENV"] = ENV["RAILS_ENV"], nil + + file = create_test_for_env("development") + results = Dir.chdir(app_path) { + `RAILS_ENV=development ruby -Ilib:test #{file}`.each_line.map { |line| JSON.parse line } + } + assert_equal 1, results.length + failures = results.first["failures"] + flunk(failures.first) unless failures.empty? + + ensure + ENV["RAILS_ENV"] = re + end + def test_rake_passes_multiple_TESTOPTS_to_minitest create_test_file :models, "account" output = Dir.chdir(app_path) { `bin/rake test TESTOPTS='-v --seed=1234'` } @@ -573,7 +633,7 @@ module ApplicationTests end RUBY assert_match(/warning: assigned but unused variable/, - capture(:stderr) { run_test_command("test/models/warnings_test.rb -w") }) + capture(:stderr) { run_test_command("test/models/warnings_test.rb -w", stderr: true) }) end def test_reset_sessions_before_rollback_on_system_tests @@ -651,12 +711,12 @@ module ApplicationTests end private - def run_test_command(arguments = "test/unit/test_test.rb") - Dir.chdir(app_path) { `bin/rails t #{arguments}` } + def run_test_command(arguments = "test/unit/test_test.rb", **opts) + rails "t", *Shellwords.split(arguments), allow_failure: true, **opts end def create_model_with_fixture - script "generate model user name:string" + rails "generate", "model", "user", "name:string" app_file "test/fixtures/users.yml", <<-YAML.strip_heredoc vampire: @@ -701,6 +761,45 @@ module ApplicationTests app_file "db/schema.rb", "" end + def create_test_for_env(env) + app_file "test/models/environment_test.rb", <<-RUBY + require 'test_helper' + class JSONReporter < Minitest::AbstractReporter + def record(result) + puts JSON.dump(klass: result.class.name, + name: result.name, + failures: result.failures, + assertions: result.assertions, + time: result.time) + end + end + + def Minitest.plugin_json_reporter_init(opts) + Minitest.reporter.reporters.clear + Minitest.reporter.reporters << JSONReporter.new + end + + Minitest.extensions << "rails" + Minitest.extensions << "json_reporter" + + # Minitest uses RubyGems to find plugins, and since RubyGems + # doesn't know about the Rails installation we're pointing at, + # Minitest won't require the Rails minitest plugin when we run + # these integration tests. So we have to manually require the + # Minitest plugin here. + require 'minitest/rails_plugin' + + class EnvironmentTest < ActiveSupport::TestCase + def test_environment + test_db = ActiveRecord::Base.configurations[#{env.dump}]["database"] + db_file = ActiveRecord::Base.connection_config[:database] + assert_match(test_db, db_file) + assert_equal #{env.dump}, ENV["RAILS_ENV"] + end + end + RUBY + end + def create_test_file(path = :unit, name = "test", pass: true) app_file "test/#{path}/#{name}_test.rb", <<-RUBY require 'test_helper' @@ -727,17 +826,17 @@ module ApplicationTests end def create_scaffold - script "generate scaffold user name:string" - Dir.chdir(app_path) { File.exist?("app/models/user.rb") } + rails "generate", "scaffold", "user", "name:string" + assert File.exist?("#{app_path}/app/models/user.rb") run_migration end def create_controller - script "generate controller admin/dashboard index" + rails "generate", "controller", "admin/dashboard", "index" end def run_migration - Dir.chdir(app_path) { `bin/rails db:migrate` } + rails "db:migrate" end end end diff --git a/railties/test/application/test_test.rb b/railties/test/application/test_test.rb index 32d2a6857c..0a51e98656 100644 --- a/railties/test/application/test_test.rb +++ b/railties/test/application/test_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "isolation/abstract_unit" module ApplicationTests @@ -55,7 +57,7 @@ module ApplicationTests end RUBY - assert_unsuccessful_run "unit/foo_test.rb", "Failed assertion" + assert_unsuccessful_run "unit/foo_test.rb", "Failure:\nFooTest#test_truth" end test "integration test" do @@ -100,7 +102,7 @@ module ApplicationTests end test "ruby schema migrations" do - output = script("generate model user name:string") + output = rails("generate", "model", "user", "name:string") version = output.match(/(\d+)_create_users\.rb/)[1] app_file "test/models/user_test.rb", <<-RUBY @@ -137,7 +139,7 @@ module ApplicationTests end test "sql structure migrations" do - output = script("generate model user name:string") + output = rails("generate", "model", "user", "name:string") version = output.match(/(\d+)_create_users\.rb/)[1] app_file "test/models/user_test.rb", <<-RUBY @@ -176,7 +178,7 @@ module ApplicationTests end test "sql structure migrations when adding column to existing table" do - output_1 = script("generate model user name:string") + output_1 = rails("generate", "model", "user", "name:string") version_1 = output_1.match(/(\d+)_create_users\.rb/)[1] app_file "test/models/user_test.rb", <<-RUBY @@ -201,7 +203,7 @@ module ApplicationTests assert_successful_test_run("models/user_test.rb") - output_2 = script("generate migration add_email_to_users") + output_2 = rails("generate", "migration", "add_email_to_users") version_2 = output_2.match(/(\d+)_add_email_to_users\.rb/)[1] app_file "test/models/user_test.rb", <<-RUBY @@ -229,7 +231,7 @@ module ApplicationTests # For now, the user has to synchronize the schema manually. # This test-case serves as a reminder for this use-case. test "manually synchronize test schema after rollback" do - output = script("generate model user name:string") + output = rails("generate", "model", "user", "name:string") version = output.match(/(\d+)_create_users\.rb/)[1] app_file "test/models/user_test.rb", <<-RUBY @@ -263,7 +265,7 @@ module ApplicationTests assert_successful_test_run "models/user_test.rb" - Dir.chdir(app_path) { `bin/rails db:test:prepare` } + rails "db:test:prepare" assert_unsuccessful_run "models/user_test.rb", <<-ASSERTION Expected: ["id", "name"] @@ -272,7 +274,7 @@ Expected: ["id", "name"] end test "hooks for plugins" do - output = script("generate model user name:string") + output = rails("generate", "model", "user", "name:string") version = output.match(/(\d+)_create_users\.rb/)[1] app_file "lib/tasks/hooks.rake", <<-RUBY @@ -332,7 +334,7 @@ Expected: ["id", "name"] end def run_test_file(name, options = {}) - Dir.chdir(app_path) { `bin/rails test "#{app_path}/test/#{name}" 2>&1` } + rails "test", "#{app_path}/test/#{name}", allow_failure: true end end end diff --git a/railties/test/application/url_generation_test.rb b/railties/test/application/url_generation_test.rb index 37f129475c..f22b9fda3d 100644 --- a/railties/test/application/url_generation_test.rb +++ b/railties/test/application/url_generation_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "isolation/abstract_unit" module ApplicationTests @@ -14,7 +16,6 @@ module ApplicationTests require "action_view/railtie" class MyApp < Rails::Application - secrets.secret_key_base = "3b7cd727ee24e8444053437c36cc66c4" config.session_store :cookie_store, key: "_myapp_session" config.active_support.deprecation = :log config.eager_load = false diff --git a/railties/test/application/version_test.rb b/railties/test/application/version_test.rb index 6b419ae7ae..ae85cf8f05 100644 --- a/railties/test/application/version_test.rb +++ b/railties/test/application/version_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "isolation/abstract_unit" require "rails/gem_version" @@ -13,12 +15,12 @@ class VersionTest < ActiveSupport::TestCase end test "command works" do - output = Dir.chdir(app_path) { `bin/rails version` } + output = rails("version") assert_equal "Rails #{Rails.gem_version}\n", output end test "short-cut alias works" do - output = Dir.chdir(app_path) { `bin/rails -v` } + output = rails("-v") assert_equal "Rails #{Rails.gem_version}\n", output end end diff --git a/railties/test/backtrace_cleaner_test.rb b/railties/test/backtrace_cleaner_test.rb index f71e56f323..70917ba20b 100644 --- a/railties/test/backtrace_cleaner_test.rb +++ b/railties/test/backtrace_cleaner_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" require "rails/backtrace_cleaner" diff --git a/railties/test/code_statistics_calculator_test.rb b/railties/test/code_statistics_calculator_test.rb index 1bd4225f34..51917de2e0 100644 --- a/railties/test/code_statistics_calculator_test.rb +++ b/railties/test/code_statistics_calculator_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" require "rails/code_statistics_calculator" @@ -317,7 +319,7 @@ class Animal private def temp_file(name, content) - dir = File.expand_path "../fixtures/tmp", __FILE__ + dir = File.expand_path "fixtures/tmp", __dir__ path = "#{dir}/#{name}" FileUtils.mkdir_p dir diff --git a/railties/test/code_statistics_test.rb b/railties/test/code_statistics_test.rb index 965b6eeb79..7ad1ac3094 100644 --- a/railties/test/code_statistics_test.rb +++ b/railties/test/code_statistics_test.rb @@ -1,9 +1,11 @@ +# frozen_string_literal: true + require "abstract_unit" require "rails/code_statistics" class CodeStatisticsTest < ActiveSupport::TestCase def setup - @tmp_path = File.expand_path(File.join(File.dirname(__FILE__), "fixtures", "tmp")) + @tmp_path = File.expand_path("fixtures/tmp", __dir__) @dir_js = File.join(@tmp_path, "lib.js") FileUtils.mkdir_p(@dir_js) end diff --git a/railties/test/command/base_test.rb b/railties/test/command/base_test.rb new file mode 100644 index 0000000000..a49ae8aae7 --- /dev/null +++ b/railties/test/command/base_test.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +require "abstract_unit" +require "rails/command" +require "rails/commands/generate/generate_command" +require "rails/commands/secrets/secrets_command" + +class Rails::Command::BaseTest < ActiveSupport::TestCase + test "printing commands" do + assert_equal %w(generate), Rails::Command::GenerateCommand.printing_commands + assert_equal %w(secrets:setup secrets:edit secrets:show), Rails::Command::SecretsCommand.printing_commands + end +end diff --git a/railties/test/commands/console_test.rb b/railties/test/commands/console_test.rb index 4fc082e4ca..45ab8d87ff 100644 --- a/railties/test/commands/console_test.rb +++ b/railties/test/commands/console_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" require "env_helpers" require "rails/command" @@ -47,7 +49,7 @@ class Rails::ConsoleTest < ActiveSupport::TestCase end def test_console_with_environment - start ["-e production"] + start ["-e", "production"] assert_match(/\sproduction\s/, output) end @@ -82,24 +84,35 @@ class Rails::ConsoleTest < ActiveSupport::TestCase assert_match(/\sspecial-production\s/, output) end + def test_e_option_is_properly_expanded + start ["-e", "prod"] + assert_match(/\sproduction\s/, output) + end + def test_environment_option start ["--environment=special-production"] assert_match(/\sspecial-production\s/, output) end def test_rails_env_is_production_when_first_argument_is_p - start ["p"] - assert_match(/\sproduction\s/, output) + assert_deprecated do + start ["p"] + assert_match(/\sproduction\s/, output) + end end def test_rails_env_is_test_when_first_argument_is_t - start ["t"] - assert_match(/\stest\s/, output) + assert_deprecated do + start ["t"] + assert_match(/\stest\s/, output) + end end def test_rails_env_is_development_when_argument_is_d - start ["d"] - assert_match(/\sdevelopment\s/, output) + assert_deprecated do + start ["d"] + assert_match(/\sdevelopment\s/, output) + end end def test_rails_env_is_dev_when_argument_is_dev_and_dev_env_is_present @@ -111,7 +124,9 @@ class Rails::ConsoleTest < ActiveSupport::TestCase end end - assert_match("dev", parse_arguments(["dev"])[:environment]) + assert_deprecated do + assert_match("dev", parse_arguments(["dev"])[:environment]) + end ensure Rails::Command::ConsoleCommand.class_eval do undef_method :available_environments @@ -157,21 +172,8 @@ class Rails::ConsoleTest < ActiveSupport::TestCase end def parse_arguments(args) - Rails::Command::ConsoleCommand.class_eval do - alias_method :old_perform, :perform - define_method(:perform) do - extract_environment_option_from_argument - - options - end - end - - Rails::Command.invoke(:console, args) - ensure - Rails::Command::ConsoleCommand.class_eval do - undef_method :perform - alias_method :perform, :old_perform - undef_method :old_perform - end + command = Rails::Command::ConsoleCommand.new([], args) + command.send(:extract_environment_option_from_argument) + command.options end end diff --git a/railties/test/commands/credentials_test.rb b/railties/test/commands/credentials_test.rb new file mode 100644 index 0000000000..7c464b3fde --- /dev/null +++ b/railties/test/commands/credentials_test.rb @@ -0,0 +1,74 @@ +# frozen_string_literal: true + +require "isolation/abstract_unit" +require "env_helpers" +require "rails/command" +require "rails/commands/credentials/credentials_command" + +class Rails::Command::CredentialsCommandTest < ActiveSupport::TestCase + include ActiveSupport::Testing::Isolation, EnvHelpers + + setup { build_app } + + teardown { teardown_app } + + test "edit without editor gives hint" do + run_edit_command(editor: "").tap do |output| + assert_match "No $EDITOR to open file in", output + assert_match "bin/rails credentials:edit", output + end + end + + test "edit credentials" do + # Run twice to ensure credentials can be reread after first edit pass. + 2.times do + assert_match(/access_key_id: 123/, run_edit_command) + end + end + + test "edit command does not add master key to gitignore when already exist" do + run_edit_command + + Dir.chdir(app_path) do + gitignore = File.read(".gitignore") + assert_equal 1, gitignore.scan(%r|config/master\.key|).length + end + end + + test "edit command does not overwrite by default if credentials already exists" do + run_edit_command(editor: "eval echo api_key: abc >") + assert_match(/api_key: abc/, run_show_command) + + run_edit_command + assert_match(/api_key: abc/, run_show_command) + end + + test "show credentials" do + assert_match(/access_key_id: 123/, run_show_command) + end + + test "show command raise error when require_master_key is specified and key does not exist" do + remove_file "config/master.key" + add_to_config "config.require_master_key = true" + + assert_match(/Missing encryption key to decrypt file with/, run_show_command(allow_failure: true)) + end + + test "show command does not raise error when require_master_key is false and master key does not exist" do + remove_file "config/master.key" + add_to_config "config.require_master_key = false" + + assert_match(/Missing master key to decrypt credentials/, run_show_command) + end + + private + def run_edit_command(editor: "cat") + switch_env("EDITOR", editor) do + rails "credentials:edit" + end + end + + def run_show_command(**options) + rails "credentials:show", **options + end +end diff --git a/railties/test/commands/dbconsole_test.rb b/railties/test/commands/dbconsole_test.rb index 0f8c5dbb79..6ad96b28c7 100644 --- a/railties/test/commands/dbconsole_test.rb +++ b/railties/test/commands/dbconsole_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" require "minitest/mock" require "rails/command" @@ -98,14 +100,24 @@ class Rails::DBConsoleTest < ActiveSupport::TestCase end def test_rails_env_is_development_when_argument_is_dev + assert_deprecated do + stub_available_environments([ "development", "test" ]) do + assert_match("development", parse_arguments([ "dev" ])[:environment]) + end + end + end + + def test_rails_env_is_development_when_environment_option_is_dev stub_available_environments([ "development", "test" ]) do - assert_match("development", parse_arguments([ "dev" ])[:environment]) + assert_match("development", parse_arguments([ "-e", "dev" ])[:environment]) end end def test_rails_env_is_dev_when_argument_is_dev_and_dev_env_is_present - stub_available_environments([ "dev" ]) do - assert_match("dev", parse_arguments([ "dev" ])[:environment]) + assert_deprecated do + stub_available_environments([ "dev" ]) do + assert_match("dev", parse_arguments([ "dev" ])[:environment]) + end end end @@ -194,12 +206,61 @@ class Rails::DBConsoleTest < ActiveSupport::TestCase assert_equal ["sqlplus", "user/secret@db"], dbconsole.find_cmd_and_exec_args end + def test_sqlserver + start(adapter: "sqlserver", database: "db", username: "user", password: "secret", host: "localhost", port: 1433) + assert_not aborted + assert_equal ["sqsh", "-D", "db", "-U", "user", "-P", "secret", "-S", "localhost:1433"], dbconsole.find_cmd_and_exec_args + end + def test_unknown_command_line_client start(adapter: "unknown", database: "db") assert aborted assert_match(/Unknown command-line client for db/, output) end + def test_primary_is_automatically_picked_with_3_level_configuration + sample_config = { + "test" => { + "primary" => { + "adapter" => "postgresql" + } + } + } + + app_db_config(sample_config) do + assert_equal "postgresql", Rails::DBConsole.new.config["adapter"] + end + end + + def test_specifying_a_custom_connection_and_environment + stub_available_environments(["development"]) do + dbconsole = parse_arguments(["-c", "custom", "-e", "development"]) + + assert_equal "development", dbconsole[:environment] + assert_equal "custom", dbconsole.connection + end + end + + def test_specifying_a_missing_connection + app_db_config({}) do + e = assert_raises(ActiveRecord::AdapterNotSpecified) do + Rails::Command.invoke(:dbconsole, ["-c", "i_do_not_exist"]) + end + + assert_includes e.message, "'i_do_not_exist' connection is not configured." + end + end + + def test_specifying_a_missing_environment + app_db_config({}) do + e = assert_raises(ActiveRecord::AdapterNotSpecified) do + Rails::Command.invoke(:dbconsole) + end + + assert_includes e.message, "'test' database is not configured." + end + end + def test_print_help_short stdout = capture(:stdout) do Rails::Command.invoke(:dbconsole, ["-h"]) diff --git a/railties/test/commands/encrypted_test.rb b/railties/test/commands/encrypted_test.rb new file mode 100644 index 0000000000..6647dcc902 --- /dev/null +++ b/railties/test/commands/encrypted_test.rb @@ -0,0 +1,94 @@ +# frozen_string_literal: true + +require "isolation/abstract_unit" +require "env_helpers" +require "rails/command" +require "rails/commands/encrypted/encrypted_command" + +class Rails::Command::EncryptedCommandTest < ActiveSupport::TestCase + include ActiveSupport::Testing::Isolation, EnvHelpers + + setup :build_app + teardown :teardown_app + + test "edit without editor gives hint" do + run_edit_command("config/tokens.yml.enc", editor: "").tap do |output| + assert_match "No $EDITOR to open file in", output + assert_match "bin/rails encrypted:edit", output + end + end + + test "edit encrypted file" do + # Run twice to ensure file can be reread after first edit pass. + 2.times do + assert_match(/access_key_id: 123/, run_edit_command("config/tokens.yml.enc")) + end + end + + test "edit command does not add master key to gitignore when already exist" do + run_edit_command("config/tokens.yml.enc") + + Dir.chdir(app_path) do + assert_match "/config/master.key", File.read(".gitignore") + end + end + + test "edit encrypts file with custom key" do + run_edit_command("config/tokens.yml.enc", key: "config/tokens.key") + + Dir.chdir(app_path) do + assert File.exist?("config/tokens.yml.enc") + assert File.exist?("config/tokens.key") + + assert_match "/config/tokens.key", File.read(".gitignore") + end + + assert_match(/access_key_id: 123/, run_edit_command("config/tokens.yml.enc", key: "config/tokens.key")) + end + + test "show encrypted file with custom key" do + run_edit_command("config/tokens.yml.enc", key: "config/tokens.key") + + assert_match(/access_key_id: 123/, run_show_command("config/tokens.yml.enc", key: "config/tokens.key")) + end + + test "show command raise error when require_master_key is specified and key does not exist" do + add_to_config "config.require_master_key = true" + + assert_match(/Missing encryption key to decrypt file with/, + run_show_command("config/tokens.yml.enc", key: "unexist.key", allow_failure: true)) + end + + test "show command does not raise error when require_master_key is false and master key does not exist" do + remove_file "config/master.key" + add_to_config "config.require_master_key = false" + + assert_match(/Missing 'config\/master\.key' to decrypt data/, run_show_command("config/tokens.yml.enc")) + end + + test "won't corrupt encrypted file when passed wrong key" do + run_edit_command("config/tokens.yml.enc", key: "config/tokens.key") + + assert_match "passed the wrong key", + run_edit_command("config/tokens.yml.enc", allow_failure: true) + + assert_match(/access_key_id: 123/, run_show_command("config/tokens.yml.enc", key: "config/tokens.key")) + end + + private + def run_edit_command(file, key: nil, editor: "cat", **options) + switch_env("EDITOR", editor) do + rails "encrypted:edit", prepare_args(file, key), **options + end + end + + def run_show_command(file, key: nil, **options) + rails "encrypted:show", prepare_args(file, key), **options + end + + def prepare_args(file, key) + args = [ file ] + args.push("--key", key) if key + args + end +end diff --git a/railties/test/commands/secrets_test.rb b/railties/test/commands/secrets_test.rb index 13fcf6c8a4..6b9f284a0c 100644 --- a/railties/test/commands/secrets_test.rb +++ b/railties/test/commands/secrets_test.rb @@ -1,24 +1,77 @@ +# frozen_string_literal: true + require "isolation/abstract_unit" +require "env_helpers" require "rails/command" require "rails/commands/secrets/secrets_command" class Rails::Command::SecretsCommandTest < ActiveSupport::TestCase - include ActiveSupport::Testing::Isolation + include ActiveSupport::Testing::Isolation, EnvHelpers + + setup :build_app + teardown :teardown_app - def setup - build_app + test "edit without editor gives hint" do + assert_match "No $EDITOR to open decrypted secrets in", run_edit_command(editor: "") end - def teardown - teardown_app + test "encrypted secrets are deprecated when using credentials" do + assert_match "Encrypted secrets is deprecated", run_setup_command + assert_equal 1, $?.exitstatus + assert_not File.exist?("config/secrets.yml.enc") end - test "edit without editor gives hint" do - assert_match "No $EDITOR to open decrypted secrets in", run_edit_command(editor: "") + test "encrypted secrets are deprecated when running edit without setup" do + assert_match "Encrypted secrets is deprecated", run_setup_command + assert_equal 1, $?.exitstatus + assert_not File.exist?("config/secrets.yml.enc") + end + + test "encrypted secrets are deprecated for 5.1 config/secrets.yml apps" do + Dir.chdir(app_path) do + FileUtils.rm("config/credentials.yml.enc") + FileUtils.touch("config/secrets.yml") + + assert_match "Encrypted secrets is deprecated", run_setup_command + assert_equal 1, $?.exitstatus + assert_not File.exist?("config/secrets.yml.enc") + end + end + + test "edit secrets" do + prevent_deprecation + + # Run twice to ensure encrypted secrets can be reread after first edit pass. + 2.times do + assert_match(/external_api_key: 1466aac22e6a869134be3d09b9e89232fc2c2289/, run_edit_command) + end + end + + test "show secrets" do + prevent_deprecation + + assert_match(/external_api_key: 1466aac22e6a869134be3d09b9e89232fc2c2289/, run_show_command) end private + def prevent_deprecation + Dir.chdir(app_path) do + File.write("config/secrets.yml.key", "f731758c639da2604dfb6bf3d1025de8") + File.write("config/secrets.yml.enc", "sEB0mHxDbeP1/KdnMk00wyzPFACl9K6t0cZWn5/Mfx/YbTHvnI07vrneqHg9kaH3wOS7L6pIQteu1P077OtE4BSx/ZRc/sgQPHyWu/tXsrfHqnPNpayOF/XZqizE91JacSFItNMWpuPsp9ynbzz+7cGhoB1S4aPNIU6u0doMrzdngDbijsaAFJmsHIQh6t/QHoJx--8aMoE0PvUWmw1Iqz--ldFqnM/K0g9k17M8PKoN/Q==") + end + end + def run_edit_command(editor: "cat") - Dir.chdir(app_path) { `EDITOR="#{editor}" bin/rails secrets:edit` } + switch_env("EDITOR", editor) do + rails "secrets:edit", allow_failure: true + end + end + + def run_show_command + rails "secrets:show", allow_failure: true + end + + def run_setup_command + rails "secrets:setup", allow_failure: true end end diff --git a/railties/test/commands/server_test.rb b/railties/test/commands/server_test.rb index d21a80982b..33715ea75f 100644 --- a/railties/test/commands/server_test.rb +++ b/railties/test/commands/server_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" require "env_helpers" require "rails/command" @@ -20,6 +22,18 @@ class Rails::ServerTest < ActiveSupport::TestCase assert_nil options[:server] end + def test_daemon_with_option + args = ["-d"] + options = parse_arguments(args) + assert_equal true, options[:daemonize] + end + + def test_daemon_without_option + args = [] + options = parse_arguments(args) + assert_equal false, options[:daemonize] + end + def test_server_option_without_environment args = ["thin"] with_rack_env nil do @@ -79,6 +93,18 @@ class Rails::ServerTest < ActiveSupport::TestCase assert_equal false, options[:caching] end + def test_early_hints_with_option + args = ["--early-hints"] + options = parse_arguments(args) + assert_equal true, options[:early_hints] + end + + def test_early_hints_is_nil_by_default + args = [] + options = parse_arguments(args) + assert_nil options[:early_hints] + end + def test_log_stdout with_rack_env nil do with_rails_env nil do @@ -139,12 +165,38 @@ class Rails::ServerTest < ActiveSupport::TestCase end end + def test_argument_precedence_over_environment_variable + switch_env "PORT", "1234" do + args = ["-p", "5678"] + options = parse_arguments(args) + assert_equal 5678, options[:Port] + end + + switch_env "PORT", "1234" do + args = ["-p", "3000"] + options = parse_arguments(args) + assert_equal 3000, options[:Port] + end + + switch_env "HOST", "1.2.3.4" do + args = ["-b", "127.0.0.1"] + options = parse_arguments(args) + assert_equal "127.0.0.1", options[:Host] + end + end + def test_records_user_supplied_options server_options = parse_arguments(["-p", 3001]) assert_equal [:Port], server_options[:user_supplied_options] server_options = parse_arguments(["--port", 3001]) assert_equal [:Port], server_options[:user_supplied_options] + + server_options = parse_arguments(["-p3001", "-C", "--binding", "127.0.0.1"]) + assert_equal [:Port, :Host, :caching], server_options[:user_supplied_options] + + server_options = parse_arguments(["--port=3001"]) + assert_equal [:Port], server_options[:user_supplied_options] end def test_default_options @@ -162,7 +214,7 @@ class Rails::ServerTest < ActiveSupport::TestCase ARGV.replace args options = parse_arguments(args) - expected = "bin/rails server -p 4567 -b 127.0.0.1 -c dummy_config.ru -d -e test -P tmp/server.pid -C" + expected = "bin/rails server -p 4567 -b 127.0.0.1 -c dummy_config.ru -d -e test -P tmp/server.pid -C --restart" assert_equal expected, options[:restart_cmd] ensure diff --git a/railties/test/configuration/middleware_stack_proxy_test.rb b/railties/test/configuration/middleware_stack_proxy_test.rb index 559ce72693..bc72b7f0c9 100644 --- a/railties/test/configuration/middleware_stack_proxy_test.rb +++ b/railties/test/configuration/middleware_stack_proxy_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "active_support" require "active_support/testing/autorun" require "rails/configuration" diff --git a/railties/test/console_helpers.rb b/railties/test/console_helpers.rb new file mode 100644 index 0000000000..8350fce5ee --- /dev/null +++ b/railties/test/console_helpers.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +begin + require "pty" +rescue LoadError +end + +module ConsoleHelpers + def assert_output(expected, io, timeout = 10) + timeout = Time.now + timeout + + output = "".dup + until output.include?(expected) || Time.now > timeout + if IO.select([io], [], [], 0.1) + output << io.read(1) + end + end + + assert_includes output, expected, "#{expected.inspect} expected, but got:\n\n#{output}" + end + + def available_pty? + defined?(PTY) && PTY.respond_to?(:open) + end +end diff --git a/railties/test/engine/commands_test.rb b/railties/test/engine/commands_test.rb index b1c937143f..aeb64d445b 100644 --- a/railties/test/engine/commands_test.rb +++ b/railties/test/engine/commands_test.rb @@ -1,10 +1,11 @@ +# frozen_string_literal: true + require "abstract_unit" -begin - require "pty" -rescue LoadError -end +require "console_helpers" class Rails::Engine::CommandsTest < ActiveSupport::TestCase + include ConsoleHelpers + def setup @destination_root = Dir.mktmpdir("bukkits") Dir.chdir(@destination_root) { `bundle exec rails plugin new bukkits --mountable` } @@ -64,19 +65,6 @@ class Rails::Engine::CommandsTest < ActiveSupport::TestCase "#{@destination_root}/bukkits" end - def assert_output(expected, io, timeout = 10) - timeout = Time.now + timeout - - output = "" - until output.include?(expected) || Time.now > timeout - if IO.select([io], [], [], 0.1) - output << io.read(1) - end - end - - assert_includes output, expected, "#{expected.inspect} expected, but got:\n\n#{output}" - end - def spawn_command(command, fd) Process.spawn( "#{plugin_path}/bin/rails #{command}", @@ -84,10 +72,6 @@ class Rails::Engine::CommandsTest < ActiveSupport::TestCase ) end - def available_pty? - defined?(PTY) && PTY.respond_to?(:open) - end - def kill(pid) Process.kill("TERM", pid) Process.wait(pid) diff --git a/railties/test/engine/test_test.rb b/railties/test/engine/test_test.rb new file mode 100644 index 0000000000..18af85a0aa --- /dev/null +++ b/railties/test/engine/test_test.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +require "abstract_unit" + +class Rails::Engine::TestTest < ActiveSupport::TestCase + setup do + @destination_root = Dir.mktmpdir("bukkits") + Dir.chdir(@destination_root) { `bundle exec rails plugin new bukkits --mountable` } + end + + teardown do + FileUtils.rm_rf(@destination_root) + end + + test "automatically synchronize test schema" do + Dir.chdir(plugin_path) do + # In order to confirm that migration files are loaded, generate multiple migration files. + `bin/rails generate model user name:string; + bin/rails generate model todo name:string; + RAILS_ENV=development bin/rails db:migrate` + + output = `bin/rails test test/models/bukkits/user_test.rb` + assert_includes(output, "0 runs, 0 assertions, 0 failures, 0 errors, 0 skips") + end + end + + private + def plugin_path + "#{@destination_root}/bukkits" + end +end diff --git a/railties/test/engine_test.rb b/railties/test/engine_test.rb index 248afa2d2c..4bd8a07085 100644 --- a/railties/test/engine_test.rb +++ b/railties/test/engine_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" class EngineTest < ActiveSupport::TestCase diff --git a/railties/test/env_helpers.rb b/railties/test/env_helpers.rb index 1f64d5fda3..336832b867 100644 --- a/railties/test/env_helpers.rb +++ b/railties/test/env_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "rails" module EnvHelpers diff --git a/railties/test/fixtures/about_yml_plugins/bad_about_yml/about.yml b/railties/test/fixtures/about_yml_plugins/bad_about_yml/about.yml deleted file mode 100644 index fe80872a16..0000000000 --- a/railties/test/fixtures/about_yml_plugins/bad_about_yml/about.yml +++ /dev/null @@ -1 +0,0 @@ -# an empty YAML file - any content in here seems to get parsed as a string
\ No newline at end of file diff --git a/railties/test/fixtures/about_yml_plugins/bad_about_yml/init.rb b/railties/test/fixtures/about_yml_plugins/bad_about_yml/init.rb deleted file mode 100644 index 636bc1a8ab..0000000000 --- a/railties/test/fixtures/about_yml_plugins/bad_about_yml/init.rb +++ /dev/null @@ -1 +0,0 @@ -# intentionally empty diff --git a/railties/test/fixtures/about_yml_plugins/plugin_without_about_yml/init.rb b/railties/test/fixtures/about_yml_plugins/plugin_without_about_yml/init.rb deleted file mode 100644 index 636bc1a8ab..0000000000 --- a/railties/test/fixtures/about_yml_plugins/plugin_without_about_yml/init.rb +++ /dev/null @@ -1 +0,0 @@ -# intentionally empty diff --git a/railties/test/fixtures/lib/create_test_dummy_template.rb b/railties/test/fixtures/lib/create_test_dummy_template.rb index e4378bbd1a..b9eb6a912d 100644 --- a/railties/test/fixtures/lib/create_test_dummy_template.rb +++ b/railties/test/fixtures/lib/create_test_dummy_template.rb @@ -1 +1,3 @@ +# frozen_string_literal: true + create_dummy_app("spec/dummy") diff --git a/railties/test/fixtures/lib/generators/active_record/fixjour_generator.rb b/railties/test/fixtures/lib/generators/active_record/fixjour_generator.rb index 1139350b94..f196971f20 100644 --- a/railties/test/fixtures/lib/generators/active_record/fixjour_generator.rb +++ b/railties/test/fixtures/lib/generators/active_record/fixjour_generator.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "rails/generators/active_record" module ActiveRecord diff --git a/railties/test/fixtures/lib/generators/fixjour_generator.rb b/railties/test/fixtures/lib/generators/fixjour_generator.rb index ef3e9edbed..22197835a8 100644 --- a/railties/test/fixtures/lib/generators/fixjour_generator.rb +++ b/railties/test/fixtures/lib/generators/fixjour_generator.rb @@ -1,2 +1,4 @@ +# frozen_string_literal: true + class FixjourGenerator < Rails::Generators::NamedBase end diff --git a/railties/test/fixtures/lib/generators/model_generator.rb b/railties/test/fixtures/lib/generators/model_generator.rb index 0905fa9631..3009472c3d 100644 --- a/railties/test/fixtures/lib/generators/model_generator.rb +++ b/railties/test/fixtures/lib/generators/model_generator.rb @@ -1 +1,3 @@ +# frozen_string_literal: true + raise "I should never be loaded" diff --git a/railties/test/fixtures/lib/generators/usage_template/usage_template_generator.rb b/railties/test/fixtures/lib/generators/usage_template/usage_template_generator.rb index 21b0ff6c28..5a847a8bd2 100644 --- a/railties/test/fixtures/lib/generators/usage_template/usage_template_generator.rb +++ b/railties/test/fixtures/lib/generators/usage_template/usage_template_generator.rb @@ -1,5 +1,7 @@ +# frozen_string_literal: true + require "rails/generators" class UsageTemplateGenerator < Rails::Generators::Base - source_root File.expand_path("templates", File.dirname(__FILE__)) + source_root File.expand_path("templates", __dir__) end diff --git a/railties/test/fixtures/lib/rails/generators/foobar/foobar_generator.rb b/railties/test/fixtures/lib/rails/generators/foobar/foobar_generator.rb index d1de8c56fa..159843866c 100644 --- a/railties/test/fixtures/lib/rails/generators/foobar/foobar_generator.rb +++ b/railties/test/fixtures/lib/rails/generators/foobar/foobar_generator.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Foobar class FoobarGenerator < Rails::Generators::Base end diff --git a/railties/test/fixtures/lib/template.rb b/railties/test/fixtures/lib/template.rb index c14a1a8784..44083c25e8 100644 --- a/railties/test/fixtures/lib/template.rb +++ b/railties/test/fixtures/lib/template.rb @@ -1 +1,3 @@ +# frozen_string_literal: true + say "It works from file!" diff --git a/railties/test/generators/actions_test.rb b/railties/test/generators/actions_test.rb index 360e8e97d7..f421207025 100644 --- a/railties/test/generators/actions_test.rb +++ b/railties/test/generators/actions_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "generators/generators_test_helper" require "rails/generators/rails/app/app_generator" require "env_helpers" @@ -69,10 +71,17 @@ class ActionsTest < Rails::Generators::TestCase def test_gem_with_version_should_include_version_in_gemfile run_generator - - action :gem, "rspec", ">=2.0.0.a5" - - assert_file "Gemfile", /gem 'rspec', '>=2.0.0.a5'/ + action :gem, "rspec", ">= 2.0.0.a5" + action :gem, "RedCloth", ">= 4.1.0", "< 4.2.0" + action :gem, "nokogiri", version: ">= 1.4.2" + action :gem, "faker", version: [">= 0.1.0", "< 0.3.0"] + + assert_file "Gemfile" do |content| + assert_match(/gem 'rspec', '>= 2\.0\.0\.a5'/, content) + assert_match(/gem 'RedCloth', '>= 4\.1\.0', '< 4\.2\.0'/, content) + assert_match(/gem 'nokogiri', '>= 1\.4\.2'/, content) + assert_match(/gem 'faker', '>= 0\.1\.0', '< 0\.3\.0'/, content) + end end def test_gem_should_insert_on_separate_lines @@ -139,14 +148,14 @@ class ActionsTest < Rails::Generators::TestCase run_generator autoload_paths = 'config.autoload_paths += %w["#{Rails.root}/app/extras"]' action :environment, autoload_paths - assert_file "config/application.rb", / class Application < Rails::Application\n #{Regexp.escape(autoload_paths)}/ + assert_file "config/application.rb", / class Application < Rails::Application\n #{Regexp.escape(autoload_paths)}\n/ end def test_environment_should_include_data_in_environment_initializer_block_with_env_option run_generator autoload_paths = 'config.autoload_paths += %w["#{Rails.root}/app/extras"]' action :environment, autoload_paths, env: "development" - assert_file "config/environments/development.rb", /Rails\.application\.configure do\n #{Regexp.escape(autoload_paths)}/ + assert_file "config/environments/development.rb", /Rails\.application\.configure do\n #{Regexp.escape(autoload_paths)}\n/ end def test_environment_with_block_should_include_block_contents_in_environment_initializer_block @@ -163,6 +172,26 @@ class ActionsTest < Rails::Generators::TestCase end end + def test_environment_with_block_should_include_block_contents_with_multiline_data_in_environment_initializer_block + run_generator + data = <<-RUBY + config.encoding = "utf-8" + config.time_zone = "UTC" + RUBY + action(:environment) { data } + assert_file "config/application.rb", / class Application < Rails::Application\n#{Regexp.escape(data.strip_heredoc.indent(4))}/ + end + + def test_environment_should_include_block_contents_with_multiline_data_in_environment_initializer_block_with_env_option + run_generator + data = <<-RUBY + config.encoding = "utf-8" + config.time_zone = "UTC" + RUBY + action(:environment, nil, env: "development") { data } + assert_file "config/environments/development.rb", /Rails\.application\.configure do\n#{Regexp.escape(data.strip_heredoc.indent(2))}/ + end + def test_git_with_symbol_should_run_command_using_git_scm assert_called_with(generator, :run, ["git init"]) do action :git, :init @@ -177,22 +206,62 @@ class ActionsTest < Rails::Generators::TestCase def test_vendor_should_write_data_to_file_in_vendor action :vendor, "vendor_file.rb", "# vendor data" - assert_file "vendor/vendor_file.rb", "# vendor data" + assert_file "vendor/vendor_file.rb", "# vendor data\n" + end + + def test_vendor_should_write_data_to_file_with_block_in_vendor + code = <<-RUBY + puts "one" + puts "two" + puts "three" + RUBY + action(:vendor, "vendor_file.rb") { code } + assert_file "vendor/vendor_file.rb", code.strip_heredoc end def test_lib_should_write_data_to_file_in_lib action :lib, "my_library.rb", "class MyLibrary" - assert_file "lib/my_library.rb", "class MyLibrary" + assert_file "lib/my_library.rb", "class MyLibrary\n" + end + + def test_lib_should_write_data_to_file_with_block_in_lib + code = <<-RUBY + class MyLib + MY_CONSTANT = 123 + end + RUBY + action(:lib, "my_library.rb") { code } + assert_file "lib/my_library.rb", code.strip_heredoc end def test_rakefile_should_write_date_to_file_in_lib_tasks action :rakefile, "myapp.rake", "task run: [:environment]" - assert_file "lib/tasks/myapp.rake", "task run: [:environment]" + assert_file "lib/tasks/myapp.rake", "task run: [:environment]\n" + end + + def test_rakefile_should_write_date_to_file_with_block_in_lib_tasks + code = <<-RUBY + task rock: :environment do + puts "Rockin'" + end + RUBY + action(:rakefile, "myapp.rake") { code } + assert_file "lib/tasks/myapp.rake", code.strip_heredoc end def test_initializer_should_write_date_to_file_in_config_initializers action :initializer, "constants.rb", "MY_CONSTANT = 42" - assert_file "config/initializers/constants.rb", "MY_CONSTANT = 42" + assert_file "config/initializers/constants.rb", "MY_CONSTANT = 42\n" + end + + def test_initializer_should_write_date_to_file_with_block_in_config_initializers + code = <<-RUBY + MyLib.configure do |config| + config.value = 123 + end + RUBY + action(:initializer, "constants.rb") { code } + assert_file "config/initializers/constants.rb", code.strip_heredoc end def test_generate_should_run_script_generate_with_argument_and_options @@ -239,6 +308,14 @@ class ActionsTest < Rails::Generators::TestCase end end + test "rake command with capture option should run rake command with capture" do + assert_called_with(generator, :run, ["rake log:clear RAILS_ENV=development", verbose: false, capture: true]) do + with_rails_env nil do + action :rake, "log:clear", capture: true + end + end + end + test "rails command should run rails_command with default env" do assert_called_with(generator, :run, ["rails log:clear RAILS_ENV=development", verbose: false]) do with_rails_env nil do @@ -277,10 +354,21 @@ class ActionsTest < Rails::Generators::TestCase end end + test "rails command with capture option should run rails_command with capture" do + assert_called_with(generator, :run, ["rails log:clear RAILS_ENV=development", verbose: false, capture: true]) do + with_rails_env nil do + action :rails_command, "log:clear", capture: true + end + end + end + def test_capify_should_run_the_capify_command - assert_called_with(generator, :run, ["capify .", verbose: false]) do - action :capify! + content = capture(:stderr) do + assert_called_with(generator, :run, ["capify .", verbose: false]) do + action :capify! + end end + assert_match(/DEPRECATION WARNING: `capify!` is deprecated/, content) end def test_route_should_add_data_to_the_routes_block_in_config_routes diff --git a/railties/test/generators/api_app_generator_test.rb b/railties/test/generators/api_app_generator_test.rb index bdef1798f8..4815cf6362 100644 --- a/railties/test/generators/api_app_generator_test.rb +++ b/railties/test/generators/api_app_generator_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "generators/generators_test_helper" require "rails/generators/rails/app/app_generator" @@ -33,26 +35,26 @@ class ApiAppGeneratorTest < Rails::Generators::TestCase def test_api_modified_files run_generator + assert_file ".gitignore" do |content| + assert_no_match(/\/public\/assets/, content) + end + assert_file "Gemfile" do |content| assert_no_match(/gem 'coffee-rails'/, content) assert_no_match(/gem 'sass-rails'/, content) assert_no_match(/gem 'web-console'/, content) + assert_no_match(/gem 'capybara'/, content) + assert_no_match(/gem 'selenium-webdriver'/, content) assert_match(/# gem 'jbuilder'/, content) + assert_match(/# gem 'rack-cors'/, content) end - assert_file "config/application.rb" do |content| - assert_match(/config.api_only = true/, content) - end - - assert_file "config/initializers/cors.rb" - - assert_file "config/initializers/wrap_parameters.rb" - + assert_file "config/application.rb", /config\.api_only = true/ assert_file "app/controllers/application_controller.rb", /ActionController::API/ end def test_generator_if_skip_action_cable_is_given - run_generator [destination_root, "--skip-action-cable"] + run_generator [destination_root, "--api", "--skip-action-cable"] assert_file "config/application.rb", /#\s+require\s+["']action_cable\/engine["']/ assert_no_file "config/cable.yml" assert_no_file "app/channels" @@ -61,32 +63,74 @@ class ApiAppGeneratorTest < Rails::Generators::TestCase end end - def test_generator_skips_per_form_csrf_token_and_origin_check_configs_for_api_apps + def test_app_update_does_not_generate_unnecessary_config_files run_generator - assert_file "config/initializers/new_framework_defaults.rb" do |initializer_content| - assert_no_match(/per_form_csrf_tokens/, initializer_content) - assert_no_match(/forgery_protection_origin_check/, initializer_content) - end + generator = Rails::Generators::AppGenerator.new ["rails"], + { api: true, update: true }, { destination_root: destination_root, shell: @shell } + quietly { generator.send(:update_config_files) } + + assert_no_file "config/initializers/cookies_serializer.rb" + assert_no_file "config/initializers/assets.rb" + assert_no_file "config/initializers/content_security_policy.rb" + end + + def test_app_update_does_not_generate_unnecessary_bin_files + run_generator + + generator = Rails::Generators::AppGenerator.new ["rails"], + { api: true, update: true }, { destination_root: destination_root, shell: @shell } + quietly { generator.send(:update_bin_files) } + + assert_no_file "bin/yarn" end private def default_files - files = %W( - .gitignore + %w(.gitignore + .ruby-version + README.md Gemfile Rakefile config.ru + app/channels app/controllers app/mailers app/models + app/views/layouts app/views/layouts/mailer.html.erb app/views/layouts/mailer.text.erb + bin/bundle + bin/rails + bin/rake + bin/setup + bin/update + config/application.rb + config/boot.rb + config/cable.yml + config/environment.rb config/environments + config/environments/development.rb + config/environments/production.rb + config/environments/test.rb config/initializers + config/initializers/application_controller_renderer.rb + config/initializers/backtrace_silencers.rb + config/initializers/cors.rb + config/initializers/filter_parameter_logging.rb + config/initializers/inflections.rb + config/initializers/mime_types.rb + config/initializers/wrap_parameters.rb config/locales + config/locales/en.yml + config/puma.rb + config/routes.rb + config/credentials.yml.enc + config/spring.rb + config/storage.yml db + db/seeds.rb lib lib/tasks log @@ -97,8 +141,6 @@ class ApiAppGeneratorTest < Rails::Generators::TestCase tmp vendor ) - files.concat %w(bin/bundle bin/rails bin/rake) - files end def skipped_files @@ -108,6 +150,7 @@ class ApiAppGeneratorTest < Rails::Generators::TestCase bin/yarn config/initializers/assets.rb config/initializers/cookies_serializer.rb + config/initializers/content_security_policy.rb lib/assets test/helpers tmp/cache/assets @@ -116,7 +159,7 @@ class ApiAppGeneratorTest < Rails::Generators::TestCase public/500.html public/apple-touch-icon-precomposed.png public/apple-touch-icon.png - public/favicon.icon + public/favicon.ico package.json ) end diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb index 986afb6d2a..60b9ff1317 100644 --- a/railties/test/generators/app_generator_test.rb +++ b/railties/test/generators/app_generator_test.rb @@ -1,39 +1,83 @@ +# frozen_string_literal: true + require "generators/generators_test_helper" require "rails/generators/rails/app/app_generator" require "generators/shared_generator_tests" DEFAULT_APP_FILES = %w( .gitignore + .ruby-version README.md Gemfile Rakefile config.ru + app/assets/config/manifest.js + app/assets/images app/assets/javascripts + app/assets/javascripts/application.js + app/assets/javascripts/cable.js + app/assets/javascripts/channels app/assets/stylesheets - app/assets/images + app/assets/stylesheets/application.css + app/channels/application_cable/channel.rb + app/channels/application_cable/connection.rb app/controllers + app/controllers/application_controller.rb app/controllers/concerns app/helpers + app/helpers/application_helper.rb app/mailers + app/mailers/application_mailer.rb app/models + app/models/application_record.rb app/models/concerns app/jobs + app/jobs/application_job.rb app/views/layouts + app/views/layouts/application.html.erb + app/views/layouts/mailer.html.erb + app/views/layouts/mailer.text.erb bin/bundle bin/rails bin/rake bin/setup + bin/update + bin/yarn + config/application.rb + config/boot.rb + config/cable.yml + config/environment.rb config/environments + config/environments/development.rb + config/environments/production.rb + config/environments/test.rb config/initializers + config/initializers/application_controller_renderer.rb + config/initializers/assets.rb + config/initializers/backtrace_silencers.rb + config/initializers/cookies_serializer.rb + config/initializers/content_security_policy.rb + config/initializers/filter_parameter_logging.rb + config/initializers/inflections.rb + config/initializers/mime_types.rb + config/initializers/wrap_parameters.rb config/locales - config/cable.yml + config/locales/en.yml config/puma.rb + config/routes.rb + config/credentials.yml.enc config/spring.rb + config/storage.yml db + db/seeds.rb lib lib/tasks lib/assets log + package.json + public + storage + test/application_system_test_case.rb test/test_helper.rb test/fixtures test/fixtures/files @@ -47,6 +91,7 @@ DEFAULT_APP_FILES = %w( tmp tmp/cache tmp/cache/assets + tmp/storage ) class AppGeneratorTest < Rails::Generators::TestCase @@ -60,6 +105,15 @@ class AppGeneratorTest < Rails::Generators::TestCase ::DEFAULT_APP_FILES end + def test_skip_bundle + assert_not_called(generator([destination_root], skip_bundle: true), :bundle_command) do + quietly { generator.invoke_all } + # skip_bundle is only about running bundle install, ensure the Gemfile is still + # generated. + assert_file "Gemfile" + end + end + def test_assets run_generator @@ -133,7 +187,7 @@ class AppGeneratorTest < Rails::Generators::TestCase end end - def test_rails_update_generates_correct_session_key + def test_app_update_generates_correct_session_key app_root = File.join(destination_root, "myapp") run_generator [app_root] @@ -156,66 +210,65 @@ class AppGeneratorTest < Rails::Generators::TestCase assert_no_file "config/initializers/cors.rb" end - def test_rails_update_keep_the_cookie_serializer_if_it_is_already_configured - app_root = File.join(destination_root, "myapp") + def test_new_application_doesnt_need_defaults + assert_no_file "config/initializers/new_framework_defaults_5_2.rb" + end + + def test_new_application_load_defaults + app_root = File.join(destination_root, "myfirstapp") run_generator [app_root] + output = nil - stub_rails_application(app_root) do - generator = Rails::Generators::AppGenerator.new ["rails"], [], destination_root: app_root, shell: @shell - generator.send(:app_const) - quietly { generator.send(:update_config_files) } - assert_file("#{app_root}/config/initializers/cookies_serializer.rb", /Rails\.application\.config\.action_dispatch\.cookies_serializer = :json/) + Dir.chdir(app_root) do + output = `./bin/rails r "puts Rails.application.config.assets.unknown_asset_fallback"` end + + assert_equal "false\n", output end - def test_rails_update_set_the_cookie_serializer_to_marshal_if_it_is_not_already_configured + def test_app_update_keep_the_cookie_serializer_if_it_is_already_configured app_root = File.join(destination_root, "myapp") run_generator [app_root] - FileUtils.rm("#{app_root}/config/initializers/cookies_serializer.rb") - stub_rails_application(app_root) do generator = Rails::Generators::AppGenerator.new ["rails"], [], destination_root: app_root, shell: @shell generator.send(:app_const) quietly { generator.send(:update_config_files) } - assert_file("#{app_root}/config/initializers/cookies_serializer.rb", - /Valid options are :json, :marshal, and :hybrid\.\nRails\.application\.config\.action_dispatch\.cookies_serializer = :marshal/) + assert_file("#{app_root}/config/initializers/cookies_serializer.rb", /Rails\.application\.config\.action_dispatch\.cookies_serializer = :json/) end end - def test_rails_update_dont_set_file_watcher + def test_app_update_set_the_cookie_serializer_to_marshal_if_it_is_not_already_configured app_root = File.join(destination_root, "myapp") run_generator [app_root] + FileUtils.rm("#{app_root}/config/initializers/cookies_serializer.rb") + stub_rails_application(app_root) do generator = Rails::Generators::AppGenerator.new ["rails"], [], destination_root: app_root, shell: @shell generator.send(:app_const) quietly { generator.send(:update_config_files) } - assert_file "#{app_root}/config/environments/development.rb" do |content| - assert_match(/# config.file_watcher/, content) - end + assert_file("#{app_root}/config/initializers/cookies_serializer.rb", + /Valid options are :json, :marshal, and :hybrid\.\nRails\.application\.config\.action_dispatch\.cookies_serializer = :marshal/) end end - def test_rails_update_does_not_create_new_framework_defaults_by_default + def test_app_update_create_new_framework_defaults app_root = File.join(destination_root, "myapp") run_generator [app_root] - FileUtils.rm("#{app_root}/config/initializers/new_framework_defaults.rb") + assert_no_file "#{app_root}/config/initializers/new_framework_defaults_5_2.rb" stub_rails_application(app_root) do - generator = Rails::Generators::AppGenerator.new ["rails"], { update: true }, destination_root: app_root, shell: @shell + generator = Rails::Generators::AppGenerator.new ["rails"], { update: true }, { destination_root: app_root, shell: @shell } generator.send(:app_const) quietly { generator.send(:update_config_files) } - assert_file "#{app_root}/config/initializers/new_framework_defaults.rb" do |content| - assert_match(/Rails\.application\.config.active_record\.belongs_to_required_by_default = false/, content) - assert_no_match(/Rails\.application\.config\.ssl_options/, content) - end + assert_file "#{app_root}/config/initializers/new_framework_defaults_5_2.rb" end end - def test_rails_update_does_not_create_rack_cors + def test_app_update_does_not_create_rack_cors app_root = File.join(destination_root, "myapp") run_generator [app_root] @@ -227,7 +280,7 @@ class AppGeneratorTest < Rails::Generators::TestCase end end - def test_rails_update_does_not_remove_rack_cors_if_already_present + def test_app_update_does_not_remove_rack_cors_if_already_present app_root = File.join(destination_root, "myapp") run_generator [app_root] @@ -241,6 +294,79 @@ class AppGeneratorTest < Rails::Generators::TestCase end end + def test_app_update_does_not_generate_action_cable_contents_when_skip_action_cable_is_given + app_root = File.join(destination_root, "myapp") + run_generator [app_root, "--skip-action-cable"] + + FileUtils.cd(app_root) do + quietly { system("bin/rails app:update") } + end + + assert_no_file "#{app_root}/config/cable.yml" + assert_file "#{app_root}/config/environments/production.rb" do |content| + assert_no_match(/config\.action_cable/, content) + end + end + + def test_active_storage_mini_magick_gem + run_generator + assert_file "Gemfile", /^# gem 'mini_magick'/ + end + + def test_app_update_does_not_generate_active_storage_contents_when_skip_active_storage_is_given + app_root = File.join(destination_root, "myapp") + run_generator [app_root, "--skip-active-storage"] + + FileUtils.cd(app_root) do + quietly { system("bin/rails app:update") } + end + + assert_file "#{app_root}/config/environments/development.rb" do |content| + assert_no_match(/config\.active_storage/, content) + end + + assert_file "#{app_root}/config/environments/production.rb" do |content| + assert_no_match(/config\.active_storage/, content) + end + + assert_file "#{app_root}/config/environments/test.rb" do |content| + assert_no_match(/config\.active_storage/, content) + end + + assert_no_file "#{app_root}/config/storage.yml" + + assert_file "#{app_root}/Gemfile" do |content| + assert_no_match(/gem 'mini_magick'/, content) + end + end + + def test_app_update_does_not_generate_active_storage_contents_when_skip_active_record_is_given + app_root = File.join(destination_root, "myapp") + run_generator [app_root, "--skip-active-record"] + + FileUtils.cd(app_root) do + quietly { system("bin/rails app:update") } + end + + assert_file "#{app_root}/config/environments/development.rb" do |content| + assert_no_match(/config\.active_storage/, content) + end + + assert_file "#{app_root}/config/environments/production.rb" do |content| + assert_no_match(/config\.active_storage/, content) + end + + assert_file "#{app_root}/config/environments/test.rb" do |content| + assert_no_match(/config\.active_storage/, content) + end + + assert_no_file "#{app_root}/config/storage.yml" + + assert_file "#{app_root}/Gemfile" do |content| + assert_no_match(/gem 'mini_magick'/, content) + end + end + def test_application_names_are_not_singularized run_generator [File.join(destination_root, "hats")] assert_file "hats/config/environment.rb", /Rails\.application\.initialize!/ @@ -272,7 +398,7 @@ class AppGeneratorTest < Rails::Generators::TestCase if defined?(JRUBY_VERSION) assert_gem "activerecord-jdbcmysql-adapter" else - assert_gem "mysql2", "'>= 0.3.18', '< 0.5'" + assert_gem "mysql2", "'~> 0.4.4'" end end @@ -287,7 +413,7 @@ class AppGeneratorTest < Rails::Generators::TestCase if defined?(JRUBY_VERSION) assert_gem "activerecord-jdbcpostgresql-adapter" else - assert_gem "pg", "'~> 0.18'" + assert_gem "pg", "'>= 0.18', '< 2.0'" end end @@ -324,24 +450,9 @@ class AppGeneratorTest < Rails::Generators::TestCase end end - def test_generator_without_skips - run_generator - assert_file "config/application.rb", /\s+require\s+["']rails\/all["']/ - assert_file "config/environments/development.rb" do |content| - assert_match(/config\.action_mailer\.raise_delivery_errors = false/, content) - end - assert_file "config/environments/test.rb" do |content| - assert_match(/config\.action_mailer\.delivery_method = :test/, content) - end - assert_file "config/environments/production.rb" do |content| - assert_match(/# config\.action_mailer\.raise_delivery_errors = false/, content) - assert_match(/^ config\.read_encrypted_secrets = true/, content) - end - end - def test_generator_defaults_to_puma_version run_generator [destination_root] - assert_gem "puma", "'~> 3.7'" + assert_gem "puma", "'~> 3.11'" end def test_generator_if_skip_puma_is_given @@ -352,43 +463,6 @@ class AppGeneratorTest < Rails::Generators::TestCase end end - def test_generator_if_skip_active_record_is_given - run_generator [destination_root, "--skip-active-record"] - assert_no_directory "db/" - assert_no_file "config/database.yml" - assert_no_file "app/models/application_record.rb" - assert_file "config/application.rb", /#\s+require\s+["']active_record\/railtie["']/ - assert_file "test/test_helper.rb" do |helper_content| - assert_no_match(/fixtures :all/, helper_content) - end - assert_file "bin/setup" do |setup_content| - assert_no_match(/db:setup/, setup_content) - end - assert_file "bin/update" do |update_content| - assert_no_match(/db:migrate/, update_content) - end - - assert_file "config/initializers/new_framework_defaults.rb" do |initializer_content| - assert_no_match(/belongs_to_required_by_default/, initializer_content) - end - end - - def test_generator_if_skip_action_mailer_is_given - run_generator [destination_root, "--skip-action-mailer"] - assert_file "config/application.rb", /#\s+require\s+["']action_mailer\/railtie["']/ - assert_file "config/environments/development.rb" do |content| - assert_no_match(/config\.action_mailer/, content) - end - assert_file "config/environments/test.rb" do |content| - assert_no_match(/config\.action_mailer/, content) - end - assert_file "config/environments/production.rb" do |content| - assert_no_match(/config\.action_mailer/, content) - end - assert_no_directory "app/mailers" - assert_no_directory "test/mailers" - end - def test_generator_has_assets_gems run_generator @@ -396,59 +470,57 @@ class AppGeneratorTest < Rails::Generators::TestCase assert_gem "uglifier" end - def test_generator_if_skip_sprockets_is_given - run_generator [destination_root, "--skip-sprockets"] - assert_no_file "config/initializers/assets.rb" - assert_file "config/application.rb" do |content| - assert_match(/#\s+require\s+["']sprockets\/railtie["']/, content) - end - assert_file "Gemfile" do |content| - assert_no_match(/sass-rails/, content) - assert_no_match(/uglifier/, content) - assert_no_match(/coffee-rails/, content) - end - assert_file "config/environments/development.rb" do |content| - assert_no_match(/config\.assets\.debug = true/, content) - end - assert_file "config/environments/production.rb" do |content| - assert_no_match(/config\.assets\.digest = true/, content) - assert_no_match(/config\.assets\.js_compressor = :uglifier/, content) - assert_no_match(/config\.assets\.css_compressor = :sass/, content) - end - assert_file "config/initializers/new_framework_defaults.rb" do |content| - assert_no_match(/unknown_asset_fallback/, content) - end + def test_action_cable_redis_gems + run_generator + assert_file "Gemfile", /^# gem 'redis'/ end - def test_generator_if_skip_yarn_is_given - run_generator [destination_root, "--skip-yarn"] + def test_generator_if_skip_test_is_given + run_generator [destination_root, "--skip-test"] + + assert_file "config/application.rb", /#\s+require\s+["']rails\/test_unit\/railtie["']/ - assert_no_file "package.json" - assert_no_file "bin/yarn" + assert_file "Gemfile" do |content| + assert_no_match(/capybara/, content) + assert_no_match(/selenium-webdriver/, content) + assert_no_match(/chromedriver-helper/, content) + end + + assert_no_directory("test") end - def test_generator_if_skip_action_cable_is_given - run_generator [destination_root, "--skip-action-cable"] - assert_file "config/application.rb", /#\s+require\s+["']action_cable\/engine["']/ - assert_no_file "config/cable.yml" - assert_no_file "app/assets/javascripts/cable.js" - assert_no_file "app/channels" + def test_generator_if_skip_system_test_is_given + run_generator [destination_root, "--skip-system-test"] assert_file "Gemfile" do |content| - assert_no_match(/redis/, content) + assert_no_match(/capybara/, content) + assert_no_match(/selenium-webdriver/, content) + assert_no_match(/chromedriver-helper/, content) end + + assert_directory("test") + + assert_no_directory("test/system") end - def test_action_cable_redis_gems - run_generator - assert_file "Gemfile", /^# gem 'redis'/ + def test_does_not_generate_system_test_files_if_skip_system_test_is_given + run_generator [destination_root, "--skip-system-test"] + + Dir.chdir(destination_root) do + quietly { `./bin/rails g scaffold User` } + + assert_no_file("test/application_system_test_case.rb") + assert_no_file("test/system/users_test.rb") + end end def test_inclusion_of_javascript_runtime run_generator if defined?(JRUBY_VERSION) assert_gem "therubyrhino" + elsif RUBY_PLATFORM =~ /mingw|mswin/ + assert_gem "duktape" else - assert_file "Gemfile", /# gem 'therubyracer', platforms: :ruby/ + assert_file "Gemfile", /# gem 'mini_racer', platforms: :ruby/ end end @@ -459,14 +531,6 @@ class AppGeneratorTest < Rails::Generators::TestCase end end - def test_inclusion_of_javascript_libraries_if_required - run_generator [destination_root, "-j", "jquery"] - assert_file "app/assets/javascripts/application.js" do |contents| - assert_match %r{^//= require jquery}, contents - end - assert_gem "jquery-rails" - end - def test_javascript_is_skipped_if_required run_generator [destination_root, "--skip-javascript"] @@ -496,26 +560,6 @@ class AppGeneratorTest < Rails::Generators::TestCase end end - def test_generator_for_yarn - run_generator([destination_root]) - assert_file "package.json", /dependencies/ - assert_file "config/initializers/assets.rb", /node_modules/ - end - - def test_generator_for_yarn_skipped - run_generator([destination_root, "--skip-yarn"]) - assert_no_file "package.json" - - assert_file "config/initializers/assets.rb" do |content| - assert_no_match(/node_modules/, content) - end - - assert_file ".gitignore" do |content| - assert_no_match(/node_modules/, content) - assert_no_match(/yarn-error\.log/, content) - end - end - def test_inclusion_of_jbuilder run_generator assert_gem "jbuilder" @@ -550,9 +594,9 @@ class AppGeneratorTest < Rails::Generators::TestCase run_generator assert_file "config/environments/development.rb" do |content| if RbConfig::CONFIG["host_os"] =~ /darwin|linux/ - assert_match(/^\s*config.file_watcher = ActiveSupport::EventedFileUpdateChecker/, content) + assert_match(/^\s*config\.file_watcher = ActiveSupport::EventedFileUpdateChecker/, content) else - assert_match(/^\s*# config.file_watcher = ActiveSupport::EventedFileUpdateChecker/, content) + assert_match(/^\s*# config\.file_watcher = ActiveSupport::EventedFileUpdateChecker/, content) end end end @@ -583,24 +627,17 @@ class AppGeneratorTest < Rails::Generators::TestCase assert_file "lib/test_file.rb", "heres test data" end - def test_tests_are_removed_from_frameworks_if_skip_test_is_given - run_generator [destination_root, "--skip-test"] - assert_file "config/application.rb", /#\s+require\s+["']rails\/test_unit\/railtie["']/ - end - - def test_no_active_record_or_tests_if_skips_given - run_generator [destination_root, "--skip-test", "--skip-active-record"] - assert_file "config/application.rb", /#\s+require\s+["']rails\/test_unit\/railtie["']/ - assert_file "config/application.rb", /#\s+require\s+["']active_record\/railtie["']/ - assert_file "config/application.rb", /\s+require\s+["']active_job\/railtie["']/ - end - def test_pretend_option output = run_generator [File.join(destination_root, "myapp"), "--pretend"] assert_no_match(/run bundle install/, output) assert_no_match(/run git init/, output) end + def test_quiet_option + output = run_generator [File.join(destination_root, "myapp"), "--quiet"] + assert_empty output + end + def test_application_name_with_spaces path = File.join(destination_root, "foo bar") @@ -620,7 +657,7 @@ class AppGeneratorTest < Rails::Generators::TestCase assert_file "Gemfile" do |content| assert_match(/gem 'web-console',\s+github: 'rails\/web-console'/, content) - assert_no_match(/\Agem 'web-console', '>= 3.3.0'\z/, content) + assert_no_match(/\Agem 'web-console', '>= 3\.3\.0'\z/, content) end end @@ -629,7 +666,7 @@ class AppGeneratorTest < Rails::Generators::TestCase assert_file "Gemfile" do |content| assert_match(/gem 'web-console',\s+github: 'rails\/web-console'/, content) - assert_no_match(/\Agem 'web-console', '>= 3.3.0'\z/, content) + assert_no_match(/\Agem 'web-console', '>= 3\.3\.0'\z/, content) end end @@ -700,6 +737,45 @@ class AppGeneratorTest < Rails::Generators::TestCase end end + def test_webpack_option + command_check = -> command, *_ do + @called ||= 0 + if command == "webpacker:install" + @called += 1 + assert_equal 1, @called, "webpacker:install expected to be called once, but was called #{@called} times." + end + end + + generator([destination_root], webpack: "webpack").stub(:rails_command, command_check) do + generator.stub :bundle_command, nil do + quietly { generator.invoke_all } + end + end + + assert_gem "webpacker" + end + + def test_webpack_option_with_js_framework + command_check = -> command, *_ do + case command + when "webpacker:install" + @webpacker ||= 0 + @webpacker += 1 + assert_equal 1, @webpacker, "webpacker:install expected to be called once, but was called #{@webpacker} times." + when "webpacker:install:react" + @react ||= 0 + @react += 1 + assert_equal 1, @react, "webpacker:install:react expected to be called once, but was called #{@react} times." + end + end + + generator([destination_root], webpack: "react").stub(:rails_command, command_check) do + generator.stub :bundle_command, nil do + quietly { generator.invoke_all } + end + end + end + def test_generator_if_skip_turbolinks_is_given run_generator [destination_root, "--skip-turbolinks"] @@ -714,27 +790,45 @@ class AppGeneratorTest < Rails::Generators::TestCase end end - def test_gitignore_when_sqlite3 + def test_bootsnap run_generator - assert_file ".gitignore" do |content| - assert_match(/sqlite3/, content) + assert_gem "bootsnap" + assert_file "config/boot.rb" do |content| + assert_match(/require 'bootsnap\/setup'/, content) end end - def test_gitignore_when_no_active_record - run_generator [destination_root, "--skip-active-record"] + def test_skip_bootsnap + run_generator [destination_root, "--skip-bootsnap"] - assert_file ".gitignore" do |content| - assert_no_match(/sqlite/i, content) + assert_file "Gemfile" do |content| + assert_no_match(/bootsnap/, content) + end + assert_file "config/boot.rb" do |content| + assert_no_match(/require 'bootsnap\/setup'/, content) end end - def test_gitignore_when_non_sqlite3_db - run_generator([destination_root, "-d", "mysql"]) + def test_bootsnap_with_dev_option + run_generator [destination_root, "--dev"] - assert_file ".gitignore" do |content| - assert_no_match(/sqlite/i, content) + assert_file "Gemfile" do |content| + assert_no_match(/bootsnap/, content) + end + assert_file "config/boot.rb" do |content| + assert_no_match(/require 'bootsnap\/setup'/, content) + end + end + + def test_inclusion_of_ruby_version + run_generator + + assert_file "Gemfile" do |content| + assert_match(/ruby '#{RUBY_VERSION}'/, content) + end + assert_file ".ruby-version" do |content| + assert_match(/#{RUBY_VERSION}/, content) end end @@ -768,7 +862,7 @@ class AppGeneratorTest < Rails::Generators::TestCase def test_psych_gem run_generator - gem_regex = /gem 'psych',\s+'~> 2.0',\s+platforms: :rbx/ + gem_regex = /gem 'psych',\s+'~> 2\.0',\s+platforms: :rbx/ assert_file "Gemfile" do |content| if defined?(Rubinius) @@ -781,7 +875,7 @@ class AppGeneratorTest < Rails::Generators::TestCase def test_after_bundle_callback path = "http://example.org/rails_template" - template = %{ after_bundle { run 'echo ran after_bundle' } } + template = %{ after_bundle { run 'echo ran after_bundle' } }.dup template.instance_eval "def read; self; end" # Make the string respond to read check_open = -> *args do @@ -791,7 +885,7 @@ class AppGeneratorTest < Rails::Generators::TestCase sequence = ["git init", "install", "exec spring binstub --all", "echo ran after_bundle"] @sequence_step ||= 0 - ensure_bundler_first = -> command do + ensure_bundler_first = -> command, options = nil do assert_equal sequence[@sequence_step], command, "commands should be called in sequence #{sequence}" @sequence_step += 1 end @@ -799,7 +893,9 @@ class AppGeneratorTest < Rails::Generators::TestCase generator([destination_root], template: path).stub(:open, check_open, template) do generator.stub(:bundle_command, ensure_bundler_first) do generator.stub(:run, ensure_bundler_first) do - quietly { generator.invoke_all } + generator.stub(:rails_command, ensure_bundler_first) do + quietly { generator.invoke_all } + end end end end @@ -807,23 +903,19 @@ class AppGeneratorTest < Rails::Generators::TestCase assert_equal 4, @sequence_step end - def test_system_tests_directory_generated + def test_gitignore run_generator - assert_file("test/system/.keep") - assert_directory("test/system") - end - - def test_system_tests_are_not_generated_on_system_test_skip - run_generator [destination_root, "--skip-system-test"] - - assert_no_directory("test/system") + assert_file ".gitignore" do |content| + assert_match(/config\/master\.key/, content) + end end - def test_system_tests_are_not_generated_on_test_skip - run_generator [destination_root, "--skip-test"] + def test_system_tests_directory_generated + run_generator - assert_no_directory("test/system") + assert_file("test/system/.keep") + assert_directory("test/system") end private @@ -851,7 +943,7 @@ class AppGeneratorTest < Rails::Generators::TestCase assert_gem "spring-watcher-listen" assert_file "config/environments/development.rb" do |content| - assert_match(/^\s*config.file_watcher = ActiveSupport::EventedFileUpdateChecker/, content) + assert_match(/^\s*config\.file_watcher = ActiveSupport::EventedFileUpdateChecker/, content) end end @@ -861,7 +953,7 @@ class AppGeneratorTest < Rails::Generators::TestCase end assert_file "config/environments/development.rb" do |content| - assert_match(/^\s*# config.file_watcher = ActiveSupport::EventedFileUpdateChecker/, content) + assert_match(/^\s*# config\.file_watcher = ActiveSupport::EventedFileUpdateChecker/, content) end end diff --git a/railties/test/generators/application_record_generator_test.rb b/railties/test/generators/application_record_generator_test.rb new file mode 100644 index 0000000000..2c0aa7211b --- /dev/null +++ b/railties/test/generators/application_record_generator_test.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +require "generators/generators_test_helper" +require "rails/generators/rails/application_record/application_record_generator" + +class ApplicationRecordGeneratorTest < Rails::Generators::TestCase + include GeneratorsTestHelper + + def test_application_record_skeleton_is_created + run_generator + assert_file "app/models/application_record.rb" do |record| + assert_match(/class ApplicationRecord < ActiveRecord::Base/, record) + assert_match(/self\.abstract_class = true/, record) + end + end +end diff --git a/railties/test/generators/argv_scrubber_test.rb b/railties/test/generators/argv_scrubber_test.rb index 7f4295a20f..9ef61dc978 100644 --- a/railties/test/generators/argv_scrubber_test.rb +++ b/railties/test/generators/argv_scrubber_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "active_support/test_case" require "active_support/testing/autorun" require "rails/generators/rails/app/app_generator" @@ -80,9 +82,8 @@ module Rails file.puts "--hello --world" file.flush - message = nil scrubber = Class.new(ARGVScrubber) { - define_method(:puts) { |msg| message = msg } + define_method(:puts) { |msg| } }.new ["new", "--rc=#{file.path}"] args = scrubber.prepare! assert_equal ["--hello", "--world"], args diff --git a/railties/test/generators/assets_generator_test.rb b/railties/test/generators/assets_generator_test.rb index 1c02c67e42..3cec41dbf8 100644 --- a/railties/test/generators/assets_generator_test.rb +++ b/railties/test/generators/assets_generator_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "generators/generators_test_helper" require "rails/generators/rails/assets/assets_generator" diff --git a/railties/test/generators/channel_generator_test.rb b/railties/test/generators/channel_generator_test.rb index a1d54200ba..e238197eba 100644 --- a/railties/test/generators/channel_generator_test.rb +++ b/railties/test/generators/channel_generator_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "generators/generators_test_helper" require "rails/generators/channel/channel_generator" @@ -25,7 +27,7 @@ class ChannelGeneratorTest < Rails::Generators::TestCase end assert_file "app/assets/javascripts/channels/chat.js" do |channel| - assert_match(/App.chat = App.cable.subscriptions.create\("ChatChannel/, channel) + assert_match(/App\.chat = App\.cable\.subscriptions\.create\("ChatChannel/, channel) end end @@ -39,7 +41,7 @@ class ChannelGeneratorTest < Rails::Generators::TestCase end assert_file "app/assets/javascripts/channels/chat.js" do |channel| - assert_match(/App.chat = App.cable.subscriptions.create\("ChatChannel/, channel) + assert_match(/App\.chat = App\.cable\.subscriptions\.create\("ChatChannel/, channel) assert_match(/,\n\n speak/, channel) assert_match(/,\n\n mute: function\(\) \{\n return this\.perform\('mute'\);\n \}\n\}\);/, channel) end diff --git a/railties/test/generators/controller_generator_test.rb b/railties/test/generators/controller_generator_test.rb index af86a0136f..a3218951a6 100644 --- a/railties/test/generators/controller_generator_test.rb +++ b/railties/test/generators/controller_generator_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "generators/generators_test_helper" require "rails/generators/rails/controller/controller_generator" @@ -100,4 +102,11 @@ class ControllerGeneratorTest < Rails::Generators::TestCase assert_match(/^ namespace :admin do\n get 'dashboard\/index'\n end$/, route) end end + + def test_namespaced_routes_with_multiple_actions_are_created_in_routes + run_generator ["admin/dashboard", "index", "show"] + assert_file "config/routes.rb" do |route| + assert_match(/^ namespace :admin do\n get 'dashboard\/index'\n get 'dashboard\/show'\n end$/, route) + end + end end diff --git a/railties/test/generators/create_migration_test.rb b/railties/test/generators/create_migration_test.rb index ddd40e4d02..3cb7fd6baa 100644 --- a/railties/test/generators/create_migration_test.rb +++ b/railties/test/generators/create_migration_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "generators/generators_test_helper" require "rails/generators/rails/migration/migration_generator" @@ -46,12 +48,12 @@ class CreateMigrationTest < Rails::Generators::TestCase def test_invoke create_migration - assert_match(/create db\/migrate\/1_create_articles.rb\n/, invoke!) + assert_match(/create db\/migrate\/1_create_articles\.rb\n/, invoke!) assert_file @migration.destination end def test_invoke_pretended - create_migration(default_destination_path, {}, pretend: true) + create_migration(default_destination_path, {}, { pretend: true }) assert_no_file @migration.destination end @@ -67,7 +69,7 @@ class CreateMigrationTest < Rails::Generators::TestCase migration_exists! create_migration - assert_match(/identical db\/migrate\/1_create_articles.rb\n/, invoke!) + assert_match(/identical db\/migrate\/1_create_articles\.rb\n/, invoke!) assert @migration.identical? end @@ -84,29 +86,29 @@ class CreateMigrationTest < Rails::Generators::TestCase create_migration(dest, force: true) { "different content" } stdout = invoke! - assert_match(/remove db\/migrate\/1_migration.rb\n/, stdout) - assert_match(/create db\/migrate\/2_migration.rb\n/, stdout) + assert_match(/remove db\/migrate\/1_migration\.rb\n/, stdout) + assert_match(/create db\/migrate\/2_migration\.rb\n/, stdout) assert_file @migration.destination assert_no_file @existing_migration.destination end def test_invoke_forced_pretended_when_exists_not_identical migration_exists! - create_migration(default_destination_path, { force: true }, pretend: true) do + create_migration(default_destination_path, { force: true }, { pretend: true }) do "different content" end stdout = invoke! - assert_match(/remove db\/migrate\/1_create_articles.rb\n/, stdout) - assert_match(/create db\/migrate\/2_create_articles.rb\n/, stdout) + assert_match(/remove db\/migrate\/1_create_articles\.rb\n/, stdout) + assert_match(/create db\/migrate\/2_create_articles\.rb\n/, stdout) assert_no_file @migration.destination end def test_invoke_skipped_when_exists_not_identical migration_exists! - create_migration(default_destination_path, {}, skip: true) { "different content" } + create_migration(default_destination_path, {}, { skip: true }) { "different content" } - assert_match(/skip db\/migrate\/2_create_articles.rb\n/, invoke!) + assert_match(/skip db\/migrate\/2_create_articles\.rb\n/, invoke!) assert_no_file @migration.destination end @@ -114,21 +116,21 @@ class CreateMigrationTest < Rails::Generators::TestCase migration_exists! create_migration - assert_match(/remove db\/migrate\/1_create_articles.rb\n/, revoke!) + assert_match(/remove db\/migrate\/1_create_articles\.rb\n/, revoke!) assert_no_file @existing_migration.destination end def test_revoke_pretended migration_exists! - create_migration(default_destination_path, {}, pretend: true) + create_migration(default_destination_path, {}, { pretend: true }) - assert_match(/remove db\/migrate\/1_create_articles.rb\n/, revoke!) + assert_match(/remove db\/migrate\/1_create_articles\.rb\n/, revoke!) assert_file @existing_migration.destination end def test_revoke_when_no_exists create_migration - assert_match(/remove db\/migrate\/1_create_articles.rb\n/, revoke!) + assert_match(/remove db\/migrate\/1_create_articles\.rb\n/, revoke!) end end diff --git a/railties/test/generators/encrypted_secrets_generator_test.rb b/railties/test/generators/encrypted_secrets_generator_test.rb deleted file mode 100644 index 747abf19ed..0000000000 --- a/railties/test/generators/encrypted_secrets_generator_test.rb +++ /dev/null @@ -1,42 +0,0 @@ -require "generators/generators_test_helper" -require "rails/generators/rails/encrypted_secrets/encrypted_secrets_generator" - -class EncryptedSecretsGeneratorTest < Rails::Generators::TestCase - include GeneratorsTestHelper - - def setup - super - cd destination_root - end - - def test_generates_key_file_and_encrypted_secrets_file - run_generator - - assert_file "config/secrets.yml.key", /[\w\d]+/ - - assert File.exist?("config/secrets.yml.enc") - assert_no_match(/production:\n# external_api_key: [\w\d]+/, IO.binread("config/secrets.yml.enc")) - assert_match(/production:\n# external_api_key: [\w\d]+/, Rails::Secrets.read) - end - - def test_appends_to_gitignore - FileUtils.touch(".gitignore") - - run_generator - - assert_file ".gitignore", /config\/secrets.yml.key/, /(?!config\/secrets.yml.enc)/ - end - - def test_warns_when_ignore_is_missing - assert_match(/Add this to your ignore file/i, run_generator) - end - - def test_doesnt_generate_a_new_key_file_if_already_opted_in_to_encrypted_secrets - FileUtils.mkdir("config") - File.open("config/secrets.yml.enc", "w") { |f| f.puts "already secrety" } - - run_generator - - assert_no_file "config/secrets.yml.key" - end -end diff --git a/railties/test/generators/generated_attribute_test.rb b/railties/test/generators/generated_attribute_test.rb index 97847c8624..c6f7bdeda1 100644 --- a/railties/test/generators/generated_attribute_test.rb +++ b/railties/test/generators/generated_attribute_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "generators/generators_test_helper" require "rails/generators/generated_attribute" diff --git a/railties/test/generators/generator_generator_test.rb b/railties/test/generators/generator_generator_test.rb index 5ff8bb0357..eaa964cabc 100644 --- a/railties/test/generators/generator_generator_test.rb +++ b/railties/test/generators/generator_generator_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "generators/generators_test_helper" require "rails/generators/rails/generator/generator_generator" diff --git a/railties/test/generators/generator_test.rb b/railties/test/generators/generator_test.rb index 4444b3a56e..5f7daf5ac3 100644 --- a/railties/test/generators/generator_test.rb +++ b/railties/test/generators/generator_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "active_support/test_case" require "active_support/testing/autorun" require "rails/generators/app_base" diff --git a/railties/test/generators/generators_test_helper.rb b/railties/test/generators/generators_test_helper.rb index 2cdddc8713..ad2a55f496 100644 --- a/railties/test/generators/generators_test_helper.rb +++ b/railties/test/generators/generators_test_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" require "active_support/core_ext/module/remove_method" require "active_support/testing/stream" @@ -9,7 +11,7 @@ module Rails class << self remove_possible_method :root def root - @root ||= Pathname.new(File.expand_path("../../fixtures", __FILE__)) + @root ||= Pathname.new(File.expand_path("../fixtures", __dir__)) end end end @@ -41,9 +43,9 @@ module GeneratorsTestHelper end def copy_routes - routes = File.expand_path("../../../lib/rails/generators/rails/app/templates/config/routes.rb", __FILE__) + routes = File.expand_path("../../lib/rails/generators/rails/app/templates/config/routes.rb.tt", __dir__) destination = File.join(destination_root, "config") FileUtils.mkdir_p(destination) - FileUtils.cp routes, destination + FileUtils.cp routes, File.join(destination, "routes.rb") end end diff --git a/railties/test/generators/helper_generator_test.rb b/railties/test/generators/helper_generator_test.rb index d9e6e0a85a..4cdb6adf82 100644 --- a/railties/test/generators/helper_generator_test.rb +++ b/railties/test/generators/helper_generator_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "generators/generators_test_helper" require "rails/generators/rails/helper/helper_generator" diff --git a/railties/test/generators/integration_test_generator_test.rb b/railties/test/generators/integration_test_generator_test.rb index 9358b63bd4..82791f1a27 100644 --- a/railties/test/generators/integration_test_generator_test.rb +++ b/railties/test/generators/integration_test_generator_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "generators/generators_test_helper" require "rails/generators/rails/integration_test/integration_test_generator" diff --git a/railties/test/generators/job_generator_test.rb b/railties/test/generators/job_generator_test.rb index 68d158eb39..13276fac65 100644 --- a/railties/test/generators/job_generator_test.rb +++ b/railties/test/generators/job_generator_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "generators/generators_test_helper" require "rails/generators/job/job_generator" diff --git a/railties/test/generators/mailer_generator_test.rb b/railties/test/generators/mailer_generator_test.rb index 7d69d7470d..ddac6e1a1e 100644 --- a/railties/test/generators/mailer_generator_test.rb +++ b/railties/test/generators/mailer_generator_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "generators/generators_test_helper" require "rails/generators/mailer/mailer_generator" @@ -9,13 +11,13 @@ class MailerGeneratorTest < Rails::Generators::TestCase run_generator assert_file "app/mailers/notifier_mailer.rb" do |mailer| assert_match(/class NotifierMailer < ApplicationMailer/, mailer) - assert_no_match(/default from: "from@example.com"/, mailer) + assert_no_match(/default from: "from@example\.com"/, mailer) assert_no_match(/layout :mailer_notifier/, mailer) end assert_file "app/mailers/application_mailer.rb" do |mailer| assert_match(/class ApplicationMailer < ActionMailer::Base/, mailer) - assert_match(/default from: 'from@example.com'/, mailer) + assert_match(/default from: 'from@example\.com'/, mailer) assert_match(/layout 'mailer'/, mailer) end end @@ -48,11 +50,11 @@ class MailerGeneratorTest < Rails::Generators::TestCase assert_match(/class NotifierMailerPreview < ActionMailer::Preview/, preview) assert_match(/\# Preview this email at http:\/\/localhost\:3000\/rails\/mailers\/notifier_mailer\/foo/, preview) assert_instance_method :foo, preview do |foo| - assert_match(/NotifierMailer.foo/, foo) + assert_match(/NotifierMailer\.foo/, foo) end assert_match(/\# Preview this email at http:\/\/localhost\:3000\/rails\/mailers\/notifier_mailer\/bar/, preview) assert_instance_method :bar, preview do |bar| - assert_match(/NotifierMailer.bar/, bar) + assert_match(/NotifierMailer\.bar/, bar) end end end @@ -137,12 +139,12 @@ class MailerGeneratorTest < Rails::Generators::TestCase assert_file "app/mailers/notifier_mailer.rb" do |mailer| assert_instance_method :foo, mailer do |foo| - assert_match(/mail to: "to@example.org"/, foo) + assert_match(/mail to: "to@example\.org"/, foo) assert_match(/@greeting = "Hi"/, foo) end assert_instance_method :bar, mailer do |bar| - assert_match(/mail to: "to@example.org"/, bar) + assert_match(/mail to: "to@example\.org"/, bar) assert_match(/@greeting = "Hi"/, bar) end end diff --git a/railties/test/generators/migration_generator_test.rb b/railties/test/generators/migration_generator_test.rb index f46278cefe..88a939a55a 100644 --- a/railties/test/generators/migration_generator_test.rb +++ b/railties/test/generators/migration_generator_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "generators/generators_test_helper" require "rails/generators/rails/migration/migration_generator" @@ -48,6 +50,17 @@ class MigrationGeneratorTest < Rails::Generators::TestCase end end + def test_add_migration_with_table_having_from_in_title + migration = "add_email_address_to_blacklisted_from_campaign" + run_generator [migration, "email_address:string"] + + assert_migration "db/migrate/#{migration}.rb" do |content| + assert_method :change, content do |change| + assert_match(/add_column :blacklisted_from_campaigns, :email_address, :string/, change) + end + end + end + def test_remove_migration_with_indexed_attribute migration = "remove_title_body_from_posts" run_generator [migration, "title:string:index", "body:text"] @@ -73,6 +86,17 @@ class MigrationGeneratorTest < Rails::Generators::TestCase end end + def test_remove_migration_with_table_having_to_in_title + migration = "remove_email_address_from_sent_to_user" + run_generator [migration, "email_address:string"] + + assert_migration "db/migrate/#{migration}.rb" do |content| + assert_method :change, content do |change| + assert_match(/remove_column :sent_to_users, :email_address, :string/, change) + end + end + end + def test_remove_migration_with_references_options migration = "remove_references_from_books" run_generator [migration, "author:belongs_to", "distributor:references{polymorphic}"] @@ -204,8 +228,8 @@ class MigrationGeneratorTest < Rails::Generators::TestCase assert_migration "db/migrate/#{migration}.rb" do |content| assert_method :change, content do |change| assert_match(/create_join_table :artists, :musics/, change) - assert_match(/# t.index \[:artist_id, :music_id\]/, change) - assert_match(/ t.index \[:music_id, :artist_id\], unique: true/, change) + assert_match(/# t\.index \[:artist_id, :music_id\]/, change) + assert_match(/ t\.index \[:music_id, :artist_id\], unique: true/, change) end end end @@ -265,8 +289,8 @@ class MigrationGeneratorTest < Rails::Generators::TestCase assert_migration "db/migrate/#{migration}.rb" do |content| assert_method :change, content do |change| assert_match(/create_join_table :artist, :music/, change) - assert_match(/# t.index \[:artist_id, :music_id\]/, change) - assert_match(/ t.index \[:music_id, :artist_id\], unique: true/, change) + assert_match(/# t\.index \[:artist_id, :music_id\]/, change) + assert_match(/ t\.index \[:music_id, :artist_id\], unique: true/, change) end end end diff --git a/railties/test/generators/model_generator_test.rb b/railties/test/generators/model_generator_test.rb index 99490af3a9..516aa0704f 100644 --- a/railties/test/generators/model_generator_test.rb +++ b/railties/test/generators/model_generator_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "generators/generators_test_helper" require "rails/generators/rails/model/model_generator" require "active_support/core_ext/string/strip" @@ -6,14 +8,6 @@ class ModelGeneratorTest < Rails::Generators::TestCase include GeneratorsTestHelper arguments %w(Account name:string age:integer) - def test_application_record_skeleton_is_created - run_generator - assert_file "app/models/application_record.rb" do |record| - assert_match(/class ApplicationRecord < ActiveRecord::Base/, record) - assert_match(/self.abstract_class = true/, record) - end - end - def test_help_shows_invoked_generators_options content = run_generator ["--help"] assert_match(/ActiveRecord options:/, content) @@ -43,17 +37,6 @@ class ModelGeneratorTest < Rails::Generators::TestCase assert_no_migration "db/migrate/create_accounts.rb" end - def test_model_with_existent_application_record - mkdir_p "#{destination_root}/app/models" - touch "#{destination_root}/app/models/application_record.rb" - - Dir.chdir(destination_root) do - run_generator ["account"] - end - - assert_file "app/models/account.rb", /class Account < ApplicationRecord/ - end - def test_plural_names_are_singularized content = run_generator ["accounts".freeze] assert_file "app/models/account.rb", /class Account < ApplicationRecord/ @@ -253,7 +236,7 @@ class ModelGeneratorTest < Rails::Generators::TestCase def test_migration_with_timestamps run_generator - assert_migration "db/migrate/create_accounts.rb", /t.timestamps/ + assert_migration "db/migrate/create_accounts.rb", /t\.timestamps/ end def test_migration_timestamps_are_skipped @@ -261,7 +244,7 @@ class ModelGeneratorTest < Rails::Generators::TestCase assert_migration "db/migrate/create_accounts.rb" do |m| assert_method :change, m do |up| - assert_no_match(/t.timestamps/, up) + assert_no_match(/t\.timestamps/, up) end end end @@ -269,19 +252,19 @@ class ModelGeneratorTest < Rails::Generators::TestCase def test_migration_is_skipped_with_skip_option run_generator output = run_generator ["Account", "--skip"] - assert_match %r{skip\s+db/migrate/\d+_create_accounts.rb}, output + assert_match %r{skip\s+db/migrate/\d+_create_accounts\.rb}, output end def test_migration_is_ignored_as_identical_with_skip_option run_generator ["Account"] output = run_generator ["Account", "--skip"] - assert_match %r{identical\s+db/migrate/\d+_create_accounts.rb}, output + assert_match %r{identical\s+db/migrate/\d+_create_accounts\.rb}, output end def test_migration_is_skipped_on_skip_behavior run_generator output = run_generator ["Account"], behavior: :skip - assert_match %r{skip\s+db/migrate/\d+_create_accounts.rb}, output + assert_match %r{skip\s+db/migrate/\d+_create_accounts\.rb}, output end def test_migration_error_is_not_shown_on_revoke diff --git a/railties/test/generators/named_base_test.rb b/railties/test/generators/named_base_test.rb index 3015b5363b..4e61b660d7 100644 --- a/railties/test/generators/named_base_test.rb +++ b/railties/test/generators/named_base_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "generators/generators_test_helper" require "rails/generators/rails/scaffold_controller/scaffold_controller_generator" @@ -31,6 +33,17 @@ class NamedBaseTest < Rails::Generators::TestCase assert_name g, "foos", :plural_name assert_name g, "admin.foo", :i18n_scope assert_name g, "admin_foos", :table_name + assert_name g, "admin/foos", :controller_name + assert_name g, %w(admin), :controller_class_path + assert_name g, "Admin::Foos", :controller_class_name + assert_name g, "admin/foos", :controller_file_path + assert_name g, "foos", :controller_file_name + assert_name g, "admin.foos", :controller_i18n_scope + assert_name g, "admin_foo", :singular_route_name + assert_name g, "admin_foos", :plural_route_name + assert_name g, "@admin_foo", :redirect_resource_name + assert_name g, "admin_foo", :model_resource_name + assert_name g, "admin_foos", :index_helper end def test_named_generator_attributes_as_ruby @@ -45,6 +58,17 @@ class NamedBaseTest < Rails::Generators::TestCase assert_name g, "foos", :plural_name assert_name g, "admin.foo", :i18n_scope assert_name g, "admin_foos", :table_name + assert_name g, "Admin::Foos", :controller_name + assert_name g, %w(admin), :controller_class_path + assert_name g, "Admin::Foos", :controller_class_name + assert_name g, "admin/foos", :controller_file_path + assert_name g, "foos", :controller_file_name + assert_name g, "admin.foos", :controller_i18n_scope + assert_name g, "admin_foo", :singular_route_name + assert_name g, "admin_foos", :plural_route_name + assert_name g, "@admin_foo", :redirect_resource_name + assert_name g, "admin_foo", :model_resource_name + assert_name g, "admin_foos", :index_helper end def test_named_generator_attributes_without_pluralized @@ -57,7 +81,7 @@ class NamedBaseTest < Rails::Generators::TestCase ActiveRecord::Base.pluralize_table_names = original_pluralize_table_names end - def test_scaffold_plural_names + def test_namespaced_scaffold_plural_names g = generator ["admin/foo"] assert_name g, "admin/foos", :controller_name assert_name g, %w(admin), :controller_class_path @@ -67,7 +91,7 @@ class NamedBaseTest < Rails::Generators::TestCase assert_name g, "admin.foos", :controller_i18n_scope end - def test_scaffold_plural_names_as_ruby + def test_namespaced_scaffold_plural_names_as_ruby g = generator ["Admin::Foo"] assert_name g, "Admin::Foos", :controller_name assert_name g, %w(admin), :controller_class_path @@ -129,6 +153,19 @@ class NamedBaseTest < Rails::Generators::TestCase assert_name g, "admin/foos", :controller_file_path assert_name g, "foos", :controller_file_name assert_name g, "admin.foos", :controller_i18n_scope + assert_name g, "admin_user", :singular_route_name + assert_name g, "admin_users", :plural_route_name + assert_name g, "[:admin, @user]", :redirect_resource_name + assert_name g, "[:admin, user]", :model_resource_name + assert_name g, "admin_users", :index_helper + end + + def test_scaffold_plural_names + g = generator ["User"] + assert_name g, "@user", :redirect_resource_name + assert_name g, "user", :model_resource_name + assert_name g, "user", :singular_route_name + assert_name g, "users", :plural_route_name end private diff --git a/railties/test/generators/namespaced_generators_test.rb b/railties/test/generators/namespaced_generators_test.rb index 1caabbe6b1..4b75a31f17 100644 --- a/railties/test/generators/namespaced_generators_test.rb +++ b/railties/test/generators/namespaced_generators_test.rb @@ -1,8 +1,11 @@ +# frozen_string_literal: true + require "generators/generators_test_helper" require "rails/generators/rails/controller/controller_generator" require "rails/generators/rails/model/model_generator" require "rails/generators/mailer/mailer_generator" require "rails/generators/rails/scaffold/scaffold_generator" +require "rails/generators/rails/application_record/application_record_generator" class NamespacedGeneratorTestCase < Rails::Generators::TestCase include GeneratorsTestHelper @@ -149,7 +152,7 @@ class NamespacedMailerGeneratorTest < NamespacedGeneratorTestCase assert_file "app/mailers/test_app/notifier_mailer.rb" do |mailer| assert_match(/module TestApp/, mailer) assert_match(/class NotifierMailer < ApplicationMailer/, mailer) - assert_no_match(/default from: "from@example.com"/, mailer) + assert_no_match(/default from: "from@example\.com"/, mailer) end end @@ -421,3 +424,13 @@ class NamespacedScaffoldGeneratorTest < NamespacedGeneratorTestCase /module TestApp\n class Admin::RolesControllerTest < ActionDispatch::IntegrationTest/ end end + +class NamespacedApplicationRecordGeneratorTest < NamespacedGeneratorTestCase + include GeneratorsTestHelper + tests Rails::Generators::ApplicationRecordGenerator + + def test_adds_namespace_to_application_record + run_generator + assert_file "app/models/test_app/application_record.rb", /module TestApp/, / class ApplicationRecord < ActiveRecord::Base/ + end +end diff --git a/railties/test/generators/orm_test.rb b/railties/test/generators/orm_test.rb index 88ae930554..6eaf2fbfd3 100644 --- a/railties/test/generators/orm_test.rb +++ b/railties/test/generators/orm_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "generators/generators_test_helper" require "rails/generators/rails/scaffold_controller/scaffold_controller_generator" diff --git a/railties/test/generators/plugin_generator_test.rb b/railties/test/generators/plugin_generator_test.rb index 8ec096e5c6..fc7584c175 100644 --- a/railties/test/generators/plugin_generator_test.rb +++ b/railties/test/generators/plugin_generator_test.rb @@ -1,6 +1,9 @@ +# frozen_string_literal: true + require "generators/generators_test_helper" require "rails/generators/rails/plugin/plugin_generator" require "generators/shared_generator_tests" +require "rails/engine/updater" DEFAULT_PLUGIN_FILES = %w( .gitignore @@ -23,6 +26,10 @@ class PluginGeneratorTest < Rails::Generators::TestCase destination File.join(Rails.root, "tmp/bukkits") arguments [destination_root] + def application_path + "#{destination_root}/test/dummy" + end + # brings setup, teardown, and some tests include SharedGeneratorTests @@ -47,7 +54,7 @@ class PluginGeneratorTest < Rails::Generators::TestCase run_generator [File.join(destination_root, "hyphenated-name")] assert_no_file "hyphenated-name/lib/hyphenated-name.rb" assert_no_file "hyphenated-name/lib/hyphenated_name.rb" - assert_file "hyphenated-name/lib/hyphenated/name.rb", /module Hyphenated\n module Name\n # Your code goes here...\n end\nend/ + assert_file "hyphenated-name/lib/hyphenated/name.rb", /module Hyphenated\n module Name\n # Your code goes here\.\.\.\n end\nend/ end def test_correct_file_in_lib_folder_of_camelcase_plugin_name @@ -62,16 +69,30 @@ class PluginGeneratorTest < Rails::Generators::TestCase assert_no_file "config/routes.rb" assert_no_file "app/assets/config/bukkits_manifest.js" assert_file "test/test_helper.rb" do |content| - assert_match(/require.+test\/dummy\/config\/environment/, content) + assert_match(/require_relative.+test\/dummy\/config\/environment/, content) assert_match(/ActiveRecord::Migrator\.migrations_paths.+test\/dummy\/db\/migrate/, content) assert_match(/Minitest\.backtrace_filter = Minitest::BacktraceFilter\.new/, content) assert_match(/Rails::TestUnitReporter\.executable = 'bin\/test'/, content) end + assert_file "lib/bukkits/railtie.rb", /module Bukkits\n class Railtie < ::Rails::Railtie\n end\nend/ + assert_file "lib/bukkits.rb", /require "bukkits\/railtie"/ assert_file "test/bukkits_test.rb", /assert_kind_of Module, Bukkits/ assert_file "bin/test" assert_no_file "bin/rails" end + def test_generating_in_full_mode_with_almost_of_all_skip_options + run_generator [destination_root, "--full", "-M", "-O", "-C", "-S", "-T"] + assert_file "bin/rails" do |content| + assert_no_match(/\s+require\s+["']rails\/all["']/, content) + end + assert_file "bin/rails", /#\s+require\s+["']active_record\/railtie["']/ + assert_file "bin/rails", /#\s+require\s+["']action_mailer\/railtie["']/ + assert_file "bin/rails", /#\s+require\s+["']action_cable\/engine["']/ + assert_file "bin/rails", /#\s+require\s+["']sprockets\/railtie["']/ + assert_file "bin/rails", /#\s+require\s+["']rails\/test_unit\/railtie["']/ + end + def test_generating_test_files_in_full_mode run_generator [destination_root, "--full"] assert_directory "test/integration/" @@ -79,6 +100,11 @@ class PluginGeneratorTest < Rails::Generators::TestCase assert_file "test/integration/navigation_test.rb", /ActionDispatch::IntegrationTest/ end + def test_inclusion_of_git_source + run_generator [destination_root] + assert_file "Gemfile", /git_source/ + end + def test_inclusion_of_a_debugger run_generator [destination_root, "--full"] if defined?(JRUBY_VERSION) || RUBY_ENGINE == "rbx" @@ -94,23 +120,19 @@ class PluginGeneratorTest < Rails::Generators::TestCase run_generator [destination_root, "-T", "--full"] assert_no_directory "test/integration/" - assert_no_file "test" + assert_no_directory "test" assert_file "Rakefile" do |contents| assert_no_match(/APP_RAKEFILE/, contents) end - end - - def test_generating_adds_dummy_app_in_full_mode_without_sprockets - run_generator [destination_root, "-S", "--full"] - - assert_file "test/dummy/config/environments/production.rb" do |contents| - assert_no_match(/config\.assets/, contents) + assert_file "bin/rails" do |contents| + assert_no_match(/APP_PATH/, contents) end end def test_generating_adds_dummy_app_rake_tasks_without_unit_test_files run_generator [destination_root, "-T", "--mountable", "--dummy-path", "my_dummy_app"] assert_file "Rakefile", /APP_RAKEFILE/ + assert_file "bin/rails", /APP_PATH/ end def test_generating_adds_dummy_app_without_javascript_and_assets_deps @@ -131,8 +153,8 @@ class PluginGeneratorTest < Rails::Generators::TestCase def test_ensure_that_test_dummy_can_be_generated_from_a_template FileUtils.cd(Rails.root) run_generator([destination_root, "-m", "lib/create_test_dummy_template.rb", "--skip-test"]) - assert_file "spec/dummy" - assert_no_file "test" + assert_directory "spec/dummy" + assert_no_directory "test" end def test_database_entry_is_generated_for_sqlite3_by_default_in_full_mode @@ -151,51 +173,17 @@ class PluginGeneratorTest < Rails::Generators::TestCase run_generator [destination_root, "--skip-active-record"] assert_file "bukkits.gemspec" do |contents| - assert_no_match(/s.add_development_dependency "sqlite3"/, contents) - end - end - - def test_app_generator_without_skips - run_generator - assert_file "test/dummy/config/application.rb", /\s+require\s+["']rails\/all["']/ - assert_file "test/dummy/config/environments/development.rb" do |content| - assert_match(/config\.action_mailer\.raise_delivery_errors = false/, content) - end - assert_file "test/dummy/config/environments/test.rb" do |content| - assert_match(/config\.action_mailer\.delivery_method = :test/, content) - end - assert_file "test/dummy/config/environments/production.rb" do |content| - assert_match(/# config\.action_mailer\.raise_delivery_errors = false/, content) + assert_no_match(/s\.add_development_dependency "sqlite3"/, contents) end end - def test_active_record_is_removed_from_frameworks_if_skip_active_record_is_given - run_generator [destination_root, "--skip-active-record"] - assert_file "test/dummy/config/application.rb", /#\s+require\s+["']active_record\/railtie["']/ - end - def test_ensure_that_skip_active_record_option_is_passed_to_app_generator run_generator [destination_root, "--skip_active_record"] - assert_no_file "test/dummy/config/database.yml" assert_file "test/test_helper.rb" do |contents| assert_no_match(/ActiveRecord/, contents) end end - def test_action_mailer_is_removed_from_frameworks_if_skip_action_mailer_is_given - run_generator [destination_root, "--skip-action-mailer"] - assert_file "test/dummy/config/application.rb", /#\s+require\s+["']action_mailer\/railtie["']/ - assert_file "test/dummy/config/environments/development.rb" do |content| - assert_no_match(/config\.action_mailer/, content) - end - assert_file "test/dummy/config/environments/test.rb" do |content| - assert_no_match(/config\.action_mailer/, content) - end - assert_file "test/dummy/config/environments/production.rb" do |content| - assert_no_match(/config\.action_mailer/, content) - end - end - def test_ensure_that_database_option_is_passed_to_app_generator run_generator [destination_root, "--database", "postgresql"] assert_file "test/dummy/config/database.yml", /postgres/ @@ -273,7 +261,7 @@ class PluginGeneratorTest < Rails::Generators::TestCase assert_file "app/views" assert_file "app/helpers" assert_file "app/mailers" - assert_file "bin/rails" + assert_file "bin/rails", /\s+require\s+["']rails\/all["']/ assert_file "config/routes.rb", /Rails.application.routes.draw do/ assert_file "lib/bukkits/engine.rb", /module Bukkits\n class Engine < ::Rails::Engine\n end\nend/ assert_file "lib/bukkits.rb", /require "bukkits\/engine"/ @@ -293,7 +281,7 @@ class PluginGeneratorTest < Rails::Generators::TestCase assert_file "hyphenated-name/config/routes.rb", /Rails.application.routes.draw do/ assert_file "hyphenated-name/lib/hyphenated/name/engine.rb", /module Hyphenated\n module Name\n class Engine < ::Rails::Engine\n end\n end\nend/ assert_file "hyphenated-name/lib/hyphenated/name.rb", /require "hyphenated\/name\/engine"/ - assert_file "hyphenated-name/bin/rails", /\.\.\/\.\.\/lib\/hyphenated\/name\/engine/ + assert_file "hyphenated-name/bin/rails", /\.\.\/lib\/hyphenated\/name\/engine/ end def test_creating_engine_with_hyphenated_and_underscored_name_in_full_mode @@ -307,14 +295,14 @@ class PluginGeneratorTest < Rails::Generators::TestCase assert_file "my_hyphenated-name/app/helpers" assert_file "my_hyphenated-name/app/mailers" assert_file "my_hyphenated-name/bin/rails" - assert_file "my_hyphenated-name/config/routes.rb", /Rails.application.routes.draw do/ + assert_file "my_hyphenated-name/config/routes.rb", /Rails\.application\.routes\.draw do/ assert_file "my_hyphenated-name/lib/my_hyphenated/name/engine.rb", /module MyHyphenated\n module Name\n class Engine < ::Rails::Engine\n end\n end\nend/ assert_file "my_hyphenated-name/lib/my_hyphenated/name.rb", /require "my_hyphenated\/name\/engine"/ - assert_file "my_hyphenated-name/bin/rails", /\.\.\/\.\.\/lib\/my_hyphenated\/name\/engine/ + assert_file "my_hyphenated-name/bin/rails", /\.\.\/lib\/my_hyphenated\/name\/engine/ end def test_being_quiet_while_creating_dummy_application - assert_no_match(/create\s+config\/application.rb/, run_generator) + assert_no_match(/create\s+config\/application\.rb/, run_generator) end def test_create_mountable_application_with_mountable_option @@ -322,13 +310,13 @@ class PluginGeneratorTest < Rails::Generators::TestCase assert_file "app/assets/javascripts/bukkits" assert_file "app/assets/stylesheets/bukkits" assert_file "app/assets/images/bukkits" - assert_file "config/routes.rb", /Bukkits::Engine.routes.draw do/ + assert_file "config/routes.rb", /Bukkits::Engine\.routes\.draw do/ assert_file "lib/bukkits/engine.rb", /isolate_namespace Bukkits/ assert_file "test/dummy/config/routes.rb", /mount Bukkits::Engine => "\/bukkits"/ assert_file "app/controllers/bukkits/application_controller.rb", /module Bukkits\n class ApplicationController < ActionController::Base/ assert_file "app/models/bukkits/application_record.rb", /module Bukkits\n class ApplicationRecord < ActiveRecord::Base/ assert_file "app/jobs/bukkits/application_job.rb", /module Bukkits\n class ApplicationJob < ActiveJob::Base/ - assert_file "app/mailers/bukkits/application_mailer.rb", /module Bukkits\n class ApplicationMailer < ActionMailer::Base\n default from: 'from@example.com'\n layout 'mailer'\n/ + assert_file "app/mailers/bukkits/application_mailer.rb", /module Bukkits\n class ApplicationMailer < ActionMailer::Base\n default from: 'from@example\.com'\n layout 'mailer'\n/ assert_file "app/helpers/bukkits/application_helper.rb", /module Bukkits\n module ApplicationHelper/ assert_file "app/views/layouts/bukkits/application.html.erb" do |contents| assert_match "<title>Bukkits</title>", contents @@ -349,15 +337,15 @@ class PluginGeneratorTest < Rails::Generators::TestCase assert_file "hyphenated-name/app/assets/javascripts/hyphenated/name" assert_file "hyphenated-name/app/assets/stylesheets/hyphenated/name" assert_file "hyphenated-name/app/assets/images/hyphenated/name" - assert_file "hyphenated-name/config/routes.rb", /Hyphenated::Name::Engine.routes.draw do/ - assert_file "hyphenated-name/lib/hyphenated/name/version.rb", /module Hyphenated\n module Name\n VERSION = '0.1.0'\n end\nend/ + assert_file "hyphenated-name/config/routes.rb", /Hyphenated::Name::Engine\.routes\.draw do/ + assert_file "hyphenated-name/lib/hyphenated/name/version.rb", /module Hyphenated\n module Name\n VERSION = '0\.1\.0'\n end\nend/ assert_file "hyphenated-name/lib/hyphenated/name/engine.rb", /module Hyphenated\n module Name\n class Engine < ::Rails::Engine\n isolate_namespace Hyphenated::Name\n end\n end\nend/ assert_file "hyphenated-name/lib/hyphenated/name.rb", /require "hyphenated\/name\/engine"/ assert_file "hyphenated-name/test/dummy/config/routes.rb", /mount Hyphenated::Name::Engine => "\/hyphenated-name"/ assert_file "hyphenated-name/app/controllers/hyphenated/name/application_controller.rb", /module Hyphenated\n module Name\n class ApplicationController < ActionController::Base\n protect_from_forgery with: :exception\n end\n end\nend\n/ assert_file "hyphenated-name/app/models/hyphenated/name/application_record.rb", /module Hyphenated\n module Name\n class ApplicationRecord < ActiveRecord::Base\n self\.abstract_class = true\n end\n end\nend/ assert_file "hyphenated-name/app/jobs/hyphenated/name/application_job.rb", /module Hyphenated\n module Name\n class ApplicationJob < ActiveJob::Base/ - assert_file "hyphenated-name/app/mailers/hyphenated/name/application_mailer.rb", /module Hyphenated\n module Name\n class ApplicationMailer < ActionMailer::Base\n default from: 'from@example.com'\n layout 'mailer'\n end\n end\nend/ + assert_file "hyphenated-name/app/mailers/hyphenated/name/application_mailer.rb", /module Hyphenated\n module Name\n class ApplicationMailer < ActionMailer::Base\n default from: 'from@example\.com'\n layout 'mailer'\n end\n end\nend/ assert_file "hyphenated-name/app/helpers/hyphenated/name/application_helper.rb", /module Hyphenated\n module Name\n module ApplicationHelper\n end\n end\nend/ assert_file "hyphenated-name/app/views/layouts/hyphenated/name/application.html.erb" do |contents| assert_match "<title>Hyphenated name</title>", contents @@ -371,15 +359,15 @@ class PluginGeneratorTest < Rails::Generators::TestCase assert_file "my_hyphenated-name/app/assets/javascripts/my_hyphenated/name" assert_file "my_hyphenated-name/app/assets/stylesheets/my_hyphenated/name" assert_file "my_hyphenated-name/app/assets/images/my_hyphenated/name" - assert_file "my_hyphenated-name/config/routes.rb", /MyHyphenated::Name::Engine.routes.draw do/ - assert_file "my_hyphenated-name/lib/my_hyphenated/name/version.rb", /module MyHyphenated\n module Name\n VERSION = '0.1.0'\n end\nend/ + assert_file "my_hyphenated-name/config/routes.rb", /MyHyphenated::Name::Engine\.routes\.draw do/ + assert_file "my_hyphenated-name/lib/my_hyphenated/name/version.rb", /module MyHyphenated\n module Name\n VERSION = '0\.1\.0'\n end\nend/ assert_file "my_hyphenated-name/lib/my_hyphenated/name/engine.rb", /module MyHyphenated\n module Name\n class Engine < ::Rails::Engine\n isolate_namespace MyHyphenated::Name\n end\n end\nend/ assert_file "my_hyphenated-name/lib/my_hyphenated/name.rb", /require "my_hyphenated\/name\/engine"/ assert_file "my_hyphenated-name/test/dummy/config/routes.rb", /mount MyHyphenated::Name::Engine => "\/my_hyphenated-name"/ assert_file "my_hyphenated-name/app/controllers/my_hyphenated/name/application_controller.rb", /module MyHyphenated\n module Name\n class ApplicationController < ActionController::Base\n protect_from_forgery with: :exception\n end\n end\nend\n/ assert_file "my_hyphenated-name/app/models/my_hyphenated/name/application_record.rb", /module MyHyphenated\n module Name\n class ApplicationRecord < ActiveRecord::Base\n self\.abstract_class = true\n end\n end\nend/ assert_file "my_hyphenated-name/app/jobs/my_hyphenated/name/application_job.rb", /module MyHyphenated\n module Name\n class ApplicationJob < ActiveJob::Base/ - assert_file "my_hyphenated-name/app/mailers/my_hyphenated/name/application_mailer.rb", /module MyHyphenated\n module Name\n class ApplicationMailer < ActionMailer::Base\n default from: 'from@example.com'\n layout 'mailer'\n end\n end\nend/ + assert_file "my_hyphenated-name/app/mailers/my_hyphenated/name/application_mailer.rb", /module MyHyphenated\n module Name\n class ApplicationMailer < ActionMailer::Base\n default from: 'from@example\.com'\n layout 'mailer'\n end\n end\nend/ assert_file "my_hyphenated-name/app/helpers/my_hyphenated/name/application_helper.rb", /module MyHyphenated\n module Name\n module ApplicationHelper\n end\n end\nend/ assert_file "my_hyphenated-name/app/views/layouts/my_hyphenated/name/application.html.erb" do |contents| assert_match "<title>My hyphenated name</title>", contents @@ -393,15 +381,15 @@ class PluginGeneratorTest < Rails::Generators::TestCase assert_file "deep-hyphenated-name/app/assets/javascripts/deep/hyphenated/name" assert_file "deep-hyphenated-name/app/assets/stylesheets/deep/hyphenated/name" assert_file "deep-hyphenated-name/app/assets/images/deep/hyphenated/name" - assert_file "deep-hyphenated-name/config/routes.rb", /Deep::Hyphenated::Name::Engine.routes.draw do/ - assert_file "deep-hyphenated-name/lib/deep/hyphenated/name/version.rb", /module Deep\n module Hyphenated\n module Name\n VERSION = '0.1.0'\n end\n end\nend/ + assert_file "deep-hyphenated-name/config/routes.rb", /Deep::Hyphenated::Name::Engine\.routes\.draw do/ + assert_file "deep-hyphenated-name/lib/deep/hyphenated/name/version.rb", /module Deep\n module Hyphenated\n module Name\n VERSION = '0\.1\.0'\n end\n end\nend/ assert_file "deep-hyphenated-name/lib/deep/hyphenated/name/engine.rb", /module Deep\n module Hyphenated\n module Name\n class Engine < ::Rails::Engine\n isolate_namespace Deep::Hyphenated::Name\n end\n end\n end\nend/ assert_file "deep-hyphenated-name/lib/deep/hyphenated/name.rb", /require "deep\/hyphenated\/name\/engine"/ assert_file "deep-hyphenated-name/test/dummy/config/routes.rb", /mount Deep::Hyphenated::Name::Engine => "\/deep-hyphenated-name"/ assert_file "deep-hyphenated-name/app/controllers/deep/hyphenated/name/application_controller.rb", /module Deep\n module Hyphenated\n module Name\n class ApplicationController < ActionController::Base\n protect_from_forgery with: :exception\n end\n end\n end\nend\n/ assert_file "deep-hyphenated-name/app/models/deep/hyphenated/name/application_record.rb", /module Deep\n module Hyphenated\n module Name\n class ApplicationRecord < ActiveRecord::Base\n self\.abstract_class = true\n end\n end\n end\nend/ assert_file "deep-hyphenated-name/app/jobs/deep/hyphenated/name/application_job.rb", /module Deep\n module Hyphenated\n module Name\n class ApplicationJob < ActiveJob::Base/ - assert_file "deep-hyphenated-name/app/mailers/deep/hyphenated/name/application_mailer.rb", /module Deep\n module Hyphenated\n module Name\n class ApplicationMailer < ActionMailer::Base\n default from: 'from@example.com'\n layout 'mailer'\n end\n end\n end\nend/ + assert_file "deep-hyphenated-name/app/mailers/deep/hyphenated/name/application_mailer.rb", /module Deep\n module Hyphenated\n module Name\n class ApplicationMailer < ActionMailer::Base\n default from: 'from@example\.com'\n layout 'mailer'\n end\n end\n end\nend/ assert_file "deep-hyphenated-name/app/helpers/deep/hyphenated/name/application_helper.rb", /module Deep\n module Hyphenated\n module Name\n module ApplicationHelper\n end\n end\n end\nend/ assert_file "deep-hyphenated-name/app/views/layouts/deep/hyphenated/name/application.html.erb" do |contents| assert_match "<title>Deep hyphenated name</title>", contents @@ -412,16 +400,16 @@ class PluginGeneratorTest < Rails::Generators::TestCase def test_creating_gemspec run_generator - assert_file "bukkits.gemspec", /s.name\s+= "bukkits"/ - assert_file "bukkits.gemspec", /s.files = Dir\["\{app,config,db,lib\}\/\*\*\/\*", "MIT-LICENSE", "Rakefile", "README\.md"\]/ - assert_file "bukkits.gemspec", /s.version\s+ = Bukkits::VERSION/ + assert_file "bukkits.gemspec", /s\.name\s+= "bukkits"/ + assert_file "bukkits.gemspec", /s\.files = Dir\["\{app,config,db,lib\}\/\*\*\/\*", "MIT-LICENSE", "Rakefile", "README\.md"\]/ + assert_file "bukkits.gemspec", /s\.version\s+ = Bukkits::VERSION/ end def test_usage_of_engine_commands run_generator [destination_root, "--full"] - assert_file "bin/rails", /ENGINE_PATH = File.expand_path\('..\/..\/lib\/bukkits\/engine', __FILE__\)/ - assert_file "bin/rails", /ENGINE_ROOT = File.expand_path\('..\/..', __FILE__\)/ - assert_file "bin/rails", %r|APP_PATH = File.expand_path\('../../test/dummy/config/application', __FILE__\)| + assert_file "bin/rails", /ENGINE_PATH = File\.expand_path\('\.\.\/lib\/bukkits\/engine', __dir__\)/ + assert_file "bin/rails", /ENGINE_ROOT = File\.expand_path\('\.\.', __dir__\)/ + assert_file "bin/rails", %r|APP_PATH = File\.expand_path\('\.\./test/dummy/config/application', __dir__\)| assert_file "bin/rails", /require 'rails\/all'/ assert_file "bin/rails", /require 'rails\/engine\/commands'/ end @@ -437,7 +425,7 @@ class PluginGeneratorTest < Rails::Generators::TestCase assert_file "spec/dummy/config/application.rb" assert_no_file "test/dummy" assert_file "test/test_helper.rb" do |content| - assert_match(/require.+spec\/dummy\/config\/environment/, content) + assert_match(/require_relative.+spec\/dummy\/config\/environment/, content) assert_match(/ActiveRecord::Migrator\.migrations_paths.+spec\/dummy\/db\/migrate/, content) end end @@ -448,17 +436,16 @@ class PluginGeneratorTest < Rails::Generators::TestCase assert_file "spec/fake/config/application.rb" assert_no_file "test/dummy" assert_file "test/test_helper.rb" do |content| - assert_match(/require.+spec\/fake\/config\/environment/, content) + assert_match(/require_relative.+spec\/fake\/config\/environment/, content) assert_match(/ActiveRecord::Migrator\.migrations_paths.+spec\/fake\/db\/migrate/, content) end end def test_creating_dummy_without_tests_but_with_dummy_path run_generator [destination_root, "--dummy_path", "spec/dummy", "--skip-test"] - assert_file "spec/dummy" - assert_file "spec/dummy/config/application.rb" - assert_no_file "test" - assert_no_file "test/test_helper.rb" + assert_directory "spec/dummy" + assert_file "spec/dummy/config/application.rb", /#\s+require\s+["']rails\/test_unit\/railtie["']/ + assert_no_directory "test" assert_file ".gitignore" do |contents| assert_match(/spec\/dummy/, contents) end @@ -468,7 +455,7 @@ class PluginGeneratorTest < Rails::Generators::TestCase run_generator assert_file "test/dummy/config/environments/development.rb" do |contents| - assert_match(/^\s*# config.file_watcher = ActiveSupport::EventedFileUpdateChecker/, contents) + assert_match(/^\s*# config\.file_watcher = ActiveSupport::EventedFileUpdateChecker/, contents) end end @@ -487,8 +474,9 @@ class PluginGeneratorTest < Rails::Generators::TestCase assert_no_file "test/dummy/Gemfile" assert_no_file "test/dummy/public/robots.txt" assert_no_file "test/dummy/README.md" + assert_no_file "test/dummy/config/master.key" + assert_no_file "test/dummy/config/credentials.yml.enc" assert_no_directory "test/dummy/lib/tasks" - assert_no_directory "test/dummy/doc" assert_no_directory "test/dummy/test" assert_no_directory "test/dummy/vendor" assert_no_directory "test/dummy/.git" @@ -496,9 +484,9 @@ class PluginGeneratorTest < Rails::Generators::TestCase def test_skipping_test_files run_generator [destination_root, "--skip-test"] - assert_no_file "test" + assert_no_directory "test" assert_file ".gitignore" do |contents| - assert_no_match(/test\dummy/, contents) + assert_no_match(/test\/dummy/, contents) end end @@ -527,10 +515,11 @@ class PluginGeneratorTest < Rails::Generators::TestCase gemfile_path = "#{Rails.root}/Gemfile" Object.const_set("APP_PATH", Rails.root) FileUtils.touch gemfile_path + File.write(gemfile_path, "#foo") run_generator - assert_file gemfile_path, /gem 'bukkits', path: 'tmp\/bukkits'/ + assert_file gemfile_path, /^gem 'bukkits', path: 'tmp\/bukkits'/ ensure Object.send(:remove_const, "APP_PATH") FileUtils.rm gemfile_path @@ -676,20 +665,6 @@ class PluginGeneratorTest < Rails::Generators::TestCase assert_file "app/models/bukkits/article.rb", /class Article < ApplicationRecord/ end - def test_generate_application_record_when_does_not_exist_in_mountable_engine - run_generator [destination_root, "--mountable"] - FileUtils.rm "#{destination_root}/app/models/bukkits/application_record.rb" - capture(:stdout) do - `#{destination_root}/bin/rails g model article` - end - - assert_file "#{destination_root}/app/models/bukkits/application_record.rb" do |record| - assert_match(/module Bukkits/, record) - assert_match(/class ApplicationRecord < ActiveRecord::Base/, record) - assert_match(/self.abstract_class = true/, record) - end - end - def test_generate_application_mailer_when_does_not_exist_in_mountable_engine run_generator [destination_root, "--mountable"] FileUtils.rm "#{destination_root}/app/mailers/bukkits/application_mailer.rb" @@ -731,6 +706,53 @@ class PluginGeneratorTest < Rails::Generators::TestCase end end + def test_app_update_generates_bin_file + run_generator [destination_root, "--mountable"] + + Object.const_set("ENGINE_ROOT", destination_root) + FileUtils.rm("#{destination_root}/bin/rails") + + quietly { Rails::Engine::Updater.run(:create_bin_files) } + + assert_file "#{destination_root}/bin/rails" do |content| + assert_match(%r|APP_PATH = File\.expand_path\('\.\./test/dummy/config/application', __dir__\)|, content) + end + ensure + Object.send(:remove_const, "ENGINE_ROOT") + end + + def test_after_bundle_callback + path = "http://example.org/rails_template" + template = %{ after_bundle { run "echo ran after_bundle" } }.dup + template.instance_eval "def read; self; end" # Make the string respond to read + + check_open = -> *args do + assert_equal [ path, "Accept" => "application/x-thor-template" ], args + template + end + + sequence = ["echo ran after_bundle"] + @sequence_step ||= 0 + ensure_bundler_first = -> command do + assert_equal sequence[@sequence_step], command, "commands should be called in sequence #{sequence}" + @sequence_step += 1 + end + + content = nil + generator([destination_root], template: path).stub(:open, check_open, template) do + generator.stub(:bundle_command, ensure_bundler_first) do + generator.stub(:run, ensure_bundler_first) do + silence_stream($stdout) do + content = capture(:stderr) { generator.invoke_all } + end + end + end + end + + assert_equal 1, @sequence_step + assert_match(/DEPRECATION WARNING: `after_bundle` is deprecated/, content) + end + private def action(*args, &block) diff --git a/railties/test/generators/plugin_test_helper.rb b/railties/test/generators/plugin_test_helper.rb index 8ac90e3484..528f8d88f9 100644 --- a/railties/test/generators/plugin_test_helper.rb +++ b/railties/test/generators/plugin_test_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" require "tmpdir" diff --git a/railties/test/generators/plugin_test_runner_test.rb b/railties/test/generators/plugin_test_runner_test.rb index 0bdd2a77d2..89c3f1e496 100644 --- a/railties/test/generators/plugin_test_runner_test.rb +++ b/railties/test/generators/plugin_test_runner_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "generators/plugin_test_helper" class PluginTestRunnerTest < ActiveSupport::TestCase @@ -71,7 +73,7 @@ class PluginTestRunnerTest < ActiveSupport::TestCase create_test_file "post", pass: false output = run_test_command("test/post_test.rb") - assert_match %r{Finished in.*\n\n1 runs, 1 assertions}, output + assert_match %r{Finished in.*\n1 runs, 1 assertions}, output end def test_fail_fast @@ -103,6 +105,12 @@ class PluginTestRunnerTest < ActiveSupport::TestCase capture(:stderr) { run_test_command("test/models/warnings_test.rb -w") }) end + def test_run_rake_test + create_test_file "foo" + result = Dir.chdir(plugin_path) { `rake test TEST=test/foo_test.rb` } + assert_match "1 runs, 1 assertions, 0 failures", result + end + private def plugin_path "#{@destination_root}/bukkits" diff --git a/railties/test/generators/resource_generator_test.rb b/railties/test/generators/resource_generator_test.rb index e976e58180..63a2cd3869 100644 --- a/railties/test/generators/resource_generator_test.rb +++ b/railties/test/generators/resource_generator_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "generators/generators_test_helper" require "rails/generators/rails/resource/resource_generator" diff --git a/railties/test/generators/scaffold_controller_generator_test.rb b/railties/test/generators/scaffold_controller_generator_test.rb index 9971626f9f..fd5aa817b4 100644 --- a/railties/test/generators/scaffold_controller_generator_test.rb +++ b/railties/test/generators/scaffold_controller_generator_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "generators/generators_test_helper" require "rails/generators/rails/scaffold_controller/scaffold_controller_generator" @@ -172,6 +174,29 @@ class ScaffoldControllerGeneratorTest < Rails::Generators::TestCase assert_instance_method :index, content do |m| assert_match("@users = User.all", m) end + + assert_instance_method :create, content do |m| + assert_match("redirect_to [:admin, @user]", m) + end + + assert_instance_method :update, content do |m| + assert_match("redirect_to [:admin, @user]", m) + end + end + + assert_file "app/views/admin/users/index.html.erb" do |content| + assert_match("'Show', [:admin, user]", content) + assert_match("'Edit', edit_admin_user_path(user)", content) + assert_match("'Destroy', [:admin, user]", content) + assert_match("'New User', new_admin_user_path", content) + end + + assert_file "app/views/admin/users/new.html.erb" do |content| + assert_match("'Back', admin_users_path", content) + end + + assert_file "app/views/admin/users/_form.html.erb" do |content| + assert_match("model: [:admin, user]", content) end end @@ -182,6 +207,7 @@ class ScaffoldControllerGeneratorTest < Rails::Generators::TestCase Dir.chdir(engine_path) do quietly { `bin/rails g controller dashboard foo` } + quietly { `bin/rails db:migrate RAILS_ENV=test` } assert_match(/2 runs, 2 assertions, 0 failures, 0 errors/, `bin/rails test 2>&1`) end end @@ -193,6 +219,7 @@ class ScaffoldControllerGeneratorTest < Rails::Generators::TestCase Dir.chdir(engine_path) do quietly { `bin/rails g controller dashboard foo` } + quietly { `bin/rails db:migrate RAILS_ENV=test` } assert_match(/2 runs, 2 assertions, 0 failures, 0 errors/, `bin/rails test 2>&1`) end end diff --git a/railties/test/generators/scaffold_generator_test.rb b/railties/test/generators/scaffold_generator_test.rb index 436fbd5d73..29426cd99f 100644 --- a/railties/test/generators/scaffold_generator_test.rb +++ b/railties/test/generators/scaffold_generator_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "generators/generators_test_helper" require "rails/generators/rails/scaffold/scaffold_generator" @@ -65,6 +67,9 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase # System tests assert_file "test/system/product_lines_test.rb" do |test| assert_match(/class ProductLinesTest < ApplicationSystemTestCase/, test) + assert_match(/visit product_lines_url/, test) + assert_match(/fill_in "Title", with: @product_line\.title/, test) + assert_match(/assert_text "Product line was successfully updated"/, test) end # Views @@ -146,6 +151,9 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase assert_no_match(/assert_redirected_to/, test) end + # System tests + assert_no_file "test/system/product_lines_test.rb" + # Views assert_no_file "app/views/layouts/product_lines.html.erb" @@ -173,6 +181,16 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase end end + def test_system_tests_without_attributes + run_generator ["product_line"] + + assert_file "test/system/product_lines_test.rb" do |content| + assert_match(/class ProductLinesTest < ApplicationSystemTestCase/, content) + assert_match(/test "visiting the index"/, content) + assert_no_match(/fill_in/, content) + end + end + def test_scaffold_on_revoke run_generator run_generator ["product_line"], behavior: :revoke @@ -192,6 +210,9 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase assert_no_file "app/controllers/product_lines_controller.rb" assert_no_file "test/controllers/product_lines_controller_test.rb" + # System tests + assert_no_file "test/system/product_lines_test.rb" + # Views assert_no_file "app/views/product_lines" assert_no_file "app/views/layouts/product_lines.html.erb" @@ -257,8 +278,18 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase assert_file "test/controllers/admin/roles_controller_test.rb", /class Admin::RolesControllerTest < ActionDispatch::IntegrationTest/ + assert_file "test/system/admin/roles_test.rb", + /class Admin::RolesTest < ApplicationSystemTestCase/ + # Views - %w(index edit new show _form).each do |view| + assert_file "app/views/admin/roles/index.html.erb" do |content| + assert_match("'Show', admin_role", content) + assert_match("'Edit', edit_admin_role_path(admin_role)", content) + assert_match("'Destroy', admin_role", content) + assert_match("'New Admin Role', new_admin_role_path", content) + end + + %w(edit new show _form).each do |view| assert_file "app/views/admin/roles/#{view}.html.erb" end assert_no_file "app/views/layouts/admin/roles.html.erb" @@ -292,6 +323,9 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase assert_no_file "app/controllers/admin/roles_controller.rb" assert_no_file "test/controllers/admin/roles_controller_test.rb" + # System tests + assert_no_file "test/system/admin/roles_test.rb" + # Views assert_no_file "app/views/admin/roles" assert_no_file "app/views/layouts/admin/roles.html.erb" @@ -437,8 +471,8 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase end assert_file "app/views/accounts/_form.html.erb" do |content| - assert_match(/^\W{4}<%= f\.text_field :name %>/, content) - assert_match(/^\W{4}<%= f\.text_field :currency_id %>/, content) + assert_match(/^\W{4}<%= form\.text_field :name %>/, content) + assert_match(/^\W{4}<%= form\.text_field :currency_id %>/, content) end end @@ -461,8 +495,8 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase end assert_file "app/views/users/_form.html.erb" do |content| - assert_match(/<%= f\.password_field :password %>/, content) - assert_match(/<%= f\.password_field :password_confirmation %>/, content) + assert_match(/<%= form\.password_field :password %>/, content) + assert_match(/<%= form\.password_field :password_confirmation %>/, content) end assert_file "app/views/users/index.html.erb" do |content| @@ -478,6 +512,11 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase assert_match(/password_confirmation: 'secret'/, content) end + assert_file "test/system/users_test.rb" do |content| + assert_match(/fill_in "Password", with: 'secret'/, content) + assert_match(/fill_in "Password Confirmation", with: 'secret'/, content) + end + assert_file "test/fixtures/users.yml" do |content| assert_match(/password_digest: <%= BCrypt::Password.create\('secret'\) %>/, content) end @@ -558,4 +597,63 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase assert_match(/6 runs, 8 assertions, 0 failures, 0 errors/, `bin/rails test 2>&1`) end end + + def test_scaffold_on_invoke_inside_mountable_engine + Dir.chdir(destination_root) { `bundle exec rails plugin new bukkits --mountable` } + engine_path = File.join(destination_root, "bukkits") + + Dir.chdir(engine_path) do + quietly { `bin/rails generate scaffold User name:string age:integer` } + + assert File.exist?("app/models/bukkits/user.rb") + assert File.exist?("test/models/bukkits/user_test.rb") + assert File.exist?("test/fixtures/bukkits/users.yml") + + assert File.exist?("app/controllers/bukkits/users_controller.rb") + assert File.exist?("test/controllers/bukkits/users_controller_test.rb") + + assert File.exist?("test/system/bukkits/users_test.rb") + + assert File.exist?("app/views/bukkits/users/index.html.erb") + assert File.exist?("app/views/bukkits/users/edit.html.erb") + assert File.exist?("app/views/bukkits/users/show.html.erb") + assert File.exist?("app/views/bukkits/users/new.html.erb") + assert File.exist?("app/views/bukkits/users/_form.html.erb") + + assert File.exist?("app/helpers/bukkits/users_helper.rb") + + assert File.exist?("app/assets/javascripts/bukkits/users.js") + assert File.exist?("app/assets/stylesheets/bukkits/users.css") + end + end + + def test_scaffold_on_revoke_inside_mountable_engine + Dir.chdir(destination_root) { `bundle exec rails plugin new bukkits --mountable` } + engine_path = File.join(destination_root, "bukkits") + + Dir.chdir(engine_path) do + quietly { `bin/rails generate scaffold User name:string age:integer` } + quietly { `bin/rails destroy scaffold User` } + + assert_not File.exist?("app/models/bukkits/user.rb") + assert_not File.exist?("test/models/bukkits/user_test.rb") + assert_not File.exist?("test/fixtures/bukkits/users.yml") + + assert_not File.exist?("app/controllers/bukkits/users_controller.rb") + assert_not File.exist?("test/controllers/bukkits/users_controller_test.rb") + + assert_not File.exist?("test/system/bukkits/users_test.rb") + + assert_not File.exist?("app/views/bukkits/users/index.html.erb") + assert_not File.exist?("app/views/bukkits/users/edit.html.erb") + assert_not File.exist?("app/views/bukkits/users/show.html.erb") + assert_not File.exist?("app/views/bukkits/users/new.html.erb") + assert_not File.exist?("app/views/bukkits/users/_form.html.erb") + + assert_not File.exist?("app/helpers/bukkits/users_helper.rb") + + assert_not File.exist?("app/assets/javascripts/bukkits/users.js") + assert_not File.exist?("app/assets/stylesheets/bukkits/users.css") + end + end end diff --git a/railties/test/generators/shared_generator_tests.rb b/railties/test/generators/shared_generator_tests.rb index cc9d3629e9..97d43af60a 100644 --- a/railties/test/generators/shared_generator_tests.rb +++ b/railties/test/generators/shared_generator_tests.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # # Tests, setup, and teardown common to the application and plugin generator suites. # @@ -20,6 +22,10 @@ module SharedGeneratorTests Rails.application = TestApp::Application.instance end + def application_path + destination_root + end + def test_skeleton_is_created run_generator @@ -45,14 +51,14 @@ module SharedGeneratorTests reserved_words = %w[application destroy plugin runner test] reserved_words.each do |reserved| content = capture(:stderr) { run_generator [File.join(destination_root, reserved)] } - assert_match(/Invalid \w+ name #{reserved}. Please give a name which does not match one of the reserved rails words: application, destroy, plugin, runner, test\n/, content) + assert_match(/Invalid \w+ name #{reserved}\. Please give a name which does not match one of the reserved rails words: application, destroy, plugin, runner, test\n/, content) end end def test_name_raises_an_error_if_name_already_used_constant %w{ String Hash Class Module Set Symbol }.each do |ruby_class| content = capture(:stderr) { run_generator [File.join(destination_root, ruby_class)] } - assert_match(/Invalid \w+ name #{ruby_class}, constant #{ruby_class} is already in use. Please choose another \w+ name.\n/, content) + assert_match(/Invalid \w+ name #{ruby_class}, constant #{ruby_class} is already in use\. Please choose another \w+ name\.\n/, content) end end @@ -77,7 +83,7 @@ module SharedGeneratorTests def test_template_is_executed_when_supplied_an_https_path path = "https://gist.github.com/josevalim/103208/raw/" - template = %{ say "It works!" } + template = %{ say "It works!" }.dup template.instance_eval "def read; self; end" # Make the string respond to read check_open = -> *args do @@ -86,7 +92,9 @@ module SharedGeneratorTests end generator([destination_root], template: path).stub(:open, check_open, template) do - quietly { assert_match(/It works!/, capture(:stdout) { generator.invoke_all }) } + generator.stub :bundle_command, nil do + quietly { assert_match(/It works!/, capture(:stdout) { generator.invoke_all }) } + end end end @@ -97,15 +105,6 @@ module SharedGeneratorTests end end - def test_skip_bundle - assert_not_called(generator([destination_root], skip_bundle: true), :bundle_command) do - quietly { generator.invoke_all } - # skip_bundle is only about running bundle install, ensure the Gemfile is still - # generated. - assert_file "Gemfile" - end - end - def test_skip_git run_generator [destination_root, "--skip-git", "--full"] assert_no_file(".gitignore") @@ -121,4 +120,245 @@ module SharedGeneratorTests assert_no_file("app/models/concerns/.keep") end + + def test_default_frameworks_are_required_when_others_are_removed + run_generator [ + destination_root, + "--skip-active-record", + "--skip-active-storage", + "--skip-action-mailer", + "--skip-action-cable", + "--skip-sprockets" + ] + + assert_file "#{application_path}/config/application.rb", /^require\s+["']rails["']/ + assert_file "#{application_path}/config/application.rb", /^require\s+["']active_model\/railtie["']/ + assert_file "#{application_path}/config/application.rb", /^require\s+["']active_job\/railtie["']/ + assert_file "#{application_path}/config/application.rb", /^# require\s+["']active_record\/railtie["']/ + assert_file "#{application_path}/config/application.rb", /^# require\s+["']active_storage\/engine["']/ + assert_file "#{application_path}/config/application.rb", /^require\s+["']action_controller\/railtie["']/ + assert_file "#{application_path}/config/application.rb", /^# require\s+["']action_mailer\/railtie["']/ + assert_file "#{application_path}/config/application.rb", /^require\s+["']action_view\/railtie["']/ + assert_file "#{application_path}/config/application.rb", /^# require\s+["']action_cable\/engine["']/ + assert_file "#{application_path}/config/application.rb", /^# require\s+["']sprockets\/railtie["']/ + assert_file "#{application_path}/config/application.rb", /^require\s+["']rails\/test_unit\/railtie["']/ + end + + def test_generator_without_skips + run_generator + assert_file "#{application_path}/config/application.rb", /\s+require\s+["']rails\/all["']/ + assert_file "#{application_path}/config/environments/development.rb" do |content| + assert_match(/config\.action_mailer\.raise_delivery_errors = false/, content) + end + assert_file "#{application_path}/config/environments/test.rb" do |content| + assert_match(/config\.action_mailer\.delivery_method = :test/, content) + end + assert_file "#{application_path}/config/environments/production.rb" do |content| + assert_match(/# config\.action_mailer\.raise_delivery_errors = false/, content) + assert_match(/^ # config\.require_master_key = true/, content) + end + end + + def test_gitignore_when_sqlite3 + run_generator + + assert_file ".gitignore" do |content| + assert_match(/sqlite3/, content) + end + end + + def test_gitignore_when_non_sqlite3_db + run_generator([destination_root, "-d", "mysql"]) + + assert_file ".gitignore" do |content| + assert_no_match(/sqlite/i, content) + end + end + + def test_generator_if_skip_active_record_is_given + run_generator [destination_root, "--skip-active-record"] + assert_no_directory "#{application_path}/db/" + assert_no_file "#{application_path}/config/database.yml" + assert_no_file "#{application_path}/app/models/application_record.rb" + assert_file "#{application_path}/config/application.rb", /#\s+require\s+["']active_record\/railtie["']/ + assert_file "test/test_helper.rb" do |helper_content| + assert_no_match(/fixtures :all/, helper_content) + end + assert_file "#{application_path}/bin/setup" do |setup_content| + assert_no_match(/db:setup/, setup_content) + end + assert_file "#{application_path}/bin/update" do |update_content| + assert_no_match(/db:migrate/, update_content) + end + assert_file ".gitignore" do |content| + assert_no_match(/sqlite/i, content) + end + end + + def test_generator_for_active_storage + run_generator + + assert_file "#{application_path}/app/assets/javascripts/application.js" do |content| + assert_match(/^\/\/= require activestorage/, content) + end + + assert_file "#{application_path}/config/environments/development.rb" do |content| + assert_match(/config\.active_storage/, content) + end + + assert_file "#{application_path}/config/environments/production.rb" do |content| + assert_match(/config\.active_storage/, content) + end + + assert_file "#{application_path}/config/environments/test.rb" do |content| + assert_match(/config\.active_storage/, content) + end + + assert_file "#{application_path}/config/storage.yml" + assert_directory "#{application_path}/storage" + assert_directory "#{application_path}/tmp/storage" + + assert_file ".gitignore" do |content| + assert_match(/\/storage\//, content) + end + end + + def test_generator_if_skip_active_storage_is_given + run_generator [destination_root, "--skip-active-storage"] + + assert_file "#{application_path}/config/application.rb", /#\s+require\s+["']active_storage\/engine["']/ + + assert_file "#{application_path}/app/assets/javascripts/application.js" do |content| + assert_no_match(/^\/\/= require activestorage/, content) + end + + assert_file "#{application_path}/config/environments/development.rb" do |content| + assert_no_match(/config\.active_storage/, content) + end + + assert_file "#{application_path}/config/environments/production.rb" do |content| + assert_no_match(/config\.active_storage/, content) + end + + assert_file "#{application_path}/config/environments/test.rb" do |content| + assert_no_match(/config\.active_storage/, content) + end + + assert_no_file "#{application_path}/config/storage.yml" + assert_no_directory "#{application_path}/storage" + assert_no_directory "#{application_path}/tmp/storage" + + assert_file ".gitignore" do |content| + assert_no_match(/\/storage\//, content) + end + end + + def test_generator_does_not_generate_active_storage_contents_if_skip_active_record_is_given + run_generator [destination_root, "--skip-active-record"] + + assert_file "#{application_path}/config/application.rb", /#\s+require\s+["']active_storage\/engine["']/ + + assert_file "#{application_path}/app/assets/javascripts/application.js" do |content| + assert_no_match(/^\/\/= require activestorage/, content) + end + + assert_file "#{application_path}/config/environments/development.rb" do |content| + assert_no_match(/config\.active_storage/, content) + end + + assert_file "#{application_path}/config/environments/production.rb" do |content| + assert_no_match(/config\.active_storage/, content) + end + + assert_file "#{application_path}/config/environments/test.rb" do |content| + assert_no_match(/config\.active_storage/, content) + end + + assert_no_file "#{application_path}/config/storage.yml" + assert_no_directory "#{application_path}/storage" + assert_no_directory "#{application_path}/tmp/storage" + + assert_file ".gitignore" do |content| + assert_no_match(/\/storage\//, content) + end + end + + def test_generator_if_skip_action_mailer_is_given + run_generator [destination_root, "--skip-action-mailer"] + assert_file "#{application_path}/config/application.rb", /#\s+require\s+["']action_mailer\/railtie["']/ + assert_file "#{application_path}/config/environments/development.rb" do |content| + assert_no_match(/config\.action_mailer/, content) + end + assert_file "#{application_path}/config/environments/test.rb" do |content| + assert_no_match(/config\.action_mailer/, content) + end + assert_file "#{application_path}/config/environments/production.rb" do |content| + assert_no_match(/config\.action_mailer/, content) + end + assert_no_directory "#{application_path}/app/mailers" + assert_no_directory "#{application_path}/test/mailers" + end + + def test_generator_if_skip_action_cable_is_given + run_generator [destination_root, "--skip-action-cable"] + assert_file "#{application_path}/config/application.rb", /#\s+require\s+["']action_cable\/engine["']/ + assert_no_file "#{application_path}/config/cable.yml" + assert_no_file "#{application_path}/app/assets/javascripts/cable.js" + assert_no_directory "#{application_path}/app/assets/javascripts/channels" + assert_no_directory "#{application_path}/app/channels" + assert_file "Gemfile" do |content| + assert_no_match(/redis/, content) + end + end + + def test_generator_if_skip_sprockets_is_given + run_generator [destination_root, "--skip-sprockets"] + + assert_no_file "#{application_path}/config/initializers/assets.rb" + + assert_file "#{application_path}/config/application.rb", /#\s+require\s+["']sprockets\/railtie["']/ + + assert_file "Gemfile" do |content| + assert_no_match(/sass-rails/, content) + assert_no_match(/uglifier/, content) + assert_no_match(/coffee-rails/, content) + end + + assert_file "#{application_path}/config/environments/development.rb" do |content| + assert_no_match(/config\.assets\.debug/, content) + end + + assert_file "#{application_path}/config/environments/production.rb" do |content| + assert_no_match(/config\.assets\.digest/, content) + assert_no_match(/config\.assets\.js_compressor/, content) + assert_no_match(/config\.assets\.css_compressor/, content) + assert_no_match(/config\.assets\.compile/, content) + end + end + + def test_generator_for_yarn + run_generator + assert_file "#{application_path}/package.json", /dependencies/ + assert_file "#{application_path}/config/initializers/assets.rb", /node_modules/ + + assert_file ".gitignore" do |content| + assert_match(/node_modules/, content) + assert_match(/yarn-error\.log/, content) + end + end + + def test_generator_for_yarn_skipped + run_generator([destination_root, "--skip-yarn"]) + assert_no_file "#{application_path}/package.json" + assert_no_file "#{application_path}/bin/yarn" + + assert_file "#{application_path}/config/initializers/assets.rb" do |content| + assert_no_match(/node_modules/, content) + end + + assert_file ".gitignore" do |content| + assert_no_match(/node_modules/, content) + assert_no_match(/yarn-error\.log/, content) + end + end end diff --git a/railties/test/generators/system_test_generator_test.rb b/railties/test/generators/system_test_generator_test.rb index e8e561ec49..efa70a050b 100644 --- a/railties/test/generators/system_test_generator_test.rb +++ b/railties/test/generators/system_test_generator_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "generators/generators_test_helper" require "rails/generators/rails/system_test/system_test_generator" @@ -9,4 +11,9 @@ class SystemTestGeneratorTest < Rails::Generators::TestCase run_generator assert_file "test/system/users_test.rb", /class UsersTest < ApplicationSystemTestCase/ end + + def test_namespaced_system_test_skeleton_is_created + run_generator %w(admin/user) + assert_file "test/system/admin/users_test.rb", /class Admin::UsersTest < ApplicationSystemTestCase/ + end end diff --git a/railties/test/generators/task_generator_test.rb b/railties/test/generators/task_generator_test.rb index 2285534bb9..5f162919d8 100644 --- a/railties/test/generators/task_generator_test.rb +++ b/railties/test/generators/task_generator_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "generators/generators_test_helper" require "rails/generators/rails/task/task_generator" diff --git a/railties/test/generators/test_runner_in_engine_test.rb b/railties/test/generators/test_runner_in_engine_test.rb index 4b5fb3ba3f..0e15b5e388 100644 --- a/railties/test/generators/test_runner_in_engine_test.rb +++ b/railties/test/generators/test_runner_in_engine_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "generators/plugin_test_helper" class TestRunnerInEngineTest < ActiveSupport::TestCase @@ -17,7 +19,7 @@ class TestRunnerInEngineTest < ActiveSupport::TestCase create_test_file "post", pass: false output = run_test_command("test/post_test.rb") - expect = %r{Running:\n\nPostTest\nF\n\nFailure:\nPostTest#test_truth \[[^\]]+test/post_test.rb:6\]:\nwups!\n\nbin/rails test test/post_test.rb:4} + expect = %r{Running:\n\nPostTest\nF\n\nFailure:\nPostTest#test_truth \[[^\]]+test/post_test\.rb:6\]:\nwups!\n\nbin/rails test test/post_test\.rb:4} assert_match expect, output end diff --git a/railties/test/generators_test.rb b/railties/test/generators_test.rb index 68ba435393..1735804664 100644 --- a/railties/test/generators_test.rb +++ b/railties/test/generators_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "generators/generators_test_helper" require "rails/generators/rails/model/model_generator" require "rails/generators/test_unit/model/model_generator" @@ -34,6 +36,19 @@ class GeneratorsTest < Rails::Generators::TestCase assert_match "Maybe you meant 'migration'", output end + def test_generator_suggestions_except_en_locale + orig_available_locales = I18n.available_locales + orig_default_locale = I18n.default_locale + I18n.available_locales = :ja + I18n.default_locale = :ja + name = :tas + output = capture(:stdout) { Rails::Generators.invoke name } + assert_match "Maybe you meant 'task', 'job' or", output + ensure + I18n.available_locales = orig_available_locales + I18n.default_locale = orig_default_locale + end + def test_generator_multiple_suggestions name = :tas output = capture(:stdout) { Rails::Generators.invoke name } @@ -124,7 +139,7 @@ class GeneratorsTest < Rails::Generators::TestCase def test_rails_generators_help_does_not_include_app_nor_plugin_new output = capture(:stdout) { Rails::Generators.help } - assert_no_match(/app/, output) + assert_no_match(/app\W/, output) assert_no_match(/[^:]plugin/, output) end @@ -200,7 +215,7 @@ class GeneratorsTest < Rails::Generators::TestCase self.class.class_eval(<<-end_eval, __FILE__, __LINE__ + 1) class WithOptionsGenerator < Rails::Generators::Base - class_option :generate, :default => true + class_option :generate, default: true, type: :boolean end end_eval @@ -233,7 +248,7 @@ class GeneratorsTest < Rails::Generators::TestCase end def test_usage_with_embedded_ruby - require File.expand_path("fixtures/lib/generators/usage_template/usage_template_generator", File.dirname(__FILE__)) + require_relative "fixtures/lib/generators/usage_template/usage_template_generator" output = capture(:stdout) { Rails::Generators.invoke :usage_template, ["--help"] } assert_match(/:: 2 ::/, output) end diff --git a/railties/test/initializable_test.rb b/railties/test/initializable_test.rb index 4b67c91cc5..59fee245f9 100644 --- a/railties/test/initializable_test.rb +++ b/railties/test/initializable_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" require "rails/initializable" diff --git a/railties/test/isolation/abstract_unit.rb b/railties/test/isolation/abstract_unit.rb index 924503a522..6568a356d6 100644 --- a/railties/test/isolation/abstract_unit.rb +++ b/railties/test/isolation/abstract_unit.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Note: # It is important to keep this file as light as possible # the goal for tests that require this is to test booting up @@ -14,7 +16,7 @@ require "active_support/testing/autorun" require "active_support/testing/stream" require "active_support/test_case" -RAILS_FRAMEWORK_ROOT = File.expand_path("#{File.dirname(__FILE__)}/../../..") +RAILS_FRAMEWORK_ROOT = File.expand_path("../../..", __dir__) # These files do not require any others and are needed # to run the tests @@ -54,10 +56,7 @@ module TestHelpers @app ||= begin ENV["RAILS_ENV"] = env - # FIXME: shush Sass warning spam, not relevant to testing Railties - Kernel.silence_warnings do - require "#{app_path}/config/environment" - end + require "#{app_path}/config/environment" Rails.application end @@ -66,7 +65,7 @@ module TestHelpers end def extract_body(response) - "".tap do |body| + "".dup.tap do |body| response[2].each { |chunk| body << chunk } end end @@ -83,22 +82,6 @@ module TestHelpers assert_match "charset=utf-8", resp[1]["Content-Type"] assert extract_body(resp).match(/Yay! You.*re on Rails!/) end - - def assert_success(resp) - assert_equal 202, resp[0] - end - - def assert_missing(resp) - assert_equal 404, resp[0] - end - - def assert_header(key, value, resp) - assert_equal value, resp[1][key.to_s] - end - - def assert_body(expected, resp) - assert_equal expected, extract_body(resp) - end end module Generation @@ -106,7 +89,6 @@ module TestHelpers def build_app(options = {}) @prev_rails_env = ENV["RAILS_ENV"] ENV["RAILS_ENV"] = "development" - ENV["SECRET_KEY_BASE"] ||= SecureRandom.hex(16) FileUtils.rm_rf(app_path) FileUtils.cp_r(app_template_path, app_path) @@ -119,7 +101,7 @@ module TestHelpers end routes = File.read("#{app_path}/config/routes.rb") - if routes =~ /(\n\s*end\s*)\Z/ + if routes =~ /(\n\s*end\s*)\z/ File.open("#{app_path}/config/routes.rb", "w") do |f| f.puts $` + "\nActiveSupport::Deprecation.silence { match ':controller(/:action(/:id))(.:format)', via: :all }\n" + $1 end @@ -155,6 +137,7 @@ module TestHelpers def teardown_app ENV["RAILS_ENV"] = @prev_rails_env if @prev_rails_env + FileUtils.rm_rf(tmp_path) end # Make a very basic app, without creating the whole directory structure. @@ -164,9 +147,10 @@ module TestHelpers require "action_controller/railtie" require "action_view/railtie" - @app = Class.new(Rails::Application) + @app = Class.new(Rails::Application) do + def self.name; "RailtiesTestApp"; end + end @app.config.eager_load = false - @app.secrets.secret_key_base = "3b7cd727ee24e8444053437c36cc66c4" @app.config.session_store :cookie_store, key: "_myapp_session" @app.config.active_support.deprecation = :log @app.config.active_support.test_order = :random @@ -222,8 +206,8 @@ module TestHelpers FileUtils.mkdir_p(dir) app = File.readlines("#{app_path}/config/application.rb") - app.insert(2, "$:.unshift(\"#{dir}/lib\")") - app.insert(3, "require #{name.inspect}") + app.insert(4, "$:.unshift(\"#{dir}/lib\")") + app.insert(5, "require #{name.inspect}") File.open("#{app_path}/config/application.rb", "r+") do |f| f.puts app @@ -234,10 +218,86 @@ module TestHelpers end end - def script(script) - Dir.chdir(app_path) do - `#{Gem.ruby} #{app_path}/bin/rails #{script}` + # Invoke a bin/rails command inside the app + # + # allow_failure:: true to return normally if the command exits with + # a non-zero status. By default, this method will raise. + # stderr:: true to pass STDERR output straight to the "real" STDERR. + # By default, the STDERR and STDOUT of the process will be + # combined in the returned string. + def rails(*args, allow_failure: false, stderr: false) + args = args.flatten + fork = true + + command = "bin/rails #{Shellwords.join args}#{' 2>&1' unless stderr}" + + # Don't fork if the environment has disabled it + fork = false if ENV["NO_FORK"] + + # Don't fork if the runtime isn't able to + fork = false if !Process.respond_to?(:fork) + + # Don't fork if we're re-invoking minitest + fork = false if args.first == "t" || args.grep(/\Atest(:|\z)/).any? + + if fork + out_read, out_write = IO.pipe + if stderr + err_read, err_write = IO.pipe + else + err_write = out_write + end + + pid = fork do + out_read.close + err_read.close if err_read + + $stdin.reopen(File::NULL, "r") + $stdout.reopen(out_write) + $stderr.reopen(err_write) + + at_exit do + case $! + when SystemExit + exit! $!.status + when nil + exit! 0 + else + err_write.puts "#{$!.class}: #{$!}" + exit! 1 + end + end + + Rails.instance_variable_set :@_env, nil + + $-v = $-w = false + Dir.chdir app_path unless Dir.pwd == app_path + + ARGV.replace(args) + load "./bin/rails" + + exit! 0 + end + + out_write.close + + if err_read + err_write.close + + $stderr.write err_read.read + end + + output = out_read.read + + Process.waitpid pid + + else + output = `cd #{app_path}; #{command}` end + + raise "rails command failed (#{$?.exitstatus}): #{command}\n#{output}" unless allow_failure || $?.success? + + output end def add_to_top_of_config(str) @@ -251,7 +311,7 @@ module TestHelpers def add_to_config(str) environment = File.read("#{app_path}/config/application.rb") - if environment =~ /(\n\s*end\s*end\s*)\Z/ + if environment =~ /(\n\s*end\s*end\s*)\z/ File.open("#{app_path}/config/application.rb", "w") do |f| f.puts $` + "\n#{str}\n" + $1 end @@ -260,7 +320,7 @@ module TestHelpers def add_to_env_config(env, str) environment = File.read("#{app_path}/config/environments/#{env}.rb") - if environment =~ /(\n\s*end\s*)\Z/ + if environment =~ /(\n\s*end\s*)\z/ File.open("#{app_path}/config/environments/#{env}.rb", "w") do |f| f.puts $` + "\n#{str}\n" + $1 end @@ -282,10 +342,12 @@ module TestHelpers end def app_file(path, contents, mode = "w") - FileUtils.mkdir_p File.dirname("#{app_path}/#{path}") - File.open("#{app_path}/#{path}", mode) do |f| + file_name = "#{app_path}/#{path}" + FileUtils.mkdir_p File.dirname(file_name) + File.open(file_name, mode) do |f| f.puts contents end + file_name end def remove_file(path) @@ -297,7 +359,7 @@ module TestHelpers end def use_frameworks(arr) - to_remove = [:actionmailer, :activerecord] - arr + to_remove = [:actionmailer, :activerecord, :activestorage, :activejob] - arr if to_remove.include?(:activerecord) remove_from_config "config.active_record.*" @@ -305,6 +367,21 @@ module TestHelpers $:.reject! { |path| path =~ %r'/(#{to_remove.join('|')})/' } end + + def use_postgresql + File.open("#{app_path}/config/database.yml", "w") do |f| + f.puts <<-YAML + default: &default + adapter: postgresql + pool: 5 + database: railties_test + development: + <<: *default + test: + <<: *default + YAML + end + end end end @@ -314,7 +391,9 @@ class ActiveSupport::TestCase include TestHelpers::Generation include ActiveSupport::Testing::Stream - self.test_order = :sorted + def frozen_error_class + Object.const_defined?(:FrozenError) ? FrozenError : RuntimeError + end end # Create a scope and build a fixture rails app @@ -329,4 +408,25 @@ Module.new do File.open("#{app_template_path}/config/boot.rb", "w") do |f| f.puts "require 'rails/all'" end + + # Fake 'Bundler.require' -- we run using the repo's Gemfile, not an + # app-specific one: we don't want to require every gem that lists. + contents = File.read("#{app_template_path}/config/application.rb") + contents.sub!(/^Bundler\.require.*/, "%w(turbolinks).each { |r| require r }") + File.write("#{app_template_path}/config/application.rb", contents) + + require "rails" + + require "active_model" + require "active_job" + require "active_record" + require "action_controller" + require "action_mailer" + require "action_view" + require "active_storage" + require "action_cable" + require "sprockets" + + require "action_view/helpers" + require "action_dispatch/routing/route_set" end unless defined?(RAILS_ISOLATED_ENGINE) diff --git a/railties/test/json_params_parsing_test.rb b/railties/test/json_params_parsing_test.rb index 7fff3bb465..65ad9673ff 100644 --- a/railties/test/json_params_parsing_test.rb +++ b/railties/test/json_params_parsing_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" require "action_dispatch" require "active_record" diff --git a/railties/test/path_generation_test.rb b/railties/test/path_generation_test.rb index c0b03d0c15..849b183b37 100644 --- a/railties/test/path_generation_test.rb +++ b/railties/test/path_generation_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" require "active_support/core_ext/object/with_options" require "active_support/core_ext/object/json" @@ -56,12 +58,14 @@ class PathGenerationTest < ActiveSupport::TestCase Rails.logger = Logger.new nil app = Class.new(Rails::Application) { + def self.name; "ScriptNameTestApp"; end + attr_accessor :controller + def initialize super app = self @routes = TestSet.new ->(c) { app.controller = c } - secrets.secret_key_base = "foo" secrets.secret_token = "foo" end def app; routes; end diff --git a/railties/test/paths_test.rb b/railties/test/paths_test.rb index 7b2551062a..854b4448a4 100644 --- a/railties/test/paths_test.rb +++ b/railties/test/paths_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" require "rails/paths" require "minitest/mock" @@ -274,3 +276,23 @@ class PathsTest < ActiveSupport::TestCase end end end + +class PathsIntegrationTest < ActiveSupport::TestCase + test "A failed symlink is still a valid file" do + Dir.mktmpdir do |dir| + Dir.chdir(dir) do + FileUtils.mkdir_p("foo") + File.symlink("foo/doesnotexist.rb", "foo/bar.rb") + assert_equal true, File.symlink?("foo/bar.rb") + + root = Rails::Paths::Root.new("foo") + root.add "bar.rb" + + exception = assert_raises(RuntimeError) do + root["bar.rb"].existent + end + assert_match File.expand_path("foo/bar.rb"), exception.message + end + end + end +end diff --git a/railties/test/rack_logger_test.rb b/railties/test/rack_logger_test.rb index 33b4bc6a3a..e47f30d5b6 100644 --- a/railties/test/rack_logger_test.rb +++ b/railties/test/rack_logger_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" require "active_support/testing/autorun" require "active_support/test_case" diff --git a/railties/test/rails_info_controller_test.rb b/railties/test/rails_info_controller_test.rb index d795629ccd..878a238f8d 100644 --- a/railties/test/rails_info_controller_test.rb +++ b/railties/test/rails_info_controller_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" module ActionController diff --git a/railties/test/rails_info_test.rb b/railties/test/rails_info_test.rb index 9f4c5bb025..43b60b9144 100644 --- a/railties/test/rails_info_test.rb +++ b/railties/test/rails_info_test.rb @@ -1,19 +1,8 @@ -require "abstract_unit" - -unless defined?(Rails) && defined?(Rails::Info) - module Rails - class Info; end - end -end +# frozen_string_literal: true -require "active_support/core_ext/kernel/reporting" +require "abstract_unit" class InfoTest < ActiveSupport::TestCase - def setup - Rails.send :remove_const, :Info - silence_warnings { load "rails/info.rb" } - end - def test_property_with_block_swallows_exceptions_and_ignores_property assert_nothing_raised do Rails::Info.module_eval do @@ -39,7 +28,7 @@ class InfoTest < ActiveSupport::TestCase def test_rails_version assert_property "Rails version", - File.read(File.realpath("../../../RAILS_VERSION", __FILE__)).chomp + File.read(File.realpath("../../RAILS_VERSION", __dir__)).chomp end def test_html_includes_middleware diff --git a/railties/test/railties/engine_test.rb b/railties/test/railties/engine_test.rb index 52d691b73b..339a56c34f 100644 --- a/railties/test/railties/engine_test.rb +++ b/railties/test/railties/engine_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "isolation/abstract_unit" require "stringio" require "rack/test" @@ -30,6 +32,11 @@ module RailtiesTest require "#{app_path}/config/environment" end + def migrations + migration_root = File.expand_path(ActiveRecord::Migrator.migrations_paths.first, app_path) + ActiveRecord::Migrator.migrations(migration_root) + end + test "serving sprocket's assets" do @plugin.write "app/assets/javascripts/engine.js.erb", "<%= :alert %>();" add_to_env_config "development", "config.assets.digest = false" @@ -80,31 +87,32 @@ module RailtiesTest end RUBY - add_to_config "ActiveRecord::Base.timestamped_migrations = false" - boot_rails Dir.chdir(app_path) do + # Install Active Storage migration file first so as not to affect test. + `bundle exec rake active_storage:install` output = `bundle exec rake bukkits:install:migrations` - assert File.exist?("#{app_path}/db/migrate/2_create_users.bukkits.rb") - assert File.exist?("#{app_path}/db/migrate/3_add_last_name_to_users.bukkits.rb") - assert_match(/Copied migration 2_create_users.bukkits.rb from bukkits/, output) - assert_match(/Copied migration 3_add_last_name_to_users.bukkits.rb from bukkits/, output) - assert_match(/NOTE: Migration 3_create_sessions.rb from bukkits has been skipped/, output) - assert_equal 3, Dir["#{app_path}/db/migrate/*.rb"].length - - output = `bundle exec rake railties:install:migrations`.split("\n") + ["CreateUsers", "AddLastNameToUsers", "CreateSessions"].each do |migration_name| + assert migrations.detect { |migration| migration.name == migration_name } + end + assert_match(/Copied migration \d+_create_users\.bukkits\.rb from bukkits/, output) + assert_match(/Copied migration \d+_add_last_name_to_users\.bukkits\.rb from bukkits/, output) + assert_match(/NOTE: Migration \d+_create_sessions\.rb from bukkits has been skipped/, output) - assert_no_match(/2_create_users/, output.join("\n")) + migrations_count = Dir["#{app_path}/db/migrate/*.rb"].length - bukkits_migration_order = output.index(output.detect { |o| /NOTE: Migration 3_create_sessions.rb from bukkits has been skipped/ =~ o }) - assert_not_nil bukkits_migration_order, "Expected migration to be skipped" + assert_equal migrations.length, migrations_count - migrations_count = Dir["#{app_path}/db/migrate/*.rb"].length - `bundle exec rake railties:install:migrations` + output = `bundle exec rake railties:install:migrations`.split("\n") assert_equal migrations_count, Dir["#{app_path}/db/migrate/*.rb"].length + + assert_no_match(/\d+_create_users/, output.join("\n")) + + bukkits_migration_order = output.index(output.detect { |o| /NOTE: Migration \d+_create_sessions\.rb from bukkits has been skipped/ =~ o }) + assert_not_nil bukkits_migration_order, "Expected migration to be skipped" end end @@ -135,8 +143,8 @@ module RailtiesTest Dir.chdir(app_path) do output = `bundle exec rake railties:install:migrations`.split("\n") - assert_match(/Copied migration \d+_create_users.bukkits.rb from bukkits/, output.first) - assert_match(/Copied migration \d+_create_blogs.blog_engine.rb from blog_engine/, output.last) + assert_match(/Copied migration \d+_create_users\.bukkits\.rb from bukkits/, output.first) + assert_match(/Copied migration \d+_create_blogs\.blog_engine\.rb from blog_engine/, output.second) end end @@ -169,10 +177,12 @@ module RailtiesTest boot_rails Dir.chdir(app_path) do + # Install Active Storage migration file first so as not to affect test. + `bundle exec rake active_storage:install` output = `bundle exec rake railties:install:migrations`.split("\n") - assert_match(/Copied migration \d+_create_users.core_engine.rb from core_engine/, output.first) - assert_match(/Copied migration \d+_create_keys.api_engine.rb from api_engine/, output.last) + assert_match(/Copied migration \d+_create_users\.core_engine\.rb from core_engine/, output.first) + assert_match(/Copied migration \d+_create_keys\.api_engine\.rb from api_engine/, output.second) end end @@ -201,9 +211,12 @@ module RailtiesTest Dir.chdir(@plugin.path) do output = `bundle exec rake app:bukkits:install:migrations` - assert File.exist?("#{app_path}/db/migrate/0_add_first_name_to_users.bukkits.rb") - assert_match(/Copied migration 0_add_first_name_to_users.bukkits.rb from bukkits/, output) - assert_equal 1, Dir["#{app_path}/db/migrate/*.rb"].length + + migration_with_engine_path = migrations.detect { |migration| migration.name == "AddFirstNameToUsers" } + assert migration_with_engine_path + assert_match(/\/db\/migrate\/\d+_add_first_name_to_users\.bukkits\.rb/, migration_with_engine_path.filename) + assert_match(/Copied migration \d+_add_first_name_to_users\.bukkits\.rb from bukkits/, output) + assert_equal migrations.length, Dir["#{app_path}/db/migrate/*.rb"].length end end @@ -503,7 +516,7 @@ YAML def call(env) response = @app.call(env) - response[2].each(&:upcase!) + response[2] = response[2].collect(&:upcase) response end end @@ -882,7 +895,17 @@ YAML end RUBY - add_to_config "isolate_namespace AppTemplate" + engine "loaded_first" do |plugin| + plugin.write "lib/loaded_first.rb", <<-RUBY + module AppTemplate + module LoadedFirst + class Engine < ::Rails::Engine + isolate_namespace(AppTemplate) + end + end + end + RUBY + end app_file "config/routes.rb", <<-RUBY Rails.application.routes.draw do end @@ -890,7 +913,7 @@ YAML boot_rails - assert_equal AppTemplate::Engine, AppTemplate.railtie_namespace + assert_equal AppTemplate::LoadedFirst::Engine, AppTemplate.railtie_namespace end test "properly reload routes" do @@ -957,14 +980,14 @@ YAML boot_rails app_generators = Rails.application.config.generators.options[:rails] - assert_equal :mongoid , app_generators[:orm] - assert_equal :liquid , app_generators[:template_engine] + assert_equal :mongoid, app_generators[:orm] + assert_equal :liquid, app_generators[:template_engine] assert_equal :test_unit, app_generators[:test_framework] generators = Bukkits::Engine.config.generators.options[:rails] assert_equal :data_mapper, generators[:orm] - assert_equal :haml , generators[:template_engine] - assert_equal :rspec , generators[:test_framework] + assert_equal :haml, generators[:template_engine] + assert_equal :rspec, generators[:test_framework] end test "engine should get default generators with ability to overwrite them" do @@ -980,10 +1003,10 @@ YAML generators = Bukkits::Engine.config.generators.options[:rails] assert_equal :active_record, generators[:orm] - assert_equal :rspec , generators[:test_framework] + assert_equal :rspec, generators[:test_framework] app_generators = Rails.application.config.generators.options[:rails] - assert_equal :test_unit , app_generators[:test_framework] + assert_equal :test_unit, app_generators[:test_framework] end test "do not create table_name_prefix method if it already exists" do @@ -1285,10 +1308,10 @@ YAML boot_rails - get("/bukkits/bukkit", {}, "SCRIPT_NAME" => "/foo") + get("/bukkits/bukkit", {}, { "SCRIPT_NAME" => "/foo" }) assert_equal "/foo/bar", last_response.body - get("/bar", {}, "SCRIPT_NAME" => "/foo") + get("/bar", {}, { "SCRIPT_NAME" => "/foo" }) assert_equal "/foo/bukkits/bukkit", last_response.body end @@ -1334,13 +1357,143 @@ YAML boot_rails - get("/bukkits/bukkit", {}, "SCRIPT_NAME" => "/foo") + get("/bukkits/bukkit", {}, { "SCRIPT_NAME" => "/foo" }) assert_equal "/foo/bar", last_response.body - get("/bar", {}, "SCRIPT_NAME" => "/foo") + get("/bar", {}, { "SCRIPT_NAME" => "/foo" }) assert_equal "/foo/bukkits/bukkit", last_response.body end + test "isolated engine can be mounted under multiple static locations" do + app_file "app/controllers/foos_controller.rb", <<-RUBY + class FoosController < ApplicationController + def through_fruits + render plain: fruit_bukkits.posts_path + end + + def through_vegetables + render plain: vegetable_bukkits.posts_path + end + end + RUBY + + app_file "config/routes.rb", <<-RUBY + Rails.application.routes.draw do + scope "/fruits" do + mount Bukkits::Engine => "/bukkits", as: :fruit_bukkits + end + + scope "/vegetables" do + mount Bukkits::Engine => "/bukkits", as: :vegetable_bukkits + end + + get "/through_fruits" => "foos#through_fruits" + get "/through_vegetables" => "foos#through_vegetables" + end + RUBY + + @plugin.write "config/routes.rb", <<-RUBY + Bukkits::Engine.routes.draw do + resources :posts, only: :index + end + RUBY + + boot_rails + + get("/through_fruits") + assert_equal "/fruits/bukkits/posts", last_response.body + + get("/through_vegetables") + assert_equal "/vegetables/bukkits/posts", last_response.body + end + + test "isolated engine can be mounted under multiple dynamic locations" do + app_file "app/controllers/foos_controller.rb", <<-RUBY + class FoosController < ApplicationController + def through_fruits + render plain: fruit_bukkits.posts_path(fruit_id: 1) + end + + def through_vegetables + render plain: vegetable_bukkits.posts_path(vegetable_id: 1) + end + end + RUBY + + app_file "config/routes.rb", <<-RUBY + Rails.application.routes.draw do + resources :fruits do + mount Bukkits::Engine => "/bukkits" + end + + resources :vegetables do + mount Bukkits::Engine => "/bukkits" + end + + get "/through_fruits" => "foos#through_fruits" + get "/through_vegetables" => "foos#through_vegetables" + end + RUBY + + @plugin.write "config/routes.rb", <<-RUBY + Bukkits::Engine.routes.draw do + resources :posts, only: :index + end + RUBY + + boot_rails + + get("/through_fruits") + assert_equal "/fruits/1/bukkits/posts", last_response.body + + get("/through_vegetables") + assert_equal "/vegetables/1/bukkits/posts", last_response.body + end + + test "route helpers resolve script name correctly when called with different script name from current one" do + @plugin.write "app/controllers/posts_controller.rb", <<-RUBY + class PostsController < ActionController::Base + def index + render plain: fruit_bukkits.posts_path(fruit_id: 2) + end + end + RUBY + + app_file "config/routes.rb", <<-RUBY + Rails.application.routes.draw do + resources :fruits do + mount Bukkits::Engine => "/bukkits" + end + end + RUBY + + @plugin.write "config/routes.rb", <<-RUBY + Bukkits::Engine.routes.draw do + resources :posts, only: :index + end + RUBY + + boot_rails + + get("/fruits/1/bukkits/posts") + assert_equal "/fruits/2/bukkits/posts", last_response.body + end + + test "active_storage:install task works within engine" do + @plugin.write "Rakefile", <<-RUBY + APP_RAKEFILE = '#{app_path}/Rakefile' + load 'rails/tasks/engine.rake' + RUBY + + Dir.chdir(@plugin.path) do + output = `bundle exec rake app:active_storage:install` + assert $?.success?, output + + active_storage_migration = migrations.detect { |migration| migration.name == "CreateActiveStorageTables" } + assert active_storage_migration + end + end + private def app Rails.application diff --git a/railties/test/railties/generators_test.rb b/railties/test/railties/generators_test.rb index 5c691b9ec4..8383cb3050 100644 --- a/railties/test/railties/generators_test.rb +++ b/railties/test/railties/generators_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + RAILS_ISOLATED_ENGINE = true require "isolation/abstract_unit" diff --git a/railties/test/railties/mounted_engine_test.rb b/railties/test/railties/mounted_engine_test.rb index 5838d0d7e7..48f0fbc80f 100644 --- a/railties/test/railties/mounted_engine_test.rb +++ b/railties/test/railties/mounted_engine_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "isolation/abstract_unit" module ApplicationTests @@ -111,6 +113,7 @@ module ApplicationTests @plugin.write "config/routes.rb", <<-RUBY Blog::Engine.routes.draw do resources :posts + get '/different_context', to: 'posts#different_context' get '/generate_application_route', to: 'posts#generate_application_route' get '/application_route_in_view', to: 'posts#application_route_in_view' get '/engine_polymorphic_path', to: 'posts#engine_polymorphic_path' @@ -125,6 +128,10 @@ module ApplicationTests render plain: blog.post_path(1) end + def different_context + render plain: blog.post_path(1, user: "ada") + end + def generate_application_route path = main_app.url_for(controller: "/main", action: "index", @@ -141,7 +148,7 @@ module ApplicationTests end def engine_asset_path - render inline: "<%= asset_path 'images/foo.png' %>" + render inline: "<%= asset_path 'images/foo.png', skip_pipeline: true %>" end end end @@ -196,8 +203,12 @@ module ApplicationTests get "/john/blog/posts" assert_equal "/john/blog/posts/1", last_response.body + # test generating engine route from engine with a different context + get "/john/blog/different_context" + assert_equal "/ada/blog/posts/1", last_response.body + # test generating engine's route from engine with default_url_options - get "/john/blog/posts", {}, "SCRIPT_NAME" => "/foo" + get "/john/blog/posts", {}, { "SCRIPT_NAME" => "/foo" } assert_equal "/foo/john/blog/posts/1", last_response.body # test generating engine's route from application @@ -211,10 +222,10 @@ module ApplicationTests assert_equal "/john/blog/posts", last_response.body # test generating engine's route from application with default_url_options - get "/engine_route", {}, "SCRIPT_NAME" => "/foo" + get "/engine_route", {}, { "SCRIPT_NAME" => "/foo" } assert_equal "/foo/anonymous/blog/posts", last_response.body - get "/url_for_engine_route", {}, "SCRIPT_NAME" => "/foo" + get "/url_for_engine_route", {}, { "SCRIPT_NAME" => "/foo" } assert_equal "/foo/john/blog/posts", last_response.body # test generating application's route from engine @@ -232,14 +243,14 @@ module ApplicationTests assert_equal "/anonymous/blog/posts/1", last_response.body # test generating engine's route from other engine with default_url_options - get "/metrics/generate_blog_route", {}, "SCRIPT_NAME" => "/foo" + get "/metrics/generate_blog_route", {}, { "SCRIPT_NAME" => "/foo" } assert_equal "/foo/anonymous/blog/posts/1", last_response.body - get "/metrics/generate_blog_route_in_view", {}, "SCRIPT_NAME" => "/foo" + get "/metrics/generate_blog_route_in_view", {}, { "SCRIPT_NAME" => "/foo" } assert_equal "/foo/anonymous/blog/posts/1", last_response.body # test generating application's route from engine with default_url_options - get "/someone/blog/generate_application_route", {}, "SCRIPT_NAME" => "/foo" + get "/someone/blog/generate_application_route", {}, { "SCRIPT_NAME" => "/foo" } assert_equal "/foo/", last_response.body # test polymorphic routes diff --git a/railties/test/railties/railtie_test.rb b/railties/test/railties/railtie_test.rb index 30cd525266..359ab0fdae 100644 --- a/railties/test/railties/railtie_test.rb +++ b/railties/test/railties/railtie_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "isolation/abstract_unit" module RailtiesTest diff --git a/railties/test/secrets_test.rb b/railties/test/secrets_test.rb index 953408f0b4..06877bc76a 100644 --- a/railties/test/secrets_test.rb +++ b/railties/test/secrets_test.rb @@ -1,30 +1,25 @@ -require "abstract_unit" +# frozen_string_literal: true + require "isolation/abstract_unit" -require "rails/generators" -require "rails/generators/rails/encrypted_secrets/encrypted_secrets_generator" require "rails/secrets" class Rails::SecretsTest < ActiveSupport::TestCase include ActiveSupport::Testing::Isolation - def setup - build_app - - @old_read_encrypted_secrets, Rails::Secrets.read_encrypted_secrets = - Rails::Secrets.read_encrypted_secrets, true - end - - def teardown - Rails::Secrets.read_encrypted_secrets = @old_read_encrypted_secrets - - teardown_app - end + setup :build_app + teardown :teardown_app test "setting read to false skips parsing" do - Rails::Secrets.read_encrypted_secrets = false + run_secrets_generator do + Rails::Secrets.write(<<-end_of_secrets) + production: + yeah_yeah: lets-walk-in-the-cool-evening-light + end_of_secrets + + add_to_env_config("production", "config.read_encrypted_secrets = false") + app("production") - Dir.chdir(app_path) do - assert_equal Hash.new, Rails::Secrets.parse(%w( config/secrets.yml.enc ), env: "production") + assert_not Rails.application.secrets.yeah_yeah end end @@ -45,7 +40,7 @@ class Rails::SecretsTest < ActiveSupport::TestCase ENV["RAILS_MASTER_KEY"] = IO.binread("config/secrets.yml.key").strip FileUtils.rm("config/secrets.yml.key") - assert_match "production:\n# external_api_key", Rails::Secrets.read + assert_match "# production:\n# external_api_key:", Rails::Secrets.read ensure ENV["RAILS_MASTER_KEY"] = old_key end @@ -67,7 +62,7 @@ class Rails::SecretsTest < ActiveSupport::TestCase Rails::Secrets.read_for_editing do |tmp_path| decrypted_path = tmp_path - assert_match(/production:\n# external_api_key/, File.read(tmp_path)) + assert_match(/# production:\n# external_api_key/, File.read(tmp_path)) File.write(tmp_path, "Empty streets, empty nights. The Downtown Lights.") end @@ -80,27 +75,102 @@ class Rails::SecretsTest < ActiveSupport::TestCase test "merging secrets with encrypted precedence" do run_secrets_generator do File.write("config/secrets.yml", <<-end_of_secrets) - test: + production: yeah_yeah: lets-go-walking-down-this-empty-street end_of_secrets Rails::Secrets.write(<<-end_of_secrets) - test: + production: yeah_yeah: lets-walk-in-the-cool-evening-light end_of_secrets - Rails.application.config.root = app_path - Rails.application.instance_variable_set(:@secrets, nil) # Dance around caching 💃🕺 + add_to_env_config("production", "config.read_encrypted_secrets = true") + app("production") + assert_equal "lets-walk-in-the-cool-evening-light", Rails.application.secrets.yeah_yeah end end + test "refer secrets inside env config" do + run_secrets_generator do + Rails::Secrets.write(<<-end_of_yaml) + production: + some_secret: yeah yeah + end_of_yaml + + add_to_env_config "production", <<-end_of_config + config.dereferenced_secret = Rails.application.secrets.some_secret + end_of_config + + app("production") + + assert_equal "yeah yeah", Rails.application.config.dereferenced_secret + end + end + + test "do not update secrets.yml.enc when secretes do not change" do + run_secrets_generator do + Rails::Secrets.read_for_editing do |tmp_path| + File.write(tmp_path, "Empty streets, empty nights. The Downtown Lights.") + end + + FileUtils.cp("config/secrets.yml.enc", "config/secrets.yml.enc.bk") + + Rails::Secrets.read_for_editing do |tmp_path| + File.write(tmp_path, "Empty streets, empty nights. The Downtown Lights.") + end + + assert_equal File.read("config/secrets.yml.enc.bk"), File.read("config/secrets.yml.enc") + end + end + + test "can read secrets written in binary" do + run_secrets_generator do + secrets = <<-end_of_secrets + production: + api_key: 00112233445566778899aabbccddeeff… + end_of_secrets + + Rails::Secrets.write(secrets.dup.force_encoding(Encoding::ASCII_8BIT)) + + Rails::Secrets.read_for_editing do |tmp_path| + assert_match(/production:\n\s*api_key: 00112233445566778899aabbccddeeff…\n/, File.read(tmp_path)) + end + + app("production") + + assert_equal "00112233445566778899aabbccddeeff…", Rails.application.secrets.api_key + end + end + + test "can read secrets written in non-binary" do + run_secrets_generator do + secrets = <<-end_of_secrets + production: + api_key: 00112233445566778899aabbccddeeff… + end_of_secrets + + Rails::Secrets.write(secrets) + + Rails::Secrets.read_for_editing do |tmp_path| + assert_equal(secrets.dup.force_encoding(Encoding::ASCII_8BIT), IO.binread(tmp_path)) + end + + app("production") + + assert_equal "00112233445566778899aabbccddeeff…", Rails.application.secrets.api_key + end + end + private def run_secrets_generator Dir.chdir(app_path) do - capture(:stdout) do - Rails::Generators::EncryptedSecretsGenerator.start - end + File.write("config/secrets.yml.key", "f731758c639da2604dfb6bf3d1025de8") + File.write("config/secrets.yml.enc", "sEB0mHxDbeP1/KdnMk00wyzPFACl9K6t0cZWn5/Mfx/YbTHvnI07vrneqHg9kaH3wOS7L6pIQteu1P077OtE4BSx/ZRc/sgQPHyWu/tXsrfHqnPNpayOF/XZqizE91JacSFItNMWpuPsp9ynbzz+7cGhoB1S4aPNIU6u0doMrzdngDbijsaAFJmsHIQh6t/QHoJx--8aMoE0PvUWmw1Iqz--ldFqnM/K0g9k17M8PKoN/Q==") + + add_to_config <<-RUBY + config.read_encrypted_secrets = true + RUBY yield end diff --git a/railties/test/test_unit/reporter_test.rb b/railties/test/test_unit/reporter_test.rb index e22c939981..91cb47779b 100644 --- a/railties/test/test_unit/reporter_test.rb +++ b/railties/test/test_unit/reporter_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" require "rails/test_unit/reporter" require "minitest/mock" @@ -16,7 +18,7 @@ class TestUnitReporterTest < ActiveSupport::TestCase @reporter.record(failed_test) @reporter.report - assert_match %r{^bin/rails test .*test/test_unit/reporter_test.rb:\d+$}, @output.string + assert_match %r{^bin/rails test .*test/test_unit/reporter_test\.rb:\d+$}, @output.string assert_rerun_snippet_count 1 end @@ -52,7 +54,7 @@ class TestUnitReporterTest < ActiveSupport::TestCase @reporter.record(failed_test) @reporter.report - assert_match %r{^bin/test .*test/test_unit/reporter_test.rb:\d+$}, @output.string + assert_match %r{^bin/test .*test/test_unit/reporter_test\.rb:\d+$}, @output.string ensure Rails::TestUnitReporter.executable = original_executable end @@ -62,7 +64,7 @@ class TestUnitReporterTest < ActiveSupport::TestCase @reporter.record(failed_test) @reporter.report - expect = %r{\AF\n\nFailure:\nTestUnitReporterTest::ExampleTest#woot \[[^\]]+\]:\nboo\n\nbin/rails test test/test_unit/reporter_test.rb:\d+\n\n\z} + expect = %r{\AF\n\nFailure:\nTestUnitReporterTest::ExampleTest#woot \[[^\]]+\]:\nboo\n\nbin/rails test test/test_unit/reporter_test\.rb:\d+\n\n\z} assert_match expect, @output.string end @@ -70,7 +72,7 @@ class TestUnitReporterTest < ActiveSupport::TestCase @reporter.record(errored_test) @reporter.report - expect = %r{\AE\n\nError:\nTestUnitReporterTest::ExampleTest#woot:\nArgumentError: wups\n No backtrace\n\nbin/rails test .*test/test_unit/reporter_test.rb:\d+\n\n\z} + expect = %r{\AE\n\nError:\nTestUnitReporterTest::ExampleTest#woot:\nArgumentError: wups\n \n\nbin/rails test .*test/test_unit/reporter_test\.rb:\d+\n\n\z} assert_match expect, @output.string end @@ -79,7 +81,7 @@ class TestUnitReporterTest < ActiveSupport::TestCase verbose.record(skipped_test) verbose.report - expect = %r{\ATestUnitReporterTest::ExampleTest#woot = 10\.00 s = S\n\n\nSkipped:\nTestUnitReporterTest::ExampleTest#woot \[[^\]]+\]:\nskipchurches, misstemples\n\nbin/rails test test/test_unit/reporter_test.rb:\d+\n\n\z} + expect = %r{\ATestUnitReporterTest::ExampleTest#woot = 10\.00 s = S\n\n\nSkipped:\nTestUnitReporterTest::ExampleTest#woot \[[^\]]+\]:\nskipchurches, misstemples\n\nbin/rails test test/test_unit/reporter_test\.rb:\d+\n\n\z} assert_match expect, @output.string end @@ -150,7 +152,7 @@ class TestUnitReporterTest < ActiveSupport::TestCase colored = Rails::TestUnitReporter.new @output, color: true, output_inline: true colored.record(errored_test) - expected = %r{\e\[31mE\e\[0m\n\n\e\[31mError:\nTestUnitReporterTest::ExampleTest#woot:\nArgumentError: wups\n No backtrace\n\e\[0m} + expected = %r{\e\[31mE\e\[0m\n\n\e\[31mError:\nTestUnitReporterTest::ExampleTest#woot:\nArgumentError: wups\n \n\e\[0m} assert_match expected, @output.string end end @@ -161,7 +163,7 @@ class TestUnitReporterTest < ActiveSupport::TestCase end def failed_test - ft = ExampleTest.new(:woot) + ft = Minitest::Result.from(ExampleTest.new(:woot)) ft.failures << begin raise Minitest::Assertion, "boo" rescue Minitest::Assertion => e @@ -171,17 +173,20 @@ class TestUnitReporterTest < ActiveSupport::TestCase end def errored_test - et = ExampleTest.new(:woot) - et.failures << Minitest::UnexpectedError.new(ArgumentError.new("wups")) + error = ArgumentError.new("wups") + error.set_backtrace([ "some_test.rb:4" ]) + + et = Minitest::Result.from(ExampleTest.new(:woot)) + et.failures << Minitest::UnexpectedError.new(error) et end def passing_test - ExampleTest.new(:woot) + Minitest::Result.from(ExampleTest.new(:woot)) end def skipped_test - st = ExampleTest.new(:woot) + st = Minitest::Result.from(ExampleTest.new(:woot)) st.failures << begin raise Minitest::Skip, "skipchurches, misstemples" rescue Minitest::Assertion => e diff --git a/railties/test/version_test.rb b/railties/test/version_test.rb index 86a482e091..17a024fe7f 100644 --- a/railties/test/version_test.rb +++ b/railties/test/version_test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "abstract_unit" class VersionTest < ActiveSupport::TestCase |