From f3aacbd37f74bb5b5b298ae189762f25e4c0bd03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Queir=C3=B3s?= Date: Sun, 3 Apr 2016 00:09:23 +0100 Subject: [PATCH] Unit tests for sparse fieldsets --- src/Manager.php | 4 +- src/Scope.php | 4 +- test/ManagerTest.php | 34 +++- test/ScopeTest.php | 180 ++++++++++++++++++++ test/Serializer/ArraySerializerTest.php | 134 ++++++++++++++- test/Serializer/DataArraySerializerTest.php | 159 ++++++++++++++++- test/Serializer/JsonApiSerializerTest.php | 151 ++++++++++++++++ 7 files changed, 655 insertions(+), 11 deletions(-) diff --git a/src/Manager.php b/src/Manager.php index e071fabf..cc3300c6 100644 --- a/src/Manager.php +++ b/src/Manager.php @@ -226,8 +226,10 @@ public function parseIncludes($includes) */ public function parseFieldsets(array $fieldsets) { + $this->requestedFieldsets = []; foreach ($fieldsets as $type => $fields) { - $this->requestedFieldsets[$type] = explode(',', $fields); + //Remove empty and repeated fields + $this->requestedFieldsets[$type] = array_unique(array_filter(explode(',', $fields))); } return $this; } diff --git a/src/Scope.php b/src/Scope.php index 545fd5f9..24bbb9c5 100644 --- a/src/Scope.php +++ b/src/Scope.php @@ -240,11 +240,11 @@ public function toArray() // If the serializer wants the includes to be side-loaded then we'll // serialize the included data and merge it with the data. if ($serializer->sideloadIncludes()) { - $includedData = $serializer->includedData($this->resource, $rawIncludedData); - //Filter out any relation that wasn't requested $rawIncludedData = array_map(array($this, 'filterFieldsets'), $rawIncludedData); + $includedData = $serializer->includedData($this->resource, $rawIncludedData); + // If the serializer wants to inject additional information // about the included resources, it can do so now. $data = $serializer->injectData($data, $rawIncludedData); diff --git a/test/ManagerTest.php b/test/ManagerTest.php index fd825e69..dc65030c 100755 --- a/test/ManagerTest.php +++ b/test/ManagerTest.php @@ -1,6 +1,7 @@ assertNull($params['totallymadeup']); } - public function testParseExcludeSelfie() + public function testParseExcludeSelfie() { $manager = new Manager(); @@ -195,6 +196,37 @@ public function testCreateDataWithCallback() } + public function testParseFieldsets() + { + $manager = new Manager(); + + $fields = [ + 'articles' => 'title,body', + 'people' => 'name' + ]; + + $expectedFieldset = [ + 'articles' => ['title' , 'body'], + 'people' => ['name'] + ]; + + $manager->parseFieldsets($fields); + $this->assertSame($expectedFieldset, $manager->getRequestedFieldsets()); + + $paramBag = new ParamBag($expectedFieldset['articles']); + $this->assertEquals($paramBag, $manager->getFieldset('articles')); + + // Are repeated fields stripped + $manager->parseFieldsets(['foo' => 'bar,baz,bar']); + $this->assertSame(['foo' => ['bar', 'baz']], $manager->getRequestedFieldsets()); + + // Are empty fields stripped + $manager->parseFieldsets(['foo' => 'bar,']); + $this->assertSame(['foo' => ['bar']], $manager->getRequestedFieldsets()); + + $this->assertSame(null, $manager->getFieldset('inexistent')); + } + public function tearDown() { Mockery::close(); diff --git a/test/ScopeTest.php b/test/ScopeTest.php index 73a76ce9..1ccbd91d 100755 --- a/test/ScopeTest.php +++ b/test/ScopeTest.php @@ -465,8 +465,188 @@ public function testNullResourceDataAndJustMeta() $this->assertSame(['meta' => ['foo' => 'bar']], $scope->toArray()); } + /** + * @covers League\Fractal\Scope::toArray + * @dataProvider fieldsetsProvider + */ + public function testToArrayWithFieldsets($fieldsetsToParse, $expected) + { + $manager = new Manager(); + + $resource = new Item( + ['foo' => 'bar', 'baz' => 'qux'], + function ($data) { + return $data; + }, + 'resourceName' + ); + + $scope = new Scope($manager, $resource); + + $manager->parseFieldsets($fieldsetsToParse); + $this->assertSame($expected, $scope->toArray()); + } + + public function fieldsetsProvider() + { + return [ + [ + ['resourceName' => 'foo'], + ['data' => ['foo' => 'bar']] + ], + [ + ['resourceName' => 'foo,baz'], + ['data' => ['foo' => 'bar', 'baz' => 'qux']] + ], + [ + ['resourceName' => 'inexistentField'], + ['data' => []] + ] + ]; + } + + /** + * @covers League\Fractal\Scope::toArray + * @dataProvider fieldsetsWithMandatorySerializerFieldsProvider + */ + public function testToArrayWithFieldsetsAndMandatorySerializerFields($fieldsetsToParse, $expected) + { + $serializer = Mockery::mock('League\Fractal\Serializer\DataArraySerializer')->makePartial(); + $serializer->shouldReceive('getMandatoryFields')->andReturn(['foo']); + + $resource = new Item( + ['foo' => 'bar', 'baz' => 'qux'], + function ($data) { + return $data; + }, + 'resourceName' + ); + + $manager = new Manager(); + $manager->setSerializer($serializer); + $scope = new Scope($manager, $resource); + + $manager->parseFieldsets($fieldsetsToParse); + $this->assertSame($expected, $scope->toArray()); + } + + public function fieldsetsWithMandatorySerializerFieldsProvider() + { + return [ + //Don't request for mandatory field + [ + ['resourceName' => 'baz'], + ['data' => ['foo' => 'bar', 'baz' => 'qux']] + ], + //Request required field anyway + [ + ['resourceName' => 'foo,baz'], + ['data' => ['foo' => 'bar', 'baz' => 'qux']] + ] + ]; + } + + /** + * @dataProvider fieldsetsWithIncludesProvider + */ + public function testToArrayWithIncludesAndFieldsets($fieldsetsToParse, $expected) + { + $transformer = $this->createTransformerWithIncludedResource('book', ['book' => ['yin' => 'yang']]); + + $resource = new Item( + ['foo' => 'bar', 'baz' => 'qux'], + $transformer, + 'resourceName' + ); + $manager = new Manager(); + $scope = new Scope($manager, $resource); + + $manager->parseIncludes('book'); + + $manager->parseFieldsets($fieldsetsToParse); + $this->assertSame($expected, $scope->toArray()); + } + + public function fieldsetsWithIncludesProvider() + { + return [ + //Included relation was not requested + [ + ['resourceName' => 'foo'], + ['data' => ['foo' => 'bar']] + ], + //Included relation was requested + [ + ['resourceName' => 'foo,book', 'book' => 'yin'], + ['data' => ['foo' => 'bar', 'book' => ['yin' => 'yang']]] + ] + ]; + } + + /** + * @covers League\Fractal\Scope::toArray + * @dataProvider fieldsetsWithSideLoadIncludesProvider + */ + public function testToArrayWithSideloadedIncludesAndFieldsets($fieldsetsToParse, $expected) + { + $serializer = Mockery::mock('League\Fractal\Serializer\DataArraySerializer')->makePartial(); + $serializer->shouldReceive('sideloadIncludes')->andReturn(true); + $serializer->shouldReceive('item')->andReturnUsing( + function ($key, $data) { + return ['data' => $data]; + } + ); + $serializer->shouldReceive('includedData')->andReturnUsing( + function ($key, $data) { + $data = array_pop($data); + return empty($data) ? [] : ['sideloaded' => $data]; + } + ); + + $manager = new Manager(); + $manager->parseIncludes('book'); + $manager->setSerializer($serializer); + + $transformer = $this->createTransformerWithIncludedResource('book', ['book' => ['yin' => 'yang']]); + + $resource = new Item(['foo' => 'bar'], $transformer, 'resourceName'); + $scope = new Scope($manager, $resource); + + $manager->parseFieldsets($fieldsetsToParse); + $this->assertSame($expected, $scope->toArray()); + } + + public function fieldsetsWithSideLoadIncludesProvider() + { + return [ + //Included relation was not requested + [ + ['resourceName' => 'foo'], + ['data' => ['foo' => 'bar']] + ], + //Included relation was requested + [ + ['resourceName' => 'foo,book', 'book' => 'yin'], + ['data' => ['foo' => 'bar'], 'sideloaded' => ['book' => ['yin' => 'yang']]] + ] + ]; + } + public function tearDown() { Mockery::close(); } + + protected function createTransformerWithIncludedResource($resourceName, $transformResult) + { + $transformer = Mockery::mock('League\Fractal\TransformerAbstract')->makePartial(); + $transformer->shouldReceive('getAvailableIncludes')->twice()->andReturn([$resourceName]); + $transformer->shouldReceive('transform')->once()->andReturnUsing( + function (array $data) { + return $data; + } + ); + $transformer->shouldReceive('processIncludedResources')->once()->andReturn($transformResult); + return $transformer; + } } diff --git a/test/Serializer/ArraySerializerTest.php b/test/Serializer/ArraySerializerTest.php index bb039a7d..23143ca1 100644 --- a/test/Serializer/ArraySerializerTest.php +++ b/test/Serializer/ArraySerializerTest.php @@ -56,7 +56,32 @@ public function testSerializingItemResource() $this->assertSame($expected, $scope->toArray()); - // Same again with meta + //Test single field + $manager->parseFieldsets(['book' => 'title']); + $expected = ['title' => 'Foo']; + $this->assertSame($expected, $scope->toArray()); + + //Test multiple field + $manager->parseFieldsets(['book' => 'title,year']); + $expected = [ + 'title' => 'Foo', + 'year' => 1991 + ]; + $this->assertSame($expected, $scope->toArray()); + + //Test with relationship field + $manager->parseFieldsets(['book' => 'title,author', 'author' => 'name']); + $expected = [ + 'title' => 'Foo', + 'author' => [ + 'name' => 'Dave' + ], + ]; + $this->assertSame($expected, $scope->toArray()); + + //Clear all sparse fieldsets + $manager->parseFieldsets([]); + //Same again with meta $resource->setMetaValue('foo', 'bar'); $scope = new Scope($manager, $resource); @@ -64,14 +89,27 @@ public function testSerializingItemResource() $expected = [ 'title' => 'Foo', 'year' => 1991, + 'author' => [ + 'name' => 'Dave' + ], + 'meta' => [ + 'foo' => 'bar' + ] + ]; + + $this->assertSame($expected, $scope->toArray()); + + //Test with relationship field + $manager->parseFieldsets(['book' => 'title,author', 'author' => 'name']); + $expected = [ + 'title' => 'Foo', 'author' => [ 'name' => 'Dave', ], 'meta' => [ 'foo' => 'bar', - ], + ] ]; - $this->assertSame($expected, $scope->toArray()); } @@ -111,6 +149,55 @@ public function testSerializingCollectionResource() $expectedJson = '{"books":[{"title":"Foo","year":1991,"author":{"name":"Dave"}},{"title":"Bar","year":1997,"author":{"name":"Bob"}}]}'; $this->assertSame($expectedJson, $scope->toJson()); + //Test single field + $manager->parseFieldsets(['books' => 'title']); + $expected = [ + 'books' => [ + ['title' => 'Foo'], + ['title' => 'Bar'] + ] + ]; + $this->assertSame($expected, $scope->toArray()); + + //Test multiple field + $manager->parseFieldsets(['books' => 'title,year']); + $expected = [ + 'books' => [ + [ + 'title' => 'Foo', + 'year' => 1991 + ], + [ + 'title' => 'Bar', + 'year' => 1997 + ] + ] + ]; + $this->assertSame($expected, $scope->toArray()); + + //Test with relationship field + $manager->parseFieldsets(['books' => 'title,author', 'author' => 'name']); + $expected = [ + 'books' => [ + [ + 'title' => 'Foo', + 'author' => [ + 'name' => 'Dave' + ] + ], + [ + 'title' => 'Bar', + 'author' => [ + 'name' => 'Bob' + ] + ] + ] + ]; + $this->assertSame($expected, $scope->toArray()); + + //Clear all sparse fieldsets + $manager->parseFieldsets([]); + // Same again with metadata $resource->setMetaValue('foo', 'bar'); @@ -142,6 +229,28 @@ public function testSerializingCollectionResource() $expectedJson = '{"books":[{"title":"Foo","year":1991,"author":{"name":"Dave"}},{"title":"Bar","year":1997,"author":{"name":"Bob"}}],"meta":{"foo":"bar"}}'; $this->assertSame($expectedJson, $scope->toJson()); + + $manager->parseFieldsets(['books' => 'title,author', 'author' => 'name']); + $expected = [ + 'books' => [ + [ + 'title' => 'Foo', + 'author' => [ + 'name' => 'Dave' + ] + ], + [ + 'title' => 'Bar', + 'author' => [ + 'name' => 'Bob' + ] + ] + ], + 'meta' => [ + 'foo' => 'bar', + ] + ]; + $this->assertSame($expected, $scope->toArray()); } public function testSerializingNullResource() @@ -162,6 +271,21 @@ public function testSerializingNullResource() $expectedJson = '[]'; $this->assertSame($expectedJson, $scope->toJson()); + //Test single field + $manager->parseFieldsets(['books' => 'title']); + $this->assertSame($expected, $scope->toArray()); + + //Test multiple fields + $manager->parseFieldsets(['books' => 'title,year']); + $this->assertSame($expected, $scope->toArray()); + + //Test with relationship + $manager->parseFieldsets(['books' => 'title,author', 'author' => 'name']); + $this->assertSame($expected, $scope->toArray()); + + //Clear all sparse fieldsets + $manager->parseFieldsets([]); + // Same again with metadata $resource->setMetaValue('foo', 'bar'); $scope = new Scope($manager, $resource); @@ -175,6 +299,10 @@ public function testSerializingNullResource() $expectedJson = '{"meta":{"foo":"bar"}}'; $this->assertSame($expectedJson, $scope->toJson()); + + //Test with relationship + $manager->parseFieldsets(['books' => 'title,author', 'author' => 'name']); + $this->assertSame($expected, $scope->toArray()); } public function testSerializingCollectionResourceWithoutName() diff --git a/test/Serializer/DataArraySerializerTest.php b/test/Serializer/DataArraySerializerTest.php index 5de42b51..d51808aa 100644 --- a/test/Serializer/DataArraySerializerTest.php +++ b/test/Serializer/DataArraySerializerTest.php @@ -42,13 +42,48 @@ public function testSerializingItemResource() $this->assertSame($expected, $scope->toArray()); + //Test single field + $manager->parseFieldsets(['book' => 'title']); + $expected = [ + 'data' => [ + 'title' => 'Foo', + ] + ]; + $this->assertSame($expected, $scope->toArray()); + + //Test multiple field + $manager->parseFieldsets(['book' => 'title,year']); + $expected = [ + 'data' => [ + 'title' => 'Foo', + 'year' => 1991 + ] + ]; + $this->assertSame($expected, $scope->toArray()); + + //Test with relationship field + $manager->parseFieldsets(['book' => 'title,author', 'author' => 'name']); + $expected = [ + 'data' => [ + 'title' => 'Foo', + 'author' => [ + 'data' => [ + 'name' => 'Dave' + ] + ] + ] + ]; + $this->assertSame($expected, $scope->toArray()); + + //Clear all sparse fieldsets + $manager->parseFieldsets([]); + // Same again with metadata $resource = new Item($bookData, new GenericBookTransformer(), 'book'); $resource->setMetaValue('foo', 'bar'); $scope = new Scope($manager, $resource); - $expected = [ 'data' => [ 'title' => 'Foo', @@ -66,6 +101,24 @@ public function testSerializingItemResource() ]; $this->assertSame($expected, $scope->toArray()); + + //Test with relationship field + $manager->parseFieldsets(['book' => 'title,author', 'author' => 'name']); + $expected = [ + 'data' => [ + 'title' => 'Foo', + 'author' => [ + 'data' => [ + 'name' => 'Dave' + + ] + ] + ], + 'meta' => [ + 'foo' => 'bar' + ], + ]; + $this->assertSame($expected, $scope->toArray()); } public function testSerializingCollectionResource() @@ -92,7 +145,7 @@ public function testSerializingCollectionResource() ]; // Try without metadata - $resource = new Collection($booksData, new GenericBookTransformer(), 'book'); + $resource = new Collection($booksData, new GenericBookTransformer(), 'books'); $scope = new Scope($manager, $resource); @@ -124,8 +177,61 @@ public function testSerializingCollectionResource() $expectedJson = '{"data":[{"title":"Foo","year":1991,"author":{"data":{"name":"Dave"}}},{"title":"Bar","year":1997,"author":{"data":{"name":"Bob"}}}]}'; $this->assertSame($expectedJson, $scope->toJson()); + //Test single field + $manager->parseFieldsets(['books' => 'title']); + $expected = [ + 'data' => [ + ['title' => 'Foo'], + ['title' => 'Bar'] + ], + ]; + $this->assertSame($expected, $scope->toArray()); + + //Test multiple field + $manager->parseFieldsets(['books' => 'title,year']); + $expected = [ + 'data' => [ + [ + 'title' => 'Foo', + 'year' => 1991 + ], + [ + 'title' => 'Bar', + 'year' => 1997 + ] + ], + ]; + $this->assertSame($expected, $scope->toArray()); + + //Test with relationship field + $manager->parseFieldsets(['books' => 'title,author', 'author' => 'name']); + $expected = [ + 'data' => [ + [ + 'title' => 'Foo', + 'author' => [ + 'data' => [ + 'name' => 'Dave' + ] + ] + ], + [ + 'title' => 'Bar', + 'author' => [ + 'data' => [ + 'name' => 'Bob' + ] + ] + ] + ] + ]; + $this->assertSame($expected, $scope->toArray()); + + //Clear all sparse fieldsets + $manager->parseFieldsets([]); + // Same again with meta - $resource = new Collection($booksData, new GenericBookTransformer(), 'book'); + $resource = new Collection($booksData, new GenericBookTransformer(), 'books'); $resource->setMetaValue('foo', 'bar'); $scope = new Scope($manager, $resource); @@ -148,7 +254,6 @@ public function testSerializingCollectionResource() 'author' => [ 'data' => [ 'name' => 'Bob', - ], ], ], @@ -162,6 +267,33 @@ public function testSerializingCollectionResource() $expectedJson = '{"data":[{"title":"Foo","year":1991,"author":{"data":{"name":"Dave"}}},{"title":"Bar","year":1997,"author":{"data":{"name":"Bob"}}}],"meta":{"foo":"bar"}}'; $this->assertSame($expectedJson, $scope->toJson()); + + $manager->parseFieldsets(['books' => 'title,author', 'author' => 'name']); + $expected = [ + 'data' => [ + [ + 'title' => 'Foo', + 'author' => [ + 'data' => [ + 'name' => 'Dave' + ] + ] + ], + [ + 'title' => 'Bar', + 'author' => [ + 'data' => [ + 'name' => 'Bob' + ] + ] + ] + ], + 'meta' => [ + 'foo' => 'bar' + ] + ]; + + $this->assertSame($expected, $scope->toArray()); } public function testSerializingNullResource() @@ -187,6 +319,21 @@ public function testSerializingNullResource() ]; $this->assertSame($expected, $scope->toArray()); + //Test single field + $manager->parseFieldsets(['book' => 'title']); + $this->assertSame($expected, $scope->toArray()); + + //Test multiple fields + $manager->parseFieldsets(['book' => 'title,year']); + $this->assertSame($expected, $scope->toArray()); + + //Test with relationship + $manager->parseFieldsets(['book' => 'title,author', 'author' => 'name']); + $this->assertSame($expected, $scope->toArray()); + + //Clear all sparse fieldsets + $manager->parseFieldsets([]); + // Same again with metadata $resource = new NullResource($bookData, new GenericBookTransformer(), 'book'); $resource->setMetaValue('foo', 'bar'); @@ -200,6 +347,10 @@ public function testSerializingNullResource() ], ]; $this->assertSame($expected, $scope->toArray()); + + //Test with relationship + $manager->parseFieldsets(['book' => 'title,author', 'author' => 'name']); + $this->assertSame($expected, $scope->toArray()); } public function tearDown() diff --git a/test/Serializer/JsonApiSerializerTest.php b/test/Serializer/JsonApiSerializerTest.php index bafcce7d..4e01f349 100644 --- a/test/Serializer/JsonApiSerializerTest.php +++ b/test/Serializer/JsonApiSerializerTest.php @@ -2160,6 +2160,157 @@ public function testCustomLinkMergeNoLink() $this->assertSame(json_encode($expected), $scope->toJson()); } + /** + * @dataProvider serializingWithFieldsetsProvider + */ + public function testSerializingWithFieldsets($fieldsetsToParse, $expected) + { + $this->manager->parseIncludes(['author', 'author.published']); + + $bookData = [ + 'id' => 1, + 'title' => 'Foo', + 'year' => '1991', + '_author' => [ + 'id' => 1, + 'name' => 'Dave', + '_published' => [ + [ + 'id' => 1, + 'title' => 'Foo', + 'year' => '1991', + ], + [ + 'id' => 2, + 'title' => 'Bar', + 'year' => '2015', + ], + ], + ], + ]; + + $resource = new Item($bookData, new JsonApiBookTransformer(), 'books'); + + $scope = new Scope($this->manager, $resource); + + $this->manager->parseFieldsets($fieldsetsToParse); + $this->assertSame($expected, $scope->toArray()); + } + + public function serializingWithFieldsetsProvider() + { + return [ + [ + //Single field + ['books' => 'title'], + [ + 'data' => [ + 'type' => 'books', + 'id' => '1', + 'attributes' => [ + 'title' => 'Foo' + ] + ] + ] + ], + [ + //Multiple fields + ['books' => 'title,year'], + [ + 'data' => [ + 'type' => 'books', + 'id' => '1', + 'attributes' => [ + 'title' => 'Foo', + 'year' => 1991 + ] + ] + ] + ], + [ + //Include 1st level relationship + ['books' => 'title,author', 'people' => 'name'], + [ + 'data' => [ + 'type' => 'books', + 'id' => '1', + 'attributes' => [ + 'title' => 'Foo' + ], + 'relationships' => [ + 'author' => [ + 'data' => [ + 'type' => 'people', + 'id' => '1' + ] + ] + ] + ], + 'included' => [ + [ + 'type' => 'people', + 'id' => '1', + 'attributes' => [ + 'name' => 'Dave' + ] + ] + ] + ] + ], + [ + //Include 2nd level relationship + ['books' => 'title,author', 'people' => 'name,published'], + [ + 'data' => [ + 'type' => 'books', + 'id' => '1', + 'attributes' => [ + 'title' => 'Foo' + ], + 'relationships' => [ + 'author' => [ + 'data' => [ + 'type' => 'people', + 'id' => '1' + ] + ] + ] + ], + 'included' => [ + [ + 'type' => 'books', + 'id' => '2', + 'attributes' => [ + 'title' => 'Bar' + ] + ], + [ + 'type' => 'people', + 'id' => '1', + 'attributes' => [ + 'name' => 'Dave' + ], + 'relationships' => [ + 'published' => [ + 'data' => [ + [ + 'type' => 'books', + 'id' => '1' + ], + [ + 'type' => 'books', + 'id' => '2' + ] + ] + ] + ] + ] + ] + ] + ] + ]; + } + public function tearDown() { Mockery::close();