jekyll-algolia/spec/jekyll/algolia/indexer_spec.rb
2018-03-12 14:08:35 +01:00

452 lines
12 KiB
Ruby
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# frozen_string_literal: true
require 'spec_helper'
# rubocop:disable Metrics/BlockLength
describe(Jekyll::Algolia::Indexer) do
let(:current) { Jekyll::Algolia::Indexer }
let(:configurator) { Jekyll::Algolia::Configurator }
let(:logger) { Jekyll::Algolia::Logger }
let(:error_handler) { Jekyll::Algolia::ErrorHandler }
let(:utils) { Jekyll::Algolia::Utils }
let(:html_extractor) { AlgoliaHTMLExtractor }
let(:dry_run) { false }
before { allow(configurator).to receive(:dry_run?).and_return(dry_run) }
before { allow(logger).to receive(:log) }
describe '.init' do
before do
allow(configurator).to receive(:application_id).and_return('app_id')
allow(configurator).to receive(:api_key).and_return('api_key')
allow(::Algolia).to receive(:init)
allow(::Algolia::Index)
.to receive(:new)
.and_return(double('Algolia::Index', name: 'index_name'))
allow(current).to receive(:set_user_agent)
end
before { current.init }
it 'should instanciate Algolia search with application id and api_key' do
expect(::Algolia)
.to have_received(:init)
.with(hash_including(
application_id: 'app_id',
api_key: 'api_key'
))
end
it 'should set the user agent' do
expect(current).to have_received(:set_user_agent)
end
it 'should make the index accessible' do
expect(current.index.name).to eq 'index_name'
end
end
describe '.set_user_agent' do
let(:user_agent) do
'Jekyll Integration (vIntegration); '\
'Algolia for Ruby (vAlgolia); '\
'Jekyll (vJekyll); '\
'Ruby (vRuby)'
end
before do
stub_const('Jekyll::Algolia::VERSION', 'vIntegration')
stub_const('::Algolia::VERSION', 'vAlgolia')
stub_const('::Jekyll::VERSION', 'vJekyll')
stub_const('RUBY_VERSION', 'vRuby')
allow(::Algolia).to receive(:set_extra_header)
end
before { current.set_user_agent }
it do
expect(::Algolia)
.to have_received(:set_extra_header)
.with('User-Agent', user_agent)
end
end
describe '.remote_object_ids' do
subject { current.remote_object_ids }
let(:index) { double('Algolia::Index').as_null_object }
before do
allow(current).to receive(:index).and_return(index)
expect(index)
.to receive(:browse)
.with(attributesToRetrieve: 'objectID')
.and_yield('objectID' => 'foo')
.and_yield('objectID' => 'bar')
end
it { should include('foo') }
it { should include('bar') }
# Should be ordered
it { should eq %w[bar foo] }
end
describe '.local_object_ids' do
subject { current.local_object_ids(records) }
let(:records) { [{ objectID: 'foo' }, { objectID: 'bar' }] }
it { should include('foo') }
it { should include('bar') }
# Should be ordered
it { should eq %w[bar foo] }
context 'with records missing their objectID' do
let(:records) do
[
{ objectID: 'foo' },
{ foo: 'foo' },
{ objectID: 'bar' },
{ bar: 'bar' }
]
end
it { should eq %w[bar foo] }
end
end
describe '.update_records' do
let(:index) { double('Algolia::Index', name: 'my_index') }
let(:old_records_ids) { %w[abc] }
let(:new_records) { [{ 'objectID' => 'def' }] }
let(:indexing_batch_size) { 1000 }
before { allow(::Algolia).to receive(:batch!) }
before do
allow(current).to receive(:index).and_return(index)
allow(configurator)
.to receive(:algolia)
.with('indexing_batch_size')
.and_return(indexing_batch_size)
end
before { current.update_records(old_records_ids, new_records) }
context 'when running a dry run' do
let(:dry_run) { true }
it do
expect(::Algolia)
.to_not have_received(:batch!)
end
end
context 'when nothing to update' do
let(:old_records_ids) { [] }
let(:new_records) { [] }
it do
expect(::Algolia)
.to_not have_received(:batch!)
end
end
it 'should batch all operations (deletions first)' do
expect(::Algolia)
.to have_received(:batch!)
.with([
{
action: 'deleteObject',
indexName: 'my_index',
body: { objectID: 'abc' }
},
{
action: 'addObject',
indexName: 'my_index',
body: { 'objectID' => 'def' }
}
])
end
context 'split in smaller batches if too many operations' do
let(:indexing_batch_size) { 1 }
it do
expect(::Algolia)
.to have_received(:batch!)
.ordered
.with([
{
action: 'deleteObject',
indexName: 'my_index',
body: { objectID: 'abc' }
}
])
expect(::Algolia)
.to have_received(:batch!)
.ordered
.with([
{
action: 'addObject',
indexName: 'my_index',
body: { 'objectID' => 'def' }
}
])
end
end
end
describe '.run' do
let(:records) { [{ objectID: 'foo' }, { objectID: 'bar' }] }
let(:remote_ids) { %w[foo baz] }
let(:index_name) { 'my_index' }
let(:index) { double('Algolia::Index', name: index_name) }
before do
allow(current).to receive(:init)
allow(current).to receive(:index).and_return(index)
allow(current).to receive(:update_settings)
allow(current).to receive(:remote_object_ids).and_return(remote_ids)
allow(current).to receive(:update_records)
end
context 'with records' do
before { current.run(records) }
it { expect(current).to have_received(:init) }
it do
expect(current)
.to have_received(:update_settings)
end
it do
expect(current)
.to have_received(:update_records)
.with(['baz'], [{ objectID: 'bar' }])
end
end
context 'with empty results' do
subject { -> { current.run(records) } }
let(:records) { [] }
before do
expect(configurator)
.to receive(:algolia)
.with('files_to_exclude')
.and_return(%w[foo.html bar.md])
expect(configurator)
.to receive(:algolia)
.with('nodes_to_index')
.and_return('p,li')
expect(logger)
.to receive(:known_message)
.with(
'no_records_found',
hash_including(
'files_to_exclude' => 'foo.html, bar.md',
'nodes_to_index' => 'p,li'
)
)
end
it { is_expected.to raise_error SystemExit }
end
end
describe '.update_settings' do
let(:diff_keys) { nil }
before do
allow(current).to receive(:set_settings)
allow(current).to receive(:warn_of_manual_dashboard_editing)
allow(current).to receive(:local_setting_id).and_return(local_setting_id)
allow(current).to receive(:remote_settings).and_return(remote_settings)
allow(utils).to receive(:diff_keys).and_return(diff_keys)
allow(current)
.to receive(:index)
.and_return(double('Algolia::index', name: 'my_index'))
current.update_settings
end
describe 'should do nothing if same settings both locally and remote' do
let(:local_setting_id) { 'foo' }
let(:remote_settings) { { 'userData' => { 'settingID' => 'foo' } } }
it { expect(current).to_not have_received(:set_settings) }
it do
expect(current).to_not have_received(:warn_of_manual_dashboard_editing)
end
context 'with remote settings manually edited' do
let(:diff_keys) { { 'foo' => 'bar' } }
it do
expect(current)
.to have_received(:warn_of_manual_dashboard_editing)
.with('foo' => 'bar')
end
end
end
describe 'should update settings if no remote settingID' do
let(:local_setting_id) { 'foo' }
let(:remote_settings) { { 'userData' => {} } }
it do
expect(current)
.to have_received(:set_settings)
end
end
describe 'should update settings if no remote userData' do
let(:local_setting_id) { 'foo' }
let(:remote_settings) { {} }
it do
expect(current).to have_received(:set_settings)
end
end
describe 'should update settings if no remote settings' do
let(:local_setting_id) { 'foo' }
let(:remote_settings) { nil }
it do
expect(current).to have_received(:set_settings)
end
end
describe 'should update settings if local and remote id are different' do
let(:local_setting_id) { 'foo' }
let(:remote_settings) { { 'userData' => { 'settingID' => 'bar' } } }
it do
expect(current).to have_received(:set_settings)
end
end
describe 'should update settings with new settingID' do
let(:local_setting_id) { 'foo' }
let(:remote_settings) { { 'userData' => { 'settingID' => 'bar' } } }
it do
expect(current)
.to have_received(:set_settings)
.with(
hash_including('userData' => { 'settingID' => 'foo' })
)
end
end
describe 'should not update in dry run' do
let(:local_setting_id) { 'foo' }
let(:remote_settings) { { 'userData' => { 'settingID' => 'bar' } } }
let(:dry_run) { true }
it do
expect(current).to_not have_received(:set_settings)
end
end
end
describe '.local_setting_id' do
subject { current.local_setting_id }
before do
expect(configurator).to receive(:settings).and_return(settings)
end
describe do
let(:settings) { { 'foo' => 'bar' } }
it { should eq '06ad47d8e64bd28de537b62ff85357c4' }
end
describe do
let(:settings) { { 'foo' => 'baz' } }
it { should_not eq '06ad47d8e64bd28de537b62ff85357c4' }
end
describe do
let(:settings) do
{ 'foo' => 'bar', 'userData' => { 'settingID': 'foo' } }
end
it { should eq '06ad47d8e64bd28de537b62ff85357c4' }
end
end
describe '.remote_settings' do
let(:index) { double('Algolia::Index') }
before do
allow(current).to receive(:index).and_return(index)
end
context 'with actual index' do
subject { current.remote_settings }
before do
allow(index).to receive(:get_settings).and_return('settings')
end
it { should eq 'settings' }
end
context 'with API error' do
subject { current.remote_settings }
before do
allow(index).to receive(:get_settings).and_raise
end
it { is_expected.to eq nil }
end
end
describe '.set_settings' do
let(:index) { double('Algolia::Index') }
let(:settings) { 'settings' }
before do
allow(current).to receive(:index).and_return(index)
end
describe 'with valid settings' do
before do
allow(index).to receive(:set_settings!)
current.set_settings(settings)
end
it do
expect(index).to have_received(:set_settings!).with(settings)
end
end
describe 'with invalid settings' do
before do
allow(index).to receive(:set_settings!).and_raise
allow(error_handler).to receive(:stop)
current.set_settings(settings)
end
it do
expect(error_handler)
.to have_received(:stop)
.with(RuntimeError, settings: settings)
end
end
end
describe '.warn_of_manual_dashboard_editing' do
let(:changed_keys) do
{
'distinct' => false,
'customRanking' => %w[foo bar baz]
}
end
before do
allow(logger).to receive(:known_message)
current.warn_of_manual_dashboard_editing(changed_keys)
end
it do
expect(logger)
.to have_received(:known_message)
.with(
'settings_manually_edited',
settings:
"W:    distinct: false\n"\
"W:    customRanking:\n"\
"W:      - foo\n"\
"W:      - bar\n"\
'W:      - baz'\
)
end
end
end
# rubocop:enable Metrics/BlockLength