3 # This file is part of Koha.
5 # Koha is free software; you can redistribute it and/or modify it
6 # under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 3 of the License, or
8 # (at your option) any later version.
10 # Koha is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with Koha; if not, see <http://www.gnu.org/licenses>.
20 use Test::More tests => 4;
29 use Koha::SearchEngine::Elasticsearch;
31 subtest '_read_configuration() tests' => sub {
36 t::lib::Mocks::mock_config( 'elasticsearch', undef );
38 # 'elasticsearch' missing in configuration
40 $configuration = Koha::SearchEngine::Elasticsearch::_read_configuration;
42 'Koha::Exceptions::Config::MissingEntry',
43 'Configuration problem, exception thrown';
46 "Missing 'elasticsearch' block in config file",
47 'Exception message is correct'
50 # 'elasticsearch' present but no 'server' entry
51 t::lib::Mocks::mock_config( 'elasticsearch', {} );
53 $configuration = Koha::SearchEngine::Elasticsearch::_read_configuration;
55 'Koha::Exceptions::Config::MissingEntry',
56 'Configuration problem, exception thrown';
59 "Missing 'server' entry in config file for elasticsearch",
60 'Exception message is correct'
63 # 'elasticsearch' and 'server' entries present, but no 'index_name'
64 t::lib::Mocks::mock_config( 'elasticsearch', { server => 'a_server' } );
66 $configuration = Koha::SearchEngine::Elasticsearch::_read_configuration;
68 'Koha::Exceptions::Config::MissingEntry',
69 'Configuration problem, exception thrown';
72 "Missing 'index_name' entry in config file for elasticsearch",
73 'Exception message is correct'
76 # Correct configuration, only one server
77 t::lib::Mocks::mock_config( 'elasticsearch', { server => 'a_server', index_name => 'index' } );
79 $configuration = Koha::SearchEngine::Elasticsearch::_read_configuration;
80 is( $configuration->{index_name}, 'index', 'Index configuration parsed correctly' );
81 is_deeply( $configuration->{nodes}, ['a_server'], 'Server configuration parsed correctly' );
83 # Correct configuration, two servers
84 my @servers = ('a_server', 'another_server');
85 t::lib::Mocks::mock_config( 'elasticsearch', { server => \@servers, index_name => 'index' } );
87 $configuration = Koha::SearchEngine::Elasticsearch::_read_configuration;
88 is( $configuration->{index_name}, 'index', 'Index configuration parsed correctly' );
89 is_deeply( $configuration->{nodes}, \@servers , 'Server configuration parsed correctly' );
92 subtest 'get_elasticsearch_settings() tests' => sub {
98 # test reading index settings
99 my $es = Koha::SearchEngine::Elasticsearch->new( {index => $Koha::SearchEngine::Elasticsearch::BIBLIOS_INDEX} );
100 $settings = $es->get_elasticsearch_settings();
101 is( $settings->{index}{analysis}{analyzer}{analyser_phrase}{tokenizer}, 'keyword', 'Index settings parsed correctly' );
104 subtest 'get_elasticsearch_mappings() tests' => sub {
110 # test reading mappings
111 my $es = Koha::SearchEngine::Elasticsearch->new( {index => $Koha::SearchEngine::Elasticsearch::BIBLIOS_INDEX} );
112 $mappings = $es->get_elasticsearch_mappings();
113 is( $mappings->{data}{_all}{type}, 'string', 'Field mappings parsed correctly' );
116 subtest 'Koha::SearchEngine::Elasticsearch::marc_records_to_documents () tests' => sub {
120 t::lib::Mocks::mock_preference('marcflavour', 'MARC21');
129 marc_type => 'marc21',
130 marc_field => '100a',
138 marc_type => 'marc21',
139 marc_field => '110a',
147 marc_type => 'marc21',
148 marc_field => '245a',
151 name => 'unimarc_title',
156 marc_type => 'unimarc',
157 marc_field => '245a',
163 suggestible => undef,
165 marc_type => 'marc21',
169 name => 'sum_item_price',
174 marc_type => 'marc21',
175 marc_field => '952g',
178 name => 'items_withdrawn_status',
183 marc_type => 'marc21',
184 marc_field => '9520',
187 name => 'type_of_record',
192 marc_type => 'marc21',
193 marc_field => 'leader_/6',
196 name => 'type_of_record_and_bib_level',
201 marc_type => 'marc21',
202 marc_field => 'leader_/6-7',
206 my $se = Test::MockModule->new('Koha::SearchEngine::Elasticsearch');
207 $se->mock('_foreach_mapping', sub {
208 my ($self, $sub) = @_;
210 foreach my $map (@mappings) {
223 my $see = Koha::SearchEngine::Elasticsearch->new({ index => 'biblios' });
225 my $marc_record_1 = MARC::Record->new();
226 $marc_record_1->leader(' cam 22 a 4500');
227 $marc_record_1->append_fields(
228 MARC::Field->new('100', '', '', a => 'Author 1'),
229 MARC::Field->new('110', '', '', a => 'Corp Author'),
230 MARC::Field->new('210', '', '', a => 'Title 1'),
231 MARC::Field->new('245', '', '', a => 'Title: first record'),
232 MARC::Field->new('999', '', '', c => '1234567'),
233 # ' ' for testing trimming of white space in boolean value callback:
234 MARC::Field->new('952', '', '', 0 => ' ', g => '123.30'),
235 MARC::Field->new('952', '', '', 0 => 0, g => '127.20'),
237 my $marc_record_2 = MARC::Record->new();
238 $marc_record_2->leader(' cam 22 a 4500');
239 $marc_record_2->append_fields(
240 MARC::Field->new('100', '', '', a => 'Author 2'),
241 # MARC::Field->new('210', '', '', a => 'Title 2'),
242 # MARC::Field->new('245', '', '', a => 'Title: second record'),
243 MARC::Field->new('999', '', '', c => '1234568'),
244 MARC::Field->new('952', '', '', 0 => 1, g => 'string where should be numeric'),
246 my $records = [$marc_record_1, $marc_record_2];
248 $see->get_elasticsearch_mappings(); #sort_fields will call this and use the actual db values unless we call it first
250 my $docs = $see->marc_records_to_documents($records);
254 is(scalar @{$docs}, 2, 'Two records converted to documents');
256 is($docs->[0][0], '1234567', 'First document biblionumber should be set as first element in document touple');
258 is(scalar @{$docs->[0][1]->{author}}, 2, 'First document author field should contain two values');
259 is_deeply($docs->[0][1]->{author}, ['Author 1', 'Corp Author'], 'First document author field should be set correctly');
261 is(scalar @{$docs->[0][1]->{author__sort}}, 2, 'First document author__sort field should have two values');
262 is_deeply($docs->[0][1]->{author__sort}, ['Author 1', 'Corp Author'], 'First document author__sort field should be set correctly');
264 is(scalar @{$docs->[0][1]->{title__sort}}, 1, 'First document title__sort field should have one value');
265 is_deeply($docs->[0][1]->{title__sort}, ['Title: first record'], 'First document title__sort field should be set correctly');
267 is(scalar @{$docs->[0][1]->{author__suggestion}}, 2, 'First document author__suggestion field should contain two values');
269 $docs->[0][1]->{author__suggestion},
272 'input' => 'Author 1'
275 'input' => 'Corp Author'
278 'First document author__suggestion field should be set correctly'
281 is(scalar @{$docs->[0][1]->{title__suggestion}}, 1, 'First document title__suggestion field should contain one value');
283 $docs->[0][1]->{title__suggestion},
284 [{ 'input' => 'Title: first record' }],
285 'First document title__suggestion field should be set correctly'
288 ok(!(defined $docs->[0][1]->{title__facet}), 'First document should have no title__facet field');
290 is(scalar @{$docs->[0][1]->{author__facet}}, 2, 'First document author__facet field should have two values');
292 $docs->[0][1]->{author__facet},
293 ['Author 1', 'Corp Author'],
294 'First document author__facet field should be set correctly'
297 is(scalar @{$docs->[0][1]->{items_withdrawn_status}}, 2, 'First document items_withdrawn_status field should have two values');
299 $docs->[0][1]->{items_withdrawn_status},
301 'First document items_withdrawn_status field should be set correctly'
305 $docs->[0][1]->{sum_item_price},
307 'First document sum_item_price field should be set correctly'
310 ok(defined $docs->[0][1]->{marc_data}, 'First document marc_data field should be set');
312 ok(defined $docs->[0][1]->{marc_format}, 'First document marc_format field should be set');
314 is(scalar @{$docs->[0][1]->{type_of_record}}, 1, 'First document type_of_record field should have one value');
316 $docs->[0][1]->{type_of_record},
318 'First document type_of_record field should be set correctly'
321 is(scalar @{$docs->[0][1]->{type_of_record_and_bib_level}}, 1, 'First document type_of_record_and_bib_level field should have one value');
323 $docs->[0][1]->{type_of_record_and_bib_level},
325 'First document type_of_record_and_bib_level field should be set correctly'
330 is(scalar @{$docs->[1][1]->{author}}, 1, 'Second document author field should contain one value');
331 is_deeply($docs->[1][1]->{author}, ['Author 2'], 'Second document author field should be set correctly');
333 is(scalar @{$docs->[1][1]->{items_withdrawn_status}}, 1, 'Second document items_withdrawn_status field should have one value');
335 $docs->[1][1]->{items_withdrawn_status},
337 'Second document items_withdrawn_status field should be set correctly'
341 $docs->[1][1]->{sum_item_price},
343 'Second document sum_item_price field should be set correctly'
346 # Mappings marc_type:
348 ok(!(defined $docs->[0][1]->{unimarc_title}), "No mapping when marc_type doesn't match marc flavour");