7c9e8be1defc281e41bcb6094945ce20e43aab3e
[koha.git] / t / db_dependent / UsageStats.t
1 # Copyright 2015 BibLibre
2 #
3 # This file is part of Koha.
4 #
5 # Koha is free software; you can redistribute it and/or modify it under the
6 # terms of the GNU General Public License as published by the Free Software
7 # Foundation; either version 3 of the License, or (at your option) any later
8 # version.
9 #
10 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
11 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12 # A PARTICULAR PURPOSE. See the GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License along
15 # with Koha; if not, see <http://www.gnu.org/licenses>.
16
17 use Modern::Perl;
18 use Test::More tests => 57;
19 use t::lib::Mocks qw(mock_preference);
20 use t::lib::TestBuilder;
21 use POSIX qw(strftime);
22 use Data::Dumper;
23 use Koha::Biblios;
24
25 use Koha::Libraries;
26
27 BEGIN {
28     use_ok('C4::UsageStats');
29     use_ok('C4::Context');
30     use_ok('C4::Biblio');
31     use_ok( 'C4::AuthoritiesMarc', qw(AddAuthority) );
32     use_ok('C4::Reserves');
33     use_ok('MARC::Record');
34     use_ok('Koha::Acquisition::Orders');
35 }
36
37 can_ok(
38     'C4::UsageStats', qw(
39       NeedUpdate
40       BuildReport
41       ReportToCommunity
42       _count )
43 );
44
45 my $schema  = Koha::Database->new->schema;
46 $schema->storage->txn_begin;
47 my $builder = t::lib::TestBuilder->new;
48 my $dbh = C4::Context->dbh;
49
50 $dbh->do('DELETE FROM issues');
51 $dbh->do('DELETE FROM biblio');
52 $dbh->do('DELETE FROM items');
53 $dbh->do('DELETE FROM auth_header');
54 $dbh->do('DELETE FROM old_issues');
55 $dbh->do('DELETE FROM old_reserves');
56 $dbh->do('DELETE FROM borrowers');
57 $dbh->do('DELETE FROM aqorders');
58 $dbh->do('DELETE FROM subscription');
59
60 #################################################
61 #             Testing Subs
62 #################################################
63
64 # ---------- Testing NeedUpdate -----------------
65
66 #Mocking C4::Context->preference("UsageStatsLastUpdateTime") to 0
67 my $now = strftime( "%s", localtime );
68 t::lib::Mocks::mock_preference( "UsageStatsLastUpdateTime", 0 );
69
70 my $update = C4::UsageStats->NeedUpdate;
71 is( $update, 1, "There is no last update, update needed" );
72
73 #Mocking C4::Context->preference("UsageStatsLastUpdateTime") to now
74 $now = strftime( "%s", localtime );
75 t::lib::Mocks::mock_preference( "UsageStatsLastUpdateTime", $now );
76
77 $update = C4::UsageStats->NeedUpdate;
78 is( $update, 0, "Last update just be done, no update needed " );
79
80 my $nb_of_libraries = Koha::Libraries->count;
81
82 # ---------- Testing BuildReport ----------------
83
84 #Test report->library -----------------
85 #mock to 0
86 t::lib::Mocks::mock_preference( "UsageStatsID",          0 );
87 t::lib::Mocks::mock_preference( "UsageStatsLibraryName", 0 );
88 t::lib::Mocks::mock_preference( "UsageStatsLibrariesInfo",  0 );
89 t::lib::Mocks::mock_preference( "UsageStatsLibraryType", 0 );
90 t::lib::Mocks::mock_preference( "UsageStatsCountry",     0 );
91 t::lib::Mocks::mock_preference( "UsageStatsLibraryUrl",  0 );
92
93 my $report = C4::UsageStats->BuildReport();
94
95 isa_ok( $report,              'HASH',  '$report is a HASH' );
96 isa_ok( $report->{libraries}, 'ARRAY', '$report->{libraries} is an ARRAY' );
97 is( scalar( @{ $report->{libraries} } ), 0, "There are 0 fields in libraries, libraries info are not shared" );
98 is( $report->{installation}->{koha_id}, 0,  "UsageStatsID          is good" );
99 is( $report->{installation}->{name},    '', "UsageStatsLibraryName is good" );
100 is( $report->{installation}->{url},     '', "UsageStatsLibraryUrl  is good" );
101 is( $report->{installation}->{type},    '', "UsageStatsLibraryType is good" );
102 is( $report->{installation}->{country}, '', "UsageStatsCountry     is good" );
103
104
105 #mock with values
106 t::lib::Mocks::mock_preference( "UsageStatsID",          1 );
107 t::lib::Mocks::mock_preference( "UsageStatsLibraryName", 'NAME' );
108 t::lib::Mocks::mock_preference( "UsageStatsLibraryUrl",  'URL' );
109 t::lib::Mocks::mock_preference( "UsageStatsLibraryType", 'TYPE' );
110 t::lib::Mocks::mock_preference( "UsageStatsCountry",     'COUNTRY' );
111 t::lib::Mocks::mock_preference( "UsageStatsLibrariesInfo", 1 );
112 t::lib::Mocks::mock_preference( "UsageStatsGeolocation", 1 );
113
114
115 $report = C4::UsageStats->BuildReport();
116
117 isa_ok( $report,              'HASH',  '$report is a HASH' );
118 isa_ok( $report->{libraries}, 'ARRAY', '$report->{libraries} is an ARRAY' );
119 is( scalar( @{ $report->{libraries} } ), $nb_of_libraries, "There are 6 fields in $report->{libraries}" );
120 is( $report->{installation}->{koha_id}, 1,     "UsageStatsID          is good" );
121 is( $report->{installation}->{name},   'NAME', "UsageStatsLibraryName is good" );
122 is( $report->{installation}->{url},     'URL', "UsageStatsLibraryUrl  is good" );
123 is( $report->{installation}->{type},   'TYPE', "UsageStatsLibraryType is good" );
124 is( $report->{installation}->{country}, 'COUNTRY', "UsageStatsCountry is good" );
125
126 #Test report->volumetry ---------------
127 #with original values
128 $report = C4::UsageStats->BuildReport();
129
130 isa_ok( $report,              'HASH', '$report is a HASH' );
131 isa_ok( $report->{volumetry}, 'HASH', '$report->{volumetry} is a HASH' );
132 is( scalar( keys %{$report->{volumetry}} ), 8, "There are 8 fields in $report->{volumetry}" );
133 is( $report->{volumetry}->{biblio},         0, "There is no biblio" );
134 is( $report->{volumetry}->{items},          0, "There is no items" );
135 is( $report->{volumetry}->{auth_header},    0, "There is no auth_header" );
136 is( $report->{volumetry}->{old_issues},     0, "There is no old_issues" );
137 is( $report->{volumetry}->{old_reserves},   0, "There is no old_reserves" );
138 is( $report->{volumetry}->{borrowers},      0, "There is no borrowers" );
139 is( $report->{volumetry}->{aqorders},       0, "There is no aqorders" );
140 is( $report->{volumetry}->{subscription},   0, "There is no subscription" );
141
142 #after adding objects
143 construct_objects_needed();
144
145 $report = C4::UsageStats->BuildReport();
146
147 isa_ok( $report,              'HASH', '$report is a HASH' );
148 isa_ok( $report->{volumetry}, 'HASH', '$report->{volumetry} is a HASH' );
149 is( scalar( keys %{$report->{volumetry}} ), 8, "There are 8 fields in $report->{volumetry}" );
150 is( $report->{volumetry}->{biblio},         3, "There are 3 biblio" );
151 is( $report->{volumetry}->{items},          3, "There are 3 items" );
152 is( $report->{volumetry}->{auth_header},    2, "There are 2 auth_header" );
153 is( $report->{volumetry}->{old_issues},     1, "There is  1 old_issues" );
154 is( $report->{volumetry}->{old_reserves},   1, "There is  1 old_reserves" );
155 is( $report->{volumetry}->{borrowers},      3, "There are 3 borrowers" );
156 is( $report->{volumetry}->{aqorders},       1, "There is  1 aqorders" );
157 is( $report->{volumetry}->{subscription},   1, "There is  1 subscription" );
158
159 #Test report->systempreferences -------
160 #mock to 0
161 mocking_systempreferences_to_a_set_value(0);
162
163 $report = C4::UsageStats->BuildReport();
164 isa_ok( $report,                      'HASH', '$report is a HASH' );
165 isa_ok( $report->{systempreferences}, 'HASH', '$report->{systempreferences} is a HASH' );
166 verif_systempreferences_values( $report, 0 );
167
168 #mock with values
169 mocking_systempreferences_to_a_set_value(1);
170
171 $report = C4::UsageStats->BuildReport();
172 isa_ok( $report,                      'HASH', '$report is a HASH' );
173 isa_ok( $report->{systempreferences}, 'HASH', '$report->{systempreferences} is a HASH' );
174 verif_systempreferences_values( $report, 1 );
175
176 #Test if unwanted syspref are not sent
177 is( $report->{systempreferences}->{useDischarge}, undef, 'useDischarge should not be shared');
178 is( $report->{systempreferences}->{OpacUserJS},   undef, 'OpacUserJS   should not be shared');
179
180 # ---------- Testing ReportToCommunity ----------
181
182 # ---------- Testing _count ---------------------
183 my $query = '
184   SELECT count(*)
185   FROM   borrowers
186   ';
187 my $count = $dbh->selectrow_array($query);
188
189 my $nb_fields = C4::UsageStats::_count('borrowers');
190 is( $nb_fields, $count, "_count return the good number of fields" );
191
192 #################################################
193 #             Subs
194 #################################################
195
196 # Adding :
197 # 3 borrowers
198 # 4 biblio
199 # 3 biblio items
200 # 3 items
201 # 2 auth_header
202 # 1 old_issues
203 # 1 old_reserves
204 # 1 subscription
205 # 1 aqorders
206 sub construct_objects_needed {
207
208     # ---------- 3 borrowers  ---------------------
209     my $surname1     = 'Borrower 1';
210     my $surname2     = 'Borrower 2';
211     my $surname3     = 'Borrower 3';
212     my $firstname1   = 'firstname 1';
213     my $firstname2   = 'firstname 2';
214     my $firstname3   = 'firstname 3';
215     my $cardnumber1  = 'test_card1';
216     my $cardnumber2  = 'test_card2';
217     my $cardnumber3  = 'test_card3';
218     my $categorycode = $builder->build({ source => 'Category' })->{categorycode};
219     my $branchcode = $builder->build({ source => 'Branch' })->{branchcode};
220
221     my $query = '
222     INSERT INTO borrowers
223       (surname, firstname, cardnumber, branchcode, categorycode)
224     VALUES (?,?,?,?,?)';
225     my $insert_sth = $dbh->prepare($query);
226     $insert_sth->execute( $surname1, $firstname1, $cardnumber1, $branchcode, $categorycode );
227     my $borrowernumber1 = $dbh->last_insert_id( undef, undef, 'borrowers', undef );
228     $insert_sth->execute( $surname2, $firstname2, $cardnumber2, $branchcode, $categorycode );
229     my $borrowernumber2 = $dbh->last_insert_id( undef, undef, 'borrowers', undef );
230     $insert_sth->execute( $surname3, $firstname3, $cardnumber3, $branchcode, $categorycode );
231     my $borrowernumber3 = $dbh->last_insert_id( undef, undef, 'borrowers', undef );
232
233     # ---------- 3 biblios -----------------------
234     my $title1  = 'Title 1';
235     my $title2  = 'Title 2';
236     my $title3  = 'Title 3';
237     my $author1 = 'Author 1';
238     my $author2 = 'Author 2';
239     my $author3 = 'Author 3';
240
241     $query = '
242     INSERT INTO biblio
243       (title, author, datecreated)
244     VALUES (?,?, NOW())';
245     $insert_sth = $dbh->prepare($query);
246     $insert_sth->execute( $title1, $author1 );
247     my $biblionumber1 = $dbh->last_insert_id( undef, undef, 'biblio', undef );
248     $insert_sth->execute( $title2, undef );
249     my $biblionumber2 = $dbh->last_insert_id( undef, undef, 'biblio', undef );
250     $insert_sth->execute( $title3, $author3 );
251     my $biblionumber3 = $dbh->last_insert_id( undef, undef, 'biblio', undef );
252
253     # ---------- 3 biblio items  -------------------------
254     $query = '
255     INSERT INTO biblioitems
256       (biblionumber, itemtype)
257     VALUES (?,?)';
258     $insert_sth = $dbh->prepare($query);
259     $insert_sth->execute( $biblionumber1, 'Book' );
260     my $biblioitemnumber1 = $dbh->last_insert_id( undef, undef, 'biblioitems', undef );
261     $insert_sth->execute( $biblionumber2, 'Music' );
262     my $biblioitemnumber2 = $dbh->last_insert_id( undef, undef, 'biblioitems', undef );
263     $insert_sth->execute( $biblionumber3, 'Book' );
264     my $biblioitemnumber3 = $dbh->last_insert_id( undef, undef, 'biblioitems', undef );
265
266     # ---------- 3 items  -------------------------
267     my $barcode1 = '111111';
268     my $barcode2 = '222222';
269     my $barcode3 = '333333';
270
271     $query = '
272     INSERT INTO items
273       (biblionumber, biblioitemnumber, barcode, itype)
274     VALUES (?,?,?,?)';
275     $insert_sth = $dbh->prepare($query);
276     $insert_sth->execute( $biblionumber1, $biblioitemnumber1, $barcode1, 'Book' );
277     my $item_number1 = $dbh->last_insert_id( undef, undef, 'items', undef );
278     $insert_sth->execute( $biblionumber2, $biblioitemnumber2, $barcode2, 'Music' );
279     my $item_number2 = $dbh->last_insert_id( undef, undef, 'items', undef );
280     $insert_sth->execute( $biblionumber3, $biblioitemnumber3, $barcode3, 'Book' );
281     my $item_number3 = $dbh->last_insert_id( undef, undef, 'items', undef );
282
283     # ---------- Add 2 auth_header
284     $query = '
285     INSERT INTO auth_header
286       (authtypecode, marcxml)
287     VALUES (?, "")';
288     $insert_sth = $dbh->prepare($query);
289     $insert_sth->execute('atc1');
290     my $authid1 = $dbh->last_insert_id( undef, undef, 'auth_header', undef );
291     $insert_sth->execute('atc2');
292     my $authid2 = $dbh->last_insert_id( undef, undef, 'auth_header', undef );
293
294     # ---------- Add 1 old_issues
295     $query = '
296     INSERT INTO old_issues
297       (issue_id, borrowernumber, branchcode, itemnumber)
298     VALUES ((select coalesce(max(issue_id), 0)+1 from issues),?,?,?)';
299     $insert_sth = $dbh->prepare($query);
300     $insert_sth->execute( $borrowernumber1, $branchcode, $item_number1 );
301     my $issue_id1 = $dbh->last_insert_id( undef, undef, 'old_issues', undef );
302
303     # ---------- Add 1 old_reserves
304     AddReserve(
305         {
306             branchcode     => $branchcode,
307             borrowernumber => $borrowernumber1,
308             biblionumber   => $biblionumber1,
309             priority       => 1,
310             title          => 'Title',
311         }
312     );
313     my $biblio = Koha::Biblios->find( $biblionumber1 );
314     my $holds = $biblio->holds;
315     $holds->next->cancel if $holds->count;
316
317     # ---------- Add 1 aqbudgets
318     $query = '
319     INSERT INTO aqbudgets
320       (budget_amount)
321     VALUES (?)';
322     $insert_sth = $dbh->prepare($query);
323     $insert_sth->execute("20.0");
324     my $aqbudgets1 = $dbh->last_insert_id( undef, undef, 'aqbudgets', undef );
325
326     # ---------- Add 1 aqorders
327     $query = '
328     INSERT INTO aqorders
329       (budget_id, basketno, biblionumber, invoiceid, subscriptionid)
330     VALUES (?,?,?,?,?)';
331     $insert_sth = $dbh->prepare($query);
332     $insert_sth->execute( $aqbudgets1, undef, undef, undef, undef );
333     my $aqorders1 = $dbh->last_insert_id( undef, undef, 'aqorders', undef );
334
335     # --------- Add 1 subscription
336     $query = '
337     INSERT INTO subscription
338       (biblionumber)
339     VALUES (?)';
340     $insert_sth = $dbh->prepare($query);
341     $insert_sth->execute($biblionumber1);
342     my $subscription1 = $dbh->last_insert_id( undef, undef, 'subscription', undef );
343
344 }
345
346 #Change systempreferences values to $set_value
347 sub mocking_systempreferences_to_a_set_value {
348     my $set_value = shift;
349
350     foreach (
351         qw/
352         AcqCreateItem
353         AcqWarnOnDuplicateInvoice
354         AcqViewBaskets
355         BasketConfirmations
356         OrderPdfFormat
357         casAuthentication
358         casLogout
359         AllowPKIAuth
360         DebugLevel
361         delimiter
362         noItemTypeImages
363         virtualshelves
364         AutoLocation
365         IndependentBranches
366         SessionStorage
367         Persona
368         AuthDisplayHierarchy
369         AutoCreateAuthorities
370         BiblioAddsAuthorities
371         AuthorityMergeLimit
372         AuthorityMergeMode
373         UseAuthoritiesForTracings
374         CatalogModuleRelink
375         hide_marc
376         IntranetBiblioDefaultView
377         LabelMARCView
378         OpacSuppression
379         SeparateHoldings
380         UseControlNumber
381         advancedMARCeditor
382         DefaultClassificationSource
383         EasyAnalyticalRecords
384         autoBarcode
385         item-level_itypes
386         marcflavour
387         PrefillItem
388         z3950NormalizeAuthor
389         SpineLabelAutoPrint
390         SpineLabelShowPrintOnBibDetails
391         BlockReturnOfLostItems
392         BlockReturnOfWithdrawnItems
393         CalculateFinesOnReturn
394         AgeRestrictionOverride
395         AllFinesNeedOverride
396         AllowFineOverride
397         AllowItemsOnHoldCheckoutSIP
398         AllowItemsOnHoldCheckoutSCO
399         AllowNotForLoanOverride
400         AllowRenewalLimitOverride
401         AllowReturnToBranch
402         AllowTooManyOverride
403         AutomaticItemReturn
404         AutoRemoveOverduesRestrictions
405         CircControl
406         HomeOrHoldingBranch
407         HomeOrHoldingBranchReturn
408         IssueLostItem
409         IssuingInProcess
410         ManInvInNoissuesCharge
411         OverduesBlockCirc
412         RenewalPeriodBase
413         RenewalSendNotice
414         RentalsInNoissuesCharge
415         ReturnBeforeExpiry
416         TransfersMaxDaysWarning
417         UseBranchTransferLimits
418         useDaysMode
419         UseTransportCostMatrix
420         UseCourseReserves
421         finesCalendar
422         FinesIncludeGracePeriod
423         finesMode
424         RefundLostOnReturnControl
425         WhenLostChargeReplacementFee
426         WhenLostForgiveFine
427         AllowHoldDateInFuture
428         AllowHoldPolicyOverride
429         AllowHoldsOnDamagedItems
430         AllowHoldsOnPatronsPossessions
431         AutoResumeSuspendedHolds
432         canreservefromotherbranches
433         decreaseLoanHighHolds
434         DisplayMultiPlaceHold
435         emailLibrarianWhenHoldIsPlaced
436         ExpireReservesMaxPickUpDelay
437         OPACAllowHoldDateInFuture
438         OPACAllowUserToChooseBranch
439         ReservesControlBranch
440         ReservesNeedReturns
441         SuspendHoldsIntranet
442         SuspendHoldsOpac
443         TransferWhenCancelAllWaitingHolds
444         AllowAllMessageDeletion
445         AllowOfflineCirculation
446         PatronAutoComplete
447         CircAutoPrintQuickSlip
448         DisplayClearScreenButton
449         FilterBeforeOverdueReport
450         FineNotifyAtCheckin
451         itemBarcodeFallbackSearch
452         itemBarcodeInputFilter
453         previousIssuesDefaultSortOrder
454         RecordLocalUseOnReturn
455         soundon
456         SpecifyDueDate
457         todaysIssuesDefaultSortOrder
458         UpdateTotalIssuesOnCirc
459         UseTablesortForCirc
460         WaitingNotifyAtCheckin
461         AllowSelfCheckReturns
462         AutoSelfCheckAllowed
463         FRBRizeEditions
464         OPACFRBRizeEditions
465         AmazonCoverImages
466         OPACAmazonCoverImages
467         Babeltheque
468         BakerTaylorEnabled
469         GoogleJackets
470         HTML5MediaEnabled
471         IDreamBooksReadometer
472         IDreamBooksResults
473         IDreamBooksReviews
474         LibraryThingForLibrariesEnabled
475         LocalCoverImages
476         OPACLocalCoverImages
477         NovelistSelectEnabled
478         OpenLibraryCovers
479         OpenLibrarySearch
480         UseKohaPlugins
481         SyndeticsEnabled
482         TagsEnabled
483         CalendarFirstDayOfWeek
484         opaclanguagesdisplay
485         AuthoritiesLog
486         BorrowersLog
487         CataloguingLog
488         FinesLog
489         IssueLog
490         LetterLog
491         ReturnLog
492         SubscriptionLog
493         BiblioDefaultView
494         COinSinOPACResults
495         DisplayOPACiconsXSLT
496         hidelostitems
497         HighlightOwnItemsOnOPAC
498         OpacAddMastheadLibraryPulldown
499         OPACDisplay856uAsImage
500         OpacHighlightedWords
501         OpacKohaUrl
502         OpacMaintenance
503         OpacPublic
504         OpacSeparateHoldings
505         OPACShowCheckoutName
506         OpacShowFiltersPulldownMobile
507         OPACShowHoldQueueDetails
508         OpacShowRecentComments
509         OPACShowUnusedAuthorities
510         OpacStarRatings
511         opacthemes
512         OPACURLOpenInNewWindow
513         OpacAuthorities
514         opacbookbag
515         OpacBrowser
516         OpacBrowseResults
517         OpacCloud
518         OPACFinesTab
519         OpacHoldNotes
520         OpacItemLocation
521         OpacPasswordChange
522         OPACPatronDetails
523         OPACpatronimages
524         OPACPopupAuthorsSearch
525         OpacTopissue
526         opacuserlogin
527         QuoteOfTheDay
528         RequestOnOpac
529         reviewson
530         ShowReviewer
531         ShowReviewerPhoto
532         SocialNetworks
533         suggestion
534         AllowPurchaseSuggestionBranchChoice
535         OpacAllowPublicListCreation
536         OpacAllowSharingPrivateLists
537         OpacRenewalAllowed
538         OpacRenewalBranch
539         OPACViewOthersSuggestions
540         SearchMyLibraryFirst
541         singleBranchMode
542         AnonSuggestions
543         EnableOpacSearchHistory
544         OPACPrivacy
545         opacreadinghistory
546         TrackClicks
547         PatronSelfRegistration
548         OPACShelfBrowser
549         AutoEmailOpacUser
550         AutoEmailPrimaryAddress
551         autoMemberNum
552         BorrowerRenewalPeriodBase
553         EnableBorrowerFiles
554         EnhancedMessagingPreferences
555         ExtendedPatronAttributes
556         intranetreadinghistory
557         patronimages
558         TalkingTechItivaPhoneNotification
559         uppercasesurnames
560         IncludeSeeFromInSearches
561         OpacGroupResults
562         QueryAutoTruncate
563         QueryFuzzy
564         QueryStemming
565         QueryWeightFields
566         TraceCompleteSubfields
567         TraceSubjectSubdivisions
568         UseICU
569         UseQueryParser
570         defaultSortField
571         displayFacetCount
572         OPACdefaultSortField
573         OPACItemsResultsDisplay
574         expandedSearchOption
575         IntranetNumbersPreferPhrase
576         OPACNumbersPreferPhrase
577         opacSerialDefaultTab
578         RenewSerialAddsSuggestion
579         RoutingListAddReserves
580         RoutingSerials
581         SubscriptionHistory
582         Display856uAsImage
583         DisplayIconsXSLT
584         template
585         yuipath
586         HidePatronName
587         intranetbookbag
588         StaffDetailItemSelection
589         viewISBD
590         viewLabeledMARC
591         viewMARC
592         ILS-DI
593         OAI-PMH
594         version
595         AudioAlerts
596         /
597       ) {
598         t::lib::Mocks::mock_preference( $_, $set_value );
599     }
600 }
601
602 #Test if all systempreferences are at $value_to_test
603 sub verif_systempreferences_values {
604     my ( $report, $value_to_test ) = @_;
605
606     my @missings;
607     foreach my $key ( keys %{$report->{systempreferences}} ) {
608         if ( $report->{systempreferences}->{$key} ne $value_to_test ) {
609             warn $key;
610             push @missings, $key;
611         }
612     }
613     unless ( @missings ) {
614         ok(1, 'All prefs are present');
615     } else {
616         ok(0, 'Some prefs are missing: ' . Dumper(\@missings));
617     }
618 }
619
620 $schema->storage->txn_rollback;