Skip to content

Commit 96a3247

Browse files
committed
feat: create Query::fromHttpClient() and use it in tests
1 parent 659ed35 commit 96a3247

File tree

5 files changed

+166
-73
lines changed

5 files changed

+166
-73
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2020
- New method `Redmine\Api\Membership::fromHttpClient()` for creating the class.
2121
- New method `Redmine\Api\News::fromHttpClient()` for creating the class.
2222
- New method `Redmine\Api\Project::fromHttpClient()` for creating the class.
23+
- New method `Redmine\Api\Query::fromHttpClient()` for creating the class.
2324
- Add support for PHP 8.5
2425
- Add support for Redmine 6.1.
2526

@@ -51,6 +52,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
5152
- Extending `Redmine\Api\News` is deprecated and will be set to final in future, create a wrapper class instead.
5253
- `Redmine\Api\Project::__construct()` is deprecated and will be set to private in future, use `\Redmine\Api\Project::fromHttpClient()` instead.
5354
- Extending `Redmine\Api\Project` is deprecated and will be set to final in future, create a wrapper class instead.
55+
- `Redmine\Api\Query::__construct()` is deprecated and will be set to private in future, use `\Redmine\Api\Query::fromHttpClient()` instead.
56+
- Extending `Redmine\Api\Query` is deprecated and will be set to final in future, create a wrapper class instead.
5457

5558
### Removed
5659

src/Redmine/Api/Query.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22

33
namespace Redmine\Api;
44

5+
use Redmine\Client\Client;
56
use Redmine\Exception;
67
use Redmine\Exception\SerializerException;
78
use Redmine\Exception\UnexpectedResponseException;
9+
use Redmine\Http\HttpClient;
810

911
/**
1012
* Custom queries retrieval.
@@ -15,6 +17,37 @@
1517
*/
1618
class Query extends AbstractApi
1719
{
20+
final public static function fromHttpClient(HttpClient $httpClient): self
21+
{
22+
return new self($httpClient, true);
23+
}
24+
25+
/**
26+
* @deprecated v2.9.0 Use fromHttpClient() instead.
27+
* @see Query::fromHttpClient()
28+
*
29+
* @param Client|HttpClient $client
30+
*/
31+
public function __construct($client/*, bool $privatelyCalled = false*/)
32+
{
33+
$privatelyCalled = (func_num_args() > 1) ? func_get_arg(1) : false;
34+
35+
if ($privatelyCalled === true) {
36+
parent::__construct($client);
37+
38+
return;
39+
}
40+
41+
if (static::class !== self::class) {
42+
$className = (new \ReflectionClass($this))->isAnonymous() ? '' : ' in `' . static::class . '`';
43+
@trigger_error('Class `' . self::class . '` will declared as final in v3.0.0, stop extending it' . $className . '.', E_USER_DEPRECATED);
44+
} else {
45+
@trigger_error('Method `' . __METHOD__ . '()` is deprecated since v2.9.0 and will declared as private in v3.0.0, use `' . self::class . '::fromHttpClient()` instead.', E_USER_DEPRECATED);
46+
}
47+
48+
parent::__construct($client);
49+
}
50+
1851
/**
1952
* Returns the list of all custom queries visible by the user (public and private queries) for all projects.
2053
*
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
namespace Redmine\Tests\Unit\Api\Query;
4+
5+
use PHPUnit\Framework\Attributes\CoversClass;
6+
use PHPUnit\Framework\TestCase;
7+
use Redmine\Api\Query;
8+
use Redmine\Http\HttpClient;
9+
10+
#[CoversClass(Query::class)]
11+
class FromHttpClientTest extends TestCase
12+
{
13+
public function testReturnsCorrectObject(): void
14+
{
15+
$httpClient = $this->createStub(HttpClient::class);
16+
17+
$api = Query::fromHttpClient($httpClient);
18+
19+
$this->assertInstanceOf(Query::class, $api);
20+
}
21+
}

tests/Unit/Api/Query/ListTest.php

Lines changed: 42 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use Redmine\Api\Query;
88
use Redmine\Client\Client;
99
use Redmine\Exception\UnexpectedResponseException;
10+
use Redmine\Tests\Fixtures\AssertingHttpClient;
1011

1112
#[CoversClass(Query::class)]
1213
class ListTest extends TestCase
@@ -17,21 +18,21 @@ public function testListWithoutParametersReturnsResponse(): void
1718
$response = '["API Response"]';
1819
$expectedReturn = ['API Response'];
1920

20-
// Create the used mock objects
21-
$client = $this->createMock(Client::class);
22-
$client->expects($this->once())
23-
->method('requestGet')
24-
->with('/queries.json')
25-
->willReturn(true);
26-
$client->expects($this->exactly(1))
27-
->method('getLastResponseBody')
28-
->willReturn($response);
29-
$client->expects($this->exactly(1))
30-
->method('getLastResponseContentType')
31-
->willReturn('application/json');
21+
$client = AssertingHttpClient::create(
22+
$this,
23+
[
24+
'GET',
25+
'/queries.json',
26+
'application/json',
27+
'',
28+
200,
29+
'application/json',
30+
$response,
31+
],
32+
);
3233

3334
// Create the object under test
34-
$api = new Query($client);
35+
$api = Query::fromHttpClient($client);
3536

3637
// Perform the tests
3738
$this->assertSame($expectedReturn, $api->list());
@@ -44,43 +45,45 @@ public function testListWithParametersReturnsResponse(): void
4445
$response = '["API Response"]';
4546
$expectedReturn = ['API Response'];
4647

47-
// Create the used mock objects
48-
$client = $this->createMock(Client::class);
49-
$client->expects($this->any())
50-
->method('requestGet')
51-
->with('/queries.json?limit=25&offset=0&0=not-used')
52-
->willReturn(true);
53-
$client->expects($this->exactly(1))
54-
->method('getLastResponseBody')
55-
->willReturn($response);
56-
$client->expects($this->exactly(1))
57-
->method('getLastResponseContentType')
58-
->willReturn('application/json');
48+
$client = AssertingHttpClient::create(
49+
$this,
50+
[
51+
'GET',
52+
'/queries.json?limit=25&offset=0&0=not-used',
53+
'application/json',
54+
'',
55+
200,
56+
'application/json',
57+
$response,
58+
],
59+
);
5960

6061
// Create the object under test
61-
$api = new Query($client);
62+
$api = Query::fromHttpClient($client);
6263

6364
// Perform the tests
6465
$this->assertSame($expectedReturn, $api->list($parameters));
6566
}
6667

6768
public function testListThrowsException(): void
6869
{
69-
// Create the used mock objects
70-
$client = $this->createMock(Client::class);
71-
$client->expects($this->exactly(1))
72-
->method('requestGet')
73-
->with('/queries.json')
74-
->willReturn(true);
75-
$client->expects($this->exactly(1))
76-
->method('getLastResponseBody')
77-
->willReturn('');
78-
$client->expects($this->exactly(1))
79-
->method('getLastResponseContentType')
80-
->willReturn('application/json');
70+
$response = '';
71+
72+
$client = AssertingHttpClient::create(
73+
$this,
74+
[
75+
'GET',
76+
'/queries.json',
77+
'application/json',
78+
'',
79+
200,
80+
'application/json',
81+
$response,
82+
],
83+
);
8184

8285
// Create the object under test
83-
$api = new Query($client);
86+
$api = Query::fromHttpClient($client);
8487

8588
$this->expectException(UnexpectedResponseException::class);
8689
$this->expectExceptionMessage('The Redmine server replied with an unexpected response.');

tests/Unit/Api/QueryTest.php

Lines changed: 67 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,59 @@
66
use PHPUnit\Framework\Attributes\DataProvider;
77
use PHPUnit\Framework\TestCase;
88
use Redmine\Api\Query;
9-
use Redmine\Client\Client;
10-
use Redmine\Tests\Fixtures\MockClient;
9+
use Redmine\Http\HttpClient;
10+
use Redmine\Tests\Fixtures\AssertingHttpClient;
1111

1212
/**
1313
* @author Malte Gerth <mail@malte-gerth.de>
1414
*/
1515
#[CoversClass(Query::class)]
1616
class QueryTest extends TestCase
1717
{
18+
public function testExtendingTheClassTriggersDeprecationWarning(): void
19+
{
20+
// PHPUnit 10 compatible way to test trigger_error().
21+
set_error_handler(
22+
function ($errno, $errstr): bool {
23+
$this->assertSame(
24+
'Class `Redmine\Api\Query` will declared as final in v3.0.0, stop extending it.',
25+
$errstr,
26+
);
27+
28+
restore_error_handler();
29+
return true;
30+
},
31+
E_USER_DEPRECATED,
32+
);
33+
34+
new class ($this->createStub(HttpClient::class)) extends Query {};
35+
}
36+
37+
public function testConstructorTriggersDeprecationWarning(): void
38+
{
39+
// PHPUnit 10 compatible way to test trigger_error().
40+
set_error_handler(
41+
function ($errno, $errstr): bool {
42+
$this->assertSame(
43+
'Method `Redmine\Api\Query::__construct()` is deprecated since v2.9.0 and will declared as private in v3.0.0, use `Redmine\Api\Query::fromHttpClient()` instead.',
44+
$errstr,
45+
);
46+
47+
restore_error_handler();
48+
return true;
49+
},
50+
E_USER_DEPRECATED,
51+
);
52+
53+
new Query($this->createStub(HttpClient::class));
54+
}
55+
1856
/**
1957
* Test all().
2058
*/
2159
public function testAllTriggersDeprecationWarning(): void
2260
{
23-
$api = new Query(MockClient::create());
61+
$api = Query::fromHttpClient($this->createStub(HttpClient::class));
2462

2563
// PHPUnit 10 compatible way to test trigger_error().
2664
set_error_handler(
@@ -47,21 +85,21 @@ function ($errno, $errstr): bool {
4785
#[DataProvider('getAllData')]
4886
public function testAllReturnsClientGetResponse(string $response, string $responseType, $expectedResponse): void
4987
{
50-
// Create the used mock objects
51-
$client = $this->createMock(Client::class);
52-
$client->expects($this->exactly(1))
53-
->method('requestGet')
54-
->with('/queries.json')
55-
->willReturn(true);
56-
$client->expects($this->atLeast(1))
57-
->method('getLastResponseBody')
58-
->willReturn($response);
59-
$client->expects($this->exactly(1))
60-
->method('getLastResponseContentType')
61-
->willReturn($responseType);
88+
$client = AssertingHttpClient::create(
89+
$this,
90+
[
91+
'GET',
92+
'/queries.json',
93+
'application/json',
94+
'',
95+
200,
96+
$responseType,
97+
$response,
98+
],
99+
);
62100

63101
// Create the object under test
64-
$api = new Query($client);
102+
$api = Query::fromHttpClient($client);
65103

66104
// Perform the tests
67105
$this->assertSame($expectedResponse, $api->all());
@@ -86,26 +124,21 @@ public function testAllReturnsClientGetResponseWithParameters(): void
86124
$response = '["API Response"]';
87125
$expectedReturn = ['API Response'];
88126

89-
// Create the used mock objects
90-
$client = $this->createMock(Client::class);
91-
$client->expects($this->any())
92-
->method('requestGet')
93-
->with(
94-
$this->logicalAnd(
95-
$this->stringStartsWith('/queries.json'),
96-
$this->stringContains('not-used'),
97-
),
98-
)
99-
->willReturn(true);
100-
$client->expects($this->exactly(1))
101-
->method('getLastResponseBody')
102-
->willReturn($response);
103-
$client->expects($this->exactly(1))
104-
->method('getLastResponseContentType')
105-
->willReturn('application/json');
127+
$client = AssertingHttpClient::create(
128+
$this,
129+
[
130+
'GET',
131+
'/queries.json?limit=25&offset=0&0=not-used',
132+
'application/json',
133+
'',
134+
200,
135+
'application/json',
136+
$response,
137+
],
138+
);
106139

107140
// Create the object under test
108-
$api = new Query($client);
141+
$api = Query::fromHttpClient($client);
109142

110143
// Perform the tests
111144
$this->assertSame($expectedReturn, $api->all($parameters));

0 commit comments

Comments
 (0)