Bug 35979: (follow-up) Add check in ->enqueue
[koha.git] / t / db_dependent / FrameworkPlugin.t
1 use Modern::Perl;
2
3 use CGI;
4 use File::Temp qw/tempfile/;
5 use Getopt::Long;
6 use Test::MockModule;
7 use Test::More tests => 6;
8
9 use t::lib::Mocks;
10 use t::lib::TestBuilder;
11
12 use C4::Auth qw( checkauth );
13 use C4::Output qw( output_html_with_http_headers );
14 use Koha::Database;
15 use Koha::FrameworkPlugin;
16 use Koha::Util::FrameworkPlugin qw( biblio_008 );
17
18 our @includes;
19 GetOptions( 'include=s{,}' => \@includes ); #not used by default !
20
21 # Because of CGI::Session being buggy (see bug 17427) and a use of get_session somewhere here
22 t::lib::Mocks::mock_preference( 'SessionStorage', 'tmp' );
23
24 my $schema  = Koha::Database->new->schema;
25 $schema->storage->txn_begin;
26 our $dbh = C4::Context->dbh;
27 our $builder = t::lib::TestBuilder->new;
28
29 subtest 'Test01 -- Simple tests for new and name' => sub {
30     plan tests => 7;
31     test01();
32 };
33 subtest 'Test02 -- test build with old styler and marc21_leader' => sub {
34     plan tests => 5;
35     test02();
36 };
37 subtest 'Test03 -- tests with bad plugins' => sub {
38     test03();
39 };
40 subtest 'Test04 -- tests with new style plugin' => sub {
41     plan tests => 5;
42     test04();
43 };
44 subtest 'Test05 -- tests with build and launch for default plugins' => sub {
45     test05( \@includes );
46 };
47
48 subtest 'Test06 -- test biblio_008' => sub {
49     plan tests => 5;
50     t::lib::Mocks::mock_preference('DefaultCountryField008', 'nl' ); # deliberately shorter than 3 pos
51     t::lib::Mocks::mock_preference('DefaultLanguageField008', 'dutch' ); # deliberately too long
52     my $field = biblio_008();
53     is( length($field), 40, 'Check length' );
54     is( substr($field, 15, 3), 'nl ', 'Check country right padded' );
55     is( substr($field, 35, 3), 'dut', 'Check language' );
56     t::lib::Mocks::mock_preference('DefaultCountryField008', '' );
57     $field = biblio_008();
58     is( substr($field, 15, 3), '|||', 'Check country fallback for empty string' );
59     t::lib::Mocks::mock_preference('DefaultCountryField008', undef );
60     $field = biblio_008();
61     is( substr($field, 15, 3), '|||', 'Check country fallback for undefined' );
62 };
63
64 $schema->storage->txn_rollback;
65
66 sub test01 {
67     #empty plugin
68     my $plugin= Koha::FrameworkPlugin->new;
69     is( ref($plugin), 'Koha::FrameworkPlugin', 'Got an object' );
70     isnt( $plugin->errstr, undef, 'We should have an error for missing name');
71     is( $plugin->build, undef, 'Build returns undef');
72
73     #tests for name and path, with/without hashref
74     $plugin= Koha::FrameworkPlugin->new( { name => 'marc21_leader.pl' } );
75     is( $plugin->name, 'marc21_leader.pl', 'Check name without path in hash' );
76     $plugin= Koha::FrameworkPlugin->new( 'marc21_leader.pl' );
77     is( $plugin->name, 'marc21_leader.pl', 'Check name without path' );
78     $plugin= Koha::FrameworkPlugin->new( 'cataloguing/value_builder/marc21_leader.pl' );
79     is( $plugin->name, 'marc21_leader.pl', 'Check name with path' );
80     $plugin= Koha::FrameworkPlugin->new({ path => 'cataloguing/value_builder', name => 'marc21_leader.pl' });
81     is( $plugin->name, 'marc21_leader.pl', 'Check name and path in hash' );
82 }
83
84 sub test02 {
85     # first test an old style item plugin
86     my $old = old01(); # plugin filename
87     my $path;
88     if( $old =~ /^(.*)\/([^\/]+)$/ ) { # extract path
89         $path = $1;
90         $old = $2;
91     }
92     my $plugin= Koha::FrameworkPlugin->new({
93         name => $old, path => $path, item_style => 1,
94     });
95     my $pars= { id => '234567' };
96     is( $plugin->build($pars), 1, 'Build oldstyler successful' );
97     is( length($plugin->javascript)>0 && !$plugin->noclick, 1,
98         'Checked javascript and noclick' );
99
100     # now test marc21_leader
101     $plugin= Koha::FrameworkPlugin->new( { name => 'marc21_leader.pl' } );
102     $pars= { dbh => $dbh, id => '123456' };
103     is( $plugin->build($pars), 1, 'Build marc21_leader successful' );
104     is( $plugin->javascript =~ /<script.*function.*\<\/script\>/s, 1,
105         'Javascript looks ok' );
106     is( $plugin->noclick, '', 'marc21_leader should have a popup');
107 }
108
109 sub test03 {
110     #file not found
111     my $plugin= Koha::FrameworkPlugin->new('file_does_not_exist');
112     $plugin->build;
113     is( $plugin->errstr =~ /not found/i, 1, 'File not found-message');
114
115     #three bad ones: no perl, syntax error, bad return value
116     foreach my $f ( bad01(), bad02(), bad03() ) {
117         next if !$f;
118         $plugin= Koha::FrameworkPlugin->new( $f );
119         $plugin->build({ id => '998877' });
120         is( defined($plugin->errstr), 1,
121             "Saw: ". ( $plugin->errstr//'no error??' ));
122     }
123     done_testing();
124 }
125
126 sub test04 {
127     #two simple new style plugins
128     my $plugin= Koha::FrameworkPlugin->new( good01() );
129     my $pars= { id => 'example_345' };
130     is( $plugin->build($pars), 1, 'Build 1 ok');
131     isnt( $plugin->javascript, '', 'Checked javascript property' );
132
133     $plugin= Koha::FrameworkPlugin->new( ugly01() );
134     $pars= { id => 'example_456' };
135     is( $plugin->build($pars), 1, 'Build 2 ok');
136     is( $plugin->build($pars), 1, 'Second build 2 ok');
137     is( $plugin->launch($pars), 'abc', 'Launcher returned something' );
138         #note: normally you will not call build and launch like that
139 }
140
141 sub test05 {
142     my ( $incl ) = @_;
143     #mock to simulate some authorization and eliminate lots of output
144     my $launched = 0;
145     my $mContext = Test::MockModule->new('C4::Context');
146     my $mAuth = Test::MockModule->new('C4::Auth');
147     my $mOutput = Test::MockModule->new('C4::Output');
148     $mContext->mock( 'userenv', \&mock_userenv );
149     $mAuth->mock( 'checkauth', sub { return ( 1, undef, 1, all_perms() ); } );
150     $mAuth->mock( 'check_cookie_auth', sub { return ('ok') } );
151     $mOutput->mock('output_html_with_http_headers',  sub { ++$launched; } );
152
153     my $cgi=CGI->new;
154     my ( $plugins, $min ) = selected_plugins( $incl );
155
156     # test building them
157     my $objs;
158     foreach my $f ( @$plugins ) {
159         $objs->{$f} = Koha::FrameworkPlugin->new( $f );
160         my $pars= { dbh => $dbh, id => $f };
161         is( $objs->{$f}->build($pars), 1, "Builded ".$objs->{$f}->name );
162     }
163
164     # test launching them (but we cannot verify returned results here)
165     undef $objs;
166     foreach my $f ( @$plugins ) {
167         $objs->{$f} = Koha::FrameworkPlugin->new( $f );
168         my $pars= { dbh => $dbh, id => $f };
169         $objs->{$f}->launch({ cgi => $cgi });
170             # may generate some uninitialized warnings for missing params
171         is( $objs->{$f}->errstr, undef, "Launched ".$objs->{$f}->name );
172     }
173     is( $launched >= $min, 1,
174             "$launched of ". scalar @$plugins.' plugins generated output ');
175     done_testing();
176 }
177
178 sub selected_plugins {
179     my ( $incl ) = @_;
180     #if you use includes, FIRST assure yourself that you do not
181     #include any destructive perl scripts! You know what you are doing..
182
183     my ( @fi, $min);
184     if( $incl && @$incl ) {
185         @fi = @$incl;
186         $min = 0; #not sure how many will output
187     } else { # some default MARC, UNIMARC and item plugins
188         @fi = qw| barcode.pl dateaccessioned.pl marc21_orgcode.pl
189 marc21_field_005.pl marc21_field_006.pl marc21_field_007.pl marc21_field_008.pl
190 marc21_field_008_authorities.pl marc21_leader.pl marc21_leader_authorities.pl
191 unimarc_leader.pl unimarc_field_100.pl unimarc_field_105.pl
192 unimarc_field_106.pl unimarc_field_110.pl unimarc_field_120.pl
193 unimarc_field_130.pl unimarc_field_140.pl unimarc_field_225a.pl
194 unimarc_field_4XX.pl |;
195         $min = 16; # the first four generate no output
196     }
197     @fi = grep
198         { !/ajax|callnumber(-KU)?\.pl|labs_theses/ } # skip these
199         @fi;
200     return ( \@fi, $min);
201 }
202
203 sub mock_userenv {
204     my $branch = $builder->build({ source => 'Branch' });
205     return { branch => $branch->{branchcode}, flags => 1, id => 1 };
206 }
207
208 sub all_perms {
209     my $p = $dbh->selectcol_arrayref("SELECT flag FROM userflags");
210     my $rv= {};
211     foreach my $module ( @$p ) {
212         $rv->{ $module } = 1;
213     }
214     return $rv;
215 }
216
217 sub mytempfile {
218     my ( $fh, $fn ) = tempfile( SUFFIX => '.plugin', UNLINK => 1 );
219     print $fh $_[0]//'';
220     close $fh;
221     return $fn;
222 }
223
224 sub old01 {
225 # simple old style item plugin: note that Focus has two pars
226 # includes a typical empty Clic function and plugin subroutine
227     return mytempfile( <<'HERE'
228 sub plugin_javascript {
229     my ($dbh,$record,$tagslib,$field_number) = @_;
230     my $function_name = $field_number;
231     my $res = "
232 <script type=\"text/javascript\">
233 //<![CDATA[
234 function Focus$function_name(subfield_managed,id) {
235     document.getElementById(id).value='test';
236     return 0;
237 }
238 function Clic$function_name(subfield_managed) {
239 }
240 //]]>
241 </script>
242 ";
243     return ($function_name,$res);
244 }
245 sub plugin {
246     return "";
247 }
248 HERE
249     );
250 }
251
252 sub good01 { #very simple new style plugin, no launcher
253     return mytempfile( <<'HERE'
254 my $builder = sub {
255     my $params = shift;
256     return qq|
257 <script type="text/javascript">
258     function Focus$params->{id}(event) {
259         if( document.getElementById(event.data.id).value == '' ) {
260             document.getElementById(event.data.id).value='EXAMPLE: ';
261         }
262     }
263 </script>|;
264 };
265 return { builder => $builder };
266 HERE
267     );
268 }
269
270 sub bad01 { # this is no plugin
271     return mytempfile( 'Just nonsense' );
272 }
273
274 sub bad02 { # common syntax error: you forgot the semicolon of sub1 declare
275     return mytempfile( <<'HERE'
276 my $sub1= sub {
277     my $params = shift;
278     return qq|<script type="text/javascript">function Change$params->{id}(event) { alert("Changed"); }</script>|;
279 }
280 return { builder => $sub1 };
281 HERE
282     );
283 }
284
285 sub bad03 { # badscript tag should trigger an error
286     return mytempfile( <<'HERE'
287 my $sub1= sub {
288     my $params = shift;
289     return qq|<badscript type="text/javascript">function Click$params->{id} (event) { alert("Hi there"); return false; }</badscript>|;
290 };
291 return { builder => $sub1 };
292 HERE
293     );
294 }
295
296 sub ugly01 { #works, but not very readable..
297     return mytempfile( <<'HERE'
298 return {builder=>sub{return qq|<script type="text/javascript">function Blur$_[0]->{id}(event){alert('Bye');}</script>|;},launcher=>sub{'abc'}};
299 HERE
300     );
301 }