diff --git a/Gemfile.lock b/Gemfile.lock index d4097c4..babdb58 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - packs (0.2.0) + packs (0.3.0) bigdecimal code_ownership (>= 1.33.0) packs-specification diff --git a/lib/packs/private.rb b/lib/packs/private.rb index 27f29e6..55b1a72 100644 --- a/lib/packs/private.rb +++ b/lib/packs/private.rb @@ -133,7 +133,7 @@ def self.move_to_pack!(pack_name:, paths_relative_to_root:, per_file_processors: ) [ file_move_operation, - file_move_operation.spec_file_move_operation, + *file_move_operation.spec_file_move_operations, ] end file_move_operations.each do |file_move_operation| @@ -360,7 +360,7 @@ def self.make_public!(paths_relative_to_root:, per_file_processors:) [ file_move_operation, - file_move_operation.spec_file_move_operation, + *file_move_operation.spec_file_move_operations, ] end diff --git a/lib/packs/private/file_move_operation.rb b/lib/packs/private/file_move_operation.rb index 6d5272a..e3e3822 100644 --- a/lib/packs/private/file_move_operation.rb +++ b/lib/packs/private/file_move_operation.rb @@ -51,8 +51,8 @@ def self.destination_pathname_for_new_public_api(origin_pathname) ).cleanpath end - sig { returns(FileMoveOperation) } - def spec_file_move_operation + sig { returns(T::Array[FileMoveOperation]) } + def spec_file_move_operations path_parts = filepath_without_pack_name.split('/') folder = T.must(path_parts[0]) file_extension = T.must(filepath_without_pack_name.split('.').last) @@ -67,11 +67,20 @@ def spec_file_move_operation new_destination_pathname = spec_pathname_for_non_app(destination_pathname, file_extension, folder) end - FileMoveOperation.new( - origin_pathname: new_origin_pathname, - destination_pathname: new_destination_pathname, - destination_pack: destination_pack - ) + ops = [ + FileMoveOperation.new( + origin_pathname: new_origin_pathname, + destination_pathname: new_destination_pathname, + destination_pack: destination_pack + ), + ] + + # For controllers, also include the request spec (spec/requests/_spec.rb) if it exists. + # Request specs are named without "controller" suffix (e.g. my_controller -> my_spec) + request_spec_op = request_spec_file_move_operation + ops << request_spec_op if request_spec_op + + ops end sig { params(filepath: Pathname, pack: T.nilable(Packs::Pack)).returns(String) } @@ -106,6 +115,35 @@ def spec_pathname_for_non_app(pathname, file_extension, folder) .sub(".#{file_extension}", '_spec.rb') end + # Maps app/controllers/*_controller.rb to spec/requests/*_spec.rb. + # Returns nil if the path is not a controller path. + sig { params(pathname: Pathname).returns(T.nilable(Pathname)) } + def pathname_to_request_spec(pathname) + return nil unless pathname.to_s.end_with?('_controller.rb') + + pathname + .sub('app/controllers/', 'spec/requests/') + .sub('/app/', '/spec/') # if destionation doesn't have controller subdirectory + .sub(%r(\Aapp/), 'spec/') + .sub(/_controller\.rb\z/, '_spec.rb') + .cleanpath + end + + sig { returns(T.nilable(FileMoveOperation)) } + def request_spec_file_move_operation + request_spec_origin = pathname_to_request_spec(origin_pathname) + return if request_spec_origin.nil? || !request_spec_origin.exist? + + request_spec_destination = pathname_to_request_spec(destination_pathname) + return if request_spec_destination.nil? + + FileMoveOperation.new( + origin_pathname: request_spec_origin, + destination_pathname: request_spec_destination, + destination_pack: destination_pack + ) + end + sig { params(path: Pathname).returns(FileMoveOperation) } def relative_to(path) FileMoveOperation.new( diff --git a/packs.gemspec b/packs.gemspec index fec2a35..bd45953 100644 --- a/packs.gemspec +++ b/packs.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |spec| spec.name = 'packs' - spec.version = '0.2.0' + spec.version = '0.3.0' spec.authors = ['Gusto Engineers'] spec.email = ['dev@gusto.com'] diff --git a/spec/packs_spec.rb b/spec/packs_spec.rb index aca548e..60d41a7 100644 --- a/spec/packs_spec.rb +++ b/spec/packs_spec.rb @@ -409,6 +409,65 @@ def write_codeownership_config ) end + it 'moves the request spec when moving a controller if spec/requests/_spec.rb exists (name = controller minus "controller")' do + write_file('app/controllers/my_controller.rb') + write_file('spec/requests/my_spec.rb') + write_package_yml('packs/my_pack') + Packs.move_to_pack!( + pack_name: 'packs/my_pack', + paths_relative_to_root: ['app/controllers/my_controller.rb'] + ) + + expect_files_to_not_exist( + [ + 'app/controllers/my_controller.rb', + 'spec/requests/my_spec.rb', + ] + ) + expect_files_to_exist( + [ + 'packs/my_pack/app/controllers/my_controller.rb', + 'packs/my_pack/spec/requests/my_spec.rb', + ] + ) + end + + it 'moves namespaced request spec when moving a namespaced controller' do + write_file('app/controllers/api/v1/users_controller.rb') + write_file('spec/requests/api/v1/users_spec.rb') + write_package_yml('packs/my_pack') + Packs.move_to_pack!( + pack_name: 'packs/my_pack', + paths_relative_to_root: ['app/controllers/api/v1/users_controller.rb'] + ) + + expect_files_to_not_exist( + [ + 'app/controllers/api/v1/users_controller.rb', + 'spec/requests/api/v1/users_spec.rb', + ] + ) + expect_files_to_exist( + [ + 'packs/my_pack/app/controllers/api/v1/users_controller.rb', + 'packs/my_pack/spec/requests/api/v1/users_spec.rb', + ] + ) + end + + it 'does not move a request spec when no file exists at spec/requests/_spec.rb' do + write_file('app/controllers/my_controller.rb') + # no spec/requests/my_spec.rb + write_package_yml('packs/my_pack') + Packs.move_to_pack!( + pack_name: 'packs/my_pack', + paths_relative_to_root: ['app/controllers/my_controller.rb'] + ) + + expect_files_to_not_exist(['app/controllers/my_controller.rb']) + expect_files_to_exist(['packs/my_pack/app/controllers/my_controller.rb']) + end + it 'can move files from non-pack packages into a pack' do target_pack = 'packs/animals' file_to_move = 'lib/tasks/donkey.rake' @@ -990,6 +1049,29 @@ def write_codeownership_config ) end + it 'makes a controller and its matching request spec public when spec/requests/_spec.rb exists (name = controller minus "controller")' do + write_file('app/controllers/my_controller.rb') + write_file('spec/requests/my_spec.rb') + + Packs.make_public!( + paths_relative_to_root: ['app/controllers/my_controller.rb'] + ) + + expect_files_to_not_exist( + [ + 'app/controllers/my_controller.rb', + 'spec/requests/my_spec.rb', + ] + ) + + expect_files_to_exist( + [ + 'app/public/my_controller.rb', + 'spec/public/my_spec.rb', + ] + ) + end + it 'can make directories in the monolith and their specs public' do write_file('app/services/fish_like/small_ones/goldfish.rb') write_file('app/services/fish_like/small_ones/seahorse.rb')