Bläddra i källkod

Fix #157 Add new binary testrail-replay

George S. Baugh 5 år sedan
förälder
incheckning
8b2857ed24
4 ändrade filer med 285 tillägg och 1 borttagningar
  1. 4 0
      Changes
  2. 181 0
      bin/testrail-replay
  3. 1 1
      dist.ini
  4. 99 0
      t/testrail-replay.t

+ 4 - 0
Changes

@@ -1,5 +1,9 @@
 Revision history for Perl module TestRail::API
 
+0.046 2020-05-8 TEODESIAN
+    - Hide password entry in bin/ utilities.
+    - Add bin/testrail-replay to re-play the results of runs and plans.
+
 0.045 2019-05-11 TEODESIAN
     - Add getReports and runReport methods to TestRail::API
     - Use a cookie jar when doing requests to use testrail sessions if enabled for the API

+ 181 - 0
bin/testrail-replay

@@ -0,0 +1,181 @@
+#!/usr/bin/perl
+# ABSTRACT: List runs in a TestRail project matching the provided filters
+# PODNAME: TestRail::Bin::Replay
+
+=head1 SYNOPSIS
+
+  testrail-replay [OPTIONS] NAME
+
+  require `which testrail-runs`;
+  TestRail::Bin::Replay::run('args' => \@args);
+
+=head1 DESCRIPTION
+
+testrail-replay - Re-play the results of a test run or plan as though executed by prove.
+Optionally wait for results to come in.
+
+Can be used as the modulino TestRail::Bin::Replay.
+Has a single 'run' function which accepts a hash with the 'args' parameter being the array of arguments.
+
+=head1 PARAMETERS:
+
+=head2 MANDATORY PARAMETERS
+
+=over 4
+
+--apiurl     : full URL to get to TestRail index document
+
+--password   : Your TestRail Password, or a valid API key (TestRail 4.2 and above).
+
+--user       : Your TestRail User Name.
+
+-j --project : desired project name.
+
+=back
+
+All mandatory options not passed with the above switches, or in your ~/.testrailrc will be prompted for.
+
+=head2 OPTIONAL PARAMETERS
+
+=over 4
+
+-p --plan     : Instead of a run to look for, look for a plan with the provided NAME.
+
+-e --encoding : Character encoding of arguments.  Defaults to UTF-8. See L<Encode::Supported> for supported encodings.
+
+-v --verbose  : Print full test output rather than summaries with links to the testrail result.
+
+-w --watch    : Watch tests until all report results other than untested or re-test.
+
+=back
+
+=head1 CONFIGURATION FILE
+
+In your \$HOME, (or the current directory, if your system has no concept of a home directory) put a file called .testrailrc with key=value syntax separated by newlines.
+Valid Keys are the same as documented by L<App::Prove::Plugin::TestRail>.
+All options specified thereby are overridden by passing the command-line switches above.
+
+=head1 MISCELLANEOUS OPTIONS:
+
+=over 4
+
+--help : show this output
+
+=back
+
+=cut
+
+package TestRail::Bin::Replay;
+
+use strict;
+use warnings;
+
+use TestRail::API;
+use TestRail::Utils;
+use TestRail::Utils::Find;
+
+use Getopt::Long qw{GetOptionsFromArray};
+use File::HomeDir qw{my_home};
+
+if (!caller()) {
+    my ($out,$code) = run('args' => \@ARGV);
+    print $out;
+    exit $code;
+}
+
+sub run {
+    my %params = @_;
+    my $opts = {};
+
+    # Parse config file
+    my $homedir = my_home() || '.';
+    if (-e $homedir . '/.testrailrc') {
+        $opts = TestRail::Utils::parseConfig($homedir);
+    }
+
+    GetOptionsFromArray($params{'args'},
+        'apiurl=s'     => \$opts->{'apiurl'},
+        'password=s'   => \$opts->{'password'},
+        'user=s'       => \$opts->{'user'},
+        'j|project=s'  => \$opts->{'project'},
+        'e|encoding=s' => \$opts->{'encoding'},
+        'p|plan'       => \$opts->{'plan'},
+        'v|verbose'    => \$opts->{'verbose'},
+        'w|watch'      => \$opts->{'watch'},
+        'h|help'       => \$opts->{'help'},
+    );
+
+    if ($opts->{help}) { return ('',TestRail::Utils::help()); }
+
+    $opts->{'browser'} = $params{'browser'};
+
+    TestRail::Utils::interrogateUser($opts,qw{apiurl user password project});
+
+    my $tr = TestRail::Utils::getHandle($opts);
+    my $project = $tr->getProjectByName($opts->{'project'});
+    return ("No such project '$opts->{project}'",2) unless $project;
+
+    my $subject = $ARGV[0];
+    my ($runs,$plan);
+
+    if ($opts->{'plan'}) {
+        $plan = $tr->getPlanByName($project->{'id'},$subject);
+        $runs = $tr->getChildRuns($plan) if $plan;
+    } else {
+        my $run = $tr->getRunByName($project->{'id'},$subject);
+        push( @$runs, $run) if $run;
+    }
+
+    return ("No runs found matching your criterion.\n",1) unless ref $runs eq 'ARRAY' && @$runs;
+
+    my @tests;
+    foreach my $run (@$runs) {
+        my $tests = $tr->getTests($run->{id});
+        @$tests = map { $_->{config} = $run->{config}; $_ } @$tests;
+        push( @tests, @{$tests}) if ref $tests eq 'ARRAY' && @$tests;
+    }
+
+    #TODO get status ids for untested/retest, check em
+    my @retry_status_ids = $tr->statusNamesToIds(qw{untested retest});
+    my @bad_status_ids   = $tr->statusNamesToIds(qw{failed todo_pass});
+
+    my $rc = 0;
+    while (my $test = shift @tests) {
+        my $results = $tr->getTestResults($test->{id},1);
+        if ( !( ref $results eq 'ARRAY') || !@$results) {
+            push(@tests,$test) if $opts->{watch};
+            next;
+        }
+        my $result = $results->[0];
+        next unless ref $result eq 'HASH';
+        if (!grep { $result->{status_id} eq $_ } @retry_status_ids) {
+            print $result->{comment}."\n" if $opts->{verbose};
+            my $line = $test->{title};
+            $line .= " ($test->{config})" if $test->{config};
+            $line .= " ...";
+            if (grep { $result->{status_id} eq $_ } @bad_status_ids) {
+                $rc = 3;
+                $line .= ' not';
+            }
+            print "# $opts->{apiurl}/index.php?/tests/view/$test->{id}\n";
+            print "$line ok\n";
+            next;
+        }
+        push(@tests,$test) if $opts->{watch};
+        # If we have to wait, back off a little to not slam the server
+        sleep 1;
+    }
+
+    return ("Done",$rc);
+}
+
+1;
+__END__
+
+L<TestRail::API>
+
+L<File::HomeDir> for the finding of .testrailrc
+
+=head1 SPECIAL THANKS
+
+Thanks to cPanel Inc, for graciously funding the creation of this distribution.

+ 1 - 1
dist.ini

@@ -1,6 +1,6 @@
 name = TestRail-API
 main_module = lib/TestRail/API.pm
-version = 0.045
+version = 0.046
 author = George S. Baugh <teodesian@cpan.org>
 license = Perl_5
 copyright_holder = George S. Baugh

+ 99 - 0
t/testrail-replay.t

@@ -0,0 +1,99 @@
+use strict;
+use warnings;
+
+use FindBin;
+
+use lib $FindBin::Bin.'/../bin';
+require 'testrail-replay';
+
+use Test::More 'tests' => 6;
+use Capture::Tiny qw{capture_merged};
+use Test::Fatal;
+use Test::MockModule qw{strict};
+
+my $utilmock = Test::MockModule->new('TestRail::Utils');
+$utilmock->redefine('getHandle', sub { bless({},"TestRail::API") } );
+
+my $apimock = Test::MockModule->new('TestRail::API');
+$apimock->redefine('new', sub{ return bless({},shift) });
+$apimock->redefine('getProjectByName', sub { { id => 666 } });
+$apimock->redefine('getRunByName', sub { { id => 333 } });
+$apimock->redefine('getPlanByName', sub { { id => 222, config => 'BogusConfig' } } );
+$apimock->redefine('getChildRuns', sub { { [{ id => 111 }] } });
+$apimock->redefine('statusNamesToIds', sub { shift; shift eq 'failed' ? [5] : [4] });
+
+$apimock->redefine('getTests', sub {
+    my ($self,$run_id) = @_;
+    return [
+        {
+            'id' => 666,
+            'title' => 'fake.test',
+            'run_id' => $run_id
+        }
+    ];
+});
+
+$apimock->redefine('getTestResults', sub {
+    return [
+        {
+            'elapsed' => '1s',
+            'status_id'  => 5
+        },
+        {
+            'elapsed' => '2s',
+            'status_id' => 4,
+            'comment'   => 'zippy'
+        }
+    ];
+});
+
+#check doing things over all projects/plans/runs
+my @args = qw{--apiurl http://testrail.local --user test@fake.fake -password fake argument1 };
+my ($out, $code);
+my $captured = capture_merged { ($out,$code) = TestRail::Bin::Replay::run('args' => \@args) };
+subtest "Happy path" => sub {
+    like($captured, qr/fake\.test \.\.\. ok/, "Expected output stream");
+    like($out, qr/Done/,"Expected termination string");
+    is($code,0,"OK Exit code");
+};
+
+@args = qw{--apiurl http://testrail.local --user test@fake.fake -password fake --plan argument1 };
+$captured = capture_merged { ($out,$code) = TestRail::Bin::Replay::run('args' => \@args) };
+subtest "Happy path - plan mode" => sub {
+    like($captured, qr/fake\.test \.\.\. ok/, "Expected output stream");
+    like($out, qr/Done/,"Expected termination string");
+    is($code,0,"OK Exit code");
+};
+
+$apimock->redefine('getTestResults', sub {
+    return [
+        {
+            'elapsed' => '1s',
+            'status_id'  => 5
+        },
+        {
+            'elapsed' => '2s',
+            'status_id' => 1,
+            'comment'   => 'zippy'
+        }
+    ];
+});
+
+@args = qw{--apiurl http://testrail.local --user test@fake.fake -password fake --wait --plan argument1 };
+$captured = capture_merged { ($out,$code) = TestRail::Bin::Replay::run('args' => \@args) };
+subtest "Happy path - wait mode" => sub {
+    like($captured, qr/fake\.test \.\.\. ok/, "Expected output stream");
+    like($out, qr/Done/,"Expected termination string");
+    is($code,0,"OK Exit code");
+};
+
+#Check help output
+@args = qw{--help};
+$0 = $FindBin::Bin.'/../bin/testrail-replay';
+($out,(undef,$code)) = capture_merged {TestRail::Bin::Replay::run('args' => \@args)};
+is($code, 0, "Exit code OK asking for help");
+like($out,qr/encoding of arguments/i,"Help output OK");
+
+#Make sure that the binary itself processes args correctly
+$out = `$^X $0 --help`;
+like($out,qr/encoding of arguments/i,"Appears we can run binary successfully");