From 6af272c01f3e0903059516e9bd90fdee93b89627 Mon Sep 17 00:00:00 2001 From: l5y <220195275+l5yth@users.noreply.github.com> Date: Fri, 31 Oct 2025 12:36:53 +0100 Subject: [PATCH] Handle missing instance domain outside production (#405) --- web/lib/potato_mesh/application/federation.rb | 15 ++++++ web/lib/potato_mesh/application/helpers.rb | 10 ++++ web/spec/app_spec.rb | 46 +++++++++++++++++++ 3 files changed, 71 insertions(+) diff --git a/web/lib/potato_mesh/application/federation.rb b/web/lib/potato_mesh/application/federation.rb index d344c88..d8da4bd 100644 --- a/web/lib/potato_mesh/application/federation.rb +++ b/web/lib/potato_mesh/application/federation.rb @@ -15,10 +15,25 @@ module PotatoMesh module App module Federation + # Resolve the canonical domain for the running instance. + # + # @return [String, nil] sanitized instance domain or nil outside production. + # @raise [RuntimeError] when the domain cannot be determined in production. def self_instance_domain sanitized = sanitize_instance_domain(app_constant(:INSTANCE_DOMAIN)) return sanitized if sanitized + unless production_environment? + debug_log( + "INSTANCE_DOMAIN unavailable; skipping self instance domain", + context: "federation.instances", + app_env: string_or_nil(ENV["APP_ENV"]), + rack_env: string_or_nil(ENV["RACK_ENV"]), + source: app_constant(:INSTANCE_DOMAIN_SOURCE), + ) + return nil + end + raise "INSTANCE_DOMAIN could not be determined" end diff --git a/web/lib/potato_mesh/application/helpers.rb b/web/lib/potato_mesh/application/helpers.rb index 44bc304..94375a6 100644 --- a/web/lib/potato_mesh/application/helpers.rb +++ b/web/lib/potato_mesh/application/helpers.rb @@ -334,6 +334,16 @@ module PotatoMesh ENV["RACK_ENV"] == "test" end + # Determine whether the application is running in a production environment. + # + # @return [Boolean] true when APP_ENV or RACK_ENV resolves to "production". + def production_environment? + app_env = string_or_nil(ENV["APP_ENV"])&.downcase + rack_env = string_or_nil(ENV["RACK_ENV"])&.downcase + + app_env == "production" || rack_env == "production" + end + # Determine whether federation features should be active. # # @return [Boolean] true when federation configuration allows it. diff --git a/web/spec/app_spec.rb b/web/spec/app_spec.rb index 14da9a7..967c881 100644 --- a/web/spec/app_spec.rb +++ b/web/spec/app_spec.rb @@ -813,6 +813,52 @@ RSpec.describe "Potato Mesh Sinatra app" do end end + describe ".self_instance_domain" do + around do |example| + original_app_env = ENV["APP_ENV"] + original_rack_env = ENV["RACK_ENV"] + begin + example.run + ensure + if original_app_env + ENV["APP_ENV"] = original_app_env + else + ENV.delete("APP_ENV") + end + + if original_rack_env + ENV["RACK_ENV"] = original_rack_env + else + ENV.delete("RACK_ENV") + end + end + end + + it "returns the sanitized domain when configuration is present" do + ENV.delete("APP_ENV") + stub_const("PotatoMesh::Application::INSTANCE_DOMAIN", " Example.Org ") do + expect(application_class.self_instance_domain).to eq("example.org") + end + end + + it "returns nil when the domain is unavailable outside production" do + ENV["APP_ENV"] = "development" + stub_const("PotatoMesh::Application::INSTANCE_DOMAIN", nil) do + expect(application_class.self_instance_domain).to be_nil + end + end + + it "raises when the domain is unavailable in production" do + ENV["APP_ENV"] = "production" + stub_const("PotatoMesh::Application::INSTANCE_DOMAIN", nil) do + expect { application_class.self_instance_domain }.to raise_error( + RuntimeError, + "INSTANCE_DOMAIN could not be determined", + ) + end + end + end + describe ".self_instance_registration_decision" do let(:domain) { "spec.mesh.test" }