31 - Add required-field validation
This commit is contained in:
parent
1a90a18b49
commit
bebc0427b9
6 changed files with 171 additions and 26 deletions
|
|
@ -1,5 +1,5 @@
|
||||||
api_platform:
|
api_platform:
|
||||||
title: Hello API Platform
|
title: Buckets API Platform
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
defaults:
|
defaults:
|
||||||
stateless: false
|
stateless: false
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,7 @@ class Bucket
|
||||||
private BucketType $type = BucketType::NEED;
|
private BucketType $type = BucketType::NEED;
|
||||||
|
|
||||||
#[ORM\Column]
|
#[ORM\Column]
|
||||||
|
#[Assert\NotNull]
|
||||||
private int $priority;
|
private int $priority;
|
||||||
|
|
||||||
#[ORM\Column(options: ['default' => 0])]
|
#[ORM\Column(options: ['default' => 0])]
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ class Stream
|
||||||
private Scenario $scenario;
|
private Scenario $scenario;
|
||||||
|
|
||||||
#[ORM\Column(length: 255)]
|
#[ORM\Column(length: 255)]
|
||||||
|
#[Assert\NotBlank]
|
||||||
private string $name;
|
private string $name;
|
||||||
|
|
||||||
#[ORM\Column]
|
#[ORM\Column]
|
||||||
|
|
|
||||||
|
|
@ -15,29 +15,4 @@ class StreamRepository extends ServiceEntityRepository
|
||||||
{
|
{
|
||||||
parent::__construct($registry, Stream::class);
|
parent::__construct($registry, Stream::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
// /**
|
|
||||||
// * @return Stream[] Returns an array of Stream objects
|
|
||||||
// */
|
|
||||||
// public function findByExampleField($value): array
|
|
||||||
// {
|
|
||||||
// return $this->createQueryBuilder('s')
|
|
||||||
// ->andWhere('s.exampleField = :val')
|
|
||||||
// ->setParameter('val', $value)
|
|
||||||
// ->orderBy('s.id', 'ASC')
|
|
||||||
// ->setMaxResults(10)
|
|
||||||
// ->getQuery()
|
|
||||||
// ->getResult()
|
|
||||||
// ;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public function findOneBySomeField($value): ?Stream
|
|
||||||
// {
|
|
||||||
// return $this->createQueryBuilder('s')
|
|
||||||
// ->andWhere('s.exampleField = :val')
|
|
||||||
// ->setParameter('val', $value)
|
|
||||||
// ->getQuery()
|
|
||||||
// ->getOneOrNullResult()
|
|
||||||
// ;
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -396,4 +396,76 @@ final class BucketApiTest extends WebTestCase
|
||||||
'POST /api/buckets without a name must return 422 Unprocessable Entity (validator-rejected, not a 500).',
|
'POST /api/buckets without a name must return 422 Unprocessable Entity (validator-rejected, not a 500).',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testPostWithMissingPriorityIsRejectedAsUnprocessable(): void
|
||||||
|
{
|
||||||
|
$scenario = $this->persistScenario('Household Budget');
|
||||||
|
|
||||||
|
$this->login();
|
||||||
|
|
||||||
|
$this->client->request(
|
||||||
|
'POST',
|
||||||
|
'/api/buckets',
|
||||||
|
server: [
|
||||||
|
'CONTENT_TYPE' => 'application/ld+json',
|
||||||
|
'HTTP_ACCEPT' => 'application/ld+json',
|
||||||
|
],
|
||||||
|
content: json_encode([
|
||||||
|
'scenario' => $this->scenarioIri($scenario),
|
||||||
|
'name' => 'Groceries',
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
|
||||||
|
$response = $this->client->getResponse();
|
||||||
|
|
||||||
|
self::assertSame(
|
||||||
|
422,
|
||||||
|
$response->getStatusCode(),
|
||||||
|
'POST /api/buckets without a priority must return 422 Unprocessable Entity (validator-rejected, not a 500).',
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertHasViolationAt('priority', $response);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetItemIsRejectedWhenUnauthenticated(): void
|
||||||
|
{
|
||||||
|
$scenario = $this->persistScenario('Household Budget');
|
||||||
|
$bucket = $this->persistBucket($scenario, 'Rent', 1);
|
||||||
|
|
||||||
|
$this->client->request('GET', '/api/buckets/'.$bucket->getId(), server: [
|
||||||
|
'HTTP_ACCEPT' => 'application/ld+json',
|
||||||
|
]);
|
||||||
|
|
||||||
|
self::assertSame(
|
||||||
|
401,
|
||||||
|
$this->client->getResponse()->getStatusCode(),
|
||||||
|
'GET /api/buckets/{id} must require authentication.',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPostWithNonJsonContentTypeIsRejected(): void
|
||||||
|
{
|
||||||
|
$this->login();
|
||||||
|
|
||||||
|
$this->client->request(
|
||||||
|
'POST',
|
||||||
|
'/api/buckets',
|
||||||
|
['name' => 'Groceries'],
|
||||||
|
);
|
||||||
|
|
||||||
|
self::assertSame(
|
||||||
|
415,
|
||||||
|
$this->client->getResponse()->getStatusCode(),
|
||||||
|
'POST /api/buckets without a JSON Content-Type must be rejected with 415 Unsupported Media Type.',
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->em->clear();
|
||||||
|
|
||||||
|
$persisted = $this->em->getRepository(Bucket::class)->findOneBy(['name' => 'Groceries']);
|
||||||
|
|
||||||
|
self::assertNull(
|
||||||
|
$persisted,
|
||||||
|
'A rejected non-JSON POST must not persist a Bucket.',
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -284,4 +284,100 @@ final class StreamApiTest extends WebTestCase
|
||||||
'POST /api/streams without a startDate must return 422 Unprocessable Entity (validator-rejected, not a 500).',
|
'POST /api/streams without a startDate must return 422 Unprocessable Entity (validator-rejected, not a 500).',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testPostWithMissingNameIsRejectedAsUnprocessable(): void
|
||||||
|
{
|
||||||
|
$scenario = $this->persistScenario('Household Budget');
|
||||||
|
|
||||||
|
$this->login();
|
||||||
|
|
||||||
|
$this->client->request(
|
||||||
|
'POST',
|
||||||
|
'/api/streams',
|
||||||
|
server: [
|
||||||
|
'CONTENT_TYPE' => 'application/ld+json',
|
||||||
|
'HTTP_ACCEPT' => 'application/ld+json',
|
||||||
|
],
|
||||||
|
content: json_encode([
|
||||||
|
'scenario' => $this->scenarioIri($scenario),
|
||||||
|
'amount' => 300000,
|
||||||
|
'type' => StreamType::INCOME->value,
|
||||||
|
'frequency' => StreamFrequency::MONTHLY->value,
|
||||||
|
'startDate' => '2026-01-01',
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
|
||||||
|
self::assertSame(
|
||||||
|
422,
|
||||||
|
$this->client->getResponse()->getStatusCode(),
|
||||||
|
'POST /api/streams without a name must return 422 Unprocessable Entity (validator-rejected, not a 500).',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetItemIsRejectedWhenUnauthenticated(): void
|
||||||
|
{
|
||||||
|
$scenario = $this->persistScenario('Household Budget');
|
||||||
|
$stream = $this->persistStream($scenario, 'Salary');
|
||||||
|
|
||||||
|
$this->client->request('GET', '/api/streams/'.$stream->getId(), server: [
|
||||||
|
'HTTP_ACCEPT' => 'application/ld+json',
|
||||||
|
]);
|
||||||
|
|
||||||
|
self::assertSame(
|
||||||
|
401,
|
||||||
|
$this->client->getResponse()->getStatusCode(),
|
||||||
|
'GET /api/streams/{id} must require authentication.',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPostIsRejectedWhenUnauthenticated(): void
|
||||||
|
{
|
||||||
|
$this->client->request(
|
||||||
|
'POST',
|
||||||
|
'/api/streams',
|
||||||
|
server: [
|
||||||
|
'CONTENT_TYPE' => 'application/ld+json',
|
||||||
|
'HTTP_ACCEPT' => 'application/ld+json',
|
||||||
|
],
|
||||||
|
content: json_encode([
|
||||||
|
'name' => 'Salary',
|
||||||
|
'amount' => 300000,
|
||||||
|
'type' => StreamType::INCOME->value,
|
||||||
|
'frequency' => StreamFrequency::MONTHLY->value,
|
||||||
|
'startDate' => '2026-01-01',
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
|
||||||
|
self::assertSame(
|
||||||
|
401,
|
||||||
|
$this->client->getResponse()->getStatusCode(),
|
||||||
|
'POST /api/streams must require authentication.',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPostWithNonJsonContentTypeIsRejected(): void
|
||||||
|
{
|
||||||
|
$this->login();
|
||||||
|
|
||||||
|
$this->client->request(
|
||||||
|
'POST',
|
||||||
|
'/api/streams',
|
||||||
|
['name' => 'Salary'],
|
||||||
|
);
|
||||||
|
|
||||||
|
self::assertSame(
|
||||||
|
415,
|
||||||
|
$this->client->getResponse()->getStatusCode(),
|
||||||
|
'POST /api/streams without a JSON Content-Type must be rejected with 415 Unsupported Media Type.',
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->em->clear();
|
||||||
|
|
||||||
|
$persisted = $this->em->getRepository(Stream::class)->findOneBy(['name' => 'Salary']);
|
||||||
|
|
||||||
|
self::assertNull(
|
||||||
|
$persisted,
|
||||||
|
'A rejected non-JSON POST must not persist a Stream.',
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue