Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 1 addition & 8 deletions bin/falcon
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,4 @@

require_relative "../lib/falcon/command"

begin
Falcon::Command.call
rescue Interrupt
# Ignore.
rescue => error
Console.error(Falcon::Command, error)
exit! 1
end
Falcon::Command.call
5 changes: 1 addition & 4 deletions bin/falcon-host
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,4 @@

require_relative "../lib/falcon/command/host"

begin
Falcon::Command::Host.call
rescue Interrupt
end
Falcon::Command::Host.call
5 changes: 5 additions & 0 deletions config/covered.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# frozen_string_literal: true

def ignore_paths
super + ["examples/"]
end
1 change: 1 addition & 0 deletions gems.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@

gem "sus-fixtures-async"
gem "sus-fixtures-async-http"
gem "sus-fixtures-console"
gem "sus-fixtures-openssl"

gem "bake"
Expand Down
7 changes: 7 additions & 0 deletions lib/falcon/command.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,20 @@

require_relative "command/top"

require "console"

module Falcon
# @namespace
module Command
# The main entry point for the `falcon` executable.
# @parameter arguments [Array(String)] The command line arguments.
def self.call(*arguments)
Top.call(*arguments)
rescue Interrupt
# Ignore.
rescue => error
Console.error(self, error)
exit! 1
end
end
end
12 changes: 12 additions & 0 deletions lib/falcon/command/host.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

require "samovar"
require "async/service/controller"
require "console"

module Falcon
module Command
Expand All @@ -17,6 +18,17 @@ module Command
class Host < Samovar::Command
self.description = "Host the specified applications."

# The main entry point for the `falcon-host` executable.
# @parameter arguments [Array(String)] The command line arguments.
def self.call(...)
super
rescue Interrupt
# Ignore.
rescue => error
Console.error(self, error)
exit! 1
end

# One or more paths to the configuration files.
# @name paths
# @attribute [Array(String)]
Expand Down
1 change: 1 addition & 0 deletions lib/falcon/command/serve.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
require_relative "../server"
require_relative "../endpoint"
require_relative "../service/server"
require_relative "../environment/server"
require_relative "../environment/rackup"

require "async/service/configuration"
Expand Down
1 change: 1 addition & 0 deletions lib/falcon/service/virtual.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
# Copyright, 2020-2024, by Samuel Williams.

require "async/service/generic"
require "console"

module Falcon
module Service
Expand Down
39 changes: 39 additions & 0 deletions test/falcon/command.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# frozen_string_literal: true

# Released under the MIT License.
# Copyright, 2026, by Samuel Williams.

require "falcon/command"
require "sus/fixtures/console/captured_logger"

describe Falcon::Command do
include Sus::Fixtures::Console::CapturedLogger

it "reports errors from the command entry point" do
error = RuntimeError.new("boom")
captured_status = nil

mock(Falcon::Command::Top) do |top|
top.replace(:call) do
raise error
end
end

mock(subject) do |command|
command.replace(:exit!) do |status|
captured_status = status
end
end

subject.call

expect(captured_status).to be == 1

expect_console.to have_logged(
severity: be == :error,
subject: be == subject,
arguments: be == [error],
message: be == "boom",
)
end
end
66 changes: 66 additions & 0 deletions test/falcon/command/host.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# frozen_string_literal: true

# Released under the MIT License.
# Copyright, 2026, by Samuel Williams.

require "falcon/command/host"
require "sus/fixtures/console/captured_logger"

describe Falcon::Command::Host do
include Sus::Fixtures::Console::CapturedLogger

let(:command) do
subject[]
end

it "runs the controller" do
captured_configuration = nil
captured_container_class = nil

mock(Async::Service::Controller) do |controller|
controller.replace(:run) do |configuration, container_class:|
captured_configuration = configuration
captured_container_class = container_class
end
end

command.call

expect(captured_configuration).to be_a(Async::Service::Configuration)
expect(captured_container_class).to be == command.container_class

expect_console.to have_logged(
severity: be == :info,
subject: be == command,
message: be(:include?, "Falcon Host v"),
)
end

it "reports errors from the host entry point" do
error = RuntimeError.new("boom")
captured_status = nil

mock(Async::Service::Controller) do |controller|
controller.replace(:run) do |configuration, container_class:|
raise error
end
end

mock(subject) do |host|
host.replace(:exit!) do |status|
captured_status = status
end
end

subject.call(["missing.rb"])

expect(captured_status).to be == 1

expect_console.to have_logged(
severity: be == :error,
subject: be == subject,
arguments: be == [error],
message: be == "boom",
)
end
end
68 changes: 68 additions & 0 deletions test/falcon/command/proxy.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# frozen_string_literal: true

# Released under the MIT License.
# Copyright, 2026, by Samuel Williams.

require "falcon/command/proxy"
require "sus/fixtures/console/captured_logger"

describe Falcon::Command::Proxy do
include Sus::Fixtures::Console::CapturedLogger

let(:command) do
subject[
"--bind", "https://localhost:8496",
"--timeout", "1",
]
end

it "builds an endpoint helper" do
expect(command.endpoint).to be_a(Async::HTTP::Endpoint)
end

it "logs resolved paths when running" do
command = subject[
"--bind", "https://localhost:8496",
"test/falcon/command/config.ru",
]

expect(command).to receive(:configuration).and_return(Async::Service::Configuration.new)
captured_configuration = nil

mock(Async::Service::Controller) do |controller|
controller.replace(:run) do |configuration|
captured_configuration = configuration
end
end

command.call

expect(captured_configuration).to be_a(Async::Service::Configuration)

expect_console.to have_logged(
severity: be == :info,
subject: be == command,
message: be(:include?, "Loading configuration from test/falcon/command/config.ru"),
)
end

it "runs the controller" do
captured_configuration = nil

mock(Async::Service::Controller) do |controller|
controller.replace(:run) do |configuration|
captured_configuration = configuration
end
end

command.call

expect(captured_configuration).to be_a(Async::Service::Configuration)

expect_console.to have_logged(
severity: be == :info,
subject: be == command,
message: be(:include?, "Falcon Proxy v"),
)
end
end
71 changes: 71 additions & 0 deletions test/falcon/command/redirect.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# frozen_string_literal: true

# Released under the MIT License.
# Copyright, 2026, by Samuel Williams.

require "falcon/command/redirect"
require "sus/fixtures/console/captured_logger"

describe Falcon::Command::Redirect do
include Sus::Fixtures::Console::CapturedLogger

let(:command) do
subject[
"--bind", "http://localhost:8095",
"--redirect", "https://localhost:8495",
"--timeout", "1",
]
end

it "builds endpoint helpers" do
expect(command.endpoint).to be_a(Async::HTTP::Endpoint)
expect(command.redirect_endpoint).to be_a(Async::HTTP::Endpoint)
end

it "logs resolved paths when running" do
command = subject[
"--bind", "http://localhost:8095",
"--redirect", "https://localhost:8495",
"test/falcon/command/config.ru",
]

expect(command).to receive(:configuration).and_return(Async::Service::Configuration.new)
captured_configuration = nil

mock(Async::Service::Controller) do |controller|
controller.replace(:run) do |configuration|
captured_configuration = configuration
end
end

command.call

expect(captured_configuration).to be_a(Async::Service::Configuration)

expect_console.to have_logged(
severity: be == :info,
subject: be == command,
message: be(:include?, "Loading configuration from test/falcon/command/config.ru"),
)
end

it "runs the controller" do
captured_configuration = nil

mock(Async::Service::Controller) do |controller|
controller.replace(:run) do |configuration|
captured_configuration = configuration
end
end

command.call

expect(captured_configuration).to be_a(Async::Service::Configuration)

expect_console.to have_logged(
severity: be == :info,
subject: be == command,
message: be(:include?, "Falcon Redirect v"),
)
end
end
Loading
Loading