Skip to content

Commit 18911bc

Browse files
Merge pull request #11 from martin-helmich/feature/uri-assertions
New assertions for URIs and form-encoded request/response bodies
2 parents 3da10b1 + ae1386e commit 18911bc

11 files changed

+470
-2
lines changed

README.md

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,9 @@ assertThat($request, hasHeaders([
143143

144144
Asserts that the message body matches the constraint `$constraint`. If
145145
`$constraint` is a primitive value, the assertion will pass when the message
146-
body is equal to the constraint. If `$constraint` is an instance of the `PHPUnit\Framework\Constraint\Constraint` class, the constraint will be evaluated as-is.
146+
body is equal to the constraint. If `$constraint` is an instance of the
147+
`PHPUnit\Framework\Constraint\Constraint` class, the constraint will be evaluated
148+
as-is.
147149

148150
##### `assertMessageBodyMatchesJson($message, $jsonConstraints)` / `bodyMatchesJson($jsonConstraints)`
149151

@@ -153,7 +155,14 @@ This actually asserts several facts:
153155
`application/json`
154156
2. The message body must be a valid JSON string (that means decodeable by
155157
`json_decode`)
156-
3. The encoded JSON object must match all constraints specified in the `$jsonConstraints` array. For this, the [helmich/phpunit-json-assert][json-assert] package will be used.
158+
3. The encoded JSON object must match all constraints specified in the
159+
`$jsonConstraints` array. For this, the [helmich/phpunit-json-assert][json-assert]
160+
package will be used.
161+
162+
##### `assertMessageBodyMatchesForm($message, $formConstraints)` / `bodyMatchesForm($formConstraints)`
163+
164+
This asserts that the message body contains `application/x-www-form-urlencoded`-encoded
165+
content, with individual variables matching the `$formConstraints` array.
157166

158167
##### `assertRequestHasMethod($request, $method)` / `hasMethod($method)`
159168

@@ -181,6 +190,22 @@ For the most common checks, some shorthand assertions are available:
181190
- `assertResponseIsClientError($response)` / `isClientError()` -- Status codes 400 to 499
182191
- `assertResponseIsServerError($response)` / `isServerError()` -- Status codes 500 to 599
183192

193+
#### `assertStringIsAbsoluteUri($uri)` / `isAbsoluteUri()`
194+
195+
Assert that the string `$uri` contains a valid absolute URL (scheme and hostname are required).
196+
197+
#### `assertHasQueryParameter($uriOrRequest, $name[, $value])` / `hasQueryParameter($name[, $value])`
198+
199+
Asserts that an URI contains a query parameter matching the given constraints.
200+
`$name` and `$value` may both be string values as well as instances of the
201+
`PHPUnit\Framework\Constraint\Constraint` interface.
202+
203+
The `$uriOrRequest` value may be
204+
205+
- a string, which will be interpreted as URI
206+
- an instance of the `Psr\Http\Message\UriInterface` interface
207+
- an instance of the `Psr\Http\Message\RequestInterface` interface
208+
184209
[composer-autoload]: https://getcomposer.org/doc/04-schema.md#autoload-dev
185210
[json-assert]: https://packagist.org/packages/helmich/phpunit-json-assert
186211
[psr7]: https://packagist.org/packages/psr/http-message
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<?php
2+
declare(strict_types = 1);
3+
namespace Helmich\Psr7Assert\Constraint;
4+
5+
use PHPUnit\Framework\Constraint\Constraint;
6+
use Psr\Http\Message\RequestInterface;
7+
use Psr\Http\Message\UriInterface;
8+
9+
class HasQueryParameterConstraint extends Constraint
10+
{
11+
/** @var UrlEncodedMatches */
12+
private $inner;
13+
14+
public function __construct($nameMatcher, $valueMatcher = null)
15+
{
16+
parent::__construct();
17+
18+
$this->inner = new UrlEncodedMatches($nameMatcher, $valueMatcher);
19+
}
20+
21+
protected function matches($other): bool
22+
{
23+
if (is_string($other)) {
24+
return $this->matchesString($other);
25+
}
26+
27+
if ($other instanceof UriInterface) {
28+
return $this->matchesPsr7Uri($other);
29+
}
30+
31+
if ($other instanceof RequestInterface) {
32+
return $this->matchesPsr7Uri($other->getUri());
33+
}
34+
35+
return false;
36+
}
37+
38+
private function matchesPsr7Uri(UriInterface $other): bool
39+
{
40+
$queryString = $other->getQuery();
41+
return $this->matchesQueryString($queryString);
42+
}
43+
44+
private function matchesString(string $other): bool
45+
{
46+
$parsedUrl = parse_url($other);
47+
if (!isset($parsedUrl["query"])) {
48+
return false;
49+
}
50+
51+
return $this->matchesQueryString($parsedUrl["query"]);
52+
}
53+
54+
private function matchesQueryString(string $query): bool
55+
{
56+
return $this->inner->evaluate($query, "", true);
57+
}
58+
59+
60+
public function toString(): string
61+
{
62+
return 'query string ' . $this->inner->toString();
63+
}
64+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
namespace Helmich\Psr7Assert\Constraint;
3+
4+
use PHPUnit\Framework\Constraint\Constraint;
5+
6+
class HasQueryParametersConstraint extends Constraint
7+
{
8+
/** @var HasQueryParameterConstraint[] */
9+
private $constraints = [];
10+
11+
public function __construct(array $constraints)
12+
{
13+
foreach ($constraints as $key => $value) {
14+
$this->constraints[] = new HasQueryParameterConstraint($key, $value);
15+
}
16+
}
17+
18+
public function matches($other): bool
19+
{
20+
foreach ($this->constraints as $constraint) {
21+
if (!$constraint->evaluate($other, "", true)) {
22+
return false;
23+
}
24+
}
25+
26+
return true;
27+
}
28+
29+
public function toString(): string
30+
{
31+
return join(" and ", array_map(function(HasQueryParameterConstraint $c) {
32+
return $c->toString();
33+
}, $this->constraints));
34+
}
35+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
declare(strict_types = 1);
3+
namespace Helmich\Psr7Assert\Constraint;
4+
5+
use PHPUnit\Framework\Constraint\Constraint;
6+
7+
class IsAbsoluteUriConstraint extends Constraint
8+
{
9+
public function toString(): string
10+
{
11+
return "is valid URI";
12+
}
13+
14+
protected function matches($other): bool
15+
{
16+
$parts = parse_url($other);
17+
if ($parts === false) {
18+
return false;
19+
}
20+
21+
if (!isset($parts["host"]) || !$parts["host"]) {
22+
return false;
23+
}
24+
25+
if (!isset($parts["scheme"]) || !$parts["scheme"]) {
26+
return false;
27+
}
28+
29+
return $parts !== false;
30+
}
31+
}

src/Constraint/UrlEncodedMatches.php

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<?php
2+
namespace Helmich\Psr7Assert\Constraint;
3+
4+
use PHPUnit\Framework\Constraint\Constraint;
5+
use PHPUnit\Framework\Constraint\IsAnything;
6+
use PHPUnit\Framework\Constraint\IsEqual;
7+
8+
class UrlEncodedMatches extends Constraint
9+
{
10+
private $nameMatcher;
11+
private $valueMatcher;
12+
13+
public function __construct($nameMatcher, $valueMatcher = null)
14+
{
15+
parent::__construct();
16+
17+
if (!($nameMatcher instanceof Constraint)) {
18+
$nameMatcher = new IsEqual($nameMatcher);
19+
}
20+
21+
if ($valueMatcher === null) {
22+
$valueMatcher = new IsAnything();
23+
} else if (!($valueMatcher instanceof Constraint)) {
24+
$valueMatcher = new IsEqual($valueMatcher);
25+
}
26+
27+
$this->nameMatcher = $nameMatcher;
28+
$this->valueMatcher = $valueMatcher;
29+
}
30+
31+
protected function matches($other): bool
32+
{
33+
parse_str($other, $parsedQuery);
34+
35+
foreach ($parsedQuery as $key => $value) {
36+
if (!$this->nameMatcher->evaluate($key, "", true)) {
37+
continue;
38+
}
39+
40+
if (!$this->valueMatcher->evaluate($value, "", true)) {
41+
continue;
42+
}
43+
44+
return true;
45+
}
46+
47+
return false;
48+
}
49+
50+
public function toString(): string
51+
{
52+
return 'contains a name matching ' . $this->nameMatcher->toString() . ' and value matching ' . $this->valueMatcher->toString();
53+
}
54+
55+
56+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
namespace Helmich\Psr7Assert\Constraint;
3+
4+
use PHPUnit\Framework\Constraint\Constraint;
5+
6+
class UrlEncodedMatchesMany extends Constraint
7+
{
8+
/** @var UrlEncodedMatches[] */
9+
private $constraints = [];
10+
11+
public function __construct(array $constraints)
12+
{
13+
parent::__construct();
14+
15+
foreach ($constraints as $key => $value) {
16+
$this->constraints[] = new UrlEncodedMatches($key, $value);
17+
}
18+
}
19+
20+
protected function matches($other): bool
21+
{
22+
foreach ($this->constraints as $constraint) {
23+
if (!$constraint->evaluate($other, "", true)) {
24+
return false;
25+
}
26+
}
27+
28+
return true;
29+
}
30+
31+
32+
public function toString(): string
33+
{
34+
return join(" and ", array_map(function(HasQueryParameterConstraint $c) {
35+
return $c->toString();
36+
}, $this->constraints));
37+
}
38+
}

src/Functions.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use Helmich\Psr7Assert\Constraint\BodyMatchesConstraint;
66
use Helmich\Psr7Assert\Constraint\HasHeaderConstraint;
77
use Helmich\Psr7Assert\Constraint\HasUriConstraint;
8+
use Helmich\Psr7Assert\Constraint\IsAbsoluteUriConstraint;
89
use Helmich\Psr7Assert\Psr7Assertions;
910
use PHPUnit\Framework\Assert;
1011
use PHPUnit\Framework\Constraint\Constraint;
@@ -29,6 +30,16 @@ function hasStatus($status): Constraint
2930
return Psr7Assertions::hasStatus($status);
3031
}
3132

33+
function hasQueryParameter($name, $value = null): Constraint
34+
{
35+
return Psr7Assertions::hasQueryParameter($name, $value);
36+
}
37+
38+
function hasQueryParameters(array $constraints): Constraint
39+
{
40+
return Psr7Assertions::hasQueryParameters($constraints);
41+
}
42+
3243
function isSuccess(): Constraint
3344
{
3445
return Psr7Assertions::isSuccess();
@@ -94,3 +105,13 @@ function bodyMatchesJson($constraints): Constraint
94105
)
95106
);
96107
}
108+
109+
function bodyMatchesForm(array $constraints): Constraint
110+
{
111+
return Psr7Assertions::bodyMatchesForm($constraints);
112+
}
113+
114+
function isAbsoluteUri(): Constraint
115+
{
116+
return new IsAbsoluteUriConstraint();
117+
}

0 commit comments

Comments
 (0)