3 # Copyright Tamil s.a.r.l. 2016
5 # This file is part of Koha.
7 # Koha is free software; you can redistribute it and/or modify it
8 # under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
12 # Koha is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with Koha; if not, see <http://www.gnu.org/licenses>.
22 use Test::More tests => 29;
36 use_ok('Koha::OAI::Server::DeletedRecord');
37 use_ok('Koha::OAI::Server::Description');
38 use_ok('Koha::OAI::Server::GetRecord');
39 use_ok('Koha::OAI::Server::Identify');
40 use_ok('Koha::OAI::Server::ListBase');
41 use_ok('Koha::OAI::Server::ListIdentifiers');
42 use_ok('Koha::OAI::Server::ListMetadataFormats');
43 use_ok('Koha::OAI::Server::ListRecords');
44 use_ok('Koha::OAI::Server::ListSets');
45 use_ok('Koha::OAI::Server::Record');
46 use_ok('Koha::OAI::Server::Repository');
47 use_ok('Koha::OAI::Server::ResumptionToken');
50 use constant NUMBER_OF_MARC_RECORDS => 10;
52 # Mocked CGI module in order to be able to send CGI parameters to OAI Server
54 my $module = Test::MockModule->new('CGI');
55 $module->mock('Vars', sub { %param; });
57 my $schema = Koha::Database->schema;
58 $schema->storage->txn_begin;
59 my $dbh = C4::Context->dbh;
61 $dbh->do("SET time_zone='+00:00'");
62 $dbh->do('DELETE FROM issues');
63 $dbh->do('DELETE FROM biblio');
64 $dbh->do('DELETE FROM deletedbiblio');
65 $dbh->do('DELETE FROM deletedbiblioitems');
66 $dbh->do('DELETE FROM deleteditems');
67 $dbh->do('DELETE FROM oai_sets');
69 my $date_added = DateTime->now() . 'Z';
70 my $date_to = substr($date_added, 0, 10) . 'T23:59:59Z';
71 my (@header, @marcxml, @oaidc);
72 my $sth = $dbh->prepare('SELECT timestamp FROM biblioitems WHERE biblionumber=?');
75 foreach my $index ( 0 .. NUMBER_OF_MARC_RECORDS - 1 ) {
76 my $record = MARC::Record->new();
77 if (C4::Context->preference('marcflavour') eq 'UNIMARC') {
78 $record->append_fields( MARC::Field->new('101', '', '', 'a' => "lng" ) );
79 $record->append_fields( MARC::Field->new('200', '', '', 'a' => "Title $index" ) );
81 $record->append_fields( MARC::Field->new('008', ' lng' ) );
82 $record->append_fields( MARC::Field->new('245', '', '', 'a' => "Title $index" ) );
84 my ($biblionumber) = AddBiblio($record, '');
85 $sth->execute($biblionumber);
86 my $timestamp = $sth->fetchrow_array . 'Z';
88 $timestamp = manipulate_timestamp( $index, $biblionumber, $timestamp );
89 $record = GetMarcBiblio($biblionumber);
90 $record = XMLin($record->as_xml_record);
91 push @header, { datestamp => $timestamp, identifier => "TEST:$biblionumber" };
93 'dc:title' => "Title $index",
94 'dc:language' => "lng",
96 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
97 'xmlns:oai_dc' => 'http://www.openarchives.org/OAI/2.0/oai_dc/',
98 'xmlns:dc' => 'http://purl.org/dc/elements/1.1/',
99 'xsi:schemaLocation' => 'http://www.openarchives.org/OAI/2.0/oai_dc/ http://www.openarchives.org/OAI/2.0/oai_dc.xsd',
101 if (C4::Context->preference('marcflavour') eq 'UNIMARC') {
102 $dc->{'dc:identifier'} = $biblionumber;
105 header => $header[$index],
111 header => $header[$index],
119 'LibraryName' => 'My Library',
121 'OAI-PMH:archiveID' => 'TEST',
122 'OAI-PMH:ConfFile' => '',
123 'OAI-PMH:MaxCount' => 3,
124 'OAI-PMH:DeletedRecord' => 'persistent',
126 while ( my ($name, $value) = each %$syspref ) {
127 t::lib::Mocks::mock_preference( $name => $value );
131 my ($test, $param, $expected) = @_;
134 my %full_expected = (
137 request => 'http://localhost',
138 xmlns => 'http://www.openarchives.org/OAI/2.0/',
139 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
140 'xsi:schemaLocation' => 'http://www.openarchives.org/OAI/2.0/ http://www.openarchives.org/OAI/2.0/OAI-PMH.xsd',
148 open STDOUT, '>', \$stdout;
149 Koha::OAI::Server::Repository->new();
150 $response = XMLin($stdout);
153 delete $response->{responseDate};
154 unless (is_deeply($response, \%full_expected, $test)) {
156 "PARAM:" . Dump($param) .
157 "EXPECTED:" . Dump(\%full_expected) .
158 "RESPONSE:" . Dump($response);
162 test_query('ListMetadataFormats', {verb => 'ListMetadataFormats'}, {
163 ListMetadataFormats => {
166 metadataNamespace => 'http://www.openarchives.org/OAI/2.0/oai_dc/',
167 metadataPrefix=> 'oai_dc',
168 schema => 'http://www.openarchives.org/OAI/2.0/oai_dc.xsd',
171 metadataNamespace => 'http://www.loc.gov/MARC21/slim http://www.loc.gov/standards/marcxml/schema/MARC21slim',
172 metadataPrefix => 'marc21',
173 schema => 'http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd',
176 metadataNamespace => 'http://www.loc.gov/MARC21/slim http://www.loc.gov/standards/marcxml/schema/MARC21slim',
177 metadataPrefix => 'marcxml',
178 schema => 'http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd',
184 test_query('ListIdentifiers without metadataPrefix', {verb => 'ListIdentifiers'}, {
186 code => 'badArgument',
187 content => "Required argument 'metadataPrefix' was undefined",
191 test_query('ListIdentifiers', {verb => 'ListIdentifiers', metadataPrefix => 'marcxml'}, {
193 header => [ @header[0..2] ],
195 content => "marcxml/3/1970-01-01T00:00:00Z/$date_to//0/0",
201 test_query('ListIdentifiers', {verb => 'ListIdentifiers', metadataPrefix => 'marcxml'}, {
203 header => [ @header[0..2] ],
205 content => "marcxml/3/1970-01-01T00:00:00Z/$date_to//0/0",
212 'ListIdentifiers with resumptionToken 1',
213 { verb => 'ListIdentifiers', resumptionToken => "marcxml/3/1970-01-01T00:00:00Z/$date_to//0/0" },
216 header => [ @header[3..5] ],
218 content => "marcxml/6/1970-01-01T00:00:00Z/$date_to//0/0",
226 'ListIdentifiers with resumptionToken 2',
227 { verb => 'ListIdentifiers', resumptionToken => "marcxml/6/1970-01-01T00:00:00Z/$date_to//0/0" },
230 header => [ @header[6..8] ],
232 content => "marcxml/9/1970-01-01T00:00:00Z/$date_to//0/0",
240 'ListIdentifiers with resumptionToken 3, response without resumption',
241 { verb => 'ListIdentifiers', resumptionToken => "marcxml/9/1970-01-01T00:00:00Z/$date_to//0/0" },
244 header => $header[9],
249 test_query('ListRecords marcxml without metadataPrefix', {verb => 'ListRecords'}, {
251 code => 'badArgument',
252 content => "Required argument 'metadataPrefix' was undefined",
256 test_query('ListRecords marcxml', {verb => 'ListRecords', metadataPrefix => 'marcxml'}, {
258 record => [ @marcxml[0..2] ],
260 content => "marcxml/3/1970-01-01T00:00:00Z/$date_to//0/0",
267 'ListRecords marcxml with resumptionToken 1',
268 { verb => 'ListRecords', resumptionToken => "marcxml/3/1970-01-01T00:00:00Z/$date_to//0/0" },
270 record => [ @marcxml[3..5] ],
272 content => "marcxml/6/1970-01-01T00:00:00Z/$date_to//0/0",
279 'ListRecords marcxml with resumptionToken 2',
280 { verb => 'ListRecords', resumptionToken => "marcxml/6/1970-01-01T00:00:00Z/$date_to//0/0" },
282 record => [ @marcxml[6..8] ],
284 content => "marcxml/9/1970-01-01T00:00:00Z/$date_to//0/0",
290 # Last record, so no resumption token
292 'ListRecords marcxml with resumptionToken 3, response without resumption',
293 { verb => 'ListRecords', resumptionToken => "marcxml/9/1970-01-01T00:00:00Z/$date_to//0/0" },
295 record => $marcxml[9],
299 test_query('ListRecords oai_dc', {verb => 'ListRecords', metadataPrefix => 'oai_dc'}, {
301 record => [ @oaidc[0..2] ],
303 content => "oai_dc/3/1970-01-01T00:00:00Z/$date_to//0/0",
310 'ListRecords oai_dc with resumptionToken 1',
311 { verb => 'ListRecords', resumptionToken => "oai_dc/3/1970-01-01T00:00:00Z/$date_to//0/0" },
313 record => [ @oaidc[3..5] ],
315 content => "oai_dc/6/1970-01-01T00:00:00Z/$date_to//0/0",
322 'ListRecords oai_dc with resumptionToken 2',
323 { verb => 'ListRecords', resumptionToken => "oai_dc/6/1970-01-01T00:00:00Z/$date_to//0/0" },
325 record => [ @oaidc[6..8] ],
327 content => "oai_dc/9/1970-01-01T00:00:00Z/$date_to//0/0",
333 # Last record, so no resumption token
335 'ListRecords oai_dc with resumptionToken 3, response without resumption',
336 { verb => 'ListRecords', resumptionToken => "oai_dc/9/1970-01-01T00:00:00Z/$date_to//0/0" },
342 subtest 'Bug 19725: OAI-PMH ListRecords and ListIdentifiers should use biblio_metadata.timestamp' => sub {
345 # Wait 1 second to be sure no timestamp will be equal to $from defined below
348 my $from_dt = DateTime->now;
349 my $from = $from_dt->ymd . 'T' . $from_dt->hms . 'Z';
351 # Modify record to trigger auto update of timestamp
352 (my $biblionumber = $marcxml[0]->{header}->{identifier}) =~ s/^.*:(.*)/$1/;
353 my $record = GetMarcBiblio($biblionumber);
354 $record->append_fields(MARC::Field->new(999, '', '', z => '_'));
355 ModBiblio($record, $biblionumber);
358 'ListRecords oai_dc with parameter from',
359 { verb => 'ListRecords', metadataPrefix => 'oai_dc', from => $from },
366 $schema->storage->txn_rollback;
368 sub manipulate_timestamp {
369 # This eliminates waiting a few seconds in order to get a higher timestamp
370 # Works only for 60 records..
371 my ( $index, $bibno, $timestamp ) = @_;
372 return $timestamp if $timestamp !~ /\d{2}Z/;
373 my $secs = sprintf( "%02d", $index );
374 $timestamp =~ s/\d{2}Z/${secs}Z/;
375 $dbh->do("UPDATE biblioitems SET timestamp=? WHERE biblionumber=?", undef,
376 ( $timestamp, $bibno ));