# frozen_string_literal: true require 'spec_helper' describe Qfill::Manager do let(:manager) { described_class.new(arguments) } describe '#new' do context 'with no arguments' do it 'raises ArgumentError' do expect { described_class.new }.to raise_error(ArgumentError) end end context 'with only popper' do let(:popper) do Qfill::Popper.from_array_of_hashes( [{ name: 'High List', elements: [1, 2, 3] }] ) end it 'raises ArgumentError' do expect { described_class.new(popper: popper) }.to raise_error(ArgumentError) end end context 'with only pusher' do let(:pusher) do Qfill::Pusher.from_array_of_hashes( [{ name: 'Some Result', ratio: 0.25 }] ) end it 'raises ArgumentError' do expect { described_class.new(pusher: pusher) }.to raise_error(ArgumentError) end end context 'with popper and pusher' do let(:popper) do Qfill::Popper.from_array_of_hashes( [{ name: 'High List', elements: [1, 2, 3] }] ) end let(:pusher) do Qfill::Pusher.from_array_of_hashes( [{ name: 'Some Result', ratio: 0.25 }] ) end let(:arguments) do { pusher: pusher, popper: popper } end it 'does not raise any errors' do expect { described_class.new(arguments) }.not_to raise_error end it 'instantiates with pusher' do expect(described_class.new(arguments).pusher).to eq(pusher) end it 'instantiates with popper' do expect(described_class.new(arguments).popper).to eq(popper) end end context 'with popper and pusher and all_list_max smaller than # total elements' do let(:popper) do Qfill::Popper.from_array_of_hashes( [{ name: 'High List', elements: [1, 2, 3] }] ) end let(:pusher) do Qfill::Pusher.from_array_of_hashes( [{ name: 'Some Result', ratio: 0.25 }] ) end let(:arguments) do { pusher: pusher, popper: popper, all_list_max: 2 } end it 'does not raise any errors' do expect { described_class.new(arguments) }.not_to raise_error end it 'instantiates with pusher' do expect(described_class.new(arguments).pusher).to eq(pusher) end it 'instantiates with popper' do expect(described_class.new(arguments).popper).to eq(popper) end it 'retains specified all_list_max' do expect(described_class.new(arguments).all_list_max).to eq(2) end end context 'all_list_max greater than # total elements' do let(:popper) do Qfill::Popper.from_array_of_hashes( [{ name: 'High List', elements: [1, 2, 3] }] ) end let(:pusher) do Qfill::Pusher.from_array_of_hashes( [{ name: 'Some Result', ratio: 0.25 }] ) end let(:arguments) do { pusher: pusher, popper: popper, all_list_max: 666 } end it 'reduces all_list_max to number of elements' do expect(described_class.new(arguments).all_list_max).to eq(3) end end end context 'when strategy => :sample' do context 'when backfill => false' do let(:popper) do Qfill::Popper.from_array_of_hashes( # We will create 4 queues, high, medium, low, and none. # These might be queue of things that have ratings, and the none queue for things which have not yet been rated. # The backfill route of the queues then, assuming we want people to rate the things that are not yet rated, # but not at the expense of hte experience, would be: # high => medium => none => low [{ name: 'high', elements: %w[h1 h2 h3 h4 h5 h6 h7 h8 h9], backfill: 'medium' }, { name: 'medium', elements: %w[m1 m2 m3 m4 m5 m6 m7 m8 m9], backfill: 'none' }, { name: 'low', elements: %w[l1 l2 l3 l4 l5 l6 l7 l8 l9], backfill: false }, { name: 'none', elements: %w[n1 n2 n3 n4 n5 n6 n7 n8 n9], backfill: 'low' }] ) end let(:pusher) do Qfill::Pusher.from_array_of_hashes( [{ name: 'first', list_ratios: { 'high' => 0.5, 'medium' => 0.1, 'none' => 0.4 }, ratio: 0.25 }, { name: 'second', ratio: 0.50 }, { name: 'third', ratio: 0.25 }] ) end let(:arguments) do { pusher: pusher, popper: popper, all_list_max: 40, strategy: :sample } end describe '#new' do it 'does not raise any errors' do expect { described_class.new(arguments) }.not_to raise_error end end describe '#fill!' do it 'instantiates with pusher' do expect { described_class.new(arguments).fill! }.not_to raise_error end end context 'results' do context 'before fill!' do it 'calculates the correct popper total elements' do expect(manager.popper.count_all_elements).to eq(36) end it 'calculates the correct popper primary elements' do expect(manager.popper.count_primary_elements).to eq(36) end it 'calculates the correct pusher total elements' do expect(manager.pusher.count_all_elements).to eq(0) end end context 'after fill!' do before do manager.fill! end it 'calculates the correct popper total elements' do expect(manager.popper.count_all_elements).to eq(0) end it 'calculates the correct popper primary elements' do expect(manager.popper.count_primary_elements).to eq(0) end it 'calculates the correct pusher total elements' do expect(manager.pusher.count_all_elements).to eq(36) end end end end context 'when backfill => true' do let(:popper) do Qfill::Popper.from_array_of_hashes( # We will create 4 queues, high, medium, low, and none. # These might be queue of things that have ratings, and the none queue for things which have not yet been rated. # The backfill route of the queues then, assuming we want people to rate the things that are not yet rated, # but not at the expense of hte experience, would be: # high => medium => none => low [{ name: 'high', elements: %w[h1 h2 h3 h4 h5 h6 h7 h8 h9], backfill: 'medium' }, { name: 'medium', elements: %w[m1 m2 m3 m4 m5 m6 m7 m8 m9], backfill: 'none' }, { name: 'low', elements: %w[l1 l2 l3 l4 l5 l6 l7 l8 l9], backfill: true }, { name: 'none', elements: %w[n1 n2 n3 n4 n5 n6 n7 n8 n9], backfill: 'low' }] ) end let(:pusher) do Qfill::Pusher.from_array_of_hashes( [{ name: 'first', list_ratios: { 'high' => 0.5, 'medium' => 0.1, 'none' => 0.4 }, ratio: 0.25 }, { name: 'second', ratio: 0.50 }, { name: 'third', ratio: 0.25 }] ) end let(:arguments) do { pusher: pusher, popper: popper, all_list_max: 28, strategy: :sample } end describe '#new' do it 'does not raise any errors' do expect { described_class.new(arguments) }.not_to raise_error end end describe '#fill!' do it 'instantiates with pusher' do expect { described_class.new(arguments).fill! }.not_to raise_error end end context 'results' do context 'before fill!' do it 'calculates the correct popper total elements' do expect(manager.popper.count_all_elements).to eq(36) end it 'calculates the correct popper primary elements' do expect(manager.popper.count_primary_elements).to eq(27) end it 'calculates the correct pusher total elements' do expect(manager.pusher.count_all_elements).to eq(0) end end context 'after fill!' do before do manager.fill! end it 'calculates the correct popper total elements' do expect(manager.popper.count_all_elements).to eq(8) end it 'calculates the correct popper primary elements' do expect(manager.popper.count_primary_elements).to eq(0) end it 'calculates the correct pusher total elements' do expect(manager.pusher.count_all_elements).to eq(28) end end end end end context 'when strategy => :drain_to_limit' do context 'when backfill => false' do let(:popper) do Qfill::Popper.from_array_of_hashes( # We will create 4 queues, high, medium, low, and none. # These might be queue of things that have ratings, and the none queue for things which have not yet been rated. # The backfill route of the queues then, assuming we want people to rate the things that are not yet rated, # but not at the expense of hte experience, would be: # high => medium => none => low [{ name: 'high', elements: %w[h1 h2 h3 h4 h5 h6 h7 h8 h9 h10 h11 h12], backfill: 'medium' }, { name: 'medium', elements: %w[m1 m2 m3 m4 m5 m6 m7 m8 m9 m10 m11 m12], backfill: 'none' }, { name: 'low', elements: %w[l1 l2 l3 l4 l5 l6 l7 l8 l9 l10 l11 l12], backfill: false }, { name: 'none', elements: %w[n1 n2 n3 n4 n5 n6 n7 n8 n9 n10 n11 n12], backfill: 'low' }] ) end let(:pusher) do Qfill::Pusher.from_array_of_hashes( [{ name: 'first', list_ratios: { 'high' => 0.5, 'medium' => 0.1, 'none' => 0.4 }, ratio: 0.25 }, { name: 'second', ratio: 0.50 }, { name: 'third', ratio: 0.25 }] ) end let(:arguments) do { pusher: pusher, popper: popper, all_list_max: 40, strategy: :drain_to_limit } end describe '#new' do it 'does not raise any errors' do expect { described_class.new(arguments) }.not_to raise_error end end describe '#fill!' do it 'instantiates with pusher' do expect { described_class.new(arguments).fill! }.not_to raise_error end end context 'results' do context 'before fill!' do it 'calculates the correct popper total elements' do expect(manager.popper.count_all_elements).to eq(48) end it 'calculates the correct popper primary elements' do expect(manager.popper.count_primary_elements).to eq(48) end it 'calculates the correct pusher total elements' do expect(manager.pusher.count_all_elements).to eq(0) end end context 'after fill!' do before do manager.fill! end it 'calculates the correct popper total elements' do expect(manager.popper.count_all_elements).to eq(8) # With drain_to_limit the results do not exactly match the requested ratios. end it 'calculates the correct popper primary elements' do expect(manager.popper.count_primary_elements).to eq(8) end it 'calculates the correct pusher total elements' do expect(manager.pusher.count_all_elements).to eq(40) end end end end context 'when backfill => true' do let(:popper) do Qfill::Popper.from_array_of_hashes( # We will create 4 queues, high, medium, low, and none. # These might be queue of things that have ratings, and the none queue for things which have not yet been rated. # The backfill route of the queues then, assuming we want people to rate the things that are not yet rated, # but not at the expense of hte experience, would be: # high => medium => none => low [{ name: 'high', elements: %w[h1 h2 h3 h4 h5 h6 h7 h8 h9], backfill: 'medium' }, { name: 'medium', elements: %w[m1 m2 m3 m4 m5 m6 m7 m8 m9], backfill: 'none' }, { name: 'low', elements: %w[l1 l2 l3 l4 l5 l6 l7 l8 l9], backfill: true }, { name: 'none', elements: %w[n1 n2 n3 n4 n5 n6 n7 n8 n9], backfill: 'low' }] ) end let(:pusher) do Qfill::Pusher.from_array_of_hashes( [{ name: 'first', list_ratios: { 'high' => 0.5, 'medium' => 0.1, 'none' => 0.4 }, ratio: 0.25 }, { name: 'second', ratio: 0.50 }, { name: 'third', ratio: 0.25 }] ) end let(:arguments) do { pusher: pusher, popper: popper, all_list_max: 40, strategy: :drain_to_limit } end describe '#new' do it 'does not raise any errors' do expect { described_class.new(arguments) }.not_to raise_error end end describe '#fill!' do it 'instantiates with pusher' do expect { described_class.new(arguments).fill! }.not_to raise_error end end context 'results' do context 'before fill!' do it 'calculates the correct popper total elements' do expect(manager.popper.count_all_elements).to eq(36) end it 'calculates the correct popper primary elements' do expect(manager.popper.count_primary_elements).to eq(27) end it 'calculates the correct pusher total elements' do expect(manager.pusher.count_all_elements).to eq(0) end end context 'after fill!' do before do manager.fill! end it 'calculates the correct popper total elements' do expect(manager.popper.count_all_elements).to eq(7) # With drain_to_limit the results do not exactly match the requested ratios. end it 'calculates the correct popper primary elements' do expect(manager.popper.count_primary_elements).to eq(0) end it 'calculates the correct pusher total elements' do expect(manager.pusher.count_all_elements).to eq(29) end end end end end context 'when strategy => :drain_to_empty' do context 'when backfill => false' do let(:popper) do Qfill::Popper.from_array_of_hashes( # We will create 4 queues, higspec/qfill/manager_spec.rb:386h, medium, low, and none. # These might be queue of things that have ratings, and the none queue for things which have not yet been rated. # The backfill route of the queues then, assuming we want people to rate the things that are not yet rated, # but not at the expense of the experience, would be: # high => medium => none => low [{ name: 'high', elements: %w[h1 h2 h3 h4 h5 h6 h7 h8 h9], backfill: 'medium' }, { name: 'medium', elements: %w[m1 m2 m3 m4 m5 m6 m7 m8 m9], backfill: 'none' }, { name: 'low', elements: %w[l1 l2 l3 l4 l5 l6 l7 l8 l9], backfill: false }, { name: 'none', elements: %w[n1 n2 n3 n4 n5 n6 n7 n8 n9], backfill: 'low' }] ) end let(:pusher) do Qfill::Pusher.from_array_of_hashes( [{ name: 'first', list_ratios: { 'high' => 0.5, 'medium' => 0.1, 'none' => 0.4 }, preferred: %w[high none] }, { name: 'second', list_ratios: { 'high' => 0.5, 'medium' => 0.4, 'none' => 0.1 }, preferred: %w[high medium] }, { name: 'third' }] ) end let(:arguments) do { pusher: pusher, popper: popper, all_list_max: 40, strategy: :drain_to_empty } end describe '#new' do it 'does not raise any errors' do expect { described_class.new(arguments) }.not_to raise_error end end describe '#fill!' do it 'instantiates with pusher' do expect { described_class.new(arguments).fill! }.not_to raise_error end end context 'results' do end context 'before fill!' do it 'calculates the correct popper total elements' do expect(manager.popper.count_all_elements).to eq(36) end it 'calculates the correct popper primary elements' do expect(manager.popper.count_primary_elements).to eq(36) end it 'calculates the correct pusher total elements' do expect(manager.pusher.count_all_elements).to eq(0) end end context 'after fill!' do before do manager.fill! end it 'calculates the correct popper total elements' do expect(manager.popper.count_all_elements).to eq(0) # With drain_to_limit the results do not exactly match the requested ratios. end it 'calculates the correct popper primary elements' do expect(manager.popper.count_primary_elements).to eq(0) end it 'calculates the correct pusher total elements' do expect(manager.pusher.count_all_elements).to eq(36) end end end context 'when backfill => true' do let(:popper) do Qfill::Popper.from_array_of_hashes( # We will create 4 queues, high, medium, low, and none. # These might be queue of things that have ratings, and the none queue for things which have not yet been rated. # The backfill route of the queues then, assuming we want people to rate the things that are not yet rated, # but not at the expense of the experience, would be: # high => medium => none => low [{ name: 'high', elements: build_elements('h', 20), backfill: 'medium' }, { name: 'medium', elements: build_elements('m', 20), backfill: 'none' }, { name: 'low', elements: build_elements('l', 20), backfill: true }, { name: 'none', elements: build_elements('n', 20), backfill: 'low' }] ) end let(:pusher) do Qfill::Pusher.from_array_of_hashes( [{ name: 'first', list_ratios: { 'high' => 0.5, 'medium' => 0.1, 'none' => 0.4 }, ratio: 1, preferred: %w[high none] }, { name: 'second', ratio: 0.50 }, { name: 'third', ratio: 0.25 }] ) end let(:arguments) do { pusher: pusher, popper: popper, all_list_max: 100, strategy: :drain_to_empty } end describe '#new' do it 'does not raise any errors' do expect { described_class.new(arguments) }.not_to raise_error end end describe '#fill!' do it 'instantiates with pusher' do expect { described_class.new(arguments).fill! }.not_to raise_error end end context 'results' do context 'before fill!' do it 'calculates the correct popper total elements' do expect(manager.popper.count_all_elements).to eq(80) end it 'calculates the correct popper primary elements' do expect(manager.popper.count_primary_elements).to eq(60) end it 'calculates the correct pusher total elements' do expect(manager.pusher.count_all_elements).to eq(0) end end context 'after fill!' do before do manager.fill! end it 'calculates the correct leftover popper total elements' do expect(manager.popper.count_all_elements).to eq(20) # TODO???: With drain_to_empty the results do not exactly match the requested ratios. end it 'calculates the correct popper primary elements' do expect(manager.popper.count_primary_elements).to eq(0) end context 'when all_list_max is higher than count of all elements' do it 'reduces all_list_max to original count of all elements' do expect(manager.all_list_max).to eq(80) end it 'is greater than count of all elements in the results (pusher)' do expect(manager.all_list_max > manager.pusher.count_all_elements).to eq(true) end end context 'when all_list_max is lower than count of all elements' do let(:arguments) do { pusher: pusher, popper: popper, all_list_max: 4, strategy: :drain_to_empty } end it 'retains specified all_list_max' do expect(manager.all_list_max).to eq(4) end it 'is equal to count of all elements in the results (pusher)' do expect(manager.pusher.count_all_elements).to eq(manager.all_list_max) end end end end end end context 'when strategy => :time_slice' do let(:elements) { 2400 } let(:window_size) { 20 } let(:pane_size) { 2 } let(:popper) do Qfill::Popper.from_array_of_hashes( [ { name: 'data', elements: build_elements('ef', elements) } ] ) end let(:arguments) do { popper: popper, strategy: :time_slice, strategy_options: { window_size: window_size, window_units: 'minutes', pane_size: pane_size, pane_units: 'seconds' } } end shared_examples_for "a good citizen" do describe '#new' do it 'does not raise any errors' do expect { described_class.new(arguments) }.not_to raise_error end end describe '#fill!' do it 'instantiates with pusher' do expect { described_class.new(arguments).fill! }.not_to raise_error end end end it_behaves_like "a good citizen" context "other options" do let(:elements) { 101 } context "5 minutes by 3 seconds" do let(:window_size) { 5 } let(:pane_size) { 3 } it_behaves_like "a good citizen" end context "7 minutes by 4 seconds" do let(:window_size) { 7 } let(:pane_size) { 4 } it_behaves_like "a good citizen" end end context 'results' do context 'before fill!' do it 'calculates the correct popper total elements' do expect(manager.popper.count_all_elements).to eq(2400) end it 'calculates the correct popper primary elements' do expect(manager.popper.count_primary_elements).to eq(2400) end it 'calculates the correct pusher total elements' do expect(manager.pusher.count_all_elements).to eq(0) end it 'has pusher queues that are "full" (because max starts at 0)' do expect(manager.pusher.queues.count(&:is_full?)).to eq(600) end it 'has pusher queues that are not really full' do expect(manager.pusher.queues.count { |x| x.elements.empty? }).to eq(600) end end context 'after fill!' do before do manager.fill! end it 'calculates the correct popper total elements' do expect(manager.popper.count_all_elements).to eq(0) end it 'calculates the correct popper primary elements' do expect(manager.popper.count_primary_elements).to eq(0) end it 'calculates the correct pusher total elements' do expect(manager.pusher.count_all_elements).to eq(2400) end it 'has correct number of pusher queues' do expect(manager.pusher.queues.length).to eq(600) end it 'has pusher queues that are full' do expect(manager.pusher.queues.count(&:is_full?)).to eq(600) end it 'has pusher queues that are not too full' do elements_per_queue = 2400 / 600 expect(manager.pusher.queues.count { |x| x.elements.length == elements_per_queue }).to eq(600) end end end end end