From 3ae6b624fc3b478f5d4e2a5c1bd8f02dc1a4e66b Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Sat, 27 Jun 2026 13:23:29 +1200 Subject: [PATCH 1/2] Run container tests in async context --- fixtures/container_context.rb | 16 +++++++++++++ test/async/service/managed/service.rb | 11 +++++---- test/async/service/policy.rb | 33 +++++++++++++++++++-------- 3 files changed, 46 insertions(+), 14 deletions(-) create mode 100644 fixtures/container_context.rb diff --git a/fixtures/container_context.rb b/fixtures/container_context.rb new file mode 100644 index 0000000..24f955e --- /dev/null +++ b/fixtures/container_context.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +require "async" +require "async/container/group" + +module ContainerContext + def container_context(&block) + # Newer async-container supervises containers using Async tasks, while + # released versions can fail when forking under a scheduler on Ruby 3.x. + if Async::Container::Group.method_defined?(:supervise) + Sync(&block) + else + block.call + end + end +end diff --git a/test/async/service/managed/service.rb b/test/async/service/managed/service.rb index 4e30395..ef645d8 100644 --- a/test/async/service/managed/service.rb +++ b/test/async/service/managed/service.rb @@ -10,8 +10,11 @@ require "async" require "sus/fixtures/async/scheduler_context" +require "container_context" describe Async::Service::Managed::Service do + include ContainerContext + let(:configuration) do Async::Service::Configuration.build do service "test-container" do @@ -365,15 +368,15 @@ def name = @name let(:controller) {Async::Service::Controller.for(test_service)} it "runs service with health checking and no restarts when async context is present" do - container = Async::Container.new - - begin + container_context do + container = Async::Container.new + controller.setup(container) controller.start sleep(0.03) ensure controller.stop - container.stop + container&.stop end end end diff --git a/test/async/service/policy.rb b/test/async/service/policy.rb index 15aa627..3599e7c 100644 --- a/test/async/service/policy.rb +++ b/test/async/service/policy.rb @@ -6,8 +6,11 @@ require "async/container/best" require "async/service/policy" require "async/container/statistics" +require "container_context" describe Async::Service::Policy do + include ContainerContext + let(:policy) {subject.new(maximum_failures: 5, window: 10)} with "::DEFAULT" do @@ -161,18 +164,28 @@ def success_status.success?; true; end with "concurrent failures" do it "only stops container once when multiple children fail simultaneously" do - container = Async::Container.best_container_class.new(policy: policy) - expect(container).to receive(:stop) - - # Spawn 10 children that all fail immediately: - 10.times do |i| - container.spawn(name: "worker-#{i}") do |instance| - instance.ready! - exit(1) + container_context do + container = Async::Container.best_container_class.new(policy: policy) + stop_count = 0 + original_stop = container.method(:stop) + + container.define_singleton_method(:stop) do |*arguments| + stop_count += 1 + original_stop.call(*arguments) + end + + # Spawn 10 children that all fail immediately: + 10.times do |i| + container.spawn(name: "worker-#{i}") do |instance| + instance.ready! + exit(1) + end end + + container.wait + + expect(stop_count).to be == 1 end - - container.wait end end end From 76bc2b66b9ad9b4d9fac39da22566229d1ece380 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Sat, 27 Jun 2026 13:54:22 +1200 Subject: [PATCH 2/2] Use scheduler context for container tests --- fixtures/container_context.rb | 16 ---------- test/async/service/managed/service.rb | 23 ++++++-------- test/async/service/policy.rb | 44 +++++++++++++-------------- 3 files changed, 31 insertions(+), 52 deletions(-) delete mode 100644 fixtures/container_context.rb diff --git a/fixtures/container_context.rb b/fixtures/container_context.rb deleted file mode 100644 index 24f955e..0000000 --- a/fixtures/container_context.rb +++ /dev/null @@ -1,16 +0,0 @@ -# frozen_string_literal: true - -require "async" -require "async/container/group" - -module ContainerContext - def container_context(&block) - # Newer async-container supervises containers using Async tasks, while - # released versions can fail when forking under a scheduler on Ruby 3.x. - if Async::Container::Group.method_defined?(:supervise) - Sync(&block) - else - block.call - end - end -end diff --git a/test/async/service/managed/service.rb b/test/async/service/managed/service.rb index ef645d8..8fcaa38 100644 --- a/test/async/service/managed/service.rb +++ b/test/async/service/managed/service.rb @@ -10,11 +10,8 @@ require "async" require "sus/fixtures/async/scheduler_context" -require "container_context" describe Async::Service::Managed::Service do - include ContainerContext - let(:configuration) do Async::Service::Configuration.build do service "test-container" do @@ -350,6 +347,8 @@ def name = @name end with "integration test with controller" do + include Sus::Fixtures::Async::SchedulerContext + let(:configuration) do Async::Service::Configuration.build do service "test-managed" do @@ -368,16 +367,14 @@ def name = @name let(:controller) {Async::Service::Controller.for(test_service)} it "runs service with health checking and no restarts when async context is present" do - container_context do - container = Async::Container.new - - controller.setup(container) - controller.start - sleep(0.03) - ensure - controller.stop - container&.stop - end + container = Async::Container.new + + controller.setup(container) + controller.start + sleep(0.03) + ensure + controller.stop + container&.stop end end end diff --git a/test/async/service/policy.rb b/test/async/service/policy.rb index 3599e7c..81825b8 100644 --- a/test/async/service/policy.rb +++ b/test/async/service/policy.rb @@ -6,11 +6,9 @@ require "async/container/best" require "async/service/policy" require "async/container/statistics" -require "container_context" +require "sus/fixtures/async/scheduler_context" describe Async::Service::Policy do - include ContainerContext - let(:policy) {subject.new(maximum_failures: 5, window: 10)} with "::DEFAULT" do @@ -163,29 +161,29 @@ def success_status.success?; true; end end with "concurrent failures" do + include Sus::Fixtures::Async::SchedulerContext + it "only stops container once when multiple children fail simultaneously" do - container_context do - container = Async::Container.best_container_class.new(policy: policy) - stop_count = 0 - original_stop = container.method(:stop) - - container.define_singleton_method(:stop) do |*arguments| - stop_count += 1 - original_stop.call(*arguments) - end - - # Spawn 10 children that all fail immediately: - 10.times do |i| - container.spawn(name: "worker-#{i}") do |instance| - instance.ready! - exit(1) - end + container = Async::Container.best_container_class.new(policy: policy) + stop_count = 0 + original_stop = container.method(:stop) + + container.define_singleton_method(:stop) do |*arguments| + stop_count += 1 + original_stop.call(*arguments) + end + + # Spawn 10 children that all fail immediately: + 10.times do |i| + container.spawn(name: "worker-#{i}") do |instance| + instance.ready! + exit(1) end - - container.wait - - expect(stop_count).to be == 1 end + + container.wait + + expect(stop_count).to be == 1 end end end