From 214777681a34acc8a11e54ded5d2e47fd9de07e1 Mon Sep 17 00:00:00 2001 From: James Couball Date: Wed, 30 Oct 2024 10:24:19 -0700 Subject: [PATCH] test: add fully integrated test that installs and runs gembytes via bundler Add cucumber tests (run via rspec with the turnip gem) that creates a sample project, installs the bundler-gem_bytes plugin, and then runs a gembytes script. The purpose is to make sure that the integration with bundler is completely working end-to-end. --- bundler-gem_bytes.gemspec | 3 + lib/bundler/gem_bytes/actions.rb | 2 - spec/features/actions_steps.rb | 95 ++++++++++++++++++++++++++++ spec/features/add_dependency.feature | 70 ++++++++++++++++++++ spec/spec_helper.rb | 23 ++++++- 5 files changed, 190 insertions(+), 3 deletions(-) create mode 100644 spec/features/actions_steps.rb create mode 100644 spec/features/add_dependency.feature diff --git a/bundler-gem_bytes.gemspec b/bundler-gem_bytes.gemspec index 2678127..2b524f1 100644 --- a/bundler-gem_bytes.gemspec +++ b/bundler-gem_bytes.gemspec @@ -51,11 +51,14 @@ Gem::Specification.new do |spec| spec.add_development_dependency 'bundler-audit', '~> 0.9' spec.add_development_dependency 'main_branch_shared_rubocop_config' + spec.add_development_dependency 'process_executer', '~> 1.2' spec.add_development_dependency 'rake', '~> 13.2' spec.add_development_dependency 'rspec', '~> 3.13' spec.add_development_dependency 'rubocop', '~> 1.66' spec.add_development_dependency 'simplecov', '~> 0.22' + spec.add_development_dependency 'simplecov-lcov', '~> 0.8' spec.add_development_dependency 'simplecov-rspec', '~> 0.4' + spec.add_development_dependency 'turnip', '~> 4.4' if RUBY_PLATFORM != 'java' spec.add_development_dependency 'redcarpet', '~> 3.5' diff --git a/lib/bundler/gem_bytes/actions.rb b/lib/bundler/gem_bytes/actions.rb index aca5c56..6a9505b 100644 --- a/lib/bundler/gem_bytes/actions.rb +++ b/lib/bundler/gem_bytes/actions.rb @@ -24,13 +24,11 @@ module Actions # @api public # def add_dependency(dependency_type, gem_name, version_constraint, force: false, gemspec: Dir['*.gemspec'].first) - puts 'Staring' source = File.read(gemspec) updated_source = Bundler::GemBytes::Gemspec::UpsertDependency.new( dependency_type, gem_name, version_constraint, force: force ).call(source) File.write(gemspec, updated_source) - puts 'Ending' end end end diff --git a/spec/features/actions_steps.rb b/spec/features/actions_steps.rb new file mode 100644 index 0000000..41ef15d --- /dev/null +++ b/spec/features/actions_steps.rb @@ -0,0 +1,95 @@ +# frozen_string_literal: true + +# In your Turnip step definitions file +require 'fileutils' +require 'process_executer' +require 'stringio' +require 'tmpdir' + +# Around hook to ensure the temp directory is cleaned up +RSpec.configure do |config| + config.around(:each, type: :feature) do |example| + @project_dir = Dir.pwd + Dir.mktmpdir do |temp_dir| + @temp_dir = temp_dir + Dir.chdir(temp_dir) do + example.run + end + end + end +end + +# rubocop:disable Style/TrivialAccessors +def project_dir = @project_dir +def temp_dir = @temp_dir +def gem_project_dir = @gem_project_dir +def gem_bytes_script_name = @gem_bytes_script_name +def gem_bytes_script_path = @gem_bytes_script_path +def gemspec_path = @gemspec_path +def env = { 'BUNDLE_IGNORE_CONFIG' => 'TRUE' } +def command_out = @command_out +def command_err = @command_err +def command_status = @command_status +# rubocop:enable Style/TrivialAccessors + +def run_command(command, raise_on_fail: true, failure_message: "#{command[0]} failed") + out_buffer = StringIO.new + out_pipe = ProcessExecuter::MonitoredPipe.new(out_buffer) + err_buffer = StringIO.new + err_pipe = ProcessExecuter::MonitoredPipe.new(err_buffer) + + ProcessExecuter.spawn(env, *command, timeout: 5, out: out_pipe, err: err_pipe).tap do |status| + @command_out = out_buffer.string + @command_err = err_buffer.string + @command_status = status + + raise "#{failure_message}: #{command_err}" if raise_on_fail && !command_status.success? + end +end + +step 'a gem project named :gem_name with the bundler-gem_bytes plugin installed' do |gem_name| + @gem_project_dir = File.join(temp_dir, gem_name) + + command = [ + 'bundle', 'gem', gem_name, '--no-test', '--no-ci', '--no-mit', '--no-coc', '--no-linter', '--no-changelog' + ] + run_command(command, failure_message: 'Failed to create gem project') + + Dir.chdir(gem_project_dir) do + command = ['bundle', 'plugin', 'install', '--path', project_dir, 'bundler-gem_bytes'] + run_command(command, failure_message: 'Failed to install plugin') + end +end + +step 'the project has a gemspec containing:' do |content| + @gemspec_path = File.join(@gem_project_dir, "#{File.basename(gem_project_dir)}.gemspec") + File.write gemspec_path, content +end + +step 'a gem-bytes script :gem_bytes_script_name containing:' do |gem_bytes_script_name, content| + @gem_bytes_script_name = gem_bytes_script_name + @gem_bytes_script_path = File.join(@gem_project_dir, gem_bytes_script_name) + File.write gem_bytes_script_path, content +end + +step 'I run :command' do |command| + Dir.chdir(gem_project_dir) do + run_command(command.split, fail_on_error: false) + end +end + +step 'the command should have succeeded' do + expect(command_status.success?).to eq(true) +end + +step 'the command should have failed' do + expect(command_status.success?).to eq(false) +end + +step 'the gemspec should contain:' do |content| + expect(File.read(gemspec_path)).to eq(content) +end + +step 'the command stderr should contain :content' do |content| + expect(command_err).to include(content) +end diff --git a/spec/features/add_dependency.feature b/spec/features/add_dependency.feature new file mode 100644 index 0000000..c60e80c --- /dev/null +++ b/spec/features/add_dependency.feature @@ -0,0 +1,70 @@ +Feature: Add or update a dependency to the project's gemspec + + Scenario: Add a dependency + + Given a gem project named "foo" with the bundler-gem_bytes plugin installed + And the project has a gemspec containing: + """ + Gem::Specification.new do |spec| + spec.name = 'foo' + spec.version = '1.0' + end + """ + And a gem-bytes script "gem_bytes_script" containing: + """ + add_dependency :runtime, 'foo', '>= 1.0' + """ + When I run "bundle gem-bytes gem_bytes_script" + Then the command should have succeeded + And the gemspec should contain: + """ + Gem::Specification.new do |spec| + spec.name = 'foo' + spec.version = '1.0' + spec.add_dependency 'foo', '>= 1.0' + end + """ + + Scenario: Update a dependency + + Given a gem project named "foo" with the bundler-gem_bytes plugin installed + And the project has a gemspec containing: + """ + Gem::Specification.new do |spec| + spec.name = 'foo' + spec.version = '1.0' + spec.add_dependency 'foo', '>= 0.9' + end + """ + And a gem-bytes script "gem_bytes_script" containing: + """ + add_dependency :runtime, 'foo', '>= 1.0' + """ + When I run "bundle gem-bytes gem_bytes_script" + Then the command should have succeeded + And the gemspec should contain: + """ + Gem::Specification.new do |spec| + spec.name = 'foo' + spec.version = '1.0' + spec.add_dependency 'foo', '>= 1.0' + end + """ + + Scenario: Update a dependency with conflicting dependency type + Given a gem project named "foo" with the bundler-gem_bytes plugin installed + And the project has a gemspec containing: + """ + Gem::Specification.new do |spec| + spec.name = 'foo' + spec.version = '1.0' + spec.add_runtime_dependency 'foo', '>= 0.9' + end + """ + And a gem-bytes script "gem_bytes_script" containing: + """ + add_dependency :runtime, 'foo', '>= 1.0' + """ + When I run "bundle gem-bytes gem_bytes_script" + Then the command should have failed + And the command stderr should contain "the dependency type is different" diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index b8a51b4..6d98b8c 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,5 +1,12 @@ # frozen_string_literal: true +# Turnip setup +# +require 'turnip/rspec' +Dir[File.join(__dir__, '**/*_steps.rb')].each { |f| require f } + +# RSpec setup +# RSpec.configure do |config| # Enable flags like --only-failures and --next-failure config.example_status_persistence_file_path = '.rspec_status' @@ -12,7 +19,21 @@ end end +# SimpleCov configuration +# +require 'simplecov' +require 'simplecov-lcov' require 'simplecov-rspec' -SimpleCov::RSpec.start + +def ci_build? = ENV.fetch('GITHUB_ACTIONS', 'false') == 'true' + +if ci_build? + SimpleCov.formatters = [ + SimpleCov::Formatter::HTMLFormatter, + SimpleCov::Formatter::LcovFormatter + ] +end + +SimpleCov::RSpec.start(list_uncovered_lines: ci_build?) require 'bundler/gem_bytes'