File Coverage

lib/HTTP/Monkeywrench.pm
Criterion Covered Total %
statement 141 174 81.0
branch 37 68 54.4
condition 6 28 21.4
subroutine 16 17 94.1
pod 5 8 62.5
total 205 295 69.4


line stmt bran cond sub pod time code
1             # HTTP::Monkeywrench.pm
2             # ---------------------------------------------------------
3             # $Revision: 1.13 $
4             # $Date: 2000/09/12 00:14:54 $
5             # ---------------------------------------------------------
6              
7             =head1 NAME
8              
9             HTTP::Monkeywrench - Web testing application
10              
11             =head1 SYNOPSIS
12              
13             use HTTP::Monkeywrench;
14             $session = [
15             {
16             name =E 'URL Name',
17             url =E 'http://url',
18             }
19             ];
20             HTTP::Monkeywrench-Etest($session);
21              
22             =head1 REQUIRES
23              
24             CGI
25             Net::SMTP
26             HTTP::Cookies
27             LWP::UserAgent
28             Time::HiRes
29             Data::Dumper
30              
31             =cut
32              
33             =head1 EXPORTS
34              
35             None
36              
37             =head1 DESCRIPTION
38              
39             HTTP::Monkeywrench is a test-harness application to test the integrity
40             of a user's path through a web site.
41              
42             To run HTTP::Monkeywrench-Etest(), first set up a Perl script that contains
43             sessions (described below), settings if desired (also described below),
44             and a call to HTTP::Monkeywrench-Etest(), passing it the settings hashref first,
45             followed by the desired session hashrefs you want to test.
46             HTTP::Monkeywrench-Etest($settings, $session1,... $sessionN)
47              
48             HTTP::Monkeywrench can also be used in an object-oriented fashion -- simply
49             take the result of HTTP::Monkeywrench-Enew (optionally passing the settings
50             hashref) and call the test() method against it as above (optionally omitting
51             the settings hashref.)
52              
53             Each session arrayref contains one or more hashrefs, called clicks,
54             which contain descriptive elements of a specific web page to be tested.
55             The elements are described below under SESSION.
56              
57             =head1 SESSION
58              
59             =over 4
60              
61             =item C<$session1> (ARRAYREF of HASHREFS)
62              
63             A session is an arrayref of complex hashrefs that can be sent to the
64             Ctest> application to perform tests on a website
65             as a virtual user.
66              
67             The following keys can be in each 'Click' hashref.
68             Fields with a "*" are required:
69              
70             =back
71              
72             =over 8
73              
74             =item name (SCALAR)
75              
76             A name to visually reference that 'click' in the reports
77              
78             =item *url (SCALAR)
79              
80             The url for Monkeywrench to test for that click.
81              
82             =item params (HASHREF)
83              
84             The params to send to dynamic pages and cgi's.
85             Params should be set up as such: { username => 'joe', password => 'blow' }
86              
87             =item method (SCALAR)
88              
89             'method' should be either 'POST' or 'GET'. If method is left
90             blank, method will default to 'GET'.
91              
92             =item auth (ARRAYREF)
93              
94             'auth' is the username and password if the site your are testing
95             is password protected. 'auth' params must be passed to each
96             element of a session that is accessing the same site.
97             Example: ['username','password']
98              
99             =item success_res (ARRAYREF)
100              
101             An arrayref of items for Monkeywrench to test for their existence.
102             Each element of the array can either be a text string or a regexp object.
103             If a string from success_res is not found in the page, Monkeywrench
104             will report an error.
105             EXAMPLE: ['string',qr/regexp_object/,'etc']
106              
107             =item error_res (ARRAYREF)
108              
109             The same as success_res, except that an error will only be reported
110             if strings in error_res ARE found on the page being tested.
111              
112             =item cookies (ARRAYREF of ARRAYREFS)
113              
114             A preset cookie can be sent to a page. In order to send a cookie to
115             a page the following elements should be included as an arrayref:
116              
117             [$version, $key, $val, $path, $domain, $port, $path_spec, $secure, $maxage, $discard, \%rest]
118              
119             An example cookie would look like:
120              
121             [['0', 'sessnum', 'expires&2592000&type&consumer', '/','cookiemonster.org', '8014', '', '', '2000-09-11 16:15:15Z', '']],
122              
123             =item acceptcookie (BIT)
124              
125             A numeric flag set to 1 or 0 to tell Monkeywrench if it should accept and
126             save a cookie passed from a server.
127             Default is 0, cookies will not be accepted.
128              
129             =item sendcookie (BIT)
130              
131             A numeric flag set to 1 or 0 to tell Monkeywrench to send a saved or
132             pre made cookie back to the server. Default is 0, cookies will not be sent.
133              
134             =item showhtml (BIT)
135              
136             A numeric flag set to 1 or 0 to have the source html of a page displayed
137             within the report. When set to 1 the reports can get messy if the page
138             is heavy on html.
139              
140             =back
141              
142             =head1 SETTINGS HASH
143              
144             =over 4
145              
146             =item $settings (HASHREF)
147              
148             The settings hash is optional as are each of the elements of $settings.
149             Elements that are not declared or set are defaulted to 0 (off).
150              
151             =back
152              
153             =over 8
154              
155             =item match_detail (BIT)
156              
157             A numeric flag set to 1 or 0. If set to 1 Match detail shows all of
158             the reports of success_res and error_res no matter if they pass or fail.
159              
160             =item show_cookies (BIT)
161              
162             A numeric flag set to 1 or 0. If set to 1 show_cookies will show all
163             the cookies in the report, either passed from the session or sent
164             from the server.
165              
166             =item smtp_server (SCALAR)
167              
168             The SMTP server to be used by Net::SMTP. Only required if user wants
169             output of Monkeywrench to be sent to an email address.
170              
171             =item send_mail (ARRAYREF)
172              
173             The send_mail arrayref is also only required if user plans on sending
174             output to one or more email addresses.
175              
176             =item send_if_err (BIT)
177              
178             The send_if_err bit is a flag that should be set to either 1 or 0 and
179             is only used if the user wants the Monkeywrench output sent via email.
180             If set to 1 the output will only be sent to the email address(es) in
181             the event of a failure in the success or error checking or any result
182             code other than 200.
183              
184             =item print_results (BIT)
185              
186             If set to 1, the results will be printed to the screen. If set to 0 nothing
187             will be printed to the screen. The default setting is 1.
188              
189              
190             =back
191              
192             =cut
193              
194             =head1 METHODS
195              
196             =over 4
197              
198             =cut
199              
200             package HTTP::Monkeywrench;
201              
202 1     1   2262 use strict;
  1         1  
  1         35  
203 1     1   4 use vars qw($totaltime $totalerrs @sessiontime $debug $default_settings $content);
  1         2  
  1         129  
204              
205 1     1   2230 use CGI;
  1         19076  
  1         7  
206 1     1   1227 use Net::SMTP;
  1         49523  
  1         49  
207 1     1   917 use HTTP::Cookies;
  1         16038  
  1         32  
208 1     1   3656 use LWP::UserAgent;
  1         49853  
  1         35  
209             #use LWP::Debug qw(+); # spits out a lot of helpful LWP debugging
210              
211 1     1   16343 use Time::HiRes qw(gettimeofday tv_interval);
  1         3029  
  1         7  
212 1     1   1463 use Data::Dumper; # also used for debugging purposes
  1         8715  
  1         131  
213              
214             BEGIN {
215 1     1   2 $HTTP::Monkeywrench::REVISION = (qw$Revision: 1.13 $)[-1];
216 1         2 $HTTP::Monkeywrench::VERSION = '1.0';
217              
218 1         2 $CGI::NO_DEBUG = 1; # again, debugging
219 1         2 $debug = undef; # set to 1 if you want to see debugging output
220 1         2233 $default_settings = {
221             match_detail => 1,
222             show_cookies => 1,
223             smtp_server => undef,
224             send_mail => undef,
225             send_if_err => 0,
226             print_results => 1
227             };
228             }
229              
230             $totalerrs = 0; # initialize the total errors string
231              
232             =item C ( [ \%settings ] )
233              
234             Returns a new Monkeywrench object. Optionally takes a settings hash.
235              
236             =cut
237             sub new {
238 1     1 1 3 my $proto = shift;
239 1   33     8 my $class = ref($proto) || $proto;
240 1         4 my $self = bless({}, $class);
241 1         7 $self->settings(shift);
242 1         13 $self->{'ua'} = new LWP::UserAgent;
243 1         3895 $self->{'ua'}->agent('Monkeywrench/'.$HTTP::Monkeywrench::VERSION . $self->{'ua'}->agent);
244 1         117 $self->{'cookie_jar'} = HTTP::Cookies->new;
245 1         35 $self->{'cgi'} = CGI->new('');
246 1         4972 return $self;
247             } # END method new
248              
249             =item C ( $self, [ \%settings ] )
250              
251             Returns settings hash. Passing hashref will change settings in object.
252              
253             =cut
254             sub settings {
255 7     7 1 16 my $self = shift;
256 7 50       26 return undef unless (ref $self);
257 7 100       23 if (my $settings = shift) {
258 1 50       4 warn "SETTINGS ==> " . Dumper($settings) if ($debug);
259 1 50       13 unless (ref($settings) eq 'HASH') {
260 0         0 carp('Settings must be called with hashref...\n');
261 0         0 return undef;
262             }
263              
264 6 100       28 $self->{'settings'} = {
265 1         6 map { $_ => defined($settings->{$_}) ? $settings->{$_} : $default_settings->{$_} }
266             keys %$default_settings };
267             }
268            
269 7         44 return $self->{'settings'};
270             } # END method settings
271            
272 3     3 0 20 sub ua { $_[0]->{'ua'}; }
273 2     2 0 18 sub cookie_jar { $_[0]->{'cookie_jar'}; }
274 1     1 0 6 sub cgi { $_[0]->{'cgi'}; }
275              
276             =item C ( [ \%settings ], \@session [ , \@session, ... ] )
277              
278             Usable as both a static method and object method.
279             Runs a series Monkeywrench tests on a web server using the parameters set forth in the
280             sessions you pass.
281              
282             =cut
283             sub test {
284 1     1 1 106 my $self = shift;
285 1 50       6 unless (ref($self)) {
286 1 50       11 $self = $self->new(ref($_[0]) eq 'HASH' ? shift : ());
287             }
288             #my $settings = $self->settings((ref($_[0]) eq 'HASH') && shift);
289 1         5 my @sessions = @_;
290 1         5 my $q = $self->cgi();
291 1         3 my $sessnum = 0;
292 1         3 my $return = {};
293 1         1 my $res;
294              
295 1         22 $content .= sprintf("============================== Monkeywrench %.2f ==============================\n",($HTTP::Monkeywrench::REVISION));
296              
297 1         3 foreach my $session (@sessions) {
298 2         9 $content .= "Session $sessnum\n";
299 2         4 my $clicknum = 0;
300 2         5 foreach (@$session) {
301 3         45 my $click = { %$_ }; # Make a copy so we don't stomp on the original
302 3         10 $click->{'params'} = join('&', map{ $q->escape($_) . '=' . $q->escape($click->{'params'}{$_} ) } keys %{$click->{'params'}} );
  2         40  
  3         14  
303 3 100       50 $click->{'method'} = $click->{'method'} ? $click->{'method'} : 'GET';
304 3   50     22 $click->{'showhtml'} = $click->{'showhtml'} || 0;
305 3         6 push(@{ $click->{'urls'} },$click->{'url'});
  3         13  
306            
307 3 50       13 if ($click->{'cookies'}) {
308 3         24 foreach my $cookie (@{ $click->{'cookies'} }) {
  3         10  
309 1         5 $self->cookie_jar->set_cookie(@$cookie);
310             }
311             }
312              
313 3         41 my $t1 = [ gettimeofday ];
314 3         12 $res = $self->get_response($click);
315 3         106 $return->{ session }[ $sessnum ][ $clicknum ]{ res } = $res->code;
316 3         76 my $t2 = [ gettimeofday ];
317            
318 3         23 $content .= " Summary for: " . $click->{'name'} . "\n";
319 3         6 my $r = 1;
320 3         7 foreach my $url (@{ $click->{'urls'} }) {
  3         14  
321 3 50       58 $content .= scalar (($r==1) ? ' URL: ' : ' Redirect: ') . "$url\n";
322 3         12 $r++;
323             }
324 3 50 66     25 if (($click->{'sendcookie'}) && ($self->settings->{'show_cookies'})) {
325 0         0 my $cookie_to_print = $self->cookie_jar->as_string;
326 0         0 $~ = "COOKIES";
327 0         0 write;
328             format COOKIES =
329             Cookie:
330             ~~ ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
331             $cookie_to_print
332             .
333             }
334 3         7 my $failed = 0;
335 3         5 my $success = 0;
336              
337 3         14 $content .= ' Code: ' . $res->code . ' ' . $res->message . "\n";
338 3 100 66     73 if ($res->is_redirect || $res->is_success) {
339 1 50       27 $content .= " Match Res:\n" if ($click->{'success_res'});
340 1         4 foreach my $sr (@{ $click->{'success_res'} }) {
  1         4  
341 1         2 my $result;
342 1 50       7 if ($res->content =~ $sr) {
343 1 50       262 $result = "PASS" if ($self->settings->{'match_detail'});
344             } else {
345 0         0 $result = "FAIL";
346 0         0 $failed++;
347 0         0 $totalerrs++;
348             }
349 1         33 pipe (RFH,WFH);
350             format WFH =
351             ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @>>>>>>>>>>
352             $sr, $result
353             .
354 1 50       12 write WFH if ($result);
355 1         22 close WFH;
356 1         7 local $/ = undef;
357 1         29 $content .= ;
358             }
359            
360 1 50       8 $content .= " Match Error:\n" if ($click->{'error_res'});
361 1         3 foreach my $er (@{ $click->{'error_res'} }) {
  1         6  
362 2         4 my $result;
363 2 50       14 if ($res->content =~ $er) {
364 0         0 $result = "FAIL";
365 0         0 $failed++;
366 0         0 $totalerrs++;
367             } else {
368 2 50       1741 $result = "PASS" if ($self->settings->{'match_detail'});
369             }
370 2         99 pipe (ERR_RFH,ERR_WFH);
371             format ERR_WFH =
372             ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @>>>>>>>>>>
373             $er, $result
374             .
375 2 50       20 write ERR_WFH if ($result);
376 2         31 close ERR_WFH;
377 2         8 local $/ = undef;
378 2         32 $content .= ;
379             }
380            
381             } else {
382 2         44 $content .= " *** Request Failed ***\n"; #. $res->error_as_HTML;
383             }
384              
385 3         22 $return->{ session }[ $sessnum ][ $clicknum ]{ clicktime } = my $clicktime = tv_interval($t1,$t2);
386 3         51 $totaltime += $clicktime;
387 3         13 $return->{ session }[ $sessnum ][ $clicknum ]{ sessiontime } = $sessiontime[$sessnum] += $clicktime;
388 3         48 $content .= "\n Pageclick: $clicktime second\n"; # must kill clicktime nazis
389 3         8 $content .= "-------------------------------------------------------------------------------\n";
390 3         25 $clicknum++;
391             }
392            
393 2         109 $content .= " Session $sessnum: $sessiontime[$sessnum]\n";
394 2         6 $content .= "===============================================================================\n";
395 2         8 $sessnum++;
396             }
397            
398 1         4 $content .= "Total Errors: $totalerrs\n";
399 1         5 $content .= " Total Test: $totaltime seconds\n\n";
400 1         4 $return->{ totaltime } = $totaltime;
401            
402 1 50       7 if ($self->settings->{'send_mail'}) {
403 0 0 0     0 if (($self->settings->{'send_if_err'} == 0) ||
      0        
      0        
404             (($self->settings->{'send_if_err'} == 1) && ((($res->code != 200) || ($totalerrs > 0))))) {
405 0 0       0 $self->send_monkeymail($content,$self->settings->{'smtp_server'},$self->settings->{'send_mail'})
406             || warn "Unable to send monkeymail";
407             }
408             }
409 1 50       4 print $content if $self->settings->{'print_results'};
410 1         79 return $return;
411             } # end method test
412              
413              
414             =item C ($click)
415              
416             get_response is a recursive method that loops through all
417             possible redirects until a final response is returned,
418             which is then returned to the caller.
419              
420             =cut
421             sub get_response {
422 3     3 1 7 my $self = shift;
423 3         5 my $click = shift;
424 3 50       14 my $method = ($click->{'REDIRECT'} ? 'GET' : $click->{'method'});
425 3 50       43 my $req = HTTP::Request->new($method => $click->{'urls'}->[-1] . (($method eq 'GET') ? '?'.$click->{'params'} : ''));
426            
427 3 50       11860 $req->authorization_basic($click->{'auth'}->[0], $click->{'auth'}->[1]) if ($click->{'auth'});
428 3 50       2157 $req->content($click->{'params'}) unless ($click->{'REDIRECT'});
429 3 100       69 $self->cookie_jar->add_cookie_header($req) if ($click->{'sendcookie'});
430            
431 3 50       479 $content .= "\$req ==> " . Dumper($req) if ($debug);
432              
433 3         14 my $res = $self->ua->request($req);
434            
435 3 50       2568537 $content .= "\$res ==> " . Dumper($res) if ($debug);
436 3 50       18 $content .= "RESPONSE ==> " . $res->content . "\n" if ($click->{'showhtml'});
437              
438 3 50       14 $self->cookie_jar->extract_cookies($res) if ($click->{'acceptcookie'});
439              
440 3 50       16 if ($res->is_redirect) {
441 0         0 $click->{'REDIRECT'} = 1;
442 0         0 push(@{ $click->{'urls'} },$res->header('Location'));
  0         0  
443 0         0 return $self->get_response($click)
444             } else {
445 3         46 return $res;
446             }
447             } # end method get_response
448              
449              
450             =item C ( $content, \$smtp_server \@address )
451              
452             send_monkeymail is called if the config script has an
453             email address and depending on how send_if_err is setup.
454             $content is the output of the session(s) called by the
455             config script and the \@address arrayref contains the
456             address(es) that the output will be sent to. $smtp_server
457             is the smtp server for Net::SMTP to connect to and is also
458             required in order for send_monkeymail to be called.
459              
460             =cut
461             sub send_monkeymail {
462 0     0 1   my $self = shift;
463 0   0       my $content = shift || 'ERROR: NO OUTPUT';
464 0   0       my $smtp_server = shift || return undef;
465 0   0       my $address = shift || return undef;
466            
467 0   0       my $smtp = Net::SMTP->new($smtp_server) || return undef;
468 0           $smtp->mail($ENV{'USER'});
469 0           $smtp->to(@{$address});
  0            
470              
471 0           $smtp->data();
472 0           $smtp->datasend("From: " . $ENV{'USER'} . "\n");
473 0           $smtp->datasend("To: @{$address}\n");
  0            
474 0           $smtp->datasend("Subject: Monkeywrench Output\n");
475 0           $smtp->datasend( "\n" . $content );
476 0           $smtp->dataend();
477            
478 0           $smtp->quit;
479             }
480              
481              
482             1;
483              
484             __END__