From 665a6c5385f2e8c94954c330d641faf5eebb2948 Mon Sep 17 00:00:00 2001 From: thibaud-lclr Date: Sun, 5 Apr 2026 20:37:26 +0200 Subject: [PATCH] feat: mark import as 'error' in case it fails --- src/Controller/ImportController.php | 17 +++- tests/Controller/ImportControllerTest.php | 116 ++++++++++++++++++++++ 2 files changed, 128 insertions(+), 5 deletions(-) create mode 100644 tests/Controller/ImportControllerTest.php diff --git a/src/Controller/ImportController.php b/src/Controller/ImportController.php index 47779c1..76ed33f 100644 --- a/src/Controller/ImportController.php +++ b/src/Controller/ImportController.php @@ -79,13 +79,20 @@ class ImportController extends AbstractController $em->persist($import); $em->flush(); - $filePath = sprintf('imports/%d/%d.csv', $user->getId(), $import->getId()); - $defaultStorage->write($filePath, file_get_contents($file->getPathname())); + try { + $filePath = sprintf('imports/%d/%d.csv', $user->getId(), $import->getId()); + $defaultStorage->write($filePath, file_get_contents($file->getPathname())); - $import->setFilePath($filePath); - $em->flush(); + $import->setFilePath($filePath); + $em->flush(); - $bus->dispatch(new ProcessImportMessage($import->getId())); + $bus->dispatch(new ProcessImportMessage($import->getId())); + } catch (\Throwable) { + $import->setStatus(Import::STATUS_FAILED); + $em->flush(); + + return $this->json(['error' => 'An error occurred while starting the import.'], Response::HTTP_INTERNAL_SERVER_ERROR); + } return $this->json([ 'id' => $import->getId(), diff --git a/tests/Controller/ImportControllerTest.php b/tests/Controller/ImportControllerTest.php new file mode 100644 index 0000000..9b24255 --- /dev/null +++ b/tests/Controller/ImportControllerTest.php @@ -0,0 +1,116 @@ +storage = $this->createStub(FilesystemOperator::class); + $this->em = $this->createStub(EntityManagerInterface::class); + $this->importRepository = $this->createStub(ImportRepository::class); + $this->bus = $this->createStub(MessageBusInterface::class); + + $this->user = new User(); + $idProp = new \ReflectionProperty(User::class, 'id'); + $idProp->setValue($this->user, 1); + + $token = $this->createStub(TokenInterface::class); + $token->method('getUser')->willReturn($this->user); + + $tokenStorage = $this->createStub(TokenStorageInterface::class); + $tokenStorage->method('getToken')->willReturn($token); + + $container = $this->createStub(ContainerInterface::class); + $container->method('has')->willReturnCallback(static fn(string $id): bool => match ($id) { + 'security.token_storage' => true, + 'serializer' => false, + default => false, + }); + $container->method('get')->willReturnCallback(static fn(string $id) => match ($id) { + 'security.token_storage' => $tokenStorage, + default => null, + }); + + $this->controller = new ImportController(); + $this->controller->setContainer($container); + } + + private function createRequest(): Request + { + $tmpFile = tempnam(sys_get_temp_dir(), 'import_test_'); + file_put_contents($tmpFile, "Date,Name,Year,Letterboxd URI,Rating\n2024-01-01,Inception,2010,https://letterboxd.com/film/inception/,5"); + $uploadedFile = new UploadedFile($tmpFile, 'films.csv', 'text/csv', null, true); + + return Request::create('/api/imports', 'POST', files: ['file' => $uploadedFile]); + } + + private function configureEmWithImportCapture(?Import &$capturedImport): void + { + $this->em->method('persist')->willReturnCallback(function (object $entity) use (&$capturedImport): void { + if ($entity instanceof Import) { + $capturedImport = $entity; + $idProp = new \ReflectionProperty(Import::class, 'id'); + $idProp->setValue($entity, 42); + } + }); + } + + public function testSetsImportStatusToFailedWhenStorageWriteThrows(): void + { + $this->importRepository->method('hasActiveImport')->willReturn(false); + + $capturedImport = null; + $this->configureEmWithImportCapture($capturedImport); + + $this->storage->method('write')->willThrowException(new \RuntimeException('Storage unavailable')); + + $bus = $this->createMock(MessageBusInterface::class); + $bus->expects($this->never())->method('dispatch'); + + $response = $this->controller->create($this->createRequest(), $this->storage, $this->em, $this->importRepository, $bus); + + $this->assertSame(Import::STATUS_FAILED, $capturedImport->getStatus()); + $this->assertSame(500, $response->getStatusCode()); + } + + public function testSetsImportStatusToFailedWhenBusDispatchThrows(): void + { + $this->importRepository->method('hasActiveImport')->willReturn(false); + + $capturedImport = null; + $this->configureEmWithImportCapture($capturedImport); + + $this->storage->method('write')->willReturnCallback(static fn() => null); + $this->bus->method('dispatch')->willThrowException(new \RuntimeException('Bus unavailable')); + + $response = $this->controller->create($this->createRequest(), $this->storage, $this->em, $this->importRepository, $this->bus); + + $this->assertSame(Import::STATUS_FAILED, $capturedImport->getStatus()); + $this->assertSame(500, $response->getStatusCode()); + } +}