require 'rails_helper' RSpec.describe PostBulkCreator do it 'limits workers to two and keeps failures in their request slots' do actor = instance_double(User, id: 123) allow(User).to receive(:find).with(123) { instance_double(User, id: 123) } mutex = Mutex.new active = 0 maximum_active = 0 allow(PostCreatePreflight).to receive(:new) do |attributes:, **| preflight = instance_double(PostCreatePreflight) allow(preflight).to receive(:run) do mutex.synchronize do active += 1 maximum_active = [maximum_active, active].max end sleep 0.02 mutex.synchronize { active -= 1 } attributes.symbolize_keys.merge( existing_post_id: nil, field_warnings: { }, base_warnings: []) end preflight end allow(PostCreator).to receive(:new) do |attributes:, **| creator = instance_double(PostCreator) if attributes[:title] == 'broken' allow(creator).to receive(:create!).and_raise(StandardError, 'broken') else post = instance_double(Post, id: attributes[:title].delete_prefix('post ').to_i) allow(creator).to receive(:create!).and_return(post) end creator end posts = [ { 'title' => 'post 1', 'url' => 'https://example.com/1' }, { 'title' => 'broken', 'url' => 'https://example.com/2' }, { 'title' => 'post 3', 'url' => 'https://example.com/3' }, { 'title' => 'post 4', 'url' => 'https://example.com/4' }] results = described_class.new( actor:, posts:, thumbnails: { }).run.fetch(:results) expect(maximum_active).to eq(2) expect(results.length).to eq(posts.length) expect(results.map { _1[:status] }).to eq( ['created', 'failed', 'created', 'created']) expect(results[0].dig(:post, :id)).to eq(1) expect(results[1]).to include(status: 'failed', recoverable: false) expect(results[2].dig(:post, :id)).to eq(3) expect(results[3].dig(:post, :id)).to eq(4) end end