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;
34 use Koha::Biblio::Metadatas;
39 use_ok('Koha::OAI::Server::DeletedRecord');
40 use_ok('Koha::OAI::Server::Description');
41 use_ok('Koha::OAI::Server::GetRecord');
42 use_ok('Koha::OAI::Server::Identify');
43 use_ok('Koha::OAI::Server::ListBase');
44 use_ok('Koha::OAI::Server::ListIdentifiers');
45 use_ok('Koha::OAI::Server::ListMetadataFormats');
46 use_ok('Koha::OAI::Server::ListRecords');
47 use_ok('Koha::OAI::Server::ListSets');
48 use_ok('Koha::OAI::Server::Record');
49 use_ok('Koha::OAI::Server::Repository');
50 use_ok('Koha::OAI::Server::ResumptionToken');
53 use constant NUMBER_OF_MARC_RECORDS => 10;
55 # Mocked CGI module in order to be able to send CGI parameters to OAI Server
57 my $module = Test::MockModule->new('CGI');
58 $module->mock('Vars', sub { %param; });
60 my $schema = Koha::Database->schema;
61 $schema->storage->txn_begin;
62 my $dbh = C4::Context->dbh;
64 $dbh->do("SET time_zone='+00:00'");
65 $dbh->do('DELETE FROM issues');
66 $dbh->do('DELETE FROM biblio');
67 $dbh->do('DELETE FROM deletedbiblio');
68 $dbh->do('DELETE FROM deletedbiblioitems');
69 $dbh->do('DELETE FROM deleteditems');
70 $dbh->do('DELETE FROM oai_sets');
72 my $date_added = DateTime->now() . 'Z';
73 my $date_to = substr($date_added, 0, 10) . 'T23:59:59Z';
74 my (@header, @marcxml, @oaidc);
75 my $sth = $dbh->prepare('SELECT timestamp FROM biblioitems WHERE biblionumber=?');
78 foreach my $index ( 0 .. NUMBER_OF_MARC_RECORDS - 1 ) {
79 my $record = MARC::Record->new();
80 if (C4::Context->preference('marcflavour') eq 'UNIMARC') {
81 $record->append_fields( MARC::Field->new('101', '', '', 'a' => "lng" ) );
82 $record->append_fields( MARC::Field->new('200', '', '', 'a' => "Title $index" ) );
84 $record->append_fields( MARC::Field->new('008', ' lng' ) );
85 $record->append_fields( MARC::Field->new('245', '', '', 'a' => "Title $index" ) );
87 my ($biblionumber) = AddBiblio($record, '');
88 $sth->execute($biblionumber);
89 my $timestamp = $sth->fetchrow_array . 'Z';
91 $record = GetMarcBiblio({ biblionumber => $biblionumber });
92 $record = XMLin($record->as_xml_record);
93 push @header, { datestamp => $timestamp, identifier => "TEST:$biblionumber" };
95 'dc:title' => "Title $index",
96 'dc:language' => "lng",
98 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
99 'xmlns:oai_dc' => 'http://www.openarchives.org/OAI/2.0/oai_dc/',
100 'xmlns:dc' => 'http://purl.org/dc/elements/1.1/',
101 'xsi:schemaLocation' => 'http://www.openarchives.org/OAI/2.0/oai_dc/ http://www.openarchives.org/OAI/2.0/oai_dc.xsd',
103 if (C4::Context->preference('marcflavour') eq 'UNIMARC') {
104 $dc->{'dc:identifier'} = $biblionumber;
107 header => $header[$index],
113 header => $header[$index],
121 'LibraryName' => 'My Library',
123 'OAI-PMH:archiveID' => 'TEST',
124 'OAI-PMH:ConfFile' => '',
125 'OAI-PMH:MaxCount' => 3,
126 'OAI-PMH:DeletedRecord' => 'persistent',
128 while ( my ($name, $value) = each %$syspref ) {
129 t::lib::Mocks::mock_preference( $name => $value );
133 my ($test, $param, $expected) = @_;
136 my %full_expected = (
139 request => 'http://localhost',
140 xmlns => 'http://www.openarchives.org/OAI/2.0/',
141 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
142 'xsi:schemaLocation' => 'http://www.openarchives.org/OAI/2.0/ http://www.openarchives.org/OAI/2.0/OAI-PMH.xsd',
150 open STDOUT, '>', \$stdout;
151 Koha::OAI::Server::Repository->new();
152 $response = XMLin($stdout);
155 delete $response->{responseDate};
156 unless (is_deeply($response, \%full_expected, $test)) {
158 "PARAM:" . Dump($param) .
159 "EXPECTED:" . Dump(\%full_expected) .
160 "RESPONSE:" . Dump($response);
164 test_query('ListMetadataFormats', {verb => 'ListMetadataFormats'}, {
165 ListMetadataFormats => {
168 metadataNamespace => 'http://www.openarchives.org/OAI/2.0/oai_dc/',
169 metadataPrefix=> 'oai_dc',
170 schema => 'http://www.openarchives.org/OAI/2.0/oai_dc.xsd',
173 metadataNamespace => 'http://www.loc.gov/MARC21/slim http://www.loc.gov/standards/marcxml/schema/MARC21slim',
174 metadataPrefix => 'marc21',
175 schema => 'http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd',
178 metadataNamespace => 'http://www.loc.gov/MARC21/slim http://www.loc.gov/standards/marcxml/schema/MARC21slim',
179 metadataPrefix => 'marcxml',
180 schema => 'http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd',
186 test_query('ListIdentifiers without metadataPrefix', {verb => 'ListIdentifiers'}, {
188 code => 'badArgument',
189 content => "Required argument 'metadataPrefix' was undefined",
193 test_query('ListIdentifiers', {verb => 'ListIdentifiers', metadataPrefix => 'marcxml'}, {
195 header => [ @header[0..2] ],
197 content => "marcxml/3/1970-01-01T00:00:00Z/$date_to//0/0",
203 test_query('ListIdentifiers', {verb => 'ListIdentifiers', metadataPrefix => 'marcxml'}, {
205 header => [ @header[0..2] ],
207 content => "marcxml/3/1970-01-01T00:00:00Z/$date_to//0/0",
214 'ListIdentifiers with resumptionToken 1',
215 { verb => 'ListIdentifiers', resumptionToken => "marcxml/3/1970-01-01T00:00:00Z/$date_to//0/0" },
218 header => [ @header[3..5] ],
220 content => "marcxml/6/1970-01-01T00:00:00Z/$date_to//0/0",
228 'ListIdentifiers with resumptionToken 2',
229 { verb => 'ListIdentifiers', resumptionToken => "marcxml/6/1970-01-01T00:00:00Z/$date_to//0/0" },
232 header => [ @header[6..8] ],
234 content => "marcxml/9/1970-01-01T00:00:00Z/$date_to//0/0",
242 'ListIdentifiers with resumptionToken 3, response without resumption',
243 { verb => 'ListIdentifiers', resumptionToken => "marcxml/9/1970-01-01T00:00:00Z/$date_to//0/0" },
246 header => $header[9],
251 test_query('ListRecords marcxml without metadataPrefix', {verb => 'ListRecords'}, {
253 code => 'badArgument',
254 content => "Required argument 'metadataPrefix' was undefined",
258 test_query('ListRecords marcxml', {verb => 'ListRecords', metadataPrefix => 'marcxml'}, {
260 record => [ @marcxml[0..2] ],
262 content => "marcxml/3/1970-01-01T00:00:00Z/$date_to//0/0",
269 'ListRecords marcxml with resumptionToken 1',
270 { verb => 'ListRecords', resumptionToken => "marcxml/3/1970-01-01T00:00:00Z/$date_to//0/0" },
272 record => [ @marcxml[3..5] ],
274 content => "marcxml/6/1970-01-01T00:00:00Z/$date_to//0/0",
281 'ListRecords marcxml with resumptionToken 2',
282 { verb => 'ListRecords', resumptionToken => "marcxml/6/1970-01-01T00:00:00Z/$date_to//0/0" },
284 record => [ @marcxml[6..8] ],
286 content => "marcxml/9/1970-01-01T00:00:00Z/$date_to//0/0",
292 # Last record, so no resumption token
294 'ListRecords marcxml with resumptionToken 3, response without resumption',
295 { verb => 'ListRecords', resumptionToken => "marcxml/9/1970-01-01T00:00:00Z/$date_to//0/0" },
297 record => $marcxml[9],
301 test_query('ListRecords oai_dc', {verb => 'ListRecords', metadataPrefix => 'oai_dc'}, {
303 record => [ @oaidc[0..2] ],
305 content => "oai_dc/3/1970-01-01T00:00:00Z/$date_to//0/0",
312 'ListRecords oai_dc with resumptionToken 1',
313 { verb => 'ListRecords', resumptionToken => "oai_dc/3/1970-01-01T00:00:00Z/$date_to//0/0" },
315 record => [ @oaidc[3..5] ],
317 content => "oai_dc/6/1970-01-01T00:00:00Z/$date_to//0/0",
324 'ListRecords oai_dc with resumptionToken 2',
325 { verb => 'ListRecords', resumptionToken => "oai_dc/6/1970-01-01T00:00:00Z/$date_to//0/0" },
327 record => [ @oaidc[6..8] ],
329 content => "oai_dc/9/1970-01-01T00:00:00Z/$date_to//0/0",
335 # Last record, so no resumption token
337 'ListRecords oai_dc with resumptionToken 3, response without resumption',
338 { verb => 'ListRecords', resumptionToken => "oai_dc/9/1970-01-01T00:00:00Z/$date_to//0/0" },
344 subtest 'Bug 19725: OAI-PMH ListRecords and ListIdentifiers should use biblio_metadata.timestamp' => sub {
347 # Wait 1 second to be sure no timestamp will be equal to $from defined below
350 # Modify record to trigger auto update of timestamp
351 (my $biblionumber = $marcxml[0]->{header}->{identifier}) =~ s/^.*:(.*)/$1/;
352 my $record = GetMarcBiblio({biblionumber => $biblionumber});
353 $record->append_fields(MARC::Field->new(999, '', '', z => '_'));
354 ModBiblio( $record, $biblionumber );
355 my $from_dt = dt_from_string(
356 Koha::Biblio::Metadatas->find({ biblionumber => $biblionumber, format => 'marcxml', marcflavour => 'MARC21' })->timestamp
358 my $from = $from_dt->ymd . 'T' . $from_dt->hms . 'Z';
359 $oaidc[0]->{header}->{datestamp} = $from;
362 'ListRecords oai_dc with parameter from',
363 { verb => 'ListRecords', metadataPrefix => 'oai_dc', from => $from },
370 $schema->storage->txn_rollback;