Bug 19977: Open only .pref files in Local Use tab (sysprefs)
[koha.git] / t / db_dependent / TestBuilder.t
blob5ee0a6a40b428f1f39f69f025b13a7f4a8948e1d
1 #!/usr/bin/perl
3 # This file is part of Koha.
5 # Copyright 2014 - Biblibre SARL
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>.
20 use Modern::Perl;
22 use Test::More tests => 13;
23 use Test::Warn;
24 use File::Basename qw(dirname);
26 use Koha::Database;
27 use Koha::Patrons;
29 BEGIN {
30 use_ok('t::lib::TestBuilder');
33 our $schema = Koha::Database->new->schema;
34 $schema->storage->txn_begin;
35 our $builder;
38 subtest 'Start with some trivial tests' => sub {
39 plan tests => 7;
41 $builder = t::lib::TestBuilder->new;
42 isnt( $builder, undef, 'We got a builder' );
44 my $data;
45 warning_like { $data = $builder->build; } qr/.+/, 'Catch a warning';
46 is( $data, undef, 'build without arguments returns undef' );
47 is( ref( $builder->schema ), 'Koha::Schema', 'check schema' );
48 is( ref( $builder->can('delete') ), 'CODE', 'found delete method' );
50 # invalid argument
51 warning_like { $builder->build({
52 source => 'Borrower',
53 value => { surname => { invalid_hash => 1 } },
54 }) } qr/^Hash not allowed for surname/,
55 'Build should not accept a hash for this column';
57 # return undef if a record exists
58 my $branchcode = $builder->build({ source => 'Branch' })->{branchcode};
59 my $param = { source => 'Branch', value => { branchcode => $branchcode } };
60 warning_like { $builder->build( $param ) }
61 qr/Violation of unique constraint/,
62 'Catch warn on adding existing record';
66 subtest 'Build all sources' => sub {
67 plan tests => 1;
69 my @sources = $builder->schema->sources;
70 my @source_in_failure;
71 for my $source ( @sources ) {
72 my $res;
73 # Skip the source if it is a view
74 next if $schema->source($source)->isa('DBIx::Class::ResultSource::View');
75 eval { $res = $builder->build( { source => $source } ); };
76 push @source_in_failure, $source if $@ || !defined( $res );
78 is( @source_in_failure, 0,
79 'TestBuilder should be able to create an object for every source' );
80 if ( @source_in_failure ) {
81 diag( "The following sources have not been generated correctly: " .
82 join ', ', @source_in_failure );
87 subtest 'Test length of some generated fields' => sub {
88 plan tests => 3;
90 # Test the length of a returned character field
91 my $bookseller = $builder->build({ source => 'Aqbookseller' });
92 my $max = $schema->source('Aqbookseller')->column_info('phone')->{size};
93 is( length( $bookseller->{phone} ) > 0, 1,
94 'The length for a generated string (phone) should not be zero' );
95 is( length( $bookseller->{phone} ) <= $max, 1,
96 'Check maximum length for a generated string (phone)' );
98 my $item = $builder->build({ source => 'Item' });
99 is( $item->{replacementprice}, sprintf("%.2f", $item->{replacementprice}), "The number of decimals for floats should not be more than 2" );
103 subtest 'Test FKs in overduerules_transport_type' => sub {
104 plan tests => 5;
106 my $my_overduerules_transport_type = {
107 message_transport_type => {
108 message_transport_type => 'my msg_t_t',
110 overduerules_id => {
111 branchcode => 'codeB',
112 categorycode => 'codeC',
116 my $overduerules_transport_type = $builder->build({
117 source => 'OverduerulesTransportType',
118 value => $my_overduerules_transport_type,
121 $overduerules_transport_type->{message_transport_type},
122 $my_overduerules_transport_type->{message_transport_type}->{message_transport_type},
123 'build stores the message_transport_type correctly'
126 $schema->resultset('Overduerule')->find( $overduerules_transport_type->{overduerules_id} )->branchcode,
127 $my_overduerules_transport_type->{overduerules_id}->{branchcode},
128 'build stores the branchcode correctly'
131 $schema->resultset('Overduerule')->find( $overduerules_transport_type->{overduerules_id} )->categorycode,
132 $my_overduerules_transport_type->{overduerules_id}->{categorycode},
133 'build stores the categorycode correctly'
136 $schema->resultset('MessageTransportType')->find( $overduerules_transport_type->{message_transport_type} )->message_transport_type,
137 $overduerules_transport_type->{message_transport_type},
138 'build stores the foreign key message_transport_type correctly'
140 isnt(
141 $schema->resultset('Overduerule')->find( $my_overduerules_transport_type->{overduerules_id} )->letter2,
142 undef,
143 'build generates values if they are not given'
148 subtest 'Tests with composite FK in userpermission' => sub {
149 plan tests => 9;
151 my $my_user_permission = default_userpermission();
152 my $user_permission = $builder->build({
153 source => 'UserPermission',
154 value => $my_user_permission,
157 # Checks on top level of userpermission
158 isnt(
159 $user_permission->{borrowernumber},
160 undef,
161 'build generates a borrowernumber correctly'
164 $user_permission->{code},
165 $my_user_permission->{code}->{code},
166 'build stores code correctly'
169 # Checks one level deeper userpermission -> borrower
170 my $patron = $schema->resultset('Borrower')->find({ borrowernumber => $user_permission->{borrowernumber} });
172 $patron->surname,
173 $my_user_permission->{borrowernumber}->{surname},
174 'build stores surname correctly'
176 isnt(
177 $patron->cardnumber,
178 undef,
179 'build generated cardnumber'
182 # Checks two levels deeper userpermission -> borrower -> branch
183 my $branch = $schema->resultset('Branch')->find({ branchcode => $patron->branchcode->branchcode });
185 $branch->branchname,
186 $my_user_permission->{borrowernumber}->{branchcode}->{branchname},
187 'build stores branchname correctly'
189 isnt(
190 $branch->branchaddress1,
191 undef,
192 'build generated branch address'
195 # Checks with composite FK: userpermission -> permission
196 my $perm = $schema->resultset('Permission')->find({ module_bit => $user_permission->{module_bit}, code => $my_user_permission->{code}->{code} });
197 isnt( $perm, undef, 'build generated record for composite FK' );
199 $perm->code,
200 $my_user_permission->{code}->{code},
201 'build stored code correctly'
204 $perm->description,
205 $my_user_permission->{code}->{description},
206 'build stored description correctly'
210 sub default_userpermission {
211 return {
212 borrowernumber => {
213 surname => 'my surname',
214 address => 'my adress',
215 city => 'my city',
216 branchcode => {
217 branchname => 'my branchname',
219 categorycode => {
220 hidelostitems => 0,
221 category_type => 'A',
222 default_privacy => 'default',
224 privacy => 1,
226 module_bit => {
227 flag => 'my flag',
229 code => {
230 code => 'my code',
231 description => 'my desc',
237 subtest 'Test build with NULL values' => sub {
238 plan tests => 3;
240 # PK should not be null
241 my $params = { source => 'Branch', value => { branchcode => undef }};
242 warning_like { $builder->build( $params ) }
243 qr/Null value for branchcode/,
244 'Catch warn on adding branch with a null branchcode';
245 # Nullable column
246 my $info = $schema->source( 'Item' )->column_info( 'barcode' );
247 $params = { source => 'Item', value => { barcode => undef }};
248 my $item = $builder->build( $params );
249 is( $info->{is_nullable} && $item && !defined( $item->{barcode} ), 1,
250 'Barcode can be NULL' );
251 # Nullable FK
252 $params = { source => 'Reserve', value => { itemnumber => undef }};
253 my $reserve = $builder->build( $params );
254 $info = $schema->source( 'Reserve' )->column_info( 'itemnumber' );
255 is( $reserve && $info->{is_nullable} && $info->{is_foreign_key} &&
256 !defined( $reserve->{itemnumber} ), 1, 'Nullable FK' );
260 subtest 'Tests for delete method' => sub {
261 plan tests => 12;
263 # Test delete with single and multiple records
264 my $basket1 = $builder->build({ source => 'Aqbasket' });
265 my $basket2 = $builder->build({ source => 'Aqbasket' });
266 my $basket3 = $builder->build({ source => 'Aqbasket' });
267 my ( $id1, $id2 ) = ( $basket1->{basketno}, $basket2->{basketno} );
268 $builder->delete({ source => 'Aqbasket', records => $basket1 });
269 isnt( exists $basket1->{basketno}, 1, 'Delete cleared PK hash value' );
271 is( $builder->schema->resultset('Aqbasket')->search({ basketno => $id1 })->count, 0, 'Basket1 is no longer found' );
272 is( $builder->schema->resultset('Aqbasket')->search({ basketno => $id2 })->count, 1, 'Basket2 is still found' );
273 is( $builder->delete({ source => 'Aqbasket', records => [ $basket2, $basket3 ] }), 2, "Returned two delete attempts" );
274 is( $builder->schema->resultset('Aqbasket')->search({ basketno => $id2 })->count, 0, 'Basket2 is no longer found' );
277 # Test delete in table without primary key (..)
278 is( $schema->source('TmpHoldsqueue')->primary_columns, 0,
279 'Table without primary key detected' );
280 my $bibno = 123; # just a number
281 my $cnt1 = $schema->resultset('TmpHoldsqueue')->count;
282 # Insert a new record in TmpHoldsqueue with that biblionumber
283 my $val = { biblionumber => $bibno };
284 my $rec = $builder->build({ source => 'TmpHoldsqueue', value => $val });
285 my $cnt2 = $schema->resultset('TmpHoldsqueue')->count;
286 is( defined($rec) && $cnt2 == $cnt1 + 1 , 1, 'Created a record' );
287 is( $builder->delete({ source => 'TmpHoldsqueue', records => $rec }),
288 undef, 'delete returns undef' );
289 is( $rec->{biblionumber}, $bibno, 'Hash value untouched' );
290 is( $schema->resultset('TmpHoldsqueue')->count, $cnt2,
291 "Method did not delete record in table without PK" );
293 # Test delete with NULL values
294 $val = { branchcode => undef };
295 is( $builder->delete({ source => 'Branch', records => $val }), 0,
296 'delete returns zero for an undef search with one key' );
297 $val = { module_bit => 1, #catalogue
298 code => undef };
299 is( $builder->delete({ source => 'Permission', records => $val }), 0,
300 'delete returns zero for an undef search with a composite PK' );
304 subtest 'Auto-increment values tests' => sub {
305 plan tests => 3;
307 # Pick a table with AI PK
308 my $source = 'Biblio'; # table
309 my $column = 'biblionumber'; # ai column
311 my $col_info = $schema->source( $source )->column_info( $column );
312 is( $col_info->{is_auto_increment}, 1, "biblio.biblionumber is detected as autoincrement");
314 # Create a biblio
315 my $biblio_1 = $builder->build({ source => $source });
316 # Get the AI value
317 my $ai_value = $biblio_1->{ biblionumber };
318 # Create a biblio
319 my $biblio_2 = $builder->build({ source => $source });
320 # Get the next AI value
321 my $next_ai_value = $biblio_2->{ biblionumber };
322 is( $ai_value + 1, $next_ai_value, "AI values are consecutive");
324 # respect autoincr column
325 warning_like { $builder->build({
326 source => $source,
327 value => { biblionumber => 123 },
328 }) } qr/^Value not allowed for auto_incr/,
329 'Build should not overwrite an auto_incr column';
332 subtest 'Date handling' => sub {
333 plan tests => 2;
335 $builder = t::lib::TestBuilder->new;
337 my $patron = $builder->build( { source => 'Borrower' } );
338 is( length( $patron->{updated_on} ), 19, 'A timestamp column value should be YYYY-MM-DD HH:MM:SS' );
339 is( length( $patron->{dateofbirth} ), 10, 'A date column value should be YYYY-MM-DD' );
342 subtest 'Default values' => sub {
343 plan tests => 2;
344 $builder = t::lib::TestBuilder->new;
345 my $item = $builder->build( { source => 'Item' } );
346 is( $item->{more_subfields_xml}, undef, 'This xml field should be undef' );
347 $item = $builder->build( { source => 'Item', value => { more_subfields_xml => 'some xml' } } );
348 is( $item->{more_subfields_xml}, 'some xml', 'Default should not overwrite assigned value' );
351 subtest 'build_object() tests' => sub {
353 plan tests => 6;
355 $builder = t::lib::TestBuilder->new();
357 my $categorycode = $builder->build( { source => 'Category' } )->{categorycode};
358 my $itemtype = $builder->build( { source => 'Itemtype' } )->{itemtype};
360 my $issuing_rule = $builder->build_object(
361 { class => 'Koha::IssuingRules',
362 value => {
363 categorycode => $categorycode,
364 itemtype => $itemtype
369 is( ref($issuing_rule), 'Koha::IssuingRule', 'Type is correct' );
370 is( $issuing_rule->categorycode,
371 $categorycode, 'Category code correctly set' );
372 is( $issuing_rule->itemtype, $itemtype, 'Item type correctly set' );
374 warning_is { $issuing_rule = $builder->build_object( {} ); }
375 { carped => 'Missing class param' },
376 'The class parameter is mandatory, raises a warning if absent';
377 is( $issuing_rule, undef,
378 'If the class parameter is missing, undef is returned' );
380 subtest 'Test all classes' => sub {
381 my $Koha_modules_dir = dirname(__FILE__) . '/../../Koha';
382 my @koha_object_based_modules = `/bin/grep -rl 'sub object_class' $Koha_modules_dir`;
383 my @source_in_failure;
384 for my $module_filepath ( @koha_object_based_modules ) {
385 chomp $module_filepath;
386 next unless $module_filepath =~ m|\.pm$|;
387 my $module = $module_filepath;
388 $module =~ s|^.*/(Koha.*)\.pm$|$1|;
389 $module =~ s|/|::|g;
390 next if $module eq 'Koha::Objects';
391 eval "require $module";;
392 my $object = $builder->build_object( { class => $module } );
393 is( ref($object), $module->object_class, "Testing $module" );
398 subtest '->build parameter' => sub {
399 plan tests => 3;
401 # Test to make sure build() warns user of unknown parameters.
402 warnings_are {
403 $builder->build({
404 source => 'Branch',
405 value => {
406 branchcode => 'BRANCH_1'
409 } [], "No warnings on correct use";
411 warnings_like {
412 $builder->build({
413 source => 'Branch',
414 branchcode => 'BRANCH_2' # This is wrong!
416 } qr/unknown param/i, "Carp unknown parameters";
418 warnings_like {
419 $builder->build({
420 zource => 'Branch', # Intentional spelling error
422 } qr/Source parameter not specified/, "Catch warning on missing source";
425 $schema->storage->txn_rollback;