Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 97df211

Browse files
committedApr 3, 2024
test: unit tests for backed enum resources
1 parent 76af4ef commit 97df211

File tree

5 files changed

+460
-0
lines changed

5 files changed

+460
-0
lines changed
 
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <dunglas@gmail.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\Issue6264;
15+
16+
use ApiPlatform\Metadata\ApiResource;
17+
use ApiPlatform\Metadata\Get;
18+
use ApiPlatform\Metadata\GetCollection;
19+
20+
#[ApiResource(normalizationContext: ['groups' => ['get']])]
21+
#[GetCollection(provider: Availability::class.'::getCases')]
22+
#[Get(provider: Availability::class.'::getCase')]
23+
enum Availability: int
24+
{
25+
use BackedEnumTrait;
26+
27+
case Available = 0;
28+
case Cancelled = 10;
29+
case Postponed = 200;
30+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <dunglas@gmail.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\Issue6264;
15+
16+
use ApiPlatform\Metadata\ApiResource;
17+
use ApiPlatform\Metadata\Get;
18+
use ApiPlatform\Metadata\GetCollection;
19+
20+
#[ApiResource(normalizationContext: ['groups' => ['get']])]
21+
#[GetCollection(provider: AvailabilityStatus::class.'::getCases')]
22+
#[Get(provider: AvailabilityStatus::class.'::getCase')]
23+
enum AvailabilityStatus: string
24+
{
25+
use BackedEnumTrait;
26+
27+
case Pending = 'pending';
28+
case Reviewed = 'reviewed';
29+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <dunglas@gmail.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\Issue6264;
15+
16+
use ApiPlatform\Metadata\Operation;
17+
use Symfony\Component\Serializer\Attribute\Groups;
18+
19+
trait BackedEnumTrait
20+
{
21+
public static function values(): array
22+
{
23+
return array_map(static fn (\BackedEnum $feature) => $feature->value, self::cases());
24+
}
25+
26+
public function getId(): string
27+
{
28+
return $this->name;
29+
}
30+
31+
#[Groups(['get'])]
32+
public function getValue(): string|int
33+
{
34+
return $this->value;
35+
}
36+
37+
public static function getCases(): array
38+
{
39+
return self::cases();
40+
}
41+
42+
public static function getCase(Operation $operation, array $uriVariables): ?self
43+
{
44+
return array_reduce(self::cases(), static fn ($c, \BackedEnum $case) => $case->name === $uriVariables['id'] ? $case : $c, null);
45+
}
46+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <dunglas@gmail.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace ApiPlatform\Tests\Functional;
15+
16+
use ApiPlatform\Symfony\Bundle\Test\ApiTestCase;
17+
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\Person;
18+
use ApiPlatform\Tests\Fixtures\TestBundle\Enum\GenderTypeEnum;
19+
use Doctrine\ORM\EntityManagerInterface;
20+
use Doctrine\ORM\Mapping\ClassMetadata;
21+
use Doctrine\ORM\Tools\SchemaTool;
22+
use Symfony\Component\HttpClient\HttpOptions;
23+
24+
final class BackedEnumPropertyTest extends ApiTestCase
25+
{
26+
public function testJson(): void
27+
{
28+
$person = $this->createPerson();
29+
30+
self::createClient()->request('GET', '/people/'.$person->getId(), ['headers' => ['Accept' => 'application/json']]);
31+
32+
$this->assertResponseIsSuccessful();
33+
$this->assertJsonEquals([
34+
'genderType' => GenderTypeEnum::FEMALE->value,
35+
'name' => 'Sonja',
36+
'pets' => [],
37+
]);
38+
}
39+
40+
/** @group legacy */
41+
public function testGraphQl(): void
42+
{
43+
$person = $this->createPerson();
44+
45+
$query = <<<'GRAPHQL'
46+
query GetPerson($identifier: ID!) {
47+
person(id: $identifier) {
48+
genderType
49+
}
50+
}
51+
GRAPHQL;
52+
$options = (new HttpOptions())
53+
->setJson(['query' => $query, 'variables' => ['identifier' => '/people/'.$person->getId()]])
54+
->setHeaders(['Content-Type' => 'application/json']);
55+
self::createClient()->request('POST', '/graphql', $options->toArray());
56+
57+
$this->assertResponseIsSuccessful();
58+
$this->assertJsonEquals([
59+
'data' => [
60+
'person' => [
61+
'genderType' => GenderTypeEnum::FEMALE->name,
62+
],
63+
],
64+
]);
65+
}
66+
67+
private function createPerson(): Person
68+
{
69+
$this->recreateSchema();
70+
71+
/** @var EntityManagerInterface $manager */
72+
$manager = static::getContainer()->get('doctrine')->getManager();
73+
$person = new Person();
74+
$person->name = 'Sonja';
75+
$person->genderType = GenderTypeEnum::FEMALE;
76+
$manager->persist($person);
77+
$manager->flush();
78+
79+
return $person;
80+
}
81+
82+
private function recreateSchema(array $options = []): void
83+
{
84+
self::bootKernel($options);
85+
86+
/** @var EntityManagerInterface $manager */
87+
$manager = static::getContainer()->get('doctrine')->getManager();
88+
/** @var ClassMetadata[] $classes */
89+
$classes = $manager->getMetadataFactory()->getAllMetadata();
90+
$schemaTool = new SchemaTool($manager);
91+
92+
@$schemaTool->dropSchema($classes);
93+
@$schemaTool->createSchema($classes);
94+
}
95+
}
Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <dunglas@gmail.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace ApiPlatform\Tests\Functional;
15+
16+
use ApiPlatform\Symfony\Bundle\Test\ApiTestCase;
17+
use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\Issue6264\Availability;
18+
use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\Issue6264\AvailabilityStatus;
19+
use Symfony\Component\HttpClient\HttpOptions;
20+
21+
final class BackedEnumResourceTest extends ApiTestCase
22+
{
23+
public static function providerEnumItemsJson(): iterable
24+
{
25+
// String cases
26+
foreach (Availability::cases() as $case) {
27+
yield ['/availabilities/'.$case->name, 'application/json', ['value' => $case->value]];
28+
29+
yield ['/availabilities/'.$case->name, 'application/vnd.api+json', [
30+
'data' => [
31+
'id' => '/availabilities/'.$case->name,
32+
'type' => 'Availability',
33+
'attributes' => [
34+
'value' => $case->value,
35+
],
36+
],
37+
]];
38+
39+
yield ['/availabilities/'.$case->name, 'application/hal+json', [
40+
'_links' => [
41+
'self' => [
42+
'href' => '/availabilities/'.$case->name,
43+
],
44+
],
45+
'value' => $case->value,
46+
]];
47+
48+
yield ['/availabilities/'.$case->name, 'application/ld+json', [
49+
'@context' => '/contexts/Availability',
50+
'@id' => '/availabilities/'.$case->name,
51+
'@type' => 'Availability',
52+
'value' => $case->value,
53+
]];
54+
}
55+
56+
// Integer cases
57+
foreach (AvailabilityStatus::cases() as $case) {
58+
yield ['/availability_statuses/'.$case->name, 'application/json', ['value' => $case->value]];
59+
60+
yield ['/availability_statuses/'.$case->name, 'application/vnd.api+json', [
61+
'data' => [
62+
'id' => '/availability_statuses/'.$case->name,
63+
'type' => 'AvailabilityStatus',
64+
'attributes' => [
65+
'value' => $case->value,
66+
],
67+
],
68+
]];
69+
70+
yield ['/availability_statuses/'.$case->name, 'application/hal+json', [
71+
'_links' => [
72+
'self' => [
73+
'href' => '/availability_statuses/'.$case->name,
74+
],
75+
],
76+
'value' => $case->value,
77+
]];
78+
79+
yield ['/availability_statuses/'.$case->name, 'application/ld+json', [
80+
'@context' => '/contexts/AvailabilityStatus',
81+
'@id' => '/availability_statuses/'.$case->name,
82+
'@type' => 'AvailabilityStatus',
83+
'value' => $case->value,
84+
]];
85+
}
86+
}
87+
88+
/** @dataProvider providerEnumItemsJson */
89+
public function testItemJson(string $uri, string $mimeType, array $expected): void
90+
{
91+
self::createClient()->request('GET', $uri, ['headers' => ['Accept' => $mimeType]]);
92+
93+
$this->assertResponseIsSuccessful();
94+
$this->assertJsonEquals($expected);
95+
}
96+
97+
public static function providerEnumItemsGraphQl(): iterable
98+
{
99+
// String cases
100+
$query = <<<'GRAPHQL'
101+
query GetAvailability($identifier: ID!) {
102+
availability(id: $identifier) {
103+
value
104+
}
105+
}
106+
GRAPHQL;
107+
foreach (Availability::cases() as $case) {
108+
yield [$query, ['identifier' => '/availabilities/'.$case->name], ['data' => ['availability' => ['value' => $case->value]]]];
109+
}
110+
111+
// Integer cases
112+
$query = <<<'GRAPHQL'
113+
query GetAvailabilityStatus($identifier: ID!) {
114+
availabilityStatus(id: $identifier) {
115+
value
116+
}
117+
}
118+
GRAPHQL;
119+
foreach (AvailabilityStatus::cases() as $case) {
120+
yield [$query, ['identifier' => '/availability_statuses/'.$case->name], ['data' => ['availability_status' => ['value' => $case->value]]]];
121+
}
122+
}
123+
124+
/**
125+
* @dataProvider providerEnumItemsGraphQl
126+
* @group legacy
127+
*/
128+
public function testItemGraphql(string $query, array $variables, array $expected): void
129+
{
130+
$options = (new HttpOptions())
131+
->setJson(['query' => $query, 'variables' => $variables])
132+
->setHeaders(['Content-Type' => 'application/json']);
133+
self::createClient()->request('POST', '/graphql', $options->toArray());
134+
135+
$this->assertResponseIsSuccessful();
136+
$this->assertJsonEquals($expected);
137+
}
138+
139+
public function testCollectionJson(): void
140+
{
141+
self::createClient()->request('GET', '/availabilities', ['headers' => ['Accept' => 'application/json']]);
142+
143+
$this->assertResponseIsSuccessful();
144+
$this->assertJsonEquals([
145+
['value' => 0],
146+
['value' => 10],
147+
['value' => 200],
148+
]);
149+
}
150+
151+
public function testCollectionJsonApi(): void
152+
{
153+
self::createClient()->request('GET', '/availabilities', ['headers' => ['Accept' => 'application/vnd.api+json']]);
154+
155+
$this->assertResponseIsSuccessful();
156+
$this->assertJsonEquals([
157+
'links' => [
158+
'self' => '/availabilities',
159+
],
160+
'meta' => [
161+
'totalItems' => 3,
162+
],
163+
'data' => [
164+
[
165+
'id' => '/availabilities/Available',
166+
'type' => 'Availability',
167+
'attributes' => [
168+
'value' => 0,
169+
],
170+
],
171+
[
172+
'id' => '/availabilities/Cancelled',
173+
'type' => 'Availability',
174+
'attributes' => [
175+
'value' => 10,
176+
],
177+
],
178+
[
179+
'id' => '/availabilities/Postponed',
180+
'type' => 'Availability',
181+
'attributes' => [
182+
'value' => 200,
183+
],
184+
],
185+
],
186+
]);
187+
}
188+
189+
public function testCollectionHal(): void
190+
{
191+
self::createClient()->request('GET', '/availabilities', ['headers' => ['Accept' => 'application/hal+json']]);
192+
193+
$this->assertResponseIsSuccessful();
194+
$this->assertJsonEquals([
195+
'_links' => [
196+
'self' => [
197+
'href' => '/availabilities',
198+
],
199+
'item' => [
200+
['href' => '/availabilities/Available'],
201+
['href' => '/availabilities/Cancelled'],
202+
['href' => '/availabilities/Postponed'],
203+
],
204+
],
205+
'totalItems' => 3,
206+
'_embedded' => [
207+
'item' => [
208+
[
209+
'_links' => [
210+
'self' => ['href' => '/availabilities/Available'],
211+
],
212+
'value' => 0,
213+
],
214+
[
215+
'_links' => [
216+
'self' => ['href' => '/availabilities/Cancelled'],
217+
],
218+
'value' => 10,
219+
],
220+
[
221+
'_links' => [
222+
'self' => ['href' => '/availabilities/Postponed'],
223+
],
224+
'value' => 200,
225+
],
226+
],
227+
],
228+
]);
229+
}
230+
231+
public function testCollectionJsonLd(): void
232+
{
233+
self::createClient()->request('GET', '/availabilities', ['headers' => ['Accept' => 'application/ld+json']]);
234+
235+
$this->assertResponseIsSuccessful();
236+
$this->assertJsonEquals([
237+
'@context' => '/contexts/Availability',
238+
'@id' => '/availabilities',
239+
'@type' => 'hydra:Collection',
240+
'hydra:totalItems' => 3,
241+
'hydra:member' => [
242+
[
243+
'@id' => '/availabilities/Available',
244+
'@type' => 'Availability',
245+
'value' => 0,
246+
],
247+
[
248+
'@id' => '/availabilities/Cancelled',
249+
'@type' => 'Availability',
250+
'value' => 10,
251+
],
252+
[
253+
'@id' => '/availabilities/Postponed',
254+
'@type' => 'Availability',
255+
'value' => 200,
256+
],
257+
],
258+
]);
259+
}
260+
}

0 commit comments

Comments
 (0)
Please sign in to comment.