From 8a46e11bf61cf91726e5e7c9813f051c250abe36 Mon Sep 17 00:00:00 2001 From: Tom H Anderson Date: Sat, 23 Jun 2018 21:33:44 -0600 Subject: [PATCH 1/3] Added unit tests for distinct --- README.md | 2 +- src/AbstractAbstractFactory.php | 4 ++++ src/Resolve/EntityResolveAbstractFactory.php | 2 +- src/Type/DateTimeType.php | 13 ++++++++++--- src/Type/EntityTypeAbstractFactory.php | 4 ++++ test/GraphQL/CriteriaFiltersTest.php | 15 +++++++++++++++ test/GraphQL/QueryBuilderFiltersTest.php | 15 +++++++++++++++ 7 files changed, 50 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 3d08fb3..c3b414b 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ $entity->getRelation()->getField1() And see it realized in GraphQL with fine grained control over each field via hydrators: ```js -{ entity { relation { field1 field2 manyToOne { name field 3} } otherRelation { field4 field5 } } } +{ entity { relation { field1 field2 manyToOne { name field3 } } otherRelation { field4 field5 } } } ``` diff --git a/src/AbstractAbstractFactory.php b/src/AbstractAbstractFactory.php index 5a0d27c..1357da6 100644 --- a/src/AbstractAbstractFactory.php +++ b/src/AbstractAbstractFactory.php @@ -31,7 +31,9 @@ protected function getCache($requestedName, array $options = null) } } + // @codeCoverageIgnoreStart throw new Exception('Cache not found for ' . $requestedName); + // @codeCoverageIgnoreEnd } protected function cache($requestedName, array $options = null, $instance) @@ -79,10 +81,12 @@ protected function mapFieldType(string $fieldType) $graphQLType = Type::listOf(Type::string()); break; default: + // @codeCoverageIgnoreStart // Do not process unknown for now $graphQLType = null; break; } + // @codeCoverageIgnoreEnd return $graphQLType; } diff --git a/src/Resolve/EntityResolveAbstractFactory.php b/src/Resolve/EntityResolveAbstractFactory.php index 6082f46..bdedfa0 100644 --- a/src/Resolve/EntityResolveAbstractFactory.php +++ b/src/Resolve/EntityResolveAbstractFactory.php @@ -303,7 +303,7 @@ public function __invoke(ContainerInterface $container, $requestedName, array $o if ($distinctField) { $distinctValueCollection = new ArrayCollection(); foreach ($resultCollection as $key => $value) { - if (! in_array($value[$distinctField], $distinctValueCollection)) { + if (! $distinctValueCollection->contains($value[$distinctField])) { $distinctValueCollection->add($value[$distinctField]); } else { $resultCollection->remove($key); diff --git a/src/Type/DateTimeType.php b/src/Type/DateTimeType.php index 9abce3e..56e4aea 100644 --- a/src/Type/DateTimeType.php +++ b/src/Type/DateTimeType.php @@ -16,6 +16,9 @@ final class DateTimeType extends ScalarType { + /** + * @codeCoverageIgnore + */ public function parseLiteral($valueNode, ?array $variables = null) { // Note: throwing GraphQL\Error\Error vs \UnexpectedValueException to benefit from GraphQL @@ -27,19 +30,23 @@ public function parseLiteral($valueNode, ?array $variables = null) return $valueNode->value; } + /** + * @codeCoverageIgnore + */ public function parseValue($value) { if (! is_string($value)) { - throw new \UnexpectedValueException('Cannot represent value as DateTime date: ' . $value); + $stringValue = print_r($value, true); + throw new \UnexpectedValueException('Date is not a string: ' . $stringValue); } - return new DateTime($value); + return DateTime::createFromFormat('Y-m-d\TH:i:sP', $value); } public function serialize($value) { if ($value instanceof DateTime) { - return $value->format('c'); + $value = $value->format('c'); } return $value; diff --git a/src/Type/EntityTypeAbstractFactory.php b/src/Type/EntityTypeAbstractFactory.php index 5624e0e..27887a9 100644 --- a/src/Type/EntityTypeAbstractFactory.php +++ b/src/Type/EntityTypeAbstractFactory.php @@ -100,7 +100,9 @@ public function __invoke(ContainerInterface $container, $requestedName, array $o break; case ClassMetadataInfo::ONE_TO_MANY: case ClassMetadataInfo::MANY_TO_MANY: + // @codeCoverageIgnoreStart case ClassMetadataInfo::TO_MANY: + // @codeCoverageIgnoreEnd $targetEntity = $associationMetadata['targetEntity']; $references[$fieldName] = function () use ( $options, @@ -279,9 +281,11 @@ public function __invoke(ContainerInterface $container, $requestedName, array $o } continue; + // @codeCoverageIgnoreStart } catch (MappingException $e) { continue; } + // @codeCoverageIgnoreEnd } $graphQLType = $this->mapFieldType($fieldMetadata['type']); diff --git a/test/GraphQL/CriteriaFiltersTest.php b/test/GraphQL/CriteriaFiltersTest.php index 4a108f6..8428faf 100644 --- a/test/GraphQL/CriteriaFiltersTest.php +++ b/test/GraphQL/CriteriaFiltersTest.php @@ -308,4 +308,19 @@ public function testMemberOf($schemaName, $context) $this->assertEquals(['b1', 'b2', 'b3'], $output['data']['user'][0]['artist'][0]['alias']); } + + /** + * @dataProvider schemaDataProvider + */ + public function testDistinct($schemaName, $context) + { + $schema = $this->getSchema($schemaName); + + $query = "{ user ( filter: { id:1 } ) { name artist ( filter: { name_distinct: true } ) { id alias } } }"; + + $result = GraphQL::executeQuery($schema, $query, $rootValue = null, $context, $variableValues = null); + $output = $result->toArray(); + + $this->assertEquals(5, sizeof($output['data']['user'][0]['artist'])); + } } diff --git a/test/GraphQL/QueryBuilderFiltersTest.php b/test/GraphQL/QueryBuilderFiltersTest.php index 9b36019..13197f2 100644 --- a/test/GraphQL/QueryBuilderFiltersTest.php +++ b/test/GraphQL/QueryBuilderFiltersTest.php @@ -307,4 +307,19 @@ public function testMemberOf($schemaName, $context) $this->assertEquals(['b1', 'b2', 'b3'], $output['data']['artist'][0]['alias']); } + + /** + * @dataProvider schemaDataProvider + */ + public function testDistinct($schemaName, $context) + { + $schema = $this->getSchema($schemaName); + + $query = "{ artist ( filter: { name_distinct: true } ) { name } }"; + + $result = GraphQL::executeQuery($schema, $query, $rootValue = null, $context, $variableValues = null); + $output = $result->toArray(); + + $this->assertEquals(5, sizeof($output['data']['artist'])); + } } From 118f010c3601ce1c3cdecbab7bdcf665670c326b Mon Sep 17 00:00:00 2001 From: Tom H Anderson Date: Sat, 23 Jun 2018 22:06:05 -0600 Subject: [PATCH 2/3] Towards 100% coverage --- src/Criteria/FilterTypeAbstractFactory.php | 2 ++ src/Filter/FilterTypeAbstractFactory.php | 5 +++++ src/Hydrator/DoctrineHydrator.php | 1 + src/Hydrator/DoctrineHydratorFactory.php | 12 +++++++++--- src/Hydrator/HydratorExtractToolDefault.php | 4 ++++ src/Resolve/EntityResolveAbstractFactory.php | 2 ++ 6 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/Criteria/FilterTypeAbstractFactory.php b/src/Criteria/FilterTypeAbstractFactory.php index fa14985..2a8cfe6 100644 --- a/src/Criteria/FilterTypeAbstractFactory.php +++ b/src/Criteria/FilterTypeAbstractFactory.php @@ -47,9 +47,11 @@ public function canCreate(ContainerInterface $container, $requestedName) public function __invoke(ContainerInterface $container, $requestedName, array $options = null) : FilterType { + // @codeCoverageIgnoreStart if ($this->isCached($requestedName, $options)) { return $this->getCache($requestedName, $options); } + // @codeCoverageIgnoreEnd $config = $container->get('config'); $fields = []; diff --git a/src/Filter/FilterTypeAbstractFactory.php b/src/Filter/FilterTypeAbstractFactory.php index 259298e..8575df2 100644 --- a/src/Filter/FilterTypeAbstractFactory.php +++ b/src/Filter/FilterTypeAbstractFactory.php @@ -47,9 +47,11 @@ public function canCreate(ContainerInterface $container, $requestedName) public function __invoke(ContainerInterface $container, $requestedName, array $options = null) : FilterType { + // @codeCoverageIgnoreStart if ($this->isCached($requestedName, $options)) { return $this->getCache($requestedName, $options); } + // @codeCoverageIgnoreEnd $fields = []; $config = $container->get('config'); @@ -235,7 +237,10 @@ public function __invoke(ContainerInterface $container, $requestedName, array $o 'name' => str_replace('\\', '_', $requestedName) . 'Filter', 'fields' => function () use ($fields, $references) { foreach ($references as $referenceName => $resolve) { + // @codeCoverageIgnoreStart + // This works fine but may need bigger unit tests $fields[$referenceName] = $resolve(); + // @codeCoverageIgnoreEnd } return $fields; diff --git a/src/Hydrator/DoctrineHydrator.php b/src/Hydrator/DoctrineHydrator.php index ea0640d..2005afd 100644 --- a/src/Hydrator/DoctrineHydrator.php +++ b/src/Hydrator/DoctrineHydrator.php @@ -31,6 +31,7 @@ public function __construct($extractService, $hydrateService) /** * @return \Zend\Hydrator\HydratorInterface + * @codeCoverageIgnore */ public function getExtractService() { diff --git a/src/Hydrator/DoctrineHydratorFactory.php b/src/Hydrator/DoctrineHydratorFactory.php index bc6552e..c6671ba 100644 --- a/src/Hydrator/DoctrineHydratorFactory.php +++ b/src/Hydrator/DoctrineHydratorFactory.php @@ -129,10 +129,12 @@ public function __invoke(ContainerInterface $container, $requestedName, array $o $hydrateService = $this->loadEntityHydrator($container, $config, $objectManager); } + // @codeCoverageIgnoreStart if ($useCustomHydrator && $config['hydrator']) { $extractService = $container->get($config['hydrator']); $hydrateService = $extractService; } + // @codeCoverageIgnoreEnd # Use DoctrineModuleHydrator by default if (! isset($extractService, $hydrateService)) { @@ -243,6 +245,7 @@ public function configureHydrator($hydrator, ContainerInterface $container, $con * @param ObjectManager $objectManager * * @throws ServiceNotCreatedException + * @codeCoverageIgnore */ public function configureHydratorNamingStrategy($hydrator, ContainerInterface $container, $config, $objectManager) { @@ -250,7 +253,6 @@ public function configureHydratorNamingStrategy($hydrator, ContainerInterface $c return; } - // @codeCoverageIgnoreStart $namingStrategyKey = $config['naming_strategy']; if (! $container->has($namingStrategyKey)) { throw new ServiceNotCreatedException(sprintf('Invalid naming strategy %s.', $namingStrategyKey)); @@ -267,7 +269,6 @@ public function configureHydratorNamingStrategy($hydrator, ContainerInterface $c if ($namingStrategy instanceof ObjectManagerAwareInterface) { $namingStrategy->setObjectManager($objectManager); } - // @codeCoverageIgnoreEnd $hydrator->setNamingStrategy($namingStrategy); } @@ -282,12 +283,14 @@ public function configureHydratorNamingStrategy($hydrator, ContainerInterface $c */ protected function configureHydratorStrategies($hydrator, ContainerInterface $container, $config, $objectManager) { + // @codeCoverageIgnoreStart if (! $hydrator instanceof StrategyEnabledInterface || ! isset($config['strategies']) || ! is_array($config['strategies']) ) { return; } + // @codeCoverageIgnoreEnd foreach ($config['strategies'] as $field => $strategyKey) { // @codeCoverageIgnoreStart @@ -304,9 +307,11 @@ protected function configureHydratorStrategies($hydrator, ContainerInterface $co // @codeCoverageIgnoreEnd // Attach object manager: + // @codeCoverageIgnoreStart if ($strategy instanceof ObjectManagerAwareInterface) { $strategy->setObjectManager($objectManager); } + // @codeCoverageIgnoreEnd $hydrator->addStrategy($field, $strategy); } @@ -356,11 +361,12 @@ protected function configureHydratorFilters($hydrator, ContainerInterface $conta sprintf('Filter service %s must implement FilterInterface', get_class($filterService)) ); } - // @codeCoverageIgnoreEnd if ($filterService instanceof ObjectManagerAwareInterface) { $filterService->setObjectManager($objectManager); } + // @codeCoverageIgnoreEnd + $hydrator->addFilter($name, $filterService, $condition); } } diff --git a/src/Hydrator/HydratorExtractToolDefault.php b/src/Hydrator/HydratorExtractToolDefault.php index 4035cb6..1899d32 100644 --- a/src/Hydrator/HydratorExtractToolDefault.php +++ b/src/Hydrator/HydratorExtractToolDefault.php @@ -30,8 +30,10 @@ public function extractToCollection($entityArray, string $hydratorAlias, $option $resultCollection = new ArrayCollection(); foreach ($entityArray as $value) { + // @codeCoverageIgnoreStart if (is_array($value)) { $resultCollection->add($value); + // @codeCoverageIgnoreEnd } else { $resultCollection->add($hydrator->extract($value)); } @@ -43,9 +45,11 @@ public function extractToCollection($entityArray, string $hydratorAlias, $option // Extract a single entity public function extract($entity, string $hydratorAlias, $options) { + // @codeCoverageIgnoreStart if (is_array($entity)) { return $entity; } + // @codeCoverageIgnoreEnd $options = $this->optionsToArray($options); $hydrator = $this->hydratorManager->build($hydratorAlias, $options); diff --git a/src/Resolve/EntityResolveAbstractFactory.php b/src/Resolve/EntityResolveAbstractFactory.php index bdedfa0..f398f79 100644 --- a/src/Resolve/EntityResolveAbstractFactory.php +++ b/src/Resolve/EntityResolveAbstractFactory.php @@ -71,9 +71,11 @@ public function canCreate(ContainerInterface $container, $requestedName) public function __invoke(ContainerInterface $container, $requestedName, array $options = null) : Closure { + // @codeCoverageIgnoreStart if ($this->isCached($requestedName, $options)) { return $this->getCache($requestedName, $options); } + // @codeCoverageIgnoreEnd // Setup Events $this->createEventManager($container->get('SharedEventManager')); From da7a27dc743b734a48103f49138e970f0d7b8ef6 Mon Sep 17 00:00:00 2001 From: Tom H Anderson Date: Sat, 23 Jun 2018 22:06:51 -0600 Subject: [PATCH 3/3] readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c3b414b..a252699 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ GraphQL for Doctrine using Hydrators ==================================== [![Build Status](https://travis-ci.org/API-Skeletons/zf-doctrine-graphql.svg)](https://travis-ci.org/API-Skeletons/zf-doctrine-graphql) -[![Coverage](https://coveralls.io/repos/github/API-Skeletons/zf-doctrine-graphql/badge.svg?branch=master&123)](https://coveralls.io/repos/github/API-Skeletons/zf-doctrine-graphql/badge.svg?branch=master&123) +[![Coverage](https://coveralls.io/repos/github/API-Skeletons/zf-doctrine-graphql/badge.svg?branch=master&12)](https://coveralls.io/repos/github/API-Skeletons/zf-doctrine-graphql/badge.svg?branch=master&12) [![Documentation Status](https://readthedocs.org/projects/zf-doctrine-graphql/badge/?version=latest)](http://graphql.apiskeletons.com/en/latest/?badge=latest) [![Gitter](https://badges.gitter.im/api-skeletons/open-source.svg)](https://gitter.im/api-skeletons/open-source) [![Total Downloads](https://poser.pugx.org/api-skeletons/zf-doctrine-graphql/downloads)](https://packagist.org/packages/api-skeletons/zf-doctrine-graphql)