TestRail-API.t 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. use strict;
  2. use warnings;
  3. use FindBin;
  4. use lib "$FindBin::Bin/lib";
  5. use TestRail::API;
  6. use Test::LWP::UserAgent::TestRailMock;
  7. use Test::More tests => 75;
  8. use Test::Fatal;
  9. use Test::Deep;
  10. use Scalar::Util ();
  11. my $apiurl = $ENV{'TESTRAIL_API_URL'};
  12. my $login = $ENV{'TESTRAIL_USER'};
  13. my $pw = $ENV{'TESTRAIL_PASSWORD'};
  14. #Mock if nothing is provided
  15. my $is_mock = (!$apiurl && !$login && !$pw);
  16. like(exception {TestRail::API->new('trash','bogus','bogus');}, qr/invalid uri/i, "Non-URIs bounce constructor");
  17. #XXX for some insane reason 'hokum.bogus' seems to be popular with cpantesters
  18. my $bogoError = exception {TestRail::API->new('http://hokum.bogus','lies','moreLies',undef,0); };
  19. SKIP: {
  20. skip("Some CPANTesters like to randomly redirect all DNS misses to some other host, apparently", 1) if ($bogoError =~ m/404|302/);
  21. like($bogoError, qr/Could not communicate with TestRail Server/i,"Bogus Testrail URI rejected");
  22. }
  23. SKIP: {
  24. skip("Testing authentication not supported with mock",2) if ($is_mock);
  25. like(exception {TestRail::API->new($apiurl,'lies','moreLies',undef,0); }, qr/Bad user credentials/i,"Bogus Testrail User rejected");
  26. like(exception {TestRail::API->new($apiurl,$login,'m043L13s ',undef,0); }, qr/Bad user credentials/i,"Bogus Testrail Password rejected");
  27. }
  28. ($apiurl,$login,$pw) = ('http://testrail.local','teodesian@cpan.org','fake') if $is_mock;
  29. my $tr = new TestRail::API($apiurl,$login,$pw,undef,1);
  30. #Mock if necesary
  31. $tr->{'debug'} = 0;
  32. $tr->{'browser'} = $Test::LWP::UserAgent::TestRailMock::mockObject if $is_mock;
  33. is($tr->_doRequest('noSuchMethod'),-404,'Requesting bad URI returns 404');
  34. #Test USER methods
  35. my $userlist = $tr->getUsers();
  36. ok(@$userlist,"Get Users returns list");
  37. my $myuser = $tr->getUserByEmail($login);
  38. is($myuser->{'email'},$login,"Can get user by email");
  39. is($tr->getUserByID($myuser->{'id'})->{'id'},$myuser->{'id'},"Can get user by ID");
  40. is($tr->getUserByName($myuser->{'name'})->{'name'},$myuser->{'name'},"Can get user by Name");
  41. my @user_names = map {$_->{'name'}} @$userlist;
  42. my @user_ids = map {$_->{'id'}} @$userlist;
  43. my @cuser_ids = $tr->userNamesToIds(@user_names);
  44. cmp_deeply(\@cuser_ids,\@user_ids,"userNamesToIds functions correctly");
  45. isnt(exception {$tr->userNamesToIds(@user_names,'potzrebie'); }, undef, "Passing invalid user name throws exception");
  46. #Test CASE TYPE method
  47. my $caseTypes = $tr->getCaseTypes();
  48. is(ref($caseTypes),'ARRAY',"getCaseTypes returns ARRAY of case types");
  49. #Test PROJECT methods
  50. my $project_name = 'CRUSH ALL HUMANS';
  51. my $new_project = $tr->createProject($project_name,'Robo-Signed Soviet 5 Year Project');
  52. is($new_project->{'name'},$project_name,"Can create new project");
  53. ok($tr->getProjects(),"Get Projects returns list");
  54. is($tr->getProjectByName($project_name)->{'name'},$project_name,"Can get project by name");
  55. my $pjid = $tr->getProjectByID($new_project->{'id'});
  56. is(Scalar::Util::reftype($pjid) eq 'HASH' ? $pjid->{'id'} : $pjid,$new_project->{'id'},"Can get project by id");
  57. #Test TESTSUITE methods
  58. my $suite_name = 'HAMBURGER-IZE HUMANITY';
  59. my $new_suite = $tr->createTestSuite($new_project->{'id'},$suite_name,"Robo-Signed Patriotic People's TestSuite");
  60. is($new_suite->{'name'},$suite_name,"Can create new testsuite");
  61. ok($tr->getTestSuites($new_project->{'id'}),"Can get listing of testsuites for project");
  62. is($tr->getTestSuiteByName($new_project->{'id'},$new_suite->{'name'})->{'name'},$new_suite->{'name'},"Can get suite by name");
  63. is($tr->getTestSuiteByID($new_suite->{'id'})->{'id'},$new_suite->{'id'},"Can get suite by id");
  64. #Test SECTION methods -- roughly analogous to TESTSUITES in TL
  65. my $section_name = 'CARBON LIQUEFACTION';
  66. my $new_section = $tr->createSection($new_project->{'id'},$new_suite->{'id'},$section_name);
  67. is($new_section->{'name'},$section_name,"Can create new section");
  68. ok($tr->getSections($new_project->{'id'},$new_suite->{'id'}),"Can get section listing");
  69. is($tr->getSectionByName($new_project->{'id'},$new_suite->{'id'},$section_name)->{'name'},$section_name,"Can get section by name");
  70. is($tr->getSectionByID($new_section->{'id'})->{'id'},$new_section->{'id'},"Can get new section by id");
  71. my @cids = $tr->sectionNamesToIds($new_project->{'id'},$new_suite->{'id'},$section_name);
  72. is($cids[0],$new_section->{'id'},"sectionNamesToIds returns correct IDs");
  73. isnt(exception {$tr->sectionNamesToIds($new_project->{'id'},$new_suite->{'id'},"No such Section");},undef,"Passing bogus section to sectionNamesToIds throws exception");
  74. #Test CASE methods
  75. my $case_name = 'STROGGIFY POPULATION CENTERS';
  76. my $new_case = $tr->createCase($new_section->{'id'},$case_name);
  77. is($new_case->{'title'},$case_name,"Can create new test case");
  78. ok($tr->getCases($new_project->{'id'},$new_suite->{'id'},$new_section->{'id'}),"Can get case listing");
  79. is($tr->getCaseByName($new_project->{'id'}, $new_suite->{'id'}, $new_section->{'id'}, $case_name)->{'title'},$case_name,"Can get case by name");
  80. is($tr->getCaseByID($new_case->{'id'})->{'id'},$new_case->{'id'},"Can get case by ID");
  81. #Test RUN methods
  82. my $run_name = 'SEND T-1000 INFILTRATION UNITS BACK IN TIME';
  83. my $new_run = $tr->createRun($new_project->{'id'},$new_suite->{'id'},$run_name,"ACQUIRE CLOTHES, BOOTS AND MOTORCYCLE");
  84. is($new_run->{'name'},$run_name,"Can create new run");
  85. ok($tr->getRuns($new_project->{'id'}),"Can get list of runs");
  86. is($tr->getRunByName($new_project->{'id'},$run_name)->{'name'},$run_name,"Can get run by name");
  87. is($tr->getRunByID($new_run->{'id'})->{'id'},$new_run->{'id'},"Can get run by ID");
  88. #Test MILESTONE methods
  89. my $milestone_name = "Humanity Exterminated";
  90. my $new_milestone = $tr->createMilestone($new_project->{'id'},$milestone_name,"Kill quota reached if not achieved in 5 years",time()+157788000); #It IS a soviet 5-year plan after all :)
  91. is($new_milestone->{'name'},$milestone_name,"Can create new milestone");
  92. ok($tr->getMilestones($new_project->{'id'}),"Can get list of milestones");
  93. is($tr->getMilestoneByName($new_project->{'id'},$milestone_name)->{'name'},$milestone_name,"Can get milestone by name");
  94. is($tr->getMilestoneByID($new_milestone->{'id'})->{'id'},$new_milestone->{'id'},"Can get milestone by ID");
  95. #Test PLAN methods
  96. my $plan_name = "GosPlan";
  97. my $new_plan = $tr->createPlan($new_project->{'id'},$plan_name,"Soviet 5-year agriculture plan to liquidate Kulaks",$new_milestone->{'id'},[{ suite_id => $new_suite->{'id'}, name => "Executing the great plan"}]);
  98. is($new_plan->{'name'},$plan_name,"Can create new plan");
  99. ok($tr->getPlans($new_project->{'id'}),"Can get list of plans");
  100. my $namePlan = $tr->getPlanByName($new_project->{'id'},$plan_name);
  101. is($namePlan->{'name'},$plan_name,"Can get plan by name");
  102. is($tr->getPlanByID($new_plan->{'id'})->{'id'},$new_plan->{'id'},"Can get plan by ID");
  103. #Get runs per plan, create runs in plan
  104. my $prun = $new_plan->{'entries'}->[0]->{'runs'}->[0];
  105. is($tr->getRunByID($prun->{'id'})->{'name'},"Executing the great plan","Can get child run of plan by ID");
  106. is($tr->getChildRunByName($new_plan,"Executing the great plan")->{'id'},$prun->{'id'},"Can find child run of plan by name");
  107. SKIP: {
  108. skip("Cannot create configurations programattically in the API like in mocks",2) if !$is_mock;
  109. isnt($tr->getChildRunByName($namePlan,"Executing the great plan",['testConfig']),0,"Getting run by name returns child runs");
  110. is($tr->getChildRunByName($namePlan,"Executing the great plan"),0,"Getting run by name without sufficient configuration data returns child runs");
  111. }
  112. #Test createRunInPlan
  113. my $updatedPlan = $tr->createRunInPlan($new_plan->{'id'},$new_suite->{'id'},'Dynamic Plan Run');
  114. $prun = $updatedPlan->{'runs'}->[0];
  115. is($tr->getRunByID($prun->{'id'})->{'name'},"Dynamic Plan Run","Can get newly created child run of plan by ID");
  116. #Test TEST/RESULT methods
  117. my $tests = $tr->getTests($new_run->{'id'});
  118. ok($tests,"Can get tests");
  119. is($tr->getTestByName($new_run->{'id'},$tests->[0]->{'title'})->{'title'},$tests->[0]->{'title'},"Can get test by name");
  120. is($tr->getTestByID($tests->[0]->{'id'})->{'id'},$tests->[0]->{'id'},"Can get test by ID");
  121. my $resTypes = $tr->getTestResultFields();
  122. my $statusTypes = $tr->getPossibleTestStatuses();
  123. ok($resTypes,"Can get test result fields");
  124. ok($statusTypes,"Can get possible test statuses");
  125. my @status_names = map {$_->{'name'}} @$statusTypes;
  126. my @status_ids = map {$_->{'id'}} @$statusTypes;
  127. my @computed_ids = $tr->statusNamesToIds(@status_names);
  128. cmp_deeply(\@computed_ids,\@status_ids,"statusNamesToIds functions correctly");
  129. isnt(exception {$tr->statusNamesToIds(@status_names,'potzrebie'); }, undef, "Passing invalid status name throws exception");
  130. #TODO make more thorough tests for options, custom options
  131. my $result = $tr->createTestResults($tests->[0]->{'id'},$statusTypes->[0]->{'id'},"REAPER FORCES INBOUND");
  132. ok(defined($result->{'id'}),"Can add test results");
  133. my $results = $tr->getTestResults($tests->[0]->{'id'});
  134. is($results->[0]->{'id'},$result->{'id'},"Can get results for test");
  135. #Bulk add results
  136. $results = $tr->bulkAddResults($new_run->{'id'}, [{ 'test_id' => $tests->[0]->{'id'},'status_id' => $statusTypes->[0]->{'id'}, "comment" => "REAPER FORCES INBOUND" }]);
  137. ok(defined($results->[0]->{'id'}),"Can bulk add test results");
  138. #Test status and assignedto filtering
  139. my $filteredTests = $tr->getTests($new_run->{'id'},[$status_ids[0]]);
  140. is(scalar(@$filteredTests),1,"Test Filtering works: status id positive");
  141. $filteredTests = $tr->getTests($new_run->{'id'},[$status_ids[1]]);
  142. is(scalar(@$filteredTests),0,"Test Filtering works: status id negative");
  143. $filteredTests = $tr->getTests($new_run->{'id'},[$status_ids[0]],[$userlist->[0]->{'id'}]);
  144. is(scalar(@$filteredTests),0,"Test Filtering works: status id positive, user id negative");
  145. $filteredTests = $tr->getTests($new_run->{'id'},undef,[$userlist->[0]->{'id'}]);
  146. is(scalar(@$filteredTests),0,"Test Filtering works: status id undef, user id negative");
  147. #XXX there is no way to programmatically assign things :( so this will remain somewhat uncovered
  148. #Get run summary
  149. my $runs = $tr->getRuns($new_project->{'id'});
  150. my ($summary) = $tr->getRunSummary(@$runs); #I only care about the first one
  151. isnt($summary->{'run_status'},undef,"Can get run statuses correctly");
  152. is($summary->{'run_status'}->{'passed'},int(!$is_mock),"Gets # of passed cases correctly");
  153. is($summary->{'run_status'}->{'untested'},int($is_mock),"Gets # of untested cases correctly");
  154. #Test configuration methods
  155. my $configs = $tr->getConfigurations($new_project->{'id'});
  156. my $is_arr = is(Scalar::Util::reftype($configs),'ARRAY',"Can get configurations for a project");
  157. my (@config_names,@config_ids);
  158. if ($is_arr) {
  159. @config_names = map {$_->{'name'}} @$configs;
  160. @config_ids = map {$_->{'id'}} @$configs;
  161. }
  162. my $t_config_ids = $tr->translateConfigNamesToIds($new_project->{'id'},\@config_names);
  163. @config_ids = sort(@config_ids);
  164. @$t_config_ids = sort(@$t_config_ids);
  165. is_deeply(\@config_ids,$t_config_ids, "Can correctly translate Project names to IDs");
  166. ############################################################
  167. # TestRail arbitrarily limits many calls to 250 result sets.
  168. # Let's make sure our getters actually get everything.
  169. ############################################################
  170. SKIP: {
  171. skip("Skipping slow tests...", 2) if $ENV{'TESTRAIL_SLOW_TESTS'};
  172. #Check get_plans
  173. foreach my $i (0..$tr->{'global_limit'}) {
  174. $tr->createPlan($new_project->{'id'},$plan_name,"PETE & RE-PIOTR");
  175. }
  176. is(scalar(@{$tr->getPlans($new_project->{'id'})}),($tr->{'global_limit'} + 2),"Can get list of plans beyond ".$tr->{'global_limit'});
  177. #Check get_runs
  178. foreach my $i (0..$tr->{'global_limit'}) {
  179. $tr->createRun($new_project->{'id'},$new_suite->{'id'},$run_name,"ACQUIRE CLOTHES, BOOTS AND MOTORCYCLE");
  180. }
  181. is(scalar(@{$tr->getRuns($new_project->{'id'})}),($tr->{'global_limit'} + 2),"Can get list of runs beyond ".$tr->{'global_limit'});
  182. }
  183. ##########
  184. # Clean up
  185. ##########
  186. #Delete a plan
  187. ok($tr->deletePlan($new_plan->{'id'}),"Can delete plan");
  188. #Delete a milestone
  189. ok($tr->deleteMilestone($new_milestone->{'id'}),"Can delete milestone");
  190. #Delete a run
  191. ok($tr->deleteRun($new_run->{'id'}),"Can delete run");
  192. #Delete a case
  193. ok($tr->deleteCase($new_case->{'id'}),"Can delete Case");
  194. #Delete a section
  195. ok($tr->deleteSection($new_section->{'id'}),"Can delete Section");
  196. #Delete a testsuite
  197. ok($tr->deleteTestSuite($new_suite->{'id'}),"Can delete TestSuite");
  198. #Delete project now that we are done with it
  199. ok($tr->deleteProject($new_project->{'id'}),"Can delete project");
  200. 1;