diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f06b7a0..4cff3d41 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - New interface `Redmine\Http\Request` for sending data with new minimalistic HTTP clients. - New method `Redmine\Api\...::getLastResponse()` to get the last response made by the API class. +### Changed + +- Calling `Redmine\Api\IssueRelation::create()` without the mandatory parameter `issue_to_id` throws a `Redmine\Exception\MissingParameterException` instead of returning a error array + ### Fixed - Parameter types for IDs were fixed in API for attachments, groups, issues, project, users and versions. @@ -23,6 +27,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Deprecated - `Redmine\Api\AbstractApi::get()` is deprecated, use `\Redmine\Http\HttpClient::request()` instead. +- `Redmine\Api\AbstractApi::post()` is deprecated, use `\Redmine\Http\HttpClient::request()` instead. ## [v2.5.0](https://github.com/kbsali/php-redmine-api/compare/v2.4.0...v2.5.0) - 2024-02-05 diff --git a/src/Redmine/Api/AbstractApi.php b/src/Redmine/Api/AbstractApi.php index 823df935..af329ca9 100644 --- a/src/Redmine/Api/AbstractApi.php +++ b/src/Redmine/Api/AbstractApi.php @@ -2,7 +2,6 @@ namespace Redmine\Api; -use Closure; use InvalidArgumentException; use Redmine\Api; use Redmine\Client\Client; @@ -142,6 +141,8 @@ protected function get($path, $decodeIfJson = true) /** * Perform the client post() method. * + * @deprecated since v2.6.0, use `\Redmine\Http\HttpClient::request()` instead + * * @param string $path * @param string $data * @@ -149,6 +150,8 @@ protected function get($path, $decodeIfJson = true) */ protected function post($path, $data) { + @trigger_error('`' . __METHOD__ . '()` is deprecated since v2.6.0, use `\Redmine\Http\HttpClient::request()` instead.', E_USER_DEPRECATED); + $this->lastResponse = $this->getHttpClient()->request(HttpFactory::makeRequest( 'POST', strval($path), diff --git a/src/Redmine/Api/Attachment.php b/src/Redmine/Api/Attachment.php index a1f3491c..6ad17939 100644 --- a/src/Redmine/Api/Attachment.php +++ b/src/Redmine/Api/Attachment.php @@ -6,6 +6,7 @@ use Redmine\Http\HttpFactory; use Redmine\Serializer\JsonSerializer; use Redmine\Serializer\PathSerializer; +use SimpleXMLElement; /** * Attachment details. @@ -80,10 +81,14 @@ public function download($id) */ public function upload($attachment, $params = []) { - return $this->post( + $this->lastResponse = $this->getHttpClient()->request(HttpFactory::makeRequest( + 'POST', PathSerializer::create('/uploads.json', $params)->getPath(), + 'application/octet-stream', $attachment - ); + )); + + return $this->lastResponse->getContent(); } /** @@ -93,7 +98,7 @@ public function upload($attachment, $params = []) * * @param int $id id of the attachment * - * @return false|\SimpleXMLElement|string + * @return false|SimpleXMLElement|string */ public function remove($id) { diff --git a/src/Redmine/Api/Group.php b/src/Redmine/Api/Group.php index 42052cce..442f848b 100644 --- a/src/Redmine/Api/Group.php +++ b/src/Redmine/Api/Group.php @@ -120,10 +120,19 @@ public function create(array $params = []) throw new MissingParameterException('Theses parameters are mandatory: `name`'); } - return $this->post( + $this->lastResponse = $this->getHttpClient()->request(HttpFactory::makeXmlRequest( + 'POST', '/groups.xml', XmlSerializer::createFromArray(['group' => $params])->getEncoded() - ); + )); + + $body = $this->lastResponse->getContent(); + + if ($body === '') { + return $body; + } + + return new SimpleXMLElement($body); } /** @@ -205,14 +214,23 @@ public function remove($id) * @param int $id id of the group * @param int $userId id of the user * - * @return string + * @return SimpleXMLElement|string */ public function addUser($id, $userId) { - return $this->post( + $this->lastResponse = $this->getHttpClient()->request(HttpFactory::makeXmlRequest( + 'POST', '/groups/' . $id . '/users.xml', XmlSerializer::createFromArray(['user_id' => $userId])->getEncoded() - ); + )); + + $body = $this->lastResponse->getContent(); + + if ($body === '') { + return $body; + } + + return new SimpleXMLElement($body); } /** diff --git a/src/Redmine/Api/Issue.php b/src/Redmine/Api/Issue.php index f8b8ebc7..f56a7ea2 100644 --- a/src/Redmine/Api/Issue.php +++ b/src/Redmine/Api/Issue.php @@ -189,10 +189,24 @@ public function create(array $params = []) $params = $this->cleanParams($params); $params = $this->sanitizeParams($defaults, $params); - return $this->post( + // FIXME: Throw exception on missing mandatory parameters + // if (!isset($params['subject']) || !isset($params['project_id']) || !isset($params['tracker_id']) || !isset($params['priority_id']) || !isset($params['status_id'])) { + // throw new MissingParameterException('Theses parameters are mandatory: `subject`, `project_id|project`, `tracker_id|tracker`, `priority_id|priority`, `status_id|status`'); + // } + + $this->lastResponse = $this->getHttpClient()->request(HttpFactory::makeXmlRequest( + 'POST', '/issues.xml', XmlSerializer::createFromArray(['issue' => $params])->getEncoded() - ); + )); + + $body = $this->lastResponse->getContent(); + + if ($body === '') { + return $body; + } + + return new SimpleXMLElement($body); } /** @@ -236,21 +250,30 @@ public function update($id, array $params) * @param int $id * @param int $watcherUserId * - * @return false|string + * @return SimpleXMLElement|string */ public function addWatcher($id, $watcherUserId) { - return $this->post( + $this->lastResponse = $this->getHttpClient()->request(HttpFactory::makeXmlRequest( + 'POST', '/issues/' . urlencode(strval($id)) . '/watchers.xml', XmlSerializer::createFromArray(['user_id' => urlencode(strval($watcherUserId))])->getEncoded() - ); + )); + + $body = $this->lastResponse->getContent(); + + if ($body === '') { + return $body; + } + + return new SimpleXMLElement($body); } /** * @param int $id * @param int $watcherUserId * - * @return false|\SimpleXMLElement|string + * @return false|SimpleXMLElement|string */ public function removeWatcher($id, $watcherUserId) { @@ -385,7 +408,7 @@ public function attachMany($id, array $attachments) * * @param int $id the issue number * - * @return false|\SimpleXMLElement|string + * @return false|SimpleXMLElement|string */ public function remove($id) { diff --git a/src/Redmine/Api/IssueCategory.php b/src/Redmine/Api/IssueCategory.php index bd635ba1..903aabf3 100644 --- a/src/Redmine/Api/IssueCategory.php +++ b/src/Redmine/Api/IssueCategory.php @@ -11,6 +11,7 @@ use Redmine\Serializer\JsonSerializer; use Redmine\Serializer\PathSerializer; use Redmine\Serializer\XmlSerializer; +use SimpleXMLElement; /** * Listing issue categories, creating, editing. @@ -161,7 +162,7 @@ public function show($id) * * @throws MissingParameterException Missing mandatory parameters * - * @return string|false + * @return SimpleXMLElement|string */ public function create($project, array $params = []) { @@ -177,10 +178,19 @@ public function create($project, array $params = []) throw new MissingParameterException('Theses parameters are mandatory: `name`'); } - return $this->post( + $this->lastResponse = $this->getHttpClient()->request(HttpFactory::makeXmlRequest( + 'POST', '/projects/' . $project . '/issue_categories.xml', XmlSerializer::createFromArray(['issue_category' => $params])->getEncoded() - ); + )); + + $body = $this->lastResponse->getContent(); + + if ($body === '') { + return $body; + } + + return new SimpleXMLElement($body); } /** diff --git a/src/Redmine/Api/IssueRelation.php b/src/Redmine/Api/IssueRelation.php index 57ef6ae1..5cb90b96 100644 --- a/src/Redmine/Api/IssueRelation.php +++ b/src/Redmine/Api/IssueRelation.php @@ -3,6 +3,7 @@ namespace Redmine\Api; use Redmine\Exception; +use Redmine\Exception\MissingParameterException; use Redmine\Exception\SerializerException; use Redmine\Exception\UnexpectedResponseException; use Redmine\Http\HttpFactory; @@ -127,10 +128,17 @@ public function remove($id) * Create a new issue relation. * * @see http://www.redmine.org/projects/redmine/wiki/Rest_IssueRelations#POST + * available $params: + * - issue_to_id (required): the id of the related issue + * - relation_type (required to explicit : default "relates"): the type of relation + * (in: "relates", "duplicates", "duplicated", "blocks", "blocked", "precedes", "follows", "copied_to", "copied_from") + * - delay (optional): the delay for a "precedes" or "follows" relation * * @param int $issueId the ID of the issue we are creating the relation on * @param array $params the new issue relation data * + * @throws MissingParameterException Missing mandatory parameters + * * @return array */ public function create($issueId, array $params = []) @@ -143,11 +151,18 @@ public function create($issueId, array $params = []) $params = $this->sanitizeParams($defaults, $params); - $response = $this->post( + if (!isset($params['issue_to_id'])) { + throw new MissingParameterException('Theses parameters are mandatory: `issue_to_id`'); + } + + $this->lastResponse = $this->getHttpClient()->request(HttpFactory::makeJsonRequest( + 'POST', '/issues/' . urlencode(strval($issueId)) . '/relations.json', JsonSerializer::createFromArray(['relation' => $params])->getEncoded() - ); + )); + + $body = $this->lastResponse->getContent(); - return JsonSerializer::createFromString($response)->getNormalized(); + return JsonSerializer::createFromString($body)->getNormalized(); } } diff --git a/src/Redmine/Api/Membership.php b/src/Redmine/Api/Membership.php index 265af375..583be03c 100644 --- a/src/Redmine/Api/Membership.php +++ b/src/Redmine/Api/Membership.php @@ -7,7 +7,9 @@ use Redmine\Exception\MissingParameterException; use Redmine\Exception\SerializerException; use Redmine\Exception\UnexpectedResponseException; +use Redmine\Http\HttpFactory; use Redmine\Serializer\XmlSerializer; +use SimpleXMLElement; /** * Handling project memberships. @@ -92,7 +94,7 @@ public function all($project, array $params = []) * * @throws MissingParameterException Missing mandatory parameters * - * @return string|false + * @return SimpleXMLElement|string */ public function create($project, array $params = []) { @@ -106,10 +108,19 @@ public function create($project, array $params = []) throw new MissingParameterException('Theses parameters are mandatory: `user_id`, `role_ids`'); } - return $this->post( + $this->lastResponse = $this->getHttpClient()->request(HttpFactory::makeXmlRequest( + 'POST', '/projects/' . $project . '/memberships.xml', XmlSerializer::createFromArray(['membership' => $params])->getEncoded() - ); + )); + + $body = $this->lastResponse->getContent(); + + if ('' !== $body) { + return new SimpleXMLElement($body); + } + + return $body; } /** diff --git a/src/Redmine/Api/Project.php b/src/Redmine/Api/Project.php index 28e4e556..9daa6d6c 100755 --- a/src/Redmine/Api/Project.php +++ b/src/Redmine/Api/Project.php @@ -162,7 +162,7 @@ public function show($id, array $params = []) * * @throws MissingParameterException * - * @return string|SimpleXMLElement|false + * @return SimpleXMLElement|string */ public function create(array $params = []) { @@ -180,10 +180,19 @@ public function create(array $params = []) throw new MissingParameterException('Theses parameters are mandatory: `name`, `identifier`'); } - return $this->post( + $this->lastResponse = $this->getHttpClient()->request(HttpFactory::makeXmlRequest( + 'POST', '/projects.xml', XmlSerializer::createFromArray(['project' => $params])->getEncoded() - ); + )); + + $body = $this->lastResponse->getContent(); + + if ('' !== $body) { + return new SimpleXMLElement($body); + } + + return $body; } /** diff --git a/src/Redmine/Api/TimeEntry.php b/src/Redmine/Api/TimeEntry.php index 4c1940bb..5aa63c85 100644 --- a/src/Redmine/Api/TimeEntry.php +++ b/src/Redmine/Api/TimeEntry.php @@ -9,6 +9,7 @@ use Redmine\Http\HttpFactory; use Redmine\Serializer\JsonSerializer; use Redmine\Serializer\XmlSerializer; +use SimpleXMLElement; /** * Listing time entries, creating, editing. @@ -111,7 +112,7 @@ public function show($id) * * @throws MissingParameterException Missing mandatory parameters * - * @return string|false + * @return SimpleXMLElement|string */ public function create(array $params = []) { @@ -132,10 +133,19 @@ public function create(array $params = []) throw new MissingParameterException('Theses parameters are mandatory: `issue_id` or `project_id`, `hours`'); } - return $this->post( + $this->lastResponse = $this->getHttpClient()->request(HttpFactory::makeXmlRequest( + 'POST', '/time_entries.xml', XmlSerializer::createFromArray(['time_entry' => $params])->getEncoded() - ); + )); + + $body = $this->lastResponse->getContent(); + + if ('' !== $body) { + return new SimpleXMLElement($body); + } + + return $body; } /** diff --git a/src/Redmine/Api/User.php b/src/Redmine/Api/User.php index ac6ff612..b6ea1aad 100644 --- a/src/Redmine/Api/User.php +++ b/src/Redmine/Api/User.php @@ -10,6 +10,7 @@ use Redmine\Serializer\JsonSerializer; use Redmine\Serializer\PathSerializer; use Redmine\Serializer\XmlSerializer; +use SimpleXMLElement; /** * Listing users, creating, editing. @@ -187,7 +188,7 @@ public function show($id, array $params = []) * * @throws MissingParameterException Missing mandatory parameters * - * @return string|false + * @return SimpleXMLElement|string */ public function create(array $params = []) { @@ -209,10 +210,19 @@ public function create(array $params = []) throw new MissingParameterException('Theses parameters are mandatory: `login`, `lastname`, `firstname`, `mail`'); } - return $this->post( + $this->lastResponse = $this->getHttpClient()->request(HttpFactory::makeXmlRequest( + 'POST', '/users.xml', XmlSerializer::createFromArray(['user' => $params])->getEncoded() - ); + )); + + $body = $this->lastResponse->getContent(); + + if ('' !== $body) { + return new SimpleXMLElement($body); + } + + return $body; } /** diff --git a/src/Redmine/Api/Version.php b/src/Redmine/Api/Version.php index be0ccb3e..4fd8faba 100644 --- a/src/Redmine/Api/Version.php +++ b/src/Redmine/Api/Version.php @@ -10,6 +10,7 @@ use Redmine\Http\HttpFactory; use Redmine\Serializer\JsonSerializer; use Redmine\Serializer\XmlSerializer; +use SimpleXMLElement; /** * Listing versions, creating, editing. @@ -164,7 +165,7 @@ public function show($id) * * @throws MissingParameterException Missing mandatory parameters * - * @return string|false + * @return SimpleXMLElement|string */ public function create($project, array $params = []) { @@ -185,10 +186,19 @@ public function create($project, array $params = []) $this->validateStatus($params); $this->validateSharing($params); - return $this->post( + $this->lastResponse = $this->getHttpClient()->request(HttpFactory::makeXmlRequest( + 'POST', '/projects/' . $project . '/versions.xml', XmlSerializer::createFromArray(['version' => $params])->getEncoded() - ); + )); + + $body = $this->lastResponse->getContent(); + + if ('' !== $body) { + return new SimpleXMLElement($body); + } + + return $body; } /** diff --git a/src/Redmine/Client/NativeCurlClient.php b/src/Redmine/Client/NativeCurlClient.php index f2129020..f22e40ce 100644 --- a/src/Redmine/Client/NativeCurlClient.php +++ b/src/Redmine/Client/NativeCurlClient.php @@ -298,7 +298,7 @@ private function createCurl(string $method, string $path, string $body = '', str case 'POST': $curlOptions[CURLOPT_POST] = 1; if ($this->isUploadCall($path) && $this->isValidFilePath($body)) { - @trigger_error('Uploading an attachment by filepath is deprecated, use file_get_contents() to upload the file content instead.', E_USER_DEPRECATED); + @trigger_error('Uploading an attachment by filepath is deprecated since v2.1.0, use file_get_contents() to upload the file content instead.', E_USER_DEPRECATED); $file = fopen($body, 'r'); $size = filesize($body); diff --git a/src/Redmine/Client/Psr18Client.php b/src/Redmine/Client/Psr18Client.php index f5f1899b..e83806ab 100644 --- a/src/Redmine/Client/Psr18Client.php +++ b/src/Redmine/Client/Psr18Client.php @@ -254,7 +254,7 @@ private function createRequest(string $method, string $path, string $body = '', switch ($method) { case 'POST': if ($this->isUploadCall($path) && $this->isValidFilePath($body)) { - @trigger_error('Uploading an attachment by filepath is deprecated, use file_get_contents() to upload the file content instead.', E_USER_DEPRECATED); + @trigger_error('Uploading an attachment by filepath is deprecated since v2.1.0, use file_get_contents() to upload the file content instead.', E_USER_DEPRECATED); $request = $request->withBody( $this->streamFactory->createStreamFromFile($body) diff --git a/src/Redmine/Http/HttpFactory.php b/src/Redmine/Http/HttpFactory.php index 77246d69..47222037 100644 --- a/src/Redmine/Http/HttpFactory.php +++ b/src/Redmine/Http/HttpFactory.php @@ -84,4 +84,9 @@ public static function makeJsonRequest(string $method, string $path, string $con { return static::makeRequest($method, $path, 'application/json', $content); } + + public static function makeXmlRequest(string $method, string $path, string $content = ''): Request + { + return static::makeRequest($method, $path, 'application/xml', $content); + } } diff --git a/tests/Behat/Bootstrap/FeatureContext.php b/tests/Behat/Bootstrap/FeatureContext.php index d51a9fa2..19821c2e 100644 --- a/tests/Behat/Bootstrap/FeatureContext.php +++ b/tests/Behat/Bootstrap/FeatureContext.php @@ -6,7 +6,6 @@ use Behat\Behat\Context\Context; use Behat\Behat\Hook\Scope\AfterScenarioScope; -use Behat\Behat\Tester\Exception\PendingException; use Behat\Gherkin\Node\PyStringNode; use Behat\Gherkin\Node\TableNode; use Behat\Testwork\Hook\Scope\AfterSuiteScope; @@ -25,9 +24,14 @@ final class FeatureContext extends TestCase implements Context { use AttachmentContextTrait; use GroupContextTrait; + use IssueContextTrait; + use IssuePriorityContextTrait; + use IssueRelationContextTrait; + use IssueStatusContextTrait; use ProjectContextTrait; use TimeEntryActivityContextTrait; use TimeEntryContextTrait; + use TrackerContextTrait; use UserContextTrait; use VersionContextTrait; use WikiContextTrait; diff --git a/tests/Behat/Bootstrap/IssueContextTrait.php b/tests/Behat/Bootstrap/IssueContextTrait.php new file mode 100644 index 00000000..c536b4aa --- /dev/null +++ b/tests/Behat/Bootstrap/IssueContextTrait.php @@ -0,0 +1,46 @@ +getNativeCurlClient()->getApi('issue'); + + $this->registerClientResponse( + $api->create($data), + $api->getLastResponse() + ); + } + + /** + * @When I show the issue with id :issueId + */ + public function iShowTheIssueWithId($issueId) + { + /** @var Issue */ + $api = $this->getNativeCurlClient()->getApi('issue'); + + $this->registerClientResponse( + $api->show($issueId), + $api->getLastResponse() + ); + } +} diff --git a/tests/Behat/Bootstrap/IssuePriorityContextTrait.php b/tests/Behat/Bootstrap/IssuePriorityContextTrait.php new file mode 100644 index 00000000..e9eab7ca --- /dev/null +++ b/tests/Behat/Bootstrap/IssuePriorityContextTrait.php @@ -0,0 +1,27 @@ +redmine->excecuteDatabaseQuery( + 'INSERT INTO enumerations(name, position, is_default, type, active) VALUES(:name, :position, :is_default, :type, :active);', + [], + [ + ':name' => $priority, + ':position' => 1, + ':is_default' => 1, + ':type' => 'IssuePriority', + ':active' => 1, + ], + ); + } +} diff --git a/tests/Behat/Bootstrap/IssueRelationContextTrait.php b/tests/Behat/Bootstrap/IssueRelationContextTrait.php new file mode 100644 index 00000000..95b0bec1 --- /dev/null +++ b/tests/Behat/Bootstrap/IssueRelationContextTrait.php @@ -0,0 +1,31 @@ +getNativeCurlClient()->getApi('issue_relation'); + + $this->registerClientResponse( + $api->create($issueId, $data), + $api->getLastResponse() + ); + } +} diff --git a/tests/Behat/Bootstrap/IssueStatusContextTrait.php b/tests/Behat/Bootstrap/IssueStatusContextTrait.php new file mode 100644 index 00000000..f8c8d16f --- /dev/null +++ b/tests/Behat/Bootstrap/IssueStatusContextTrait.php @@ -0,0 +1,25 @@ +redmine->excecuteDatabaseQuery( + 'INSERT INTO issue_statuses(name, is_closed, position) VALUES(:name, :is_closed, :position);', + [], + [ + ':name' => $issueStatusName, + ':is_closed' => 0, + ':position' => 1, + ], + ); + } +} diff --git a/tests/Behat/Bootstrap/TrackerContextTrait.php b/tests/Behat/Bootstrap/TrackerContextTrait.php new file mode 100644 index 00000000..d44f2e46 --- /dev/null +++ b/tests/Behat/Bootstrap/TrackerContextTrait.php @@ -0,0 +1,27 @@ +redmine->excecuteDatabaseQuery( + 'INSERT INTO trackers(name, position, is_in_roadmap, fields_bits, default_status_id) VALUES(:name, :position, :is_in_roadmap, :fields_bits, :default_status_id);', + [], + [ + ':name' => $trackerName, + ':position' => 1, + ':is_in_roadmap' => 1, + ':fields_bits' => 0, + ':default_status_id' => $statusId, + ], + ); + } +} diff --git a/tests/Behat/Bootstrap/UserContextTrait.php b/tests/Behat/Bootstrap/UserContextTrait.php index 42b71ba8..cbd05631 100644 --- a/tests/Behat/Bootstrap/UserContextTrait.php +++ b/tests/Behat/Bootstrap/UserContextTrait.php @@ -4,10 +4,31 @@ namespace Redmine\Tests\Behat\Bootstrap; +use Behat\Gherkin\Node\TableNode; use Redmine\Api\User; trait UserContextTrait { + /** + * @When I create a user with the following data + */ + public function iCreateAUserWithTheFollowingData(TableNode $table) + { + $data = []; + + foreach ($table as $row) { + $data[$row['property']] = $row['value']; + } + + /** @var User */ + $api = $this->getNativeCurlClient()->getApi('user'); + + $this->registerClientResponse( + $api->create($data), + $api->getLastResponse() + ); + } + /** * @When I show the user with id :userId */ diff --git a/tests/Behat/features/issue.feature b/tests/Behat/features/issue.feature new file mode 100644 index 00000000..58238d5a --- /dev/null +++ b/tests/Behat/features/issue.feature @@ -0,0 +1,123 @@ +Feature: Interacting with the REST API for issues + In order to interact with REST API for issues + As a user + I want to make sure the Redmine server replies with the correct response + + @issue + Scenario: Creating an issue with miminal data + Given I have a "NativeCurlClient" client + And I have an issue status with the name "New" + And I have an issue priority with the name "Normal" + And I have a tracker with the name "Defect" and default status id "1" + And I create a project with name "Test Project" and identifier "test-project" + When I create an issue with the following data + | property | value | + | subject | issue subject | + | project | Test Project | + | tracker | Defect | + | priority | Normal | + | status | New | + Then the response has the status code "201" + And the response has the content type "application/xml" + And the returned data is an instance of "SimpleXMLElement" + And the returned data has only the following properties + """ + id + project + tracker + status + priority + author + subject + description + start_date + due_date + done_ratio + is_private + estimated_hours + total_estimated_hours + created_on + updated_on + closed_on + """ + And the returned data has proterties with the following data + | property | value | + | id | 1 | + | subject | issue subject | + | description | [] | + | due_date | [] | + | done_ratio | 0 | + | is_private | false | + | estimated_hours | [] | + | total_estimated_hours | [] | + And the returned data "project" property is an array + And the returned data "project" property contains "1" items + And the returned data "project.@attributes" property is an array + And the returned data "project.@attributes" property has only the following properties + """ + id + name + """ + And the returned data "project.@attributes" property contains the following data + | property | value | + | id | 1 | + | name | Test Project | + And the returned data "tracker" property is an array + And the returned data "tracker" property contains "1" items + And the returned data "tracker.@attributes" property is an array + And the returned data "tracker.@attributes" property has only the following properties + """ + id + name + """ + And the returned data "tracker.@attributes" property contains the following data + | property | value | + | id | 1 | + | name | Defect | + And the returned data "status" property is an array + And the returned data "status" property contains "1" items + And the returned data "status.@attributes" property is an array + And the returned data "status.@attributes" property has only the following properties + """ + id + name + is_closed + """ + And the returned data "status.@attributes" property contains the following data + | property | value | + | id | 1 | + | name | New | + | is_closed | false | + And the returned data "priority" property is an array + And the returned data "priority" property contains "1" items + And the returned data "priority.@attributes" property is an array + And the returned data "priority.@attributes" property has only the following properties + """ + id + name + """ + And the returned data "priority.@attributes" property contains the following data + | property | value | + | id | 1 | + | name | Normal | + And the returned data "author" property is an array + And the returned data "author" property contains "1" items + And the returned data "author.@attributes" property is an array + And the returned data "author.@attributes" property has only the following properties + """ + id + name + """ + And the returned data "author.@attributes" property contains the following data + | property | value | + | id | 1 | + | name | Redmine Admin | + + @issue @error + Scenario: Showing a not existing issue + Given I have a "NativeCurlClient" client + When I show the issue with id "10" + Then the response has the status code "404" + And the response has the content type "application/json" + And the response has the content "" + And the returned data is false diff --git a/tests/Behat/features/issue_relation.feature b/tests/Behat/features/issue_relation.feature new file mode 100644 index 00000000..7b5db667 --- /dev/null +++ b/tests/Behat/features/issue_relation.feature @@ -0,0 +1,51 @@ +Feature: Interacting with the REST API for issue relations + In order to interact with REST API for issue relations + As a user + I want to make sure the Redmine server replies with the correct response + + @issue_relation + Scenario: Creating an issue relation with miminal data + Given I have a "NativeCurlClient" client + And I have an issue status with the name "New" + And I have an issue priority with the name "Normal" + And I have a tracker with the name "Defect" and default status id "1" + And I create a project with name "Test Project" and identifier "test-project" + And I create an issue with the following data + | property | value | + | subject | first issue | + | project | Test Project | + | tracker | Defect | + | priority | Normal | + | status | New | + And I create an issue with the following data + | property | value | + | subject | second issue | + | project | Test Project | + | tracker | Defect | + | priority | Normal | + | status | New | + When I create an issue relation for issue id "1" with the following data + | property | value | + | issue_to_id | 2 | + Then the response has the status code "201" + And the response has the content type "application/json" + And the returned data has only the following properties + """ + relation + """ + And the returned data "relation" property is an array + And the returned data "relation" property has only the following properties + """ + id + issue_id + issue_to_id + relation_type + delay + """ + And the returned data "relation" property contains the following data + | property | value | + | id | 1 | + | issue_id | 1 | + | issue_to_id | 2 | + | relation_type | relates | + | delay | null | diff --git a/tests/Behat/features/user.feature b/tests/Behat/features/user.feature index a46619fc..fce3ee8b 100644 --- a/tests/Behat/features/user.feature +++ b/tests/Behat/features/user.feature @@ -3,6 +3,47 @@ Feature: Interacting with the REST API for users As a user I want to make sure the Redmine server replies with the correct response + @user + Scenario: Creating a user + Given I have a "NativeCurlClient" client + When I create a user with the following data + | property | value | + | login | username | + | firstname | first | + | lastname | last | + | mail | mail@example.com | + Then the response has the status code "201" + And the response has the content type "application/xml" + And the returned data is an instance of "SimpleXMLElement" + And the returned data has only the following properties + """ + id + login + admin + firstname + lastname + mail + created_on + updated_on + last_login_on + passwd_changed_on + twofa_scheme + api_key + status + """ + And the returned data has proterties with the following data + | property | value | + | id | 4 | + | login | username | + | admin | false | + | firstname | first | + | lastname | last | + | mail | mail@example.com | + | last_login_on | [] | + | passwd_changed_on | [] | + | twofa_scheme | [] | + | status | 1 | + @user Scenario: Showing a user Given I have a "NativeCurlClient" client diff --git a/tests/Integration/GroupXmlTest.php b/tests/Integration/GroupXmlTest.php index 6b64ce41..6ac92200 100644 --- a/tests/Integration/GroupXmlTest.php +++ b/tests/Integration/GroupXmlTest.php @@ -3,50 +3,10 @@ namespace Redmine\Tests\Integration; use PHPUnit\Framework\TestCase; -use Redmine\Exception\MissingParameterException; use Redmine\Tests\Fixtures\MockClient; class GroupXmlTest extends TestCase { - public function testCreateBlank() - { - /** @var \Redmine\Api\Group */ - $api = MockClient::create()->getApi('group'); - $this->assertInstanceOf('Redmine\Api\Group', $api); - - $this->expectException(MissingParameterException::class); - $this->expectExceptionMessage('Theses parameters are mandatory: `name`'); - - $api->create(); - } - - public function testCreateComplex() - { - /** @var \Redmine\Api\Group */ - $api = MockClient::create()->getApi('group'); - $res = $api->create([ - 'name' => 'Developers', - 'user_ids' => [3, 5], - ]); - $response = json_decode($res, true); - - $this->assertEquals('POST', $response['method']); - $this->assertEquals('/groups.xml', $response['path']); - $this->assertXmlStringEqualsXmlString( - <<< XML - - - Developers - - 3 - 5 - - - XML, - $response['data'] - ); - } - public function testUpdateComplex() { /** @var \Redmine\Api\Group */ diff --git a/tests/Integration/IssueCategoryXmlTest.php b/tests/Integration/IssueCategoryXmlTest.php index 80493131..355e401d 100644 --- a/tests/Integration/IssueCategoryXmlTest.php +++ b/tests/Integration/IssueCategoryXmlTest.php @@ -20,28 +20,6 @@ public function testCreateBlank() $api->create('aProject'); } - public function testCreateComplex() - { - /** @var \Redmine\Api\IssueCategory */ - $api = MockClient::create()->getApi('issue_category'); - $res = $api->create('otherProject', [ - 'name' => 'test category', - ]); - $response = json_decode($res, true); - - $this->assertEquals('POST', $response['method']); - $this->assertEquals('/projects/otherProject/issue_categories.xml', $response['path']); - $this->assertXmlStringEqualsXmlString( - <<< XML - - - test category - - XML, - $response['data'] - ); - } - public function testUpdate() { /** @var \Redmine\Api\IssueCategory */ diff --git a/tests/Integration/IssueXmlTest.php b/tests/Integration/IssueXmlTest.php index e1de9248..ebe70756 100644 --- a/tests/Integration/IssueXmlTest.php +++ b/tests/Integration/IssueXmlTest.php @@ -7,171 +7,6 @@ class IssueXmlTest extends TestCase { - public function testCreateBlank() - { - /** @var \Redmine\Api\Issue */ - $api = MockClient::create()->getApi('issue'); - $this->assertInstanceOf('Redmine\Api\Issue', $api); - - $res = $api->create(); - $response = json_decode($res, true); - - $this->assertEquals('POST', $response['method']); - $this->assertEquals('/issues.xml', $response['path']); - $this->assertXmlStringEqualsXmlString( - <<< XML - - - XML, - $response['data'] - ); - } - - public function testCreateComplexWithUpload() - { - /** @var \Redmine\Api\Issue */ - $api = MockClient::create()->getApi('issue'); - $res = $api->create([ - 'project_id' => 'myproject', - 'subject' => 'A test issue', - 'description' => 'Here goes the issue description', - 'uploads' => [ - [ - 'token' => 'asdfasdfasdfasdf', - 'filename' => 'MyFile.pdf', - 'description' => 'MyFile is better then YourFile...', - 'content_type' => 'application/pdf', - ], - ], - ]); - $response = json_decode($res, true); - - $this->assertEquals('POST', $response['method']); - $this->assertEquals('/issues.xml', $response['path']); - $this->assertXmlStringEqualsXmlString( - <<< XML - - - A test issue - Here goes the issue description - myproject - - - asdfasdfasdfasdf - MyFile.pdf - MyFile is better then YourFile... - application/pdf - - - - XML, - $response['data'] - ); - } - - public function testCreateComplex() - { - /** @var \Redmine\Api\Issue */ - $api = MockClient::create()->getApi('issue'); - $res = $api->create([ - 'project_id' => 'test', - 'subject' => 'test api (xml) 3', - 'description' => 'test api', - 'assigned_to_id' => 1, - 'custom_fields' => [ - [ - 'id' => 2, - 'name' => 'Issuer', - 'value' => 'asdf', - ], - [ - 'id' => 5, - 'name' => 'Phone', - 'value' => '9939494', - ], - [ - 'id' => '8', - 'name' => 'Email', - 'value' => 'asdf@asdf.com', - ], - ], - 'watcher_user_ids' => [], - ]); - $response = json_decode($res, true); - - $this->assertEquals('POST', $response['method']); - $this->assertEquals('/issues.xml', $response['path']); - $this->assertXmlStringEqualsXmlString( - <<< XML - - - test api (xml) 3 - test api - test - 1 - - asdf - 9939494 - asdf@asdf.com - - - XML, - $response['data'] - ); - } - - public function testCreateComplexWithLineBreakInDescription() - { - /** @var \Redmine\Api\Issue */ - $api = MockClient::create()->getApi('issue'); - $res = $api->create([ - 'project_id' => 'test', - 'subject' => 'test api (xml) 3', - 'description' => "line1\nline2", - 'assigned_to_id' => 1, - 'custom_fields' => [ - [ - 'id' => 2, - 'name' => 'Issuer', - 'value' => 'asdf', - ], - [ - 'id' => 5, - 'name' => 'Phone', - 'value' => '9939494', - ], - [ - 'id' => '8', - 'name' => 'Email', - 'value' => 'asdf@asdf.com', - ], - ], - 'watcher_user_ids' => [], - ]); - $response = json_decode($res, true); - - $this->assertEquals('POST', $response['method']); - $this->assertEquals('/issues.xml', $response['path']); - $this->assertXmlStringEqualsXmlString( - <<< XML - - - test api (xml) 3 - line1 - line2 - test - 1 - - asdf - 9939494 - asdf@asdf.com - - - XML, - $response['data'] - ); - } - public function testUpdateIssue() { /** @var \Redmine\Api\Issue */ diff --git a/tests/Integration/MembershipXmlTest.php b/tests/Integration/MembershipXmlTest.php index e4fa1488..9eb35907 100644 --- a/tests/Integration/MembershipXmlTest.php +++ b/tests/Integration/MembershipXmlTest.php @@ -3,50 +3,10 @@ namespace Redmine\Tests\Integration; use PHPUnit\Framework\TestCase; -use Redmine\Exception\MissingParameterException; use Redmine\Tests\Fixtures\MockClient; class MembershipXmlTest extends TestCase { - public function testCreateBlank() - { - /** @var \Redmine\Api\Membership */ - $api = MockClient::create()->getApi('membership'); - $this->assertInstanceOf('Redmine\Api\Membership', $api); - - $this->expectException(MissingParameterException::class); - $this->expectExceptionMessage('Theses parameters are mandatory: `user_id`, `role_ids`'); - - $api->create('aProject'); - } - - public function testCreateComplex() - { - /** @var \Redmine\Api\Membership */ - $api = MockClient::create()->getApi('membership'); - $res = $api->create('otherProject', [ - 'user_id' => 1, - 'role_ids' => [1, 2], - ]); - $response = json_decode($res, true); - - $this->assertEquals('POST', $response['method']); - $this->assertEquals('/projects/otherProject/memberships.xml', $response['path']); - $this->assertXmlStringEqualsXmlString( - <<< XML - - - 1 - - 1 - 2 - - - XML, - $response['data'] - ); - } - public function testUpdate() { /** @var \Redmine\Api\Membership */ diff --git a/tests/Integration/ProjectXmlTest.php b/tests/Integration/ProjectXmlTest.php index c0fc42b0..06ba06fe 100644 --- a/tests/Integration/ProjectXmlTest.php +++ b/tests/Integration/ProjectXmlTest.php @@ -8,91 +8,6 @@ class ProjectXmlTest extends TestCase { - public function testCreateBlank() - { - /** @var \Redmine\Api\Project */ - $api = MockClient::create()->getApi('project'); - $this->assertInstanceOf('Redmine\Api\Project', $api); - - $this->expectException(MissingParameterException::class); - $this->expectExceptionMessage('Theses parameters are mandatory: `name`, `identifier`'); - - $api->create(); - } - - public function testCreateComplex() - { - /** @var \Redmine\Api\Project */ - $api = MockClient::create()->getApi('project'); - $res = $api->create([ - 'name' => 'some name', - 'identifier' => 'the_identifier', - 'custom_fields' => [ - [ - 'id' => 123, - 'name' => 'cf_name', - 'field_format' => 'string', - 'value' => [1, 2, 3], - ], - ], - ]); - $response = json_decode($res, true); - - $this->assertEquals('POST', $response['method']); - $this->assertEquals('/projects.xml', $response['path']); - $this->assertXmlStringEqualsXmlString( - <<< XML - - - some name - the_identifier - - - - 1 - 2 - 3 - - - - - XML, - $response['data'] - ); - } - - public function testCreateComplexWithTrackerIds() - { - /** @var \Redmine\Api\Project */ - $api = MockClient::create()->getApi('project'); - $res = $api->create([ - 'name' => 'some name', - 'identifier' => 'the_identifier', - 'tracker_ids' => [ - 1, 2, 3, - ], - ]); - $response = json_decode($res, true); - - $this->assertEquals('POST', $response['method']); - $this->assertEquals('/projects.xml', $response['path']); - $this->assertXmlStringEqualsXmlString( - <<< XML - - - some name - the_identifier - - 1 - 2 - 3 - - - XML, - $response['data'] - ); - } - public function testUpdate() { /** @var \Redmine\Api\Project */ diff --git a/tests/Integration/UrlTest.php b/tests/Integration/UrlTest.php index 34affe41..4c226947 100644 --- a/tests/Integration/UrlTest.php +++ b/tests/Integration/UrlTest.php @@ -15,12 +15,6 @@ public function testAttachment() $this->assertEquals('/attachments/1.json', $res['path']); $this->assertEquals('GET', $res['method']); - - $res = $api->upload('asdf'); - $res = json_decode($res, true); - - $this->assertEquals('/uploads.json', $res['path']); - $this->assertEquals('POST', $res['method']); } public function testCustomFields() @@ -37,13 +31,6 @@ public function testGroup() { /** @var \Redmine\Api\Group */ $api = MockClient::create()->getApi('group'); - $res = $api->create([ - 'name' => 'asdf', - ]); - $res = json_decode($res, true); - - $this->assertEquals('/groups.xml', $res['path']); - $this->assertEquals('POST', $res['method']); $res = $api->all(); @@ -61,12 +48,6 @@ public function testGroup() $this->assertEquals('/groups/1.xml', $res['path']); $this->assertEquals('DELETE', $res['method']); - $res = $api->addUser(1, 1); - $res = json_decode($res, true); - - $this->assertEquals('/groups/1/users.xml', $res['path']); - $this->assertEquals('POST', $res['method']); - $res = $api->removeUser(1, 1); $res = json_decode($res, true); @@ -78,13 +59,6 @@ public function testIssue() { /** @var \Redmine\Api\Issue */ $api = MockClient::create()->getApi('issue'); - $res = $api->create([ - 'name' => 'asdf', - ]); - $res = json_decode($res, true); - - $this->assertEquals('/issues.xml', $res['path']); - $this->assertEquals('POST', $res['method']); $res = $api->update(1, [ 'name' => 'asdf', @@ -135,13 +109,6 @@ public function testIssueCategory() { /** @var \Redmine\Api\IssueCategory */ $api = MockClient::create()->getApi('issue_category'); - $res = $api->create('testProject', [ - 'name' => 'asdf', - ]); - $res = json_decode($res, true); - - $this->assertEquals('/projects/testProject/issue_categories.xml', $res['path']); - $this->assertEquals('POST', $res['method']); $res = $api->update(1, [ 'name' => 'asdf', @@ -217,14 +184,6 @@ public function testMembership() { /** @var \Redmine\Api\Membership */ $api = MockClient::create()->getApi('membership'); - $res = $api->create('testProject', [ - 'user_id' => 1, - 'role_ids' => [1], - ]); - $res = json_decode($res, true); - - $this->assertEquals('/projects/testProject/memberships.xml', $res['path']); - $this->assertEquals('POST', $res['method']); $res = $api->update(1, [ 'user_id' => 1, @@ -266,14 +225,6 @@ public function testProject() { /** @var \Redmine\Api\Project */ $api = MockClient::create()->getApi('project'); - $res = $api->create([ - 'name' => 'asdf', - 'identifier' => 'asdf', - ]); - $res = json_decode($res, true); - - $this->assertEquals('/projects.xml', $res['path']); - $this->assertEquals('POST', $res['method']); $res = $api->update(1, [ 'name' => 'asdf', @@ -329,14 +280,6 @@ public function testTimeEntry() { /** @var \Redmine\Api\TimeEntry */ $api = MockClient::create()->getApi('time_entry'); - $res = $api->create([ - 'issue_id' => 1, - 'hours' => 12, - ]); - $res = json_decode($res, true); - - $this->assertEquals('/time_entries.xml', $res['path']); - $this->assertEquals('POST', $res['method']); $res = $api->update(1, []); $res = json_decode($res, true); @@ -399,16 +342,6 @@ public function testUser() { /** @var \Redmine\Api\User */ $api = MockClient::create()->getApi('user'); - $res = $api->create([ - 'login' => 'asdf', - 'lastname' => 'asdf', - 'firstname' => 'asdf', - 'mail' => 'asdf', - ]); - $res = json_decode($res, true); - - $this->assertEquals('/users.xml', $res['path']); - $this->assertEquals('POST', $res['method']); $res = $api->update(1, []); $res = json_decode($res, true); @@ -452,13 +385,6 @@ public function testVersion() { /** @var \Redmine\Api\Version */ $api = MockClient::create()->getApi('version'); - $res = $api->create('testProject', [ - 'name' => 'asdf', - ]); - $res = json_decode($res, true); - - $this->assertEquals('/projects/testProject/versions.xml', $res['path']); - $this->assertEquals('POST', $res['method']); $res = $api->update(1, []); $res = json_decode($res, true); diff --git a/tests/Integration/UserXmlTest.php b/tests/Integration/UserXmlTest.php index 38de230b..9c817b0f 100644 --- a/tests/Integration/UserXmlTest.php +++ b/tests/Integration/UserXmlTest.php @@ -20,34 +20,6 @@ public function testCreateBlank() $api->create(); } - public function testCreateComplex() - { - /** @var \Redmine\Api\User */ - $api = MockClient::create()->getApi('user'); - $res = $api->create([ - 'login' => 'test', - 'firstname' => 'test', - 'lastname' => 'test', - 'mail' => 'test@example.com', - ]); - $response = json_decode($res, true); - - $this->assertEquals('POST', $response['method']); - $this->assertEquals('/users.xml', $response['path']); - $this->assertXmlStringEqualsXmlString( - <<< XML - - - test - test - test - test@example.com - - XML, - $response['data'] - ); - } - public function testUpdate() { /** @var \Redmine\Api\User */ diff --git a/tests/Unit/Api/AbstractApiTest.php b/tests/Unit/Api/AbstractApiTest.php index 07302875..39a473c7 100644 --- a/tests/Unit/Api/AbstractApiTest.php +++ b/tests/Unit/Api/AbstractApiTest.php @@ -58,7 +58,12 @@ public function testGetTriggersDeprecationWarning() { $client = $this->createMock(HttpClient::class); - $api = new class ($client) extends AbstractApi {}; + $api = new class ($client) extends AbstractApi { + public function runGet($path) + { + return $this->get($path); + } + }; // PHPUnit 10 compatible way to test trigger_error(). set_error_handler( @@ -74,10 +79,7 @@ function ($errno, $errstr): bool { E_USER_DEPRECATED ); - $method = new ReflectionMethod($api, 'get'); - $method->setAccessible(true); - - $method->invoke($api, '/path.json'); + $api->runGet('/path.json'); } /** @@ -92,6 +94,37 @@ public function testGetLastResponseWithHttpClientWorks() $this->assertInstanceOf(Response::class, $api->getLastResponse()); } + /** + * @covers ::post + */ + public function testPostTriggersDeprecationWarning() + { + $client = $this->createMock(HttpClient::class); + + $api = new class ($client) extends AbstractApi { + public function runPost($path, $data) + { + return $this->post($path, $data); + } + }; + + // PHPUnit 10 compatible way to test trigger_error(). + set_error_handler( + function ($errno, $errstr): bool { + $this->assertSame( + '`Redmine\Api\AbstractApi::post()` is deprecated since v2.6.0, use `\Redmine\Http\HttpClient::request()` instead.', + $errstr + ); + + restore_error_handler(); + return true; + }, + E_USER_DEPRECATED + ); + + $api->runPost('/path.json', 'data'); + } + /** * @test * @dataProvider getIsNotNullReturnsCorrectBooleanData diff --git a/tests/Unit/Api/Attachment/UploadTest.php b/tests/Unit/Api/Attachment/UploadTest.php new file mode 100644 index 00000000..f7eabdaf --- /dev/null +++ b/tests/Unit/Api/Attachment/UploadTest.php @@ -0,0 +1,84 @@ +assertSame($expectedReturn, $api->upload($attachment, $params)); + } + + public static function getUploadData(): array + { + return [ + 'test attachment without params' => [ + 'attachment-content', + [], + 'attachment-content', + '/uploads.json', + 201, + '{}', + '{}', + ], + 'test attachment returns empty string' => [ + 'attachment-content', + [], + 'attachment-content', + '/uploads.json', + 201, + '', + '', + ], + 'test attachment with params' => [ + 'attachment-content', + [ + 'filename' => 'testfile.txt' + ], + 'attachment-content', + '/uploads.json?filename=testfile.txt', + 201, + '{"upload":{}}', + '{"upload":{}}', + ], + 'test attachment with filepath' => [ + '/path/to/testfile_01.txt', + [ + 'filename' => 'testfile.txt' + ], + '/path/to/testfile_01.txt', + '/uploads.json?filename=testfile.txt', + 201, + '{"upload":{}}', + '{"upload":{}}', + ], + ]; + } +} diff --git a/tests/Unit/Api/AttachmentTest.php b/tests/Unit/Api/AttachmentTest.php index d37f2c9e..67171210 100644 --- a/tests/Unit/Api/AttachmentTest.php +++ b/tests/Unit/Api/AttachmentTest.php @@ -57,37 +57,4 @@ public static function responseCodeProvider(): array [500, true], ]; } - - /** - * Test upload(). - * - * @covers ::post - * @covers ::upload - * @test - */ - public function testUploadReturnsClientPostResponse() - { - // Test values - $postRequestData = 'API Response'; - $response = 'API Response'; - - // Create the used mock objects - $client = $this->createMock(Client::class); - $client->expects($this->once()) - ->method('requestPost') - ->with( - $this->stringStartsWith('/uploads.json'), - $this->equalTo($postRequestData) - ) - ->willReturn(true); - $client->expects($this->exactly(1)) - ->method('getLastResponseBody') - ->willReturn($response); - - // Create the object under test - $api = new Attachment($client); - - // Perform the tests - $this->assertSame($response, $api->upload($postRequestData)); - } } diff --git a/tests/Unit/Api/Group/AddUserTest.php b/tests/Unit/Api/Group/AddUserTest.php new file mode 100644 index 00000000..175c6a05 --- /dev/null +++ b/tests/Unit/Api/Group/AddUserTest.php @@ -0,0 +1,83 @@ +addUser($groupId, $userId); + + $this->assertInstanceOf(SimpleXMLElement::class, $return); + $this->assertXmlStringEqualsXmlString($response, $return->asXml()); + } + + public static function getAddUserData(): array + { + return [ + 'test with integers' => [ + 25, + 5, + '/groups/25/users.xml', + << + 5 + XML, + 201, + '', + ], + ]; + } + + public function testAddUserReturnsEmptyString() + { + $client = AssertingHttpClient::create( + $this, + [ + 'POST', + '/groups/1/users.xml', + 'application/xml', + '2', + 500, + '', + '' + ] + ); + + // Create the object under test + $api = new Group($client); + + // Perform the tests + $return = $api->addUser(1, 2); + + $this->assertSame('', $return); + } +} diff --git a/tests/Unit/Api/Group/CreateTest.php b/tests/Unit/Api/Group/CreateTest.php index 6cc1ef3c..b99238f4 100644 --- a/tests/Unit/Api/Group/CreateTest.php +++ b/tests/Unit/Api/Group/CreateTest.php @@ -16,18 +16,21 @@ */ class CreateTest extends TestCase { - public function testCreateWithNameCreatesGroup() + /** + * @dataProvider getCreateData + */ + public function testCreateReturnsCorrectResponse($parameters, $expectedPath, $expectedBody, $responseCode, $response) { $client = AssertingHttpClient::create( $this, [ 'POST', - '/groups.xml', + $expectedPath, 'application/xml', - 'Group Name', - 200, + $expectedBody, + $responseCode, 'application/xml', - '' + $response ] ); @@ -35,44 +38,75 @@ public function testCreateWithNameCreatesGroup() $api = new Group($client); // Perform the tests - $xmlElement = $api->create(['name' => 'Group Name']); + $return = $api->create($parameters); - $this->assertInstanceOf(SimpleXMLElement::class, $xmlElement); - $this->assertXmlStringEqualsXmlString( - '', - $xmlElement->asXml(), - ); + $this->assertInstanceOf(SimpleXMLElement::class, $return); + $this->assertXmlStringEqualsXmlString($response, $return->asXml()); } - public function testCreateWithNameAndUserIdsCreatesGroup() + public static function getCreateData(): array { - $client = AssertingHttpClient::create( - $this, - [ - 'POST', + return [ + 'test with minimal parameters' => [ + [ + 'name' => 'Group Name', + ], '/groups.xml', - 'application/xml', - 'Group Name123', - 200, - 'application/xml', - '' - ] - ); - - // Create the object under test - $api = new Group($client); - - // Perform the tests - $xmlElement = $api->create(['name' => 'Group Name', 'user_ids' => [1, 2, 3]]); - - $this->assertInstanceOf(SimpleXMLElement::class, $xmlElement); - $this->assertXmlStringEqualsXmlString( - '', - $xmlElement->asXml(), - ); + << + + Group Name + + XML, + 201, + '', + ], + 'test with user ids' => [ + [ + 'name' => 'Group Name', + 'user_ids' => [1, 2, 3], + ], + '/groups.xml', + << + + Group Name + + 1 + 2 + 3 + + + XML, + 201, + '', + ], + 'test with custom fields' => [ + [ + 'name' => 'Group Name', + 'custom_fields' => [ + ['id' => 1, 'value' => 5], + ], + ], + '/groups.xml', + << + + Group Name + + + 5 + + + + XML, + 201, + '', + ], + ]; } - public function testCreateWithNameAndCustomFieldsCreatesGroup() + public function testCreateReturnsEmptyString() { $client = AssertingHttpClient::create( $this, @@ -80,10 +114,10 @@ public function testCreateWithNameAndCustomFieldsCreatesGroup() 'POST', '/groups.xml', 'application/xml', - 'Group Name5', - 200, - 'application/xml', - '' + 'Group Name', + 500, + '', + '' ] ); @@ -91,18 +125,9 @@ public function testCreateWithNameAndCustomFieldsCreatesGroup() $api = new Group($client); // Perform the tests - $xmlElement = $api->create([ - 'name' => 'Group Name', - 'custom_fields' => [ - ['id' => 1, 'value' => 5], - ], - ]); + $return = $api->create(['name' => 'Group Name']); - $this->assertInstanceOf(SimpleXMLElement::class, $xmlElement); - $this->assertXmlStringEqualsXmlString( - '', - $xmlElement->asXml(), - ); + $this->assertSame('', $return); } public function testCreateThrowsExceptionIfNameIsMissing() diff --git a/tests/Unit/Api/GroupTest.php b/tests/Unit/Api/GroupTest.php index 84bf87c9..d4acdde6 100644 --- a/tests/Unit/Api/GroupTest.php +++ b/tests/Unit/Api/GroupTest.php @@ -272,44 +272,6 @@ public function testRemoveCallsDelete() $this->assertSame($expectedReturn, $api->remove(5)); } - /** - * Test removeUser(). - * - * @covers ::addUser - * @covers ::post - * @test - */ - public function testAddUserCallsPost() - { - // Test values - $response = 'API Response'; - - // Create the used mock objects - $client = $this->createMock(Client::class); - $client->expects($this->once()) - ->method('requestPost') - ->with( - $this->logicalAnd( - $this->stringStartsWith('/groups/5/users'), - $this->logicalXor( - $this->stringEndsWith('.json'), - $this->stringEndsWith('.xml') - ) - ), - $this->stringContains('10') - ) - ->willReturn(true); - $client->expects($this->exactly(1)) - ->method('getLastResponseBody') - ->willReturn($response); - - // Create the object under test - $api = new Group($client); - - // Perform the tests - $this->assertSame($response, $api->addUser(5, 10)); - } - /** * Test removeUser(). * diff --git a/tests/Unit/Api/Issue/AddWatcherTest.php b/tests/Unit/Api/Issue/AddWatcherTest.php new file mode 100644 index 00000000..9e1d6492 --- /dev/null +++ b/tests/Unit/Api/Issue/AddWatcherTest.php @@ -0,0 +1,83 @@ +addWatcher($issueId, $watcherUserId); + + $this->assertInstanceOf(SimpleXMLElement::class, $return); + $this->assertXmlStringEqualsXmlString($response, $return->asXml()); + } + + public static function getAddWatcherData(): array + { + return [ + 'test with integers' => [ + 25, + 5, + '/issues/25/watchers.xml', + << + 5 + XML, + 201, + '', + ], + ]; + } + + public function testAddWatcherReturnsEmptyString() + { + $client = AssertingHttpClient::create( + $this, + [ + 'POST', + '/issues/1/watchers.xml', + 'application/xml', + '2', + 500, + '', + '' + ] + ); + + // Create the object under test + $api = new Issue($client); + + // Perform the tests + $return = $api->addWatcher(1, 2); + + $this->assertSame('', $return); + } +} diff --git a/tests/Unit/Api/Issue/CreateTest.php b/tests/Unit/Api/Issue/CreateTest.php new file mode 100644 index 00000000..1334a8ea --- /dev/null +++ b/tests/Unit/Api/Issue/CreateTest.php @@ -0,0 +1,564 @@ +create($parameters); + + $this->assertInstanceOf(SimpleXMLElement::class, $return); + $this->assertXmlStringEqualsXmlString($response, $return->asXml()); + } + + public static function getCreateData(): array + { + return [ + 'test without parameters' => [ + [], + '/issues.xml', + << + + XML, + 201, + '', + ], + 'test with minimal parameters' => [ + [ + 'subject' => 'Issue subject', + 'project_id' => 1, + 'tracker_id' => 2, + 'priority_id' => 3, + 'status_id' => 4, + ], + '/issues.xml', + << + + Issue subject + 1 + 3 + 4 + 2 + + XML, + 201, + '', + ], + 'test with line break in description' => [ + [ + 'subject' => 'Issue subject', + 'description' => "line1\nline2", + 'project_id' => 1, + 'tracker_id' => 2, + 'priority_id' => 3, + 'status_id' => 4, + ], + '/issues.xml', + << + + Issue subject + line1 + line2 + 1 + 3 + 4 + 2 + + XML, + 201, + '', + ], + 'test with with some xml entities' => [ + [ + 'subject' => 'Issue subject with some xml entities: & < > " \' ', + 'project_id' => 1, + 'tracker_id' => 2, + 'priority_id' => 3, + 'status_id' => 4, + 'description' => 'Description with some xml entities: & < > " \' ', + ], + '/issues.xml', + << + + Issue subject with some xml entities: & < > " ' + Description with some xml entities: & < > " ' + 1 + 3 + 4 + 2 + + XML, + 201, + '', + ], + 'test with all possible parameters' => [ + [ + 'project_id' => 1, + 'tracker_id' => 2, + 'status_id' => 3, + 'priority_id' => 4, + 'subject' => 'Issue subject', + 'description' => 'Issue description', + 'category_id' => 5, + 'fixed_version_id' => 6, + 'assigned_to_id' => 7, + 'parent_issue_id' => 8, + 'custom_fields' => [ + [ + 'id' => 9, + 'name' => 'Custom field 9', + 'value' => 'value of cf9', + ], + [ + 'id' => 123, + 'name' => 'cf_name', + 'field_format' => 'string', + 'value' => [1, 2, 3], + ], + [ + 'id' => 321, + 'value' => 'https://example.com/?one=first&two=second', + ], + ], + 'watcher_user_ids' => [10, 11], + 'is_private' => false, + 'estimated_hours' => 2.5, + 'author_id' => 12, + 'due_date' => '2024-12-31', + 'start_date' => '2024-01-01', + 'uploads' => [ + [ + 'token' => '1.first-token', + 'filename' => 'SomeRandomFile.txt', + 'description' => 'Simple description', + 'content_type' => 'text/plain', + ], + [ + 'token' => '2.second-token', + 'filename' => 'An-Other-File.css', + 'content_type' => 'text/css', + ], + ], + ], + '/issues.xml', + << + + Issue subject + Issue description + 1 + 5 + 4 + 3 + 2 + 7 + 12 + 2024-12-31 + 2024-01-01 + + 10 + 11 + + 6 + 8 + + + value of cf9 + + + + 1 + 2 + 3 + + + + https://example.com/?one=first&two=second + + + 2.5 + + + 1.first-token + SomeRandomFile.txt + Simple description + text/plain + + + 2.second-token + An-Other-File.css + text/css + + + + XML, + 201, + '', + ], + ]; + } + + public function testCreateReturnsEmptyString() + { + $client = AssertingHttpClient::create( + $this, + [ + 'POST', + '/issues.xml', + 'application/xml', + '', + 500, + '', + '' + ] + ); + + // Create the object under test + $api = new Issue($client); + + // Perform the tests + $return = $api->create([]); + + $this->assertSame('', $return); + } + + /** + * @covers \Redmine\Api\Issue::cleanParams + * @covers \Redmine\Api\Issue::getIssueStatusApi + */ + public function testCreateWithHttpClientRetrievesIssueStatusId() + { + $client = AssertingHttpClient::create( + $this, + [ + 'GET', + '/issue_statuses.json', + 'application/json', + '', + 200, + 'application/json', + '{"issue_statuses":[{"name":"Status Name","id":123}]}' + ], + [ + 'POST', + '/issues.xml', + 'application/xml', + '123', + 200, + 'application/xml', + '' + ] + ); + + // Create the object under test + $api = new Issue($client); + + // Perform the tests + $xmlElement = $api->create(['status' => 'Status Name']); + + $this->assertInstanceOf(SimpleXMLElement::class, $xmlElement); + $this->assertXmlStringEqualsXmlString( + '', + $xmlElement->asXml(), + ); + } + + /** + * @covers \Redmine\Api\Issue::cleanParams + * @covers \Redmine\Api\Issue::getProjectApi + */ + public function testCreateWithHttpClientRetrievesProjectId() + { + $client = AssertingHttpClient::create( + $this, + [ + 'GET', + '/projects.json', + 'application/json', + '', + 200, + 'application/json', + '{"projects":[{"name":"Project Name","id":3}]}' + ], + [ + 'POST', + '/issues.xml', + 'application/xml', + '3', + 200, + 'application/xml', + '' + ] + ); + + // Create the object under test + $api = new Issue($client); + + // Perform the tests + $xmlElement = $api->create(['project' => 'Project Name']); + + $this->assertInstanceOf(SimpleXMLElement::class, $xmlElement); + $this->assertXmlStringEqualsXmlString( + '', + $xmlElement->asXml(), + ); + } + + /** + * @covers \Redmine\Api\Issue::cleanParams + * @covers \Redmine\Api\Issue::getIssueCategoryApi + */ + public function testCreateWithHttpClientRetrievesIssueCategoryId() + { + $client = AssertingHttpClient::create( + $this, + [ + 'GET', + '/projects/3/issue_categories.json', + 'application/json', + '', + 200, + 'application/json', + '{"issue_categories":[{"name":"Category Name","id":45}]}' + ], + [ + 'POST', + '/issues.xml', + 'application/xml', + '345', + 200, + 'application/xml', + '' + ] + ); + + // Create the object under test + $api = new Issue($client); + + // Perform the tests + $xmlElement = $api->create(['project_id' => 3, 'category' => 'Category Name']); + + $this->assertInstanceOf(SimpleXMLElement::class, $xmlElement); + $this->assertXmlStringEqualsXmlString( + '', + $xmlElement->asXml(), + ); + } + + /** + * @covers \Redmine\Api\Issue::cleanParams + * @covers \Redmine\Api\Issue::getTrackerApi + */ + public function testCreateWithHttpClientRetrievesTrackerId() + { + $client = AssertingHttpClient::create( + $this, + [ + 'GET', + '/trackers.json', + 'application/json', + '', + 200, + 'application/json', + '{"trackers":[{"name":"Tracker Name","id":9}]}' + ], + [ + 'POST', + '/issues.xml', + 'application/xml', + '9', + 200, + 'application/xml', + '' + ] + ); + + // Create the object under test + $api = new Issue($client); + + // Perform the tests + $xmlElement = $api->create(['tracker' => 'Tracker Name']); + + $this->assertInstanceOf(SimpleXMLElement::class, $xmlElement); + $this->assertXmlStringEqualsXmlString( + '', + $xmlElement->asXml(), + ); + } + + /** + * @covers \Redmine\Api\Issue::cleanParams + * @covers \Redmine\Api\Issue::getUserApi + */ + public function testCreateWithHttpClientRetrievesUserId() + { + $client = AssertingHttpClient::create( + $this, + [ + 'GET', + '/users.json', + 'application/json', + '', + 200, + 'application/json', + '{"users":[{"login":"Author Name","id":5},{"login":"Assigned to User Name","id":6}]}' + ], + [ + 'POST', + '/issues.xml', + 'application/xml', + '65', + 200, + 'application/xml', + '' + ] + ); + + // Create the object under test + $api = new Issue($client); + + // Perform the tests + $xmlElement = $api->create(['assigned_to' => 'Assigned to User Name', 'author' => 'Author Name']); + + $this->assertInstanceOf(SimpleXMLElement::class, $xmlElement); + $this->assertXmlStringEqualsXmlString( + '', + $xmlElement->asXml(), + ); + } + + /** + * Test cleanParams(). + * + * @covers \Redmine\Api\Issue::cleanParams + * @covers \Redmine\Api\Issue::getIssueCategoryApi + * @covers \Redmine\Api\Issue::getIssueStatusApi + * @covers \Redmine\Api\Issue::getProjectApi + * @covers \Redmine\Api\Issue::getTrackerApi + * @covers \Redmine\Api\Issue::getUserApi + */ + public function testCreateWithClientCleansParameters() + { + $client = AssertingHttpClient::create( + $this, + [ + 'GET', + '/projects.json', + 'application/json', + '', + 200, + 'application/json', + '{"projects":[{"name":"Project Name","id":3}]}' + ], + [ + 'GET', + '/projects/3/issue_categories.json', + 'application/json', + '', + 200, + 'application/json', + '{"issue_categories":[{"name":"Category Name","id":45}]}' + ], + [ + 'GET', + '/issue_statuses.json', + 'application/json', + '', + 200, + 'application/json', + '{"issue_statuses":[{"name":"Status Name","id":123}]}' + ], + [ + 'GET', + '/trackers.json', + 'application/json', + '', + 200, + 'application/json', + '{"trackers":[{"name":"Tracker Name","id":9}]}' + ], + [ + 'GET', + '/users.json', + 'application/json', + '', + 200, + 'application/json', + '{"users":[{"login":"Author Name","id":5},{"login":"Assigned to User Name","id":6}]}' + ], + [ + 'POST', + '/issues.xml', + 'application/xml', + << + + 3 + 45 + 123 + 9 + 6 + 5 + + XML, + 200, + 'application/xml', + '' + ] + ); + + $parameters = [ + 'project' => 'Project Name', + 'category' => 'Category Name', + 'status' => 'Status Name', + 'tracker' => 'Tracker Name', + 'assigned_to' => 'Assigned to User Name', + 'author' => 'Author Name', + ]; + + // Create the object under test + $api = new Issue($client); + + // Perform the tests + $xmlElement = $api->create($parameters); + + $this->assertInstanceOf(SimpleXMLElement::class, $xmlElement); + $this->assertXmlStringEqualsXmlString( + '', + $xmlElement->asXml(), + ); + } +} diff --git a/tests/Unit/Api/IssueCategory/CreateTest.php b/tests/Unit/Api/IssueCategory/CreateTest.php new file mode 100644 index 00000000..588c3b5f --- /dev/null +++ b/tests/Unit/Api/IssueCategory/CreateTest.php @@ -0,0 +1,150 @@ +create($identifier, $parameters); + + $this->assertInstanceOf(SimpleXMLElement::class, $return); + $this->assertXmlStringEqualsXmlString($response, $return->asXml()); + } + + public static function getCreateData(): array + { + return [ + 'test with minimal parameters' => [ + 5, + ['name' => 'Test Category'], + '/projects/5/issue_categories.xml', + 'Test Category', + 201, + '', + ], + 'test with minimal parameters and project identifier as string' => [ + 'test-project', + ['name' => 'Test Category'], + '/projects/test-project/issue_categories.xml', + 'Test Category', + 201, + '', + ], + 'test with all parameters' => [ + 5, + ['name' => 'Test Category', 'assigned_to_id' => 2], + '/projects/5/issue_categories.xml', + 'Test Category2', + 201, + '', + ], + ]; + } + + public function testCreateReturnsEmptyString() + { + $client = AssertingHttpClient::create( + $this, + [ + 'POST', + '/projects/5/issue_categories.xml', + 'application/xml', + 'Test Category', + 500, + '', + '' + ] + ); + + // Create the object under test + $api = new IssueCategory($client); + + // Perform the tests + $return = $api->create(5, ['name' => 'Test Category']); + + $this->assertSame('', $return); + } + + public function testCreateThrowsExceptionWithEmptyParameters() + { + // Create the used mock objects + $client = $this->createMock(HttpClient::class); + + // Create the object under test + $api = new IssueCategory($client); + + $this->expectException(MissingParameterException::class); + $this->expectExceptionMessage('Theses parameters are mandatory: `name'); + + // Perform the tests + $api->create(5); + } + + /** + * @dataProvider incompleteCreateParameterProvider + */ + public function testCreateThrowsExceptionIfMandatoyParametersAreMissing($parameters) + { + // Create the used mock objects + $client = $this->createMock(HttpClient::class); + + // Create the object under test + $api = new IssueCategory($client); + + $this->expectException(MissingParameterException::class); + $this->expectExceptionMessage('Theses parameters are mandatory: `name'); + + // Perform the tests + $api->create('5', $parameters); + } + + /** + * Provider for incomplete create parameters. + * + * @return array[] + */ + public static function incompleteCreateParameterProvider(): array + { + return [ + 'missing all mandatory parameters' => [ + [], + ], + 'missing `name` parameter' => [ + [ + 'assigned_to_id' => 2, + ], + ], + ]; + } +} diff --git a/tests/Unit/Api/IssueCategoryTest.php b/tests/Unit/Api/IssueCategoryTest.php index 36caaec4..00b854ba 100644 --- a/tests/Unit/Api/IssueCategoryTest.php +++ b/tests/Unit/Api/IssueCategoryTest.php @@ -341,75 +341,6 @@ public function testGetIdByNameMakesGetRequest() $this->assertSame(5, $api->getIdByName(5, 'IssueCategory 5')); } - /** - * Test create(). - * - * @covers ::post - * @covers ::create - * @test - */ - public function testCreateThrowsExceptionIfNameIsMissing() - { - // Test values - $parameters = [ - 'name' => null, - 'assigned_to_id' => 2, - ]; - - // Create the used mock objects - $client = $this->createMock(Client::class); - - // Create the object under test - $api = new IssueCategory($client); - - $this->expectException(MissingParameterException::class); - $this->expectExceptionMessage('Theses parameters are mandatory: `name`'); - - // Perform the tests - $api->create(5, $parameters); - } - - /** - * Test create(). - * - * @covers ::post - * @covers ::create - * @test - */ - public function testCreateCallsPost() - { - // Test values - $response = 'API Response'; - $parameters = [ - 'name' => 'Test Category', - 'assigned_to_id' => 2, - ]; - - // Create the used mock objects - $client = $this->createMock(Client::class); - $client->expects($this->once()) - ->method('requestPost') - ->with( - '/projects/5/issue_categories.xml', - $this->logicalAnd( - $this->stringStartsWith('' . "\n" . ''), - $this->stringEndsWith('' . "\n"), - $this->stringContains('Test Category'), - $this->stringContains('2') - ) - ) - ->willReturn(true); - $client->expects($this->exactly(1)) - ->method('getLastResponseBody') - ->willReturn($response); - - // Create the object under test - $api = new IssueCategory($client); - - // Perform the tests - $this->assertSame($response, $api->create(5, $parameters)); - } - /** * Test update(). * diff --git a/tests/Unit/Api/IssueRelation/CreateTest.php b/tests/Unit/Api/IssueRelation/CreateTest.php new file mode 100644 index 00000000..ecbf35a8 --- /dev/null +++ b/tests/Unit/Api/IssueRelation/CreateTest.php @@ -0,0 +1,136 @@ +create($issueId, $parameters); + + $this->assertIsArray($return); + $this->assertSame($expectedReturn, $return); + } + + public static function getCreateData(): array + { + return [ + 'test with minimal parameters' => [ + 5, + ['issue_to_id' => 10], + '/issues/5/relations.json', + '{"relation":{"issue_to_id":10,"relation_type":"relates"}}', + 201, + '{"relation":{}}', + ['relation' => []], + ], + ]; + } + + public function testCreateThrowsExceptionIfResponseContainsEmptyString() + { + $client = AssertingHttpClient::create( + $this, + [ + 'POST', + '/issues/5/relations.json', + 'application/json', + '{"relation":{"issue_to_id":10,"relation_type":"relates"}}', + 500, + '', + '' + ] + ); + + // Create the object under test + $api = new IssueRelation($client); + + $this->expectException(SerializerException::class); + $this->expectExceptionMessage('Catched error "Syntax error" while decoding JSON: '); + + // Perform the tests + $api->create(5, ['issue_to_id' => 10]); + } + + public function testCreateThrowsExceptionWithEmptyParameters() + { + // Create the used mock objects + $client = $this->createMock(HttpClient::class); + + // Create the object under test + $api = new IssueRelation($client); + + $this->expectException(MissingParameterException::class); + $this->expectExceptionMessage('Theses parameters are mandatory: `issue_to_id`'); + + // Perform the tests + $api->create(5); + } + + /** + * @dataProvider incompleteCreateParameterProvider + */ + public function testCreateThrowsExceptionIfMandatoyParametersAreMissing($parameters) + { + // Create the used mock objects + $client = $this->createMock(HttpClient::class); + + // Create the object under test + $api = new IssueRelation($client); + + $this->expectException(MissingParameterException::class); + $this->expectExceptionMessage('Theses parameters are mandatory: `issue_to_id`'); + + // Perform the tests + $api->create(5, $parameters); + } + + /** + * Provider for incomplete create parameters. + * + * @return array[] + */ + public static function incompleteCreateParameterProvider(): array + { + return [ + 'missing all mandatory parameters' => [ + [], + ], + 'missing `issue_to_id` parameter' => [ + [ + 'relation_type' => 'relates', + ], + ], + ]; + } +} diff --git a/tests/Unit/Api/IssueRelation/ShowTest.php b/tests/Unit/Api/IssueRelation/ShowTest.php index e806c7db..144ac1e2 100644 --- a/tests/Unit/Api/IssueRelation/ShowTest.php +++ b/tests/Unit/Api/IssueRelation/ShowTest.php @@ -41,6 +41,7 @@ public static function getShowData(): array return [ 'array response with integer id' => [5, '/relations/5.json', '{"relation":{"child":[5,2,3]}}', ['child' => [5, 2, 3]]], 'array response with string id' => ['5', '/relations/5.json', '{"relation":{"child":[5,2,3]}}', ['child' => [5, 2, 3]]], + 'array response on object without relation key error' => [5, '/relations/5.json', '{}', []], 'array response on string error' => [5, '/relations/5.json', 'string', []], 'array response on empty error' => [5, '/relations/5.json', '', []], ]; diff --git a/tests/Unit/Api/IssueRelationTest.php b/tests/Unit/Api/IssueRelationTest.php index bc939f02..ba97896c 100644 --- a/tests/Unit/Api/IssueRelationTest.php +++ b/tests/Unit/Api/IssueRelationTest.php @@ -156,45 +156,4 @@ public function testRemoveCallsDelete() // Perform the tests $this->assertSame($expectedReturn, $api->remove(5)); } - - /** - * Test create(). - * - * @covers ::create - * @covers ::post - * @test - */ - public function testCreateCallsPost() - { - // Test values - $parameters = []; - $response = '{"test":"response"}'; - $expectedReturn = ['test' => 'response']; - - // Create the used mock objects - $client = $this->createMock(Client::class); - $client->expects($this->once()) - ->method('requestPost') - ->with( - '/issues/1/relations.json', - json_encode([ - 'relation' => [ - 'relation_type' => 'relates', - ], - ]) - ) - ->willReturn(true); - $client->expects($this->exactly(1)) - ->method('getLastResponseBody') - ->willReturn($response); - $client->expects($this->exactly(1)) - ->method('getLastResponseContentType') - ->willReturn('application/json'); - - // Create the object under test - $api = new IssueRelation($client); - - // Perform the tests - $this->assertSame($expectedReturn, $api->create(1, $parameters)); - } } diff --git a/tests/Unit/Api/IssueTest.php b/tests/Unit/Api/IssueTest.php index 034cca23..a278d71e 100644 --- a/tests/Unit/Api/IssueTest.php +++ b/tests/Unit/Api/IssueTest.php @@ -226,37 +226,6 @@ public function testAttachCallsPut() $this->assertSame($response, $api->attach(5, $attachment)); } - /** - * Test addWatcher(). - * - * @covers ::addWatcher - * @test - */ - public function testAddWatcherCallsPost() - { - // Test values - $response = 'API Response'; - - // Create the used mock objects - $client = $this->createMock(Client::class); - $client->expects($this->once()) - ->method('requestPost') - ->with( - $this->stringStartsWith('/issues/5/watchers.xml'), - $this->stringEndsWith('10' . "\n") - ) - ->willReturn(true); - $client->expects($this->exactly(1)) - ->method('getLastResponseBody') - ->willReturn($response); - - // Create the object under test - $api = new Issue($client); - - // Perform the tests - $this->assertSame($response, $api->addWatcher(5, 10)); - } - /** * Test removeWatcher(). * @@ -288,40 +257,7 @@ public function testRemoveWatcherCallsPost() } /** - * Test create(). - * - * @covers ::create - * @covers ::post - * @test - */ - public function testCreateCallsPost() - { - // Test values - $response = 'API Response'; - $parameters = []; - - // Create the used mock objects - $client = $this->createMock(Client::class); - $client->expects($this->once()) - ->method('requestPost') - ->with( - '/issues.xml', - '' . "\n" . '' . "\n" - ) - ->willReturn(true); - $client->expects($this->exactly(1)) - ->method('getLastResponseBody') - ->willReturn($response); - - // Create the object under test - $api = new Issue($client); - - // Perform the tests - $this->assertSame($response, $api->create($parameters)); - } - - /** - * Test cleanParams(). + * Test cleanParams() with Client for BC * * @covers ::create * @covers ::cleanParams @@ -335,7 +271,7 @@ public function testCreateCallsPost() public function testCreateWithClientCleansParameters() { // Test values - $response = 'API Response'; + $response = ''; $parameters = [ 'project' => 'Project Name', 'category' => 'Category Name', @@ -391,281 +327,15 @@ public function testCreateWithClientCleansParameters() $client->expects($this->exactly(1)) ->method('getLastResponseBody') ->willReturn($response); - - // Create the object under test - $api = new Issue($client); - - // Perform the tests - $this->assertSame($response, $api->create($parameters)); - } - - /** - * @covers ::create - * @covers ::cleanParams - * @covers ::getIssueStatusApi - * @test - */ - public function testCreateWithHttpClientRetrievesIssueStatusId() - { - $client = AssertingHttpClient::create( - $this, - [ - 'GET', - '/issue_statuses.json', - 'application/json', - '', - 200, - 'application/json', - '{"issue_statuses":[{"name":"Status Name","id":123}]}' - ], - [ - 'POST', - '/issues.xml', - 'application/xml', - '123', - 200, - 'application/xml', - '' - ] - ); - - // Create the object under test - $api = new Issue($client); - - $xmlElement = $api->create(['status' => 'Status Name']); - - // Perform the tests - $this->assertInstanceOf(SimpleXMLElement::class, $xmlElement); - $this->assertXmlStringEqualsXmlString( - '', - $xmlElement->asXml(), - ); - } - - /** - * @covers ::create - * @covers ::cleanParams - * @covers ::getProjectApi - * @test - */ - public function testCreateWithHttpClientRetrievesProjectId() - { - $client = AssertingHttpClient::create( - $this, - [ - 'GET', - '/projects.json', - 'application/json', - '', - 200, - 'application/json', - '{"projects":[{"name":"Project Name","id":3}]}' - ], - [ - 'POST', - '/issues.xml', - 'application/xml', - '3', - 200, - 'application/xml', - '' - ] - ); - - // Create the object under test - $api = new Issue($client); - - $xmlElement = $api->create(['project' => 'Project Name']); - - // Perform the tests - $this->assertInstanceOf(SimpleXMLElement::class, $xmlElement); - $this->assertXmlStringEqualsXmlString( - '', - $xmlElement->asXml(), - ); - } - - /** - * @covers ::create - * @covers ::cleanParams - * @covers ::getIssueCategoryApi - * @test - */ - public function testCreateWithHttpClientRetrievesIssueCategoryId() - { - $client = AssertingHttpClient::create( - $this, - [ - 'GET', - '/projects/3/issue_categories.json', - 'application/json', - '', - 200, - 'application/json', - '{"issue_categories":[{"name":"Category Name","id":45}]}' - ], - [ - 'POST', - '/issues.xml', - 'application/xml', - '345', - 200, - 'application/xml', - '' - ] - ); - - // Create the object under test - $api = new Issue($client); - - $xmlElement = $api->create(['project_id' => 3, 'category' => 'Category Name']); - - // Perform the tests - $this->assertInstanceOf(SimpleXMLElement::class, $xmlElement); - $this->assertXmlStringEqualsXmlString( - '', - $xmlElement->asXml(), - ); - } - - /** - * @covers ::create - * @covers ::cleanParams - * @covers ::getTrackerApi - * @test - */ - public function testCreateWithHttpClientRetrievesTrackerId() - { - $client = AssertingHttpClient::create( - $this, - [ - 'GET', - '/trackers.json', - 'application/json', - '', - 200, - 'application/json', - '{"trackers":[{"name":"Tracker Name","id":9}]}' - ], - [ - 'POST', - '/issues.xml', - 'application/xml', - '9', - 200, - 'application/xml', - '' - ] - ); - - // Create the object under test - $api = new Issue($client); - - $xmlElement = $api->create(['tracker' => 'Tracker Name']); - - // Perform the tests - $this->assertInstanceOf(SimpleXMLElement::class, $xmlElement); - $this->assertXmlStringEqualsXmlString( - '', - $xmlElement->asXml(), - ); - } - - /** - * @covers ::create - * @covers ::cleanParams - * @covers ::getUserApi - * @test - */ - public function testCreateWithHttpClientRetrievesUserId() - { - $client = AssertingHttpClient::create( - $this, - [ - 'GET', - '/users.json', - 'application/json', - '', - 200, - 'application/json', - '{"users":[{"login":"Author Name","id":5},{"login":"Assigned to User Name","id":6}]}' - ], - [ - 'POST', - '/issues.xml', - 'application/xml', - '65', - 200, - 'application/xml', - '' - ] - ); - - // Create the object under test - $api = new Issue($client); - - $xmlElement = $api->create(['assigned_to' => 'Assigned to User Name', 'author' => 'Author Name']); - - // Perform the tests - $this->assertInstanceOf(SimpleXMLElement::class, $xmlElement); - $this->assertXmlStringEqualsXmlString( - '', - $xmlElement->asXml(), - ); - } - - /** - * Test create() and buildXML(). - * - * @covers ::create - * @covers ::attachCustomFieldXML - * @test - */ - public function testCreateBuildsXmlForCustomFields() - { - // Test values - $response = 'API Response'; - $parameters = [ - 'custom_fields' => [ - [ - 'id' => 123, - 'name' => 'cf_name', - 'field_format' => 'string', - 'value' => [1, 2, 3], - ], - ], - ]; - - // Create the used mock objects - $client = $this->createMock(Client::class); - - $client->expects($this->once()) - ->method('requestPost') - ->with( - '/issues.xml', - $this->logicalAnd( - $this->stringStartsWith(''), - $this->stringContains(''), - $this->stringContains(''), - $this->stringContains(''), - $this->stringContains('1'), - $this->stringContains('2'), - $this->stringContains('3'), - $this->stringContains(''), - $this->stringContains(''), - $this->stringEndsWith('' . "\n") - ) - ) - ->willReturn(true); $client->expects($this->exactly(1)) - ->method('getLastResponseBody') - ->willReturn($response); + ->method('getLastResponseContentType') + ->willReturn('application/xml'); // Create the object under test $api = new Issue($client); // Perform the tests - $this->assertSame($response, $api->create($parameters)); + $this->assertXmlStringEqualsXmlString($response, $api->create($parameters)->asXML()); } /** @@ -901,203 +571,6 @@ public function testAddNoteToIssue() $this->assertSame($response, $api->addNoteToIssue(5, 'Note content')); } - /** - * Test buildXML(). - * - * @test - */ - public function testBuildXmlWithCustomFields() - { - // Test values - $parameters = [ - 'custom_fields' => [ - ['id' => 225, 'value' => 'One Custom Field'], - ['id' => 25, 'value' => 'Second Custom Field'], - ['id' => 321, 'value' => 'http://test.local/?one=first&two=second'], - ], - ]; - - // Create the used mock objects - $client = $this->createMock(Client::class); - $client->expects($this->once()) - ->method('requestPost') - ->with( - '/issues.xml', - $this->logicalAnd( - $this->stringStartsWith('' . "\n" . ''), - $this->stringEndsWith('' . "\n"), - $this->stringContains(''), - $this->stringContains(''), - $this->stringContains('One Custom Field'), - $this->stringContains('Second Custom Field'), - $this->stringContains('http://test.local/?one=first&two=second') - ) - ); - - // Create the object under test - $api = new Issue($client); - - // Perform the tests - $api->create($parameters); - } - - /** - * Test buildXML(). - * - * @test - */ - public function testBuildXmlWithWatchers() - { - // Test values - $parameters = [ - 'watcher_user_ids' => [5, 25], - ]; - - // Create the used mock objects - $client = $this->createMock(Client::class); - $client->expects($this->once()) - ->method('requestPost') - ->with( - '/issues.xml', - $this->logicalAnd( - $this->stringStartsWith('' . "\n" . ''), - $this->stringEndsWith('' . "\n"), - $this->stringContains(''), - $this->stringContains(''), - $this->stringContains('5'), - $this->stringContains('25') - ) - ); - - // Create the object under test - $api = new Issue($client); - - // Perform the tests - $api->create($parameters); - } - - /** - * Test buildXML(). - * - * @test - */ - public function testBuildXmlWithUploads() - { - // Test values - $parameters = [ - 'uploads' => [ - [ - 'token' => 'first-token', - 'filename' => 'SomeRandomFile.txt', - 'description' => 'Simple description', - 'content_type' => 'text/plain', - ], - [ - 'token' => 'second-token', - 'filename' => 'An-Other-File.css', - 'content_type' => 'text/css', - ], - ], - ]; - - // Create the used mock objects - $client = $this->createMock(Client::class); - $client->expects($this->once()) - ->method('requestPost') - ->with( - '/issues.xml', - $this->logicalAnd( - $this->stringStartsWith('' . "\n" . ''), - $this->stringEndsWith('' . "\n"), - $this->stringContains(''), - $this->stringContains(''), - $this->stringContains( - '' - . 'first-token' - . 'SomeRandomFile.txt' - . 'Simple description' - . 'text/plain' - . '' - ), - $this->stringContains( - '' - . 'second-token' - . 'An-Other-File.css' - . 'text/css' - . '' - ) - ) - ); - - // Create the object under test - $api = new Issue($client); - - // Perform the tests - $api->create($parameters); - } - - /** - * Test buildXML(). - * - * @test - */ - public function testBuildXmlWithWatcherAndUploadAndCustomFieldAndStandard() - { - // Test values - $parameters = [ - 'watcher_user_ids' => [5], - 'subject' => 'Issue subject with some xml entities: & < > " \' ', - 'description' => 'Description with some xml entities: & < > " \' ', - 'uploads' => [ - [ - 'token' => 'first-token', - 'filename' => 'SomeRandomFile.txt', - 'description' => 'Simple description', - 'content_type' => 'text/plain', - ], - ], - 'custom_fields' => [ - ['id' => 25, 'value' => 'Second Custom Field'], - ], - ]; - - // Create the used mock objects - $client = $this->createMock(Client::class); - $client->expects($this->once()) - ->method('requestPost') - ->with( - '/issues.xml', - $this->logicalAnd( - $this->stringStartsWith('' . "\n" . ''), - $this->stringEndsWith('' . "\n"), - $this->stringContains(''), - $this->stringContains(''), - $this->stringContains('5'), - $this->stringContains( - '' - . 'first-token' - . 'SomeRandomFile.txt' - . 'Simple description' - . 'text/plain' - . '' - ), - $this->stringContains( - '' - . 'Second Custom Field' - . '' - ), - $this->stringContains('Issue subject with some xml entities: & < > " \' '), - $this->stringContains('Description with some xml entities: & < > " \' ') - ) - ); - - // Create the object under test - $api = new Issue($client); - - // Perform the tests - $api->create($parameters); - } - /** * Test assign an user to an issue. * diff --git a/tests/Unit/Api/Membership/CreateTest.php b/tests/Unit/Api/Membership/CreateTest.php new file mode 100644 index 00000000..f736e52c --- /dev/null +++ b/tests/Unit/Api/Membership/CreateTest.php @@ -0,0 +1,147 @@ +create($identifier, $parameters); + + $this->assertInstanceOf(SimpleXMLElement::class, $return); + $this->assertXmlStringEqualsXmlString($response, $return->asXml()); + } + + public static function getCreateData(): array + { + return [ + 'test with on role_id' => [ + 5, + ['user_id' => 4, 'role_ids' => 2], + '/projects/5/memberships.xml', + '42', + 201, + '', + ], + 'test with multiple role_ids' => [ + 5, + ['user_id' => 4, 'role_ids' => [5, 6]], + '/projects/5/memberships.xml', + '456', + 201, + '', + ], + ]; + } + + public function testCreateReturnsEmptyString() + { + $client = AssertingHttpClient::create( + $this, + [ + 'POST', + '/projects/5/memberships.xml', + 'application/xml', + '42', + 500, + '', + '' + ] + ); + + // Create the object under test + $api = new Membership($client); + + // Perform the tests + $return = $api->create(5, ['user_id' => 4, 'role_ids' => 2]); + + $this->assertSame('', $return); + } + + public function testCreateThrowsExceptionWithEmptyParameters() + { + // Create the used mock objects + $client = $this->createMock(HttpClient::class); + + // Create the object under test + $api = new Membership($client); + + $this->expectException(MissingParameterException::class); + $this->expectExceptionMessage('Theses parameters are mandatory: `user_id`, `role_ids`'); + + // Perform the tests + $api->create(5); + } + + /** + * @dataProvider incompleteCreateParameterProvider + */ + public function testCreateThrowsExceptionIfMandatoyParametersAreMissing($parameters) + { + // Create the used mock objects + $client = $this->createMock(HttpClient::class); + + // Create the object under test + $api = new Membership($client); + + $this->expectException(MissingParameterException::class); + $this->expectExceptionMessage('Theses parameters are mandatory: `user_id`, `role_ids`'); + + // Perform the tests + $api->create('test', $parameters); + } + + /** + * Provider for incomplete create parameters. + * + * @return array[] + */ + public static function incompleteCreateParameterProvider(): array + { + return [ + 'missing all mandatory parameters' => [ + [], + ], + 'missing `user_id` parameter' => [ + [ + 'role_ids' => 2, + ], + ], + 'missing `role_ids` parameter' => [ + [ + 'user_id' => 4, + ], + ], + ]; + } +} diff --git a/tests/Unit/Api/MembershipTest.php b/tests/Unit/Api/MembershipTest.php index f8522488..2e9a22a3 100644 --- a/tests/Unit/Api/MembershipTest.php +++ b/tests/Unit/Api/MembershipTest.php @@ -258,144 +258,6 @@ public function testRemoveMemberReturnsFalseIfMemberlistIsMissing() $this->assertFalse($api->removeMember(1, 2)); } - /** - * Test create(). - * - * @covers ::create - * - * @test - */ - public function testCreateThrowsExceptionWithEmptyParameters() - { - // Test values - $response = 'API Response'; - - // Create the used mock objects - $client = $this->createMock(Client::class); - - // Create the object under test - $api = new Membership($client); - - $this->expectException(MissingParameterException::class); - $this->expectExceptionMessage('Theses parameters are mandatory: `user_id`, `role_ids`'); - - // Perform the tests - $this->assertSame($response, $api->create(5)); - } - - /** - * Test create(). - * - * @covers ::create - * - * @test - */ - public function testCreateThrowsExceptionIfRoleIdsAreMissingInParameters() - { - // Test values - $response = 'API Response'; - - // Create the used mock objects - $client = $this->createMock(Client::class); - - // Create the object under test - $api = new Membership($client); - - $this->expectException(MissingParameterException::class); - $this->expectExceptionMessage('Theses parameters are mandatory: `user_id`, `role_ids`'); - - // Perform the tests - $this->assertSame($response, $api->create(5, ['user_id' => 4])); - } - - /** - * Test create(). - * - * @covers ::create - * @covers ::post - * @test - */ - public function testCreateCallsPost() - { - // Test values - $response = 'API Response'; - $parameters = [ - 'user_id' => 1, - 'role_ids' => 1, - ]; - - // Create the used mock objects - $client = $this->createMock(Client::class); - $client->expects($this->once()) - ->method('requestPost') - ->with( - $this->logicalAnd( - $this->stringStartsWith('/projects/5/memberships'), - $this->stringEndsWith('.xml') - ), - $this->logicalAnd( - $this->stringStartsWith('' . "\n" . ''), - $this->stringEndsWith('' . "\n") - ) - ) - ->willReturn(true); - $client->expects($this->exactly(1)) - ->method('getLastResponseBody') - ->willReturn($response); - - // Create the object under test - $api = new Membership($client); - - // Perform the tests - $this->assertSame($response, $api->create(5, $parameters)); - } - - /** - * Test create(). - * - * @covers ::create - * @test - */ - public function testCreateBuildsXml() - { - // Test values - $response = 'API Response'; - $parameters = [ - 'user_id' => 10, - 'role_ids' => [5, 6], - ]; - - // Create the used mock objects - $client = $this->createMock(Client::class); - $client->expects($this->once()) - ->method('requestPost') - ->with( - $this->logicalAnd( - $this->stringStartsWith('/projects/5/memberships'), - $this->stringEndsWith('.xml') - ), - $this->logicalAnd( - $this->stringStartsWith('' . "\n" . ''), - $this->stringEndsWith('' . "\n"), - $this->stringContains(''), - $this->stringContains('5'), - $this->stringContains('6'), - $this->stringContains(''), - $this->stringContains('10') - ) - ) - ->willReturn(true); - $client->expects($this->exactly(1)) - ->method('getLastResponseBody') - ->willReturn($response); - - // Create the object under test - $api = new Membership($client); - - // Perform the tests - $this->assertSame($response, $api->create(5, $parameters)); - } - /** * Test update(). * diff --git a/tests/Unit/Api/Project/CreateTest.php b/tests/Unit/Api/Project/CreateTest.php new file mode 100644 index 00000000..780ab6f9 --- /dev/null +++ b/tests/Unit/Api/Project/CreateTest.php @@ -0,0 +1,215 @@ +create($parameters); + + $this->assertInstanceOf(SimpleXMLElement::class, $return); + $this->assertXmlStringEqualsXmlString($response, $return->asXml()); + } + + public static function getCreateData(): array + { + return [ + 'test with minimal parameters' => [ + [ + 'identifier' => 'test-project', + 'name' => 'Test Project', + ], + '/projects.xml', + 'Test Projecttest-project', + 201, + '', + ], + 'test with special chars in parameters' => [ + [ + 'identifier' => 'test-project', + 'name' => 'Test Project with some xml entities: & < > " \' ', + 'description' => 'Description with some xml entities: & < > " \' ', + ], + '/projects.xml', + << + + Test Project with some xml entities: & < > " ' + test-project + Description with some xml entities: & < > " ' + + XML, + 201, + '', + ], + 'test with all parameters' => [ + [ + 'identifier' => 'test-project', + 'name' => 'Test Project', + 'tracker_ids' => [10, 5], + ], + '/projects.xml', + << + + Test Project + test-project + + 10 + 5 + + + XML, + 201, + '', + ], + 'test with custom fields' => [ + [ + 'identifier' => 'test-project', + 'name' => 'Test Project', + 'custom_fields' => [ + [ + 'id' => 123, + 'name' => 'cf_name', + 'field_format' => 'string', + 'value' => [1, 2, 3], + ], + ], + ], + '/projects.xml', + << + + Test Project + test-project + + + + 1 + 2 + 3 + + + + + XML, + 201, + '', + ], + ]; + } + + public function testCreateReturnsEmptyString() + { + $client = AssertingHttpClient::create( + $this, + [ + 'POST', + '/projects.xml', + 'application/xml', + 'Test Projecttest-project', + 500, + '', + '' + ] + ); + + // Create the object under test + $api = new Project($client); + + // Perform the tests + $return = $api->create(['identifier' => 'test-project', 'name' => 'Test Project']); + + $this->assertSame('', $return); + } + + public function testCreateThrowsExceptionWithEmptyParameters() + { + // Create the used mock objects + $client = $this->createMock(HttpClient::class); + + // Create the object under test + $api = new Project($client); + + $this->expectException(MissingParameterException::class); + $this->expectExceptionMessage('Theses parameters are mandatory: `name`, `identifier`'); + + // Perform the tests + $api->create(); + } + + /** + * @dataProvider incompleteCreateParameterProvider + */ + public function testCreateThrowsExceptionIfMandatoyParametersAreMissing($parameters) + { + // Create the used mock objects + $client = $this->createMock(HttpClient::class); + + // Create the object under test + $api = new Project($client); + + $this->expectException(MissingParameterException::class); + $this->expectExceptionMessage('Theses parameters are mandatory: `name`, `identifier`'); + + // Perform the tests + $api->create($parameters); + } + + /** + * Provider for incomplete create parameters. + * + * @return array[] + */ + public static function incompleteCreateParameterProvider(): array + { + return [ + 'missing all mandatory parameters' => [ + [ + 'description' => 'project description', + ], + ], + 'missing `identifier` parameters' => [ + [ + 'name' => 'Test Project', + ], + ], + 'missing `name` parameter' => [ + [ + 'identifier' => 'test-project', + ], + ], + ]; + } +} diff --git a/tests/Unit/Api/Project/ShowTest.php b/tests/Unit/Api/Project/ShowTest.php index f023afcd..21429ee2 100644 --- a/tests/Unit/Api/Project/ShowTest.php +++ b/tests/Unit/Api/Project/ShowTest.php @@ -39,11 +39,63 @@ public function testShowReturnsCorrectResponse($identifier, array $params, $expe public static function getShowData(): array { return [ - 'array response with integer id' => [5, [], '/projects/5.json?include=trackers%2Cissue_categories%2Cattachments%2Crelations', '["API Response"]', ['API Response']], - 'array response with string id' => ['test', [], '/projects/test.json?include=trackers%2Cissue_categories%2Cattachments%2Crelations', '["API Response"]', ['API Response']], - 'array response with parameters' => [5, ['parameter1', 'parameter2', 'enabled_modules'], '/projects/5.json?0=parameter1&1=parameter2&2=enabled_modules&include=trackers%2Cissue_categories%2Cattachments%2Crelations', '["API Response"]', ['API Response']], - 'string response' => [5, [], '/projects/5.json?include=trackers%2Cissue_categories%2Cattachments%2Crelations', 'string', 'Error decoding body as JSON: Syntax error'], - 'false response' => [5, [], '/projects/5.json?include=trackers%2Cissue_categories%2Cattachments%2Crelations', '', false], + 'array response with integer id' => [ + 5, + [], + '/projects/5.json?include=trackers%2Cissue_categories%2Cattachments%2Crelations', + '["API Response"]', + ['API Response'], + ], + 'array response with string id' => [ + 'test', + [], + '/projects/test.json?include=trackers%2Cissue_categories%2Cattachments%2Crelations', + '["API Response"]', + ['API Response'], + ], + 'test include parameter as array' => [ + 'test', + [ + 'include' => ['resource1', 'resource2'], + ], + '/projects/test.json?include=resource1%2Cresource2', + '["API Response"]', + ['API Response'], + ], + 'test include parameter as string will be ignored' => [ + 'test', + [ + 'include' => 'resource1,resource2', + ], + '/projects/test.json?include=trackers%2Cissue_categories%2Cattachments%2Crelations', + '["API Response"]', + ['API Response'], + ], + 'array response with parameters' => [ + 5, + [ + 'parameter1', + 'parameter2', + 'enabled_modules', + ], + '/projects/5.json?0=parameter1&1=parameter2&2=enabled_modules&include=trackers%2Cissue_categories%2Cattachments%2Crelations', + '["API Response"]', + ['API Response'], + ], + 'string response' => [ + 5, + [], + '/projects/5.json?include=trackers%2Cissue_categories%2Cattachments%2Crelations', + 'string', + 'Error decoding body as JSON: Syntax error', + ], + 'false response' => [ + 5, + [], + '/projects/5.json?include=trackers%2Cissue_categories%2Cattachments%2Crelations', + '', + false, + ], ]; } } diff --git a/tests/Unit/Api/ProjectTest.php b/tests/Unit/Api/ProjectTest.php index ccaafa1c..18d07b6e 100644 --- a/tests/Unit/Api/ProjectTest.php +++ b/tests/Unit/Api/ProjectTest.php @@ -295,174 +295,6 @@ public function testGetIdByNameMakesGetRequest() $this->assertSame(5, $api->getIdByName('Project 5')); } - /** - * Test create(). - * - * @covers ::create - * - * @test - */ - public function testCreateThrowsExceptionWithEmptyParameters() - { - // Test values - $response = 'API Response'; - - // Create the used mock objects - $client = $this->createMock(Client::class); - - // Create the object under test - $api = new Project($client); - - $this->expectException(MissingParameterException::class); - $this->expectExceptionMessage('Theses parameters are mandatory: `name`, `identifier`'); - - // Perform the tests - $this->assertSame($response, $api->create(['id' => 5])); - } - - /** - * Test create(). - * - * @covers ::create - * - * @test - */ - public function testCreateThrowsExceptionIfIdentifierIsMissingInParameters() - { - // Test values - $response = 'API Response'; - $parameters = [ - 'name' => 'Test Project', - ]; - - // Create the used mock objects - $client = $this->createMock(Client::class); - - // Create the object under test - $api = new Project($client); - - $this->expectException(MissingParameterException::class); - $this->expectExceptionMessage('Theses parameters are mandatory: `name`, `identifier`'); - - // Perform the tests - $this->assertSame($response, $api->create($parameters)); - } - - /** - * Test create(). - * - * @covers ::create - * - * @test - */ - public function testCreateThrowsExceptionIfNameIsMissingInParameters() - { - // Test values - $response = 'API Response'; - $parameters = [ - 'identifier' => 'test-project', - ]; - - // Create the used mock objects - $client = $this->createMock(Client::class); - - // Create the object under test - $api = new Project($client); - - $this->expectException(MissingParameterException::class); - $this->expectExceptionMessage('Theses parameters are mandatory: `name`, `identifier`'); - - // Perform the tests - $this->assertSame($response, $api->create($parameters)); - } - - /** - * Test create(). - * - * @covers ::create - * @covers ::post - * @test - */ - public function testCreateCallsPost() - { - // Test values - $response = 'API Response'; - $parameters = [ - 'identifier' => 'test-project', - 'name' => 'Test Project with some xml entities: & < > " \' ', - 'description' => 'Description with some xml entities: & < > " \' ', - ]; - - // Create the used mock objects - $client = $this->createMock(Client::class); - $client->expects($this->once()) - ->method('requestPost') - ->with( - '/projects.xml', - $this->logicalAnd( - $this->stringStartsWith('' . "\n" . ''), - $this->stringEndsWith('' . "\n"), - $this->stringContains('test-project'), - $this->stringContains('Test Project with some xml entities: & < > " \' '), - $this->stringContains('Description with some xml entities: & < > " \' ') - ) - ) - ->willReturn(true); - $client->expects($this->exactly(1)) - ->method('getLastResponseBody') - ->willReturn($response); - - // Create the object under test - $api = new Project($client); - - // Perform the tests - $this->assertSame($response, $api->create($parameters)); - } - - /** - * Test create(). - * - * @covers ::create - * @covers ::post - * @test - */ - public function testCreateCallsPostWithTrackers() - { - // Test values - $response = 'API Response'; - $parameters = [ - 'identifier' => 'test-project', - 'name' => 'Test Project', - 'tracker_ids' => [10, 5], - ]; - - // Create the used mock objects - $client = $this->createMock(Client::class); - $client->expects($this->once()) - ->method('requestPost') - ->with( - '/projects.xml', - $this->logicalAnd( - $this->stringStartsWith('' . "\n" . ''), - $this->stringEndsWith('' . "\n"), - $this->stringContains(''), - $this->stringContains('10'), - $this->stringContains('5'), - $this->stringContains('') - ) - ) - ->willReturn(true); - $client->expects($this->exactly(1)) - ->method('getLastResponseBody') - ->willReturn($response); - - // Create the object under test - $api = new Project($client); - - // Perform the tests - $this->assertSame($response, $api->create($parameters)); - } - /** * Test update(). * diff --git a/tests/Unit/Api/TimeEntry/CreateTest.php b/tests/Unit/Api/TimeEntry/CreateTest.php new file mode 100644 index 00000000..753735c1 --- /dev/null +++ b/tests/Unit/Api/TimeEntry/CreateTest.php @@ -0,0 +1,191 @@ +create($parameters); + + $this->assertInstanceOf(SimpleXMLElement::class, $return); + $this->assertXmlStringEqualsXmlString($response, $return->asXml()); + } + + public static function getCreateData(): array + { + return [ + 'test with issue_id' => [ + ['issue_id' => 5, 'hours' => 5.25], + '/time_entries.xml', + '55.25', + 201, + '', + ], + 'test with project_id' => [ + ['project_id' => 5, 'hours' => 5.25], + '/time_entries.xml', + '55.25', + 201, + '', + ], + 'test with all parameters' => [ + [ + 'issue_id' => '15', + 'project_id' => '25', + 'hours' => '5.25', + 'comments' => 'some text with xml entities: & < > " \' ', + 'custom_fields' => [ + [ + 'id' => 1, + 'name' => 'Affected version', + 'value' => '1.0.1', + ], + [ + 'id' => 2, + 'name' => 'Resolution', + 'value' => 'Fixed', + ], + ], + ], + '/time_entries.xml', + << + + 15 + 25 + 5.25 + some text with xml entities: & < > " ' + + + 1.0.1 + + + Fixed + + + + XML, + 201, + '', + ], + ]; + } + + public function testCreateReturnsEmptyString() + { + $client = AssertingHttpClient::create( + $this, + [ + 'POST', + '/time_entries.xml', + 'application/xml', + '55.25', + 500, + '', + '' + ] + ); + + // Create the object under test + $api = new TimeEntry($client); + + // Perform the tests + $return = $api->create(['issue_id' => 5, 'hours' => 5.25]); + + $this->assertSame('', $return); + } + + public function testCreateThrowsExceptionWithEmptyParameters() + { + // Test values + $response = 'API Response'; + + // Create the used mock objects + $client = $this->createMock(HttpClient::class); + + // Create the object under test + $api = new TimeEntry($client); + + $this->expectException(MissingParameterException::class); + $this->expectExceptionMessage('Theses parameters are mandatory: `issue_id` or `project_id`, `hours`'); + + // Perform the tests + $this->assertSame($response, $api->create()); + } + + /** + * @dataProvider incompleteCreateParameterProvider + */ + public function testCreateThrowsExceptionIfValueIsMissingInParameters($parameters) + { + // Create the used mock objects + $client = $this->createMock(HttpClient::class); + + // Create the object under test + $api = new TimeEntry($client); + + $this->expectException(MissingParameterException::class); + $this->expectExceptionMessage('Theses parameters are mandatory: `issue_id` or `project_id`, `hours`'); + + // Perform the tests + $api->create($parameters); + } + + /** + * Provider for incomplete create parameters. + * + * @return array[] + */ + public static function incompleteCreateParameterProvider(): array + { + return [ + 'missing all mandatory parameters' => [ + [ + 'id' => '5', + ], + ], + 'missing `issue_id` or `project_id` parameters' => [ + [ + 'hours' => '5.25', + ], + ], + 'missing `hours` parameter' => [ + [ + 'issue_id' => 5, + 'project_id' => 5, + ], + ], + ]; + } +} diff --git a/tests/Unit/Api/TimeEntryTest.php b/tests/Unit/Api/TimeEntryTest.php index d864f608..4f2f25e2 100644 --- a/tests/Unit/Api/TimeEntryTest.php +++ b/tests/Unit/Api/TimeEntryTest.php @@ -152,146 +152,6 @@ public function testRemoveCallsDelete() $this->assertSame($response, $api->remove(5)); } - /** - * Test create(). - * - * @covers ::create - * - * @test - */ - public function testCreateThrowsExceptionWithEmptyParameters() - { - // Test values - $response = 'API Response'; - - // Create the used mock objects - $client = $this->createMock(Client::class); - - // Create the object under test - $api = new TimeEntry($client); - - $this->expectException(MissingParameterException::class); - $this->expectExceptionMessage('Theses parameters are mandatory: `issue_id` or `project_id`, `hours`'); - - // Perform the tests - $this->assertSame($response, $api->create(['id' => 5])); - } - - /** - * Test create(). - * - * @covers ::create - * - * @test - */ - public function testCreateThrowsExceptionIfIssueIdAndProjectIdAreMissingInParameters() - { - // Test values - $response = 'API Response'; - $parameters = [ - 'hours' => '5.25', - ]; - - // Create the used mock objects - $client = $this->createMock(Client::class); - - // Create the object under test - $api = new TimeEntry($client); - - $this->expectException(MissingParameterException::class); - $this->expectExceptionMessage('Theses parameters are mandatory: `issue_id` or `project_id`, `hours`'); - - // Perform the tests - $this->assertSame($response, $api->create($parameters)); - } - - /** - * Test create(). - * - * @covers ::create - * - * @test - */ - public function testCreateThrowsExceptionIfHoursAreMissingInParameters() - { - // Test values - $response = 'API Response'; - $parameters = [ - 'issue_id' => '15', - 'project_id' => '25', - ]; - - // Create the used mock objects - $client = $this->createMock(Client::class); - - // Create the object under test - $api = new TimeEntry($client); - - $this->expectException(MissingParameterException::class); - $this->expectExceptionMessage('Theses parameters are mandatory: `issue_id` or `project_id`, `hours`'); - - // Perform the tests - $this->assertSame($response, $api->create($parameters)); - } - - /** - * Test create(). - * - * @covers ::create - * @covers ::post - * @test - */ - public function testCreateCallsPost() - { - // Test values - $response = 'API Response'; - $parameters = [ - 'issue_id' => '15', - 'project_id' => '25', - 'hours' => '5.25', - 'comments' => 'some text with xml entities: & < > " \' ', - 'custom_fields' => [ - [ - 'id' => 1, - 'name' => 'Affected version', - 'value' => '1.0.1', - ], - [ - 'id' => 2, - 'name' => 'Resolution', - 'value' => 'Fixed', - ], - ], - ]; - - // Create the used mock objects - $client = $this->createMock(Client::class); - $client->expects($this->once()) - ->method('requestPost') - ->with( - '/time_entries.xml', - $this->logicalAnd( - $this->stringStartsWith('' . "\n" . ''), - $this->stringEndsWith('' . "\n"), - $this->stringContains('15'), - $this->stringContains('25'), - $this->stringContains('5.25'), - $this->stringContains('some text with xml entities: & < > " \' '), - $this->stringContains('1.0.1Fixed') - ) - ) - ->willReturn(true); - $client->expects($this->exactly(1)) - ->method('getLastResponseBody') - ->willReturn($response); - - // Create the object under test - $api = new TimeEntry($client); - - // Perform the tests - $this->assertSame($response, $api->create($parameters)); - } - /** * Test update(). * diff --git a/tests/Unit/Api/User/CreateTest.php b/tests/Unit/Api/User/CreateTest.php new file mode 100644 index 00000000..0bd6be32 --- /dev/null +++ b/tests/Unit/Api/User/CreateTest.php @@ -0,0 +1,198 @@ +create($parameters); + + $this->assertInstanceOf(SimpleXMLElement::class, $return); + $this->assertXmlStringEqualsXmlString($response, $return->asXml()); + } + + public static function getCreateData(): array + { + return [ + 'test with minimal parameters' => [ + ['login' => 'user', 'lastname' => 'last', 'firstname' => 'first', 'mail' => 'mail@example.com'], + '/users.xml', + 'userlastfirstmail@example.com', + 201, + '', + ], + 'test with all parameters' => [ + [ + 'login' => 'user', + 'lastname' => 'last', + 'firstname' => 'first', + 'mail' => 'mail@example.com', + 'password' => 'secret', + 'custom_fields' => [ + ['id' => 5, 'value' => 'Value 5'], + ['id' => 13, 'value' => 'Value 13', 'name' => 'CF Name'], + ], + ], + '/users.xml', + << + + user + secret + last + first + mail@example.com + + + Value 5 + + + Value 13 + + + + XML, + 201, + '', + ], + ]; + } + + public function testCreateReturnsEmptyString() + { + $client = AssertingHttpClient::create( + $this, + [ + 'POST', + '/users.xml', + 'application/xml', + 'userlastfirstmail@example.com', + 500, + '', + '' + ] + ); + + // Create the object under test + $api = new User($client); + + // Perform the tests + $return = $api->create(['login' => 'user', 'lastname' => 'last', 'firstname' => 'first', 'mail' => 'mail@example.com']); + + $this->assertSame('', $return); + } + + public function testCreateThrowsExceptionWithEmptyParameters() + { + // Test values + $response = 'API Response'; + + // Create the used mock objects + $client = $this->createMock(HttpClient::class); + + // Create the object under test + $api = new User($client); + + $this->expectException(MissingParameterException::class); + $this->expectExceptionMessage('Theses parameters are mandatory: `login`, `lastname`, `firstname`, `mail`'); + + // Perform the tests + $this->assertSame($response, $api->create()); + } + + /** + * @dataProvider incompleteCreateParameterProvider + */ + public function testCreateThrowsExceptionIfValueIsMissingInParameters($parameters) + { + // Create the used mock objects + $client = $this->createMock(HttpClient::class); + + // Create the object under test + $api = new User($client); + + $this->expectException(MissingParameterException::class); + $this->expectExceptionMessage('Theses parameters are mandatory: `login`, `lastname`, `firstname`, `mail`'); + + // Perform the tests + $api->create($parameters); + } + + /** + * Provider for incomplete create parameters. + * + * @return array[] + */ + public static function incompleteCreateParameterProvider(): array + { + return [ + // Missing Login + [ + [ + 'password' => 'secretPass', + 'lastname' => 'Last Name', + 'firstname' => 'Firstname', + 'mail' => 'mail@example.com', + ], + ], + // Missing last name + [ + [ + 'login' => 'TestUser', + 'password' => 'secretPass', + 'firstname' => 'Firstname', + 'mail' => 'mail@example.com', + ], + ], + // Missing first name + [ + [ + 'login' => 'TestUser', + 'password' => 'secretPass', + 'lastname' => 'Last Name', + 'mail' => 'mail@example.com', + ], + ], + // Missing email + [ + [ + 'login' => 'TestUser', + 'password' => 'secretPass', + 'lastname' => 'Last Name', + 'firstname' => 'Firstname', + ], + ], + ]; + } +} diff --git a/tests/Unit/Api/UserTest.php b/tests/Unit/Api/UserTest.php index 1865bb6f..12f60e6d 100644 --- a/tests/Unit/Api/UserTest.php +++ b/tests/Unit/Api/UserTest.php @@ -5,7 +5,6 @@ use PHPUnit\Framework\TestCase; use Redmine\Api\User; use Redmine\Client\Client; -use Redmine\Exception\MissingParameterException; use Redmine\Tests\Fixtures\MockClient; /** @@ -224,209 +223,6 @@ public function testRemoveCallsDelete() $this->assertSame($response, $api->remove(5)); } - /** - * Test create(). - * - * @covers ::create - * - * @test - */ - public function testCreateThrowsExceptionWithEmptyParameters() - { - // Test values - $response = 'API Response'; - - // Create the used mock objects - $client = $this->createMock(Client::class); - - // Create the object under test - $api = new User($client); - - $this->expectException(MissingParameterException::class); - $this->expectExceptionMessage('Theses parameters are mandatory: `login`, `lastname`, `firstname`, `mail`'); - - // Perform the tests - $this->assertSame($response, $api->create()); - } - - /** - * Test create(). - * - * @covers ::create - * @dataProvider incompleteCreateParameterProvider - * - * @test - * - * @param array $parameters Parameters for create() - */ - public function testCreateThrowsExceptionIfValueIsMissingInParameters($parameters) - { - // Create the used mock objects - $client = $this->createMock(Client::class); - - // Create the object under test - $api = new User($client); - - $this->expectException(MissingParameterException::class); - $this->expectExceptionMessage('Theses parameters are mandatory: `login`, `lastname`, `firstname`, `mail`'); - - // Perform the tests - $api->create($parameters); - } - - /** - * Provider for incomplete create parameters. - * - * @return array[] - */ - public static function incompleteCreateParameterProvider(): array - { - return [ - // Missing Login - [ - [ - 'password' => 'secretPass', - 'lastname' => 'Last Name', - 'firstname' => 'Firstname', - 'mail' => 'mail@example.com', - ], - ], - // Missing last name - [ - [ - 'login' => 'TestUser', - 'password' => 'secretPass', - 'firstname' => 'Firstname', - 'mail' => 'mail@example.com', - ], - ], - // Missing first name - [ - [ - 'login' => 'TestUser', - 'password' => 'secretPass', - 'lastname' => 'Last Name', - 'mail' => 'mail@example.com', - ], - ], - // Missing email - [ - [ - 'login' => 'TestUser', - 'password' => 'secretPass', - 'lastname' => 'Last Name', - 'firstname' => 'Firstname', - ], - ], - ]; - } - - /** - * Test create(). - * - * @covers ::create - * @covers ::post - * @test - */ - public function testCreateCallsPost() - { - // Test values - $response = 'API Response'; - $parameters = [ - 'login' => 'TestUser', - 'password' => 'secretPass', - 'lastname' => 'Last Name', - 'firstname' => 'Firstname', - 'mail' => 'mail@example.com', - ]; - - // Create the used mock objects - $client = $this->createMock(Client::class); - $client->expects($this->once()) - ->method('requestPost') - ->with( - '/users.xml', - $this->logicalAnd( - $this->stringStartsWith('' . "\n" . ''), - $this->stringEndsWith('' . "\n"), - $this->stringContains('TestUser'), - $this->stringContains('secretPass'), - $this->stringContains('Last Name'), - $this->stringContains('Firstname'), - $this->stringContains('mail@example.com') - ) - ) - ->willReturn(true); - $client->expects($this->exactly(1)) - ->method('getLastResponseBody') - ->willReturn($response); - - // Create the object under test - $api = new User($client); - - // Perform the tests - $this->assertSame($response, $api->create($parameters)); - } - - /** - * Test create(). - * - * @covers ::create - * @covers ::post - * @covers ::attachCustomFieldXML - * @test - */ - public function testCreateWithCustomField() - { - // Test values - $response = 'API Response'; - $parameters = [ - 'login' => 'TestUser', - 'password' => 'secretPass', - 'lastname' => 'Last Name', - 'firstname' => 'Firstname', - 'mail' => 'mail@example.com', - 'custom_fields' => [ - ['id' => 5, 'value' => 'Value 5'], - ['id' => 13, 'value' => 'Value 13', 'name' => 'CF Name'], - ], - ]; - - // Create the used mock objects - $client = $this->createMock(Client::class); - $client->expects($this->once()) - ->method('requestPost') - ->with( - '/users.xml', - $this->logicalAnd( - $this->stringStartsWith('' . "\n" . ''), - $this->stringEndsWith('' . "\n"), - $this->stringContains('TestUser'), - $this->stringContains('secretPass'), - $this->stringContains('Last Name'), - $this->stringContains('Firstname'), - $this->stringContains('mail@example.com'), - $this->stringContains(''), - $this->stringContains(''), - $this->stringContains(''), - $this->stringContains('Value 13'), - $this->stringContains(''), - $this->stringContains('Value 5'), - $this->stringContains('') - ) - ) - ->willReturn(true); - $client->expects($this->exactly(1)) - ->method('getLastResponseBody') - ->willReturn($response); - - // Create the object under test - $api = new User($client); - - // Perform the tests - $this->assertSame($response, $api->create($parameters)); - } - /** * Test update(). * diff --git a/tests/Unit/Api/Version/CreateTest.php b/tests/Unit/Api/Version/CreateTest.php new file mode 100644 index 00000000..4531710c --- /dev/null +++ b/tests/Unit/Api/Version/CreateTest.php @@ -0,0 +1,212 @@ +create($identifier, $parameters); + + $this->assertInstanceOf(SimpleXMLElement::class, $return); + $this->assertXmlStringEqualsXmlString($response, $return->asXml()); + } + + public static function getCreateData(): array + { + return [ + 'test with minimal parameters' => [ + 5, + ['name' => 'test'], + '/projects/5/versions.xml', + 'test', + 201, + '', + ], + 'test with status parameter' => [ + 5, + ['name' => 'test', 'status' => 'locked'], + '/projects/5/versions.xml', + 'testlocked', + 201, + '', + ], + 'test with sharing parameter none' => [ + 5, + ['name' => 'test', 'sharing' => 'none'], + '/projects/5/versions.xml', + 'testnone', + 201, + '', + ], + 'test with sharing parameter descendants' => [ + 5, + ['name' => 'test', 'sharing' => 'descendants'], + '/projects/5/versions.xml', + 'testdescendants', + 201, + '', + ], + 'test with sharing parameter hierarchy' => [ + 5, + ['name' => 'test', 'sharing' => 'hierarchy'], + '/projects/5/versions.xml', + 'testhierarchy', + 201, + '', + ], + 'test with sharing parameter tree' => [ + 5, + ['name' => 'test', 'sharing' => 'tree'], + '/projects/5/versions.xml', + 'testtree', + 201, + '', + ], + 'test with sharing parameter system' => [ + 5, + ['name' => 'test', 'sharing' => 'system'], + '/projects/5/versions.xml', + 'testsystem', + 201, + '', + ], + 'test with empty sharing parameter null' => [ + 5, + ['name' => 'test', 'sharing' => null], + '/projects/5/versions.xml', + 'test', + 201, + '', + ], + 'test with empty sharing parameter false' => [ + 5, + ['name' => 'test', 'sharing' => false], + '/projects/5/versions.xml', + 'test', + 201, + '', + ], + 'test with empty sharing parameter empty string' => [ + 5, + ['name' => 'test', 'sharing' => ''], + '/projects/5/versions.xml', + 'test', + 201, + '', + ], + ]; + } + + public function testCreateReturnsEmptyString() + { + $client = AssertingHttpClient::create( + $this, + [ + 'POST', + '/projects/5/versions.xml', + 'application/xml', + 'test', + 500, + '', + '' + ] + ); + + // Create the object under test + $api = new Version($client); + + // Perform the tests + $return = $api->create(5, ['name' => 'test']); + + $this->assertSame('', $return); + } + + public function testCreateThrowsExceptionWithEmptyParameters() + { + // Create the used mock objects + $client = $this->createMock(HttpClient::class); + + // Create the object under test + $api = new Version($client); + + $this->expectException(MissingParameterException::class); + $this->expectExceptionMessage('Theses parameters are mandatory: `name`'); + + // Perform the tests + $api->create(5); + } + + public function testCreateThrowsExceptionWithMissingNameInParameters() + { + // Test values + $parameters = [ + 'description' => 'Test version description', + ]; + + // Create the used mock objects + $client = $this->createMock(HttpClient::class); + + // Create the object under test + $api = new Version($client); + + $this->expectException(MissingParameterException::class); + $this->expectExceptionMessage('Theses parameters are mandatory: `name`'); + + // Perform the tests + $api->create(5, $parameters); + } + + public function testCreateThrowsExceptionWithInvalidStatus() + { + // Test values + $parameters = [ + 'name' => 'test', + 'description' => 'Test version description', + 'status' => 'invalid', + ]; + + // Create the used mock objects + $client = $this->createMock(HttpClient::class); + + // Create the object under test + $api = new Version($client); + + $this->expectException(InvalidParameterException::class); + $this->expectExceptionMessage('Possible values for status : open, locked, closed'); + + // Perform the tests + $api->create('test', $parameters); + } +} diff --git a/tests/Unit/Api/VersionTest.php b/tests/Unit/Api/VersionTest.php index 39ce5fde..cc276b9f 100644 --- a/tests/Unit/Api/VersionTest.php +++ b/tests/Unit/Api/VersionTest.php @@ -6,7 +6,6 @@ use Redmine\Api\Version; use Redmine\Client\Client; use Redmine\Exception\InvalidParameterException; -use Redmine\Exception\MissingParameterException; use Redmine\Tests\Fixtures\MockClient; /** @@ -180,165 +179,6 @@ public function testRemoveWithStringCallsDelete() $this->assertSame($response, $api->remove(5)); } - /** - * Test create(). - * - * @covers ::create - * - * @test - */ - public function testCreateThrowsExceptionWithEmptyParameters() - { - // Create the used mock objects - $client = $this->createMock(Client::class); - - // Create the object under test - $api = new Version($client); - - $this->expectException(MissingParameterException::class); - $this->expectExceptionMessage('Theses parameters are mandatory: `name`'); - - // Perform the tests - $api->create(5); - } - - /** - * Test create(). - * - * @covers ::create - * - * @test - */ - public function testCreateThrowsExceptionWithMissingNameInParameters() - { - // Test values - $parameters = [ - 'description' => 'Test version description', - ]; - - // Create the used mock objects - $client = $this->createMock(Client::class); - - // Create the object under test - $api = new Version($client); - - $this->expectException(MissingParameterException::class); - $this->expectExceptionMessage('Theses parameters are mandatory: `name`'); - - // Perform the tests - $api->create(5, $parameters); - } - - /** - * Test create(). - * - * @covers ::create - * @covers ::validateStatus - * - * @test - */ - public function testCreateThrowsExceptionWithInvalidStatus() - { - // Test values - $parameters = [ - 'description' => 'Test version description', - 'status' => 'invalid', - ]; - - // Create the used mock objects - $client = $this->createMock(Client::class); - - // Create the object under test - $api = new Version($client); - - $this->expectException(MissingParameterException::class); - $this->expectExceptionMessage('Theses parameters are mandatory: `name`'); - - // Perform the tests - $api->create('test', $parameters); - } - - /** - * Test create(). - * - * @covers ::create - * @covers ::post - * @test - */ - public function testCreateCallsPost() - { - // Test values - $response = 'API Response'; - $parameters = [ - 'name' => 'Test version', - ]; - - // Create the used mock objects - $client = $this->createMock(Client::class); - $client->expects($this->once()) - ->method('requestPost') - ->with( - '/projects/5/versions.xml', - $this->logicalAnd( - $this->stringStartsWith('' . "\n" . ''), - $this->stringEndsWith('' . "\n"), - $this->stringContains('Test version') - ) - ) - ->willReturn(true); - $client->expects($this->once()) - ->method('getLastResponseBody') - ->willReturn($response); - - // Create the object under test - $api = new Version($client); - - // Perform the tests - $this->assertSame($response, $api->create(5, $parameters)); - } - - /** - * Test create(). - * - * @covers ::create - * @covers ::post - * @covers ::validateStatus - * @test - */ - public function testCreateWithValidStatusCallsPost() - { - // Test values - $response = 'API Response'; - $parameters = [ - 'name' => 'Test version', - 'status' => 'locked', - ]; - - // Create the used mock objects - $client = $this->createMock(Client::class); - $client->expects($this->once()) - ->method('requestPost') - ->with( - '/projects/5/versions.xml', - $this->logicalAnd( - $this->stringStartsWith('' . "\n" . ''), - $this->stringEndsWith('' . "\n"), - $this->stringContains('Test version'), - $this->stringContains('locked') - ) - ) - ->willReturn(true); - $client->expects($this->once()) - ->method('getLastResponseBody') - ->willReturn($response); - - // Create the object under test - $api = new Version($client); - - // Perform the tests - $this->assertSame($response, $api->create(5, $parameters)); - } - /** * Test update(). * @@ -625,97 +465,6 @@ public function testGetIdByNameMakesGetRequest() $this->assertSame(5, $api->getIdByName(5, 'Version 5')); } - /** - * Test validateSharing(). - * - * @covers ::create - * @covers ::validateSharing - * @dataProvider validSharingProvider - * @test - * - * @param string $sharingValue - * @param string $sharingXmlElement - */ - public function testCreateWithValidSharing($sharingValue, $sharingXmlElement) - { - // Test values - $response = 'API Response'; - $parameters = [ - 'name' => 'Test version', - 'sharing' => $sharingValue, - ]; - - // Create the used mock objects - $client = $this->createMock(Client::class); - $client->expects($this->once()) - ->method('requestPost') - ->with( - '/projects/test/versions.xml', - $this->logicalAnd( - $this->stringStartsWith('' . "\n" . ''), - $this->stringEndsWith('' . "\n"), - $this->stringContains('Test version'), - $this->stringContains($sharingXmlElement) - ) - ) - ->willReturn(true); - $client->expects($this->once()) - ->method('getLastResponseBody') - ->willReturn($response); - - // Create the object under test - $api = new Version($client); - - // Perform the tests - $this->assertSame($response, $api->create('test', $parameters)); - } - - /** - * Test validateSharing(). - * - * @covers ::create - * @covers ::validateSharing - * @dataProvider validEmptySharingProvider - * @test - * - * @param string $sharingValue - */ - public function testCreateWithValidEmptySharing($sharingValue) - { - // Test values - $response = 'API Response'; - $parameters = [ - 'name' => 'Test version', - 'sharing' => $sharingValue, - ]; - - // Create the used mock objects - $client = $this->createMock(Client::class); - $client->expects($this->once()) - ->method('requestPost') - ->with( - '/projects/test/versions.xml', - $this->logicalAnd( - $this->stringStartsWith('' . "\n" . ''), - $this->stringEndsWith('' . "\n"), - $this->stringContains('Test version'), - $this->logicalNot( - $this->stringContains('willReturn(true); - $client->expects($this->once()) - ->method('getLastResponseBody') - ->willReturn($response); - - // Create the object under test - $api = new Version($client); - - // Perform the tests - $this->assertSame($response, $api->create('test', $parameters)); - } - /** * Test validateSharing(). * diff --git a/tests/Unit/Client/NativeCurlClient/RequestTest.php b/tests/Unit/Client/NativeCurlClient/RequestTest.php index b1cf4fe5..ccdc05c4 100644 --- a/tests/Unit/Client/NativeCurlClient/RequestTest.php +++ b/tests/Unit/Client/NativeCurlClient/RequestTest.php @@ -12,6 +12,7 @@ /** * @covers \Redmine\Client\NativeCurlClient::request * @covers \Redmine\Client\NativeCurlClient::runRequest + * @covers \Redmine\Client\NativeCurlClient::createCurl * @covers \Redmine\Client\NativeCurlClient::createHttpHeader */ class RequestTest extends TestCase @@ -92,4 +93,63 @@ public static function getRequestReponseData(): array ['DELETE', '', 500, 'text/plain', 'Internal Server Error'], ]; } + + public function testRequestWithUploadAndFilepathReturnsCorrectResponse() + { + $namespace = 'Redmine\Client'; + + $curl = $this->createMock(stdClass::class); + + $curlInit = $this->getFunctionMock($namespace, 'curl_init'); + $curlInit->expects($this->exactly(1))->willReturn($curl); + + $curlExec = $this->getFunctionMock($namespace, 'curl_exec'); + $curlExec->expects($this->exactly(1))->willReturn('{"upload":{}}'); + + $curlSetoptArray = $this->getFunctionMock($namespace, 'curl_setopt_array'); + + $curlGetinfo = $this->getFunctionMock($namespace, 'curl_getinfo'); + $curlGetinfo->expects($this->exactly(2))->will($this->returnValueMap(([ + [$curl, CURLINFO_HTTP_CODE, 201], + [$curl, CURLINFO_CONTENT_TYPE, 'application/json'], + ]))); + + $curlErrno = $this->getFunctionMock($namespace, 'curl_errno'); + $curlErrno->expects($this->exactly(1))->willReturn(CURLE_OK); + + $curlClose = $this->getFunctionMock($namespace, 'curl_close'); + + $client = new NativeCurlClient( + 'http://test.local', + 'access_token' + ); + + // PHPUnit 10 compatible way to test trigger_error(). + set_error_handler( + function ($errno, $errstr): bool { + $this->assertSame( + 'Uploading an attachment by filepath is deprecated since v2.1.0, use file_get_contents() to upload the file content instead.', + $errstr + ); + + restore_error_handler(); + return true; + }, + E_USER_DEPRECATED + ); + + $request = $this->createConfiguredMock(Request::class, [ + 'getMethod' => 'POST', + 'getPath' => '/uploads.json', + 'getContentType' => 'application/octet-stream', + 'getContent' => realpath(__DIR__ . '/../../../Fixtures/testfile_01.txt'), + ]); + + $response = $client->request($request); + + $this->assertInstanceOf(Response::class, $response); + $this->assertSame(201, $response->getStatusCode()); + $this->assertSame('application/json', $response->getContentType()); + $this->assertSame('{"upload":{}}', $response->getContent()); + } } diff --git a/tests/Unit/Exception/ClientExceptionTest.php b/tests/Unit/Exception/ClientExceptionTest.php index c0195534..200e0941 100644 --- a/tests/Unit/Exception/ClientExceptionTest.php +++ b/tests/Unit/Exception/ClientExceptionTest.php @@ -7,11 +7,11 @@ use Redmine\Exception as RedmineException; use Redmine\Exception\ClientException; +/** + * @coversDefaultClass \Redmine\Exception\ClientException + */ class ClientExceptionTest extends TestCase { - /** - * @test - */ public function testClientException() { $exception = new ClientException(); diff --git a/tests/Unit/Exception/InvalidApiNameExceptionTest.php b/tests/Unit/Exception/InvalidApiNameExceptionTest.php index 838aade8..9078d8a3 100644 --- a/tests/Unit/Exception/InvalidApiNameExceptionTest.php +++ b/tests/Unit/Exception/InvalidApiNameExceptionTest.php @@ -7,11 +7,11 @@ use Redmine\Exception as RedmineException; use Redmine\Exception\InvalidApiNameException; +/** + * @coversDefaultClass \Redmine\Exception\InvalidApiNameException + */ class InvalidApiNameExceptionTest extends TestCase { - /** - * @test - */ public function testInvalidApiNameException() { $exception = new InvalidApiNameException(); diff --git a/tests/Unit/Http/HttpFactory/MakeXmlRequestTest.php b/tests/Unit/Http/HttpFactory/MakeXmlRequestTest.php new file mode 100644 index 00000000..5a0bf923 --- /dev/null +++ b/tests/Unit/Http/HttpFactory/MakeXmlRequestTest.php @@ -0,0 +1,26 @@ +assertInstanceOf(Request::class, $response); + $this->assertSame('GET', $response->getMethod()); + $this->assertSame('path.xml', $response->getPath()); + $this->assertSame('application/xml', $response->getContentType()); + $this->assertSame('content', $response->getContent()); + } +} diff --git a/tests/Unit/Serializer/JsonSerializerTest.php b/tests/Unit/Serializer/JsonSerializerTest.php index de9a2307..afed796d 100644 --- a/tests/Unit/Serializer/JsonSerializerTest.php +++ b/tests/Unit/Serializer/JsonSerializerTest.php @@ -8,6 +8,9 @@ use Redmine\Exception\SerializerException; use Redmine\Serializer\JsonSerializer; +/** + * @coversDefaultClass \Redmine\Serializer\JsonSerializer + */ class JsonSerializerTest extends TestCase { public static function getEncodedToNormalizedData(): array @@ -180,8 +183,6 @@ public static function getNormalizedToEncodedData(): array } /** - * @covers \Redmine\Serializer\JsonSerializer::getEncoded - * @covers \Redmine\Serializer\JsonSerializer::__toString * @test * * @dataProvider getNormalizedToEncodedData diff --git a/tests/Unit/Serializer/PathSerializerTest.php b/tests/Unit/Serializer/PathSerializerTest.php index 439a4e84..35a8499a 100644 --- a/tests/Unit/Serializer/PathSerializerTest.php +++ b/tests/Unit/Serializer/PathSerializerTest.php @@ -7,6 +7,9 @@ use PHPUnit\Framework\TestCase; use Redmine\Serializer\PathSerializer; +/** + * @coversDefaultClass \Redmine\Serializer\PathSerializer + */ class PathSerializerTest extends TestCase { public static function getPathData(): array @@ -48,8 +51,6 @@ public static function getPathData(): array } /** - * @covers \Redmine\Serializer\PathSerializer::getPath - * @covers \Redmine\Serializer\PathSerializer::__toString * @test * * @dataProvider getPathData diff --git a/tests/Unit/Serializer/XmlSerializerTest.php b/tests/Unit/Serializer/XmlSerializerTest.php index aefa4296..170e456c 100644 --- a/tests/Unit/Serializer/XmlSerializerTest.php +++ b/tests/Unit/Serializer/XmlSerializerTest.php @@ -8,28 +8,31 @@ use Redmine\Exception\SerializerException; use Redmine\Serializer\XmlSerializer; +/** + * @coversDefaultClass \Redmine\Serializer\XmlSerializer + */ class XmlSerializerTest extends TestCase { public static function getEncodedToNormalizedData(): array { return [ - [ + 'test with single tag' => [ '', [], ], - [ + 'test with closed tag' => [ '', [], ], - [ + 'test with open and close tag' => [ '', [], ], - [ + 'test with integer' => [ '1', ['1'], ], - [ + 'test with array' => [ <<< XML @@ -107,13 +110,22 @@ public function createFromStringWithInvalidStringThrowsException(string $message $this->expectException(SerializerException::class); $this->expectExceptionMessage($message); - $serializer = XmlSerializer::createFromString($data); + XmlSerializer::createFromString($data); } public static function getNormalizedToEncodedData(): array { return [ - [ + 'test with simple string' => [ + [ + 'key' => 'value', + ], + <<< XML + + value + XML, + ], + 'test with simple array' => [ [ 'issue' => [ 'project_id' => 1, @@ -124,13 +136,13 @@ public static function getNormalizedToEncodedData(): array <<< XML - 1 - Example - 4 + 1 + Example + 4 XML, ], - [ + 'test with ignored elements' => [ [ 'issue' => [ 'project_id' => 1, @@ -144,13 +156,41 @@ public static function getNormalizedToEncodedData(): array <<< XML - 1 - Example - 4 + 1 + Example + 4 XML, ], - [ + 'test with custom fields with single value' => [ + [ + 'project' => [ + 'name' => 'some name', + 'identifier' => 'the_identifier', + 'custom_fields' => [ + [ + 'id' => 123, + 'name' => 'cf_name', + 'field_format' => 'string', + 'value' => 1, + ], + ], + ], + ], + <<< XML + + + some name + the_identifier + + + 1 + + + + XML, + ], + 'test with custom fields with array value' => [ [ 'project' => [ 'name' => 'some name', @@ -168,26 +208,51 @@ public static function getNormalizedToEncodedData(): array <<< XML - some name - the_identifier - - - - 1 - 2 - 3 - - - + some name + the_identifier + + + + 1 + 2 + 3 + + + XML, ], + 'test with uploads' => [ + [ + 'issue' => [ + 'uploads' => [ + [ + 'token' => 'asdfasdfasdfasdf', + 'filename' => 'MyFile.pdf', + 'description' => 'MyFile is better then YourFile...', + 'content_type' => 'application/pdf', + ], + ], + ], + ], + <<< XML + + + + + asdfasdfasdfasdf + MyFile.pdf + MyFile is better then YourFile... + application/pdf + + + + XML, + ], ]; } /** - * @covers \Redmine\Serializer\XmlSerializer::getEncoded - * @covers \Redmine\Serializer\XmlSerializer::__toString * @test * * @dataProvider getNormalizedToEncodedData @@ -219,6 +284,6 @@ public function createFromArrayWithInvalidDataThrowsException(string $message, a $this->expectException(SerializerException::class); $this->expectExceptionMessage($message); - $serializer = XmlSerializer::createFromArray($data); + XmlSerializer::createFromArray($data); } }