spec/core/connector_settings_spec.rb (453 lines of code) (raw):

# # Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one # or more contributor license agreements. Licensed under the Elastic License; # you may not use this file except in compliance with the Elastic License. # # frozen_string_literal: true require 'core' describe Core::ConnectorSettings do let(:elasticsearch_response) { {} } let(:connectors_meta) { {} } subject { described_class.send(:new, elasticsearch_response, connectors_meta) } before(:each) do allow(Core::ElasticConnectorActions).to receive(:connectors_meta).and_return(connectors_meta) end context '.fetch_by_id' do let(:connector_id) { '123' } let(:elasticsearch_response) do { :found => found } end before(:each) do allow(Core::ElasticConnectorActions).to receive(:get_connector).and_return(elasticsearch_response) end context 'when connector does not exist' do let(:found) { false } it 'returns nil' do expect(described_class.fetch_by_id(connector_id)).to be_nil end end context 'when connector exists' do let(:found) { true } it 'returns a connector entity' do expect(described_class.fetch_by_id(connector_id)).to be_kind_of(described_class) end end end context 'pipeline settings' do it 'has defaults' do expect(subject.request_pipeline).to eq(Core::ConnectorSettings::DEFAULT_REQUEST_PIPELINE) end context 'global meta defaults are present' do let(:connectors_meta) { { :pipeline => { :default_name => 'foo' } } } it 'defers to globals' do expect(subject.request_pipeline).to eq('foo') end context 'index specific values are present' do let(:elasticsearch_response) { { :_source => { :pipeline => { :name => 'bar' } } } } it 'defers to index specific' do expect(subject.request_pipeline).to eq('bar') end end end end describe '#filtering' do context 'filtering is present' do let(:elasticsearch_response) { { :_source => { :filtering => [ { :domain => 'DEFAULT', :active => { :rules => [], :advanced_snippet => {}, } } ] } } } it 'extracts filtering field' do filter = subject.filtering expect(filter[:domain]).to eq('DEFAULT') expect(filter[:active][:rules]).to_not be_nil expect(filter[:active][:advanced_snippet]).to_not be_nil end end context 'filtering is not present' do it 'returns default filtering object' do filtering = subject.filtering expect(filtering).to_not be_nil expect(filtering).to be_empty end end end describe '#update_last_sync!' do let(:id) { 'id' } let(:job) { double } let(:job_status) { Connectors::SyncStatus::ERROR } let(:job_error) { 'something wrong' } let(:terminated?) { true } let(:indexed_document_count) { 10 } let(:deleted_document_count) { 5 } let(:expected_connector_status) { Connectors::ConnectorStatus::ERROR } before(:each) do allow(subject).to receive(:id).and_return(id) allow(job).to receive(:status).and_return(job_status) allow(job).to receive(:error).and_return(job_error) allow(job).to receive(:terminated?).and_return(terminated?) allow(job).to receive(:[]).with(:indexed_document_count).and_return(indexed_document_count) allow(job).to receive(:[]).with(:deleted_document_count).and_return(deleted_document_count) end it 'updates connector' do expect(Core::ElasticConnectorActions).to receive(:update_connector_fields).with( id, hash_including( :last_sync_status => job_status, :last_synced => anything, :last_sync_error => job_error, :status => expected_connector_status, :error => job_error, :last_indexed_document_count => indexed_document_count, :last_deleted_document_count => deleted_document_count ) ) subject.update_last_sync!(job) end context 'when it\'s not terminated' do let(:terminated?) { false } it 'does not update stats' do expect(Core::ElasticConnectorActions).to receive(:update_connector_fields).with( id, hash_excluding(:last_indexed_document_count, :last_deleted_document_count) ) subject.update_last_sync!(job) end end context 'with nil job' do let(:job) { nil } let(:expected_error) { 'Could\'t find the job' } it 'updates connector with error' do expect(Core::ElasticConnectorActions).to receive(:update_connector_fields).with( id, hash_including( :last_sync_status => Connectors::SyncStatus::ERROR, :last_synced => anything, :last_sync_error => expected_error, :status => expected_connector_status, :error => expected_error ) ) subject.update_last_sync!(job) end end context 'with error job without error message' do let(:job_error) { nil } let(:expected_error) { 'unknown error' } it 'updates with error' do expect(Core::ElasticConnectorActions).to receive(:update_connector_fields).with( id, hash_including(:last_sync_error => expected_error, :error => expected_error) ) subject.update_last_sync!(job) end end end describe '.fetch_native_connectors' do let(:connectors_meta) { { :pipeline => { :default_name => 'foo', :default_extract_binary_content => false, :default_reduce_whitespace => false, :default_run_ml_inference => true } } } let(:connectors) do [ { '_id' => '123', '_source' => { 'something' => 'something', 'is_native' => true } }.with_indifferent_access, { '_id' => '456', '_source' => { 'something' => 'something', 'is_native' => true } }.with_indifferent_access, { '_id' => '789', '_source' => { 'something' => 'something', 'is_native' => true } }.with_indifferent_access ] end context 'when no paging is needed' do before(:each) do allow(Core::ElasticConnectorActions).to receive(:search_connectors).and_return({ 'hits' => { 'hits' => connectors, 'total' => { 'value' => connectors.size } } }) end it 'returns three connector settings instances' do results = described_class.fetch_native_connectors(connectors.size) expected_connector_ids = results.map(&:id) actual_connector_ids = connectors.map { |c| c['_id'] } expect(expected_connector_ids).to eq(actual_connector_ids) end end context 'when paging is needed' do before(:each) do (0..2).each do |i| allow(Core::ElasticConnectorActions).to receive(:search_connectors).with(anything, anything, i).and_return({ 'hits' => { 'hits' => [connectors[i]], 'total' => { 'value' => connectors.size } } }) end end it 'returns three connector settings instances' do results = described_class.fetch_native_connectors(1) expected_connector_ids = results.map(&:id) actual_connector_ids = connectors.map { |c| c['_id'] } expect(expected_connector_ids).to eq(actual_connector_ids) end it 'fetches connectors meta only once' do expect(Core::ElasticConnectorActions).to receive(:connectors_meta).exactly(1).time described_class.fetch_native_connectors(1) end end end shared_context 'filtering features' do shared_examples_for 'filtering rule feature is disabled' do it '' do expect(subject.filtering_rule_feature_enabled?).to be_falsey end end shared_examples_for 'filtering advanced config feature is disabled' do it '' do expect(subject.filtering_advanced_config_feature_enabled?).to be_falsey end end shared_examples_for 'all filtering features are disabled' do it '' do expect(subject.any_filtering_feature_enabled?).to be_falsey end end shared_examples_for 'at least one filtering feature is enabled' do it '' do expect(subject.any_filtering_feature_enabled?).to be_truthy end end let(:filtering_rules_feature_enabled) { true } let(:filtering_advanced_config_feature_enabled) { true } let(:features) { { :sync_rules => { :basic => { :enabled => filtering_rules_feature_enabled }, :advanced => { :enabled => filtering_advanced_config_feature_enabled } } } } let(:elasticsearch_response) { { :_source => { :features => features } } } end describe '#features' do include_context 'filtering features' do context 'when features are not present' do context 'when features is an empty dict' do let(:features) { {} } it 'returns empty features' do expect(subject.features).to be_empty end end context 'when features are nil' do let(:features) { nil } it 'returns nil features' do expect(subject.features).to be_empty end end end end end describe '#filtering_rule_feature_enabled?' do include_context 'filtering features' context 'when features are not present' do context 'when features are empty' do let(:features) { {} } it_behaves_like 'filtering rule feature is disabled' end context 'when features are nil' do let(:features) { nil } it_behaves_like 'filtering rule feature is disabled' end end context 'when features are present' do context 'when features are in legacy format' do let(:features) { { :filtering_rules => filtering_rules_feature_enabled, :filtering_advanced_config => filtering_advanced_config_feature_enabled } } context 'when filtering rule feature is disabled' do let(:filtering_rules_feature_enabled) { false } it_behaves_like 'filtering rule feature is disabled' end context 'when filtering rule feature is enabled' do let(:filtering_rules_feature_enabled) { true } it 'returns enabled' do expect(subject.filtering_rule_feature_enabled?).to be_truthy end end end context 'when filtering rule feature is disabled' do let(:filtering_rules_feature_enabled) { false } it_behaves_like 'filtering rule feature is disabled' end context 'when filtering rule feature is enabled' do let(:filtering_rules_feature_enabled) { true } it 'returns enabled' do expect(subject.filtering_rule_feature_enabled?).to be_truthy end end end end describe '#filtering_advanced_config_feature_enabled?' do include_context 'filtering features' context 'when features are not present' do context 'when features are empty' do let(:features) { {} } it_behaves_like 'filtering advanced config feature is disabled' end context 'when features are nil' do let(:features) { nil } it_behaves_like 'filtering advanced config feature is disabled' end end context 'when features are present' do context 'when features are in legacy format' do let(:features) { { :filtering_rules => filtering_rules_feature_enabled, :filtering_advanced_config => filtering_advanced_config_feature_enabled } } context 'when filtering advanced config feature is disabled' do let(:filtering_advanced_config_feature_enabled) { false } it_behaves_like 'filtering advanced config feature is disabled' end context 'when filtering advanced config feature is enabled' do let(:filtering_advanced_config_feature_enabled) { true } it 'returns enabled' do expect(subject.filtering_advanced_config_feature_enabled?).to be_truthy end end end context 'when filtering advanced config feature is disabled' do let(:filtering_advanced_config_feature_enabled) { false } it_behaves_like 'filtering advanced config feature is disabled' end context 'when filtering advanced config feature is enabled' do let(:filtering_advanced_config_feature_enabled) { true } it 'returns enabled' do expect(subject.filtering_advanced_config_feature_enabled?).to be_truthy end end end end describe '#any_filtering_feature_enabled?' do include_context 'filtering features' context 'when features are not present' do context 'when features are empty' do let(:features) { {} } it_behaves_like 'all filtering features are disabled' end context 'when features are nil' do let(:features) { nil } it_behaves_like 'all filtering features are disabled' end end context 'when filtering advanced config feature and filtering rule feature are disabled' do let(:filtering_advanced_config_feature_enabled) { false } let(:filtering_rules_feature_enabled) { false } it_behaves_like 'all filtering features are disabled' end context 'when filtering advanced config feature is enabled and filtering rule feature is disabled' do let(:filtering_advanced_config_feature_enabled) { true } let(:filtering_rules_feature_enabled) { false } it_behaves_like 'at least one filtering feature is enabled' end context 'when filtering advanced config feature is disabled and filtering rule feature is enabled' do let(:filtering_advanced_config_feature_enabled) { false } let(:filtering_rules_feature_enabled) { true } it_behaves_like 'at least one filtering feature is enabled' end context 'when filtering advanced config feature and filtering rule feature are enabled' do let(:filtering_advanced_config_feature_enabled) { true } let(:filtering_rules_feature_enabled) { true } it_behaves_like 'at least one filtering feature is enabled' end end end