Skip to content

Add IssueCategory::listNamesByProject() method as replacement for IssueCategory::listing() #406

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Jun 28, 2024
37 changes: 37 additions & 0 deletions src/Redmine/Api/IssueCategory.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ class IssueCategory extends AbstractApi
{
private $issueCategories = [];

private $issueCategoriesNames = [];

/**
* List issue categories for a given project.
*
Expand Down Expand Up @@ -53,6 +55,41 @@ final public function listByProject($projectIdentifier, array $params = []): arr
}
}

/**
* Returns an array of all issue categories by a project with id/name pairs.
*
* @param string|int $projectIdentifier project id or literal identifier
*
* @throws InvalidParameterException if $projectIdentifier is not of type int or string
*
* @return array<int,string> list of issue category names (id => name)
*/
final public function listNamesByProject($projectIdentifier): array
{
if (! is_int($projectIdentifier) && ! is_string($projectIdentifier)) {
throw new InvalidParameterException(sprintf(
'%s(): Argument #1 ($projectIdentifier) must be of type int or string',
__METHOD__,
));
}

if (array_key_exists($projectIdentifier, $this->issueCategoriesNames)) {
return $this->issueCategoriesNames[$projectIdentifier];
}

$this->issueCategoriesNames[$projectIdentifier] = [];

$list = $this->listByProject($projectIdentifier);

if (array_key_exists('issue_categories', $list)) {
foreach ($list['issue_categories'] as $category) {
$this->issueCategoriesNames[$projectIdentifier][(int) $category['id']] = $category['name'];
}
}

return $this->issueCategoriesNames[$projectIdentifier];
}

/**
* List issue categories.
*
Expand Down
28 changes: 28 additions & 0 deletions tests/Behat/Bootstrap/IssueCategoryContextTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,34 @@ public function iCreateAnIssueCategoryForProjectIdentifierAndWithTheFollowingDat
);
}

/**
* @When I list all issue categories for project identifier :identifier
*/
public function iListAllIssueCategoriesForProjectIdentifier($identifier)
{
/** @var IssueCategory */
$api = $this->getNativeCurlClient()->getApi('issue_category');

$this->registerClientResponse(
$api->listByProject($identifier),
$api->getLastResponse(),
);
}

/**
* @When I list all issue category names for project identifier :identifier
*/
public function iListAllIssueCategoryNamesForProjectIdentifier($identifier)
{
/** @var IssueCategory */
$api = $this->getNativeCurlClient()->getApi('issue_category');

$this->registerClientResponse(
$api->listNamesByProject($identifier),
$api->getLastResponse(),
);
}

/**
* @When I update the issue category with id :id and the following data
*/
Expand Down
20 changes: 10 additions & 10 deletions tests/Behat/features/groups.feature
Original file line number Diff line number Diff line change
Expand Up @@ -60,23 +60,23 @@ Feature: Interacting with the REST API for groups
@group
Scenario: Listing names of all groups
Given I have a "NativeCurlClient" client
And I create a group with name "Test Group 1"
And I create a group with name "Test Group 2"
And I create a group with name "Test Group 3"
And I create a group with name "Test Group 4"
And I create a group with name "Test Group 5"
And I create a group with name "Test Group D"
And I create a group with name "Test Group E"
And I create a group with name "Test Group C"
And I create a group with name "Test Group B"
And I create a group with name "Test Group A"
When I list the names of all groups
Then the response has the status code "200"
And the response has the content type "application/json"
And the returned data is an array
And the returned data contains "5" items
And the returned data contains the following data
| property | value |
| 4 | Test Group 1 |
| 5 | Test Group 2 |
| 6 | Test Group 3 |
| 7 | Test Group 4 |
| 8 | Test Group 5 |
| 4 | Test Group D |
| 5 | Test Group E |
| 6 | Test Group C |
| 7 | Test Group B |
| 8 | Test Group A |

@group
Scenario: Showing a specific group
Expand Down
86 changes: 86 additions & 0 deletions tests/Behat/features/issue_category.feature
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,92 @@ Feature: Interacting with the REST API for issue categories
| id | 1 |
| name | Redmine Admin |

Scenario: Listing of zero issue categories
Given I have a "NativeCurlClient" client
And I create a project with name "Test Project" and identifier "test-project"
When I list all issue categories for project identifier "test-project"
Then the response has the status code "200"
And the response has the content type "application/json"
And the returned data has only the following properties
"""
issue_categories
total_count
"""
And the returned data contains the following data
| property | value |
| issue_categories | [] |
| total_count | 0 |

Scenario: Listing of multiple issue categories
Given I have a "NativeCurlClient" client
And I create a project with name "Test Project" and identifier "test-project"
And I create an issue category for project identifier "test-project" and with the following data
| property | value |
| name | Category name B |
And I create an issue category for project identifier "test-project" and with the following data
| property | value |
| name | Category name A |
When I list all issue categories for project identifier "test-project"
Then the response has the status code "200"
And the response has the content type "application/json"
And the returned data has only the following properties
"""
issue_categories
total_count
"""
And the returned data contains the following data
| property | value |
| total_count | 2 |
And the returned data "issue_categories" property is an array
And the returned data "issue_categories" property contains "2" items
And the returned data "issue_categories.0" property is an array
And the returned data "issue_categories.0" property has only the following properties
"""
id
project
name
"""
And the returned data "issue_categories.0" property contains the following data
| property | value |
| id | 2 |
| name | Category name A |
And the returned data "issue_categories.0.project" property contains the following data
| property | value |
| id | 1 |
| name | Test Project |
And the returned data "issue_categories.1" property is an array
And the returned data "issue_categories.1" property has only the following properties
"""
id
project
name
"""
And the returned data "issue_categories.1" property contains the following data
| property | value |
| id | 1 |
| name | Category name B |
And the returned data "issue_categories.1.project" property contains the following data
| property | value |
| id | 1 |
| name | Test Project |

Scenario: Listing of multiple issue category names
Given I have a "NativeCurlClient" client
And I create a project with name "Test Project" and identifier "test-project"
And I create an issue category for project identifier "test-project" and with the following data
| property | value |
| name | Category name B |
And I create an issue category for project identifier "test-project" and with the following data
| property | value |
| name | Category name A |
When I list all issue category names for project identifier "test-project"
Then the response has the status code "200"
And the response has the content type "application/json"
And the returned data contains the following data
| property | value |
| 1 | Category name B |
| 2 | Category name A |

@issue_category
Scenario: Updating an issue category with all data
Given I have a "NativeCurlClient" client
Expand Down
8 changes: 4 additions & 4 deletions tests/Unit/Api/Group/ListNamesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,16 +58,16 @@ public static function getListNamesData(): array
<<<JSON
{
"groups": [
{"id": 9, "name": "Group 1"},
{"id": 7, "name": "Group 3"},
{"id": 8, "name": "Group 2"},
{"id": 7, "name": "Group 3"}
{"id": 9, "name": "Group 1"}
]
}
JSON,
[
9 => "Group 1",
8 => "Group 2",
7 => "Group 3",
8 => "Group 2",
9 => "Group 1",
],
],
];
Expand Down
139 changes: 139 additions & 0 deletions tests/Unit/Api/IssueCategory/ListNamesByProjectTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
<?php

declare(strict_types=1);

namespace Redmine\Tests\Unit\Api\IssueCategory;

use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;
use Redmine\Api\IssueCategory;
use Redmine\Exception\InvalidParameterException;
use Redmine\Http\HttpClient;
use Redmine\Tests\Fixtures\AssertingHttpClient;
use stdClass;

#[CoversClass(IssueCategory::class)]
class ListNamesByProjectTest extends TestCase
{
/**
* @dataProvider getListNamesByProjectData
*/
#[DataProvider('getListNamesByProjectData')]
public function testListNamesByProjectReturnsCorrectResponse($projectIdentifier, $expectedPath, $responseCode, $response, $expectedResponse)
{
$client = AssertingHttpClient::create(
$this,
[
'GET',
$expectedPath,
'application/json',
'',
$responseCode,
'application/json',
$response,
],
);

// Create the object under test
$api = new IssueCategory($client);

// Perform the tests
$this->assertSame($expectedResponse, $api->listNamesByProject($projectIdentifier));
}

public static function getListNamesByProjectData(): array
{
return [
'test without issue categories' => [
5,
'/projects/5/issue_categories.json',
201,
<<<JSON
{
"issue_categories": []
}
JSON,
[],
],
'test with multiple categories' => [
'test-project',
'/projects/test-project/issue_categories.json',
201,
<<<JSON
{
"issue_categories": [
{"id": 9, "name": "IssueCategory 1"},
{"id": 8, "name": "IssueCategory 2"},
{"id": 7, "name": "IssueCategory 3"}
]
}
JSON,
[
9 => "IssueCategory 1",
8 => "IssueCategory 2",
7 => "IssueCategory 3",
],
],
];
}

public function testListNamesByProjectCallsHttpClientOnlyOnce()
{
$client = AssertingHttpClient::create(
$this,
[
'GET',
'/projects/5/issue_categories.json',
'application/json',
'',
200,
'application/json',
<<<JSON
{
"issue_categories": [
{
"id": 1,
"name": "IssueCategory 1"
}
]
}
JSON,
],
);

// Create the object under test
$api = new IssueCategory($client);

// Perform the tests
$this->assertSame([1 => 'IssueCategory 1'], $api->listNamesByProject(5));
$this->assertSame([1 => 'IssueCategory 1'], $api->listNamesByProject(5));
$this->assertSame([1 => 'IssueCategory 1'], $api->listNamesByProject(5));
}

/**
* @dataProvider getInvalidProjectIdentifiers
*/
#[DataProvider('getInvalidProjectIdentifiers')]
public function testListNamesByProjectWithWrongProjectIdentifierThrowsException($projectIdentifier)
{
$api = new IssueCategory($this->createMock(HttpClient::class));

$this->expectException(InvalidParameterException::class);
$this->expectExceptionMessage('Redmine\Api\IssueCategory::listNamesByProject(): Argument #1 ($projectIdentifier) must be of type int or string');

$api->listNamesByProject($projectIdentifier);
}

public static function getInvalidProjectIdentifiers(): array
{
return [
'null' => [null],
'true' => [true],
'false' => [false],
'float' => [0.0],
'array' => [[]],
'object' => [new stdClass()],
];
}
}
Loading