RSpec.describe MetasploitDataModels::Search::Visitor::Joins, type: :model do subject(:visitor) do described_class.new end it_should_behave_like 'Metasploit::Concern.run' context '#visit' do subject(:visit) do visitor.visit(node) end intersection_classes = [ Metasploit::Model::Search::Group::Intersection, Metasploit::Model::Search::Operation::Group::Intersection ] intersection_classes.each do |intersection_class| context "with #{intersection_class}" do let(:children) do 2.times.collect { |n| double("Child #{n}") } end let(:node) do intersection_class.new( :children => children ) end it 'should visit each child' do # needed for call to visit subject expect(visitor).to receive(:visit).with(node).and_call_original children.each do |child| expect(visitor).to receive(:visit).with(child).and_return([]) end visit end it 'should return Array of all child visits' do child_visits = [] expect(visitor).to receive(:visit).with(node).and_call_original children.each_with_index do |child, i| child_visit = ["Visited Child #{i}"] allow(visitor).to receive(:visit).with(child).and_return(child_visit) child_visits.concat(child_visit) end expect(visit).to eq(child_visits) end end end union_classes = [ Metasploit::Model::Search::Group::Union, Metasploit::Model::Search::Operation::Group::Union ] union_classes.each do |union_class| context "with #{union_class}" do let(:node) do union_class.new( children: children ) end context 'with children' do context 'without child joins' do let(:children) do Array.new(2) { operator = Metasploit::Model::Search::Operator::Attribute.new(type: :string) operator.operate_on('formatted_value') } end it { is_expected.to eq([]) } end context 'with association and attribute' do let(:association) do FactoryGirl.generate :metasploit_model_search_operator_association_association end let(:association_operation) { association_operator.operate_on('formatted_value') } let(:association_operator) do source_operator = Metasploit::Model::Search::Operator::Attribute.new(type: :string) Metasploit::Model::Search::Operator::Association.new( association: association, source_operator: source_operator ) end let(:attribute_operation) { attribute_operator.operate_on('formatted_value') } let(:attribute_operator) do Metasploit::Model::Search::Operator::Attribute.new(type: :string) end let(:children) do [ association_operation, attribute_operation ] end it { is_expected.to eq([]) } end context 'with the same child join for all' do let(:association) do FactoryGirl.generate :metasploit_model_search_operator_association_association end let(:association_operation) { association_operator.operate_on('formatted_value') } let(:association_operator) do source_operator = Metasploit::Model::Search::Operator::Attribute.new(type: :string) Metasploit::Model::Search::Operator::Association.new( association: association, source_operator: source_operator ) end let(:children) do Array.new(2) { association_operation } end it 'should include association' do expect(visit).to include association end end context 'with union of intersections' do let(:disjoint_associations) do Array.new(2) { FactoryGirl.generate :metasploit_model_search_operator_association_association } end let(:first_associations) do disjoint_associations[0, 1] + common_associations end let(:first_association_operations) { first_association_operators.map { |association_operator| association_operator.operate_on('formatted_value') } } let(:first_association_operators) do first_associations.collect { |association| source_operator = Metasploit::Model::Search::Operator::Attribute.new(type: :string) Metasploit::Model::Search::Operator::Association.new( association: association, source_operator: source_operator ) } end let(:second_associations) do disjoint_associations[1, 1] + common_associations end let(:second_association_operations) { second_association_operators.map { |association_operator| association_operator.operate_on('formatted_value') } } let(:second_association_operators) do second_associations.collect { |association| source_operator = Metasploit::Model::Search::Operator::Attribute.new(type: :string) Metasploit::Model::Search::Operator::Association.new( association: association, source_operator: source_operator ) } end let(:children) do [first_association_operations, second_association_operations].collect { |grandchildren| Metasploit::Model::Search::Group::Intersection.new( children: grandchildren ) } end context 'with a common subset of child join' do let(:common_associations) do Array.new(2) { FactoryGirl.generate :metasploit_model_search_operator_association_association } end it 'should include common associations' do common_associations.each do |association| expect(visit).to include(association) end end it 'should not include disjoint associations' do disjoint_associations.each do |association| expect(visit).not_to include(association) end end end context 'without a common subset of child joins' do let(:common_associations) do [] end it { is_expected.to eq([]) } end end end context 'without children' do let(:children) do [] end it { is_expected.to eq([]) } end end end context 'with Metasploit::Model::Search::Operation::Association' do let(:association) { :parent_association } let(:node) { operator.operate_on('formatted_value') } let(:operator) { Metasploit::Model::Search::Operator::Association.new( association: association, source_operator: source_operator ) } context '#source_operation' do let(:attribute_operator) { Metasploit::Model::Search::Operator::Attribute.new( type: :string ) } context 'with Metasploit::Model::Search::Operation::Association' do let(:source_operator) { Metasploit::Model::Search::Operator::Association.new( association: source_operator_association, source_operator: attribute_operator ) } let(:source_operator_association) { :child_association } it 'is [{ association => nested associations }]' do expect(visit).to eq([{association => [source_operator_association]}]) end end context 'without Metasploit::Model::Search::Operation::Association' do let(:source_operator) { attribute_operator } it 'is [association]' do expect(visit).to eq([association]) end end end end operation_classes = [ Metasploit::Model::Search::Operation::Boolean, Metasploit::Model::Search::Operation::Date, Metasploit::Model::Search::Operation::Integer, Metasploit::Model::Search::Operation::Null, Metasploit::Model::Search::Operation::Set::Integer, Metasploit::Model::Search::Operation::Set::String, Metasploit::Model::Search::Operation::String ] operation_classes.each do |operation_class| context "with #{operation_class}" do it_should_behave_like 'MetasploitDataModels::Search::Visitor::Includes#visit with Metasploit::Model::Search::Operation::Base' do let(:node_class) do operation_class end end end end context 'with Metasploit::Model::Search::Operator::Association' do let(:association) do FactoryGirl.generate :metasploit_model_search_operator_association_association end let(:node) do Metasploit::Model::Search::Operator::Association.new( :association => association ) end it 'is #association' do expect(visit).to eq(association) end end context 'with Metasploit::Model::Search::Operator::Attribute' do let(:node) do Metasploit::Model::Search::Operator::Attribute.new end it { is_expected.to eq([]) } end context 'with MetasploitDataModels::Search::Operator::Port::List' do let(:node) do MetasploitDataModels::Search::Operator::Port::List.new end it { is_expected.to eq([]) } end context 'with Metasploit::Model::Search::Query#tree' do let(:node) do query.tree end let(:query) do Metasploit::Model::Search::Query.new( :formatted => formatted, :klass => klass ) end context 'Metasploit::Model::Search:Query#klass' do context 'with Mdm::Host' do let(:klass) { Mdm::Host } context 'with name' do let(:name) do FactoryGirl.generate :mdm_host_name end let(:formatted) do "name:\"#{name}\"" end it { is_expected.to be_empty } end context 'with services.name' do let(:name) do FactoryGirl.generate :mdm_service_name end let(:formatted) do "services.name:\"#{name}\"" end it { is_expected.to include :services } end end end end end end