Translation for 3.20.11
[koha.git] / t / lib / TestBuilder.pm
blob5d3157a356de506f7d3c9cb8c490bbb9090bf73f
1 package t::lib::TestBuilder;
3 use Modern::Perl;
4 use Koha::Database;
5 use String::Random;
8 my $gen_type = {
9 tinyint => \&_gen_int,
10 smallint => \&_gen_int,
11 mediumint => \&_gen_int,
12 integer => \&_gen_int,
13 bigint => \&_gen_int,
15 float => \&_gen_real,
16 decimal => \&_gen_real,
17 double_precision => \&_gen_real,
19 timestamp => \&_gen_date,
20 datetime => \&_gen_date,
21 date => \&_gen_date,
23 char => \&_gen_text,
24 varchar => \&_gen_text,
25 tinytext => \&_gen_text,
26 text => \&_gen_text,
27 mediumtext => \&_gen_text,
28 longtext => \&_gen_text,
30 set => \&_gen_set_enum,
31 enum => \&_gen_set_enum,
33 tinyblob => \&_gen_blob,
34 mediumblob => \&_gen_blob,
35 blob => \&_gen_blob,
36 longblob => \&_gen_blob,
39 our $default_value = {
40 UserPermission => {
41 borrowernumber => {
42 surname => 'my surname',
43 address => 'my adress',
44 city => 'my city',
45 branchcode => {
46 branchcode => 'cB',
47 branchname => 'my branchname',
49 categorycode => {
50 categorycode => 'cC',
51 hidelostitems => 0,
52 category_type => 'A',
53 default_privacy => 'default',
55 privacy => 1,
57 module_bit => {
58 module_bit => {
59 bit => '10',
61 code => 'my code',
63 code => undef,
66 $default_value->{UserPermission}->{code} = $default_value->{UserPermission}->{module_bit};
69 sub new {
70 my ($class) = @_;
71 my $self = {};
72 bless( $self, $class );
74 $self->schema( Koha::Database->new()->schema );
75 $self->schema->txn_begin();
76 $self->schema->storage->sql_maker->quote_char('`');
77 return $self;
80 sub schema {
81 my ($self, $schema) = @_;
83 if( defined( $schema ) ) {
84 $self->{schema} = $schema;
86 return $self->{schema};
89 sub clear {
90 my ($self, $params) = @_;
91 my $source = $self->schema->resultset( $params->{source} );
92 return $source->delete_all();
95 sub build {
96 my ($self, $params) = @_;
97 my $source = $params->{source} || return;
98 my $value = $params->{value};
99 my $only_fk = $params->{only_fk} || 0;
101 my $col_values = $self->_buildColumnValues({
102 source => $source,
103 value => $value,
106 my $data;
107 my $foreign_keys = $self->_getForeignKeys( { source => $source } );
108 for my $fk ( @$foreign_keys ) {
109 my $fk_value;
110 my $col_name = $fk->{keys}->[0]->{col_name};
111 if( ref( $col_values->{$col_name} ) eq 'HASH' ) {
112 $fk_value = $col_values->{$col_name};
114 elsif( defined( $col_values->{$col_name} ) ) {
115 next;
118 my $fk_row = $self->build({
119 source => $fk->{source},
120 value => $fk_value,
123 my $keys = $fk->{keys};
124 for my $key( @$keys ) {
125 $col_values->{ $key->{col_name} } = $fk_row->{ $key->{col_fk_name} };
126 $data->{ $key->{col_name} } = $fk_row;
130 my $new_row;
131 if( $only_fk ) {
132 $new_row = $col_values;
134 else {
135 $new_row = $self->_storeColumnValues({
136 source => $source,
137 values => $col_values,
140 $new_row->{_fk} = $data if( defined( $data ) );
141 return $new_row;
144 sub _formatSource {
145 my ($params) = @_;
146 my $source = $params->{source};
147 $source =~ s|(\w+)$|$1|;
148 return $source;
151 sub _buildColumnValues {
152 my ($self, $params) = @_;
153 my $source = _formatSource( { source => $params->{source} } );
154 my $original_value = $params->{value};
156 my $col_values;
157 my @columns = $self->schema->source($source)->columns;
158 my %unique_constraints = $self->schema->source($source)->unique_constraints();
160 my $build_value = 1;
161 BUILD_VALUE: while ( $build_value ) {
162 # generate random values for all columns
163 for my $col_name( @columns ) {
164 my $col_value = $self->_buildColumnValue({
165 source => $source,
166 column_name => $col_name,
167 value => $original_value,
169 $col_values->{$col_name} = $col_value if( defined( $col_value ) );
171 $build_value = 0;
173 # If default values are set, maybe the data exist in the DB
174 # But no need to wait for another value
175 # FIXME this can be wrong if a default value is defined for a field
176 # which is not a constraint and that the generated value for the
177 # constraint already exists.
178 last BUILD_VALUE if exists( $default_value->{$source} );
180 # If there is no original value given and unique constraints exist,
181 # check if the generated values do not exist yet.
182 if ( not defined $original_value and scalar keys %unique_constraints > 0 ) {
184 # verify the data would respect each unique constraint
185 CONSTRAINTS: foreach my $constraint (keys %unique_constraints) {
187 my $condition;
188 my $constraint_columns = $unique_constraints{$constraint};
189 # loop through all constraint columns and build the condition
190 foreach my $constraint_column ( @$constraint_columns ) {
191 # build the filter
192 $condition->{ $constraint_column } =
193 $col_values->{ $constraint_column };
196 my $count = $self->schema
197 ->resultset( $source )
198 ->search( $condition )
199 ->count();
200 if ( $count > 0 ) {
201 # no point checking more stuff, exit the loop
202 $build_value = 1;
203 last CONSTRAINTS;
208 return $col_values;
211 # Returns [ {
212 # rel_name => $rel_name,
213 # source => $table_name,
214 # keys => [ {
215 # col_name => $col_name,
216 # col_fk_name => $col_fk_name,
217 # }, ... ]
218 # }, ... ]
219 sub _getForeignKeys {
220 my ($self, $params) = @_;
221 my $source = $self->schema->source( $params->{source} );
223 my @foreign_keys = ();
224 my @relationships = $source->relationships;
225 for my $rel_name( @relationships ) {
226 my $rel_info = $source->relationship_info($rel_name);
227 if( $rel_info->{attrs}->{is_foreign_key_constraint} ) {
228 my $rel = {
229 rel_name => $rel_name,
230 source => $rel_info->{source},
233 my @keys = ();
234 while( my ($col_fk_name, $col_name) = each(%{$rel_info->{cond}}) ) {
235 $col_name =~ s|self.(\w+)|$1|;
236 $col_fk_name =~ s|foreign.(\w+)|$1|;
237 push @keys, {
238 col_name => $col_name,
239 col_fk_name => $col_fk_name,
242 $rel->{keys} = \@keys;
244 push @foreign_keys, $rel;
247 return \@foreign_keys;
250 sub _storeColumnValues {
251 my ($self, $params) = @_;
252 my $source = $params->{source};
253 my $col_values = $params->{values};
255 my $new_row;
256 eval {
257 $new_row = $self->schema->resultset($source)->update_or_create($col_values);
259 die "$source - $@\n" if ($@);
261 eval {
262 $new_row = { $new_row->get_columns };
264 warn "$source - $@\n" if ($@);
265 return $new_row;
268 sub _buildColumnValue {
269 my ($self, $params) = @_;
270 my $source = $params->{source};
271 my $value = $params->{value};
272 my $col_name = $params->{column_name};
273 my $col_info = $self->schema->source($source)->column_info($col_name);
275 my $col_value;
276 if( exists( $value->{$col_name} ) ) {
277 $col_value = $value->{$col_name};
279 elsif( exists $default_value->{$source} and exists $default_value->{$source}->{$col_name} ) {
280 $col_value = $default_value->{$source}->{$col_name};
282 elsif( not $col_info->{default_value} and not $col_info->{is_auto_increment} and not $col_info->{is_foreign_key} ) {
283 eval {
284 my $data_type = $col_info->{data_type};
285 $data_type =~ s| |_|;
286 $col_value = $gen_type->{$data_type}->( $self, { info => $col_info } );
288 die "The type $col_info->{data_type} is not defined\n" if ($@);
290 return $col_value;
294 sub _gen_int {
295 my ($self, $params) = @_;
296 my $data_type = $params->{info}->{data_type};
298 my $max = 1;
299 if( $data_type eq 'tinyint' ) {
300 $max = 127;
302 elsif( $data_type eq 'smallint' ) {
303 $max = 32767;
305 elsif( $data_type eq 'mediumint' ) {
306 $max = 8388607;
308 elsif( $data_type eq 'integer' ) {
309 $max = 2147483647;
311 elsif( $data_type eq 'bigint' ) {
312 $max = 9223372036854775807;
314 return int( rand($max+1) );
317 sub _gen_real {
318 my ($self, $params) = @_;
319 my $max = 10 ** 38;
320 if( defined( $params->{info}->{size} ) ) {
321 $max = 10 ** ($params->{info}->{size}->[0] - $params->{info}->{size}->[1]);
323 return rand($max) + 1;
326 sub _gen_date {
327 my ($self, $params) = @_;
328 return $self->schema->storage->datetime_parser->format_datetime(DateTime->now());
331 sub _gen_text {
332 my ($self, $params) = @_;
333 # From perldoc String::Random
334 # max: specify the maximum number of characters to return for * and other
335 # regular expression patters that don't return a fixed number of characters
336 my $regex = '[A-Za-z][A-Za-z0-9_]*';
337 my $size = $params->{info}{size};
338 if ( defined $size and $size > 1 ) {
339 $size--;
340 } elsif ( defined $size and $size == 1 ) {
341 $regex = '[A-Za-z]';
343 my $random = String::Random->new( max => $size );
344 return $random->randregex($regex);
347 sub _gen_set_enum {
348 my ($self, $params) = @_;
349 return $params->{info}->{extra}->{list}->[0];
352 sub _gen_blob {
353 my ($self, $params) = @_;;
354 return 'b';
358 sub DESTROY {
359 my $self = shift;
360 $self->schema->txn_rollback();
364 =head1 NAME
366 t::lib::TestBuilder.pm - Koha module to simplify the writing of tests
368 =head1 SYNOPSIS
370 use t::lib::TestBuilder;
372 Koha module to insert the foreign keys automatically for the tests
374 =head1 DESCRIPTION
376 This module allows to insert automatically an entry in the database. All the database changes are wrapped in a transaction.
377 The foreign keys are created according to the DBIx::Class schema.
378 The taken values are the values by default if it is possible or randomly generated.
380 =head1 FUNCTIONS
382 =head2 new
384 $builder = t::lib::TestBuilder->new()
386 Constructor - Begins a transaction and returns the object TestBuilder
388 =head2 schema
390 $schema = $builder->schema
392 Getter - Returns the schema of DBIx::Class
394 =head2 clear
396 $builder->clear({ source => $source_name })
398 =over
400 =item C<$source_name> is the name of the source in the DBIx::Class schema (required)
402 =back
404 Clears all the data of this source (database table)
406 =head2 build
408 $builder->build({
409 source => $source_name,
410 value => $value,
411 only_fk => $only_fk,
414 =over
416 =item C<$source_name> is the name of the source in the DBIx::Class schema (required)
418 =item C<$value> is the values for the entry (optional)
420 =item C<$only_fk> is a boolean to indicate if only the foreign keys are created (optional)
422 =back
424 Inserts an entry in the database by instanciating all the foreign keys.
425 The values can be specified, the values which are not given are default values if they exists or generated randomly.
426 Returns the values of the entry as a hashref with an extra key : _fk which contains all the values of the generated foreign keys.
428 =head1 AUTHOR
430 Yohann Dufour <yohann.dufour@biblibre.com>
432 =head1 COPYRIGHT
434 Copyright 2014 - Biblibre SARL
436 =head1 LICENSE
438 This file is part of Koha.
440 Koha is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by
441 the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
443 Koha is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
445 You should have received a copy of the GNU General Public License along with Koha; if not, see <http://www.gnu.org/licenses>.
447 =cut