Skip to content

Commit fb5cbb9

Browse files
committed
Merge branch 'release/3.5.0'
2 parents cdb2d82 + 7b07b71 commit fb5cbb9

File tree

5 files changed

+228
-15
lines changed

5 files changed

+228
-15
lines changed

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,18 @@
33
All notable changes to this project will be documented in this file. This project adheres to
44
[Semantic Versioning](http://semver.org/) and [this changelog format](http://keepachangelog.com/).
55

6+
## [3.5.0] - 2022-01-22
7+
8+
### Added
9+
10+
- [#18](https://github.com/cloudcreativity/json-api-testing/issues/18) The `assertMetaWithoutData` and
11+
`assertExactMetaWithoutData` assertions now assert a successful HTTP status code. Previously they were expecting
12+
`200 OK` though this is too restrictive for a meta-only response. However, the assertions will continue to fail for
13+
`204 No Content` responses because they are expecting the response to have content.
14+
- [#14](https://github.com/cloudcreativity/json-api-testing/issues/14) The expected `Location` header passed to the
15+
`assertCreatedWithClientId` assertion can now include the expected resource id. Previously the expected header value
16+
had to be passed without the id.
17+
618
## [3.4.0] - 2022-01-16
719

820
### Added
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
<?php
2+
/*
3+
* Copyright 2022 Cloud Creativity Limited
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
namespace CloudCreativity\JsonApi\Testing\Constraints;
19+
20+
use CloudCreativity\JsonApi\Testing\Document;
21+
use PHPUnit\Framework\Constraint\Constraint;
22+
23+
/**
24+
* Class HttpStatusIsSuccessful
25+
*
26+
* @package CloudCreativity\JsonApi\Testing
27+
*/
28+
class HttpStatusIsSuccessful extends Constraint
29+
{
30+
31+
/**
32+
* @var string|null
33+
*/
34+
private ?string $content;
35+
36+
/**
37+
* HttpStatusIsSuccessful constructor.
38+
*
39+
* @param string|null $content
40+
*/
41+
public function __construct(string $content = null)
42+
{
43+
$this->content = $content;
44+
}
45+
46+
/**
47+
* @inheritDoc
48+
*/
49+
public function toString(): string
50+
{
51+
return 'successful';
52+
}
53+
54+
/**
55+
* @inheritdoc
56+
*/
57+
protected function failureDescription($other): string
58+
{
59+
if (204 === intval($other)) {
60+
return 'the HTTP status 204 No Content is invalid as there is content.';
61+
}
62+
63+
$message = "the HTTP status {$other} is {$this->toString()}";
64+
$document = Document::create($this->content);
65+
66+
if ($document && $document->has('errors')) {
67+
return $message . ". The response errors were:" . PHP_EOL . $document;
68+
}
69+
70+
return $message;
71+
}
72+
73+
/**
74+
* @param mixed $other
75+
* @return bool
76+
*/
77+
protected function matches($other): bool
78+
{
79+
if (!is_numeric($other)) {
80+
return false;
81+
}
82+
83+
$other = (int) $other;
84+
85+
if (204 === $other && !empty($this->content)) {
86+
return false;
87+
}
88+
89+
return 200 <= $other && 299 >= $other;
90+
}
91+
92+
}

src/HttpAssert.php

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
namespace CloudCreativity\JsonApi\Testing;
1919

2020
use CloudCreativity\JsonApi\Testing\Constraints\HttpStatusIs;
21+
use CloudCreativity\JsonApi\Testing\Constraints\HttpStatusIsSuccessful;
22+
use Illuminate\Support\Str;
2123
use PHPUnit\Framework\Assert as PHPUnitAssert;
2224

2325
/**
@@ -62,6 +64,25 @@ public static function assertStatusCode(
6264
);
6365
}
6466

67+
/**
68+
* Assert that the provided HTTP status is successful.
69+
*
70+
* The HTTP content must be provided for this assertion, because a 204 No Content status
71+
* would not be valid if there is content.
72+
*
73+
* @param int $status
74+
* @param string|null $content
75+
* @param string $message
76+
*/
77+
public static function assertStatusIsSuccessful(int $status, ?string $content, string $message = ''): void
78+
{
79+
PHPUnitAssert::assertThat(
80+
$status,
81+
new HttpStatusIsSuccessful($content),
82+
$message
83+
);
84+
}
85+
6586
/**
6687
* Assert that there is content with the expected media type.
6788
*
@@ -132,6 +153,27 @@ public static function assertJsonApi(
132153
return self::assertContent($contentType, $content, self::JSON_API_MEDIA_TYPE, $message);
133154
}
134155

156+
/**
157+
* Assert a JSON API HTTP message with a successful status.
158+
*
159+
* @param int $status
160+
* @param string $contentType
161+
* @param string|null $content
162+
* @param string $message
163+
* @return Document
164+
*/
165+
public static function assertJsonApiIsSuccessful(
166+
int $status,
167+
string $contentType,
168+
?string $content,
169+
string $message = ''
170+
): Document
171+
{
172+
self::assertStatusIsSuccessful($status, $content, $message);
173+
174+
return self::assertContent($contentType, $content, self::JSON_API_MEDIA_TYPE, $message);
175+
}
176+
135177
/**
136178
* Assert that a resource was fetched.
137179
*
@@ -430,8 +472,11 @@ public static function assertCreatedWithClientId(
430472
PHPUnitAssert::assertNull($location, 'Expecting no Location header.');
431473
} else {
432474
PHPUnitAssert::assertNotNull($location, 'Missing Location header.');
475+
$expectedLocationWithId = Str::endsWith($expectedLocation, '/' . $expectedId) ?
476+
$expectedLocation :
477+
"$expectedLocation/{$expectedId}";
433478
PHPUnitAssert::assertSame(
434-
"$expectedLocation/{$expectedId}",
479+
$expectedLocationWithId,
435480
$location,
436481
$message ?: 'Unexpected Location header.'
437482
);
@@ -536,7 +581,7 @@ public static function assertMetaWithoutData(
536581
string $message = ''
537582
): Document
538583
{
539-
return self::assertJsonApi($status, $contentType, $content, self::STATUS_OK, $message)
584+
return self::assertJsonApiIsSuccessful($status, $contentType, $content, $message)
540585
->assertNotExists('/data', $message ?: 'Data member exists.')
541586
->assertMeta($expected, $strict, $message);
542587
}
@@ -561,7 +606,7 @@ public static function assertExactMetaWithoutData(
561606
string $message = ''
562607
): Document
563608
{
564-
return self::assertJsonApi($status, $contentType, $content, self::STATUS_OK, $message)
609+
return self::assertJsonApiIsSuccessful($status, $contentType, $content, $message)
565610
->assertNotExists('/data', $message ?: 'Data member exists.')
566611
->assertExactMeta($expected, $strict, $message);
567612
}

tests/Assertions/CreatedTest.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,12 +140,22 @@ public function testWithServerIdDataDoesNotMatch(): void
140140
);
141141
}
142142

143+
/**
144+
* Expecting the location to work either with or without the client id in it.
145+
*
146+
* @see https://github.com/cloudcreativity/json-api-testing/issues/14
147+
*/
143148
public function testWithClientId(): void
144149
{
145150
$this->http->assertCreatedWithClientId(
146151
'http://localhost/api/v1/posts',
147152
$this->resource
148153
);
154+
155+
$this->http->assertCreatedWithClientId(
156+
'http://localhost/api/v1/posts/' . $this->resource['id'],
157+
$this->resource
158+
);
149159
}
150160

151161
public function testWithClientIdUnexpectedStatusCode(): void

tests/Assertions/MetaTest.php

Lines changed: 66 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -69,15 +69,35 @@ protected function setUp(): void
6969
);
7070
}
7171

72-
public function testMetaWithoutData(): void
72+
/**
73+
* @return array
74+
*/
75+
public function statusCodeProvider(): array
7376
{
77+
return [
78+
'200' => [200],
79+
'201' => [201],
80+
'202' => [202],
81+
'299' => [299],
82+
];
83+
}
84+
85+
/**
86+
* @param int $status
87+
* @return void
88+
* @dataProvider statusCodeProvider
89+
*/
90+
public function testMetaWithoutData(int $status): void
91+
{
92+
$http = $this->http->withStatusCode($status);
93+
7494
$partial = $this->meta;
7595
unset($partial['baz']);
7696

77-
$this->http->assertMetaWithoutData($this->meta);
78-
$this->http->assertMetaWithoutData($partial);
97+
$http->assertMetaWithoutData($this->meta);
98+
$http->assertMetaWithoutData($partial);
7999

80-
$data = $this->http->withContent(json_encode([
100+
$data = $http->withContent(json_encode([
81101
'data' => $this->post,
82102
'meta' => $this->meta,
83103
]));
@@ -88,19 +108,26 @@ public function testMetaWithoutData(): void
88108
);
89109
}
90110

91-
public function testExactMetaWithoutData(): void
111+
/**
112+
* @param int $status
113+
* @return void
114+
* @dataProvider statusCodeProvider
115+
*/
116+
public function testExactMetaWithoutData(int $status): void
92117
{
118+
$http = $this->http->withStatusCode($status);
119+
93120
$partial = $this->meta;
94121
unset($partial['baz']);
95122

96-
$this->http->assertExactMetaWithoutData($this->meta);
123+
$http->assertExactMetaWithoutData($this->meta);
97124

98125
$this->assertThatItFails(
99126
'member at [/meta] exactly matches',
100-
fn() => $this->http->assertExactMetaWithoutData($partial),
127+
fn() => $http->assertExactMetaWithoutData($partial),
101128
);
102129

103-
$data = $this->http->withContent(json_encode([
130+
$data = $http->withContent(json_encode([
104131
'data' => $this->post,
105132
'meta' => $this->meta,
106133
]));
@@ -111,17 +138,44 @@ public function testExactMetaWithoutData(): void
111138
);
112139
}
113140

114-
public function testInvalidStatusCode(): void
141+
/**
142+
* @return array
143+
*/
144+
public function invalidStatusCodeProvider(): array
115145
{
116-
$http = $this->http->withStatusCode(201);
146+
return [
147+
'100' => [100],
148+
'199' => [199],
149+
// 204 is invalid as the response has content.
150+
'204' => [204, 'HTTP status 204 No Content is invalid as there is content'],
151+
'300' => [300],
152+
'399' => [399],
153+
'400' => [400],
154+
'499' => [499],
155+
'500' => [500],
156+
'599' => [599],
157+
];
158+
}
159+
160+
/**
161+
* @param int $status
162+
* @param string $expected
163+
* @return void
164+
* @dataProvider invalidStatusCodeProvider
165+
*/
166+
public function testInvalidStatusCode(int $status, string $expected = ''): void
167+
{
168+
$expected = $expected ?: "HTTP status {$status} is successful";
169+
170+
$http = $this->http->withStatusCode($status);
117171

118172
$this->assertThatItFails(
119-
'status 201 is 200',
173+
$expected,
120174
fn() => $http->assertMetaWithoutData($this->meta)
121175
);
122176

123177
$this->assertThatItFails(
124-
'status 201 is 200',
178+
$expected,
125179
fn() => $http->assertExactMetaWithoutData($this->meta)
126180
);
127181
}

0 commit comments

Comments
 (0)