# Copyright © 2025-26 l5yth & contributors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # frozen_string_literal: true require "spec_helper" require "timeout" RSpec.describe PotatoMesh::App::WorkerPool do def with_pool(size: 2, queue: 2) pool = PotatoMesh::App::WorkerPool.new(size: size, max_queue: queue, name: "spec-pool") yield pool ensure pool&.shutdown(timeout: 0.5) end describe "#schedule" do it "executes jobs asynchronously and exposes their return values" do with_pool do |pool| task = pool.schedule { 21 + 21 } expect(task.wait(timeout: 1)).to eq(42) end end it "propagates exceptions raised by the job block" do with_pool do |pool| task = pool.schedule { raise ArgumentError, "boom" } expect { task.wait(timeout: 1) }.to raise_error(ArgumentError, "boom") end end it "raises an error when the queue is saturated" do with_pool(size: 1, queue: 1) do |pool| gate = Queue.new first_task = pool.schedule { gate.pop; :first } Timeout.timeout(1) do sleep 0.01 until gate.num_waiting.positive? end second_task = pool.schedule { gate.pop; :second } expect do pool.schedule { :third } end.to raise_error(described_class::QueueFullError) gate << nil gate << nil expect(first_task.wait(timeout: 1)).to eq(:first) expect(second_task.wait(timeout: 1)).to eq(:second) end end end describe "#shutdown" do it "prevents new work from being scheduled" do pool = described_class.new(size: 1, max_queue: 1, name: "spec-pool") pool.shutdown(timeout: 0.5) expect do pool.schedule { :after_shutdown } end.to raise_error(described_class::ShutdownError) ensure pool.shutdown(timeout: 0.5) end end describe PotatoMesh::App::WorkerPool::Task do it "raises a timeout when the job exceeds the provided deadline" do with_pool do |pool| task = pool.schedule { sleep 0.1; :done } expect do task.wait(timeout: 0.01) end.to raise_error(PotatoMesh::App::WorkerPool::TaskTimeoutError) expect(task.wait(timeout: 1)).to eq(:done) end end it "reports completion status" do with_pool do |pool| task = pool.schedule { :result } expect(task.complete?).to be(false) expect(task.wait(timeout: 1)).to eq(:result) expect(task.complete?).to be(true) end end end end