From 3464192479d6b19602c497c96dca50a7f1dbd831 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20=C5=9Awi=C4=85tkowski?= Date: Thu, 22 Jan 2026 00:36:04 +0100 Subject: [PATCH] Replace exits with throws Commands calling `exit` directly cause instability in test runs, which also exit early. This leads to false positives. There was a mechanism injecting alternative exit mock, but you had to remember to use it every time. We already fixed all (?) the cases once, but the problem came back. --- exe/hanami | 17 +++--- lib/hanami/cli/commands/app/db/create.rb | 4 +- lib/hanami/cli/commands/app/db/drop.rb | 4 +- lib/hanami/cli/commands/app/db/migrate.rb | 5 +- lib/hanami/cli/commands/app/db/prepare.rb | 4 +- lib/hanami/cli/commands/app/db/rollback.rb | 48 +++++++---------- .../cli/commands/app/db/structure/dump.rb | 4 +- .../cli/commands/app/db/structure/load.rb | 4 +- lib/hanami/cli/commands/app/run.rb | 7 +-- .../hanami/cli/commands/app/db/create_spec.rb | 25 +++++---- .../hanami/cli/commands/app/db/drop_spec.rb | 21 +++++--- .../cli/commands/app/db/prepare_spec.rb | 7 ++- .../cli/commands/app/db/rollback_spec.rb | 52 +++++++++++-------- .../commands/app/db/structure/dump_spec.rb | 21 +++++--- .../commands/app/db/structure/load_spec.rb | 14 +++-- spec/unit/hanami/cli/commands/app/run_spec.rb | 26 ++++++---- 16 files changed, 147 insertions(+), 116 deletions(-) diff --git a/exe/hanami b/exe/hanami index 76aac463..b55e2f79 100755 --- a/exe/hanami +++ b/exe/hanami @@ -7,9 +7,14 @@ require "hanami/cli" cli = Dry::CLI.new(Hanami::CLI) Hanami::CLI::Bundler.require(:cli) -begin - cli.call -rescue Hanami::CLI::Error => exception - $stderr.puts(exception.message) # rubocop:disable Style/StderrPuts - exit(1) -end +code = catch(:exit) { + begin + cli.call + 0 + rescue Hanami::CLI::Error => exception + $stderr.puts(exception.message) # rubocop:disable Style/StderrPuts + 1 + end +} + +exit(code) diff --git a/lib/hanami/cli/commands/app/db/create.rb b/lib/hanami/cli/commands/app/db/create.rb index 385b08e3..564fa189 100644 --- a/lib/hanami/cli/commands/app/db/create.rb +++ b/lib/hanami/cli/commands/app/db/create.rb @@ -11,7 +11,7 @@ class Create < DB::Command option :gateway, required: false, desc: "Use database for gateway" - def call(app: false, slice: nil, gateway: nil, command_exit: method(:exit), **) + def call(app: false, slice: nil, gateway: nil, **) exit_codes = [] databases(app: app, slice: slice, gateway: gateway).each do |database| @@ -27,7 +27,7 @@ def call(app: false, slice: nil, gateway: nil, command_exit: method(:exit), **) end exit_codes.each do |code| - break command_exit.(code) if code > 0 + throw(:exit, code) if code > 0 end re_run_development_command_in_test diff --git a/lib/hanami/cli/commands/app/db/drop.rb b/lib/hanami/cli/commands/app/db/drop.rb index 227e5405..d9fd9f9a 100644 --- a/lib/hanami/cli/commands/app/db/drop.rb +++ b/lib/hanami/cli/commands/app/db/drop.rb @@ -11,7 +11,7 @@ class Drop < DB::Command option :gateway, required: false, desc: "Use database for gateway" - def call(app: false, slice: nil, gateway: nil, command_exit: method(:exit), **) + def call(app: false, slice: nil, gateway: nil, **) exit_codes = [] databases(app: app, slice: slice, gateway: gateway).each do |database| @@ -27,7 +27,7 @@ def call(app: false, slice: nil, gateway: nil, command_exit: method(:exit), **) end exit_codes.each do |code| - break command_exit.(code) if code > 0 + throw(:exit, code) if code > 0 end re_run_development_command_in_test diff --git a/lib/hanami/cli/commands/app/db/migrate.rb b/lib/hanami/cli/commands/app/db/migrate.rb index 698c5c7b..3f53b500 100644 --- a/lib/hanami/cli/commands/app/db/migrate.rb +++ b/lib/hanami/cli/commands/app/db/migrate.rb @@ -17,7 +17,7 @@ class Migrate < DB::Command default: true, desc: "Dump the database structure after migrating" - def call(target: nil, app: false, slice: nil, gateway: nil, dump: true, command_exit: method(:exit), **) + def call(target: nil, app: false, slice: nil, gateway: nil, dump: true, **) databases(app: app, slice: slice, gateway: gateway).each do |database| if migrations_dir_missing?(database) warn_on_missing_migrations_dir(database) @@ -32,8 +32,7 @@ def call(target: nil, app: false, slice: nil, gateway: nil, dump: true, command_ if dump && !re_running_in_test? run_command( Structure::Dump, - app: app, slice: slice, gateway: gateway, - command_exit: command_exit + app: app, slice: slice, gateway: gateway ) end diff --git a/lib/hanami/cli/commands/app/db/prepare.rb b/lib/hanami/cli/commands/app/db/prepare.rb index 67cab143..fd50f7c7 100644 --- a/lib/hanami/cli/commands/app/db/prepare.rb +++ b/lib/hanami/cli/commands/app/db/prepare.rb @@ -35,7 +35,7 @@ def call(app: false, slice: nil, **) # rubocop:disable Metrics/AbcSize nil end - return exit exit_code if exit_code.to_i > 1 + throw(:exit, exit_code) if exit_code.to_i > 1 end # Once all databases are created, the migrator will properly load for each slice, and @@ -54,7 +54,7 @@ def call(app: false, slice: nil, **) # rubocop:disable Metrics/AbcSize nil end - return exit exit_code if exit_code.to_i > 1 + throw(:exit, exit_code) if exit_code.to_i > 1 end # Finally, load the seeds for the slice overall, which is a once-per-slice operation. diff --git a/lib/hanami/cli/commands/app/db/rollback.rb b/lib/hanami/cli/commands/app/db/rollback.rb index cc84b74a..c206ecbf 100644 --- a/lib/hanami/cli/commands/app/db/rollback.rb +++ b/lib/hanami/cli/commands/app/db/rollback.rb @@ -24,7 +24,6 @@ def call( gateway: nil, target: nil, dump: true, - command_exit: method(:exit), ** ) # We allow either a number of steps or a target migration number to be provided @@ -33,7 +32,7 @@ def call( target = steps if steps && !target && !code_is_number?(steps) steps_count = steps && code_is_number?(steps) ? Integer(steps) : 1 - database = resolve_target_database(app: app, slice: slice, gateway: gateway, command_exit: command_exit) + database = resolve_target_database(app: app, slice: slice, gateway: gateway) return unless database migration_code, migration_name = find_migration_target(target, steps_count, database) @@ -61,8 +60,7 @@ def call( Structure::Dump, app: database.slice == self.app, slice: database.slice == self.app ? nil : database.slice.slice_name.to_s, - gateway: database.gateway_name == :default ? nil : database.gateway_name.to_s, - command_exit: command_exit + gateway: database.gateway_name == :default ? nil : database.gateway_name.to_s ) end @@ -72,24 +70,23 @@ def call( private - def resolve_target_database(app:, slice:, gateway:, command_exit:) + def resolve_target_database(app:, slice:, gateway:) if gateway && !app && !slice err.puts "When specifying --gateway, an --app or --slice must also be given" - command_exit.(1) - return + throw :exit, 1 end if slice - resolve_slice_database(slice, gateway, command_exit) + resolve_slice_database(slice, gateway) elsif app - resolve_app_database(gateway, command_exit) + resolve_app_database(gateway) else - resolve_default_database(command_exit) + resolve_default_database end end - def resolve_slice_database(slice_name, gateway, command_exit) - slice = resolve_slice(slice_name, command_exit) + def resolve_slice_database(slice_name, gateway) + slice = resolve_slice(slice_name) return unless slice databases = build_databases(slice) @@ -98,43 +95,41 @@ def resolve_slice_database(slice_name, gateway, command_exit) database = databases[gateway.to_sym] unless database err.puts %(No gateway "#{gateway}" found in slice "#{slice_name}") - command_exit.(1) - return + throw :exit, 1 end database elsif databases.size == 1 databases.values.first else err.puts "Multiple gateways found in slice #{slice_name}. Please specify --gateway option." - command_exit.(1) + throw :exit, 1 end end - def resolve_app_database(gateway, command_exit) + def resolve_app_database(gateway) databases = build_databases(app) if gateway database = databases[gateway.to_sym] unless database err.puts %(No gateway "#{gateway}" found in app) - command_exit.(1) - return + throw :exit, 1 end database elsif databases.size == 1 databases.values.first else err.puts "Multiple gateways found in app. Please specify --gateway option." - command_exit.(1) + throw :exit, 1 end end - def resolve_default_database(command_exit) + def resolve_default_database all_dbs = all_databases if all_dbs.empty? err.puts "No databases found" - command_exit.(1) + throw :exit, 1 elsif all_dbs.size == 1 all_dbs.first else @@ -143,24 +138,21 @@ def resolve_default_database(command_exit) app_databases.values.first elsif app_databases.size > 1 err.puts "Multiple gateways found in app. Please specify --gateway option." - command_exit.(1) - nil + throw :exit, 1 else err.puts "Multiple database contexts found. Please specify --app or --slice option." - command_exit.(1) - nil + throw :exit, 1 end end end - def resolve_slice(slice_name, command_exit) + def resolve_slice(slice_name) slice_name_sym = inflector.underscore(Shellwords.shellescape(slice_name)).to_sym slice = app.slices[slice_name_sym] unless slice err.puts %(Slice "#{slice_name}" not found) - command_exit.(1) - return + throw :exit, 1 end ensure_database_slice(slice) diff --git a/lib/hanami/cli/commands/app/db/structure/dump.rb b/lib/hanami/cli/commands/app/db/structure/dump.rb index 5a58a9a0..cb8aa80f 100644 --- a/lib/hanami/cli/commands/app/db/structure/dump.rb +++ b/lib/hanami/cli/commands/app/db/structure/dump.rb @@ -14,7 +14,7 @@ class Dump < DB::Command option :gateway, required: false, desc: "Use database for gateway" # @api private - def call(app: false, slice: nil, gateway: nil, command_exit: method(:exit), **) # rubocop:disable Metrics/AbcSize + def call(app: false, slice: nil, gateway: nil, **) # rubocop:disable Metrics/AbcSize exit_codes = [] databases(app: app, slice: slice, gateway: gateway).each do |database| @@ -48,7 +48,7 @@ def call(app: false, slice: nil, gateway: nil, command_exit: method(:exit), **) end exit_codes.each do |code| - break command_exit.(code) if code > 0 + throw(:exit, code) if code > 0 end end end diff --git a/lib/hanami/cli/commands/app/db/structure/load.rb b/lib/hanami/cli/commands/app/db/structure/load.rb index 25999a9c..6f935f0a 100644 --- a/lib/hanami/cli/commands/app/db/structure/load.rb +++ b/lib/hanami/cli/commands/app/db/structure/load.rb @@ -17,7 +17,7 @@ class Load < DB::Command option :gateway, required: false, desc: "Use database for gateway" # @api private - def call(app: false, slice: nil, gateway: nil, command_exit: method(:exit), **) # rubocop:disable Metrics/AbcSize + def call(app: false, slice: nil, gateway: nil, **) # rubocop:disable Metrics/AbcSize exit_codes = [] databases(app: app, slice: slice, gateway: gateway).each do |database| @@ -42,7 +42,7 @@ def call(app: false, slice: nil, gateway: nil, command_exit: method(:exit), **) end exit_codes.each do |code| - break command_exit.(code) if code > 0 + throw(:exit, code) if code > 0 end re_run_development_command_in_test diff --git a/lib/hanami/cli/commands/app/run.rb b/lib/hanami/cli/commands/app/run.rb index e50026a6..67b5f9fa 100644 --- a/lib/hanami/cli/commands/app/run.rb +++ b/lib/hanami/cli/commands/app/run.rb @@ -32,11 +32,6 @@ class Run < Hanami::CLI::Command argument :code_or_path, required: true, desc: "Path to a Ruby file or inline Ruby code to be executed" - def initialize(command_exit: method(:exit), **opts) - super(**opts) - @command_exit = command_exit - end - # rubocop:disable Metrics/AbcSize def call(code_or_path:, **) require "hanami/prepare" @@ -60,7 +55,7 @@ def call(code_or_path:, **) end end rescue RunError - @command_exit.call(1) + throw :exit, 1 end # rubocop:enable Metrics/AbcSize diff --git a/spec/unit/hanami/cli/commands/app/db/create_spec.rb b/spec/unit/hanami/cli/commands/app/db/create_spec.rb index eb4d12d3..5dff2d02 100644 --- a/spec/unit/hanami/cli/commands/app/db/create_spec.rb +++ b/spec/unit/hanami/cli/commands/app/db/create_spec.rb @@ -11,17 +11,10 @@ let(:system_call) { Hanami::CLI::SystemCall.new } let(:test_env_executor) { instance_spy(Hanami::CLI::InteractiveSystemCall) } - let(:exit_double) { double(:exit_method) } let(:out) { StringIO.new } def output = out.string - before do - # Prevent the command from exiting the spec run in the case of unexpected system call failures - allow(command).to receive(:exit) - allow(exit_double).to receive(:call) - end - before do @env = ENV.to_h allow(Hanami::Env).to receive(:loaded?).and_return(false) @@ -95,7 +88,7 @@ def before_prepare end it "does not create the database if it already exists" do - command.run_command(Hanami::CLI::Commands::App::DB::Create, command_exit: exit_double) + command.run_command(Hanami::CLI::Commands::App::DB::Create) out.truncate(0) command.call @@ -120,7 +113,7 @@ def before_prepare end it "does not create the database if it already exists" do - command.run_command(Hanami::CLI::Commands::App::DB::Create, command_exit: exit_double) + command.run_command(Hanami::CLI::Commands::App::DB::Create) out.truncate(0) command.call @@ -190,7 +183,10 @@ def before_prepare .with(a_string_matching(/sqlite3.+app.sqlite3/)) .and_return Hanami::CLI::SystemCall::Result.new(exit_code: 2, out: "", err: "app-db-err") - command.call + exit_code = catch(:exit) { + command.call + 0 + } expect { Main::Slice["db.gateway"] }.not_to raise_error @@ -202,7 +198,7 @@ def before_prepare expect(output).to include "database db/main.sqlite3 created" - expect(command).to have_received(:exit).with(2).once + expect(exit_code).to eq(2) end context "app with gateways" do @@ -283,7 +279,10 @@ def before_prepare .with(a_string_matching(/createdb.+_app/), anything) .and_return Hanami::CLI::SystemCall::Result.new(exit_code: 2, out: "", err: "app-db-err") - command.call + exit_code = catch(:exit) { + command.call + 0 + } expect { Hanami.app["db.gateway"] }.to raise_error Sequel::DatabaseConnectionError expect { Main::Slice["db.gateway"] }.not_to raise_error @@ -293,7 +292,7 @@ def before_prepare expect(output).to include "database #{POSTGRES_BASE_DB_NAME}_main created" - expect(command).to have_received(:exit).with(2).once + expect(exit_code).to eq(2) end end diff --git a/spec/unit/hanami/cli/commands/app/db/drop_spec.rb b/spec/unit/hanami/cli/commands/app/db/drop_spec.rb index 8bfa4003..b0b82c77 100644 --- a/spec/unit/hanami/cli/commands/app/db/drop_spec.rb +++ b/spec/unit/hanami/cli/commands/app/db/drop_spec.rb @@ -135,7 +135,10 @@ def before_prepare .with(a_string_including("db/app.sqlite3")) .and_raise Errno::EACCES - command.call + exit_code = catch(:exit) { + command.call + 0 + } expect(File.exist?(@dir.join("db", "app.sqlite3"))).to be true expect(File.exist?(@dir.join("db", "main.sqlite3"))).to be false @@ -145,7 +148,7 @@ def before_prepare expect(output).to include "database db/main.sqlite3 dropped" - expect(command).to have_received(:exit).with(1).once + expect(exit_code).to eq(1) end context "app and slice with gateways" do @@ -351,7 +354,10 @@ def before_prepare .with(a_string_matching(/dropdb.+_app/), anything) .and_return Hanami::CLI::SystemCall::Result.new(exit_code: 2, out: "", err: "app-db-err") - command.call + exit_code = catch(:exit) { + command.call + 0 + } expect { Hanami.app["db.gateway"].connection.test_connection }.not_to raise_error @@ -360,7 +366,7 @@ def before_prepare expect(output).to include "database #{POSTGRES_BASE_DB_NAME}_main dropped" - expect(command).to have_received(:exit).with(2).once + expect(exit_code).to eq(2) end it "raises exception when DB existence check fails" do @@ -420,14 +426,17 @@ def before_prepare .with(a_string_matching(/-e "DROP DATABASE/), anything) .and_return Hanami::CLI::SystemCall::Result.new(exit_code: 2, out: "", err: "app-db-err") - command.call + exit_code = catch(:exit) { + command.call + 0 + } expect { Hanami.app["db.gateway"].connection.test_connection }.not_to raise_error expect(output).to include "failed to drop database #{POSTGRES_BASE_DB_NAME}_app" expect(output).to include "app-db-err" - expect(command).to have_received(:exit).with(2).once + expect(exit_code).to eq(2) end it "prints errors when check for DB existence fails" do diff --git a/spec/unit/hanami/cli/commands/app/db/prepare_spec.rb b/spec/unit/hanami/cli/commands/app/db/prepare_spec.rb index fb628481..087e50ed 100644 --- a/spec/unit/hanami/cli/commands/app/db/prepare_spec.rb +++ b/spec/unit/hanami/cli/commands/app/db/prepare_spec.rb @@ -419,7 +419,10 @@ class Comments < Hanami::DB::Relation .with(a_string_matching(/createdb.+_main/), anything) .and_return Hanami::CLI::SystemCall::Result.new(exit_code: 2, out: "", err: "main-db-err") - command.call + exit_code = catch(:exit) { + command.call + 0 + } expect { Hanami.app["relations.posts"] }.to raise_error Sequel::DatabaseError expect { Main::Slice["relations.comments"] }.to raise_error Sequel::DatabaseError @@ -433,7 +436,7 @@ class Comments < Hanami::DB::Relation expect(output).not_to include "migrated" expect(output).not_to include "seed data loaded" - expect(command).to have_received(:exit).with 2 + expect(exit_code).to eq(2) end end diff --git a/spec/unit/hanami/cli/commands/app/db/rollback_spec.rb b/spec/unit/hanami/cli/commands/app/db/rollback_spec.rb index 7647acf4..f5f094fd 100644 --- a/spec/unit/hanami/cli/commands/app/db/rollback_spec.rb +++ b/spec/unit/hanami/cli/commands/app/db/rollback_spec.rb @@ -16,28 +16,34 @@ shared_examples "multiple gateways error handling" do |db_type| it "fails with clear error message when multiple gateways exist without specification" do - exit_mock = instance_double("Method", call: nil) - allow(exit_mock).to receive(:call).with(1).and_raise(SystemExit) + exit_code = catch(:exit) { + command.call + 0 + } - expect { command.call(command_exit: exit_mock) }.to raise_error(SystemExit) + expect(exit_code).to eq(1) expect(output).to include "Multiple gateways found in app. Please specify --gateway option." end end shared_examples "invalid argument handling" do it "fails when gateway is specified without app or slice" do - exit_mock = instance_double("Method", call: nil) - allow(exit_mock).to receive(:call).with(1).and_raise(SystemExit) + exit_code = catch(:exit) { + command.call(gateway: "default") + 0 + } - expect { command.call(gateway: "default", command_exit: exit_mock) }.to raise_error(SystemExit) + expect(exit_code).to eq(1) expect(output).to include "When specifying --gateway, an --app or --slice must also be given" end it "fails when gateway does not exist" do - exit_mock = instance_double("Method", call: nil) - allow(exit_mock).to receive(:call).with(1).and_raise(SystemExit) + exit_code = catch(:exit) { + command.call(app: true, gateway: "nonexistent") + 0 + } - expect { command.call(app: true, gateway: "nonexistent", command_exit: exit_mock) }.to raise_error(SystemExit) + expect(exit_code).to eq(1) expect(output).to include %(No gateway "nonexistent" found in app) end end @@ -63,10 +69,6 @@ def output = out.string + err.string allow(Hanami::CLI::Commands::App::DB::Structure::Dump).to receive(:new) { dump_command } end - before do - allow(command).to receive(:exit) - end - before do @env = ENV.to_h allow(Hanami::Env).to receive(:loaded?).and_return(false) @@ -154,20 +156,24 @@ class App < Hanami::App context "with invalid args combinations" do context "when gateway is specified without app or slice" do it "does not allow ambiguity, asks for details" do - exit_mock = instance_double("Method", call: nil) - allow(exit_mock).to receive(:call).with(1).and_raise(SystemExit) + exit_code = catch(:exit) { + command.call(gateway: "default") + 0 + } - expect { command.call(gateway: "default", command_exit: exit_mock) }.to raise_error(SystemExit) + expect(exit_code).to eq(1) expect(output).to include "When specifying --gateway, an --app or --slice must also be given" end end context "when gateway that does not exist in the context is specified" do it "informs about invalid gateway" do - exit_mock = instance_double("Method", call: nil) - allow(exit_mock).to receive(:call).with(1).and_raise(SystemExit) + exit_code = catch(:exit) { + command.call(app: true, gateway: "nonexistent") + 0 + } - expect { command.call(app: true, gateway: "nonexistent", command_exit: exit_mock) }.to raise_error(SystemExit) + expect(exit_code).to eq(1) expect(output).to include %(No gateway "nonexistent" found in app) end end @@ -298,10 +304,12 @@ class App < Hanami::App context "with no arguments" do it "asks for more detailed prompt" do - exit_mock = instance_double("Method", call: nil) - allow(exit_mock).to receive(:call).with(1).and_raise(SystemExit) + exit_code = catch(:exit) { + command.call + 0 + } - expect { command.call(command_exit: exit_mock) }.to raise_error(SystemExit) + expect(exit_code).to eq(1) expect(output).to include "Multiple gateways found in app. Please specify --gateway option." end end diff --git a/spec/unit/hanami/cli/commands/app/db/structure/dump_spec.rb b/spec/unit/hanami/cli/commands/app/db/structure/dump_spec.rb index 0ef061cb..7babde96 100644 --- a/spec/unit/hanami/cli/commands/app/db/structure/dump_spec.rb +++ b/spec/unit/hanami/cli/commands/app/db/structure/dump_spec.rb @@ -205,7 +205,10 @@ def before_prepare .with(a_string_including("db/app.sqlite3")) .and_return Hanami::CLI::SystemCall::Result.new(exit_code: 2, out: "", err: "dump-err") - command.call + exit_code = catch(:exit) { + command.call + 0 + } expect(Main::Slice.root.join("config", "db", "structure.sql").exist?).to be true expect(Hanami.app.root.join("config", "db", "structure.sql").exist?).to be false @@ -213,7 +216,7 @@ def before_prepare expect(output).to include %("db/app.sqlite3 structure dumped to config/db/structure.sql" FAILED) expect(output).to include "db/main.sqlite3 structure dumped to slices/main/config/db/structure.sql" - expect(command).to have_received(:exit).with 2 + expect(exit_code).to eq(2) end end @@ -289,7 +292,10 @@ def before_prepare .with(a_string_including("app"), anything) .and_return Hanami::CLI::SystemCall::Result.new(exit_code: 2, out: "", err: "dump-err") - command.call + exit_code = catch(:exit) { + command.call + 0 + } expect(Main::Slice.root.join("config", "db", "structure.sql").exist?).to be true expect(Hanami.app.root.join("config", "db", "structure.sql").exist?).to be false @@ -297,7 +303,7 @@ def before_prepare expect(output).to include %("#{POSTGRES_BASE_DB_NAME}_app structure dumped to config/db/structure.sql" FAILED) expect(output).to include "#{POSTGRES_BASE_DB_NAME}_main structure dumped to slices/main/config/db/structure.sql" - expect(command).to have_received(:exit).with 2 + expect(exit_code).to eq(2) end end @@ -330,13 +336,16 @@ def before_prepare .with(a_string_including("mysqldump"), anything) .and_return Hanami::CLI::SystemCall::Result.new(exit_code: 2, out: "", err: "dump-err") - command.call + exit_code = catch(:exit) { + command.call + 0 + } expect(Hanami.app.root.join("config", "db", "structure.sql").exist?).to be false expect(output).to include %("#{MYSQL_BASE_DB_NAME}_app structure dumped to config/db/structure.sql" FAILED) - expect(command).to have_received(:exit).with 2 + expect(exit_code).to eq(2) end end end diff --git a/spec/unit/hanami/cli/commands/app/db/structure/load_spec.rb b/spec/unit/hanami/cli/commands/app/db/structure/load_spec.rb index 7e7d48f3..6433f84f 100644 --- a/spec/unit/hanami/cli/commands/app/db/structure/load_spec.rb +++ b/spec/unit/hanami/cli/commands/app/db/structure/load_spec.rb @@ -237,7 +237,10 @@ def before_prepare .with(a_string_including("#{POSTGRES_BASE_DB_NAME}_app"), anything) .and_return Hanami::CLI::SystemCall::Result.new(exit_code: 2, out: "", err: "app-load-err") - command.call + exit_code = catch(:exit) { + command.call + 0 + } expect(Hanami.app["db.gateway"].connection.tables.include?(:posts)).to be false expect(Main::Slice["db.gateway"].connection.tables.include?(:comments)).to be true @@ -245,7 +248,7 @@ def before_prepare expect(output).to include %("#{POSTGRES_BASE_DB_NAME}_app structure loaded from config/db/structure.sql" FAILED) expect(output).to include "#{POSTGRES_BASE_DB_NAME}_main structure loaded from slices/main/config/db/structure.sql" - expect(command).to have_received(:exit).with 2 + expect(exit_code).to eq(2) end end @@ -273,13 +276,16 @@ def before_prepare .with(a_string_including("#{MYSQL_BASE_DB_NAME}_app"), anything) .and_return Hanami::CLI::SystemCall::Result.new(exit_code: 2, out: "", err: "app-load-err") - command.call + exit_code = catch(:exit) { + command.call + 0 + } expect(Hanami.app["db.gateway"].connection.tables.include?(:posts)).to be false expect(output).to include %("#{MYSQL_BASE_DB_NAME}_app structure loaded from config/db/structure.sql" FAILED) - expect(command).to have_received(:exit).with 2 + expect(exit_code).to eq(2) end end diff --git a/spec/unit/hanami/cli/commands/app/run_spec.rb b/spec/unit/hanami/cli/commands/app/run_spec.rb index 616f857d..16320762 100644 --- a/spec/unit/hanami/cli/commands/app/run_spec.rb +++ b/spec/unit/hanami/cli/commands/app/run_spec.rb @@ -4,16 +4,13 @@ require "hanami/cli/commands/app/run" RSpec.describe Hanami::CLI::Commands::App::Run do - subject { described_class.new(out: out, err: err, command_exit: command_exit) } + subject { described_class.new(out: out, err: err) } let(:out) { StringIO.new } let(:err) { StringIO.new } - let(:command_exit) { double(:command_exit) } before do # Mock the hanami/prepare requirement allow(subject).to receive(:require).with("hanami/prepare") - - allow(command_exit).to receive(:call) end describe "#call" do @@ -42,28 +39,37 @@ context "with syntax errors" do it "prints error message and exits with code 1" do - subject.call(code_or_path: "puts 'unclosed string") + exit_code = catch(:exit) { + subject.call(code_or_path: "puts 'unclosed string") + 0 + } expect(err.string).to include("Syntax error in code") - expect(command_exit).to have_received(:call).with(1) + expect(exit_code).to eq(1) end end context "with name errors" do it "prints error message and exits with code 1" do - subject.call(code_or_path: "undefined_variable") + exit_code = catch(:exit) { + subject.call(code_or_path: "undefined_variable") + 0 + } expect(err.string).to include("Name error in code") - expect(command_exit).to have_received(:call).with(1) + expect(exit_code).to eq(1) end end context "with runtime errors" do it "prints error message and exits with code 1" do - subject.call(code_or_path: "1 / 0") + exit_code = catch(:exit) { + subject.call(code_or_path: "1 / 0") + 0 + } expect(err.string).to include("Error executing code") - expect(command_exit).to have_received(:call).with(1) + expect(exit_code).to eq(1) end end end