201, IRI renvoyee, ligne persistee, * checksum sha256 calcule cote serveur ; * - POST d'un MIME hors whitelist (text/plain) -> 422 ; * - POST sans fichier -> 422 ; * - POST anonyme -> 401 (acces /api protege globalement). * * @internal */ final class UploadedDocumentApiTest extends AbstractApiTestCase { private const string ENDPOINT = '/api/uploaded_documents'; /** @var list */ private array $tempFiles = []; protected function tearDown(): void { foreach ($this->tempFiles as $path) { if (is_file($path)) { @unlink($path); } } parent::tearDown(); } public function testUploadValidPdfReturnsIriAndPersistsRowWithChecksum(): void { $client = $this->authenticatedClient('admin', 'admin'); $content = $this->minimalPdf(); $file = $this->makeUploadedFile($content, 'facture.pdf'); $response = $client->request('POST', self::ENDPOINT, [ 'headers' => ['Accept' => 'application/ld+json'], 'extra' => ['files' => ['file' => $file]], ]); self::assertResponseStatusCodeSame(201); $data = $response->toArray(); self::assertArrayHasKey('@id', $data); self::assertStringStartsWith(self::ENDPOINT.'/', $data['@id']); self::assertSame('facture.pdf', $data['originalFilename']); self::assertSame('application/pdf', $data['mimeType']); self::assertSame(\strlen($content), $data['sizeBytes']); self::assertSame(hash('sha256', $content), $data['checksum']); self::assertSame(64, \strlen($data['checksum'])); // La ligne est bien persistee et relisible via le repository. $id = $data['id']; $document = $this->getEm()->getRepository(UploadedDocument::class)->find($id); self::assertInstanceOf(UploadedDocument::class, $document); self::assertSame(hash('sha256', $content), $document->getChecksum()); } public function testUploadDisallowedMimeTypeReturns422(): void { $client = $this->authenticatedClient('admin', 'admin'); $file = $this->makeUploadedFile('just some plain text content', 'note.txt'); $client->request('POST', self::ENDPOINT, [ 'headers' => ['Accept' => 'application/ld+json'], 'extra' => ['files' => ['file' => $file]], ]); self::assertResponseStatusCodeSame(422); } public function testUploadWithoutFileReturns422(): void { $client = $this->authenticatedClient('admin', 'admin'); $client->request('POST', self::ENDPOINT, [ 'headers' => ['Accept' => 'application/ld+json'], 'extra' => ['files' => []], ]); self::assertResponseStatusCodeSame(422); } public function testUploadAnonymousIsRejected(): void { $client = self::createClient(); $file = $this->makeUploadedFile($this->minimalPdf(), 'facture.pdf'); $client->request('POST', self::ENDPOINT, [ 'headers' => ['Accept' => 'application/ld+json'], 'extra' => ['files' => ['file' => $file]], ]); self::assertResponseStatusCodeSame(401); } /** * Cree un UploadedFile en mode test (move() autorise hors contexte HTTP). */ private function makeUploadedFile(string $content, string $clientName): UploadedFile { $path = sys_get_temp_dir().'/erp154-api-'.bin2hex(random_bytes(4)); file_put_contents($path, $content); $this->tempFiles[] = $path; return new UploadedFile($path, $clientName, null, null, true); } /** * Contenu PDF minimal valide (entete `%PDF-1.4` -> finfo `application/pdf`). */ private function minimalPdf(): string { return "%PDF-1.4\n" ."1 0 obj<>endobj\n" ."2 0 obj<>endobj\n" ."3 0 obj<>endobj\n" ."trailer<>\n" ."%%EOF\n"; } }