Bug 17600: Standardize our EXPORT_OK
[srvgit] / t / db_dependent / OAI / Server.t
old mode 100644 (file)
new mode 100755 (executable)
index 97d2c8d..48fea41
 # along with Koha; if not, see <http://www.gnu.org/licenses>.
 
 use Modern::Perl;
+use Test::Deep qw( cmp_deeply re );
+use Test::MockTime qw/set_fixed_time restore_time/;
 
-use Test::More tests => 28;
+use Test::More tests => 31;
 use DateTime;
+use File::Basename;
+use File::Spec;
 use Test::MockModule;
 use Test::Warn;
 use XML::Simple;
-use YAML;
+use YAML::XS;
 
 use t::lib::Mocks;
 
-use C4::Biblio;
+use C4::Biblio qw( AddBiblio GetMarcBiblio ModBiblio );
 use C4::Context;
+
+use Koha::Biblio::Metadatas;
 use Koha::Database;
+use Koha::DateUtils;
 
 BEGIN {
     use_ok('Koha::OAI::Server::DeletedRecord');
@@ -66,10 +73,15 @@ $dbh->do('DELETE FROM deletedbiblioitems');
 $dbh->do('DELETE FROM deleteditems');
 $dbh->do('DELETE FROM oai_sets');
 
-my $date_added = DateTime->now() . 'Z';
+set_fixed_time(CORE::time());
+
+my $base_datetime = dt_from_string(undef, undef, 'UTC');
+my $date_added = $base_datetime->ymd . ' ' .$base_datetime->hms . 'Z';
 my $date_to = substr($date_added, 0, 10) . 'T23:59:59Z';
-my (@header, @marcxml, @oaidc);
-my $sth = $dbh->prepare('SELECT timestamp FROM biblioitems WHERE biblionumber=?');
+my (@header, @marcxml, @oaidc, @marcxml_transformed);
+my $sth = $dbh->prepare('UPDATE biblioitems     SET timestamp=? WHERE biblionumber=?');
+my $sth2 = $dbh->prepare('UPDATE biblio_metadata SET timestamp=? WHERE biblionumber=?');
+my $first_bn = 0;
 
 # Add biblio records
 foreach my $index ( 0 .. NUMBER_OF_MARC_RECORDS - 1 ) {
@@ -77,30 +89,41 @@ foreach my $index ( 0 .. NUMBER_OF_MARC_RECORDS - 1 ) {
     if (C4::Context->preference('marcflavour') eq 'UNIMARC') {
         $record->append_fields( MARC::Field->new('101', '', '', 'a' => "lng" ) );
         $record->append_fields( MARC::Field->new('200', '', '', 'a' => "Title $index" ) );
+        $record->append_fields( MARC::Field->new('952', '', '', 'a' => "Code" ) );
     } else {
         $record->append_fields( MARC::Field->new('008', '                                   lng' ) );
         $record->append_fields( MARC::Field->new('245', '', '', 'a' => "Title $index" ) );
+        $record->append_fields( MARC::Field->new('952', '', '', 'a' => "Code" ) );
     }
     my ($biblionumber) = AddBiblio($record, '');
-    $sth->execute($biblionumber);
-    my $timestamp = $sth->fetchrow_array . 'Z';
+    $first_bn = $biblionumber unless $first_bn;
+    my $timestamp = $base_datetime->ymd . ' ' .$base_datetime->hms;
+    $sth->execute($timestamp,$biblionumber);
+    $sth2->execute($timestamp,$biblionumber);
+    $timestamp .= 'Z';
     $timestamp =~ s/ /T/;
-    $timestamp = manipulate_timestamp( $index, $biblionumber, $timestamp );
-    $record = GetMarcBiblio($biblionumber);
+    $record = GetMarcBiblio({ biblionumber => $biblionumber });
+    my $record_transformed = $record->clone;
+    $record_transformed->delete_fields( $record_transformed->field('952'));
+    $record_transformed = XMLin($record_transformed->as_xml_record);
     $record = XMLin($record->as_xml_record);
     push @header, { datestamp => $timestamp, identifier => "TEST:$biblionumber" };
+    my $dc = {
+        'dc:title' => "Title $index",
+        'dc:language' => "lng",
+        'dc:type' => {},
+        'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
+        'xmlns:oai_dc' => 'http://www.openarchives.org/OAI/2.0/oai_dc/',
+        'xmlns:dc' => 'http://purl.org/dc/elements/1.1/',
+        'xsi:schemaLocation' => 'http://www.openarchives.org/OAI/2.0/oai_dc/ http://www.openarchives.org/OAI/2.0/oai_dc.xsd',
+    };
+    if (C4::Context->preference('marcflavour') eq 'UNIMARC') {
+        $dc->{'dc:identifier'} = $biblionumber;
+    }
     push @oaidc, {
         header => $header[$index],
         metadata => {
-            'oai_dc:dc' => {
-                'dc:title' => "Title $index",
-                'dc:language' => "lng",
-                'dc:type' => {},
-                'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
-                'xmlns:oai_dc' => 'http://www.openarchives.org/OAI/2.0/oai_dc/',
-                'xmlns:dc' => 'http://purl.org/dc/elements/1.1/',
-                'xsi:schemaLocation' => 'http://www.openarchives.org/OAI/2.0/oai_dc/ http://www.openarchives.org/OAI/2.0/oai_dc.xsd',
-            },
+            'oai_dc:dc' => $dc,
         },
     };
     push @marcxml, {
@@ -109,6 +132,13 @@ foreach my $index ( 0 .. NUMBER_OF_MARC_RECORDS - 1 ) {
             record => $record,
         },
     };
+
+    push @marcxml_transformed, {
+        header => $header[$index],
+        metadata => {
+            record => $record_transformed,
+        },
+    };
 }
 
 my $syspref = {
@@ -147,11 +177,11 @@ sub test_query {
     }
 
     delete $response->{responseDate};
-    unless (is_deeply($response, \%full_expected, $test)) {
+    unless (cmp_deeply($response, \%full_expected, $test)) {
         diag
-            "PARAM:" . Dump($param) .
-            "EXPECTED:" . Dump(\%full_expected) .
-            "RESPONSE:" . Dump($response);
+            "PARAM:" . YAML::XS::Dump($param) .
+            "EXPECTED:" . YAML::XS::Dump(\%full_expected) .
+            "RESPONSE:" . YAML::XS::Dump($response);
     }
 }
 
@@ -188,7 +218,7 @@ test_query('ListIdentifiers', {verb => 'ListIdentifiers', metadataPrefix => 'mar
     ListIdentifiers => {
         header => [ @header[0..2] ],
         resumptionToken => {
-            content => "marcxml/3/1970-01-01T00:00:00Z/$date_to//0/0",
+            content => re( qr{^marcxml/3////0/0/\d+$} ),
             cursor  => 3,
         },
     },
@@ -198,7 +228,7 @@ test_query('ListIdentifiers', {verb => 'ListIdentifiers', metadataPrefix => 'mar
     ListIdentifiers => {
         header => [ @header[0..2] ],
         resumptionToken => {
-            content => "marcxml/3/1970-01-01T00:00:00Z/$date_to//0/0",
+            content => re( qr{^marcxml/3////0/0/\d+$} ),
             cursor  => 3,
         },
     },
@@ -206,12 +236,12 @@ test_query('ListIdentifiers', {verb => 'ListIdentifiers', metadataPrefix => 'mar
 
 test_query(
     'ListIdentifiers with resumptionToken 1',
-    { verb => 'ListIdentifiers', resumptionToken => "marcxml/3/1970-01-01T00:00:00Z/$date_to//0/0" },
+    { verb => 'ListIdentifiers', resumptionToken => "marcxml/3/1970-01-01T00:00:00Z/$date_to//0/0/" . ($first_bn + 3) },
     {
         ListIdentifiers => {
             header => [ @header[3..5] ],
             resumptionToken => {
-              content => "marcxml/6/1970-01-01T00:00:00Z/$date_to//0/0",
+              content => re( qr{^marcxml/6/1970-01-01T00:00:00Z/$date_to//0/0/\d+$} ),
               cursor  => 6,
             },
           },
@@ -220,12 +250,12 @@ test_query(
 
 test_query(
     'ListIdentifiers with resumptionToken 2',
-    { verb => 'ListIdentifiers', resumptionToken => "marcxml/6/1970-01-01T00:00:00Z/$date_to//0/0" },
+    { verb => 'ListIdentifiers', resumptionToken => "marcxml/6/1970-01-01T00:00:00Z/$date_to//0/0/" . ($first_bn + 6) },
     {
         ListIdentifiers => {
             header => [ @header[6..8] ],
             resumptionToken => {
-              content => "marcxml/9/1970-01-01T00:00:00Z/$date_to//0/0",
+              content => re( qr{^marcxml/9/1970-01-01T00:00:00Z/$date_to//0/0/\d+$} ),
               cursor  => 9,
             },
           },
@@ -234,7 +264,7 @@ test_query(
 
 test_query(
     'ListIdentifiers with resumptionToken 3, response without resumption',
-    { verb => 'ListIdentifiers', resumptionToken => "marcxml/9/1970-01-01T00:00:00Z/$date_to//0/0" },
+    { verb => 'ListIdentifiers', resumptionToken => "marcxml/9/1970-01-01T00:00:00Z/$date_to//0/0/" . ($first_bn + 9) },
     {
         ListIdentifiers => {
             header => $header[9],
@@ -253,7 +283,7 @@ test_query('ListRecords marcxml', {verb => 'ListRecords', metadataPrefix => 'mar
     ListRecords => {
         record => [ @marcxml[0..2] ],
         resumptionToken => {
-          content => "marcxml/3/1970-01-01T00:00:00Z/$date_to//0/0",
+          content => re( qr{^marcxml/3////0/0/\d+$} ),
           cursor  => 3,
         },
     },
@@ -261,11 +291,11 @@ test_query('ListRecords marcxml', {verb => 'ListRecords', metadataPrefix => 'mar
 
 test_query(
     'ListRecords marcxml with resumptionToken 1',
-    { verb => 'ListRecords', resumptionToken => "marcxml/3/1970-01-01T00:00:00Z/$date_to//0/0" },
+    { verb => 'ListRecords', resumptionToken => "marcxml/3////0/0/" . ($first_bn + 3) },
     { ListRecords => {
         record => [ @marcxml[3..5] ],
         resumptionToken => {
-          content => "marcxml/6/1970-01-01T00:00:00Z/$date_to//0/0",
+          content => re( qr{^marcxml/6////0/0/\d+$} ),
           cursor  => 6,
         },
     },
@@ -273,11 +303,11 @@ test_query(
 
 test_query(
     'ListRecords marcxml with resumptionToken 2',
-    { verb => 'ListRecords', resumptionToken => "marcxml/6/1970-01-01T00:00:00Z/$date_to//0/0" },
+    { verb => 'ListRecords', resumptionToken => "marcxml/6/1970-01-01T00:00:00Z/$date_to//0/0/" . ($first_bn + 6) },
     { ListRecords => {
         record => [ @marcxml[6..8] ],
         resumptionToken => {
-          content => "marcxml/9/1970-01-01T00:00:00Z/$date_to//0/0",
+          content => re( qr{^marcxml/9/1970-01-01T00:00:00Z/$date_to//0/0/\d+$} ),
           cursor  => 9,
         },
     },
@@ -286,7 +316,7 @@ test_query(
 # Last record, so no resumption token
 test_query(
     'ListRecords marcxml with resumptionToken 3, response without resumption',
-    { verb => 'ListRecords', resumptionToken => "marcxml/9/1970-01-01T00:00:00Z/$date_to//0/0" },
+    { verb => 'ListRecords', resumptionToken => "marcxml/9/1970-01-01T00:00:00Z/$date_to//0/0/" . ($first_bn + 9) },
     { ListRecords => {
         record => $marcxml[9],
     },
@@ -296,7 +326,7 @@ test_query('ListRecords oai_dc', {verb => 'ListRecords', metadataPrefix => 'oai_
     ListRecords => {
         record => [ @oaidc[0..2] ],
         resumptionToken => {
-          content => "oai_dc/3/1970-01-01T00:00:00Z/$date_to//0/0",
+          content => re( qr{^oai_dc/3////0/0/\d+$} ),
           cursor  => 3,
         },
     },
@@ -304,11 +334,11 @@ test_query('ListRecords oai_dc', {verb => 'ListRecords', metadataPrefix => 'oai_
 
 test_query(
     'ListRecords oai_dc with resumptionToken 1',
-    { verb => 'ListRecords', resumptionToken => "oai_dc/3/1970-01-01T00:00:00Z/$date_to//0/0" },
+    { verb => 'ListRecords', resumptionToken => "oai_dc/3////0/0/" . ($first_bn + 3) },
     { ListRecords => {
         record => [ @oaidc[3..5] ],
         resumptionToken => {
-          content => "oai_dc/6/1970-01-01T00:00:00Z/$date_to//0/0",
+          content => re( qr{^oai_dc/6////0/0/\d+$} ),
           cursor  => 6,
         },
     },
@@ -316,11 +346,11 @@ test_query(
 
 test_query(
     'ListRecords oai_dc with resumptionToken 2',
-    { verb => 'ListRecords', resumptionToken => "oai_dc/6/1970-01-01T00:00:00Z/$date_to//0/0" },
+    { verb => 'ListRecords', resumptionToken => "oai_dc/6/1970-01-01T00:00:00Z/$date_to//0/0/" . ($first_bn + 6) },
     { ListRecords => {
         record => [ @oaidc[6..8] ],
         resumptionToken => {
-          content => "oai_dc/9/1970-01-01T00:00:00Z/$date_to//0/0",
+          content => re( qr{^oai_dc/9/1970-01-01T00:00:00Z/$date_to//0/0/\d+$} ),
           cursor  => 9,
         },
     },
@@ -329,22 +359,74 @@ test_query(
 # Last record, so no resumption token
 test_query(
     'ListRecords oai_dc with resumptionToken 3, response without resumption',
-    { verb => 'ListRecords', resumptionToken => "oai_dc/9/1970-01-01T00:00:00Z/$date_to//0/0" },
+    { verb => 'ListRecords', resumptionToken => "oai_dc/9/1970-01-01T00:00:00Z/$date_to//0/0/" . ($first_bn + 9) },
     { ListRecords => {
         record => $oaidc[9],
     },
 });
 
-$schema->storage->txn_rollback;
+#  List records, but now transformed by XSLT
+t::lib::Mocks::mock_preference("OAI-PMH:ConfFile" =>  File::Spec->rel2abs(dirname(__FILE__)) . "/oaiconf.yaml");
+test_query('ListRecords marcxml with xsl transformation',
+    { verb => 'ListRecords', metadataPrefix => 'marcxml' },
+    { ListRecords => {
+        record => [ @marcxml_transformed[0..2] ],
+        resumptionToken => {
+            content => re( qr{^marcxml/3////0/0/\d+$} ),
+            cursor => 3,
+        }
+    },
+});
+t::lib::Mocks::mock_preference("OAI-PMH:ConfFile" => '');
 
-sub manipulate_timestamp {
-# This eliminates waiting a few seconds in order to get a higher timestamp
-# Works only for 60 records..
-    my ( $index, $bibno, $timestamp ) = @_;
-    return $timestamp if $timestamp !~ /\d{2}Z/;
-    my $secs = sprintf( "%02d", $index );
-    $timestamp =~ s/\d{2}Z/${secs}Z/;
-    $dbh->do("UPDATE biblioitems SET timestamp=? WHERE biblionumber=?", undef,
-        ( $timestamp, $bibno ));
-    return $timestamp;
-}
+restore_time();
+
+subtest 'Bug 19725: OAI-PMH ListRecords and ListIdentifiers should use biblio_metadata.timestamp' => sub {
+    plan tests => 1;
+
+    # Wait 1 second to be sure no timestamp will be equal to $from defined below
+    sleep 1;
+
+    # Modify record to trigger auto update of timestamp
+    (my $biblionumber = $marcxml[0]->{header}->{identifier}) =~ s/^.*:(.*)/$1/;
+    my $record = GetMarcBiblio({biblionumber => $biblionumber});
+    $record->append_fields(MARC::Field->new(999, '', '', z => '_'));
+    ModBiblio( $record, $biblionumber );
+    my $from_dt = dt_from_string(
+        Koha::Biblio::Metadatas->find({ biblionumber => $biblionumber, format => 'marcxml', schema => 'MARC21' })->timestamp
+    );
+    my $from = $from_dt->ymd . 'T' . $from_dt->hms . 'Z';
+    $oaidc[0]->{header}->{datestamp} = $from;
+
+    test_query(
+        'ListRecords oai_dc with parameter from',
+        { verb => 'ListRecords', metadataPrefix => 'oai_dc', from => $from },
+        { ListRecords => {
+            record => $oaidc[0],
+        },
+    });
+};
+
+subtest 'Bug 20665: OAI-PMH Provider should reset the MySQL connection time zone' => sub {
+    plan tests => 2;
+
+    # Set time zone to SYSTEM so that it can be checked later
+    $dbh->do("SET time_zone='SYSTEM'");
+
+
+    test_query('ListIdentifiers without metadataPrefix', {verb => 'ListIdentifiers'}, {
+        error => {
+            code => 'badArgument',
+            content => "Required argument 'metadataPrefix' was undefined",
+        },
+    });
+
+    my $sth = C4::Context->dbh->prepare('SELECT @@session.time_zone');
+    $sth->execute();
+    my ( $tz ) = $sth->fetchrow();
+
+    ok ( $tz eq 'SYSTEM', 'MySQL connection time zone is SYSTEM' );
+};
+
+
+$schema->storage->txn_rollback;