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>.
21 use Test::Deep qw( cmp_deeply re );
22 use Test::MockTime qw/set_fixed_time set_relative_time restore_time/;
24 use Test::More tests => 34;
34 use t::lib::TestBuilder;
36 use C4::Biblio qw( AddBiblio GetMarcBiblio ModBiblio DelBiblio );
38 use C4::OAI::Sets qw(AddOAISet);
40 use Koha::Biblio::Metadatas;
42 use Koha::DateUtils qw( dt_from_string );
45 use_ok('Koha::OAI::Server::DeletedRecord');
46 use_ok('Koha::OAI::Server::Description');
47 use_ok('Koha::OAI::Server::GetRecord');
48 use_ok('Koha::OAI::Server::Identify');
49 use_ok('Koha::OAI::Server::ListBase');
50 use_ok('Koha::OAI::Server::ListIdentifiers');
51 use_ok('Koha::OAI::Server::ListMetadataFormats');
52 use_ok('Koha::OAI::Server::ListRecords');
53 use_ok('Koha::OAI::Server::ListSets');
54 use_ok('Koha::OAI::Server::Record');
55 use_ok('Koha::OAI::Server::Repository');
56 use_ok('Koha::OAI::Server::ResumptionToken');
59 use constant NUMBER_OF_MARC_RECORDS => 10;
61 # Mocked CGI module in order to be able to send CGI parameters to OAI Server
63 my $module = Test::MockModule->new('CGI');
64 $module->mock('Vars', sub { %param; });
66 my $schema = Koha::Database->schema;
67 $schema->storage->txn_begin;
68 my $dbh = C4::Context->dbh;
70 $dbh->do("SET time_zone='+00:00'");
71 $dbh->do('DELETE FROM issues');
72 $dbh->do('DELETE FROM biblio');
73 $dbh->do('DELETE FROM deletedbiblio');
74 $dbh->do('DELETE FROM deletedbiblioitems');
75 $dbh->do('DELETE FROM deleteditems');
76 $dbh->do('DELETE FROM oai_sets');
78 set_fixed_time(CORE::time());
80 my $base_datetime = dt_from_string(undef, undef, 'UTC');
81 my $date_added = $base_datetime->ymd . ' ' .$base_datetime->hms . 'Z';
82 my $date_to = substr($date_added, 0, 10) . 'T23:59:59Z';
83 my (@header, @marcxml, @oaidc, @marcxml_transformed);
84 my $sth = $dbh->prepare('UPDATE biblioitems SET timestamp=? WHERE biblionumber=?');
85 my $sth2 = $dbh->prepare('UPDATE biblio_metadata SET timestamp=? WHERE biblionumber=?');
89 foreach my $index ( 0 .. NUMBER_OF_MARC_RECORDS - 1 ) {
90 my $record = MARC::Record->new();
91 if (C4::Context->preference('marcflavour') eq 'UNIMARC') {
92 $record->append_fields( MARC::Field->new('101', '', '', 'a' => "lng" ) );
93 $record->append_fields( MARC::Field->new('200', '', '', 'a' => "Title $index" ) );
94 $record->append_fields( MARC::Field->new('952', '', '', 'a' => "Code" ) );
96 $record->append_fields( MARC::Field->new('008', ' lng' ) );
97 $record->append_fields( MARC::Field->new('245', '', '', 'a' => "Title $index" ) );
98 $record->append_fields( MARC::Field->new('952', '', '', 'a' => "Code" ) );
100 my ($biblionumber) = AddBiblio($record, '');
101 $first_bn = $biblionumber unless $first_bn;
102 my $timestamp = $base_datetime->ymd . ' ' .$base_datetime->hms;
103 $sth->execute($timestamp,$biblionumber);
104 $sth2->execute($timestamp,$biblionumber);
106 $timestamp =~ s/ /T/;
107 $record = GetMarcBiblio({ biblionumber => $biblionumber });
108 my $record_transformed = $record->clone;
109 $record_transformed->delete_fields( $record_transformed->field('952'));
110 $record_transformed = XMLin($record_transformed->as_xml_record);
111 $record = XMLin($record->as_xml_record);
112 push @header, { datestamp => $timestamp, identifier => "TEST:$biblionumber" };
114 'dc:title' => "Title $index",
115 'dc:language' => "lng",
117 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
118 'xmlns:oai_dc' => 'http://www.openarchives.org/OAI/2.0/oai_dc/',
119 'xmlns:dc' => 'http://purl.org/dc/elements/1.1/',
120 'xsi:schemaLocation' => 'http://www.openarchives.org/OAI/2.0/oai_dc/ http://www.openarchives.org/OAI/2.0/oai_dc.xsd',
122 if (C4::Context->preference('marcflavour') eq 'UNIMARC') {
123 $dc->{'dc:identifier'} = $biblionumber;
126 header => $header[$index],
132 header => $header[$index],
138 push @marcxml_transformed, {
139 header => $header[$index],
141 record => $record_transformed,
147 'LibraryName' => 'My Library',
149 'OAI-PMH:archiveID' => 'TEST',
150 'OAI-PMH:ConfFile' => '',
151 'OAI-PMH:MaxCount' => 3,
152 'OAI-PMH:DeletedRecord' => 'persistent',
154 while ( my ($name, $value) = each %$syspref ) {
155 t::lib::Mocks::mock_preference( $name => $value );
159 my ($test, $param, $expected) = @_;
162 my %full_expected = (
165 request => 'http://localhost',
166 xmlns => 'http://www.openarchives.org/OAI/2.0/',
167 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
168 'xsi:schemaLocation' => 'http://www.openarchives.org/OAI/2.0/ http://www.openarchives.org/OAI/2.0/OAI-PMH.xsd',
176 open STDOUT, '>', \$stdout;
177 Koha::OAI::Server::Repository->new();
178 $response = XMLin($stdout);
181 delete $response->{responseDate};
182 unless (cmp_deeply($response, \%full_expected, $test)) {
184 "PARAM:" . YAML::XS::Dump($param) .
185 "EXPECTED:" . YAML::XS::Dump(\%full_expected) .
186 "RESPONSE:" . YAML::XS::Dump($response);
190 test_query('ListMetadataFormats', {verb => 'ListMetadataFormats'}, {
191 ListMetadataFormats => {
194 metadataNamespace => 'http://www.openarchives.org/OAI/2.0/oai_dc/',
195 metadataPrefix=> 'oai_dc',
196 schema => 'http://www.openarchives.org/OAI/2.0/oai_dc.xsd',
199 metadataNamespace => 'http://www.loc.gov/MARC21/slim http://www.loc.gov/standards/marcxml/schema/MARC21slim',
200 metadataPrefix => 'marc21',
201 schema => 'http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd',
204 metadataNamespace => 'http://www.loc.gov/MARC21/slim http://www.loc.gov/standards/marcxml/schema/MARC21slim',
205 metadataPrefix => 'marcxml',
206 schema => 'http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd',
212 test_query('ListIdentifiers without metadataPrefix', {verb => 'ListIdentifiers'}, {
214 code => 'badArgument',
215 content => "Required argument 'metadataPrefix' was undefined",
219 test_query('ListIdentifiers', {verb => 'ListIdentifiers', metadataPrefix => 'marcxml'}, {
221 header => [ @header[0..2] ],
223 content => re( qr{^marcxml/3////0/0/\d+$} ),
229 test_query('ListIdentifiers', {verb => 'ListIdentifiers', metadataPrefix => 'marcxml'}, {
231 header => [ @header[0..2] ],
233 content => re( qr{^marcxml/3////0/0/\d+$} ),
240 'ListIdentifiers with resumptionToken 1',
241 { verb => 'ListIdentifiers', resumptionToken => "marcxml/3/1970-01-01T00:00:00Z/$date_to//0/0/" . ($first_bn + 3) },
244 header => [ @header[3..5] ],
246 content => re( qr{^marcxml/6/1970-01-01T00:00:00Z/$date_to//0/0/\d+$} ),
254 'ListIdentifiers with resumptionToken 2',
255 { verb => 'ListIdentifiers', resumptionToken => "marcxml/6/1970-01-01T00:00:00Z/$date_to//0/0/" . ($first_bn + 6) },
258 header => [ @header[6..8] ],
260 content => re( qr{^marcxml/9/1970-01-01T00:00:00Z/$date_to//0/0/\d+$} ),
268 'ListIdentifiers with resumptionToken 3, response without resumption',
269 { verb => 'ListIdentifiers', resumptionToken => "marcxml/9/1970-01-01T00:00:00Z/$date_to//0/0/" . ($first_bn + 9) },
272 header => $header[9],
277 test_query('ListRecords marcxml without metadataPrefix', {verb => 'ListRecords'}, {
279 code => 'badArgument',
280 content => "Required argument 'metadataPrefix' was undefined",
284 test_query('ListRecords marcxml', {verb => 'ListRecords', metadataPrefix => 'marcxml'}, {
286 record => [ @marcxml[0..2] ],
288 content => re( qr{^marcxml/3////0/0/\d+$} ),
295 'ListRecords marcxml with resumptionToken 1',
296 { verb => 'ListRecords', resumptionToken => "marcxml/3////0/0/" . ($first_bn + 3) },
298 record => [ @marcxml[3..5] ],
300 content => re( qr{^marcxml/6////0/0/\d+$} ),
307 'ListRecords marcxml with resumptionToken 2',
308 { verb => 'ListRecords', resumptionToken => "marcxml/6/1970-01-01T00:00:00Z/$date_to//0/0/" . ($first_bn + 6) },
310 record => [ @marcxml[6..8] ],
312 content => re( qr{^marcxml/9/1970-01-01T00:00:00Z/$date_to//0/0/\d+$} ),
318 # Last record, so no resumption token
320 'ListRecords marcxml with resumptionToken 3, response without resumption',
321 { verb => 'ListRecords', resumptionToken => "marcxml/9/1970-01-01T00:00:00Z/$date_to//0/0/" . ($first_bn + 9) },
323 record => $marcxml[9],
327 test_query('ListRecords oai_dc', {verb => 'ListRecords', metadataPrefix => 'oai_dc'}, {
329 record => [ @oaidc[0..2] ],
331 content => re( qr{^oai_dc/3////0/0/\d+$} ),
338 'ListRecords oai_dc with resumptionToken 1',
339 { verb => 'ListRecords', resumptionToken => "oai_dc/3////0/0/" . ($first_bn + 3) },
341 record => [ @oaidc[3..5] ],
343 content => re( qr{^oai_dc/6////0/0/\d+$} ),
350 'ListRecords oai_dc with resumptionToken 2',
351 { verb => 'ListRecords', resumptionToken => "oai_dc/6/1970-01-01T00:00:00Z/$date_to//0/0/" . ($first_bn + 6) },
353 record => [ @oaidc[6..8] ],
355 content => re( qr{^oai_dc/9/1970-01-01T00:00:00Z/$date_to//0/0/\d+$} ),
361 # Last record, so no resumption token
363 'ListRecords oai_dc with resumptionToken 3, response without resumption',
364 { verb => 'ListRecords', resumptionToken => "oai_dc/9/1970-01-01T00:00:00Z/$date_to//0/0/" . ($first_bn + 9) },
370 # List records, but now transformed by XSLT
371 t::lib::Mocks::mock_preference("OAI-PMH:ConfFile" => File::Spec->rel2abs(dirname(__FILE__)) . "/oaiconf.yaml");
372 test_query('ListRecords marcxml with xsl transformation',
373 { verb => 'ListRecords', metadataPrefix => 'marcxml' },
375 record => [ @marcxml_transformed[0..2] ],
377 content => re( qr{^marcxml/3////0/0/\d+$} ),
382 t::lib::Mocks::mock_preference("OAI-PMH:ConfFile" => '');
386 subtest 'Bug 19725: OAI-PMH ListRecords and ListIdentifiers should use biblio_metadata.timestamp' => sub {
389 # Wait 1 second to be sure no timestamp will be equal to $from defined below
392 # Modify record to trigger auto update of timestamp
393 (my $biblionumber = $marcxml[0]->{header}->{identifier}) =~ s/^.*:(.*)/$1/;
394 my $record = GetMarcBiblio({biblionumber => $biblionumber});
395 $record->append_fields(MARC::Field->new(999, '', '', z => '_'));
396 ModBiblio( $record, $biblionumber );
397 my $from_dt = dt_from_string(
398 Koha::Biblio::Metadatas->find({ biblionumber => $biblionumber, format => 'marcxml', schema => 'MARC21' })->timestamp
400 my $from = $from_dt->ymd . 'T' . $from_dt->hms . 'Z';
401 $oaidc[0]->{header}->{datestamp} = $from;
404 'ListRecords oai_dc with parameter from',
405 { verb => 'ListRecords', metadataPrefix => 'oai_dc', from => $from },
412 subtest 'Bug 20665: OAI-PMH Provider should reset the MySQL connection time zone' => sub {
415 # Set time zone to SYSTEM so that it can be checked later
416 $dbh->do("SET time_zone='SYSTEM'");
419 test_query('ListIdentifiers without metadataPrefix', {verb => 'ListIdentifiers'}, {
421 code => 'badArgument',
422 content => "Required argument 'metadataPrefix' was undefined",
426 my $sth = C4::Context->dbh->prepare('SELECT @@session.time_zone');
428 my ( $tz ) = $sth->fetchrow();
430 ok ( $tz eq 'SYSTEM', 'MySQL connection time zone is SYSTEM' );
434 $schema->storage->txn_rollback;
436 subtest 'ListSets tests' => sub {
440 t::lib::Mocks::mock_preference( 'OAI::PMH' => 1 );
441 t::lib::Mocks::mock_preference( 'OAI-PMH:MaxCount' => 3 );
443 $schema->storage->txn_begin;
445 $dbh->do('DELETE FROM oai_sets');
447 # Add a bunch of sets
448 my @first_page_sets = ();
449 for my $i ( 1 .. 3 ) {
452 { 'spec' => "setSpec_$i",
453 'name' => "setName_$i",
456 push @first_page_sets, { setSpec => "setSpec_$i", setName => "setName_$i" };
459 # Add more to force pagination
460 my @second_page_sets = ();
461 for my $i ( 4 .. 6 ) {
464 { 'spec' => "setSpec_$i",
465 'name' => "setName_$i",
468 push @second_page_sets, { setSpec => "setSpec_$i", setName => "setName_$i" };
472 { 'spec' => "setSpec_7",
473 'name' => "setName_7",
479 { verb => 'ListSets' },
482 content => re( qr{^/3////1/0/4$} ),
485 set => \@first_page_sets
492 { verb => 'ListSets', resumptionToken => '/3////1/0/4' },
495 content => re( qr{^/6////1/0/7$} ),
498 set => \@second_page_sets
505 { verb => 'ListSets', resumptionToken => "/6////1/0/7" },
507 set => { setSpec => "setSpec_7", setName => "setName_7" }
512 $schema->storage->txn_rollback;
515 subtest 'Tests for timestamp handling' => sub {
519 t::lib::Mocks::mock_preference( 'OAI::PMH' => 1 );
520 t::lib::Mocks::mock_preference( 'OAI-PMH:MaxCount' => 3 );
521 t::lib::Mocks::mock_preference( 'OAI-PMH:ConfFile' => File::Spec->rel2abs(dirname(__FILE__)) . '/oaiconf_items.yaml' );
523 $schema->storage->txn_begin;
525 my $sth_metadata = $dbh->prepare('UPDATE biblio_metadata SET timestamp=? WHERE biblionumber=?');
526 my $sth_del_metadata = $dbh->prepare('UPDATE deletedbiblio_metadata SET timestamp=? WHERE biblionumber=?');
527 my $sth_item = $dbh->prepare('UPDATE items SET timestamp=? WHERE itemnumber=?');
528 my $sth_del_item = $dbh->prepare('UPDATE deleteditems SET timestamp=? WHERE itemnumber=?');
530 my $builder = t::lib::TestBuilder->new;
532 set_fixed_time(CORE::time());
534 my $utc_datetime = dt_from_string(undef, undef, 'UTC');
535 my $utc_timestamp = $utc_datetime->ymd . 'T' . $utc_datetime->hms . 'Z';
536 my $timestamp = dt_from_string(undef, 'sql');
538 # Test a bib with one item
539 my $biblio1 = $builder->build_sample_biblio;
540 Koha::Biblios->find($biblio1->biblionumber)->timestamp('1970-05-07 13:36:23')->store;
542 $sth_metadata->execute($timestamp, $biblio1->biblionumber);
543 my $item1 = $builder->build_sample_item(
545 biblionumber => $biblio1->biblionumber
548 $sth_item->execute($timestamp, $item1->itemnumber);
551 verb => 'ListRecords',
552 metadataPrefix => 'marc21',
553 from => $utc_timestamp
555 my $list_no_items = {
556 verb => 'ListRecords',
557 metadataPrefix => 'marcxml',
558 from => $utc_timestamp
563 metadataPrefix => 'marc21',
564 identifier => 'TEST:' . $biblio1->biblionumber
568 metadataPrefix => 'marcxml',
569 identifier => 'TEST:' . $biblio1->biblionumber
575 datestamp => $utc_timestamp,
576 identifier => 'TEST:' . $biblio1->biblionumber
580 GetMarcBiblio({ biblionumber => $biblio1->biblionumber, embed_items => 1, opac => 1 })->as_xml_record()
585 my $expected_no_items = {
588 datestamp => $utc_timestamp,
589 identifier => 'TEST:' . $biblio1->biblionumber
593 GetMarcBiblio({ biblionumber => $biblio1->biblionumber, embed_items => 0, opac => 1 })->as_xml_record()
600 'ListRecords - biblio with a single item',
602 { ListRecords => $expected }
605 'ListRecords - biblio with a single item (items not returned)',
607 { ListRecords => $expected_no_items }
610 'GetRecord - biblio with a single item',
612 { GetRecord => $expected }
615 'GetRecord - biblio with a single item (items not returned)',
617 { GetRecord => $expected_no_items }
619 t::lib::Mocks::mock_preference('KohaAdminEmailAddress', 'root@localhost');
621 'Identify - earliestDatestamp in the right format',
622 { verb => 'Identify' },
624 adminEmail => 'root@localhost',
625 baseURL => 'http://localhost',
626 compression => 'gzip',
627 deletedRecord => 'persistent',
628 earliestDatestamp => '1970-05-07T13:36:23Z',
629 granularity => 'YYYY-MM-DDThh:mm:ssZ',
630 protocolVersion => '2.0',
631 repositoryName => 'My Library',
636 # Add an item 10 seconds later and check results
637 set_relative_time(10);
639 $utc_datetime = dt_from_string(undef, undef, 'UTC');
640 $utc_timestamp = $utc_datetime->ymd . 'T' . $utc_datetime->hms . 'Z';
641 $timestamp = dt_from_string(undef, 'sql');
643 my $item2 = $builder->build_sample_item(
645 biblionumber => $biblio1->biblionumber
648 $sth_item->execute($timestamp, $item2->itemnumber);
650 $expected->{record}{header}{datestamp} = $utc_timestamp;
651 $expected->{record}{metadata}{record} = XMLin(
652 GetMarcBiblio({ biblionumber => $biblio1->biblionumber, embed_items => 1, opac => 1 })->as_xml_record()
656 'ListRecords - biblio with two items',
658 { ListRecords => $expected }
661 'ListRecords - biblio with two items (items not returned)',
663 { ListRecords => $expected_no_items }
666 'GetRecord - biblio with a two items',
668 { GetRecord => $expected }
671 'GetRecord - biblio with a two items (items not returned)',
673 { GetRecord => $expected_no_items }
676 # Set biblio timestamp 10 seconds later and check results
677 set_relative_time(10);
678 $utc_datetime = dt_from_string(undef, undef, 'UTC');
679 $utc_timestamp= $utc_datetime->ymd . 'T' . $utc_datetime->hms . 'Z';
680 $timestamp = dt_from_string(undef, 'sql');
682 $sth_metadata->execute($timestamp, $biblio1->biblionumber);
684 $expected->{record}{header}{datestamp} = $utc_timestamp;
685 $expected_no_items->{record}{header}{datestamp} = $utc_timestamp;
688 "ListRecords - biblio with timestamp higher than item's",
690 { ListRecords => $expected }
693 "ListRecords - biblio with timestamp higher than item's (items not returned)",
695 { ListRecords => $expected_no_items }
698 "GetRecord - biblio with timestamp higher than item's",
700 { GetRecord => $expected }
703 "GetRecord - biblio with timestamp higher than item's (items not returned)",
705 { GetRecord => $expected_no_items }
708 # Delete an item 10 seconds later and check results
709 set_relative_time(10);
710 $utc_datetime = dt_from_string(undef, undef, 'UTC');
711 $utc_timestamp = $utc_datetime->ymd . 'T' . $utc_datetime->hms . 'Z';
713 $item1->safe_delete({ skip_record_index =>1 });
714 $sth_del_item->execute($timestamp, $item1->itemnumber);
716 $expected->{record}{header}{datestamp} = $utc_timestamp;
717 $expected->{record}{metadata}{record} = XMLin(
718 GetMarcBiblio({ biblionumber => $biblio1->biblionumber, embed_items => 1, opac => 1 })->as_xml_record()
722 'ListRecords - biblio with existing and deleted item',
724 { ListRecords => $expected }
727 'ListRecords - biblio with existing and deleted item (items not returned)',
729 { ListRecords => $expected_no_items }
732 'GetRecord - biblio with existing and deleted item',
734 { GetRecord => $expected }
737 'GetRecord - biblio with existing and deleted item (items not returned)',
739 { GetRecord => $expected_no_items }
742 # Delete also the second item and verify results
743 $item2->safe_delete({ skip_record_index =>1 });
744 $sth_del_item->execute($timestamp, $item2->itemnumber);
746 $expected->{record}{metadata}{record} = XMLin(
747 GetMarcBiblio({ biblionumber => $biblio1->biblionumber, embed_items => 1, opac => 1 })->as_xml_record()
751 'ListRecords - biblio with two deleted items',
753 { ListRecords => $expected }
756 'ListRecords - biblio with two deleted items (items not returned)',
758 { ListRecords => $expected_no_items }
761 'GetRecord - biblio with two deleted items',
763 { GetRecord => $expected }
766 'GetRecord - biblio with two deleted items (items not returned)',
768 { GetRecord => $expected_no_items }
771 # Delete the biblio 10 seconds later and check results
772 set_relative_time(10);
773 $utc_datetime = dt_from_string(undef, undef, 'UTC');
774 $utc_timestamp = $utc_datetime->ymd . 'T' . $utc_datetime->hms . 'Z';
775 $timestamp = dt_from_string(undef, 'sql');
777 is(undef, DelBiblio($biblio1->biblionumber, { skip_record_index =>1 }), 'Biblio deleted');
778 $sth_del_metadata->execute($timestamp, $biblio1->biblionumber);
780 my $expected_header = {
783 datestamp => $utc_timestamp,
784 identifier => 'TEST:' . $biblio1->biblionumber,
791 'ListRecords - deleted biblio with two deleted items',
793 { ListRecords => $expected_header }
796 'ListRecords - deleted biblio with two deleted items (items not returned)',
798 { ListRecords => $expected_header }
801 'GetRecord - deleted biblio with two deleted items',
803 { GetRecord => $expected_header }
806 'GetRecord - deleted biblio with two deleted items (items not returned)',
808 { GetRecord => $expected_header }
811 # Add a second biblio 10 seconds later and check that both are returned properly
812 set_relative_time(10);
813 $utc_datetime = dt_from_string(undef, undef, 'UTC');
814 $utc_timestamp = $utc_datetime->ymd . 'T' . $utc_datetime->hms . 'Z';
815 $timestamp = dt_from_string(undef, 'sql');
817 my $biblio2 = $builder->build_sample_biblio();
818 $sth_metadata->execute($timestamp, $biblio2->biblionumber);
822 $expected_header->{record},
825 datestamp => $utc_timestamp,
826 identifier => 'TEST:' . $biblio2->biblionumber
830 GetMarcBiblio({ biblionumber => $biblio2->biblionumber, embed_items => 1, opac => 1 })->as_xml_record()
836 my $expected2_no_items = {
838 $expected_header->{record},
841 datestamp => $utc_timestamp,
842 identifier => 'TEST:' . $biblio2->biblionumber
846 GetMarcBiblio({ biblionumber => $biblio2->biblionumber, embed_items => 0, opac => 1 })->as_xml_record()
854 'ListRecords - deleted biblio and normal biblio',
856 { ListRecords => $expected2 }
859 'ListRecords - deleted biblio and normal biblio (items not returned)',
861 { ListRecords => $expected2_no_items }
866 $schema->storage->txn_rollback;
869 subtest 'ListSets() tests' => sub {
873 $schema->storage->txn_begin;
876 $schema->resultset('OaiSet')->delete;
879 'ListSets - no sets should return a noSetHierarchy exception',
880 { verb => 'ListSets' },
883 code => 'noSetHierarchy',
884 content => 'There are no OAI sets defined',
890 AddOAISet({ spec => 'set_1', name => 'Set 1' });
891 AddOAISet({ spec => 'set_2', name => 'Set 2' });
894 'ListSets - no sets should return a noSetHierarchy exception',
895 { verb => 'ListSets' },
899 { setSpec => 'set_1', setName => 'Set 1' },
900 { setSpec => 'set_2', setName => 'Set 2' },
906 $schema->storage->txn_rollback;