From 3438f152744728a0988a867b39cd8462f3662ac0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20B=C3=B6hmer?= Date: Sat, 9 Nov 2019 16:14:57 +0100 Subject: [PATCH] Added some more tests. --- composer.lock | 20 ++--- .../AdminPages/BaseAdminController.php | 17 ++-- src/Services/ElementTypeNameGenerator.php | 5 +- src/Services/EntityImporter.php | 34 ++++--- .../Services/ElementTypeNameGeneratorTest.php | 89 +++++++++++++++++++ tests/Services/EntityImporterTest.php | 84 +++++++++++++++++ 6 files changed, 219 insertions(+), 30 deletions(-) create mode 100644 tests/Services/ElementTypeNameGeneratorTest.php create mode 100644 tests/Services/EntityImporterTest.php diff --git a/composer.lock b/composer.lock index 322a6bc9..47067177 100644 --- a/composer.lock +++ b/composer.lock @@ -4955,16 +4955,16 @@ }, { "name": "symfony/http-client-contracts", - "version": "v1.1.7", + "version": "v1.1.8", "source": { "type": "git", "url": "https://github.com/symfony/http-client-contracts.git", - "reference": "353b2a3e907e5c34cf8f74827a4b21eb745aab1d" + "reference": "088bae75cfa2ec5eb6d33dce17dbd8613150ce6e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/353b2a3e907e5c34cf8f74827a4b21eb745aab1d", - "reference": "353b2a3e907e5c34cf8f74827a4b21eb745aab1d", + "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/088bae75cfa2ec5eb6d33dce17dbd8613150ce6e", + "reference": "088bae75cfa2ec5eb6d33dce17dbd8613150ce6e", "shasum": "" }, "require": { @@ -5008,7 +5008,7 @@ "interoperability", "standards" ], - "time": "2019-09-26T22:09:58+00:00" + "time": "2019-11-07T12:44:51+00:00" }, { "name": "symfony/http-foundation", @@ -6566,16 +6566,16 @@ }, { "name": "symfony/service-contracts", - "version": "v1.1.7", + "version": "v1.1.8", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "ffcde9615dc5bb4825b9f6aed07716f1f57faae0" + "reference": "ffc7f5692092df31515df2a5ecf3b7302b3ddacf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/ffcde9615dc5bb4825b9f6aed07716f1f57faae0", - "reference": "ffcde9615dc5bb4825b9f6aed07716f1f57faae0", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/ffc7f5692092df31515df2a5ecf3b7302b3ddacf", + "reference": "ffc7f5692092df31515df2a5ecf3b7302b3ddacf", "shasum": "" }, "require": { @@ -6620,7 +6620,7 @@ "interoperability", "standards" ], - "time": "2019-09-17T11:12:18+00:00" + "time": "2019-10-14T12:27:06+00:00" }, { "name": "symfony/stopwatch", diff --git a/src/Controller/AdminPages/BaseAdminController.php b/src/Controller/AdminPages/BaseAdminController.php index fe545f1e..f2901087 100644 --- a/src/Controller/AdminPages/BaseAdminController.php +++ b/src/Controller/AdminPages/BaseAdminController.php @@ -199,16 +199,21 @@ abstract class BaseAdminController extends AbstractController if ($mass_creation_form->isSubmitted() && $mass_creation_form->isValid()) { $data = $mass_creation_form->getData(); - dump($data); - //Create entries based on input - $errors = $importer->massCreation($data['lines'], $this->entity_class, $data['parent']); + $errors = []; + $results = $importer->massCreation($data['lines'], $this->entity_class, $data['parent'], $errors); //Show errors to user: - foreach ($errors as $name => $error) { - /* @var $error ConstraintViolationList */ - $this->addFlash('error', $name.':'.$error); + foreach ($errors as $error) { + dump($error); + $this->addFlash('error', $error['entity']->getFullPath().':'.$error['violations']); } + + //Persist valid entities to DB + foreach ($results as $result) { + $em->persist($result); + } + $em->flush(); } return $this->render($this->twig_template, [ diff --git a/src/Services/ElementTypeNameGenerator.php b/src/Services/ElementTypeNameGenerator.php index fba29366..275554d3 100644 --- a/src/Services/ElementTypeNameGenerator.php +++ b/src/Services/ElementTypeNameGenerator.php @@ -111,14 +111,15 @@ class ElementTypeNameGenerator * @param bool $use_html If set to true, a html string is returned, where the type is set italic * * @return string The localized string + * @throws EntityNotSupportedException When the passed entity is not supported. */ public function getTypeNameCombination(NamedDBElement $entity, bool $use_html = false): string { $type = $this->getLocalizedTypeLabel($entity); if ($use_html) { - return ''.$type.': '.$entity->getName(); + return ''.$type.': '.htmlspecialchars($entity->getName()); } - return $type.': '.htmlspecialchars($entity->getName()); + return $type.': '.$entity->getName(); } } diff --git a/src/Services/EntityImporter.php b/src/Services/EntityImporter.php index a12b958a..0f056cb7 100644 --- a/src/Services/EntityImporter.php +++ b/src/Services/EntityImporter.php @@ -21,6 +21,7 @@ namespace App\Services; +use App\Entity\Base\DBElement; use App\Entity\Base\StructuralDBElement; use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\HttpFoundation\File\File; @@ -44,7 +45,7 @@ class EntityImporter protected function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults([ - 'csv_separator' => ';', + 'csv_separator' => ';', 'format' => 'json', 'preserve_children' => true, 'parent' => null, @@ -53,24 +54,37 @@ class EntityImporter } /** - * Creates many entries at once, based on a (text) list of names. + * Creates many entries at once, based on a (text) list of name. + * The created enties are not persisted to database yet, so you have to do it yourself. * * @param string $lines The list of names seperated by \n * @param string $class_name The name of the class for which the entities should be created * @param StructuralDBElement|null $parent The element which will be used as parent element for new elements. + * @param array $errors An associative array containing all validation errors. * - * @return array An associative array containing an ConstraintViolationList and the entity name as key are returned, - * if an error happened during validation. When everything was successfull, the array should be empty. + * @return StructuralDBElement[] An array containing all valid imported entities (with the type $class_name) */ - public function massCreation(string $lines, string $class_name, ?StructuralDBElement $parent): array + public function massCreation(string $lines, string $class_name, ?StructuralDBElement $parent = null, array &$errors = []): array { //Expand every line to a single entry: $names = explode("\n", $lines); + if (!is_a($class_name, StructuralDBElement::class, true)) { + throw new \InvalidArgumentException('$class_name must be a StructuralDBElement type!'); + } + if ($parent !== null && !is_a($parent, $class_name)) { + throw new \InvalidArgumentException('$parent must have the same type as specified in $class_name!'); + } + $errors = []; + $valid_entities = []; foreach ($names as $name) { $name = trim($name); + if ($name === '') { + //Skip empty lines (StrucuralDBElements must have a name) + continue; + } /** @var $entity StructuralDBElement */ //Create new element with given name $entity = new $class_name(); @@ -81,17 +95,13 @@ class EntityImporter $tmp = $this->validator->validate($entity); //If no error occured, write entry to DB: if (0 === \count($tmp)) { - $this->em->persist($entity); + $valid_entities[] = $entity; } else { //Otherwise log error - dump($tmp); - $errors[$entity->getFullPath()] = $tmp; + $errors[] = ['entity' => $entity, 'violations' => $tmp]; } } - //Save changes to database - $this->em->flush(); - - return $errors; + return $valid_entities; } /** diff --git a/tests/Services/ElementTypeNameGeneratorTest.php b/tests/Services/ElementTypeNameGeneratorTest.php new file mode 100644 index 00000000..12b2b968 --- /dev/null +++ b/tests/Services/ElementTypeNameGeneratorTest.php @@ -0,0 +1,89 @@ +service = self::$container->get(ElementTypeNameGenerator::class); + } + + public function testGetLocalizedTypeNameCombination() + { + //We only test in english + $this->assertEquals('Part', $this->service->getLocalizedTypeLabel(new Part())); + $this->assertEquals('Category', $this->service->getLocalizedTypeLabel(new Category())); + + //Test inheritance + $this->assertEquals('Attachment', $this->service->getLocalizedTypeLabel(new PartAttachment())); + + //Test exception for unknpwn type + $this->expectException(EntityNotSupportedException::class); + $this->service->getLocalizedTypeLabel(new class extends DBElement { + public function getIDString(): string + { + return 'Stub'; + } + }); + } + + public function testGetTypeNameCombination() + { + $part = new Part(); + $part->setName('TestassertEquals('Part: Testservice->getTypeNameCombination($part, false)); + + $this->assertEquals('Part: Test<Part', $this->service->getTypeNameCombination($part, true)); + + //Test exception + $this->expectException(EntityNotSupportedException::class); + $this->service->getTypeNameCombination(new class extends NamedDBElement { + public function getIDString(): string + { + return 'Stub'; + } + }); + } +} \ No newline at end of file diff --git a/tests/Services/EntityImporterTest.php b/tests/Services/EntityImporterTest.php new file mode 100644 index 00000000..36a4912c --- /dev/null +++ b/tests/Services/EntityImporterTest.php @@ -0,0 +1,84 @@ +service = self::$container->get(EntityImporter::class); + } + + public function testMassCreationResults() + { + $errors = []; + $results = $this->service->massCreation('', AttachmentType::class, null, $errors); + $this->assertEmpty($results); + $this->assertEmpty($errors); + + $errors = []; + $lines = "Test 1 \n Test 2 \n Test 3"; + $results = $this->service->massCreation($lines, AttachmentType::class, null, $errors); + $this->assertCount(0, $errors); + $this->assertCount(3, $results); + //Check type + $this->assertInstanceOf(AttachmentType::class, $results[0]); + //Check names + $this->assertEquals('Test 1', $results[0]->getName()); + $this->assertEquals('Test 2', $results[1]->getName()); + //Check parent + $this->assertNull($results[0]->getMasterPictureAttachment()); + + $parent = new AttachmentType(); + $results = $this->service->massCreation($lines, AttachmentType::class, $parent, $errors); + $this->assertCount(3, $results); + $this->assertEquals($parent, $results[0]->getParent()); + } + + public function testMassCreationErrors() + { + $errors = []; + //Node 1 and Node 2 are created in datafixtures, so their attemp to create them again must fail. + $lines = "Test 1 \n Node 1 \n Node 2"; + $results = $this->service->massCreation($lines, AttachmentType::class, null, $errors); + $this->assertCount(1, $results); + $this->assertEquals('Test 1', $results[0]->getName()); + $this->assertCount(2, $errors); + $this->assertEquals('Node 1', $errors[0]['entity']->getName()); + } +} \ No newline at end of file