From 69c6757cf892238cf0977ff4fa643cec7b325fc1 Mon Sep 17 00:00:00 2001 From: "ext.jeremy.guillot@maxicoffee.domains" Date: Thu, 26 Mar 2026 18:22:31 +0100 Subject: [PATCH] fix: corriger l'erreur HTTP 400 sur les endpoints content-sources POST/PUT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ContentSourceForm.vue : convertir testChapterNumber en float/null avant envoi (évite d'envoyer "" pour ?float, rejeté par Symfony 8 strict) - UpsertContentSourceResource : ajouter collectDenormalizationErrors: true pour que les erreurs de type retournent 422 au lieu de 400 via le chemin input: de API Platform 4 - ContentSource entity : corriger setImageSelector(string) → setImageSelector(?string) cohérent avec la colonne nullable - Ajouter les tests manquants (testChapterNumber float/null/chaîne vide) qui auraient détecté ces bugs plus tôt --- .../components/ContentSourceForm.vue | 13 +++++- config/reference.php | 4 +- .../Resource/UpsertContentSourceResource.php | 2 + src/Entity/ContentSource.php | 2 +- .../Setting/CreateContentSourceTest.php | 31 +++++++++++++ .../Setting/UpdateContentSourceTest.php | 45 +++++++++++++++++++ 6 files changed, 92 insertions(+), 5 deletions(-) diff --git a/assets/vue/app/domain/setting/presentation/components/ContentSourceForm.vue b/assets/vue/app/domain/setting/presentation/components/ContentSourceForm.vue index 0761ad0..1d3222b 100644 --- a/assets/vue/app/domain/setting/presentation/components/ContentSourceForm.vue +++ b/assets/vue/app/domain/setting/presentation/components/ContentSourceForm.vue @@ -242,8 +242,17 @@ watch(() => props.source, (newSource) => { } }, { immediate: true }); +const buildPayload = (formData) => { + const data = { ...formData }; + const raw = data.testChapterNumber; + data.testChapterNumber = (raw === '' || raw === null || raw === undefined) + ? null + : parseFloat(raw); + return data; +}; + const handleSubmit = () => { - emit('submit', { ...form.value }); + emit('submit', buildPayload(form.value)); }; defineExpose({ submitForm: handleSubmit }); @@ -252,7 +261,7 @@ const testConfiguration = async () => { testing.value = true; try { await emit('test', { - configuration: { ...form.value }, + configuration: buildPayload(form.value), testData: { mangaSlug: form.value.testSlug, chapterNumber: form.value.testChapterNumber, diff --git a/config/reference.php b/config/reference.php index eb5a969..f20bcc9 100644 --- a/config/reference.php +++ b/config/reference.php @@ -1834,8 +1834,8 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param; * } * @psalm-type MercureConfig = array{ * hubs?: arrayimageSelector; } - public function setImageSelector(string $imageSelector): static + public function setImageSelector(?string $imageSelector): static { $this->imageSelector = $imageSelector; diff --git a/tests/Feature/Setting/CreateContentSourceTest.php b/tests/Feature/Setting/CreateContentSourceTest.php index 3b1bed0..84367a7 100644 --- a/tests/Feature/Setting/CreateContentSourceTest.php +++ b/tests/Feature/Setting/CreateContentSourceTest.php @@ -181,4 +181,35 @@ final class CreateContentSourceTest extends AbstractApiTestCase $this->assertResponseStatusCodeSame(Response::HTTP_UNPROCESSABLE_ENTITY); } + + public function testItAcceptsTestChapterNumberAsFloat(): void + { + $response = static::createClient()->request('POST', '/api/content-sources', [ + 'json' => [ + 'baseUrl' => 'https://mangadex.org', + 'chapterUrlFormat' => 'https://mangadex.org/chapter/{id}', + 'scrapingType' => 'html', + 'testSlug' => 'one-piece', + 'testChapterNumber' => 1.5, + ], + ]); + + $this->assertResponseStatusCodeSame(Response::HTTP_CREATED); + } + + public function testItRejectsTestChapterNumberAsEmptyStringWith422(): void + { + // Cas réel du formulaire Vue : le champ vide envoie "" au lieu de null. + // Doit retourner 422 (erreur de validation) et non 400 (données malformées). + $response = static::createClient()->request('POST', '/api/content-sources', [ + 'json' => [ + 'baseUrl' => 'https://mangadex.org', + 'chapterUrlFormat' => 'https://mangadex.org/chapter/{id}', + 'scrapingType' => 'html', + 'testChapterNumber' => '', + ], + ]); + + $this->assertResponseStatusCodeSame(Response::HTTP_UNPROCESSABLE_ENTITY); + } } diff --git a/tests/Feature/Setting/UpdateContentSourceTest.php b/tests/Feature/Setting/UpdateContentSourceTest.php index 947727f..d3eac5e 100644 --- a/tests/Feature/Setting/UpdateContentSourceTest.php +++ b/tests/Feature/Setting/UpdateContentSourceTest.php @@ -188,4 +188,49 @@ final class UpdateContentSourceTest extends AbstractApiTestCase $this->assertResponseStatusCodeSame(Response::HTTP_UNPROCESSABLE_ENTITY); } + + public function testItAcceptsTestChapterNumberAsFloat(): void + { + $response = static::createClient()->request('PUT', "/api/content-sources/{$this->sourceId}", [ + 'json' => [ + 'baseUrl' => 'https://mangadex.org', + 'chapterUrlFormat' => 'https://mangadex.org/chapter/{id}', + 'scrapingType' => 'html', + 'testSlug' => 'one-piece', + 'testChapterNumber' => 1.5, + ], + ]); + + $this->assertResponseIsSuccessful(); + } + + public function testItAcceptsTestChapterNumberAsNull(): void + { + $response = static::createClient()->request('PUT', "/api/content-sources/{$this->sourceId}", [ + 'json' => [ + 'baseUrl' => 'https://mangadex.org', + 'chapterUrlFormat' => 'https://mangadex.org/chapter/{id}', + 'scrapingType' => 'html', + 'testChapterNumber' => null, + ], + ]); + + $this->assertResponseIsSuccessful(); + } + + public function testItRejectsTestChapterNumberAsEmptyStringWith422(): void + { + // Cas réel du formulaire Vue : le champ vide envoie "" au lieu de null. + // Doit retourner 422 (erreur de validation) et non 400 (données malformées). + $response = static::createClient()->request('PUT', "/api/content-sources/{$this->sourceId}", [ + 'json' => [ + 'baseUrl' => 'https://mangadex.org', + 'chapterUrlFormat' => 'https://mangadex.org/chapter/{id}', + 'scrapingType' => 'html', + 'testChapterNumber' => '', + ], + ]); + + $this->assertResponseStatusCodeSame(Response::HTTP_UNPROCESSABLE_ENTITY); + } }