Files
potato-mesh/web/spec/worker_pool_spec.rb
2025-11-08 10:41:57 +01:00

100 lines
3.0 KiB
Ruby

# 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