Bug 19040: Refactor GetMarcBiblio parameters
[srvgit] / t / db_dependent / OAI / Server.t
1 #!/usr/bin/perl
2
3 # Copyright Tamil s.a.r.l. 2016
4 #
5 # This file is part of Koha.
6 #
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.
11 #
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.
16 #
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>.
19
20 use Modern::Perl;
21
22 use Test::More tests => 28;
23 use DateTime;
24 use Test::MockModule;
25 use Test::Warn;
26 use XML::Simple;
27 use YAML;
28
29 use t::lib::Mocks;
30
31 use C4::Biblio;
32 use C4::Context;
33 use Koha::Database;
34
35 BEGIN {
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');
48 }
49
50 use constant NUMBER_OF_MARC_RECORDS => 10;
51
52 # Mocked CGI module in order to be able to send CGI parameters to OAI Server
53 my %param;
54 my $module = Test::MockModule->new('CGI');
55 $module->mock('Vars', sub { %param; });
56
57 my $schema = Koha::Database->schema;
58 $schema->storage->txn_begin;
59 my $dbh = C4::Context->dbh;
60
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');
68
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=?');
73
74 # Add biblio records
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" ) );
80     } else {
81         $record->append_fields( MARC::Field->new('008', '                                   lng' ) );
82         $record->append_fields( MARC::Field->new('245', '', '', 'a' => "Title $index" ) );
83     }
84     my ($biblionumber) = AddBiblio($record, '');
85     $sth->execute($biblionumber);
86     my $timestamp = $sth->fetchrow_array . 'Z';
87     $timestamp =~ s/ /T/;
88     $timestamp = manipulate_timestamp( $index, $biblionumber, $timestamp );
89     $record = GetMarcBiblio({ biblionumber => $biblionumber });
90     $record = XMLin($record->as_xml_record);
91     push @header, { datestamp => $timestamp, identifier => "TEST:$biblionumber" };
92     push @oaidc, {
93         header => $header[$index],
94         metadata => {
95             'oai_dc:dc' => {
96                 'dc:title' => "Title $index",
97                 'dc:language' => "lng",
98                 'dc:type' => {},
99                 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
100                 'xmlns:oai_dc' => 'http://www.openarchives.org/OAI/2.0/oai_dc/',
101                 'xmlns:dc' => 'http://purl.org/dc/elements/1.1/',
102                 'xsi:schemaLocation' => 'http://www.openarchives.org/OAI/2.0/oai_dc/ http://www.openarchives.org/OAI/2.0/oai_dc.xsd',
103             },
104         },
105     };
106     push @marcxml, {
107         header => $header[$index],
108         metadata => {
109             record => $record,
110         },
111     };
112 }
113
114 my $syspref = {
115     'LibraryName'           => 'My Library',
116     'OAI::PMH'              => 1,
117     'OAI-PMH:archiveID'     => 'TEST',
118     'OAI-PMH:ConfFile'      => '',
119     'OAI-PMH:MaxCount'      => 3,
120     'OAI-PMH:DeletedRecord' => 'persistent',
121 };
122 while ( my ($name, $value) = each %$syspref ) {
123     t::lib::Mocks::mock_preference( $name => $value );
124 }
125
126 sub test_query {
127     my ($test, $param, $expected) = @_;
128
129     %param = %$param;
130     my %full_expected = (
131         %$expected,
132         (
133             request      => 'http://localhost',
134             xmlns        => 'http://www.openarchives.org/OAI/2.0/',
135             'xmlns:xsi'  => 'http://www.w3.org/2001/XMLSchema-instance',
136             'xsi:schemaLocation' => 'http://www.openarchives.org/OAI/2.0/ http://www.openarchives.org/OAI/2.0/OAI-PMH.xsd',
137         )
138     );
139
140     my $response;
141     {
142         my $stdout;
143         local *STDOUT;
144         open STDOUT, '>', \$stdout;
145         Koha::OAI::Server::Repository->new();
146         $response = XMLin($stdout);
147     }
148
149     delete $response->{responseDate};
150     unless (is_deeply($response, \%full_expected, $test)) {
151         diag
152             "PARAM:" . Dump($param) .
153             "EXPECTED:" . Dump(\%full_expected) .
154             "RESPONSE:" . Dump($response);
155     }
156 }
157
158 test_query('ListMetadataFormats', {verb => 'ListMetadataFormats'}, {
159     ListMetadataFormats => {
160         metadataFormat => [
161             {
162                 metadataNamespace => 'http://www.openarchives.org/OAI/2.0/oai_dc/',
163                 metadataPrefix=> 'oai_dc',
164                 schema => 'http://www.openarchives.org/OAI/2.0/oai_dc.xsd',
165             },
166             {
167                 metadataNamespace => 'http://www.loc.gov/MARC21/slim http://www.loc.gov/standards/marcxml/schema/MARC21slim',
168                 metadataPrefix => 'marc21',
169                 schema => 'http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd',
170             },
171             {
172                 metadataNamespace => 'http://www.loc.gov/MARC21/slim http://www.loc.gov/standards/marcxml/schema/MARC21slim',
173                 metadataPrefix => 'marcxml',
174                 schema => 'http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd',
175             },
176         ],
177     },
178 });
179
180 test_query('ListIdentifiers without metadataPrefix', {verb => 'ListIdentifiers'}, {
181     error => {
182         code => 'badArgument',
183         content => "Required argument 'metadataPrefix' was undefined",
184     },
185 });
186
187 test_query('ListIdentifiers', {verb => 'ListIdentifiers', metadataPrefix => 'marcxml'}, {
188     ListIdentifiers => {
189         header => [ @header[0..2] ],
190         resumptionToken => {
191             content => "marcxml/3/1970-01-01T00:00:00Z/$date_to//0/0",
192             cursor  => 3,
193         },
194     },
195 });
196
197 test_query('ListIdentifiers', {verb => 'ListIdentifiers', metadataPrefix => 'marcxml'}, {
198     ListIdentifiers => {
199         header => [ @header[0..2] ],
200         resumptionToken => {
201             content => "marcxml/3/1970-01-01T00:00:00Z/$date_to//0/0",
202             cursor  => 3,
203         },
204     },
205 });
206
207 test_query(
208     'ListIdentifiers with resumptionToken 1',
209     { verb => 'ListIdentifiers', resumptionToken => "marcxml/3/1970-01-01T00:00:00Z/$date_to//0/0" },
210     {
211         ListIdentifiers => {
212             header => [ @header[3..5] ],
213             resumptionToken => {
214               content => "marcxml/6/1970-01-01T00:00:00Z/$date_to//0/0",
215               cursor  => 6,
216             },
217           },
218     },
219 );
220
221 test_query(
222     'ListIdentifiers with resumptionToken 2',
223     { verb => 'ListIdentifiers', resumptionToken => "marcxml/6/1970-01-01T00:00:00Z/$date_to//0/0" },
224     {
225         ListIdentifiers => {
226             header => [ @header[6..8] ],
227             resumptionToken => {
228               content => "marcxml/9/1970-01-01T00:00:00Z/$date_to//0/0",
229               cursor  => 9,
230             },
231           },
232     },
233 );
234
235 test_query(
236     'ListIdentifiers with resumptionToken 3, response without resumption',
237     { verb => 'ListIdentifiers', resumptionToken => "marcxml/9/1970-01-01T00:00:00Z/$date_to//0/0" },
238     {
239         ListIdentifiers => {
240             header => $header[9],
241           },
242     },
243 );
244
245 test_query('ListRecords marcxml without metadataPrefix', {verb => 'ListRecords'}, {
246     error => {
247         code => 'badArgument',
248         content => "Required argument 'metadataPrefix' was undefined",
249     },
250 });
251
252 test_query('ListRecords marcxml', {verb => 'ListRecords', metadataPrefix => 'marcxml'}, {
253     ListRecords => {
254         record => [ @marcxml[0..2] ],
255         resumptionToken => {
256           content => "marcxml/3/1970-01-01T00:00:00Z/$date_to//0/0",
257           cursor  => 3,
258         },
259     },
260 });
261
262 test_query(
263     'ListRecords marcxml with resumptionToken 1',
264     { verb => 'ListRecords', resumptionToken => "marcxml/3/1970-01-01T00:00:00Z/$date_to//0/0" },
265     { ListRecords => {
266         record => [ @marcxml[3..5] ],
267         resumptionToken => {
268           content => "marcxml/6/1970-01-01T00:00:00Z/$date_to//0/0",
269           cursor  => 6,
270         },
271     },
272 });
273
274 test_query(
275     'ListRecords marcxml with resumptionToken 2',
276     { verb => 'ListRecords', resumptionToken => "marcxml/6/1970-01-01T00:00:00Z/$date_to//0/0" },
277     { ListRecords => {
278         record => [ @marcxml[6..8] ],
279         resumptionToken => {
280           content => "marcxml/9/1970-01-01T00:00:00Z/$date_to//0/0",
281           cursor  => 9,
282         },
283     },
284 });
285
286 # Last record, so no resumption token
287 test_query(
288     'ListRecords marcxml with resumptionToken 3, response without resumption',
289     { verb => 'ListRecords', resumptionToken => "marcxml/9/1970-01-01T00:00:00Z/$date_to//0/0" },
290     { ListRecords => {
291         record => $marcxml[9],
292     },
293 });
294
295 test_query('ListRecords oai_dc', {verb => 'ListRecords', metadataPrefix => 'oai_dc'}, {
296     ListRecords => {
297         record => [ @oaidc[0..2] ],
298         resumptionToken => {
299           content => "oai_dc/3/1970-01-01T00:00:00Z/$date_to//0/0",
300           cursor  => 3,
301         },
302     },
303 });
304
305 test_query(
306     'ListRecords oai_dc with resumptionToken 1',
307     { verb => 'ListRecords', resumptionToken => "oai_dc/3/1970-01-01T00:00:00Z/$date_to//0/0" },
308     { ListRecords => {
309         record => [ @oaidc[3..5] ],
310         resumptionToken => {
311           content => "oai_dc/6/1970-01-01T00:00:00Z/$date_to//0/0",
312           cursor  => 6,
313         },
314     },
315 });
316
317 test_query(
318     'ListRecords oai_dc with resumptionToken 2',
319     { verb => 'ListRecords', resumptionToken => "oai_dc/6/1970-01-01T00:00:00Z/$date_to//0/0" },
320     { ListRecords => {
321         record => [ @oaidc[6..8] ],
322         resumptionToken => {
323           content => "oai_dc/9/1970-01-01T00:00:00Z/$date_to//0/0",
324           cursor  => 9,
325         },
326     },
327 });
328
329 # Last record, so no resumption token
330 test_query(
331     'ListRecords oai_dc with resumptionToken 3, response without resumption',
332     { verb => 'ListRecords', resumptionToken => "oai_dc/9/1970-01-01T00:00:00Z/$date_to//0/0" },
333     { ListRecords => {
334         record => $oaidc[9],
335     },
336 });
337
338 $schema->storage->txn_rollback;
339
340 sub manipulate_timestamp {
341 # This eliminates waiting a few seconds in order to get a higher timestamp
342 # Works only for 60 records..
343     my ( $index, $bibno, $timestamp ) = @_;
344     return $timestamp if $timestamp !~ /\d{2}Z/;
345     my $secs = sprintf( "%02d", $index );
346     $timestamp =~ s/\d{2}Z/${secs}Z/;
347     $dbh->do("UPDATE biblioitems SET timestamp=? WHERE biblionumber=?", undef,
348         ( $timestamp, $bibno ));
349     return $timestamp;
350 }