APP-REST-RestTestSuite

 view release on metacpan or  search on metacpan

lib/APP/REST/RestTestSuite.pm  view on Meta::CPAN

package APP::REST::RestTestSuite;

use 5.006;
use strict;
use warnings FATAL => 'all';
use Data::Dumper;
use HTTP::Request;
use Time::HiRes qw( time sleep );
use File::Path;
use Cwd;
use LWP::UserAgent;
use APP::REST::ParallelMyUA;


use constant LOG_FILE     => 'rest_client.log';
use constant ERR_LOG_FILE => 'rest_client_error.log';
use constant LINE         => '=' x 50;

$|                    = 1;    #make the pipe hot
$Data::Dumper::Indent = 1;

=head1 NAME

APP::REST::RestTestSuite - Suite for testing restful web services 

=head1 VERSION

Version 0.03

=cut

our $VERSION = '0.03';

=head1 SYNOPSIS

use APP::REST::RestTestSuite;
my $suite = APP::REST::RestTestSuite->new();

$suite->execute_test_cases( $suite->get_test_cases() );
my ( $cases_in_config, $executed, $skipped, $passed, $failed ) =
  $suite->get_result_summary();

#OR

use APP::REST::RestTestSuite;

# overrides the default config and log file paths
my $suite = APP::REST::RestTestSuite->new(
    REST_CONFIG_FILE => <config file>,
    LOG_FILE_PATH    => <path>,
);

$suite->execute_test_cases( $suite->get_test_cases() );
my ( $cases_in_config, $executed, $skipped, $passed, $failed ) =
  $suite->get_result_summary();

 
=head1 DESCRIPTION

APP::REST::RestTestSuite object is instantiated with the data in config file. 
Default config file format is defined in __DATA__ and that can be overridden
by passing the config file as an argument to the class.
Default LOG file path is the current working directory of the script which 
calls this module

=head1 SUBROUTINES/METHODS

=head2 new

Object Constructor

=cut

sub new {
    my ( $class, %args ) = @_;

lib/APP/REST/RestTestSuite.pm  view on Meta::CPAN

    my ( $config, $total, $total_response_time, $skip, $pass, $fail ) = (0) x 6;
    my ( $uri, $method, $req_content_type, $req_body, $status ) = (undef) x 5;
    my ( $request,  $response ) = (undef) x 2;
    my ( $username, $password ) = (undef) x 2;

    $username = $self->{username};
    $password = $self->{password};

    my $fh = $self->get_log_file_handle();

    if ( $self->{html_log_required}
        && ( $self->{html_log_required} =~ /yes/i ) )
    {
        print $fh
          qq|<HTML> <HEAD> <TITLE>LOG for $self->{endpoint}</TITLE> </HEAD>|
          . qq|<BODY><textarea rows="999999" cols="120" style="border:none;">|;
    }

    print STDERR "\nTest Suite executed on $self->{endpoint}\n";
    print $fh "\nTest Suite executed on $self->{endpoint}\n";

    my @reqs;

    foreach my $count ( sort { $a <=> $b } keys(%test_cases) ) {

        my $tc = $test_cases{$count};

        $config++;
        if ( $tc->{execute} =~ /no/i ) {
            print $fh "\nSkipping Test case $count => $tc->{test_case} \n";
            $skip++;
            next;
        }

        $uri = qq|$self->{rest_uri_base}| . qq|$tc->{uri}|;

        #Support only GET methods at present
        if ( $tc->{request_method} =~ /get/i ) {

            # Create HTTP request pool for later execution by parallel useragent
            $method           = uc( $tc->{request_method} );
            $req_content_type = $tc->{request_content_type};
            $req_body         = $tc->{request_body} || 0;
            $status           = $tc->{response_status};

            my $request = HTTP::Request->new( $method, $uri );
            $request->authorization_basic( $username, $password )
              if ( $username && $password );
            push( @reqs, $request );
        }

        $total++;

    }

    print STDERR "\nRequesting [$total] web services together.\n";
    foreach my $req (@reqs) {

        # register all requests and wait for them to finish
        if ( my $res = $pua->register($req) ) {
            print STDERR $res->error_as_HTML;
        }
    }
    print STDERR "Receiving response from web services. Please wait..!\n";

    # will return once all forked web services are either completed or timeout
    my $entries = $pua->wait();

    print STDERR "\n\n";

    foreach ( keys %$entries ) {
        my $response  = $entries->{$_}->response;
        my $tick      = $entries->{$_}->{tick};
        my $exec_time = ( $tick->{end} - $tick->{start} ) * 1000 ;

        $total_response_time += $exec_time;
        $exec_time =  sprintf( "%.2f", $exec_time );

        print STDERR "\n", $response->request->url,
          "\n !  Response Status [", $response->code,
          "]\tResponse Time [$exec_time ms]";
        $self->_print_logs(
            fh       => $fh,
            uri      => $response->request->url,
            method   => $response->request->method,
            req_body => ''
        );
        $self->_print_logs(
            fh        => $fh,
            res       => $response,
            exec_time => $exec_time,
        );

    }

    #convert milli seconds to seconds for total_exec_time
    $total_response_time = sprintf( "%.2f", $total_response_time / 1000 );
    my $avg_response_time =
      sprintf( "%.2f", ( $total_response_time * 1000 ) / $total );

    print STDERR
      "\n\n\nComplete test case report is in $self->{file}->{log_file}";

    print STDERR
"\n\nResponse time of $total web service calls => [$total_response_time seconds]\n";
    print STDERR
"Average response time of a web service => [$avg_response_time milli seconds]\n\n";

    print $fh
"\n\nResponse time of $total web service calls => [$total_response_time seconds]\n";
    print $fh
"Average response time of a web service => [$avg_response_time milli seconds]\n\n";

    if ( $self->{html_log_required}
        && ( $self->{html_log_required} =~ /yes/i ) )
    {
        print $fh qq|</textarea></BODY></HTML>|;
    }

    $self->{test_result_log} = {
        test_cases_in_config  => $config,

lib/APP/REST/RestTestSuite.pm  view on Meta::CPAN


}

sub _init_config_files {
    my ( $self, %args ) = @_;

    $self->{file}->{config_file} = $args{REST_CONFIG_FILE};
}

sub _init_sample_config_file {
    my ( $self, %args ) = @_;

    my $separator;
    if ( $^O =~ /Win/ ) {
        $separator = '\\';
    } else {
        $separator = '/';
    }

    my $scfg = getcwd() || $ENV{PWD};
    my $scfg_file = $scfg . $separator . 'rest-project-xxxx.txt';

    $self->{file}->{sample_config_file} = $scfg_file;

}

sub _init_log_files {
    my ( $self, %args ) = @_;

    my $separator;
    if ( $^O =~ /Win/ ) {
        $separator = '\\';
    } else {
        $separator = '/';
    }

    my $log_path = $args{LOG_FILE_PATH} || getcwd() || $ENV{PWD};
    my $log_dir = $log_path . $separator . 'LOG';

    eval { mkpath( $log_dir, 0, 0755 ) unless ( -d $log_dir ); };

    if ($@) {
        my $err = $@;
        $err =~ s/line\s+\d+//g;
        die qq|Unable to create LOG directory ERROR: $err\n|;
    }

    my $log_file = join(
        $separator,
        (
            $log_dir,
            (
                $self->{html_log_required}
                  && ( $self->{html_log_required} =~ /yes/i )
              )
            ? LOG_FILE
              . ".html"
            : LOG_FILE
        )
    );
    my $error_log_file = join(
        $separator,
        (
            $log_dir,
            (
                $self->{html_log_required}
                  && ( $self->{html_log_required} =~ /yes/i )
              )
            ? ERR_LOG_FILE
              . ".html"
            : ERR_LOG_FILE
        )
    );
    $self->{file}->{log_file}     = $log_file;
    $self->{file}->{err_log_file} = $error_log_file;
}

sub _open_fh {
    my ( $self, %args ) = @_;

    my ( $fh, $err ) = (undef) x 2;

    my $file = $args{FILE};
    my $mode = $args{MODE};

    if ( $mode =~ m/READ/i ) {
        open( $fh, '<', "$file" ) or ( $err = 'yes' );
    } elsif ( $mode =~ m/WRITE/i ) {
        open( $fh, '>', "$file" ) or ( $err = 'yes' );
    } elsif ( $mode =~ m/APPEND/i ) {
        open( $fh, '>>', "$file" ) or ( $err = 'yes' );
    }

    if ($err) {
        die qq|\nUnable to open file '$file' for $mode\nERROR: $!\n|;
    }

    return $fh;

}

sub _trim($) {

    return unless ( $_[0] );

    my $str = $_[0];
    $str =~ s/^\s+//g;
    $str =~ s/\s+$//g;

    return $str;
}

sub _print_logs {
    my ( $self, %args ) = @_;

    no warnings;

    my $fh       = $args{fh};
    my $res      = $args{res};
    my $uri      = $args{uri};
    my $method   = $args{method};
    my $req_body = $args{req_body};

    my $define =
"Definition of status code not available; Please define in config if this is a custom code";

    unless ( $args{res} ) {
        print $fh "\n";
        print $fh "URI           => $uri\n";
        print $fh "HTTP Method   => $method\n";
        print $fh "Request Body  => \n$req_body\n" if ( $method !~ /get/i );
    } else {

        print $fh "\n";
        print $fh "Response code => ";

lib/APP/REST/RestTestSuite.pm  view on Meta::CPAN

#   request_body             :
#   [START] 
#   xml or json or  form based 
#   [END]
################
#Set below values to configure the base URL for all test cases

####################
#START_COMMON_CONFIG
################################################################################
  endpoint                  : www.thomas-bayer.com 
  port                      :
  base_uri                  : /sqlrest 
  html_log_required         : no 
  username                  : 

################################################################################
#END_COMMON_CONFIG
##################

#####################
#START_TEST_CASE
#####################
  test_case                : get_product
  uri                      : /PRODUCT/49
  request_content_type     : application/xml
  request_method           : GET
  request_body             :
  response_status          : 200
  execute                  : yes
  response_content_type    : application/xml 
#####################
#END_TEST_CASE
#####################

###########################
#START_HTTP_CODE_DEF
############################
200 :  operation successful.
201 :  Successful creation of a resource.
202 :  The request was received.
204 :  The request was processed successfully, but no response body is needed.
301 :  Resource has moved.
303 :  Redirection.
304 :  Resource has not been modified.
400 :  Malformed syntax or a bad query.
401 :  Action requires user authentication.
403 :  Authentication failure or invalid Application ID.
404 :  Resource not found.
405 :  Method not allowed on resource.
406 :  Requested representation not available for the resource.
408 :  Request has timed out.
409 :  State of the resource doesn't permit request.
410 :  The URI used to refer to a resource.
411 :  The server needs to know the size of the entity body and it should be specified in the Content Length header.
412 :  Operation not completed because preconditions were not met.
413 :  The representation was too large for the server to handle.
414 :  The URI has more than 2k characters.
415 :  Representation not supported for the resource.
416 :  Requested range not satisfiable.
500 :  Internal server error.
501 :  Requested HTTP operation not supported.
502 :  Backend service failure (data store failure).
505 :  HTTP version not supported.
############################
#END_HTTP_CODE_DEF
############################

####################
#END_OF_CONFIG_FILE
####################



( run in 0.462 second using v1.01-cache-2.11-cpan-501a3233654 )